This repository is a proof-of-concept to run Rust code on the OpenTitan "earlgrey" chip and its RISC-V Ibex core using register definitions generated by svd2rust
.
It is neither pretty code nor finished. YMMV.
To demonstrate running code this implements a trivial "Hello, Rust" over the earlgrey UART.
Most makefiles and Rust code are Copyright 2020 Nathan Holstein and released under the Apache 2.0 license.
The Rust nostd
code was written while closely following the RISC-V OS using Rust blog, licensed under the MIT license.
Linker scripts and assembly routine were directly lifted from the OpenTitan project, and Copyright 2020 lowRISC contributors.
The earlgrey-registers
crate consists of code generated by svd2rust
from HJSON/SVD files also from OpenTitan.
In addition to the developer setup described in the OpenTitan docs, you'll need at least the following tools:
-
make, xz
-
rust & cargo; I've tested only on 1.42-nightly
-
svd2rust
andrustfmt
installed via cargo -
either:
-
a development toolchain for your host machine, or
-
a prebuilt riscv32-unknown-elf binutils+GCC toolchain
-
This uses the SVD file generated by the build process from the generate-svd branch.
It should be possible to checkout that branch, and with Python >= 3.5 run pip3 install -r python-requirements.txt && make -C hw top_earlgrey/data/top_earlgrey.svd
.
With the SVD generated, it should be possible to use svd2rust
to generate a crate with all the register definitions.
If you checkout this repository and opentitan
in the same directory the make build should automatically pick up the latest version.
For ease of use, the latest code is checked into this repository in the earlgrey-registers
crate.
With this, rust, cargo, and the toolchain in place you should be able to simply run make
.
With any luck this produces earlgrey_uart.elf
in the top of the repo.
Again, I recommend following OpenTitan's getting started documentation.
If successful, after running their hello_world
you should be left with (among other things) a uart.log
:
root@2c90fb446a5f:/opentitan# cat uart0.log
Version: opentitan-snapshot-20191101-1-415-g1882bad
Build Date: 2020-01-06, 23:08:25
INFO: Boot ROM initialisation has completed, jump into flash!
Hello World! Jan 6 2020 23:16:19
Watch the LEDs!
Try out the switches on the board
or type anything into the console window.
The LEDs show the ASCII code of the last character.
You're now ready to run the Rust P.O.C. This should entail nothing more than swapping out hello_world.elf
for earlgrey_uart.elf
from the previous command:
root@2c90fb446a5f:/opentitan# build/lowrisc_systems_top_earlgrey_verilator_0.1/sim-verilator/Vtop_earlgrey_verilator \
--meminit=rom,build-bin/sw/device/sim-verilator/boot_rom/boot_rom.elf \
--meminit=flash,../opentitan.rs/earlgrey_uart.elf
And again, check uart.log
for results:
root@2c90fb446a5f:/opentitan# cat uart0.log
Version: opentitan-snapshot-20191101-1-415-g1882bad
Build Date: 2020-01-06, 23:08:25
INFO: Boot ROM initialisation has completed, jump into flash!
Hello, Rust
(A note for anyone testing on an FPGA: I've only run this code in Verilator, at the very least you'll need to change the default "verilator" feature in Cargo to get this running.)
If you don't have a risvc32 toolchain kicking around, the makefile includes a rule to download binutils-2.33.1 and compile the bare minimum riscv32-unknown-elf-ld
and riscv32-unknown-elf-as
.
Running make toolchain
shouldn't take more than a few minutes with a decent connection and development setup.
I've tested this only on OSX Catalina, but binutils should have a stable build.
If you don't run this step you'll need to provide the necessary toolchain. The OpenTitan project distributes a docker container with the toolchain preinstalled.
This was mostly meant as a proof-of-concept, and likely won't be taken much further. Hopefully, this can help inform decisions regarding OpenTitan's consideration to use Rust for future software development.
That said, there are still some missing features. Primarily, this is supposed to implement a UART echo routine, but the UART appears misconfigured for reads. So, a bit of debugging, and perhaps IRQ intergration. Aside from that, this code likely won't gain any additional features.
Outstanding questions:
-
svd2rust
generates single-bit-writrs which don't require use ofunsafe
. However, multi-byte writers are markedunsafe
. Is it possible to avoid this for registers to which any value can be written? (Compare UART baud rate multiplier to UART date write.) -
The OpenTitanThe small bits of disassembled Rust instructions I've reviewed seem space efficient and wouldn't explain the difference. Perhaps there's some necessary GC to perform during linking?hello_world.elf
binary is 67K, compared toearlgrey_uart.elf
weighing in at 1.1M, whenhello_world
has many more features.The size discrepancy was caused by lack of stripping symbols; additionally the linker wasn't garbage collecting unused sections.
-
Meson vs. Make?
I've spend some time playing around with trying to internalize as much of the build as possible into Cargo itself. This involved some experimentation into various features of Cargo which in some instances aren't clearly documented. I've written up some notes from this.
-
svd2rust
doesn't propagate any comments/license through to the generated source. Is it worth adding this?