Assembly examples for Red-V Thing Plus (SiFive FE310-G002)
Go to file
flabbergast 546b3002a4 Stashing the state. 2023-12-05 11:13:53 +00:00
01-blink Add top-of-file comments. 2021-05-23 20:34:38 +01:00
02-chase Add top-of-file comments. 2021-05-23 20:34:38 +01:00
03-clock Add top-of-file comments. 2021-05-23 20:34:38 +01:00
04-uart Stashing the state. 2023-12-05 11:13:53 +00:00
05-write Stashing the state. 2023-12-05 11:13:53 +00:00
06-irq-delay Stashing the state. 2023-12-05 11:13:53 +00:00
07-uart0irq Fix some bugs (write/irq). 2021-05-26 09:05:13 +01:00
08-i2c Stashing the state. 2023-12-05 11:13:53 +00:00
bootloader Shave 2 bytes from bootloader. 2021-05-23 19:28:10 +01:00
work Stashing the state. 2023-12-05 11:13:53 +00:00
.gitignore Add bootloader.hex and gitignore. 2021-05-18 20:35:41 +01:00
02-chase.gif Add 03-clock. 2021-05-15 21:35:41 +01:00
README.md Add debugging info into README. 2021-05-27 09:41:35 +01:00
UNLICENSE Add UNLICENSE. 2021-05-18 20:34:15 +01:00
memmap Add top-of-file comments. 2021-05-23 20:34:38 +01:00
platform_regs.inc Stashing the state. 2023-12-05 11:13:53 +00:00

README.md

A bit of assembly on RED-V Thing Plus

This repo contains a progression of example programs written in RISC-V assembly, to run on Sparkfun's RED-V Thing Plus. It's a result of my attempt to learn a bit of RISC-V assembly.

02-chase-animated-gif

Setup

You'll need a compiler capable of compiling for (bare metal) rv32imac architecture. There are precompiled toolchains available from SiFive. The Makefiles are set to use riscv32-elf-as by default, but riscv64-unknown-elf-as and variations should be also fine (you'll need to adjust PREFIX in the Makefiles).

Another piece of software that you may want to use is JLinkExe from Segger, for flashing via make flash. You'll also need to adjust the JLINKEXE path in the Makefiles. This is avoidable; you can also just copy the resulting .hex file(s) onto the virtual "drive" that the onboard JLink emulates for the PC; JLink will then automatically flash it.

Code

The examples are in the numbered subdirectories; they get progressively more complicated. Most of the code is carried over from the earlier to the later ones.

Two files in the main directory are reused (via symlinks) in all the examples: memmap (which tell the linker addresses for flash and RAM), and platform_regs.inc which have definitions for some of the MCU memory-mapped registers, from the manual.

The filenames in the examples follow the convention that main.s is the main file, which includes various other .s files. The p_*.s contain the MCU-specific code.

Miscellaneous

Debugging

I did not set up any Makefile debugging targets (yet). One can use JLinkExe directly, or JLinkGDBServer and then gdb. Or, instead of JLinkExe, openocd also works somewhat -- but I've had limited "success" with it.

The commands are:

JLinkExe directly

	JLinkExe -device FE310 -if JTAG -speed 4000 -si JTAG -jtagconf -1,-1 -autoconnect 1

After connecting to the board, one has a "command line" interface to the debugger. ? will bring up help. The commands I use most often are loadfile main.hex to flash, r for reset, g for go (as in let the mcu run), h for halt, s for step one instruction, regs to display all the registers, mem8 and mem32 to read memory. setbp HEXADDRESS to set a breakpoint, clrbp HANDLE to clear a breakpoint. The biggest downside to this is that JLinkExe does not read elf, so one has to have main.list open in a side window to make the connection with where in the program the execution currently is, or at which address one should set a breakpoint, etc.

An alternative is to run a gdb server and use riscv32-elf-gdb or something on top of that. So, run a JLink gdb server with

	JLinkGDBServer -device FE310 -if JTAG -speed 4000 -jtagconf -1,-1

and then in another window gdb with

	riscv32-elf-gdb main.elf

(or cgdb with cgdb -d riscv32-elf-gdb main.elf), and then connect it to the jlink server with tar ext :2331. After that the normal gdb things apply. Like mon[itor] help (talks to the jlink process) will show which special commands does the jlink gdb server support; mon regs with show regs, mon reset will reset the mcu. Other useful gdb commands are: load (flash the firmware), cont (let the mcu run), Ctrl+C (halt the mcu), disassemble (show disassembly of the current function), etc... Cgdb will also show the corresponding portion of the source code, one can visually set breakpoints (space), step (f8), etc... :help will bring up cgdb help which also describes the keybindings (quit help with :set nodis or :set dis ... oh well).

