This is the last post in the series.

Is it possible to write pure modern C++ bare-metal firmware from the ground up for RISC-V?

The answer is a qualified yes.


This is the seventh post in a series. This post is about RISC-V interrupt handling in C++.

What are the basics of interrupt handing in RISC-V? Can we utilize modern C++ to simplify interrupt handling?

RISC-V Machine Mode Interrupts

The RISC-V ISA is not specialized for embedded applications (when compared to an ISA such as the ARM Cortex-M). Keeping this in mind, the core ISA interrupt handing is limited — an interrupt controller is not in the core ISA specification.

What do we have in the base ISA? A timer, an external interrupt, and a software interrupt (mti, mei, msi). …


This is the sixth post in a series, about the RISC-V machine mode timer and timing keeping using the C++ std::chrono library.

How does RISC-V keep time? How can we perform a periodic task with no operating system?

You may take for granted that you can simply ask the operating system to sleep and wake you up in a second. If you have programmed bare-metal systems, you’ll understand it’s not as straightforward as calling sleep().

The Machine Level ISA Timer

The RISC-V machine level ISA defines a real-time counter. It is defined as two MMIO system registers mtime and mtimer .

To get an interrupt…


This is the fifth post in a series.

What are system registers in RISC-V? How can we access them with modern C++?

System registers require special instructions to access, so unlike memory-mapped registers (MMIO) we can’t just cast a pointer to memory to get access them in C++.

Do we need to embed inline assembly in our code, destroying the flow of our clean C++? No, with some abstraction we can write code like this:

RISC-V Special Instructions and C++

auto this_cause = riscv::csrs.mcause.read();
riscv::csrs.mie.mti.set();
riscv::csrs.mtvec.write(
reinterpret_cast<std::uintptr_t>(irq_vector));

How does the above code generate custom instructions? The riscv-csr.hpp header provides the abstractions. …


In the last post, we set up the development environment. This post is about how the RISC-V core executes our program.

How do we go from reset to entering the main() function in C++ in RISC-V? Startup code is generally not something you need to worry about, however, it is of interest when bringing up a device from scratch.

Can we write our own startup code in pure C++?

Booting a RISC-V Core

From here https://commons.wikimedia.org/wiki/File:Linux_boot_screen_compact.png
From here https://commons.wikimedia.org/wiki/File:Linux_boot_screen_compact.png
Booting an OS is much more complex, but the entry point relies on the same principles.

Let’s start by looking at the RISC-V specifics needed to boot. This is where the software starts executing.

Where does the execution start at reset?

RISC-V does not define a standard…


Following on from Part 2, how do we compile this project and run it?

For this series of posts, my platform is a SiFive HiFive1 Rev B development board. It’s equipped with a 320MHz RV32IMAC (FE310 core). For software build and debug the choice is Platform IO, an IDE integrated into VS Code.

Setup

To setup a new project in Platform IO just select the board, framework, and path for the files. For this exercise, the choices are HiFive1 Rev B, Freedom E SDK, and a custom path. Customization can be done via the platformio.ini file. …


As described in Part 1, a simple C++ application to blink an LED, what does this look like with no operating system?

Blinky in C++

Here we have blinky on SiFive HiFive1 Rev B development board, built and loaded via Platform IO.

Let’s look at the program flow, and the C++ and RISC-V features used. All code here is C++. The drivers and startup routine not shown here are also C++.

(1) Instantiating the drivers for the timer and GPIO.


What does it look like to program with no operating system? Can we have direct access to hardware using a high-level language like C++? How does RISC-V work at the most stripped-back bare metal level?

This is a series of posts where I’d like to combine those topics for embedded systems programming. RISC-V and C++ have been evolving rapidly, and you will see modern C++ is a great way to explore RISC-V and develop embedded systems.

Is it possible to write pure modern C++ bare-metal firmware from the ground up for RISC-V?

RISC-V?

RISC-V has been getting a lot of publicity…

Phil Mulholland

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

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