![64-bit RISC-V Sophgo SG2000 (T-Head C906 / Milk-V Duo S)](https://lupyuen.github.io/images/sg2000-title.jpg) [(Watch the Demo on YouTube)](https://youtu.be/24azql1rzc4?si=rMId8LWVEM4_Wj4D) # Apache NuttX RTOS on 64-bit RISC-V Sophgo SG2000 (T-Head C906 / Milk-V Duo S) [![Daily Build of NuttX for SG2000](https://github.com/lupyuen/nuttx-sg2000/actions/workflows/sg2000.yml/badge.svg)](https://github.com/lupyuen/nuttx-sg2000/actions/workflows/sg2000.yml) [![Daily Test of NuttX for SG2000](https://github.com/lupyuen/nuttx-sg2000/actions/workflows/sg2000-test.yml/badge.svg)](https://github.com/lupyuen/nuttx-sg2000/actions/workflows/sg2000-test.yml) Read the articles... - ["Apache NuttX RTOS on Sophgo SG2000 RISC-V SoC (Milk-V Duo S SBC)"](https://lupyuen.github.io/articles/sg2000) - ["Daily Automated Testing for Milk-V Duo S RISC-V SBC (IKEA TRETAKT / Apache NuttX RTOS)"](https://lupyuen.github.io/articles/sg2000a) - ["RISC-V Emulator for Sophgo SG2000 SoC (Pine64 Oz64 / Milk-V Duo S)"](https://lupyuen.github.io/articles/sg2000b) __UPDATE:__ Sophgo SG2000 is officially supported by NuttX Mainline! (Including Milk-V Duo S and Pine64 Oz64) - ["NuttX Mainline now supports SG2000"](https://lupyuen.github.io/articles/sg2000#appendix-nuttx-mainline-now-supports-sg2000) - ["NuttX for Milk-V Duo S"](https://nuttx.apache.org/docs/latest/platforms/risc-v/sg2000/boards/milkv_duos/index.html) - ["NuttX for Pine64 Oz64"](https://lupyuen.github.io/articles/sg2000#appendix-apache-nuttx-rtos-for-pine64-oz64-sbc) 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) - [SG2000 Overview](https://www.cnx-software.com/2024/02/07/sophgo-sg2000-sg2002-ai-soc-features-risc-v-arm-8051-cores-android-linux-freertos/) - [SG2000 Reference Manual](https://github.com/sophgo/sophgo-doc/releases) 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?_ 1. We hear that there will be plenty of interesting new SBCs based on Sophgo SG2000 and SG2002. Perfect for NuttX! 1. 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! ![Sophgo SG2000 RISC-V SoC](https://lupyuen.github.io/images/sg2000-soc.jpg) # NuttX Automated Daily Build for SG2000 NuttX for SG2000 is now built automatically every day via GitHub Actions. The Daily Releases are available here... - [nuttx-sg2000/releases](https://github.com/lupyuen/nuttx-sg2000/tags) (Skip the Special Builds) [nuttx.hash](https://github.com/lupyuen/nuttx-sg2000/releases/download/nuttx-sg2000-2024-06-18/nuttx.hash) contains the Commit Hash of the NuttX Kernel and NuttX Apps repos... ```text 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... - [sg2000.yml](https://github.com/lupyuen/nuttx-sg2000/blob/main/.github/workflows/sg2000.yml) We are now running Daily Automated Testing of NuttX on a Real Milk-V Duo S SBC... 1. Download the Daily Build to TFTP Server 1. Power on our SBC with an [IKEA Smart Power Plug via Home Assistant](https://github.com/lupyuen2/autotest-nuttx-sg2000#control-our-sbc-with-an-ikea-smart-power-plug-and-home-assistant) 1. Our SBC boots the Daily Build over TFTP 1. Capture the Automated Testing Log and write to the Release Notes [(See the Automated Test Logs)](https://github.com/lupyuen/nuttx-sg2000/releases) [(See the Automated Test Script)](https://github.com/lupyuen2/autotest-nuttx-sg2000) We are also running Daily Automated Testing with an SG2000 Emulator (in GitHub Actions)... - [See the Automated Test Logs](https://github.com/lupyuen/nuttx-sg2000/actions/workflows/sg2000-test.yml) - [See the GitHub Actions Workflow](https://github.com/lupyuen/nuttx-sg2000/blob/main/.github/workflows/sg2000-test.yml) - ["Emulate Sophgo SG2000 SoC / Milk-V Duo S SBC with TinyEMU RISC-V Emulator"](https://github.com/lupyuen2/sg2000-emulator) # Sophgo SG2000 RISC-V SoC Read the article... - ["Apache NuttX RTOS on Sophgo SG2000 RISC-V SoC (Milk-V Duo S SBC)"](https://lupyuen.github.io/articles/sg2000) __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) ![Sophgo SG2000 RISC-V SoC](https://lupyuen.github.io/images/sg2000-arch.jpg) _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!) ![Milk-V Duo S](https://lupyuen.github.io/images/sg2000-board.jpg) # Boot Milk-V Duo S without MicroSD Read the article... - ["Apache NuttX RTOS on Sophgo SG2000 RISC-V SoC (Milk-V Duo S SBC)"](https://lupyuen.github.io/articles/sg2000) _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__ | [(Source)](https://milkv.io/docs/duo/getting-started/duos#uart-serial-console) USB UART Dongle must be [__CP2102__](http://sun-light.com.sg/index.php?route=product/product&product_id=2367), it doesn't like [__CH340G__](https://pine64.com/product/serial-console-woodpecker-edition/) 😬 Flip the switch so it's set to "__`RV`__" (RISC-V) instead of "__`Arm`__"... ![Switch to "RV" (RISC-V) instead of "Arm"](https://lupyuen.github.io/images/sg2000-switch.jpg) Power up the board via the USB-C Port. We should see... https://gist.github.com/lupyuen/a7c3af98be36dcd5cc5b45f5aadc5d16 ```bash 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 ```bash 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](https://pine64.com/product/serial-console-woodpecker-edition/) (instead of [CP2102](http://sun-light.com.sg/index.php?route=product/product&product_id=2367)): 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... # Boot Linux on Milk-V Duo S Read the article... - ["Apache NuttX RTOS on Sophgo SG2000 RISC-V SoC (Milk-V Duo S SBC)"](https://lupyuen.github.io/articles/sg2000) _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__](https://github.com/Fishwaldo) (Fishwaldo)... - [__Debian Images for Sophgo SG2000__](https://github.com/Fishwaldo/sophgo-sg200x-debian/releases) We download the Latest Release for __Milk-V Duo S__ (SG2000)... - [__duos_sd.img.lz4__](https://github.com/Fishwaldo/sophgo-sg200x-debian/releases/download/v1.2.0/duos_sd.img.lz4) ```bash $ 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... ```bash → 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 ```bash 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 ```bash 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! ```bash $ 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 # Boot Milk-V Duo S over TFTP Read the article... - ["Apache NuttX RTOS on Sophgo SG2000 RISC-V SoC (Milk-V Duo S SBC)"](https://lupyuen.github.io/articles/sg2000) 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 ```bash ## 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... ```bash ## 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... ```bash ## 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... ```bash ## 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... ```bash $ 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... # UART Controller for SG2000 Read the article... - ["Apache NuttX RTOS on Sophgo SG2000 RISC-V SoC (Milk-V Duo S SBC)"](https://lupyuen.github.io/articles/sg2000) According to the [SG2000 Reference Manual](https://github.com/sophgo/sophgo-doc/releases), 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... # Print to SG2000 UART in RISC-V Assembly Read the article... - ["Apache NuttX RTOS on Sophgo SG2000 RISC-V SoC (Milk-V Duo S SBC)"](https://lupyuen.github.io/articles/sg2000) 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 ```c /* 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! # Build Apache NuttX RTOS for Milk-V Duo S Read the article... - ["Apache NuttX RTOS on Sophgo SG2000 RISC-V SoC (Milk-V Duo S SBC)"](https://lupyuen.github.io/articles/sg2000) Follow these steps to build (work-in-progress) Apache NuttX RTOS for SG2000 / Milk-V Duo S... ```bash ## 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... # Apache NuttX RTOS boots a tiny bit on Milk-V Duo S Read the article... - ["Apache NuttX RTOS on Sophgo SG2000 RISC-V SoC (Milk-V Duo S SBC)"](https://lupyuen.github.io/articles/sg2000) 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 ```bash ## 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! ```bash ## 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... 1. Fix the Boot Address of NuttX, so the rest of NuttX can start 1. 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)](https://www.tindie.com/products/badgerdnl/sdwire-usb-c-sd-card-reader-sd-mux/) # Set the NuttX Memory Map for SG2000 Read the article... - ["Apache NuttX RTOS on Sophgo SG2000 RISC-V SoC (Milk-V Duo S SBC)"](https://lupyuen.github.io/articles/sg2000) From the U-Boot Bootloader Config above: We see that SG2000 boots at this address... ```bash 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](https://github.com/lupyuen2/wip-nuttx/commit/c6c0bd3882a855420acf0d53fa9d37bbd9d125b2#diff-fa4b30efe1c5e19ba2fdd2216528406d85fa89bf3d2d0e5161794191c1566078) ```bash ## 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](https://github.com/lupyuen2/wip-nuttx/commit/c6c0bd3882a855420acf0d53fa9d37bbd9d125b2#diff-769e7c2389b298f666c84b92f36d3c42fa852fda61dbf20b93e603df98b7bd37) ```c 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; ``` # Disable the PLIC Interrupt Controller Read the article... - ["Apache NuttX RTOS on Sophgo SG2000 RISC-V SoC (Milk-V Duo S SBC)"](https://lupyuen.github.io/articles/sg2000) 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. # Select the NuttX Driver for 16550 UART Read the article... - ["Apache NuttX RTOS on Sophgo SG2000 RISC-V SoC (Milk-V Duo S SBC)"](https://lupyuen.github.io/articles/sg2000) 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](https://github.com/lupyuen2/wip-nuttx/commit/8f8831d15d6ddc913e6dd1c6c49fb0067640f6ec#diff-fa4b30efe1c5e19ba2fdd2216528406d85fa89bf3d2d0e5161794191c1566078) ```bash 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... ```bash ## 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 ```bash CONFIG_16550_UART0_IRQ=69 ``` We saw this in the [SG2000 Reference Manual](https://github.com/sophgo/sophgo-doc/releases)... > 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)... ```c 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](https://lupyuen.github.io/articles/plic2#uart-interrupt) = 25 + RISC-V IRQ = 69 TODO: Fix the UART Clock: 16550_UART0_CLOCK # Enable Logging for NuttX Scheduler and Binary Loader Read the article... - ["Apache NuttX RTOS on Sophgo SG2000 RISC-V SoC (Milk-V Duo S SBC)"](https://lupyuen.github.io/articles/sg2000) 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](https://github.com/lupyuen2/wip-nuttx/commit/4cee79630359f6b31fc9fa40f31bb476c8bc4d47#diff-fa4b30efe1c5e19ba2fdd2216528406d85fa89bf3d2d0e5161794191c1566078) ```bash 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! # NuttX Crash Dump on SG2000 Read the article... - ["Apache NuttX RTOS on Sophgo SG2000 RISC-V SoC (Milk-V Duo S SBC)"](https://lupyuen.github.io/articles/sg2000) 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 ```bash ## 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?_ ```bash _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... ```bash ## 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... ![NuttX Kernel Boots OK on SG2000](https://lupyuen.github.io/images/sg2000-kernel.png) # NuttX Kernel Boots OK on SG2000 Read the article... - ["Apache NuttX RTOS on Sophgo SG2000 RISC-V SoC (Milk-V Duo S SBC)"](https://lupyuen.github.io/articles/sg2000) Earlier we made these fixes... 1. We set the __NuttX Memory Map__ for SG2000: [nsh/defconfig](https://github.com/lupyuen2/wip-nuttx/commit/c6c0bd3882a855420acf0d53fa9d37bbd9d125b2#diff-fa4b30efe1c5e19ba2fdd2216528406d85fa89bf3d2d0e5161794191c1566078) ```bash ## Kernel RAM CONFIG_RAM_START=0x80200000 CONFIG_RAM_SIZE=1048576 ``` 1. Also the __NuttX Linker Script__: [ld.script](https://github.com/lupyuen2/wip-nuttx/commit/c6c0bd3882a855420acf0d53fa9d37bbd9d125b2#diff-769e7c2389b298f666c84b92f36d3c42fa852fda61dbf20b93e603df98b7bd37) ```c MEMORY { kflash (rx) : ORIGIN = 0x80200000, LENGTH = 2048K /* w/ cache */ ... SECTIONS { . = 0x80200000; ``` 1. We select the __NuttX Driver for 16550 UART__: [nsh/defconfig](https://github.com/lupyuen2/wip-nuttx/commit/8f8831d15d6ddc913e6dd1c6c49fb0067640f6ec#diff-fa4b30efe1c5e19ba2fdd2216528406d85fa89bf3d2d0e5161794191c1566078) ```bash 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 ``` 1. Enable Logging for __NuttX Scheduler and Binary Loader__: [nsh/defconfig](https://github.com/lupyuen2/wip-nuttx/commit/4cee79630359f6b31fc9fa40f31bb476c8bc4d47#diff-fa4b30efe1c5e19ba2fdd2216528406d85fa89bf3d2d0e5161794191c1566078) ```bash 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 ``` 1. And disable the __PLIC Interrupt Controller__ (until we figure it out) Also we used the correct U-Boot Command... ```bash ## 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 ```bash ## 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 ``` [(Watch the Demo on YouTube)](https://www.youtube.com/watch?v=pPNDiC5NLqM) _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... # Dump the SG2000 Linux Device Tree Read the article... - ["Apache NuttX RTOS on Sophgo SG2000 RISC-V SoC (Milk-V Duo S SBC)"](https://lupyuen.github.io/articles/sg2000) 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](cv181x_milkv_duos_sd.dtb) And convert it to Device Tree Source: [cv181x_milkv_duos_sd.dts](cv181x_milkv_duos_sd.dts) ```bash ## Convert the SG2000 Device Tree dtc \ -o cv181x_milkv_duos_sd.dts \ -O dts \ -I dtb \ cv181x_milkv_duos_sd.dtb ``` # Interrupt Controller for SG2000 Read the article... - ["Apache NuttX RTOS on Sophgo SG2000 RISC-V SoC (Milk-V Duo S SBC)"](https://lupyuen.github.io/articles/sg2000) 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](cv181x_milkv_duos_sd.dts) ```c 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... # Fix the PLIC Interrupt Controller for SG2000 Read the article... - ["Apache NuttX RTOS on Sophgo SG2000 RISC-V SoC (Milk-V Duo S SBC)"](https://lupyuen.github.io/articles/sg2000) Based on the PLIC Address from above: We fix the PLIC Interrupt Controller for SG2000... https://github.com/lupyuen2/wip-nuttx/commit/f5f1aeac36350b8149fc2a77c817217711f082f6 ![Platform-Level Interrupt Controller](https://lupyuen.github.io/images/sg2000-plic.jpg) Now we see a bit more NuttX... https://gist.github.com/lupyuen/922e6379375fbc5d775d1e83cac4deb5 ```text 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 ```bash CONFIG_16550_UART0_IRQ=69 ``` _How did we get IRQ 69 for UART?_ We saw this in the [SG2000 Reference Manual](https://github.com/sophgo/sophgo-doc/releases)... > 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)... ```c 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](https://lupyuen.github.io/articles/plic2#uart-interrupt) = 25 + RISC-V IRQ = 69 ![NuttX Shell runs OK](https://lupyuen.github.io/images/sg2000-nsh.png) # NuttX Shell runs OK on SG2000 Read the article... - ["Apache NuttX RTOS on Sophgo SG2000 RISC-V SoC (Milk-V Duo S SBC)"](https://lupyuen.github.io/articles/sg2000) Earlier we made these fixes... 1. We dumped the __Linux Device Tree__ for SG2000... ```bash ## Convert the SG2000 Device Tree dtc \ -o cv181x_milkv_duos_sd.dts \ -O dts \ -I dtb \ cv181x_milkv_duos_sd.dtb ``` 1. Snooped the __PLIC Interrupt Controller__ in the Device Tree: [cv181x_milkv_duos_sd.dts](https://github.com/lupyuen/nuttx-sg2000/blob/main/cv181x_milkv_duos_sd.dts) ```c 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"; ``` 1. And fixed the __NuttX Driver__ for PLIC Interrupts: [bl808_memorymap.h](https://github.com/lupyuen2/wip-nuttx/commit/f5f1aeac36350b8149fc2a77c817217711f082f6#diff-8fffa570a48f8f10004d9da8d4c671d34336f6c4b8dcfc2bd72275d8cda4ac04) ```c // 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 ```bash 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... ```bash 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. [(See the __Complete Log__)](https://gist.github.com/lupyuen/fff5242cf77a3f52d81f3effb9aa402f) # U-Boot Commands for Milk-V Duo S Read the article... - ["Apache NuttX RTOS on Sophgo SG2000 RISC-V SoC (Milk-V Duo S SBC)"](https://lupyuen.github.io/articles/sg2000) Here are the U-Boot Commands available for Milk-V Duo S... https://gist.github.com/lupyuen/000b55a46336cddf217a589f469d60e2 ```bash 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 ```