Nim on a Real-Time Operating System: Apache NuttX RTOS + Ox64 BL808 SBC

📝 1 Jan 2024

Apache NuttX RTOS on Ox64 BL808 RISC-V SBC: Works great with Nim!

Happy New Year! 2024 is here and we’re running Apache NuttX RTOS (Real-Time Operating System) on Single-Board Computers with plenty of RAM

Like Pine64 Ox64 BL808 RISC-V SBC with 64 MB RAM! (Pic below)

How will we use the Plentiful RAM?

In this article, we create a Blinky LED app with a Modern, Garbage-Collected Language: Nim Programming Language.

Garbage-Collected Languages (like Nim) require a bit more RAM than Low-Level Languages (like C). Perfect for our roomy (and vroomy) SBC!

(Watch the Demo on YouTube)

But we need a RISC-V SBC?

No worries! We’ll run Nim + NuttX on the QEMU Emulator for 64-bit RISC-V. Which works great on Linux, macOS and Windows machines.

Everything that happens on Ox64 SBC, we’ll see the exact same thing in QEMU!

(Except the blinkenlight)

Hmmm Garbage Collection… Won’t it jitter: run-pause-run-pause?

The fine folks at Wilderness Labs are running .NET on NuttX with Garbage Collection. Optimising for performance really helps!

(See TinyGo and MicroPython)

How is Nim different from Rust and Zig?

We’ve tested Rust on NuttX and Zig on NuttX. Nim is different because it…

First we say hello to Nim…

Pine64 Ox64 64-bit RISC-V SBC (Sorry for my substandard soldering)

§1 Basic Nim from scratch

(3 languages in a title heh heh)

This is the simplest Nim Program that will run on NuttX: hello_nim_async.nim

## Main Function in Nim.
## Will be called by NuttX, so we export to C.
proc hello_nim() {.exportc, cdecl.} =

  ## Print something
  echo "Hello Nim!"

  ## Force the Garbage Collection
  GC_runOrc()

Which looks a lot like Python!

What’s GC_runOrc?

Our Nim Program will be called by C. (Remember NuttX?)

And Nim works with Garbage Collection. Thus we call GC_runOrc to…

What if we forget to call GC_runOrc?

Erm don’t! To make it unforgettable, we defer the Garbage Collection: hello_nim_async.nim

## Main Function in Nim
proc hello_nim() {.exportc, cdecl.} =

  ## On Return: Force the Garbage Collection
  defer: GC_runOrc()

  ## Print something
  echo "Hello Nim!"

defer ensures that the Garbage Collection will always happen, as soon as we return from the Main Function.

Now we do something cool and enlightening…

(hello_nim is called by our C Program hello_nim_main.c)

(Should we call GC_runOrc?)

Blink an LED with Nim

§2 Blink an LED

This is how we blink an LED with Nim on NuttX: hello_nim_async.nim

## Blink the LED
proc blink_led() =

  ## Open the LED Driver
  echo "Opening /dev/userleds"
  let fd = c_open("/dev/userleds", O_WRONLY)

  ## Check the File Descriptor for error
  if fd < 0:
    echo "Failed to open /dev/userleds"
    return

First we call the NuttX Function open to access the LED Driver.

We might forget to close the LED Driver (in case of error), so we defer the closing…

  ## On Return: Close the LED Driver
  defer: c_close(fd)

Next we call the NuttX Function ioctl to flip LED 0 to On

  ## Turn on LED
  echo "Set LED 0 to 1"
  var ret = c_ioctl(fd, ULEDIOC_SETALL, 1)
  if ret < 0:
    echo "ioctl(ULEDIOC_SETALL) failed"
    return

ULEDIOC_SETALL accepts a Bit Mask of LED States. The value 1 says that LED 0 (Bit 0) will be flipped On.

(Other LEDs will be flipped Off)

We pause a while

  ## Wait a second (literally)
  ## Because 1 million microseconds = 1 second
  echo "Waiting..."
  c_usleep(1000_000)

Finally we flip LED 0 to Off

  ## Turn off LED
  echo "Set LED 0 to 0"
  ret = c_ioctl(fd, ULEDIOC_SETALL, 0)
  if ret < 0:
    echo "ioctl(ULEDIOC_SETALL) failed"
    return

  ## Wait again
  echo "Waiting..."
  c_usleep(1000_000)

In our Main Function: We call the above function 20 times to blink our LED (pic below)…

