# NuttX RTOS on PicoCalc

**URL:** <https://forum.clockworkpi.com/t/nuttx-rtos-on-picocalc/16783>\
**Category:** PicoCalc\
**Tags:** tinkering, community, firmware\
**Created:** [April 15, 2025, 8:10am UTC](https://forum.clockworkpi.com/t/nuttx-rtos-on-picocalc/16783 "2025-04-15T08:10:20Z")\
**Posts on this page:** 20\
**Page:** 1

<div class="post-metadata">

**Author:** ![shtirlic](https://yyz1.discourse-cdn.com/flex029/user_avatar/forum.clockworkpi.com/shtirlic/32/11561_2.png) [@shtirlic](https://forum.clockworkpi.com/u/shtirlic)\
**Post date:** [April 15, 2025, 8:10am UTC](https://forum.clockworkpi.com/t/nuttx-rtos-on-picocalc/16783/1 "2025-04-15T08:10:20Z")

</div>

Hi, this is my initial work on getting NuttX to run on PicoCalc with Pico 2 on board

Currently: SD Card, SPI, I2C, PWM, Speakers, Serial connection are working.  
Next: Display and builtin PicoCalc keyboard.

 ![image](https://canada1.discourse-cdn.com/flex029/uploads/clockworkpi/original/2X/8/8ced94099476dff27e2b35f8ea69af72e15c6d2c.png)

> **[GitHub - shtirlic/picocalc-nx: NuttX for PicoCalc](https://github.com/shtirlic/picocalc-nx)**
>
> NuttX for PicoCalc

There is also firmware uf2 file in pre-release on github for pico 2 based PicoCalc

More info about NuttX [https://nuttx.apache.org/](https://nuttx.apache.org/)

PS  
Also year ago NuttX landed on the moon [Apache NuttX Powers World's Smallest Lunar Robot in Japan's Historic Autonomous Lunar Exploration Mission | Developer World](https://developer.sony.com/posts/apache-nuttx-powers-worlds-smallest-lunar-robot-in-japans-historic-autonomous-lunar-exploration-mission)

---

<div class="post-metadata">

**Author:** ![shtirlic](https://yyz1.discourse-cdn.com/flex029/user_avatar/forum.clockworkpi.com/shtirlic/32/11561_2.png) [@shtirlic](https://forum.clockworkpi.com/u/shtirlic)\
**Post date:** [July 3, 2025, 5:21am UTC](https://forum.clockworkpi.com/t/nuttx-rtos-on-picocalc/16783/2 "2025-07-03T05:21:02Z")

</div>

Update:  
Currently I am working at porting NuttX to RISC-V cores on rp2350

> <https://github.com/apache/nuttx/pull/16424>
>
> \## Summary
> 
> Add support for RP2350 RISC-V cores, Add Pico 2 board.
> 
> \### Nami…ng
> \`\`\`
> Chip name : rp23xx-rv
> Board name : raspberrypi-pico-2-rv 
> Arch : risc-v
> \`\`\`
> 
> \### Changes from ARM rp23xx impl
> 
> \- Linker script update
> \- ASM head start 
> \- Update chip start
> \- New Hazard3 include regs
> \- Remove rp23xx chip hw spinlocks/testset
> \- New irq handling (external IRQ interrupt)
> \- New timerisr based on RISC-V std MTIME and alarm arch
> \- No SMP yet
> \- Tickless option
> \- Double size for idle, irq and main stacks 
> \- Board reset via watchdog trigger
> ...
> 
> \### Todo
> 
> \- \[x\] Uart defconfig
> \- \[x\] ostest pass 
> \- \[x\] usb defconfig
> \- \[\] SMP
> \- \[x\] Reset (via watchdog trigger without wd dev)
> \- \[x\] Hazard3 external IRQ
> \- \[\] More timers
> \- \[\] Tickless testing
> \- \[x\] Peripherals testing
> - \[x\] WDOG
> - \[x\] UART 
> - \[x\] USB
> - \[x\] PWM
> - \[x\] I2C
> - \[x\] SPI
> - \[x\] DMA
> \- \[\] Docs
> - \[\] Add chip
> - \[\] Add board
> - \[\] Toolchain info 
> \- \[\] Other boards
> \- \[x\] RISC-V Custom extensions (zba\_zbb\_zbs\_zbkb\_zca\_zcb\_zcmp in defconfig, add to chip?)
> \- \[\] Compare getprime perf
> \- \[\] LVGL perf test
> 
> \### Toolchain
> Use RISC-V gnu toolchain with soft float abi. Default riscv-collab uses double float abi
> 
> This is config for https://github.com/riscv-collab/riscv-gnu-toolchain
> \`\`\`
> ./configure --prefix=/opt/riscv --with-arch=rv32imac --with-abi=ilp32
> \`\`\`
> 
> \### Config
> 
> Serial NSH
> \`\`\`
> /tools/configure.sh raspberrypi-pico-2-rv:nsh
> \`\`\`
> 
> USB CDC/ACM Serial NSH
> \`\`\`
> /tools/configure.sh raspberrypi-pico-2-rv:usbnsh
> \`\`\`
> 
> \## Testing
> 
> cpuinfo
> \`\`\`
> nsh\> cat /proc/cpuinfo
> processor : 0
> hart : 0
> isa : rv32imac
> mmu : none
> 
> nsh\> free
> total used free maxused maxfree nused nfree name
> 516820 7116 509704 45768 490456 25 3 Umem
> 
> \`\`\`
> getprime
> \`\`\`
> nsh\> getprime
> Set thread priority to 10
> Set thread policy to SCHED\_RR
> Start thread #0
> thread #0 started, looking for primes \< 10000, doing 10 run(s)
> thread #0 finished, found 1230 primes, last one was 9973
> Done
> getprime took 4281 msec
> \`\`\`
> 
> \<details\>\<summary\>ostest nsh output\</summary\>
> 
> \`\`\`
> NuttShell (NSH) NuttX-12.9.0
> nsh\> 
> nsh\> ostest
> stdio\_test: write fd=1
> stdio\_test: Standard I/O Check: printf
> stdio\_test: write fd=2
> stdio\_test: Standard I/O Check: fprintf to stderr
> ostest\_main: putenv(Variable1=BadValue3)
> ostest\_main: setenv(Variable1, GoodValue1, TRUE)
> ostest\_main: setenv(Variable2, BadValue1, FALSE)
> ostest\_main: setenv(Variable2, GoodValue2, TRUE)
> ostest\_main: setenv(Variable3, GoodValue3, FALSE)
> ostest\_main: setenv(Variable3, BadValue2, FALSE)
> show\_variable: Variable=Variable1 has value=GoodValue1
> show\_variable: Variable=Variable2 has value=GoodValue2
> show\_variable: Variable=Variable3 has value=GoodValue3
> ostest\_main: Started user\_main at PID=3
> 
> user\_main: Begin argument test
> user\_main: Started with argc=5
> user\_main: argv\[0\]="ostest"
> user\_main: argv\[1\]="Arg1"
> user\_main: argv\[2\]="Arg2"
> user\_main: argv\[3\]="Arg3"
> user\_main: argv\[4\]="Arg4"
> 
> End of test memory usage:
> VARIABLE BEFORE AFTER
> ======== ======== ========
> arena 7e2d4 7e2d4
> ordblks 1 1
> mxordblk 79ce8 79ce8
> uordblks 45ec 45ec
> fordblks 79ce8 79ce8
> 
> user\_main: getopt() test
> getopt(): Simple test
> getopt(): Invalid argument
> getopt(): Missing optional argument
> getopt\_long(): Simple test
> getopt\_long(): No short options
> getopt\_long(): Argument for --option=argument
> getopt\_long(): Invalid long option
> getopt\_long(): Mixed long and short options
> getopt\_long(): Invalid short option
> getopt\_long(): Missing optional arguments
> getopt\_long\_only(): Mixed long and short options
> getopt\_long\_only(): Single hyphen long options
> 
> End of test memory usage:
> VARIABLE BEFORE AFTER
> ======== ======== ========
> arena 7e2d4 7e2d4
> ordblks 1 1
> mxordblk 79ce8 79ce8
> uordblks 45ec 45ec
> fordblks 79ce8 79ce8
> 
> user\_main: libc tests
> 
> End of test memory usage:
> VARIABLE BEFORE AFTER
> ======== ======== ========
> arena 7e2d4 7e2d4
> ordblks 1 1
> mxordblk 79ce8 79ce8
> uordblks 45ec 45ec
> fordblks 79ce8 79ce8
> show\_variable: Variable=Variable1 has value=GoodValue1
> show\_variable: Variable=Variable2 has value=GoodValue2
> show\_variable: Variable=Variable3 has value=GoodValue3
> show\_variable: Variable=Variable1 has no value
> show\_variable: Variable=Variable2 has value=GoodValue2
> show\_variable: Variable=Variable3 has value=GoodValue3
> 
> End of test memory usage:
> VARIABLE BEFORE AFTER
> ======== ======== ========
> arena 7e2d4 7e2d4
> ordblks 1 2
> mxordblk 79ce8 79ce8
> uordblks 45ec 45cc
> fordblks 79ce8 79d08
> show\_variable: Variable=Variable1 has no value
> show\_variable: Variable=Variable2 has no value
> show\_variable: Variable=Variable3 has no value
> 
> End of test memory usage:
> VARIABLE BEFORE AFTER
> ======== ======== ========
> arena 7e2d4 7e2d4
> ordblks 2 2
> mxordblk 79ce8 79ce8
> uordblks 45cc 4554
> fordblks 79d08 79d80
> 
> user\_main: setvbuf test
> setvbuf\_test: Test NO buffering
> setvbuf\_test: Using NO buffering
> setvbuf\_test: Test default FULL buffering
> setvbuf\_test: Using default FULL buffering
> setvbuf\_test: Test FULL buffering, buffer size 64
> setvbuf\_test: Using FULL buffering, buffer size 64
> setvbuf\_test: Test FULL buffering, pre-allocated buffer
> setvbuf\_test: Using FULL buffering, pre-allocated buffer
> setvbuf\_test: Test LINE buffering, buffer size 64
> setvbuf\_test: Using LINE buffering, buffer size 64
> setvbuf\_test: Test FULL buffering, pre-allocated buffer
> setvbuf\_test: Using FULL buffering, pre-allocated buffer
> 
> End of test memory usage:
> VARIABLE BEFORE AFTER
> ======== ======== ========
> arena 7e2d4 7e2d4
> ordblks 2 2
> mxordblk 79ce8 79ce8
> uordblks 4554 4554
> fordblks 79d80 79d80
> 
> user\_main: /dev/null test
> dev\_null: Read 0 bytes from /dev/null
> dev\_null: Wrote 1024 bytes to /dev/null
> 
> End of test memory usage:
> VARIABLE BEFORE AFTER
> ======== ======== ========
> arena 7e2d4 7e2d4
> ordblks 2 2
> mxordblk 79ce8 79ce8
> uordblks 4554 4554
> fordblks 79d80 79d80
> 
> user\_main: task\_restart test
> 
> Test task\_restart()
> restart\_main: setenv(VarName, VarValue, TRUE)
> restart\_main: Started restart\_main at PID=4
> restart\_main: Started with argc=4
> restart\_main: argv\[0\]="ostest"
> restart\_main: argv\[1\]="This is argument 1"
> restart\_main: argv\[2\]="Argument 2 here"
> restart\_main: argv\[3\]="Lastly, the 3rd argument"
> restart\_main: Variable=VarName has value=VarValue
> restart\_main: I am still here
> restart\_main: I am still here
> restart\_main: Started restart\_main at PID=4
> restart\_main: Started with argc=4
> restart\_main: argv\[0\]="ostest"
> restart\_main: argv\[1\]="This is argument 1"
> restart\_main: argv\[2\]="Argument 2 here"
> restart\_main: argv\[3\]="Lastly, the 3rd argument"
> restart\_main: Variable=VarName has value=VarValue
> restart\_main: Started with argc=4
> restart\_main: argv\[0\]="ostest"
> restart\_main: argv\[1\]="This is argument 1"
> restart\_main: argv\[2\]="Argument 2 here"
> restart\_main: argv\[3\]="Lastly, the 3rd argument"
> restart\_main: Variable=VarName has value=VarValue
> restart\_main: Exiting
> 
> End of test memory usage:
> VARIABLE BEFORE AFTER
> ======== ======== ========
> arena 7e2d4 7e2d4
> ordblks 2 2
> mxordblk 79ce8 77930
> uordblks 4554 6944
> fordblks 79d80 77990
> 
> user\_main: waitpid test
> 
> Test waitpid()
> waitpid\_start\_child: Started waitpid\_main at PID=5
> waitpid\_start\_child: Started waitpid\_main at PID=6
> waitpid\_main: PID 5 Started
> waitpid\_main: PID 6 Started
> waitpid\_start\_child: Started waitpid\_main at PID=7
> waitpid\_main: PID 7 Started
> waitpid\_test: Waiting for PID=5 with waitpid()
> waitpid\_main: PID 5 exitting with result=14
> waitpid\_main: PID 6 exitting with result=14
> waitpid\_test: PID 5 waitpid succeeded with stat\_loc=0e00
> waitpid\_last: Waiting for PID=7 with waitpid()
> waitpid\_main: PID 7 exitting with result=14
> waitpid\_last: PASS: PID 7 waitpid succeeded with stat\_loc=0e00
> 
> End of test memory usage:
> VARIABLE BEFORE AFTER
> ======== ======== ========
> arena 7e2d4 7e2d4
> ordblks 2 5
> mxordblk 77930 73170
> uordblks 6944 a954
> fordblks 77990 73980
> 
> user\_main: mutex test
> Initializing mutex
> Starting thread 1
> Starting thread 2
> Thread1 Thread2
> Loops 32 32
> Errors 0 0
> 
> Testing moved mutex
> Starting moved mutex thread 1
> Starting moved mutex thread 2
> Thread1 Thread2
> Moved Loops 32 32
> Moved Errors 0 0
> 
> End of test memory usage:
> VARIABLE BEFORE AFTER
> ======== ======== ========
> arena 7e2d4 7e2d4
> ordblks 5 2
> mxordblk 73170 79408
> uordblks a954 4e6c
> fordblks 73980 79468
> 
> user\_main: timed mutex test
> mutex\_test: Initializing mutex
> mutex\_test: Starting thread
> pthread: Started
> pthread: Waiting for lock or timeout
> mutex\_test: Unlocking
> pthread: Got the lock
> pthread: Waiting for lock or timeout
> pthread: Got the timeout. Terminating
> mutex\_test: PASSED
> 
> End of test memory usage:
> VARIABLE BEFORE AFTER
> ======== ======== ========
> arena 7e2d4 7e2d4
> ordblks 2 2
> mxordblk 79408 79408
> uordblks 4e6c 4e6c
> fordblks 79468 79468
> 
> user\_main: cancel test
> cancel\_test: Test 1a: Normal Cancellation
> cancel\_test: Starting thread
> start\_thread: Initializing mutex
> start\_thread: Initializing cond
> start\_thread: Starting thread
> start\_thread: Yielding
> sem\_waiter: Taking mutex
> sem\_waiter: Starting wait for condition
> cancel\_test: Canceling thread
> cancel\_test: Joining
> cancel\_test: waiter exited with result=0xffffffff
> cancel\_test: PASS thread terminated with PTHREAD\_CANCELED
> cancel\_test: Test 2: Asynchronous Cancellation
> ... Skipped
> cancel\_test: Test 3: Cancellation of detached thread
> cancel\_test: Re-starting thread
> restart\_thread: Destroying cond
> restart\_thread: Destroying mutex
> restart\_thread: Re-starting thread
> start\_thread: Initializing mutex
> start\_thread: Initializing cond
> start\_thread: Starting thread
> start\_thread: Yielding
> sem\_waiter: Taking mutex
> sem\_waiter: Starting wait for condition
> cancel\_test: Canceling thread
> cancel\_test: Joining
> cancel\_test: PASS pthread\_join failed with status=ESRCH
> cancel\_test: Test 5: Non-cancelable threads
> cancel\_test: Re-starting thread (non-cancelable)
> restart\_thread: Destroying cond
> restart\_thread: Destroying mutex
> restart\_thread: Re-starting thread
> start\_thread: Initializing mutex
> start\_thread: Initializing cond
> start\_thread: Starting thread
> start\_thread: Yielding
> sem\_waiter: Taking mutex
> sem\_waiter: Starting wait for condition
> sem\_waiter: Setting non-cancelable
> cancel\_test: Canceling thread
> cancel\_test: Joining
> sem\_waiter: Releasing mutex
> sem\_waiter: Setting cancelable
> cancel\_test: waiter exited with result=0xffffffff
> cancel\_test: PASS thread terminated with PTHREAD\_CANCELED
> cancel\_test: Test 6: Cancel message queue wait
> cancel\_test: Starting thread (cancelable)
> Skipped
> cancel\_test: Test 7: Cancel signal wait
> cancel\_test: Starting thread (cancelable)
> Skipped
> 
> End of test memory usage:
> VARIABLE BEFORE AFTER
> ======== ======== ========
> arena 7e2d4 7e2d4
> ordblks 2 2
> mxordblk 79408 77c08
> uordblks 4e6c 666c
> fordblks 79468 77c68
> 
> user\_main: robust test
> robust\_test: Initializing mutex
> robust\_test: Starting thread
> robust\_waiter: Taking mutex
> robust\_waiter: Exiting with mutex
> robust\_test: Take the lock again
> robust\_test: Make the mutex consistent again.
> robust\_test: Take the lock again
> robust\_test: Joining
> robust\_test: waiter exited with result=0
> robust\_test: Test complete with nerrors=0
> 
> End of test memory usage:
> VARIABLE BEFORE AFTER
> ======== ======== ========
> arena 7e2d4 7e2d4
> ordblks 2 2
> mxordblk 77c08 77c08
> uordblks 666c 666c
> fordblks 77c68 77c68
> 
> user\_main: semaphore test
> sem\_test: Initializing semaphore to 0
> sem\_test: Starting waiter thread 1
> sem\_test: Set thread 1 priority to 191
> waiter\_func: Thread 1 Started
> sem\_test: Starting waiter thread 2
> waiter\_func: Thread 1 initial semaphore value = 0
> sem\_test: Set thread 2 priority to 128
> waiter\_func: Thread 1 waiting on semaphore
> waiter\_func: Thread 2 Started
> waiter\_func: Thread 2 initial semaphore value = -1
> waiter\_func: Thread 2 waiting on semaphore
> sem\_test: Starting poster thread 3
> sem\_test: Set thread 3 priority to 64
> poster\_func: Thread 3 started
> poster\_func: Thread 3 semaphore value = -2
> poster\_func: Thread 3 posting semaphore
> waiter\_func: Thread 1 awakened
> waiter\_func: Thread 1 new semaphore value = -1
> waiter\_func: Thread 1 done
> poster\_func: Thread 3 new semaphore value = -1
> poster\_func: Thread 3 semaphore value = -1
> poster\_func: Thread 3 posting semaphore
> waiter\_func: Thread 2 awakened
> poster\_func: Thread 3 new semaphore value = 0
> waiter\_func: Thread 2 new semaphore value = 0
> poster\_func: Thread 3 done
> waiter\_func: Thread 2 done
> 
> End of test memory usage:
> VARIABLE BEFORE AFTER
> ======== ======== ========
> arena 7e2d4 7e2d4
> ordblks 2 3
> mxordblk 77c08 78248
> uordblks 666c 574c
> fordblks 77c68 78b88
> 
> user\_main: timed semaphore test
> semtimed\_test: Initializing semaphore to 0
> semtimed\_test: Waiting for two second timeout
> semtimed\_test: PASS: first test returned timeout
> BEFORE: (33 sec, 682764966 nsec)
> AFTER: (35 sec, 690225406 nsec)
> semtimed\_test: Starting poster thread
> semtimed\_test: Set thread 1 priority to 191
> semtimed\_test: Starting poster thread 3
> semtimed\_test: Set thread 3 priority to 64
> semtimed\_test: Waiting for two second timeout
> poster\_func: Waiting for 1 second
> poster\_func: Posting
> semtimed\_test: PASS: sem\_timedwait succeeded
> BEFORE: (35 sec, 693431933 nsec)
> AFTER: (36 sec, 700571253 nsec)
> 
> End of test memory usage:
> VARIABLE BEFORE AFTER
> ======== ======== ========
> arena 7e2d4 7e2d4
> ordblks 3 2
> mxordblk 78248 79408
> uordblks 574c 4e6c
> fordblks 78b88 79468
> 
> user\_main: condition variable test
> cond\_test: Initializing mutex
> cond\_test: Initializing cond
> cond\_test: Starting waiter
> cond\_test: Set thread 1 priority to 128
> waiter\_thread: Started
> cond\_test: Starting signaler
> cond\_test: Set thread 2 priority to 64
> thread\_signaler: Started
> thread\_signaler: Terminating
> cond\_test: signaler terminated, now cancel the waiter
> cond\_test: Waiter Signaler
> cond\_test: Loops 32 32
> cond\_test: Errors 0 0
> cond\_test:
> cond\_test: 0 times, waiter did not have to wait for data
> cond\_test: 0 times, data was already available when the signaler run
> cond\_test: 0 times, the waiter was in an unexpected state when the signaler ran
> 
> End of test memory usage:
> VARIABLE BEFORE AFTER
> ======== ======== ========
> arena 7e2d4 7e2d4
> ordblks 2 3
> mxordblk 79408 78b28
> uordblks 4e6c 4e6c
> fordblks 79468 79468
> 
> user\_main: pthread\_exit() test
> pthread\_exit\_test: Started pthread\_exit\_main at PID=27
> pthread\_exit\_main 27: Starting pthread\_exit\_thread
> pthread\_exit\_main 27: Sleeping for 5 seconds
> pthread\_exit\_thread 28: Sleeping for 10 second
> pthread\_exit\_thread 28: Still running...
> pthread\_exit\_main 27: Calling pthread\_exit()
> 
> End of test memory usage:
> VARIABLE BEFORE AFTER
> ======== ======== ========
> arena 7e2d4 7e2d4
> ordblks 3 2
> mxordblk 78b28 77328
> uordblks 4e6c 6f4c
> fordblks 79468 77388
> 
> user\_main: pthread\_rwlock test
> pthread\_rwlock: Initializing rwlock
> pthread\_exit\_thread 28: Exiting
> 
> End of test memory usage:
> VARIABLE BEFORE AFTER
> ======== ======== ========
> arena 7e2d4 7e2d4
> ordblks 2 3
> mxordblk 77328 77328
> uordblks 6f4c 4e7c
> fordblks 77388 79458
> 
> user\_main: pthread\_rwlock\_cancel test
> pthread\_rwlock\_cancel: Starting test
> 
> End of test memory usage:
> VARIABLE BEFORE AFTER
> ======== ======== ========
> arena 7e2d4 7e2d4
> ordblks 3 2
> mxordblk 77328 79ce8
> uordblks 4e7c 459c
> fordblks 79458 79d38
> 
> user\_main: timed wait test
> thread\_waiter: Initializing mutex
> timedwait\_test: Initializing cond
> timedwait\_test: Starting waiter
> timedwait\_test: Set thread 2 priority to 177
> thread\_waiter: Taking mutex
> thread\_waiter: Starting 5 second wait for condition
> timedwait\_test: Joining
> thread\_waiter: pthread\_cond\_timedwait timed out
> thread\_waiter: Releasing mutex
> thread\_waiter: Exit with status 0x12345678
> timedwait\_test: waiter exited with result=0x12345678
> 
> End of test memory usage:
> VARIABLE BEFORE AFTER
> ======== ======== ========
> arena 7e2d4 7e2d4
> ordblks 2 2
> mxordblk 79ce8 79408
> uordblks 459c 4e7c
> fordblks 79d38 79458
> 
> user\_main: message queue test
> mqueue\_test: Starting receiver
> mqueue\_test: Set receiver priority to 128
> receiver\_thread: Starting
> mqueue\_test: Starting sender
> mqueue\_test: Set sender thread priority to 64
> mqueue\_test: Waiting for sender to complete
> sender\_thread: Starting
> receiver\_thread: mq\_receive succeeded on msg 0
> sender\_thread: mq\_send succeeded on msg 0
> receiver\_thread: mq\_receive succeeded on msg 1
> sender\_thread: mq\_send succeeded on msg 1
> receiver\_thread: mq\_receive succeeded on msg 2
> sender\_thread: mq\_send succeeded on msg 2
> receiver\_thread: mq\_receive succeeded on msg 3
> sender\_thread: mq\_send succeeded on msg 3
> receiver\_thread: mq\_receive succeeded on msg 4
> sender\_thread: mq\_send succeeded on msg 4
> receiver\_thread: mq\_receive succeeded on msg 5
> sender\_thread: mq\_send succeeded on msg 5
> receiver\_thread: mq\_receive succeeded on msg 6
> sender\_thread: mq\_send succeeded on msg 6
> receiver\_thread: mq\_receive succeeded on msg 7
> sender\_thread: mq\_send succeeded on msg 7
> receiver\_thread: mq\_receive succeeded on msg 8
> sender\_thread: mq\_send succeeded on msg 8
> receiver\_thread: mq\_receive succeeded on msg 9
> sender\_thread: mq\_send succeeded on msg 9
> sender\_thread: returning nerrors=0
> mqueue\_test: Killing receiver
> receiver\_thread: mq\_receive interrupted!
> receiver\_thread: returning nerrors=0
> mqueue\_test: Canceling receiver
> mqueue\_test: receiver has already terminated
> 
> End of test memory usage:
> VARIABLE BEFORE AFTER
> ======== ======== ========
> arena 7e2d4 7e2d4
> ordblks 2 2
> mxordblk 79408 77bd8
> uordblks 4e7c 66dc
> fordblks 79458 77bf8
> 
> user\_main: timed message queue test
> timedmqueue\_test: Starting sender
> timedmqueue\_test: Waiting for sender to complete
> sender\_thread: Starting
> sender\_thread: mq\_timedsend succeeded on msg 0
> sender\_thread: mq\_timedsend succeeded on msg 1
> sender\_thread: mq\_timedsend succeeded on msg 2
> sender\_thread: mq\_timedsend succeeded on msg 3
> sender\_thread: mq\_timedsend succeeded on msg 4
> sender\_thread: mq\_timedsend succeeded on msg 5
> sender\_thread: mq\_timedsend succeeded on msg 6
> sender\_thread: mq\_timedsend succeeded on msg 7
> sender\_thread: mq\_timedsend succeeded on msg 8
> sender\_thread: mq\_timedsend 9 timed out as expected
> sender\_thread: returning nerrors=0
> timedmqueue\_test: Starting receiver
> timedmqueue\_test: Waiting for receiver to complete
> receiver\_thread: Starting
> receiver\_thread: mq\_timedreceive succeed on msg 0
> receiver\_thread: mq\_timedreceive succeed on msg 1
> receiver\_thread: mq\_timedreceive succeed on msg 2
> receiver\_thread: mq\_timedreceive succeed on msg 3
> receiver\_thread: mq\_timedreceive succeed on msg 4
> receiver\_thread: mq\_timedreceive succeed on msg 5
> receiver\_thread: mq\_timedreceive succeed on msg 6
> receiver\_thread: mq\_timedreceive succeed on msg 7
> receiver\_thread: mq\_timedreceive succeed on msg 8
> receiver\_thread: Receive 9 timed out as expected
> receiver\_thread: returning nerrors=0
> timedmqueue\_test: Test complete
> 
> End of test memory usage:
> VARIABLE BEFORE AFTER
> ======== ======== ========
> arena 7e2d4 7e2d4
> ordblks 2 2
> mxordblk 77bd8 77bd8
> uordblks 66dc 66dc
> fordblks 77bf8 77bf8
> 
> user\_main: sigprocmask test
> sigprocmask\_test: SUCCESS
> 
> End of test memory usage:
> VARIABLE BEFORE AFTER
> ======== ======== ========
> arena 7e2d4 7e2d4
> ordblks 2 2
> mxordblk 77bd8 77bd8
> uordblks 66dc 66dc
> fordblks 77bf8 77bf8
> 
> user\_main: signal handler test
> sighand\_test: Initializing semaphore to 0
> sighand\_test: Starting waiter task
> sighand\_test: Started waiter\_main pid=44
> waiter\_main: Waiter started
> waiter\_main: Unmasking signal 32
> waiter\_main: Registering signal handler
> waiter\_main: oact.sigaction=0 oact.sa\_flags=0 oact.sa\_mask=0000000000000000
> waiter\_main: Waiting on semaphore
> sighand\_test: Signaling pid=44 with signo=32 sigvalue=42
> waiter\_main: sem\_wait() successfully interrupted by signal
> waiter\_main: done
> sighand\_test: done
> 
> End of test memory usage:
> VARIABLE BEFORE AFTER
> ======== ======== ========
> arena 7e2d4 7e2d4
> ordblks 2 1
> mxordblk 77bd8 77bc8
> uordblks 66dc 670c
> fordblks 77bf8 77bc8
> 
> user\_main: nested signal handler test
> signest\_test: Starting signal waiter task at priority 101
> waiter\_main: Waiter started
> waiter\_main: Setting signal mask
> signest\_test: Started waiter\_main pid=45
> waiter\_main: Registering signal handler
> signest\_test: Starting interfering task at priority 102
> waiter\_main: Waiting on semaphore
> interfere\_main: Waiting on semaphore
> signest\_test: Started interfere\_main pid=46
> signest\_test: Simple case:
> Total signalled 1240 Odd=620 Even=620
> Total handled 1240 Odd=620 Even=620
> Total nested 0 Odd=0 Even=0  
> signest\_test: With task locking
> Total signalled 2480 Odd=1240 Even=1240
> Total handled 2480 Odd=1240 Even=1240
> Total nested 0 Odd=0 Even=0  
> signest\_test: With intefering thread
> Total signalled 3720 Odd=1860 Even=1860
> Total handled 3720 Odd=1860 Even=1860
> Total nested 0 Odd=0 Even=0  
> signest\_test: done
> 
> End of test memory usage:
> VARIABLE BEFORE AFTER
> ======== ======== ========
> arena 7e2d4 7e2d4
> ordblks 1 2
> mxordblk 77bc8 75398
> uordblks 670c 6e6c
> fordblks 77bc8 77468
> 
> user\_main: wdog test
> wdog\_test start...
> wdtest\_once 0 ns
> wdtest\_once 0 ns
> wdtest\_once 0 ns
> wdtest\_once 0 ns
> wdtest\_once 1 ns
> wdtest\_once 1 ns
> wdtest\_once 1 ns
> wdtest\_once 1 ns
> wdtest\_once 10 ns
> wdtest\_once 10 ns
> wdtest\_once 10 ns
> wdtest\_once 10 ns
> wdtest\_once 100 ns
> wdtest\_once 100 ns
> wdtest\_once 100 ns
> wdtest\_once 100 ns
> wdtest\_once 1000 ns
> wdtest\_once 1000 ns
> wdtest\_once 1000 ns
> wdtest\_once 1000 ns
> wdtest\_once 10000 ns
> wdtest\_once 10000 ns
> wdtest\_once 10000 ns
> wdtest\_once 10000 ns
> wdtest\_once 100000 ns
> wdtest\_once 100000 ns
> wdtest\_once 100000 ns
> wdtest\_once 100000 ns
> wdtest\_once 1000000 ns
> wdtest\_once 1000000 ns
> wdtest\_once 1000000 ns
> wdtest\_once 1000000 ns
> wd\_start with maximum delay, cancel OK, rest 2147483646
> wdtest\_recursive 1000000us
> wd\_start with maximum delay, cancel OK, rest 2147483646
> wdtest\_recursive 1000000us
> wd\_start with maximum delay, cancel OK, rest 2147483645
> wdtest\_recursive 1000000us
> wd\_start with maximum delay, cancel OK, rest 2147483645
> wdtest\_recursive 1000000us
> recursive wdog triggered 6 times, elapsed tick 12
> wdtest\_recursive 10000000us
> recursive wdog triggered 6 times, elapsed tick 12
> wdtest\_recursive 10000000us
> recursive wdog triggered 6 times, elapsed tick 12
> wdtest\_recursive 10000000us
> recursive wdog triggered 6 times, elapsed tick 12
> wdtest\_recursive 10000000us
> recursive wdog triggered 6 times, elapsed tick 12
> recursive wdog triggered 6 times, elapsed tick 12
> recursive wdog triggered 6 times, elapsed tick 12
> recursive wdog triggered 6 times, elapsed tick 12
> wdog\_test end...
> 
> End of test memory usage:
> VARIABLE BEFORE AFTER
> ======== ======== ========
> arena 7e2d4 7e2d4
> ordblks 2 3
> mxordblk 75398 75398
> uordblks 6e6c 710c
> fordblks 77468 771c8
> 
> user\_main: round-robin scheduler test
> rr\_test: Set thread priority to 1
> rr\_test: Set thread policy to SCHED\_RR
> rr\_test: Starting first get\_primes\_thread
> First get\_primes\_thread: 55
> rr\_test: Starting second get\_primes\_thread
> Second get\_primes\_thread: 56
> rr\_test: Waiting for threads to complete -- this should take awhile
> If RR scheduling is working, they should start and complete at
> about the same time
> get\_primes\_thread id=1 started, looking for primes \< 10000, doing 10 run(s)
> get\_primes\_thread id=2 started, looking for primes \< 10000, doing 10 run(s)
> get\_primes\_thread id=1 finished, found 1230 primes, last one was 9973
> get\_primes\_thread id=2 finished, found 1230 primes, last one was 9973
> rr\_test: Done
> 
> End of test memory usage:
> VARIABLE BEFORE AFTER
> ======== ======== ========
> arena 7e2d4 7e2d4
> ordblks 3 3
> mxordblk 75398 75398
> uordblks 710c 5f4c
> fordblks 771c8 78388
> 
> user\_main: barrier test
> barrier\_test: Initializing barrier
> barrier\_test: Thread 0 created
> barrier\_test: Thread 1 created
> barrier\_func: Thread 0 started
> barrier\_func: Thread 1 started
> barrier\_test: Thread 2 created
> barrier\_func: Thread 2 started
> barrier\_test: Thread 3 created
> barrier\_func: Thread 3 started
> barrier\_test: Thread 4 created
> barrier\_func: Thread 4 started
> barrier\_test: Thread 5 created
> barrier\_func: Thread 5 started
> barrier\_test: Thread 6 created
> barrier\_func: Thread 6 started
> barrier\_test: Thread 7 created
> barrier\_func: Thread 7 started
> barrier\_func: Thread 0 calling pthread\_barrier\_wait()
> barrier\_func: Thread 1 calling pthread\_barrier\_wait()
> barrier\_func: Thread 2 calling pthread\_barrier\_wait()
> barrier\_func: Thread 3 calling pthread\_barrier\_wait()
> barrier\_func: Thread 4 calling pthread\_barrier\_wait()
> barrier\_func: Thread 5 calling pthread\_barrier\_wait()
> barrier\_func: Thread 6 calling pthread\_barrier\_wait()
> barrier\_func: Thread 7 calling pthread\_barrier\_wait()
> barrier\_func: Thread 7, back with status=PTHREAD\_BARRIER\_SERIAL\_THREAD (I AM SPECIAL)
> barrier\_func: Thread 0, back with status=0 (I am not special)
> barrier\_func: Thread 1, back with status=0 (I am not special)
> barrier\_func: Thread 2, back with status=0 (I am not special)
> barrier\_func: Thread 3, back with status=0 (I am not special)
> barrier\_func: Thread 4, back with status=0 (I am not special)
> barrier\_func: Thread 5, back with status=0 (I am not special)
> barrier\_func: Thread 6, back with status=0 (I am not special)
> barrier\_func: Thread 7 done
> barrier\_func: Thread 0 done
> barrier\_func: Thread 1 done
> barrier\_test: Thread 0 completed with result=0
> barrier\_test: Thread 1 completed with result=0
> barrier\_func: Thread 2 done
> barrier\_func: Thread 3 done
> barrier\_test: Thread 2 completed with result=0
> barrier\_test: Thread 3 completed with result=0
> barrier\_func: Thread 4 done
> barrier\_func: Thread 5 done
> barrier\_test: Thread 4 completed with result=0
> barrier\_test: Thread 5 completed with result=0
> barrier\_func: Thread 6 done
> barrier\_test: Thread 6 completed with result=0
> barrier\_test: Thread 7 completed with result=0
> 
> End of test memory usage:
> VARIABLE BEFORE AFTER
> ======== ======== ========
> arena 7e2d4 7e2d4
> ordblks 3 5
> mxordblk 75398 75398
> uordblks 5f4c 5f4c
> fordblks 78388 78388
> 
> user\_main: scheduler lock test
> sched\_lock: Starting lowpri\_thread at 97
> sched\_lock: Set lowpri\_thread priority to 97
> sched\_lock: Starting highpri\_thread at 98
> sched\_lock: Set highpri\_thread priority to 98
> sched\_lock: Waiting...
> sched\_lock: PASSED No pre-emption occurred while scheduler was locked.
> sched\_lock: Starting lowpri\_thread at 97
> sched\_lock: Set lowpri\_thread priority to 97
> sched\_lock: Starting highpri\_thread at 98
> sched\_lock: Set highpri\_thread priority to 98
> sched\_lock: Waiting...
> sched\_lock: PASSED No pre-emption occurred while scheduler was locked.
> sched\_lock: Finished
> 
> End of test memory usage:
> VARIABLE BEFORE AFTER
> ======== ======== ========
> arena 7e2d4 7e2d4
> ordblks 5 4
> mxordblk 75398 75398
> uordblks 5f4c 566c
> fordblks 78388 78c68
> 
> user\_main: vfork() test
> vfork\_test: Child 73 ran successfully
> 
> Final memory usage:
> VARIABLE BEFORE AFTER
> ======== ======== ========
> arena 7e2d4 7e2d4
> ordblks 1 3
> mxordblk 79ce8 75398
> uordblks 45ec 6d94
> fordblks 79ce8 77540
> user\_main: Exiting
> ostest\_main: Exiting with status 0
> \`\`\`
> \</details\>

At current state It’s usable right now and will be ready soon to merge, after the platform docs, cleanup etc.

- Screen is working
- Keyboard is working
- SD card working, FAT fs working fine
- PWM is working (test sounds from speakers), no audio driver yet for NuttX
- WAMR WASM runtime ([WAMR - WebAssembly Micro Runtime](https://bytecodealliance.github.io/wamr.dev/)) included

I will update [GitHub - shtirlic/picocalc-nx: NuttX for PicoCalc](https://github.com/shtirlic/picocalc-nx) main branch with configs and other stuff after PR merge in upstream.

Also I am writing the UI shell for NuttX(LVGL), where you can run normal apps and WASM apps.  
Source also will be available as custom app for NuttX and will be used as init/startup app in picocalc-nx board.

 ![image](https://canada1.discourse-cdn.com/flex029/uploads/clockworkpi/original/2X/e/e5d5922ce1f04b3c48a907097ebe585160c3c166.png)

---

<div class="post-metadata">

**Author:** ![shtirlic](https://yyz1.discourse-cdn.com/flex029/user_avatar/forum.clockworkpi.com/shtirlic/32/11561_2.png) [@shtirlic](https://forum.clockworkpi.com/u/shtirlic)\
**Post date:** [August 14, 2025, 7:44am UTC](https://forum.clockworkpi.com/t/nuttx-rtos-on-picocalc/16783/3 "2025-08-14T07:44:55Z")

</div>

Update: my RISC-V port merged to NuttX, so I continue to expand picocalc-nx to new features and update custom UI shell.

> **[✨ More choices for using the open-hardware Hazard3 RISC-V cores on Raspberry...](https://www.linkedin.com/posts/raspberrypi_more-choices-for-using-the-open-hardware-activity-7361336276987355136-hKjF)**
>
> ✨ More choices for using the open-hardware Hazard3 RISC-V cores on Raspberry Pi Pico 2 ✨

 ![image](https://canada1.discourse-cdn.com/flex029/uploads/clockworkpi/original/2X/1/127a8f31c7b22e2a7b28c56458b408062c2863e5.jpeg)

---

<div class="post-metadata">

**Author:** ![JackCarterSmith](https://yyz1.discourse-cdn.com/flex029/user_avatar/forum.clockworkpi.com/jackcartersmith/32/11509_2.png) [@JackCarterSmith](https://forum.clockworkpi.com/u/JackCarterSmith)\
**Post date:** [August 15, 2025, 3:51pm UTC](https://forum.clockworkpi.com/t/nuttx-rtos-on-picocalc/16783/4 "2025-08-15T15:51:07Z")

</div>

Knock, knock,  
Wake up NEO2350!

---

<div class="post-metadata">

**Author:** ![BlairLeduc](https://yyz1.discourse-cdn.com/flex029/user_avatar/forum.clockworkpi.com/blairleduc/32/11808_2.png) [@BlairLeduc](https://forum.clockworkpi.com/u/BlairLeduc)\
**Post date:** [August 15, 2025, 6:32pm UTC](https://forum.clockworkpi.com/t/nuttx-rtos-on-picocalc/16783/5 "2025-08-15T18:32:07Z")

</div>

I am very interested in this project.

Unfortunately, it obliterates uf2loader by @pelrun.

Since I switch between uf2’s on the fly (on the PicoCalc) using uf2loader, I really cannot get into this project. ☹ I would really love to, though.

---

<div class="post-metadata">

**Author:** ![JackCarterSmith](https://yyz1.discourse-cdn.com/flex029/user_avatar/forum.clockworkpi.com/jackcartersmith/32/11509_2.png) [@JackCarterSmith](https://forum.clockworkpi.com/u/JackCarterSmith)\
**Post date:** [August 15, 2025, 9:42pm UTC](https://forum.clockworkpi.com/t/nuttx-rtos-on-picocalc/16783/6 "2025-08-15T21:42:19Z")

</div>

> [@NuttX RTOS on PicoCalc](https://forum.clockworkpi.com/t/nuttx-rtos-on-picocalc/16783/5):
>
> I am very interested in this project. Unfortunately, it obliterates uf2loader by @pelrun. Since I switch between uf2’s on the fly (on the PicoCalc) using uf2loader, I really cannot get into this project. frowning I would really love to, though.

NuttX mess up with the bootloader loaded in RAM?  
That’s good to know indeed.

---

<div class="post-metadata">

**Author:** ![BlairLeduc](https://yyz1.discourse-cdn.com/flex029/user_avatar/forum.clockworkpi.com/blairleduc/32/11808_2.png) [@BlairLeduc](https://forum.clockworkpi.com/u/BlairLeduc)\
**Post date:** [August 15, 2025, 10:43pm UTC](https://forum.clockworkpi.com/t/nuttx-rtos-on-picocalc/16783/7 "2025-08-15T22:43:25Z")

</div>

I am not sure what is happening. The Nuttx uf2 may be over writing the flash partition table?

Honestly, I am not an expert on the flash stuff and the RP2350. @pelrun might have an idea?

---

<div class="post-metadata">

**Author:** ![pelrun](https://yyz1.discourse-cdn.com/flex029/user_avatar/forum.clockworkpi.com/pelrun/32/12323_2.png) [@pelrun](https://forum.clockworkpi.com/u/pelrun)\
**Post date:** [August 16, 2025, 5:58am UTC](https://forum.clockworkpi.com/t/nuttx-rtos-on-picocalc/16783/8 "2025-08-16T05:58:15Z")

</div>

I’ll add this to the list of apps to investigate - it’ll be using the old flash API, most likely.

---

<div class="post-metadata">

**Author:** ![shtirlic](https://yyz1.discourse-cdn.com/flex029/user_avatar/forum.clockworkpi.com/shtirlic/32/11561_2.png) [@shtirlic](https://forum.clockworkpi.com/u/shtirlic)\
**Post date:** [August 16, 2025, 5:58am UTC](https://forum.clockworkpi.com/t/nuttx-rtos-on-picocalc/16783/9 "2025-08-16T05:58:46Z")

</div>

I think I can make a build with modified linker script to be compatible with SD boot  
as I can see the difference only on flash position leaving it to bootloader

```auto
FLASH(rx) : ORIGIN = 0x10000000 + 256k, LENGTH = 4096k - 256k

```

this is original

```auto
FLASH(rx) : ORIGIN = 0x10000000, LENGTH = 4096k

```

---

<div class="post-metadata">

**Author:** ![pelrun](https://yyz1.discourse-cdn.com/flex029/user_avatar/forum.clockworkpi.com/pelrun/32/12323_2.png) [@pelrun](https://forum.clockworkpi.com/u/pelrun)\
**Post date:** [August 16, 2025, 6:00am UTC](https://forum.clockworkpi.com/t/nuttx-rtos-on-picocalc/16783/10 "2025-08-16T06:00:55Z")

</div>

No! 😃

Changing the linker script was required for SD Boot, not UF2 Loader.

Don’t worry about trying to figure this out yourself - I’m doing a bunch of sneaky things under the hood and it’s my job to figure out how to get everything to play nice.

(also, this is the first thing that seems to be built for RISC-V and I really want to see that working!)

---

<div class="post-metadata">

**Author:** ![pelrun](https://yyz1.discourse-cdn.com/flex029/user_avatar/forum.clockworkpi.com/pelrun/32/12323_2.png) [@pelrun](https://forum.clockworkpi.com/u/pelrun)\
**Post date:** [August 16, 2025, 6:38am UTC](https://forum.clockworkpi.com/t/nuttx-rtos-on-picocalc/16783/11 "2025-08-16T06:38:41Z")

</div>

WHOA this is WEIRD.

The bootloader isn’t actually overwritten at all, but for some reason it can no longer read the sd card!

NuttX, _what did you do!??!_ 😅

(edit: even weirder, if I get picotool to erase the partition, it starts working again! Nothing in the partition should be affecting this stage of a cold boot!)

---

<div class="post-metadata">

**Author:** ![shtirlic](https://yyz1.discourse-cdn.com/flex029/user_avatar/forum.clockworkpi.com/shtirlic/32/11561_2.png) [@shtirlic](https://forum.clockworkpi.com/u/shtirlic)\
**Post date:** [August 16, 2025, 7:00am UTC](https://forum.clockworkpi.com/t/nuttx-rtos-on-picocalc/16783/12 "2025-08-16T07:00:49Z")

</div>

There is some complicated process of choosing what arch should be run from the start, risc-v or arm. Maybe if they mixed arch arm and risc-v there is some issue.

---

<div class="post-metadata">

**Author:** ![shtirlic](https://yyz1.discourse-cdn.com/flex029/user_avatar/forum.clockworkpi.com/shtirlic/32/11561_2.png) [@shtirlic](https://forum.clockworkpi.com/u/shtirlic)\
**Post date:** [August 16, 2025, 7:05am UTC](https://forum.clockworkpi.com/t/nuttx-rtos-on-picocalc/16783/13 "2025-08-16T07:05:29Z")

</div>

Made 0.4.0 RISC-V Pico 2 pre-release uf2

> **[Release v0.4.0 RISC-V Pico 2 · shtirlic/picocalc-nx](https://github.com/shtirlic/picocalc-nx/releases/tag/v0.4.0)**
>
> Full Changelog: v0.3.0...v0.4.0

---

<div class="post-metadata">

**Author:** ![JackCarterSmith](https://yyz1.discourse-cdn.com/flex029/user_avatar/forum.clockworkpi.com/jackcartersmith/32/11509_2.png) [@JackCarterSmith](https://forum.clockworkpi.com/u/JackCarterSmith)\
**Post date:** [August 16, 2025, 1:27pm UTC](https://forum.clockworkpi.com/t/nuttx-rtos-on-picocalc/16783/14 "2025-08-16T13:27:37Z")

</div>

I don’t remember if you can “switch” the rp2350 arch on the fly (without through a power reset).  
Technically, I would say no, since it is possible to load RISC code on the second core when the first is running in ARM mode…

---

<div class="post-metadata">

**Author:** ![shtirlic](https://yyz1.discourse-cdn.com/flex029/user_avatar/forum.clockworkpi.com/shtirlic/32/11561_2.png) [@shtirlic](https://forum.clockworkpi.com/u/shtirlic)\
**Post date:** [August 16, 2025, 2:09pm UTC](https://forum.clockworkpi.com/t/nuttx-rtos-on-picocalc/16783/15 "2025-08-16T14:09:42Z")

</div>

Yes, reset is needed but you can use 2 different cores in the same time.

> 3.9.2. Mixed Architecture Combinations  
> The ARCHSEL register has one bit for each processor socket, so it is possible to request mixed combinations of Arm  
> and RISC-V processors: either Arm core 0 and RISC-V core 1, or RISC-V core 0 and Arm core 1.

So it’s possible to have ARM image and second RISC-V image, for example run the ARM image then boot to second RISC-V image.

---

<div class="post-metadata">

**Author:** ![pelrun](https://yyz1.discourse-cdn.com/flex029/user_avatar/forum.clockworkpi.com/pelrun/32/12323_2.png) [@pelrun](https://forum.clockworkpi.com/u/pelrun)\
**Post date:** [August 16, 2025, 3:59pm UTC](https://forum.clockworkpi.com/t/nuttx-rtos-on-picocalc/16783/16 "2025-08-16T15:59:21Z")

</div>

The problem is happening long before the system is asked to start executing the application, so what arch it’s using shouldn’t really matter (and for the record I was using the ARM uf2 anyway.)

I suspect a bug in my implementation is spuriously looking deep into the application partition, and just happens to work by coincidence when that location is empty.

---

<div class="post-metadata">

**Author:** ![pelrun](https://yyz1.discourse-cdn.com/flex029/user_avatar/forum.clockworkpi.com/pelrun/32/12323_2.png) [@pelrun](https://forum.clockworkpi.com/u/pelrun)\
**Post date:** [August 17, 2025, 1:10pm UTC](https://forum.clockworkpi.com/t/nuttx-rtos-on-picocalc/16783/17 "2025-08-17T13:10:58Z")

</div>

Yeah, this is still being weird. I’ve replaced my low-level sd driver with ChaN’s, and my Sandisk cards work, but the Lexar still fails (and in a weird way - it claims everything is fine but gives back all zeros when asked to read a sector.)

This’ll probably be where I have to leave it for v2.1 - I need to hook up the logic analyzer to dig into it further, but that’ll take some time.

---

<div class="post-metadata">

**Author:** ![BlairLeduc](https://yyz1.discourse-cdn.com/flex029/user_avatar/forum.clockworkpi.com/blairleduc/32/11808_2.png) [@BlairLeduc](https://forum.clockworkpi.com/u/BlairLeduc)\
**Post date:** [August 17, 2025, 2:42pm UTC](https://forum.clockworkpi.com/t/nuttx-rtos-on-picocalc/16783/18 "2025-08-17T14:42:15Z")

</div>

Until I worked on my own FAT32 code, I never truly appreciated that SD Cards are not media, but a hardware device with which one communicates and every manufacturer or card has its own quirks.

The current initialisation code I now have works with the smattering of SD Cards I own, but it didn’t start that way.

---

<div class="post-metadata">

**Author:** ![shtirlic](https://yyz1.discourse-cdn.com/flex029/user_avatar/forum.clockworkpi.com/shtirlic/32/11561_2.png) [@shtirlic](https://forum.clockworkpi.com/u/shtirlic)\
**Post date:** [August 18, 2025, 3:51am UTC](https://forum.clockworkpi.com/t/nuttx-rtos-on-picocalc/16783/19 "2025-08-18T03:51:30Z")

</div>

JFYI, I managed to get QSPI working on PSRAM by modified version of rp2040-psram project, so we will get the speed bump for PSRAM.

The only thing that not working right now is variable bytes write/reads, reads/writes up to 128bits is working now.

Update: Initially wrote about SD card, but then remembered it’s for PSRAM 😉

Here is the stats:

All tests done on on **RISC-V core**

Full chart without 8bit test for 150mhz sysclock

```auto
Testing PSRAM(SPI)...
16 bit: PSRAM write 8MB in 3299527 us, 2542366 B/s
16 bit: PSRAM read 8MB in 6263501 us, 1339284 B/s
32 bit: PSRAM write 8MB in 2306877 us, 3636348 B/s
32 bit: PSRAM read 8MB in 3704978 us, 2264145 B/s
128 bit: PSRAM write 8MB in 1250219 us, 6709711 B/s
128 bit: PSRAM read 8MB in 1613943 us, 5197586 B/s
Testing PSRAM(QSPI)...
16 bit: PSRAM write 8MB in 2963981 us, 2830182 B/s
16 bit: PSRAM read 8MB in 4026536 us, 2083331 B/s
32 bit: PSRAM write 8MB in 1761612 us, 4761893 B/s
32 bit: PSRAM read 8MB in 2278914 us, 3680967 B/s
128 bit: PSRAM write 8MB in 774859 us, 10825980 B/s
128 bit: PSRAM read 8MB in 768089 us, 10921401 B/s

```

**Key Gains:**

- **Write speeds:** 11% to 61% improvement

- **Read speeds:** 55% to 110% improvement

This is with sysclock 200mhz

```auto
Testing PSRAM(SPI)...
16 bit: PSRAM write 8MB in 2474646 us, 3389821 B/s
16 bit: PSRAM read 8MB in 4697627 us, 1785711 B/s
32 bit: PSRAM write 8MB in 1730158 us, 4848463 B/s
32 bit: PSRAM read 8MB in 2778734 us, 3018859 B/s
128 bit: PSRAM write 8MB in 937664 us, 8946284 B/s
128 bit: PSRAM read 8MB in 1210457 us, 6930116 B/s
Testing PSRAM(QSPI)...
16 bit: PSRAM write 8MB in 2222985 us, 3773578 B/s
16 bit: PSRAM read 8MB in 3019904 us, 2777773 B/s
32 bit: PSRAM write 8MB in 1321209 us, 6349191 B/s
32 bit: PSRAM read 8MB in 1709185 us, 4907958 B/s
128 bit: PSRAM write 8MB in 581143 us, 14434671 B/s
128 bit: PSRAM read 8MB in 576068 us, 14561836 B/s

```

sysclock 230mhz

```auto
Testing PSRAM(SPI)...
16 bit: PSRAM write 8MB in 2151866 us, 3898294 B/s
16 bit: PSRAM read 8MB in 4084893 us, 2053568 B/s
32 bit: PSRAM write 8MB in 1504485 us, 5575734 B/s
32 bit: PSRAM read 8MB in 2416290 us, 3471689 B/s
128 bit: PSRAM write 8MB in 815360 us, 10288226 B/s
128 bit: PSRAM read 8MB in 1052571 us, 7969636 B/s
Testing PSRAM(QSPI)...
16 bit: PSRAM write 8MB in 1933030 us, 4339616 B/s
16 bit: PSRAM read 8MB in 2626002 us, 3194440 B/s
32 bit: PSRAM write 8MB in 1148877 us, 7301572 B/s
32 bit: PSRAM read 8MB in 1486247 us, 5644154 B/s
128 bit: PSRAM write 8MB in 505343 us, 16599830 B/s
128 bit: PSRAM read 8MB in 500928 us, 16746135 B/s

```

If I set higher sysclock I got some failures and bitflips while reading

PS I guess we can use PSRAM as framebuffer?

```auto
320 × 320 × 2 bytes × 30 fps = 6.14 MB/s
320 × 320 × 2 bytes × 45 fps = 9.22 MB/s
320 × 320 × 2 bytes × 60 fps = 12.29 MB/s

```

Here is the draft pull to upstream [Initial QSPI(QPI) support by shtirlic · Pull Request #15 · polpo/rp2040-psram · GitHub](https://github.com/polpo/rp2040-psram/pull/15)

---

<div class="post-metadata">

**Author:** ![JackCarterSmith](https://yyz1.discourse-cdn.com/flex029/user_avatar/forum.clockworkpi.com/jackcartersmith/32/11509_2.png) [@JackCarterSmith](https://forum.clockworkpi.com/u/JackCarterSmith)\
**Post date:** [August 18, 2025, 1:02pm UTC](https://forum.clockworkpi.com/t/nuttx-rtos-on-picocalc/16783/20 "2025-08-18T13:02:03Z")

</div>

> [@shtirlic](#):
>
> PS I guess we can use PSRAM as framebuffer?
> 
> ```auto
> 320 × 320 × 2 bytes × 30 fps = 6.14 MB/s
> 320 × 320 × 2 bytes × 45 fps = 9.22 MB/s
> 320 × 320 × 2 bytes × 60 fps = 12.29 MB/s
> 
> ```

We’ve talked a little about this possibility [here](https://forum.clockworkpi.com/t/framebuffer-on-psram/16667) and [here](https://forum.clockworkpi.com/t/ideas-for-improving-lcd-speed/17159).

The main issue is the maximum LCD SPI speed, which seem to be 62.5 MHz (the maximum for the RP2350, 75 MHz in overclock, can’t say the screen don’t have some artefacts).  
So the maximum bitrate shoul be around 7.8 MB/s. 30 or 45 FPS seem feasable, but the 60 is far away unfortunatly. 😕

[Next page](https://forum.clockworkpi.com/t/nuttx-rtos-on-picocalc/16783.md?page=2)