TUI cheatsheet

Gdb TUI seems to work fine as well: C-x C-a in gdb; does not need anything beyond gdb itself, but it needs to be built into it (e.g. the SiFive toolchain-supplied gdb does not). Better layout with tui new-layout rv {-horizontal src 2 regs 1} 2 status 0 cmd 1 and switch to it with tui layout rv. Then tui reg all to actually display the registers. C-x 2 cycles through layouts (one of them has assembly view).

C-x s switches to "single key" mode; q switches out of it. Then: continue, down, finish, next, run, step, up, where.

Breakpoints just from the command line, so: b[reak] file.s:LINENUM or b[reak] *ADDRESS sets; i[nfo] b[reakpoints] lists; d[elete] NUM deletes.

openocd

When I run it with openocd -f /usr/share/openocd/scripts/board/sifive-hifive1-revb.cfg it does connect; but seems to often not do what's asked about it regarding the mcu. I suppose this improves with time.

Commentary about the board

The RED-V Thing board carries SiFive's FE310-G002 RV32IMAC microcontroller (with supporting crystals and flash), as well as a JLink debugger (actually licensed from Segger). This is very nice - no external hardware needed for flashing, and debugging via JTAG, and a convenient access to UART0. (Most conveniently used using the non-open-source JLink software; but openocd also works.) The board is roughly compatible with SiFive's Hifive1 RevB board, to the point that Hifive1B firmware runs fine on the RED-V, except the RED-V is missing the LEDs and the (imo pointless) ESP32 wifi.

Commentary about the MCU

To do anything with the onboard FE310-G002, one will need FE310 datasheet and most importantly FE310 manual. These are refreshingly short and to the point. They sort of reinforce that FE310-G002 isn't really meant (I guess) to be used in real-world embedded applications: only the basic peripherals (exactly those that I use the most), with just the basic features. On the other hand, this makes it superb to play around with - they are really dead simple and straightforward; one doesn't have to read through hundreds of pages of descriptions of tens of registers that allow (somewhat) obscure features. Just a RISC-V core with basic UART, SPI, I2C, PWM, timer, interrupts. These are designed quite well to go with a RISC-V core: tricks like error flags in bit31 for easy sign tests, etc. There seems to be also a reasonably good attempt at a "sleep" solution (Always-On domain), but I am not that sure about the low power requirements - there are a bunch of extra parts (crystals, flash) that would need to be taken care of in sleep.

Anyway, the MCU is a good demonstration that SiFive digital designers know what they're doing.

Bootloader

On reset, FE310-G002 jumps to 0x20000000 (usually, this is to some extent configurable), which is mapped to the beginning of the onboard flash. From factory, this contains SiFive's bootloader (which they didn't release sources for). In any case, the RED-V board uses the exact same bootloader as the HiFive1B board. Its function is to

a) guard against the scenario when the MCU makes itself inaccessible to JTAG (e.g. sleep without wake sources) - this is done by blinking a LED after 1 second, if RESET is pressed again just after that, then the bootloader does not jump to user code, but just stalls. This allows the JLink to connect to the chip and so reprogram the flash.

b) initialise the ESP32 on the HiFive1B board.

It emits some text over UART (115200 baud); e.g. the result of b).

Now on RED-V: For a), the LED that's blinked on HiFive1B does not exist on RED-V, so that's not visible; but otherwise the mechanism works. For b), there is no ESP32, so it just errors out.

The user code (where the bootloader jumps if it's happy) is at 0x20010000. Most of the sample code that's around (e.g. riscv-rust-quickstart or platformio) does have this set up automatically.

Someone disassebled the original bootloader and also rewrote it into C here. It turns out that it does a few other things, so that more of the MCU peripherals are not in their reset states:

  • it seems to use PMU/sleep mechanism on power-up-reset to reset(?) the ESP32, so it clobbers the PMUSLEEPn registers and triggers sleep once. It uses another "magic value" in AON_BACKUP15 do that it isn't done on subsequent boots - but if the user application uses AON_BACKUP15 for something else, it will happen again.
  • it sets up UART, switches the clock to external crystal (!), and does a "bench clock reset", which seems to only do some computation with numbers of cycles?
  • leaves the SPI peripheral and SPI pins enabled - makes sense on HiFive1b board, but the LED on the RED-V Thing uses one of those pins...

Anyway, I wrote a simple alternative one which just does a) above. It's in the bootloader directory.