## Main Function in Nim
proc hello_nim() {.exportc, cdecl.} =

  ## On Return: Force the Garbage Collection
  defer: GC_runOrc()

  ## Blink the LED 20 times
  for loop in 0..19:
    blink_led()

(Looks mighty similar to the C Version)

And we’re almost done! Nim needs to discover our NuttX Functions…

Apache NuttX RTOS on Ox64 BL808 RISC-V SBC: Nim blinks our LED

§3 Import NuttX Functions

How will Nim know about open, close, ioctl, usleep?

We import the NuttX Functions from C into Nim: hello_nim_async.nim

## Import NuttX Functions from C.
## Based on https://github.com/nim-lang/Nim/blob/devel/lib/std/syncio.nim

proc c_open(filename: cstring, mode: cint): cint {.
  importc: "open",
  header: "<fcntl.h>".}

proc c_close(fd: cint): cint {.
  importc: "close",
  header: "<fcntl.h>",
  discardable.}

proc c_ioctl(fd: cint, request: cint): cint {.
  importc: "ioctl",
  header: "<sys/ioctl.h>",
  varargs.}

proc c_usleep(usec: cuint): cint {.
  importc: "usleep",
  header: "<unistd.h>",
  discardable.}

(discardable tells Nim Compiler that the Return Value is Optional)

We do the same for NuttX Macros

## Import NuttX Macros from C.
## Based on https://github.com/nim-lang/Nim/blob/devel/lib/std/syncio.nim

var O_WRONLY {.
  importc: "O_WRONLY", 
  header: "<fcntl.h>".}: cint

var ULEDIOC_SETALL {.
  importc: "ULEDIOC_SETALL", 
  header: "<nuttx/leds/userled.h>".}: cint

We’re ready to run this!

Nim with Apache NuttX RTOS on QEMU RISC-V (64-bit)

§4 Run Nim on QEMU

How to run Nim Blinky on QEMU Emulator?

We begin by booting NuttX RTOS on RISC-V QEMU Emulator (64-bit)…

  1. Install QEMU Emulator for RISC-V (64-bit)

    ## For macOS:
    brew install qemu
    
    ## For Debian and Ubuntu:
    sudo apt install qemu-system-riscv64
    
  2. Download nuttx from the NuttX Release

    nuttx: NuttX Image for 64-bit RISC-V QEMU

    If we prefer to build NuttX ourselves: Follow these steps

  3. Start the QEMU RISC-V Emulator (64-bit) with NuttX RTOS…

    qemu-system-riscv64 \
      -semihosting \
      -M virt,aclint=on \
      -cpu rv64 \
      -smp 8 \
      -bios none \
      -kernel nuttx \
      -nographic
    

    (About the QEMU Options)

  4. NuttX is now running in the QEMU Emulator! (Pic above)

    NuttShell (NSH) NuttX-12.0.3
    nsh>
    

    (See the Complete Log)

  5. At the NuttX Prompt, enter “hello_nim”…

    nsh> hello_nim
    Hello Nim!
    Opening /dev/userleds
    

    (Enter “help” to see the available commands)

  6. Nim on NuttX blinks our Simulated LED

    Set LED 0 to 1
    board_userled_all: led=0, val=1
    Waiting...
    
    Set LED 0 to 0
    board_userled_all: led=0, val=0
    Waiting...
    
    Set LED 0 to 1
    board_userled_all: led=0, val=1
    Waiting...
    

    (See the Complete Log)

  7. To Exit QEMU: Press Ctrl-A then x

Now we step out from the Virtual World into the Real World (like “The Matrix”)…

Connect an LED to Ox64 SBC at GPIO 29, Pin 21

§5 Nim Blinky on Ox64

Will Nim Blinky run on a real RISC-V SBC?

Yep! Connect an LED to Ox64 SBC at GPIO 29, Pin 21 (pic above)…

ConnectToWire
Ox64 Pin 21
(GPIO 29)
Resistor
(47 Ohm)
Red
Resistor
(47 Ohm)
LED +
(Curved Edge)
Breadboard
LED -
(Flat Edge)
Ox64 Pin 23
(GND)
Black

(See the Ox64 Pinout)

(Resistor is 47 Ohm, yellow-purple-black-gold, almost Karma Chameleon)

