Ecosyste.ms: Awesome
An open API service indexing awesome lists of open source software.
https://github.com/lupyuen/nuttx-nim
Experiments with Nim on Apache NuttX Real-Time Operating System
https://github.com/lupyuen/nuttx-nim
bl808 nim nuttx ox64 qemu riscv riscv64
Last synced: about 8 hours ago
JSON representation
Experiments with Nim on Apache NuttX Real-Time Operating System
- Host: GitHub
- URL: https://github.com/lupyuen/nuttx-nim
- Owner: lupyuen
- License: apache-2.0
- Created: 2023-12-23T07:32:23.000Z (11 months ago)
- Default Branch: main
- Last Pushed: 2024-01-02T07:24:42.000Z (11 months ago)
- Last Synced: 2024-05-01T12:44:15.955Z (7 months ago)
- Topics: bl808, nim, nuttx, ox64, qemu, riscv, riscv64
- Homepage: https://lupyuen.github.io/articles/nim
- Size: 159 KB
- Stars: 2
- Watchers: 2
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- Funding: .github/FUNDING.yml
- License: LICENSE
Awesome Lists containing this project
README
![Nim App runs OK on Apache NuttX Real-Time Operating System](https://lupyuen.github.io/images/nim-title.png)
# Experiments with Nim on Apache NuttX Real-Time Operating System
Read the article...
- ["Nim on a Real-Time Operating System: Apache NuttX RTOS + Ox64 BL808 SBC"](https://lupyuen.github.io/articles/nim)
Today Apache NuttX RTOS runs on SBCs that have plenty of RAM: Ox64 with 64 MB RAM!
Now that we have plentiful RAM: Maybe we should build NuttX Apps with a Memory-Safe, Garbage-Collected language... Like Nim!
This Nim App: [hello_nim_async.nim](https://github.com/lupyuen2/wip-pinephone-nuttx-apps/blob/nim/examples/hello_nim/hello_nim_async.nim)
```nim
import std/asyncdispatch
import std/strformatproc hello_nim() {.exportc, cdecl.} =
echo "Hello Nim!"
GC_runOrc()
```Runs OK on NuttX for QEMU RISC-V 64-bit!
```text
+ qemu-system-riscv64 -semihosting -M virt,aclint=on -cpu rv64 -smp 8 -bios none -kernel nuttx -nographicNuttShell (NSH) NuttX-12.0.3
nsh> uname -a
NuttX 12.0.3 45150e164c5 Dec 23 2023 07:24:20 risc-v rv-virtnsh> hello_nim
Hello Nim!
```This is how we build NuttX with the Nim App inside...
```bash
## Install choosenim, add to PATH, select latest Dev Version of Nim Compiler
curl https://nim-lang.org/choosenim/init.sh -sSf | sh
export PATH=/home/vscode/.nimble/bin:$PATH
choosenim devel --latest## Download WIP NuttX and Apps
git clone --branch nim https://github.com/lupyuen2/wip-pinephone-nuttx nuttx
git clone --branch nim https://github.com/lupyuen2/wip-pinephone-nuttx-apps apps## Configure NuttX for QEMU RISC-V (64-bit)
cd nuttx
tools/configure.sh rv-virt:nsh64## Build NuttX
make## Start NuttX with QEMU RISC-V (64-bit)
qemu-system-riscv64 \
-semihosting \
-M virt,aclint=on \
-cpu rv64 \
-smp 8 \
-bios none \
-kernel nuttx \
-nographic
```We made some minor tweaks in NuttX...
![Nim + NuttX on QEMU Emulator](https://lupyuen.github.io/images/nim-qemu.png)
# Fix NuttX for Nim
Read the article...
- ["Nim on a Real-Time Operating System: Apache NuttX RTOS + Ox64 BL808 SBC"](https://lupyuen.github.io/articles/nim)
_How did we fix NuttX to compile Nim Apps correctly?_
We moved .nimcache 2 levels up: [apps/examples/hello_nim/Makefile](https://github.com/lupyuen2/wip-pinephone-nuttx-apps/blob/nim/examples/hello_nim/Makefile)
```text
## Move .nimcache 2 levels up
CFLAGS += -I $(NIMPATH)/lib -I ../../.nimcache
CSRCS += $(wildcard ../../.nimcache/*.c)## Previously:
## CFLAGS += -I $(NIMPATH)/lib -I ./.nimcache
## CSRCS += $(wildcard .nimcache/*.c)
```And we switched the Nim Target Architecture from RISC-V 32-bit to 64-bit: [apps/config.nims](https://github.com/lupyuen2/wip-pinephone-nuttx-apps/blob/nim/config.nims)
```nim
## Assume we are compiling with `riscv-none-elf-gcc` instead of `riscv64-unknown-elf-gcc`
switch "riscv32.nuttx.gcc.exe", "riscv-none-elf-gcc" ## TODO: Check for riscv64-unknown-elf-gcc
switch "riscv64.nuttx.gcc.exe", "riscv-none-elf-gcc" ## TODO: Check for riscv64-unknown-elf-gcc
## Previously: switch "riscv32.nuttx.gcc.exe", "riscv64-unknown-elf-gcc"
...
case arch
...
of "risc-v":
## TODO: Check for riscv32 or riscv3
## CONFIG_ARCH_RV32=y or CONFIG_ARCH_RV64=y
result.arch = "riscv64"
## Previously: result.arch = "riscv32"
```See the modified files...
- [Changes to NuttX Apps](https://github.com/lupyuen2/wip-pinephone-nuttx-apps/pull/3/files)
- [Changes to NuttX Kernel](https://github.com/lupyuen2/wip-pinephone-nuttx/pull/47/files)
# Nim on Apache NuttX RTOS and Ox64 BL808 RISC-V SBC
Read the article...
- ["Nim on a Real-Time Operating System: Apache NuttX RTOS + Ox64 BL808 SBC"](https://lupyuen.github.io/articles/nim)
Nim also runs OK on Apache NuttX RTOS and Ox64 BL808 RISC-V SBC!
This Nim App: [hello_nim_async.nim](https://github.com/lupyuen2/wip-pinephone-nuttx-apps/blob/nim/examples/hello_nim/hello_nim_async.nim)
```nim
import std/asyncdispatch
import std/strformatproc hello_nim() {.exportc, cdecl.} =
echo "Hello Nim!"
GC_runOrc()
```Produces this output...
```text
Starting kernel ...
ABC
NuttShell (NSH) NuttX-12.0.3
nsh> uname -a
NuttX 12.0.3 d27d0fd4be1-dirty Dec 24 2023 12:32:23 risc-v ox64nsh> hello_nim
Hello Nim!
```[(Source)](https://gist.github.com/lupyuen/adef0acd97669cd3570a0614e32166fc)
To build NuttX + Nim for Ox64 BL808 SBC...
```bash
## Install choosenim, add to PATH, select latest Dev Version of Nim Compiler
curl https://nim-lang.org/choosenim/init.sh -sSf | sh
export PATH=/home/vscode/.nimble/bin:$PATH
choosenim devel --latest## Download WIP NuttX and Apps
git clone --branch nim https://github.com/lupyuen2/wip-pinephone-nuttx nuttx
git clone --branch nim https://github.com/lupyuen2/wip-pinephone-nuttx-apps apps## Configure NuttX for Ox64 BL808 RISC-V SBC
cd nuttx
tools/configure.sh ox64:nsh## Build NuttX Kernel
make## Build Apps Filesystem
make -j 8 export
pushd ../apps
./tools/mkimport.sh -z -x ../nuttx/nuttx-export-*.tar.gz
make -j 8 import
popd## Export the Binary Image to `nuttx.bin`
riscv-none-elf-objcopy \
-O binary \
nuttx \
nuttx.bin## 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## Copy NuttX Image to Ox64 Linux microSD
cp Image "/Volumes/NO NAME/"
diskutil unmountDisk /dev/disk2## TODO: Boot Ox64 with the microSD
```See the modified files...
- [Changes to NuttX Apps](https://github.com/lupyuen2/wip-pinephone-nuttx-apps/pull/3/files)
- [Changes to NuttX Kernel](https://github.com/lupyuen2/wip-pinephone-nuttx/pull/47/files)
![Nim App runs OK on Apache NuttX Real-Time Operating System and Ox64 BL808 RISC-V SBC](https://lupyuen.github.io/images/nim-ox64.png)
# Blink an LED with Nim
Read the article...
- ["Nim on a Real-Time Operating System: Apache NuttX RTOS + Ox64 BL808 SBC"](https://lupyuen.github.io/articles/nim)
This is how we Blink an LED with Nim on NuttX: [hello_nim_async.nim](https://github.com/lupyuen2/wip-pinephone-nuttx-apps/blob/nim/examples/hello_nim/hello_nim_async.nim)
```nim
import std/strformat ## String Formatting## Import NuttX Functions and Macros 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: "",
nodecl.}
proc c_close(fd: cint): cint {.
importc: "close", header: "",
nodecl, discardable.}
proc c_ioctl(fd: cint, request: cint): cint {.
importc: "ioctl", header: "",
nodecl, varargs.}
proc c_usleep(usec: cuint): cint {.
importc: "usleep", header: "",
nodecl, discardable.}
var O_WRONLY {.
importc: "O_WRONLY", header: "".}: cint
var ULEDIOC_SETALL {.
importc: "ULEDIOC_SETALL", header: "".}: cint## Blink the LED
proc blink_led() =## Open the LED Driver
echo "Opening /dev/userleds"
let fd = c_open("/dev/userleds", O_WRONLY)
if fd < 0:
echo "Failed to open /dev/userleds"
return## On Return: Close the LED Driver
defer: c_close(fd)## 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## Wait a second (literally)
echo "Waiting..."
c_usleep(1000_000)## 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)## Main Function in Nim
proc hello_nim() {.exportc, cdecl.} =## On Return: Force the Garbage Collection
defer: GC_runOrc()## Print something
echo "Hello Nim!"## Blink the LED 20 times
for loop in 0..19:
blink_led()
```Which calls our barebones NuttX LED Driver for Ox64 BL808...
- ["LED Driver for Ox64 BL808"](https://github.com/lupyuen/nuttx-ox64#led-driver-for-ox64-bl808)
And Nim blinks our LED on Ox64 BL808 SBC!
- [Watch the Demo on YouTube](https://youtube.com/shorts/KCkiXFxBgxQ)
- [See the Log](https://gist.github.com/lupyuen/553c2da4ad5d119468d223e162573e96)
![Nim blinks our LED on Ox64 BL808 SBC](https://lupyuen.github.io/images/nim-blink2.jpg)
_How did we figure out the Nim Code?_
The code above is equivalent to this in C: [hello_main.c](https://github.com/lupyuen2/wip-pinephone-nuttx-apps/blob/nim/examples/hello/hello_main.c#L25-L85)
```c
#include
#include
#include
#include
#includeint main(int argc, FAR char *argv[])
{
printf("Hello, World!!\n");// Open the LED driver
printf("Opening /dev/userleds\n");
int fd = open("/dev/userleds", O_WRONLY);
if (fd < 0)
{
int errcode = errno;
printf("ERROR: Failed to open /dev/userleds: %d\n",
errcode);
return EXIT_FAILURE;
}// Turn on LED
puts("Set LED 0 to 1");
int ret = ioctl(fd, ULEDIOC_SETALL, 1);
if (ret < 0)
{
int errcode = errno;
printf("ERROR: ioctl(ULEDIOC_SUPPORTED) failed: %d\n",
errcode);
return EXIT_FAILURE;
}// Sleep a while
puts("Waiting...");
usleep(500 * 1000L);// Turn off LED
puts("Set LED 0 to 0");
ret = ioctl(fd, ULEDIOC_SETALL, 0);
if (ret < 0)
{
int errcode = errno;
printf("ERROR: ioctl(ULEDIOC_SUPPORTED) failed: %d\n",
errcode);
return EXIT_FAILURE;
}// Close the LED Driver
close(fd);return 0;
}
```![Blink an LED with Nim](https://lupyuen.github.io/images/nim-code.png)
# Inside a Nim App for NuttX
Read the article...
- ["Nim on a Real-Time Operating System: Apache NuttX RTOS + Ox64 BL808 SBC"](https://lupyuen.github.io/articles/nim)
TODO: What happens inside a NuttX App for NuttX?
https://github.com/lupyuen2/wip-pinephone-nuttx-apps/blob/c714a317e531aa8ab2de7b9a8e4c4b0f89f66626/config.nims
```text
$ export TOPDIR=/workspaces/bookworm/nuttx
$ cd /workspaces/bookworm/apps/examples/hello_nim
$ nim c --header hello_nim_async.nimread_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]
```# Build NuttX with Debian Container in VSCode
Nim Compiler won't install on some machines (like a 10-year-old Mac). So we create a Debian Bookworm Container in VSCode that will compile Nim and NuttX...
1. Install [Rancher Desktop](https://rancherdesktop.io/)
1. In Rancher Desktop, click "Settings"...
Set "Container Engine" to "dockerd (moby)"
Under "Kubernetes", uncheck "Enable Kubernetes"
(To reduce CPU Utilisation)
1. Restart VSCode to use the new PATH
Install the [VSCode Dev Containers Extension](https://code.visualstudio.com/docs/devcontainers/containers)
1. In VSCode, click the "Remote Explorer" icon in the Left Bar
1. Under "Dev Container", click "+" (New Dev Container)
1. Select "New Dev Container"
1. Select "Debian"
1. Select "Additional Options" > "Bookworm"
(With other versions of Debian, "apt install" will install outdated packages)
Inside the Debian Bookworm Container:
Install NuttX Prerequisites...
```bash
## From https://lupyuen.github.io/articles/nuttx#install-prerequisites
sudo apt update && sudo apt upgrade
sudo apt install \
bison flex gettext texinfo libncurses5-dev libncursesw5-dev \
gperf automake libtool pkg-config build-essential gperf genromfs \
libgmp-dev libmpc-dev libmpfr-dev libisl-dev binutils-dev libelf-dev \
libexpat-dev gcc-multilib g++-multilib picocom u-boot-tools util-linux \
kconfig-frontends## Extra Tools for RISCV QEMU
sudo apt install xxd
sudo apt install qemu-system-riscv64
```Install RISC-V Toolchain...
```bash
## Download xPack GNU RISC-V Embedded GCC Toolchain for 64-bit RISC-V
wget https://github.com/xpack-dev-tools/riscv-none-elf-gcc-xpack/releases/download/v13.2.0-2/xpack-riscv-none-elf-gcc-13.2.0-2-linux-x64.tar.gz
tar xf xpack-riscv-none-elf-gcc-13.2.0-2-linux-x64.tar.gz## Add to PATH
export PATH=$PWD/xpack-riscv-none-elf-gcc-13.2.0-2/bin:$PATH## Test gcc:
## gcc version 13.2.0 (xPack GNU RISC-V Embedded GCC x86_64)
riscv-none-elf-gcc -v
```[(Why we use xPack Toolchain)](https://lupyuen.github.io/articles/riscv#appendix-xpack-gnu-risc-v-embedded-gcc-toolchain-for-64-bit-risc-v)
Assuming that we need [Nim Compiler](https://nim-lang.org/install_unix.html)...
1. Install [Nim Compiler](https://nim-lang.org/install_unix.html)...
```bash
curl https://nim-lang.org/choosenim/init.sh -sSf | sh
```1. Add to PATH...
```bash
export PATH=/home/vscode/.nimble/bin:$PATH
```1. Select Latest Dev Version of Nim...
```bash
## Will take a while!
choosenim devel --latest
```1. Create a file named `a.nim`...
```text
echo "Hello World"
```1. Test Nim...
```bash
$ nim c a.nim
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: [Link]
Hint: mm: orc; threads: on; opt: none (DEBUG BUILD, `-d:release` generates faster code)
27941 lines; 0.342s; 30.445MiB peakmem; proj: /workspaces/debian/a.nim; out: /workspaces/debian/a [SuccessX]$ ls -l a
-rwxr-xr-x 1 vscode vscode 96480 Dec 22 12:19 a$ ./a
Hello World
```Git Clone the `nuttx` and `apps` folders. Then configure NuttX...
```bash
## TODO: git clone ... nuttx
## TODO: git clone ... apps## Configure NuttX for QEMU RISC-V (64-bit)
cd nuttx
tools/configure.sh rv-virt:nsh64
make menuconfig
```Enable the settings...
- "Device Drivers > LED Support > LED Driver"
- "Device Drivers > LED Support > Generic Lower Half LED Driver"
- "Application Configuration > Examples > Hello World Example (Nim)"
- "Application Configuration > Examples > LED Driver Example"
If the build fails with missing `getsockopt`...
```text
ioselectors_epoll.nim:407: undefined reference to `getsockopt'
```Then we enable NuttX Networking: Select...
```text
Networking support: Enable "Networking support"
Networking Support → SocketCAN Support:
Enable "SocketCAN Support"
Enable "sockopt support"
RTOS Features → Tasks and Scheduling:
Enable "Support parent/child task relationships"
Enable "Retain child exit status"
```Save and exit menuconfig, then build and run NuttX...
```bash
## Build NuttX
make## Start NuttX with QEMU RISC-V (64-bit)
qemu-system-riscv64 \
-semihosting \
-M virt,aclint=on \
-cpu rv64 \
-smp 8 \
-bios none \
-kernel nuttx \
-nographic
```If the build fails with missing `arm64_netinitialize`...
```text
arm64_initialize.c:219: undefined reference to `arm64_netinitialize'
```Then [add the function `arm64_netinitialize`](https://github.com/lupyuen2/wip-pinephone-nuttx/commit/77b8295eb761954370481ed113254b7e930f65c0)
If the build fails with missing `riscv_netinitialize`...
```text
riscv_initialize.c:124: undefined reference to `riscv_netinitialize'
```Then [add the function `riscv_netinitialize'](https://github.com/lupyuen2/wip-pinephone-nuttx/commit/594761cb413a3413a2d9deae15d2f5c31fcc1b65)
If the build fails with `const` errors...
```text
../../.nimcache/@m..@s..@s..@s..@s..@shome@[email protected]@stoolchains@snim-@hdevel@slib@[email protected]:362:285: error: initialization discards 'const' qualifier from pointer target type [-Werror=discarded-qualifiers]
362 | IGNOF(tyObject_SelectorImpl__JWlu9aJYbxUSEZxA8xSjuMg), .depth = 0, .display = TM__gfe9a52b5gJr8GSrnr883iA_3, .traceImpl = (void*)eqtrace___pureZasyncdispatch_u2061, .flags = 0};
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~
```Then [disable the `-Werror` option](https://github.com/lupyuen2/wip-pinephone-nuttx/commit/e82be7d1f9f662ef369a3c4adc8290a4035f9857)
# System Call for clock_nanosleep
TODO: usleep in our NuttX App calls clock_nanosleep in NuttX Kernel...
```text
00000000000007e8 :
usleep():
/workspaces/bookworm/nuttx/libs/libc/unistd/lib_usleep.c:100
{
struct timespec rqtp;
time_t sec;
int ret = 0;if (usec)
7e8: cd15 beqz a0,824 <.L3> 7e8: R_RISCV_RVC_BRANCH .L300000000000007ea <.LVL1>:
/workspaces/bookworm/nuttx/libs/libc/unistd/lib_usleep.c:104
{
/* Let clock_nanosleep() do all of the work. */sec = usec / 1000000;
7ea: 000f47b7 lui a5,0xf4
7ee: 2407879b addw a5,a5,576 # f4240 <.LASF110+0xe2ec1>
7f2: 02f5573b divuw a4,a0,a5
/workspaces/bookworm/nuttx/libs/libc/unistd/lib_usleep.c:95
{
7f6: 1101 add sp,sp,-32
/workspaces/bookworm/nuttx/libs/libc/unistd/lib_usleep.c:108
rqtp.tv_sec = sec;
rqtp.tv_nsec = (usec - (sec * 1000000)) * 1000;ret = clock_nanosleep(CLOCK_REALTIME, 0, &rqtp, NULL);
7f8: 860a mv a2,sp
7fa: 4681 li a3,0
7fc: 4581 li a1,0
```clock_nanosleep makes ecall to Kernel clock_nanosleep...
```text
0000000000001dee :
clock_nanosleep():
/workspaces/bookworm/nuttx/syscall/proxies/PROXY_clock_nanosleep.c:8
#include
#include
#includeint clock_nanosleep(clockid_t parm1, int parm2, FAR const struct timespec * parm3, FAR struct timespec * parm4)
{
1dee: 88aa mv a7,a00000000000001df0 <.LVL1>:
1df0: 882e mv a6,a10000000000001df2 <.LVL2>:
1df2: 87b2 mv a5,a20000000000001df4 <.LVL3>:
1df4: 8736 mv a4,a30000000000001df6 <.LBB4>:
sys_call4():
/workspaces/bookworm/nuttx/include/arch/syscall.h:281
register long r0 asm("a0") = (long)(nbr);
1df6: 03100513 li a0,490000000000001dfa <.LVL5>:
/workspaces/bookworm/nuttx/include/arch/syscall.h:282
register long r1 asm("a1") = (long)(parm1);
1dfa: 85c6 mv a1,a70000000000001dfc <.LVL6>:
/workspaces/bookworm/nuttx/include/arch/syscall.h:283
register long r2 asm("a2") = (long)(parm2);
1dfc: 8642 mv a2,a60000000000001dfe <.LVL7>:
/workspaces/bookworm/nuttx/include/arch/syscall.h:284
register long r3 asm("a3") = (long)(parm3);
1dfe: 86be mv a3,a50000000000001e00 <.LVL8>:
/workspaces/bookworm/nuttx/include/arch/syscall.h:287
asm volatile
1e00: 00000073 ecall
/workspaces/bookworm/nuttx/include/arch/syscall.h:294
asm volatile("nop" : "=r"(r0));
1e04: 0001 nop0000000000001e06 <.LBE4>:
clock_nanosleep():
/workspaces/bookworm/nuttx/syscall/proxies/PROXY_clock_nanosleep.c:10
return (int)sys_call4((unsigned int)SYS_clock_nanosleep, (uintptr_t)parm1, (uintptr_t)parm2, (uintptr_t)parm3, (uintptr_t)parm4);
}
1e06: 2501 sext.w a0,a0
1e08: 8082 ret
```System Call Number for clock_nanosleep is 49...
```text
<2>: Abbrev Number: 1 (DW_TAG_enumerator)
DW_AT_name : (strp) (offset: 0x8ca9): SYS_clock_nanosleep
DW_AT_const_value : (data1) 49
```# Documentation
- ["Nim on a Real-Time Operating System: Apache NuttX RTOS + Ox64 BL808 SBC"](https://lupyuen.github.io/articles/nim)
- [NuttX support for Nim](https://github.com/apache/nuttx-apps/pull/1597)
- [Nim support for NuttX](https://github.com/nim-lang/Nim/pull/21372/files)
- [For Nuttx, change ioselectors to use "select"](https://github.com/nim-lang/Nim/pull/21384)
- [Which implementation of NuttX select/poll/EPOLL is recommended in terms of performance and efficiency](https://github.com/apache/nuttx/issues/8604)
- [Nim on Arduino](https://disconnected.systems/blog/nim-on-adruino/)
- [Nim for Embedded Systems](https://github.com/nim-lang/Nim/blob/devel/doc/nimc.md#nim-for-embedded-systems)
- [Nim Compiler User Guide](https://nim-lang.org/docs/nimc.html)
- [Nim Wrapper for LVGL](https://github.com/mantielero/lvgl.nim)