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
-
svd2rustandrustfmtinstalled 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:
-
svd2rustgenerates 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.elfbinary is 67K, compared toearlgrey_uart.elfweighing in at 1.1M, whenhello_worldhas 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.
-
svd2rustdoesn't propagate any comments/license through to the generated source. Is it worth adding this?