Follow these steps to boot NuttX RTOS on our Ox64 BL808 SBC…

  1. Flash OpenSBI and U-Boot Bootloader to Ox64

  2. Prepare a Linux microSD for Ox64 as described in the previous article

  3. Download Image from the NuttX Release

    Image: NuttX Image for Ox64 BL808 SBC

    If we prefer to build NuttX ourselves: Follow these steps

  4. Copy the Image file and overwrite the Image in the Linux microSD

  5. Insert the microSD into Ox64 and power up Ox64

  6. NuttX is now running on our Ox64 SBC! (Pic below)

    Starting kernel...
    NuttShell (NSH) NuttX-12.0.3
    nsh>
    

    (See the Complete Log)

  7. At the NuttX Prompt, enter “hello_nim”…

    nsh> hello_nim
    Hello Nim!
    Opening /dev/userleds
    

    (Enter “help” to see the available commands)

  8. Nim on NuttX blinks our LED

    Set LED 0 to 1
    board_userled_all: led=0, val=1
    Waiting...
    
    Set LED 0 to 0
    board_userled_all: led=0, val=0
    Waiting...
    
    Set LED 0 to 1
    board_userled_all: led=0, val=1
    Waiting...
    

    (Watch the Demo on YouTube)

    (See the Complete Log)

Nim blinks a real LED on a real RISC-V SBC! Let’s figure out how it works…

Apache NuttX RTOS on Ox64 BL808 RISC-V SBC: Works great with Nim!

§6 Inside Nim on NuttX

Nim runs incredibly well on NuttX. How is that possible?

That’s because Nim compiles to C. As far as NuttX is concerned…

Nim looks like any other C Program!

Whoa! How is Nim compiled to C?

Our NuttX Makefile calls the Nim Compiler…

## Compile Nim to C
export TOPDIR=$PWD/nuttx
cd apps/examples/hello_nim
nim c --header hello_nim_async.nim 

Nim Compiler compiles our Nim Program

## Nim Program that prints something
proc hello_nim() {.exportc, cdecl.} =
  echo "Hello Nim!"

Into this C Program

// Main Function compiled from Nim to C:
// echo "Hello Nim!"
N_LIB_PRIVATE N_CDECL(void, hello_nim)(void) {
  ...
  // `echo` comes from the Nim System Library
  // https://github.com/nim-lang/Nim/blob/devel/lib/system.nim#L2849-L2902
  echoBinSafe(TM__1vqzGCGyH8jPEpAwiaNwvg_2, 1);
  ...
}

// String "Hello Nim!" compiled from Nim to C
static NIM_CONST tyArray__nHXaesL0DJZHyVS07ARPRA TM__1vqzGCGyH8jPEpAwiaNwvg_2 
  = {{10, (NimStrPayload*)&TM__1vqzGCGyH8jPEpAwiaNwvg_3}};

// Actual String for "Hello Nim!"
static const struct { NI cap; NIM_CHAR data[10+1]; } TM__1vqzGCGyH8jPEpAwiaNwvg_3 
  = { 10 | NIM_STRLIT_FLAG, "Hello Nim!" };

(From .nimcache/@mhello_nim_async.nim.c)

(See the nimcache)

Hence Nim Compiler has produced a perfectly valid C Program. That will compile with any C Compiler!

How will NuttX compile this?

Nim Compiler generates the code above into the .nimcache folder.

Our NuttX Makefile compiles everything inside .nimcache with the GCC Compiler…

