📝 1 Jan 2024
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!
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!
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!
How is Nim different from Rust and Zig?
We’ve tested Rust on NuttX and Zig on NuttX. Nim is different because it…
Compiles to C (instead of Machine Code)
Syntax is Python-like (but Statically Compiled)
Automatic Garbage Collection (no Borrow Checker)
And it’s Memory Safe (like Rust)
First we say hello to Nim…
(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…
Force the Garbage Collection to complete
Clean up all remaining Nim Objects
Then return to C and NuttX
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)
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…
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!
How to run Nim Blinky on QEMU Emulator?
We begin by booting NuttX RTOS on RISC-V QEMU Emulator (64-bit)…
Install QEMU Emulator for RISC-V (64-bit)…
## For macOS:
brew install qemu
## For Debian and Ubuntu:
sudo apt install qemu-system-riscv64
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
Start the QEMU RISC-V Emulator (64-bit) with NuttX RTOS…
qemu-system-riscv64 \
-semihosting \
-M virt,aclint=on \
-cpu rv64 \
-bios none \
-kernel nuttx \
-nographic
NuttX is now running in the QEMU Emulator! (Pic above)
NuttShell (NSH) NuttX-12.0.3
nsh>
At the NuttX Prompt, enter “hello_nim”…
nsh> hello_nim
Hello Nim!
Opening /dev/userleds
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...
To Exit QEMU: Press Ctrl-A
then x
Now we step out from the Virtual World into the Real World (like “The Matrix”)…
Will Nim Blinky run on a real RISC-V SBC?
Yep! Connect an LED to Ox64 SBC at GPIO 29, Pin 21 (pic above)…
Connect | To | Wire |
---|---|---|
Ox64 Pin 21 (GPIO 29) | Resistor (47 Ohm) | Red |
Resistor (47 Ohm) | LED + (Curved Edge) | Breadboard |
LED - (Flat Edge) | Ox64 Pin 23 (GND) | Black |
(Resistor is 47 Ohm, yellow-purple-black-gold, almost Karma Chameleon)
Follow these steps to boot NuttX RTOS on our Ox64 BL808 SBC…
Flash OpenSBI and U-Boot Bootloader to Ox64
Prepare a Linux microSD for Ox64 as described in the previous article
Download Image
from the NuttX Release…
Image: NuttX Image for Ox64 BL808 SBC
If we prefer to build NuttX ourselves: Follow these steps
Copy the Image
file and overwrite the Image
in the Linux microSD
Insert the microSD into Ox64 and power up Ox64
NuttX is now running on our Ox64 SBC! (Pic below)
Starting kernel...
NuttShell (NSH) NuttX-12.0.3
nsh>
At the NuttX Prompt, enter “hello_nim”…
nsh> hello_nim
Hello Nim!
Opening /dev/userleds
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...
Nim blinks a real LED on a real RISC-V SBC! Let’s figure out how it works…
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)
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…
Makefile: Nimcache has moved 2 folders up
config.nims: Add support for 64-bit RISC-V
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)
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…
Write to GPIO 29 Register at 0x2000
0938
(gpio_cfg29)
Register Value 0x100
0B40
will flip the LED On
Register Value 0xB40
will flip the LED Off
How did we figure out the Magic Bits for GPIO 29?
From BL808 Reference Manual (Page 56), “Normal GPIO Output Mode”…
Set reg_gpio_29_oe (Bit 6) to 1
to enable GPIO Output Mode
= (1 << 6)
Set reg_gpio_29_func_sel (Bits 8 to 12) to 11
to enter SWGPIO Mode
= (11 << 8)
Set reg_gpio_29_mode (Bits 30 to 31) to 0
to enable Normal Output Function of I/O
= (0 << 30)
Set reg_gpio_29_pu (Bit 4) and reg_gpio_29_pd (Bit 5) to 0
to disable Internal Pull-Up and Pull-Down functions
= (0 << 4)
Set the Pin Level (0
or 1
) through reg_gpio_29_o (Bit 24)
= Either (0 << 24) Or (1 << 24)
(GPIO Bits are listed in the pic above)
Which means…
Set GPIO Output to 0
= (1 << 6) | (11 << 8) | (0 << 30) | (0 << 4) | (0 << 24)
= 0xB40
Set GPIO Output to 1
= (1 << 6) | (11 << 8) | (0 << 30) | (0 << 4) | (1 << 24)
= 0x100
0B40
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)
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)…
Today we ran some Fun Experiments with Nim on NuttX…
We blinked a Simulated LED on QEMU Emulator
Then we blinked a Real LED on Ox64 BL808 RISC-V SBC (pic above)
We peeked inside Nim on NuttX
To figure out how Nim compiles to C
Even though it’s Python-like with Garbage Collection
And Everything works OK!
(With minor fixes to Nim Config, LED Driver and RISC-V Timer)
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…
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…
nsh64/defconfig: NuttX Config for QEMU
qemu_rv_autoleds.c : Auto LED Driver for QEMU
qemu_rv_userleds.c: User LED Driver for QEMU
qemu_rv_appinit.c: Start LED Driver
Makefile: Nimcache has moved 2 folders up
config.nims: Add support for 64-bit RISC-V
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)
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 \
-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...
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.
In this article, we compiled a Work-In-Progress Version of Apache NuttX RTOS for Ox64 that has Minor Fixes for Nim…
nsh/defconfig: NuttX Config for Ox64
bl808_timerisr.c: RISC-V Timer for Ox64
bl808_autoleds.c: Auto LED Driver for Ox64
bl808_userleds.c: User LED Driver for Ox64
bl808_appinit.c: Start LED Driver for Ox64
Makefile: Nimcache has moved 2 folders up
config.nims: Add support for 64-bit RISC-V
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
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.
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.
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…
(Ignore riscv_mtimer.c)
We only need to change the Timer Initialisation: bl808_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…
riscv_mtimer_initialize which calls…
riscv_mtimer_set_mtimecmp which calls…
riscv_sbi_set_timer which calls…
sbi_ecall which makes an ecall to OpenSBI
Which accesses the RISC-V System Timer
Originally we set MTIMER_FREQ to 10000000
: bl808_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.