Read the articles...
-
"Apache NuttX RTOS on Sophgo SG2000 RISC-V SoC (Milk-V Duo S SBC)"
-
"Daily Automated Testing for Milk-V Duo S RISC-V SBC (IKEA TRETAKT / Apache NuttX RTOS)"
-
"RISC-V Emulator for Sophgo SG2000 SoC (Pine64 Oz64 / Milk-V Duo S)"
UPDATE: Sophgo SG2000 is officially supported by NuttX Mainline! (Including Milk-V Duo S and Pine64 Oz64)
Here's how everything got started...
64-bit RISC-V Sophgo SG2000 SoC ... Will it boot Apache NuttX RTOS? 🤔 (T-Head C906 / Milk-V Duo S)
Let's find out!
Is this a sponsored review?
I was given a Milk-V Duo S, and I bought another. So it cancels out, I guess?
Why are we doing all this?
-
We hear that there will be plenty of interesting new SBCs based on Sophgo SG2000 and SG2002. Perfect for NuttX!
-
NuttX has been ported from QEMU RISC-V to Star64 JH7110 to Ox64 BL808 and now Sophgo SG2000. Let's find the most efficient way to port NuttX to new RISC-V Devices!
NuttX for SG2000 is now built automatically every day via GitHub Actions.
The Daily Releases are available here...
-
(Skip the Special Builds)
nuttx.hash contains the Commit Hash of the NuttX Kernel and NuttX Apps repos...
NuttX Source: https://github.com/apache/nuttx/tree/28ae3b38499c6c7bac0d432658da263b9eab2981
NuttX Apps: https://github.com/apache/nuttx-apps/tree/00f98947786892eaabf60b2e61fad553aee1c36c
The GitHub Actions Workflow is here...
We are now running Daily Automated Testing of NuttX on a Real Milk-V Duo S SBC...
-
Download the Daily Build to TFTP Server
-
Power on our SBC with an IKEA Smart Power Plug via Home Assistant
-
Our SBC boots the Daily Build over TFTP
-
Capture the Automated Testing Log and write to the Release Notes
We are also running Daily Automated Testing with an SG2000 Emulator (in GitHub Actions)...
Read the article...
Sophgo SG2000 SoC has a fascinating mix of 64-bit RISC-V Cores (Arm too)...
-
Main Processor: 64-bit RISC-V Core
T-Head C906 (1.0 GHz)
(For NuttX and Linux)
-
Co-Processor: 64-bit RISC-V Core
T-Head C906 (700 MHz)
(No Cache)
-
Alt-Main Processor: 64-bit Arm Core
Cortex-A53 (1.0 GHz)
Plus a Low-Power 8051 MCU (for wakeup duties) and a Tensor Processing Unit (for image recognition, not LLM)
Whoa RISC-V AND Arm CPUs in a single SoC?
Actually there's a Hardware Switch that selects the Main CPU: RISC-V OR Arm.
(Don't let yer pet hamster flip it... It will get super frustrating!)
Read the article...
What happens if we boot Milk-V Duo S? Fresh from the box?
Connect our USB UART Dongle like so (pic above)...
Milk-V Duo S | USB UART |
---|---|
GND (Pin 6) | GND |
TX (Pin 8) | RX |
RX (Pin 10) | TX |
USB UART Dongle must be CP2102, it doesn't like CH340G 😬
Flip the switch so it's set to "RV
" (RISC-V) instead of "Arm
"...
Power up the board via the USB-C Port. We should see...
https://gist.github.com/lupyuen/a7c3af98be36dcd5cc5b45f5aadc5d16
C.SCS/0/0.WD.URPL.USBI.USBEF.BS/EMMC.EMI/25000000/12000000. E:bm_emmc_send_cmd_without_data CMD: 1 INT_STAT: 0x E:bm_emmc_send_cmd_without_data CMD: 0 INT_STAT: 0x E:eMMC init failed, 3
E:eMMC initializing failed
PS. E:bm_emmc_send_cmd_without_data CMD: 6 INT_STAT: 0x E:load param1 (-5)
E:Boot failed (8).
E:RESET:plat/mars/platform.c:114
WD.C.SCS/0/0.WD.URPL.USBI.USBEF.BS/EMMC.EMI/25000000/12000000. E:bm_emmc_send_cmd_without_data CMD: 1 INT_STAT: 0x E:bm_emmc_send_cmd_without_data CMD: 0 INT_STAT: 0x E:eMMC init failed, 3
E:eMMC initializing failed
If we see B.SCS
instead of C.SCS
...
https://gist.github.com/lupyuen/d55b77a51ee8b258d6d1c0799770742a
B.SCS/0/0.WD.URPL.USBI.USBEF.BS/EMMC.EMI/25000000/12000000.
Nope we're in Arm Mode! Flip the switch back to RISC-V!
If we use CH340G (instead of CP2102): UART Output will be garbled (happens on Linux and macOS, maybe Windows too)...
https://gist.github.com/lupyuen/1d5ba1b2a47c110ee7ff265102b1aae5
Milk-V Duo S doesn't ship with U-Boot Bootloader preinstalled in Flash Memory. We'll need U-Boot on MicroSD...
Read the article...
Milk-V Duo S won't boot without MicroSD. How now?
Let's boot Linux on MicroSD, thanks to the awesome work by Justin Hammond (Fishwaldo)...
We download the Latest Release for Milk-V Duo S (SG2000)...
$ brew install lz4
$ lz4 ~/Downloads/duos_sd.img.lz4
## TODO: Write duos_sd.img to MicroSD with Balena Etcher
→ ls -l /Volumes/boot
total 17488
-rwxrwxrwx 1 Luppy staff 3494900 Apr 24 11:33 System.map-5.10.4-20240329-1+
-rwxrwxrwx 1 Luppy staff 125534 Apr 24 11:33 config-5.10.4-20240329-1+
drwxrwxrwx 1 Luppy staff 2048 Apr 24 11:33 extlinux
drwxrwxrwx 1 Luppy staff 2048 Apr 24 11:33 fdt
-rwxrwxrwx 1 Luppy staff 388608 Apr 24 11:33 fip.bin
-rwxrwxrwx 1 Luppy staff 4937389 Apr 24 11:33 vmlinuz-5.10.4-20240329-1+
→ ls -l /Volumes/boot/extlinux
total 4
-rwxrwxrwx 1 Luppy staff 749 Apr 24 11:33 extlinux.conf
→ ls -l /Volumes/boot/fdt
total 4
drwxrwxrwx 1 Luppy staff 2048 Apr 24 11:33 linux-image-duos-5.10.4-20240329-1+
→ ls -l /Volumes/boot/fdt/linux-image-duos-5.10.4-20240329-1+
total 44
-rwxrwxrwx 1 Luppy staff 21575 Apr 24 11:33 cv181x_milkv_duos_sd.dtb
Here's the Boot Config...
→ cat /Volumes/boot/extlinux/extlinux.conf
## /boot/extlinux/extlinux.conf
##
## IMPORTANT WARNING
##
## The configuration of this file is generated automatically.
## Do not edit this file manually, use: u-boot-update
default l0
menu title U-Boot menu
prompt 1
timeout 50
label l0
menu label Debian GNU/Linux trixie/sid 5.10.4-20240329-1+
linux /vmlinuz-5.10.4-20240329-1+
fdtdir /fdt/linux-image-duos-5.10.4-20240329-1+/
append root=/dev/root console=ttyS0,115200 earlycon=sbi root=/dev/mmcblk0p2 rootwait rw
label l0r
menu label Debian GNU/Linux trixie/sid 5.10.4-20240329-1+ (rescue target)
linux /vmlinuz-5.10.4-20240329-1+
fdtdir /fdt/linux-image-duos-5.10.4-20240329-1+/
append root=/dev/root console=ttyS0,115200 earlycon=sbi root=/dev/mmcblk0p2 rootwait rw single
Yep Linux boots OK on Milk-V Duo S! (Together with OpenSBI and U-Boot Bootloader)
https://gist.github.com/lupyuen/01d409b7bde9607a96cd4d460e53330a
OpenSBI v0.9
____ _____ ____ _____
/ __ \ / ____| _ \_ _|
| | | |_ __ ___ _ __ | (___ | |_) || |
| | | | '_ \ / _ \ '_ \ \___ \| _ < | |
| |__| | |_) | __/ | | |____) | |_) || |_
\____/| .__/ \___|_| |_|_____/|____/_____|
| |
|_|
Platform Name : Milk-V DuoS
Platform Features : mfdeleg
Platform HART Count : 1
Platform IPI Device : clint
Platform Timer Device : clint
Platform Console Device : uart8250
Platform HSM Device : ---
Platform SysReset Device : ---
Firmware Base : 0x80000000
Firmware Size : 132 KB
Runtime SBI Version : 0.3
Domain0 Name : root
Domain0 Boot HART : 0
Domain0 HARTs : 0*
Domain0 Region00 : 0x0000000074000000-0x000000007400ffff (I)
Domain0 Region01 : 0x0000000080000000-0x000000008003ffff ()
Domain0 Region02 : 0x0000000000000000-0xffffffffffffffff (R,W,X)
Domain0 Next Address : 0x0000000080200020
Domain0 Next Arg1 : 0x0000000080080000
Domain0 Next Mode : S-mode
Domain0 SysReset : yes
Boot HART ID : 0
Boot HART Domain : root
Boot HART ISA : rv64imafdcvsux
Boot HART Features : scounteren,mcounteren,time
Boot HART PMP Count : 16
Boot HART PMP Granularity : 4096
Boot HART PMP Address Bits: 38
Boot HART MHPM Count : 8
Boot HART MHPM Count : 8
Boot HART MIDELEG : 0x0000000000000222
Boot HART MEDELEG : 0x000000000000b109
...
Debian GNU/Linux trixie/sid duos ttyS0
duos login:
Let's dump the U-Boot Config...
https://gist.github.com/lupyuen/000b55a46336cddf217a589f469d60e2
U-Boot 2021.10-ga57aa1f2-dirty (May 07 2024 - 08:13:12 +0000) cvitek_cv181x
DRAM: 510 MiB
gd->relocaddr=0x9fbc6000. offset=0x1f9c6000
set_rtc_register_for_power
MMC: cv-sd@4310000: 0, wifi-sd@4320000: 1
Loading Environment from FAT... mmc1 : finished tuning, code:53
OK
In: serial
Out: serial
Err: serial
Net:
Warning: ethernet@4070000 (eth0) using random MAC address - 0a:fa:e9:48:cc:c1
eth0: ethernet@4070000
Hit any key to stop autoboot: 0
cv181x_c906# printenv
arch=riscv
baudrate=115200
board=mars
board_name=mars
boot_a_script=load ${devtype} ${devnum}:${distro_bootpart} ${scriptaddr} ${prefix}${script}; source ${scriptaddr}
boot_efi_binary=load ${devtype} ${devnum}:${distro_bootpart} ${kernel_addr_r} efi/boot/bootriscv64.efi; if fdt addr ${fdt_addr_r}; then bootefi ${kernel_addr_r} ${fdt_addr_r};else bootefi ${kernel_addr_r} ${fdtcontroladdr};fi
boot_efi_bootmgr=if fdt addr ${fdt_addr_r}; then bootefi bootmgr ${fdt_addr_r};else bootefi bootmgr;fi
boot_extlinux=sysboot ${devtype} ${devnum}:${distro_bootpart} any ${scriptaddr} ${prefix}${boot_syslinux_conf}
boot_prefixes=/ /boot/
boot_script_dhcp=boot.scr.uimg
boot_scripts=boot.scr.uimg boot.scr
boot_syslinux_conf=extlinux/extlinux.conf
boot_targets=mmc0 dhcp pxe
bootcmd=run distro_bootcmd || run sdboot || run sdbootauto
bootcmd_dhcp=devtype=dhcp; if dhcp ${scriptaddr} ${boot_script_dhcp}; then source ${scriptaddr}; fi;setenv efi_fdtfile ${fdtfile}; setenv efi_old_vci ${bootp_vci};setenv efi_old_arch ${bootp_arch};setenv bootp_vci PXEClient:Arch:00027:UNDI:003000;setenv bootp_arch 0x1b;if dhcp ${kernel_addr_r}; then tftpboot ${fdt_addr_r} dtb/${efi_fdtfile};if fdt addr ${fdt_addr_r}; then bootefi ${kernel_addr_r} ${fdt_addr_r}; else bootefi ${kernel_addr_r} ${fdtcontroladdr};fi;fi;setenv bootp_vci ${efi_old_vci};setenv bootp_arch ${efi_old_arch};setenv efi_fdtfile;setenv efi_old_arch;setenv efi_old_vci;
bootcmd_mmc0=devnum=0; run mmc_boot
bootcmd_pxe=dhcp; if pxe get; then pxe boot; fi
bootdelay=1
consoledev=ttyS0
cpu=generic
distro_bootcmd=for target in ${boot_targets}; do run bootcmd_${target}; done
efi_dtb_prefixes=/ /dtb/ /dtb/current/
fdt_addr_r=0x81200000
fdtcontroladdr=9f27f810
fdtfile=cv181x_milkv_duos_sd.dtb
fdtoverlay_addr_r=0x81300000
gatewayip=192.168.0.11
ipaddr=192.168.0.3
kernel_addr_r=0x80200000
kernel_comp_addr_r=0x81800000
kernel_comp_size=0x1000000
load_efi_dtb=load ${devtype} ${devnum}:${distro_bootpart} ${fdt_addr_r} ${prefix}${efi_fdtfile}
mmc_boot=if mmc dev ${devnum}; then devtype=mmc; run scan_dev_for_boot_part; fi
netdev=eth0
netmask=255.255.255.0
othbootargs=earlycon=sbi riscv.fwsz=0x80000 loglevel=9
pxefile_addr_r=0x81400000
ramdisk_addr_r=0x84000000
root=root=/dev/mmcblk0p2 rootwait rw
scan_dev_for_boot=echo Scanning ${devtype} ${devnum}:${distro_bootpart}...; for prefix in ${boot_prefixes}; do run scan_dev_for_extlinux; run scan_dev_for_scripts; done;run scan_dev_for_efi;
scan_dev_for_boot_part=part list ${devtype} ${devnum} -bootable devplist; env exists devplist || setenv devplist 1; for distro_bootpart in ${devplist}; do if fstype ${devtype} ${devnum}:${distro_bootpart} bootfstype; then run scan_dev_for_boot; fi; done; setenv devplist
scan_dev_for_efi=setenv efi_fdtfile ${fdtfile}; for prefix in ${efi_dtb_prefixes}; do if test -e ${devtype} ${devnum}:${distro_bootpart} ${prefix}${efi_fdtfile}; then run load_efi_dtb; fi;done;run boot_efi_bootmgr;if test -e ${devtype} ${devnum}:${distro_bootpart} efi/boot/bootriscv64.efi; then echo Found EFI removable media binary efi/boot/bootriscv64.efi; run boot_efi_binary; echo EFI LOAD FAILED: continuing...; fi; setenv efi_fdtfile
scan_dev_for_extlinux=if test -e ${devtype} ${devnum}:${distro_bootpart} ${prefix}${boot_syslinux_conf}; then echo Found ${prefix}${boot_syslinux_conf}; run boot_extlinux; echo SCRIPT FAILED: continuing...; fi
scan_dev_for_scripts=for script in ${boot_scripts}; do if test -e ${devtype} ${devnum}:${distro_bootpart} ${prefix}${script}; then echo Found U-Boot script ${prefix}${script}; run boot_a_script; echo SCRIPT FAILED: continuing...; fi; done
scriptaddr=0x81500000
sdboot=setenv bootargs ${reserved_mem} ${root} ${mtdparts} console=$consoledev,$baudrate $othbootargs;echo Boot from SD dev ${sddev} ...;mmc dev ${sddev} && fatload mmc ${sddev} ${uImage_addr} boot.sd;if test $? -eq 0; then bootm ${uImage_addr}#config-cv181x_milkv_duos_sd;fi;
sdbootauto=cvi_sd_boot;setenv bootargs ${reserved_mem} ${root} ${mtdparts} console=$consoledev,$baudrate $othbootargs;echo Boot from SD dev ${sddev} auto ...;mmc dev ${sddev} && fatload mmc ${sddev} ${uImage_addr} boot.sd;if test $? -eq 0; then bootm ${uImage_addr}#config-cv181x_milkv_duos_sd;fi;
sddev=0
serverip=192.168.56.101
stderr=serial
stdin=serial
stdout=serial
uImage_addr=0x81800000
update_addr=0x9fe00000
vendor=cvitek
Environment size: 4333/131068 bytes
Aha Ethernet Driver is available in U-Boot. Which means we can boot NuttX over TFTP yay!
$ net list
eth0 : ethernet@4070000 00:00:00:00:00:00 active
(See below for the U-Boot Commands)
Here's another Linux Image: https://github.com/logicethos/Milk-V_Duo_Linux2SD
Read the article...
We'll port NuttX by booting over TFTP, but let's test TFTP first. Here are the steps to boot Milk-V Duo S over TFTP...
https://lupyuen.github.io/articles/tftp#configure-u-boot-for-tftp
## Set the U-Boot TFTP Server
setenv tftp_server 192.168.31.10
printenv tftp_server
## If Initial RAM Disk is needed (like for Linux)...
## Set the RAM Disk Size (assume the max)
## setenv ramdisk_size 0x1000000
## printenv ramdisk_size
## Save the U-Boot Config for future reboots
saveenv
We load the NuttX Image over TFTP...
## Fetch the IP Address over DHCP
## Load the NuttX Image from TFTP Server
## kernel_addr_r=0x80200000
## tftp_server=192.168.x.x
dhcp ${kernel_addr_r} ${tftp_server}:Image-sg2000
## Load the Device Tree from TFTP Server
## fdt_addr_r=0x81200000
## TODO: Fix the Device Tree, it's not needed by NuttX
tftpboot ${fdt_addr_r} ${tftp_server}:jh7110-star64-pine64.dtb
## Set the RAM Address of Device Tree
## fdt_addr_r=0x81200000
## TODO: Fix the Device Tree, it's not needed by NuttX
fdt addr ${fdt_addr_r}
## If Initial RAM Disk is needed...
## Load the Intial RAM Disk from TFTP Server
## ramdisk_addr_r=0x81600000
## tftpboot ${ramdisk_addr_r} ${tftp_server}:initrd
## Boot the NuttX Image with the Device Tree
## kernel_addr_r=0x80200000
## fdt_addr_r=0x81200000
## TODO: Fix the Device Tree, it's not needed by NuttX
booti ${kernel_addr_r} - ${fdt_addr_r}
## For Linux: We need the RAM Disk Address
## ramdisk_addr_r=0x81600000
## ramdisk_size=0x1000000
## booti ${kernel_addr_r} ${ramdisk_addr_r}:${ramdisk_size} ${fdt_addr_r}
Which becomes this, mashed up in a single line...
## Boot NuttX Image over TFTP
dhcp ${kernel_addr_r} ${tftp_server}:Image-sg2000 ; tftpboot ${fdt_addr_r} ${tftp_server}:jh7110-star64-pine64.dtb ; fdt addr ${fdt_addr_r} ; booti ${kernel_addr_r} - ${fdt_addr_r}
Now we automate the TFTP Booting for future reboots...
## Add the Boot Command for TFTP
setenv bootcmd_tftp 'dhcp ${kernel_addr_r} ${tftp_server}:Image-sg2000 ; tftpboot ${fdt_addr_r} ${tftp_server}:jh7110-star64-pine64.dtb ; fdt addr ${fdt_addr_r} ; booti ${kernel_addr_r} - ${fdt_addr_r}'
## Check that it's correct
printenv bootcmd_tftp
## Save it for future reboots
saveenv
## Test the Boot Command for TFTP, then reboot
run bootcmd_tftp
## Remember the Original Boot Targets
setenv orig_boot_targets "$boot_targets"
## Should show `mmc0 dhcp pxe`
printenv orig_boot_targets
## Save it for future reboots
saveenv
## Prepend TFTP to the Boot Targets
setenv boot_targets "tftp $boot_targets"
## Should show `tftp mmc0 dhcp pxe`
printenv boot_targets
## Save it for future reboots
saveenv
What happens when we run it at the U-Boot Command Prompt?
This happens...
$ setenv tftp_server 192.168.31.10
$ printenv tftp_server
tftp_server=192.168.31.10
$ setenv ramdisk_size 0x1000000
$ printenv ramdisk_size
ramdisk_size=0x1000000
$ dhcp ${kernel_addr_r} ${tftp_server}:Image
Speed: 100, full duplex
BOOTP broadcast 1
BOOTP broadcast 2
*** Unhandled DHCP Option in OFFER/ACK: 43
*** Unhandled DHCP Option in OFFER/ACK: 43
DHCP client bound to address 192.168.31.47 (550 ms)
Using ethernet@4070000 device
TFTP from server 192.168.31.10; our IP address is 192.168.31.47
Filename 'Image'.
Load address: 0x80200000
Loading: #################################################################
. 1.2 MiB/s
done
Bytes transferred = 2097800 (200288 hex)
$ tftpboot ${fdt_addr_r} ${tftp_server}:jh7110-star64-pine64.dtb
Speed: 100, full duplex
Using ethernet@4070000 device
TFTP from server 192.168.31.10; our IP address is 192.168.31.47
Filename 'jh7110-star64-pine64.dtb'.
Load address: 0x81200000
Loading: ####
. 1.1 MiB/s
done
Bytes transferred = 50235 (c43b hex)
$ fdt addr ${fdt_addr_r}
$ booti ${kernel_addr_r} - ${fdt_addr_r}
## Flattened Device Tree blob at 81200000
Booting using the fdt blob at 0x81200000
Loading Ramdisk to 9e27f000, end 9f27f000 ... OK
Loading Device Tree to 000000009e26f000, end 000000009e27e43a ... OK
Starting kernel ...
NuttX Kernel hangs, but that's OK! We haven't modified NuttX Kernel for SG2000 yet. Let's print something...
Read the article...
According to the SG2000 Reference Manual, the UART Controller Base Addresses are...
GPIO Module | Base Address |
---|---|
UART0 | 0x04140000 |
UART1 | 0x04150000 |
UART2 | 0x04160000 |
UART3 | 0x04170000 |
UART4 | 0x041C0000 |
RTCSYS_UART | 0x05022000 |
We'll print to UART0 in NuttX.
What UART Controller is inside SG2000 / Milk-V Duo S?
According to the OpenSBI Log above: The UART Controller is uart8250
.
Which is supported by NuttX yay!
Let's modify the NuttX Boot Code to write to the UART Output Register. We'll do this in RISC-V Assembly...
Read the article...
Here's how we print to 8250 UART in RISC-V Assembly...
https://lupyuen.github.io/articles/nuttx2#print-to-qemu-console
From the previous section: SG2000 UART0 Controller is at 0x04140000. We'll write to that address, to print something to Serial Console.
We insert this RISC-V Assembly at the top of the NuttX Boot Code...
https://github.com/lupyuen2/wip-nuttx/blob/sg2000/arch/risc-v/src/bl808/bl808_head.S#L70-L89
/* RISC-V Boot Code for Apache NuttX RTOS */
real_start:
/* Print `123` to UART */
/* Load UART Base Address to Register t0 */
li t0, 0x04140000
/* Load `1` to Register t1 */
li t1, 0x31
/* Store byte from Register t1 to UART Base Address, Offset 0 */
sb t1, 0(t0)
/* Load `2` to Register t1 */
li t1, 0x32
/* Store byte from Register t1 to UART Base Address, Offset 0 */
sb t1, 0(t0)
/* Load `3` to Register t1 */
li t1, 0x33
/* Store byte from Register t1 to UART Base Address, Offset 0 */
sb t1, 0(t0)
Which will print 123
when NuttX boots. Let's build and test this!
Read the article...
Follow these steps to build (work-in-progress) Apache NuttX RTOS for SG2000 / Milk-V Duo S...
## TODO: Set PATH
export PATH="$HOME/riscv64-unknown-elf-toolchain-10.2.0-2020.12.8-x86_64-apple-darwin/bin:$PATH"
set -e # Exit when any command fails
set -x # Echo commands
## Build NuttX
function build_nuttx {
## Go to NuttX Folder
pushd ../nuttx
## Build NuttX
make -j 8
## Return to previous folder
popd
}
## Build Apps Filesystem
function build_apps {
## Go to NuttX Folder
pushd ../nuttx
## Build Apps Filesystem
make -j 8 export
pushd ../apps
./tools/mkimport.sh -z -x ../nuttx/nuttx-export-*.tar.gz
make -j 8 import
popd
## Return to previous folder
popd
}
## Download WIP NuttX for SG2000 (based on Ox64 BL808)
git clone --branch sg2000 \
https://github.com/lupyuen2/wip-nuttx \
nuttx
git clone --branch sg2000 \
https://github.com/lupyuen2/wip-nuttx-apps \
apps
cd nuttx
## Pull updates
git pull && git status && hash1=`git rev-parse HEAD`
pushd ../apps
git pull && git status && hash2=`git rev-parse HEAD`
popd
echo NuttX Source: https://github.com/apache/nuttx/tree/$hash1 >nuttx.hash
echo NuttX Apps: https://github.com/apache/nuttx-apps/tree/$hash2 >>nuttx.hash
## Show the version of GCC
riscv64-unknown-elf-gcc -v
## Configure build
tools/configure.sh ox64:nsh
## Build NuttX
build_nuttx
## Build Apps Filesystem
build_apps
## Generate Initial RAM Disk
genromfs -f initrd -d ../apps/bin -V "NuttXBootVol"
## Show the size
riscv64-unknown-elf-size nuttx
## Export the Binary Image to `nuttx.bin`
riscv64-unknown-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 the config
cp .config nuttx.config
## 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 init disassembly to init.S
riscv64-unknown-elf-objdump \
--syms --source --reloc --demangle --line-numbers --wide \
--debugging \
../apps/bin/init \
>init.S \
2>&1
## Dump the hello disassembly to hello.S
riscv64-unknown-elf-objdump \
--syms --source --reloc --demangle --line-numbers --wide \
--debugging \
../apps/bin/hello \
>hello.S \
2>&1
## Copy NuttX Image to TFTP Server
scp Image tftpserver:/tftpboot/Image-sg2000
ssh tftpserver ls -l /tftpboot/Image-sg2000
Build Outputs are here: https://github.com/lupyuen2/wip-nuttx/releases/tag/sg2000-1
We have copied the NuttX Image to our TFTP Server. Let's boot this on Milk-V Duo S...
Read the article...
Earlier we have...
-
Inserted the RISC-V Boot Code to print
123
(at NuttX Startup) -
Compiled Apache NuttX RTOS for Milk-V Duo S
-
Copied the NuttX Image to our TFTP Server
Let's boot NuttX over TFTP, with a little help from U-Boot Bootloader! Run these commands at the U-Boot Command Prompt...
https://gist.github.com/lupyuen/78b54326daf0894a2c23ab6d2c03456d
## TODO: Change to your TFTP Server IP Address
$ setenv tftp_server 192.168.31.10
## Download NuttX Image from TFTP Server
$ dhcp ${kernel_addr_r} ${tftp_server}:Image-sg2000
Speed: 100, full duplex
BOOTP broadcast 1
BOOTP broadcast 2
*** Unhandled DHCP Option in OFFER/ACK: 43
*** Unhandled DHCP Option in OFFER/ACK: 43
DHCP client bound to address 192.168.31.243 (424 ms)
Using ethernet@4070000 device
TFTP from server 192.168.31.10; our IP address is 192.168.31.243
Filename 'Image-sg2000'.
Load address: 0x80200000
Loading: #################################################################
. 1.2 MiB/s
done
Bytes transferred = 14195281 (d89a51 hex)
## TODO: NuttX doesn't need the Device Tree. Remove this.
$ tftpboot ${fdt_addr_r} ${tftp_server}:jh7110-star64-pine64.dtb
Speed: 100, full duplex
Using ethernet@4070000 device
TFTP from server 192.168.31.10; our IP address is 192.168.31.243
Filename 'jh7110-star64-pine64.dtb'.
Load address: 0x81200000
Loading: ####
. 1.2 MiB/s
done
Bytes transferred = 50235 (c43b hex)
## TODO: NuttX doesn't need the Device Tree. Remove this.
$ fdt addr ${fdt_addr_r}
## Boot NuttX from RAM. RAM Disk Address must be `-`!
$ booti ${kernel_addr_r} - ${fdt_addr_r}
## For Linux: We need the RAM Disk Address
## booti ${kernel_addr_r} ${ramdisk_addr_r}:${ramdisk_size} ${fdt_addr_r}
NuttX boots a tiny bit, and prints 123
yay!
## Boot NuttX from RAM
$ booti ${kernel_addr_r} - ${fdt_addr_r}
## Flattened Device Tree blob at 81200000
Booting using the fdt blob at 0x81200000
Loading Ramdisk to 9fe00000, end 9fe00000 ... OK
Loading Device Tree to 000000009f26f000, end 000000009f27e43a ... OK
Starting kernel ...
123
Our NuttX Boot Code is actually running on SG2000 / Milk-V Duo S!
Coming up...
-
Fix the Boot Address of NuttX, so the rest of NuttX can start
-
Configure the 16550 UART Driver for NuttX, so can see the Console Output
TODO: Can we run expect
with screen
to automate the testing of NuttX on SG2000?
TODO: If we prefer to boot NuttX with MicroSD instead of TFTP, try this MicroSD Multiplexer (USB / FTDI)
Read the article...
From the U-Boot Bootloader Config above: We see that SG2000 boots at this address...
kernel_addr_r=0x80200000
Thus we define the NuttX Memory Map for SG2000 like so...
https://github.com/lupyuen2/wip-nuttx/commit/c6c0bd3882a855420acf0d53fa9d37bbd9d125b2
NuttX Kernel will boot at 0x8020_0000, NuttX Apps will run at Virtual Address 0xC000_0000.
Here's the NuttX Config: boards/risc-v/bl808/ox64/configs/nsh/defconfig
## Kernel RAM
CONFIG_RAM_START=0x80200000
CONFIG_RAM_SIZE=1048576
## Kernel Paged Pool (Allocated to NuttX Apps)
CONFIG_ARCH_PGPOOL_PBASE=0x80600000
CONFIG_ARCH_PGPOOL_VBASE=0x80600000
CONFIG_ARCH_PGPOOL_SIZE=4194304
## Virtual Memory for NuttX App Code
CONFIG_ARCH_TEXT_VBASE=0xC0000000
CONFIG_ARCH_TEXT_NPAGES=128
## Virtual Memory for NuttX App Data
CONFIG_ARCH_DATA_VBASE=0xC0100000
CONFIG_ARCH_DATA_NPAGES=128
## Virtual Memory for NuttX App Heap
CONFIG_ARCH_HEAP_VBASE=0xC0200000
CONFIG_ARCH_HEAP_NPAGES=128
And here's the NuttX Linker Script: boards/risc-v/bl808/ox64/scripts/ld.script
MEMORY {
kflash (rx) : ORIGIN = 0x80200000, LENGTH = 2048K /* w/ cache */
ksram (rwx) : ORIGIN = 0x80400000, LENGTH = 2048K /* w/ cache */
pgram (rwx) : ORIGIN = 0x80600000, LENGTH = 4096K /* w/ cache */
ramdisk (rwx) : ORIGIN = 0x80A00000, LENGTH = 16M /* w/ cache */
}
...
SECTIONS {
. = 0x80200000;
Read the article...
Most RISC-V SBCs (Ox64, Star64) will manage Interrupts with a Platform-Level Interrupt Controller (PLIC). For now, let's disable PLIC in NuttX...
https://github.com/lupyuen2/wip-nuttx/commit/6d66caa1408d7a7d7b21b0e876ce32ceb5b93ec4
Later we'll dump the SG2000 Linux Device Tree to understand the Interrupt Controller.
Read the article...
From the OpenSBI Log above: We see that SG2000 operates with a 8250 UART Controller.
Thus we select the NuttX Driver for 16550 UART, which is compatible with 8250...
https://github.com/lupyuen2/wip-nuttx/commit/8f8831d15d6ddc913e6dd1c6c49fb0067640f6ec
Here's the NuttX Config: boards/risc-v/bl808/ox64/configs/nsh/defconfig
CONFIG_16550_ADDRWIDTH=0
CONFIG_16550_REGINCR=4
CONFIG_16550_UART0=y
CONFIG_16550_UART0_BASE=0x04140000
CONFIG_16550_UART0_CLOCK=23040000
CONFIG_16550_UART0_SERIAL_CONSOLE=y
CONFIG_16550_UART=y
CONFIG_16550_WAIT_LCR=y
CONFIG_SERIAL_UART_ARCH_MMIO=y
Don't update the NuttX Config File directly! We ran make menuconfig
to generate the above file...
## Update NuttX Config
make menuconfig \
&& make savedefconfig \
&& grep -v CONFIG_HOST defconfig \
>boards/risc-v/bl808/ox64/configs/nsh/defconfig
To find the menuconfig settings: Press "/
" and enter the name of the setting, like "16550_ADDRWIDTH". This ensures that the Kconfig Dependencies are correctly updated.
How did we get IRQ 69 for UART?
https://github.com/lupyuen2/wip-nuttx/commit/122c717447f81c310a4fb082101213ad338dfb0e
CONFIG_16550_UART0_IRQ=69
We saw this in the SG2000 Reference Manual...
3.1 Interrupt Subsystem
Table 3.2: Interrupt number and Interrupt source mapping for Master RISCV C906 @ 1.0Ghz
Int #44: UART0
Linx Device Tree also says UART0 IRQ is 44 (0x2C)...
serial@04140000 {
compatible = "snps,dw-apb-uart";
reg = <0x00 0x4140000 0x00 0x1000>;
clock-frequency = <0x17d7840>;
reg-shift = <0x02>;
reg-io-width = <0x04>;
status = "okay";
interrupts = <0x2c 0x04>;
interrupt-parent = <0x04>;
};
Thus we compute NuttX IRQ = 25 + RISC-V IRQ = 69
TODO: Fix the UART Clock: 16550_UART0_CLOCK
Read the article...
For easier troubleshooting: We enable the Logging for NuttX Scheduler and Binary Loader...
https://github.com/lupyuen2/wip-nuttx/commit/4cee79630359f6b31fc9fa40f31bb476c8bc4d47
Here's the NuttX Config: boards/risc-v/bl808/ox64/configs/nsh/defconfig
CONFIG_DEBUG_BINFMT=y
CONFIG_DEBUG_BINFMT_ERROR=y
CONFIG_DEBUG_BINFMT_WARN=y
CONFIG_DEBUG_SCHED=y
CONFIG_DEBUG_SCHED_ERROR=y
CONFIG_DEBUG_SCHED_INFO=y
CONFIG_DEBUG_SCHED_WARN=y
Remember: Always use make menuconfig
to update the settings!
Read the article...
We apply the fixes above. Now NuttX boots some more on RISC-V SG2000 SoC / Milk-V Duo S. And shows our very first NuttX Crash Dump yay!
https://gist.github.com/lupyuen/594f0df20d39001bac171412d594d517
## Flattened Device Tree blob at 81200000
Booting using the fdt blob at 0x81200000
Loading Ramdisk to 9fe00000, end 9fe00000 ... OK
Loading Device Tree to 000000009f26f000, end 000000009f27e43a ... OK
Starting kernel ...
123ABCnx_start: Entry
uart_register: Registering /dev/console
uart_register: Registering /dev/ttyS0
work_start_lowpri: Starting low-priority kernel worker thread(s)
nxtask_activate: lpwork pid=1,TCB=0x80408130
nxtask_activate: AppBringUp pid=2,TCB=0x80408740
nx_start_application: Starting init task: /system/bin/init
elf_symname: Symbol has no name
elf_symvalue: SHN_UNDEF: Failed to get symbol name: -3
elf_relocateadd: Section 2 reloc 2: Undefined symbol[0] has no name: -3
_assert: Current Version: NuttX 12.4.0 f37a380-dirty May 7 2024 10:31:33 risc-v
_assert: Assertion failed 0x17 == (insn & 0x7F): at file: machine/risc-v/arch_elf.c:494 task: AppBringUp process: Kernel 0x80200f34
up_dump_register: EPC: 000000008021087a
up_dump_register: A0: 0000000080401b70 A1: 00000000000001ee A2: 0000000080228ef8 A3: 0000000000000000
up_dump_register: A4: 0000000000000017 A5: 0000000000000002 A6: 000000000000ab9c A7: fffffffffffffff8
up_dump_register: T0: 000000000000002e T1: 0000000000000007 T2: 00000000000001ff T3: 000000008040c3fc
up_dump_register: T4: 000000008040c3f0 T5: 0000000000000009 T6: 000000000000002a
up_dump_register: S0: 0000000000000000 S1: 0000000080408740 S2: 0000000000000017 S3: 0000000000000000
up_dump_register: S4: 0000000080228ef8 S5: 0000000080228de8 S6: 0000000080401e10 S7: 8000000201842022
up_dump_register: S8: 00000000000001ee S9: 000000008040b9a0 S10: 0000000000000070 S11: 000000008040b990
up_dump_register: SP: 000000008040c330 FP: 0000000000000000 TP: 0000000000000000 RA: 000000008021087a
dump_stack: User Stack:
dump_stack: base: 0x8040c030
dump_stack: size: 00002000
dump_stack: sp: 0x8040c330
What's this Assertion Failure?
_assert: Assertion failed 0x17 == (insn & 0x7F):
at file: machine/risc-v/arch_elf.c:494
task: AppBringUp process: Kernel 0x80200f34
Oops we goofed and used the wrong U-Boot Command...
## Nope! This won't work for NuttX. RAM Disk Address must be `-`!
setenv tftp_server 192.168.31.10 ; dhcp ${kernel_addr_r} ${tftp_server}:Image-sg2000 ;
tftpboot ${fdt_addr_r} ${tftp_server}:jh7110-star64-pine64.dtb ; fdt addr ${fdt_addr_r} ;
booti ${kernel_addr_r} ${ramdisk_addr_r}:${ramdisk_size} ${fdt_addr_r}
Which overwrites the NuttX Image in RAM. Watch what happens when we use the correct U-Boot Command...
Read the article...
Earlier we made these fixes...
-
We set the NuttX Memory Map for SG2000: nsh/defconfig
## Kernel RAM CONFIG_RAM_START=0x80200000 CONFIG_RAM_SIZE=1048576
-
Also the NuttX Linker Script: ld.script
MEMORY { kflash (rx) : ORIGIN = 0x80200000, LENGTH = 2048K /* w/ cache */ ... SECTIONS { . = 0x80200000;
-
We select the NuttX Driver for 16550 UART: nsh/defconfig
CONFIG_16550_REGINCR=4 CONFIG_16550_UART0=y CONFIG_16550_UART0_BASE=0x04140000 CONFIG_16550_UART0_SERIAL_CONSOLE=y CONFIG_16550_UART=y CONFIG_16550_WAIT_LCR=y CONFIG_SERIAL_UART_ARCH_MMIO=y
-
Enable Logging for NuttX Scheduler and Binary Loader: nsh/defconfig
CONFIG_DEBUG_BINFMT=y CONFIG_DEBUG_BINFMT_ERROR=y CONFIG_DEBUG_BINFMT_WARN=y CONFIG_DEBUG_SCHED=y CONFIG_DEBUG_SCHED_ERROR=y CONFIG_DEBUG_SCHED_INFO=y CONFIG_DEBUG_SCHED_WARN=y
-
And disable the PLIC Interrupt Controller (until we figure it out)
Also we used the correct U-Boot Command...
## This works OK for NuttX. RAM Disk Address must be `-`!
setenv tftp_server 192.168.31.10 ; dhcp ${kernel_addr_r} ${tftp_server}:Image-sg2000 ;
tftpboot ${fdt_addr_r} ${tftp_server}:jh7110-star64-pine64.dtb ; fdt addr ${fdt_addr_r} ;
booti ${kernel_addr_r} - ${fdt_addr_r}
NuttX Kernel boots OK on SG2000 yay!
https://gist.github.com/lupyuen/aaa0a6646490d45e5cd99b781cbe59f8
## Flattened Device Tree blob at 81200000
Booting using the fdt blob at 0x81200000
Loading Device Tree to 000000009f26f000, end 000000009f27e43a ... OK
Starting kernel ...
123ABCnx_start: Entry
uart_register: Registering /dev/console
uart_register: Registering /dev/ttyS0
work_start_lowpri: Starting low-priority kernel worker thread(s)
nxtask_activate: lpwork pid=1,TCB=0x80408130
nxtask_activate: AppBringUp pid=2,TCB=0x80408740
nx_start_application: Starting init task: /system/bin/init
elf_symname: Symbol has no name
elf_symvalue: SHN_UNDEF: Failed to get symbol name: -3
elf_relocateadd: Section 2 reloc 2: Undefined symbol[0] has no name: -3
nxtask_activate: /system/bin/init pid=3,TCB=0x80409140
nxtask_exit: AppBringUp pid=2,TCB=0x80408740
But where's the NuttX Shell?
We won't see the NuttX Shell until we fix the Interrupt Controller for SG2000. Which is NOT documented!
Let's sniff around and find out how it works...
Read the article...
Let's dump the SG2000 Linux Device Tree to understand the Interrupt Controller.
From the SG2000 Debian Release: https://github.com/Fishwaldo/sophgo-sg200x-debian/releases
We pick the Latest Release for Milk-V Duo S: https://github.com/Fishwaldo/sophgo-sg200x-debian/releases/download/v1.2.0/duos_sd.img.lz4
We copy out the SG2000 Device Tree Binary: cv181x_milkv_duos_sd.dtb
And convert it to Device Tree Source: cv181x_milkv_duos_sd.dts
## Convert the SG2000 Device Tree
dtc \
-o cv181x_milkv_duos_sd.dts \
-O dts \
-I dtb \
cv181x_milkv_duos_sd.dtb
Read the article...
We dumped the SG2000 Linux Device Tree. Let's extract the Interrupt Controller to understand it.
Based on the SG2000 Device Tree: cv181x_milkv_duos_sd.dts
cpus {
#address-cells = <0x01>;
#size-cells = <0x00>;
timebase-frequency = <0x17d7840>;
cpu-map {
cluster0 {
core0 {
cpu = <0x01>;
};
};
};
cpu@0 {
device_type = "cpu";
reg = <0x00>;
status = "okay";
compatible = "riscv";
riscv,isa = "rv64imafdvcsu";
mmu-type = "riscv,sv39";
clock-frequency = <0x17d7840>;
interrupt-controller {
#interrupt-cells = <0x01>;
interrupt-controller;
compatible = "riscv,cpu-intc";
phandle = <0x16>;
};
};
};
soc {
#address-cells = <0x02>;
#size-cells = <0x02>;
compatible = "simple-bus";
ranges;
interrupt-controller@70000000 {
riscv,ndev = <0x65>;
riscv,max-priority = <0x07>;
reg-names = "control";
reg = <0x00 0x70000000 0x00 0x4000000>;
interrupts-extended = <0x16 0xffffffff 0x16 0x09>;
interrupt-controller;
compatible = "riscv,plic0";
#interrupt-cells = <0x02>;
#address-cells = <0x00>;
phandle = <0x04>;
};
clint@74000000 {
interrupts-extended = <0x16 0x03 0x16 0x07>;
reg = <0x00 0x74000000 0x00 0x10000>;
compatible = "riscv,clint0";
clint,has-no-64bit-mmio;
};
};
We see that PLIC is at 0x7000_0000, CLINT at 0x7400_0000. Let's implement this in NuttX...
Read the article...
Based on the PLIC Address from above: We fix the PLIC Interrupt Controller for SG2000...
https://github.com/lupyuen2/wip-nuttx/commit/f5f1aeac36350b8149fc2a77c817217711f082f6
Now we see a bit more NuttX...
https://gist.github.com/lupyuen/922e6379375fbc5d775d1e83cac4deb5
Starting kernel ...
123ABCnx_start: Entry
uart_register: Registering /dev/console
uart_register: Registering /dev/ttyS0
work_start_lowpri: Starting low-priority kernel worker thread(s)
nxtask_activate: lpwork pid=1,TCB=0x80409130
nxtask_activate: AppBringUp pid=2,TCB=0x80409740
nx_start_application: Starting init task: /system/bin/init
elf_symname: Symbol has no name
elf_symvalue: SHN_UNDEF: Failed to get symbol name: -3
elf_relocateadd: Section 2 reloc 2: Undefined symbol[0] has no name: -3
nxtask_activate: /system/bin/init pid=3,TCB=0x8040b730
nxtask_exit: AppBringUp pid=2,TCB=0x80409740
Nuttnx_start: CPU0: Beginning Idle Loop
Why did it stop?
Duh we set the wrong UART0 IRQ! Here's the fix...
https://github.com/lupyuen2/wip-nuttx/commit/122c717447f81c310a4fb082101213ad338dfb0e
CONFIG_16550_UART0_IRQ=69
How did we get IRQ 69 for UART?
We saw this in the SG2000 Reference Manual...
3.1 Interrupt Subsystem
Table 3.2: Interrupt number and Interrupt source mapping for Master RISCV C906 @ 1.0Ghz
Int #44: UART0
Linx Device Tree also says UART0 IRQ is 44 (0x2C)...
serial@04140000 {
compatible = "snps,dw-apb-uart";
reg = <0x00 0x4140000 0x00 0x1000>;
clock-frequency = <0x17d7840>;
reg-shift = <0x02>;
reg-io-width = <0x04>;
status = "okay";
interrupts = <0x2c 0x04>;
interrupt-parent = <0x04>;
};
Thus we compute NuttX IRQ = 25 + RISC-V IRQ = 69
Read the article...
Earlier we made these fixes...
-
We dumped the Linux Device Tree for SG2000...
## Convert the SG2000 Device Tree dtc \ -o cv181x_milkv_duos_sd.dts \ -O dts \ -I dtb \ cv181x_milkv_duos_sd.dtb
-
Snooped the PLIC Interrupt Controller in the Device Tree: cv181x_milkv_duos_sd.dts
interrupt-controller@70000000 { riscv,ndev = <0x65>; riscv,max-priority = <0x07>; reg-names = "control"; reg = <0x00 0x70000000 0x00 0x4000000>; interrupts-extended = <0x16 0xffffffff 0x16 0x09>; interrupt-controller; compatible = "riscv,plic0";
-
And fixed the NuttX Driver for PLIC Interrupts: bl808_memorymap.h
// Base Address of PLIC Interrupt Controller #define BL808_PLIC_BASE 0x70000000ul
After fixing the Interrupt Controller and UART Interrupt: NuttX Kernel now boots all the way to NuttX Shell yay!
https://gist.github.com/lupyuen/b778986ba87c18067cd993b92c673634
Starting kernel ...
123ABCnx_start: Entry
uart_register: Registering /dev/console
uart_register: Registering /dev/ttyS0
work_start_lowpri: Starting low-priority kernel worker thread(s)
nxtask_activate: lpwork pid=1,TCB=0x80409130
nxtask_activate: AppBringUp pid=2,TCB=0x80409740
nx_start_application: Starting init task: /system/bin/init
elf_symname: Symbol has no name
elf_symvalue: SHN_UNDEF: Failed to get symbol name: -3
elf_relocateadd: Section 2 reloc 2: Undefined symbol[0] has no name: -3
nxtask_activate: /system/bin/init pid=3,TCB=0x8040b730
nxtask_exit: AppBringUp pid=2,TCB=0x80409740
NuttShell (NSH) NuttX-12.4.0
nsh> nx_start: CPU0: Beginning Idle Loop
ls
posix_spawn: pid=0xc0202968 path=ls file_actions=0xc0202970 attr=0xc0202978 argv=0xc0202a18
exec_internal: ERROR: Failed to load program 'ls': -2
nxposix_spawn_exec: ERROR: exec failed: 2
/:
dev/
proc/
system/
nsh>
nsh> uname -a
posix_spawn: pid=0xc0202968 path=uname file_actions=0xc0202970 attr=0xc0202978 argv=0xc0202a18
exec_internal: ERROR: Failed to load program 'uname': -2
nxposix_spawn_exec: ERROR: exec failed: 2
NuttX 12.4.0 122c717 May 8 2024 18:13:30 risc-v ox64
nsh>
nsh> free
posix_spawn: pid=0xc0202968 path=free file_actions=0xc0202970 attr=0xc0202978 argv=0xc0202a18
exec_internal: ERROR: Failed to load program 'free': -2
nxposix_spawn_exec: ERROR: exec failed: 2
total used free maxused maxfree nused nfree
Kmem: 2065400 14296 2051104 76632 2049392 36 3
Page: 20971520 647168 20324352 20324352
nsh>
nsh> ls /dev
posix_spawn: pid=0xc0202968 path=ls file_actions=0xc0202970 attr=0xc0202978 argv=0xc0202a18
exec_internal: ERROR: Failed to load program 'ls': -2
nxposix_spawn_exec: ERROR: exec failed: 2
/dev:
console
null
ram0
ttyS0
zero
nsh>
nsh>
What about the rest of NuttX?
NuttX OSTest is the perfect way to test everything in NuttX...
nsh> ostest
user_main: mutex test
riscv_exception:
EXCEPTION: Load access fault
MCAUSE: 5
EPC: 802189ce
MTVAL: 0000000000000000
Segmentation fault in PID 7: ostest
Sadly we're hitting a RISC-V Exception: Load Access Fault. Needs more troubleshooting sigh.
Read the article...
Here are the U-Boot Commands available for Milk-V Duo S...
https://gist.github.com/lupyuen/000b55a46336cddf217a589f469d60e2
cv181x_c906# help
? - alias for 'help'
base - print or set address offset
bdinfo - print Board Info structure
blkcache - block cache diagnostics and control
boot - boot default, i.e., run 'bootcmd'
bootd - boot default, i.e., run 'bootcmd'
bootefi - Boots an EFI payload from memory
bootelf - Boot from an ELF image in memory
booti - boot Linux kernel 'Image' format from memory
bootm - boot application image from memory
bootp - boot image via network using BOOTP/TFTP protocol
bootvx - Boot vxWorks from an ELF image
cmp - memory compare
cp - memory copy
cpu - display information about CPUs
cvi_sd_boot- boot from SD card
cvi_update- cvi_update [eth, sd, usb]- check boot status and update if necessary
cvi_utask - bootloader control block command
dcache - enable or disable data cache
dhcp - boot image via network using DHCP/TFTP protocol
echo - echo args to console
efuser - Read efuse
efuser_dump- Read/Dump efuse
efusew - Write efuse
efusew_word- Write word to efuse
env - environment handling commands
erase - erase FLASH memory
exit - exit script
ext2load - load binary file from a Ext2 filesystem
ext2ls - list files in a directory (default /)
ext4load - load binary file from a Ext4 filesystem
ext4ls - list files in a directory (default /)
ext4size - determine a file's size
false - do nothing, unsuccessfully
fatinfo - print information about filesystem
fatload - load binary file from a dos filesystem
fatls - list files in a directory (default /)
fatmkdir - create a directory
fatrm - delete a file
fatsize - determine a file's size
fatwrite - write file into a dos filesystem
fdt - flattened device tree utility commands
flinfo - print FLASH memory information
fstype - Look up a filesystem type
fstypes - List supported filesystem types
go - start application at address 'addr'
help - print command description/usage
icache - enable or disable instruction cache
iminfo - print header information for application image
ln - Create a symbolic link
load - load binary file from a filesystem
loadb - load binary file over serial line (kermit mode)
loadx - load binary file over serial line (xmodem mode)
loady - load binary file over serial line (ymodem mode)
loop - infinite loop on address range
ls - list files in a directory (default /)
md - memory display
mdio - MDIO utility commands
mii - MII utility commands
mm - memory modify (auto-incrementing address)
mmc - MMC sub system
mmcinfo - display MMC info
mw - memory write (fill)
net - NET sub-system
nfs - boot image via network using NFS protocol
nm - memory modify (constant address)
panic - Panic with optional message
part - disk partition related commands
ping - send ICMP ECHO_REQUEST to network host
printenv - print environment variables
protect - enable or disable FLASH write protection
pxe - commands to get and boot from pxe files
random - fill memory with random pattern
reset - Perform RESET of the CPU
run - run commands in an environment variable
save - save file to a filesystem
saveenv - save environment variables to persistent storage
setenv - set environment variables
setexpr - set environment variable as the result of eval expression
showvar - print local hushshell variables
size - determine a file's size
sleep - delay execution for some time
source - run script from memory
sysboot - command to get and boot from syslinux files
test - minimal test like /bin/sh
tftpboot - boot image via network using TFTP protocol
true - do nothing, successfully
version - print monitor, compiler and linker version