## Compile everything in the .nimcache folder
NIMPATH = $(shell choosenim show path)
CFLAGS += -I $(NIMPATH)/lib -I ../../.nimcache
CSRCS  += $(wildcard ../../.nimcache/*.c)

And links the Nim Modules (compiled by GCC) into NuttX.

So Nim Compiler is aware of NuttX?

Yep! Nim Compiler is internally wired to produce NuttX Code (that GCC will compile correctly)…

Kudos to centurysys and the Nim Community for making this possible!

Everything is hunky dory with Nim on NuttX?

We made some Minor Fixes, we’ll upstream to NuttX Mainline shortly…

Here we see the Nim Compiler working perfectly, compiling our program for NuttX (by parsing the NuttX Build Config)…

$ export TOPDIR=/workspaces/bookworm/nuttx
$ cd /workspaces/bookworm/apps/examples/hello_nim
$ nim c --header hello_nim_async.nim

read_config: /workspaces/bookworm/nuttx/.config
line=CONFIG_DEBUG_SYMBOLS=y
line=CONFIG_DEBUG_FULLOPT=y
line=CONFIG_ARCH="risc-v"
@["keyval=", "ARCH", "\"risc-v\""]
keyval[1]="risc-v"
line=CONFIG_RAM_SIZE=33554432
* arch:    riscv64
* opt:     oSize
* debug:   true
* ramSize: 33554432
* isSim:   false
Hint: used config file '/home/vscode/.choosenim/toolchains/nim-#devel/config/nim.cfg' [Conf]
Hint: used config file '/home/vscode/.choosenim/toolchains/nim-#devel/config/config.nims' [Conf]
Hint: used config file '/workspaces/bookworm/apps/config.nims' [Conf]
....................................................................................................................................
Hint: mm: orc; opt: size; options: -d:danger
92931 lines; 1.214s; 137.633MiB peakmem; proj: /workspaces/bookworm/apps/examples/hello_nim/hello_nim_async.nim; out: /workspaces/bookworm/apps/.nimcache/hello_nim_async.json [SuccessX]

Isn’t Nim supposed to be Memory Safe?

Yeah so far we’re doing Low-Level Coding with NuttX. And the Nim Memory Safety doesn’t shine through.

Later when we write LVGL Graphical Apps in Nim, we’ll appreciate the safety and simplicity of Nim…

GPIO 29 in BL808 Reference Manual (Page 119)

GPIO 29 in BL808 Reference Manual (Page 119)

§7 LED Driver for Ox64

Nim Blinky needs an LED Driver for Ox64…

What’s the Quickest Way to create a NuttX LED Driver?

U-Boot Bootloader can help! Power up Ox64 and press Enter a few times to reveal the U-Boot Command Prompt.

We enter these U-Boot Commands

## Dump the GPIO 29 Register at 0x20000938 (gpio_cfg29)
$ md 0x20000938 1
20000938: 00400803                             ..@.

## Set GPIO 29 Output to 1:
## (1 << 6) | (11 << 8) | (0 << 30) | (0 << 4) | (1 << 24)
## = 0x1000b40
$ mw 0x20000938 0x1000b40 1

## Dump the GPIO 29 Register to verify
$ md 020000938 1
20000938: 01000b40                             @...

## Set GPIO 29 Output to 0:
## (1 << 6) | (11 << 8) | (0 << 30) | (0 << 4) | (0 << 24)
## = 0xb40
$ mw 0x20000938 0xb40 1

## Dump the GPIO 29 Register to verify
$ md 0x20000938 1
20000938: 00000b40                             @...

And our LED (GPIO 29) will flip On and Off!

Thus we have verified the Magic Bits for flipping our LED…

How did we figure out the Magic Bits for GPIO 29?

From BL808 Reference Manual (Page 56), “Normal GPIO Output Mode”…

(GPIO Bits are listed in the pic above)

Which means…

And we write the above values to GPIO 29 Register at 0x2000 0938 (gpio_cfg29)

How to flip the GPIO in our LED Driver?

We do this in our NuttX LED Driver: bl808_userleds.c

// Flip the LEDs On and Off according to the LED Set
// (Bit 0 = LED 0)
void board_userled_all(uint32_t ledset) {

  // For LED 0 to 2...
  for (int i = 0; i < BOARD_LEDS; i++) {

    // Get the desired state of the LED
    const bool val = ((ledset & g_led_setmap[i]) != 0);

    // If this is LED 0...
    if (i == 0) {

      // Flip it On or Off?
      if (val) {

        // Flip LED 0 (GPIO 29) to On:
        // Set gpio_cfg29 to (1 << 6) | (11 << 8) | (0 << 30) | (0 << 4) | (1 << 24)
        // mw 0x20000938 0x1000b40 1
        *(volatile uint32_t *) 0x20000938 = 0x1000b40;
      } else {

        // Flip LED 0 (GPIO 29) to Off:
        // Set gpio_cfg29 to (1 << 6) | (11 << 8) | (0 << 30) | (0 << 4) | (0 << 24)
        // mw 0x20000938 0xb40 1
        *(volatile uint32_t *) 0x20000938 = 0xb40;
      }
    }
  }
}

That’s how we created a barebones LED Driver for Ox64!

(Remember to add the Auto LED Driver)

(And update the Board Kconfig)

(And start our LED Driver)

Ahem it looks a little messy…

No Worries! Later we’ll replace the (awful) code above by the BL808 GPIO Driver. Which we’ll copy from NuttX for BL602

// Get the desired state of LED[i]
const bool val = ((ledset & g_led_setmap[i]) != 0);

// Call the BL808 GPIO Driver to flip the LED On or Off
bl808_gpio_write(  // Write to the GPIO Output...
  g_led_map[i],    // GPIO Number for LED[i]
  val              // Flip it On or Off
);

(See the Upcoming GPIO Driver)

Anything else we patched?

We fixed the NuttX Timer for Ox64 (otherwise we can’t blink)…

Apache NuttX RTOS on Ox64 BL808 RISC-V SBC: Nim blinks our LED

§8 What’s Next

Today we ran some Fun Experiments with Nim on NuttX

We’ll do more with Nim on NuttX. (Maybe LVGL?) Stay Tuned!

Many Thanks to my GitHub Sponsors (and the awesome NuttX Community) for supporting my work! This article wouldn’t have been possible without your support.

Got a question, comment or suggestion? Create an Issue or submit a Pull Request here…

lupyuen.github.io/src/nim.md

Nim with Apache NuttX RTOS on QEMU RISC-V (64-bit)

§9 Appendix: Build NuttX for QEMU

In this article, we compiled a Work-In-Progress Version of Apache NuttX RTOS for QEMU RISC-V (64-bit) that has Minor Fixes for Nim…

First we install Nim Compiler (only the Latest Dev Version supports NuttX)…

## Install Nim Compiler: https://nim-lang.org/install_unix.html
curl https://nim-lang.org/choosenim/init.sh -sSf | sh

## Add Nim to PATH
export PATH=$HOME/.nimble/bin:$PATH

## Select Latest Dev Version of Nim. Will take a while!
choosenim devel --latest

## Version should be 2.1.1 or later:
## Nim Compiler Version 2.1.1 [Linux: amd64]
## Compiled at 2023-12-22
nim -v

(Nim won’t install? Try a Linux Container)

Then we download and build NuttX for QEMU RISC-V (64-bit)

## Download the WIP NuttX Source Code
git clone \
  --branch nim \
  https://github.com/lupyuen2/wip-nuttx \
  nuttx
git clone \
  --branch nim \
  https://github.com/lupyuen2/wip-nuttx-apps \
  apps

## Configure NuttX for QEMU RISC-V (64-bit)
cd nuttx
tools/configure.sh rv-virt:nsh64

## Build NuttX
make

## Dump the disassembly to nuttx.S
riscv64-unknown-elf-objdump \
  --syms --source --reloc --demangle --line-numbers --wide \
  --debugging \
  nuttx \
  >nuttx.S \
  2>&1

(Remember to install the Build Prerequisites and Toolchain)

(See the Build Script)

(See the Build Log)

(See the Build Outputs)

This produces the NuttX ELF Image nuttx that we may boot on QEMU RISC-V Emulator…

## Start the QEMU RISC-V Emulator (64-bit) with NuttX RTOS
qemu-system-riscv64 \
  -semihosting \
  -M virt,aclint=on \
  -cpu rv64 \
  -smp 8 \
  -bios none \
  -kernel nuttx \
  -nographic

At the NuttX Prompt, enter “hello_nim”…

nsh> hello_nim
Hello Nim!
Opening /dev/userleds

(Enter “help” to see the available commands)

Nim on NuttX blinks our Simulated LED

Set LED 0 to 1
board_userled_all: led=0, val=1
Waiting...

Set LED 0 to 0
board_userled_all: led=0, val=0
Waiting...

Set LED 0 to 1
board_userled_all: led=0, val=1
Waiting...

(See the NuttX Log)

To Exit QEMU: Press Ctrl-A then x

How to run our own Nim Code on NuttX?

Locate this Nim Source File and replace by our own Nim Code…

apps/examples/hello_nim/hello_nim_async.nim

Then rebuild and restart NuttX.

Apache NuttX RTOS on Ox64 BL808 RISC-V SBC: Works great with Nim!

§10 Appendix: Build NuttX for Ox64

In this article, we compiled a Work-In-Progress Version of Apache NuttX RTOS for Ox64 that has Minor Fixes for Nim…

First we install Nim Compiler (only the Latest Dev Version supports NuttX)…

## Install Nim Compiler: https://nim-lang.org/install_unix.html
curl https://nim-lang.org/choosenim/init.sh -sSf | sh

## Add Nim to PATH
export PATH=$HOME/.nimble/bin:$PATH

## Select Latest Dev Version of Nim. Will take a while!
choosenim devel --latest

## Version should be 2.1.1 or later:
## Nim Compiler Version 2.1.1 [Linux: amd64]
## Compiled at 2023-12-22
nim -v

(Nim won’t install? Try a Linux Container)

Then we download and build NuttX for Ox64 BL808 SBC

## Download the WIP NuttX Source Code
git clone \
  --branch nim \
  https://github.com/lupyuen2/wip-nuttx \
  nuttx
git clone \
  --branch nim \
  https://github.com/lupyuen2/wip-nuttx-apps \
  apps

## Configure NuttX for Ox64 BL808 RISC-V SBC
cd nuttx
tools/configure.sh ox64:nsh

## Build NuttX
make

## Export the NuttX Kernel
## to `nuttx.bin`
riscv64-unknown-elf-objcopy \
  -O binary \
  nuttx \
  nuttx.bin

## Dump the disassembly to nuttx.S
riscv64-unknown-elf-objdump \
  --syms --source --reloc --demangle --line-numbers --wide \
  --debugging \
  nuttx \
  >nuttx.S \
  2>&1

## Dump the hello_nim disassembly to hello_nim.S
riscv64-unknown-elf-objdump \
  --syms --source --reloc --demangle --line-numbers --wide \
  --debugging \
  ../apps/bin/hello_nim \
  >hello_nim.S \
  2>&1

(Remember to install the Build Prerequisites and Toolchain)

We build the Initial RAM Disk that contains NuttX Shell and NuttX Apps…

## Build the Apps Filesystem
make -j 8 export
pushd ../apps
./tools/mkimport.sh -z -x ../nuttx/nuttx-export-*.tar.gz
make -j 8 import
popd

## Generate the Initial RAM Disk `initrd`
## in ROMFS Filesystem Format
## from the Apps Filesystem `../apps/bin`
## and label it `NuttXBootVol`
genromfs \
  -f initrd \
  -d ../apps/bin \
  -V "NuttXBootVol"

## Prepare a Padding with 64 KB of zeroes
head -c 65536 /dev/zero >/tmp/nuttx.pad

## Append Padding and Initial RAM Disk to NuttX Kernel
cat nuttx.bin /tmp/nuttx.pad initrd \
  >Image

(See the Build Script)

(See the Build Log)

(See the Build Outputs)

This produces the NuttX Image for Ox64: Image

Next we prepare a Linux microSD for Ox64 as described in the previous article.

(Remember to flash OpenSBI and U-Boot Bootloader)

And we do the Linux-To-NuttX Switcheroo: Copy the Image file (from above) and overwrite the Image in the Linux microSD…

## Overwrite the Linux Image
## on Ox64 microSD
cp Image \
  "/Volumes/NO NAME/Image"
diskutil unmountDisk /dev/disk2

Insert the microSD into Ox64 and power up Ox64.

Ox64 boots OpenSBI, which starts U-Boot Bootloader, which starts NuttX Kernel and the NuttX Shell (NSH).

At the NuttX Prompt, enter “hello_nim”…

nsh> hello_nim
Hello Nim!
Opening /dev/userleds

Set LED 0 to 1
board_userled_all: led=0, val=1
Waiting...

Set LED 0 to 0
board_userled_all: led=0, val=0
Waiting...

(Enter “help” to see the available commands)

Nim on NuttX blinks our LED.

(Watch the Demo on YouTube)

(See the NuttX Log)

How to run our own Nim Code on NuttX?

Locate this Nim Source File and replace by our own Nim Code…

apps/examples/hello_nim/hello_nim_async.nim

Then rebuild NuttX, copy to microSD and restart NuttX.

OpenSBI Supervisor Binary Interface

§11 Appendix: OpenSBI Timer for NuttX

The sleep command hangs in NuttX Shell. How to fix it?

That’s because we haven’t implemented the RISC-V Timer for Ox64! We should call OpenSBI Supervisor Binary Interface to handle the Timer…

We only need to change the Timer Initialisationbl808_timerisr.c

// Timer Frequency
#define MTIMER_FREQ 1000000

// This function is called during start-up to initialize the timer interrupt.
void up_timer_initialize(void) {
  struct oneshot_lowerhalf_s *lower = riscv_mtimer_initialize(
    0, 0, RISCV_IRQ_STIMER, MTIMER_FREQ);
  DEBUGASSERT(lower);
  up_alarm_set_lowerhalf(lower);
}

How it works: At startup, up_timer_initialize (above) calls…

Originally we set MTIMER_FREQ to 10000000bl808_timerisr.c

#define MTIMER_FREQ 10000000

But this causes the command sleep 1 to pause for 10 seconds. So we divide the frequency by 10: bl808_timerisr.c

#define MTIMER_FREQ 1000000

Now the sleep command works correctly in NuttX Shell! Here’s the log (ignore the errors)…

We’re now upstreaming the Ox64 Timer to NuttX Mainline.

(See the Pull Request)