RISC-V: A Baremetal Introduction using C++. Interrupt Handling.

RISC-V Machine Mode Interrupts

The standard RISC-V ISA does not specify how to wire up a tangle of system interrupts.

C++ Callbacks

Can we register a C++ lambda function as an interrupt handler?

        static const auto handler = [&] (void) 
{
auto this_cause = riscv::csrs.mcause.read();
// ...more code...
}

Installing an Interrupt Handler with GCC

namespace irq {
static void entry(void)
__attribute__ ((interrupt ("machine")));
#pragma GCC push_options
// Force the alignment for mtvec.BASE.
#pragma GCC optimize ("align-functions=4")
static void entry(void) {
// Jump into the function defined within
// the irq::handler class.
handler::handler_entry();
}
#pragma GCC pop_options
}
riscv::csrs.mtvec.write(reinterpret_cast<std::uintptr_t>(irq::entry) );

Trampoline into C++

namespace irq {
class handler {
public:
/** Create an IRQ handler class to install a
function as the machine mode irq handler */
template<class T> handler(T const &isr_handler);
inline static void (*_execute_handler)(void);
// Trampoline function is required to bridge
// from the entry point function declared with
// specific attributes and alignments to this class member.
friend void entry(void);
/* Step 1 */
static inline void handler_entry(void) {
_execute_handler();
}
}
template<class T> handler::handler(T const &isr_handler) {
// This will call the C++ function object method
// that represents the lambda function above.
// This is required to provide the context of
// the function call that is captured by the lambda.
// A RISC-V optimization uses the MSCRATCH register
// to hold the function object context pointer.
/* Step 2 */
_execute_handler = [](void)
{
// Read the context from the interrupt
// scratch register.
/* Step 4 */
uintptr_t isr_context = riscv::csrs.mscratch.read();
// Call into the lambda function.
/* Step 5 */
return ((T *)isr_context)->operator()();
};
// Get a pointer to the IRQ context and save
// in the interrupt scratch register.
uintptr_t isr_context = (uintptr_t)&isr_handler;
/* Step 3 */
riscv::csrs.mscratch.write(
reinterpret_cast<std::uintptr_t>(isr_context) );
// Write the entry() function to the mtvec register
/// to install our IRQ handler.
riscv::csrs.mtvec.write(
reinterpret_cast<std::uintptr_t>(entry) );
}
}

Conclusion

--

--

--

Software Engineer, Software Architect, Embedded Systems, Distributed Systems, Digital Design, ソフトウェアエンジニア http://www.linkedin.com/in/phil-mulholland-884a8

Love podcasts or audiobooks? Learn on the go with our new app.

Let’s look at Physics and Unity — how to make them work!

Extending Linux Photographic Workflow with a QNAP NAS

Benchmarking Apache Kafka using DI-KafkaMeter

Satellite Imagery Analysis with Python

CS373 Spring 2020: Laith Alsukhni — Week 12

Setting up Jenkins with Dockerfile

My Way to Scala — part 2

Governing API Resource Modelling

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store
Phil Mulholland

Phil Mulholland

Software Engineer, Software Architect, Embedded Systems, Distributed Systems, Digital Design, ソフトウェアエンジニア http://www.linkedin.com/in/phil-mulholland-884a8

More from Medium

C++20 Concepts: part 3

Modern C++ in Advent of Code: Day4

Modern C++ In-Depth — Move Semantics, Part 1

Friendly Introduction Pointers in C++