First attempt at using Rust on RISC-V

Rust seems to becoming more and more popular these days. For a embedded C developer like myself, I became interested in what it can be used for in the embedded space.

Working for a company that heavily utilizes RISC-V I wanted to see how hard it was to get Rust running on this platform.

Setting up the project

I started off using cargo to build a new project.

cargo new riscv

That created the standard Rust project inside a directory called riscv.

Then came time to install the target and also the llvm tools. This can be achieved using the rustup tool.

rustup target install riscv32imac-unknown-none-elf
rustup component add llvm-tools-preview

Now we are ready to go

Configuring the appropriate dependencies

For my project, I started by having two dependencies. The riscv runtime crate and the panic-halt crate. The following goes into my Cargo.toml file

[dependencies]
riscv-rt = "0.6.1"
panic-halt = "0.2.0"

Configuring to build

You need to configure the compiler to use the right build target, but also to use the correct linker script. The linker script shown below will depend on the specific board you have, and in this case isn't an accurate example.

MEMORY
{
    FLASH : ORIGIN = 0x00000000, LENGTH = 256K
    RAM : ORIGIN = 0x20000000, LENGTH = 64K
}

REGION_ALIAS("REGION_TEXT", FLASH);
REGION_ALIAS("REGION_RODATA", FLASH);
REGION_ALIAS("REGION_DATA", RAM);
REGION_ALIAS("REGION_BSS", RAM);
REGION_ALIAS("REGION_HEAP", RAM);
REGION_ALIAS("REGION_STACK", RAM);

Then we must create a .cargo directory, and within that create a config.toml file

[target.riscv32imac-unknown-none-elf]
rustflags = [
    "-C", "link-arg=-Tmemory.x",
    "-C", "link-arg=-Tlink.x",
]

[build]
target = "riscv32imac-unknown-none-elf"

main.rs

Next we need our main.rs file. This would have been generated by the cargo tool when we created the project.

To start we need to add the following to the top of the file

#![no_std]
#![no_main]

The first line tells Rust that we are not operating in the standard Rust environment, and won't be using the standard library. A much better write up can be found in the rust embedded book. The second line prevents the main symbol from being emitted from the crate.

Now we must define the panic behavior and import the riscv runtime entry

use panic_halt as _;
use riscv_rt::entry;

Then we mark the main() function as the entry point for the code.

#[entry]
fn main() -> ! {

Putting it all together we can get quite a simple rust application that is as follows

#![no_std]
#![no_main]

use panic_halt as _;
use riscv_rt::entry;

#[entry]
fn main() -> ! {
    loop {
        // your code goes here
    }
}

Compiling

Now that you have the code all in place, it's time to build. That can be achieved by running

cargo build

Then running a command like objdump will show you the compiled source code to convince yourself that it's actually generated RISC-V assembler

cargo objdump --bin riscv -- --source

Next we'll download the code onto a RISC-V processor and execute it