Minimal Zephyr OS on RISC-V and other tests

Intro

This is a followup article on my endeavors to run RISC-V soft core on Lattice ECP5 series FPGA.

I moved all my changes to a new github repository. This contains all modifications I’ve made to the original litexOnColorlightLab004 code.

The CPU reports that it’s running at 198MHz (It is a bit too high to be believable, but its working). I also managed to enable a few of the hardware components connected to the FPGA:

    • The 4MB SDRAM is running at 66MHz (running it at higher frequency makes the memory test fail).
    • Configured the CPU with 32KB cache.
    • Enabled the 4MB SPI flash, so now it can be used to binaries.
    • Enabled one of the Ethernet PHYs (net-boot works about half the time).

I also managed to flash the FPGA configuration to the flash, so you don’t have to load it every time.

In my previous article I had some trouble with my hacked JTAG probe. It was not working very well – about 2/3 of the time it would not upload the FPGA configuration correctly etc. The issue turned up to be quite simple – I was using bad cables to connect the blue pill board to the JTAG connector.

First thing’s first, assuming you have all litex and yosys software installed from the previous article. Simply clone the following github repository: https://github.com/ghent360/riscvOnColorlight-5A-75B.git

Go to the riscvOnColorlight-5A-75B folder and run:

./base.py --build

Build the test firmware:

cd firmware
make
cd ..

And load it with the lxterm tool (run this in a separate terminal):

~/.local/bin/lxterm /dev/ttyUSB0 --kernel firmware/firmware.bin

Reload the FPGA configuration and reset the board with:

./base.py --load

I switched the default cable type to ‘dyrtyJtag’ so you don’t have to type it anymore.

You wold see a new boot scree with the following text in the lxterm window:

[LXTERM] Starting....
        __   _ __      _  __
       / /  (_) /____ | |/_/
      / /__/ / __/ -_)>  <
     /____/_/\__/\__/_/|_|
   Build your hardware, easily!

 (c) Copyright 2012-2020 Enjoy-Digital
 (c) Copyright 2007-2015 M-Labs

 BIOS built on Jul 26 2020 02:12:52
 BIOS CRC passed (6b395de0)

 Migen git sha1: 7bc4eb1
 LiteX git sha1: 1fdffdfd

--=============== SoC ==================--
CPU:       VexRiscv @ 198MHz
BUS:       WISHBONE 32-bit @ 4GiB
CSR:       8-bit data
ROM:       32KiB
SRAM:      8KiB
L2:        32KiB
MAIN-RAM:  4096KiB

--========== Initialization ============--
Ethernet init...
Initializing DRAM @0x40000000...
SDRAM now under software control
SDRAM now under hardware control
Memtest at 0x40000000...
[########################################]
[########################################]
Memtest OK
Memspeed at 0x40000000...
Writes: 461 Mbps
Reads:  382 Mbps

--============== Boot ==================--
Booting from serial...
Press Q or ESC to abort boot completely.
sL5DdSMmkekro
[LXTERM] Received firmware download request from the device.
[LXTERM] Uploading firmware/firmware.bin to 0x40000000 (14852 bytes)...
[LXTERM] Upload complete (3.2KB/s).
[LXTERM] Booting the device.
[LXTERM] Done.
Executing booted program at 0x40000000

--============= Liftoff! ===============--

Hello RISC-V core on Lattice EPC5 FPGA
Testing software built Jul 26 2020 02:15:37

Available commands:
help                            - this command
reboot                          - reboot CPU
led                             - led test
dhry                            - Dhrystone benchmark
RUNTIME>

I added the ability to run the Dhrystone benchmark to the test firmware. You can try it by issuing the ‘dhry’ command. On my board it looks like this:

RUNTIME>dhry

Dhrystone Benchmark, Version 2.1 (Language: C)

Program compiled without 'register' attribute

Execution starts, 200000 runs through Dhrystone
Execution ends

Final values of the variables used in the benchmark:

Int_Glob:            5
        should be:   5
Bool_Glob:           1
        should be:   1
Ch_1_Glob:           A
        should be:   A
Ch_2_Glob:           B
        should be:   B
Arr_1_Glob[8]:       7
        should be:   7
Arr_2_Glob[8][7]:    200010
        should be:   Number_Of_Runs + 10
Ptr_Glob->
  Ptr_Comp:          1073756824
        should be:   (implementation-dependent)
  Discr:             0
        should be:   0
  Enum_Comp:         2
        should be:   2
  Int_Comp:          17
        should be:   17
  Str_Comp:          DHRYSTONE PROGRAM, SOME STRING
        should be:   DHRYSTONE PROGRAM, SOME STRING
Next_Ptr_Glob->
  Ptr_Comp:          1073756824
        should be:   (implementation-dependent), same as above
  Discr:             0
        should be:   0
  Enum_Comp:         1
        should be:   1
  Int_Comp:          18
        should be:   18
  Str_Comp:          DHRYSTONE PROGRAM, SOME STRING
        should be:   DHRYSTONE PROGRAM, SOME STRING
Int_1_Loc:           5
        should be:   5
Int_2_Loc:           13
        should be:   13
Int_3_Loc:           7
        should be:   7
Enum_Loc:            1
        should be:   1
Str_1_Loc:           DHRYSTONE PROGRAM, 1'ST STRING
        should be:   DHRYSTONE PROGRAM, 1'ST STRING
Str_2_Loc:           DHRYSTONE PROGRAM, 2'ND STRING
        should be:   DHRYSTONE PROGRAM, 2'ND STRING

Microseconds for one run through Dhrystone: 4.11 
Dhrystones per Second:                      243186.4 
DMIPS                                       138.40 

RUNTIME>

An alternative command to load the FPGA configuration a bit faster is

openFPGALoader -c dirtyJtag -r build/colorlight_5a_75b/gateware/colorlight_5a_75b.bit

If you are happy with the configuration you can save it to the flash memory and it will be loaded automatically when you power the board on:

openFPGALoader -c dirtyJtag -f build/colorlight_5a_75b/gateware/colorlight_5a_75b.bit

How to compile and run Zephyr OS

If you have never heard of Zephyr OS you can read more about it on the project page. In short it is real-time OS similar to mbed and FreeRTOS. Why not Linux? Well the board has limited memory (4MB) and running Linux would be a challenge – not impossible, but not practical.

Most of the instructions are taken from this article. I did some modifications that are needed for the Colorlight board.

Install the Zephyr prerequisites – instructions from this article. Follow the steps up to #5. Skip step #4.3, we already did the udev rules in my previous tutorial. We would build the sample code a bit differently. Why, you ask? Well, the Zephyr OS already supports a variation of the litex generated RISC-V SoC, that however is different from the hardware configuration we have, so we’ll have to modify some things to get it to work.

Modify the board DTS code

In my repository folder (riscvOnColorlight-5A-75B) there should be a sub-folder called zephyr. Copy these modified files to the following locations:

$COLORLIGHT/zephyr/riscv32-litex-vexriscv.dtsi to zephyrproject/dts/riscv/riscv32-litex-vexriscv.dtsi
$COLORLIGHT/zephyr/litex_vexriscv.dts to zephyrproject/boards/riscv/litex_vexriscv/litex_vexriscv.dts

Why these changes. We are editing the hardware configuration for the OS to match the hardware we have generated with the litex software. In the future I hope I can automate this step.

Note if you are wondering what are all the magic numbers in the dts file. They are addresses in memory where the control registers for the peripherals are. You can find a full list of these registers in a file called ‘csr.json’ in the $COLORLIGHT/build/colorlight_5a_75b folder.

Modify the board configuration

Go to the hello_world sample project  in zephyr and create a build folder:

cd zephyrproject/samples/hello_world
mkdir build
cd build
cmake -DBOARD=litex_vexriscv ..

Now before you continue we have to change the OS default configuration – this removes support for networking and some device drivers that we don’t have.

Copy the simplified .config file:

$COLORLIGHT/zephyr/.config to zephyrproject/samples/hello_world/build/zephyr/

Build the Zephyr OS code

Now we are ready to build the hello_world example:

cd zephyrproject/samples/hello_world/build
make

If the build completes with no errors there should be a binary file zephyr.bin in the build/zephyr folder.

Let’s try to run the compiled binary on the FPGA board. Start the lxterm command in a new terminal window:

cd zephyrproject/samples/hellow_world/build
~/.local/bin/lxterm /dev/ttyUSB0 --kernel zephyr/zephyr.bin --speed 38400

Now reset your FPGA board – either load the FPGA configuration again or if you have stored the configuration in flash, power down and then back up.

You would see the following text in the ‘lxterm’ boot screen:

...
--============== Boot ==================--
Booting from serial...
Press Q or ESC to abort boot completely.
sL5DdSMmkekro
[LXTERM] Received firmware download request from the device.
[LXTERM] Uploading zephyr/zephyr.bin to 0x40000000 (41184 bytes)...
[LXTERM] Upload complete (3.2KB/s).
[LXTERM] Booting the device.
[LXTERM] Done.
Executing booted program at 0x40000000

--============= Liftoff! ===============--
*** Booting Zephyr OS build zephyr-v2.3.0-1314-g52f993de1172  ***
Hello World! litex_vexriscv


uart:~$ 

Congratulations you have booted the zephyr OS, executed a sample app and now you have access to the Zephyr simple shell module.

You can press <tab><tab> to get a list of the shell commands. It is not much but as far as examples go it is enough.

Enjoy