Milkv-duo 系列开发板 rt-smart/rt-thread 双核/双系统完整上手指南

rt-smart 实时操作系统是 rt-thread 面向带MMU,中高端应用的芯片,例如ARM Cortex-A系列芯片,MIPS芯片,带MMU的RISC-V芯片等。rt-smart 在 RT-Thread 操作系统的基础上启用独立、完整的进程方式,同时以混合微内核模式执行。

rt-thread 官网:https://www.rt-thread.org/
rt-smart 官方文档中心:https://www.rt-thread.org/document/site/#/rt-thread-version/rt-thread-smart/introduction/rt-smart-intro/rt-smart-intro

注:文档中心相关文档可能并未及时更新

milkv-duo 系列开发板包括 duo、duo256、duos 均已支持 rt-thread 标准版、rt-smart 版本运行。

支持开发板以及集成 SoC 芯片信息如下:

Duo 家族开发板采用 CV18xx 系列芯片。芯片的工作模式总结如下:

  • CV1800B,支持一种工作模式:
    • 大核(RISC-V C906@1GHz)+ 小核(RISC-V C906@700MHz)。
  • SG2002(原 CV181xC),支持两种工作模式,通过管脚 GPIO_RTX 的外围电路控制进行切换:
    • 大核(RISC-V C906@1GHz)+ 小核(RISC-V C906@700MHz)。
    • 大核(ARM Cortex-A53@1GHz)+ 小核(RISC-V C906@700MHz)。
  • SG2000(原 CV181xH),支持两种工作模式,通过管脚 GPIO_RTX 的外围电路控制进行切换:
    • 大核(RISC-V C906@1GHz)+ 小核(RISC-V C906@700MHz)。
    • 大核(ARM Cortex-A53@1GHz)+ 小核(RISC-V C906@700MHz)。

异构芯片有大小核多个不同核的存在,以及不同 SoC 下不同工作模式的存在,bsp/cvitek 提供了三种不同 BSP/OS,需要单独编译。

BSP 名称 大小核 芯片架构 默认串口控制台 备注
cv18xx_risc-v 大核 RISC-V C906 uart0 支持 MMU,支持 RT-Thread 标准版 和 RT-SMART 模式,默认运行 RT-SMART 版本
c906-little 小核 RISC-V C906 uart1 无 MMU,运行 RT-Thread 标准版
cv18xx_aarch64 大核 ARM Cortex A53 uart0 支持 MMU, 支持 RT-Thread 标准版 和 RT-SMART 版,默认运行 RT-Thread 标准版本

由于开发板默认运行的大核为 “cv18xx_risc-v”, 所以本文将主要介绍 cv18xx_risc-vc906-little 的构建和使用。

代码下载

目前 rt-thread 官方仓库 master 已支持 rt-smart 运行模式,按照以下方式下载代码:

$ git clone https://github.com/rt-thread/rt-thread

milkv-duo 系列开发板对应的 bsp 位于 bsp/cvitek 目录结构如下:

├── board_env.sh
├── c906_little				# risc-v c906 小核
├── combine-fip.sh
├── cv18xx_aarch64			# arm Cortex-A53 核
├── cv18xx_risc-v			# risc-v c906 大核
├── drivers					# 驱动
├── mkimage
├── mksdimg.sh
└── README.md

目前只支持 Linux 环境下的开发,推荐 ubuntu 22.04。不支持 Windows 环境,在 Windows 环境下,可使用 WSL 进行开发。

环境搭建

依赖安装

RT-Thread 实时操作系统采用 SCons 作为构建工具。

$ sudo apt-get install -y scons python3 python3-pip libncurses5-dev device-tree-compiler

rt-thread 使用 kconfiglib 作为 menuconfig 工具,使用以下命令安装:

$ pip3 install kconfiglib

milkv-duo 系列开发板会自动下载包括 opensbi、u-boot 等相关依赖代码包,并自动编译,需安装以下依赖:

$ sudo apt-get install -y wget bison flex

toolchain 下载

  1. rt-smart 运行使用专用 musl gcc 编译器,下载地址 https://github.com/RT-Thread/toolchains-ci/releases/download/v1.7/riscv64-linux-musleabi_for_x86_64-pc-linux-gnu_latest.tar.bz2

    下载完成后使用如下命令解压:

    $ sudo tar -xvf riscv64-linux-musleabi_for_x86_64-pc-linux-gnu_latest.tar.bz2 -C /opt
    
  2. rt-thread 标准版使用 xuantie newlib gcc 编译器,下载地址 https://occ-oss-prod.oss-cn-hangzhou.aliyuncs.com/resource//1705395512373/Xuantie-900-gcc-elf-newlib-x86_64-V2.8.1-20240115.tar.gz

    下载完成后使用如下命令解压:

    $ sudo tar -zxvf Xuantie-900-gcc-elf-newlib-x86_64-V2.8.1-20240115.tar.gz -C /opt
    

编译

小核编译

小核相关操作均在 bsp/c906_little 目录下进行。

  1. 配置 toolchain 地址(配置方法任选一种)
  • 手工修改
    rt-thread toolchain 可通过 bsp/cvitek/c906_little/rtconfig.py 文件中的 EXEC_PATH 变量手工设置,请根据上诉 newlib gcc 地址正确设置。

    if  CROSS_TOOL == 'gcc':
        PLATFORM    = 'gcc'
        EXEC_PATH   = r'/opt/Xuantie-900-gcc-elf-newlib-x86_64-V2.8.1/bin'
    else:
        print('Please make sure your toolchains is GNU GCC!')
        exit(0)
    
  • 环境变量设置

    $ export RTT_CC_PREFIX=riscv64-unknown-elf-
    $ export RTT_EXEC_PATH=/opt/Xuantie-900-gcc-elf-newlib-x86_64-V2.8.1/bin
    
  • 在编译命令中通过参数传入 toolchain 路径,方法见3. 编译

  1. 选择对应的开发板
    c906_little 默认开发板为 milkv-duo256m sd卡版本,可通过 menuconfig 进入修改选择

    $ scons --menuconfig
        RT-Thread Kernel  --->
        RT-Thread Components  --->
        RT-Thread Utestcases  --->
        RT-Thread online packages  --->
        General Drivers Configuration  --->
        Board Type (milkv-duo256m)  --->
            ( ) milkv-duo
            ( ) milkv-duo-spinor
            (X) milkv-duo256m
            ( ) milkv-duo256m-spinor
            ( ) milkv-duos
    

    同时也可以在修改 RT-Thread Kernel、Components 等相关配置选择。修改完成后,保存并退出。

  2. 编译
    使用如下命令编译:

     $ scons
    # 或
    $ scons --exec-path=/opt/Xuantie-900-gcc-elf-newlib-x86_64-V2.8.1/bin
    

    小核首次编译会自动下载 opensbi、u-boot 等相关依赖代码包,并自动编译后与 c906_little 目录下的 rtthread.bin 合并成 fip.bin 文件。该文件位于 bsp/cvitek/output/milkv-duo256m/fip.bin

    成功编译打包后会显示如下信息:

     [LS]  -rw-r--r-- 1 root root 456704 Nov 2 11:47 /home/share/samba/rt-thread/bsp/cvitek/cvitek_bootloader/fsbl/build/cv1812cp_milkv_duo256m_sd/fip.bin
     make[1]: Leaving directory '/home/share/samba/rt-thread/bsp/cvitek/cvitek_bootloader/fsbl'
     cp /home/share/samba/rt-thread/bsp/cvitek/cvitek_bootloader/fsbl/build/cv1812cp_milkv_duo256m_sd/fip.bin /home/share/samba/rt-thread/bsp/cvitek/cvitek_bootloader/install/soc_cv1812cp_milkv_duo256m_sd/
     cp /home/share/samba/rt-thread/bsp/cvitek/cvitek_bootloader/fsbl/build/cv1812cp_milkv_duo256m_sd/fip.bin /home/share/samba/rt-thread/bsp/cvitek/cvitek_bootloader/install/soc_cv1812cp_milkv_duo256m_sd/fip_spl.bin
     /home/share/samba/rt-thread/bsp/cvitek
     scons: done building targets.
    

    注: 路径名会根据编译环境不同而变化。

大核编译

大核相关操作均在 bsp/cv18xx_risc-v 目录下进行。

编译前须确认当前大核运行 rt-thread 标准版还是 rt-smart 版,默认为 rt-smart 版。标准版编译流程同c906_little,下面介绍 rt-smart 编译方法。

  1. 配置 toolchain 地址(配置方法任选一种)
  • 手工修改
    rt-smart toolchain 可通过 bsp/cvitek/cv18xx_risc-v/rtconfig.py 文件中的 EXEC_PATH 变量手工设置,请根据上诉 musl gcc 地址正确设置。

    if  CROSS_TOOL == 'gcc':
        PLATFORM    = 'gcc'
        EXEC_PATH   = r'/opt/riscv64-linux-musleabi_for_x86_64-pc-linux-gnu/bin'
    else:
        print('Please make sure your toolchains is GNU GCC!')
        exit(0)
    
  • 环境变量设置

    $ export RTT_CC_PREFIX=riscv64-unknown-linux-musl-
    $ export RTT_EXEC_PATH=/opt/riscv64-linux-musleabi_for_x86_64-pc-linux-gnu/bin
    
  • 在编译命令中通过参数传入 toolchain 路径,方法见4. 编译

  1. rt-smart 功能开启与内核虚拟地址确认

    大核 cv18xx_risc-v 默认运行 rt-smart 版,也可以运行 rt-thread 标准版,可通过 menuconfig 进入 RT-Thread Kernel 修改选择。

    $ scons --menuconfig
        RT-Thread Kernel  --->
        	[*] Enable RT-Thread Smart (microkernel on kernel/userland)
    

    开启 Enable RT-Thread Smart 选项后,在 menuconfig 主页面下可看到内核虚拟地址,该地址必须为 0xFFFFFFC000200000

    $ scons --menuconfig
        RT-Thread Kernel  --->
    (0xFFFFFFC000200000) The virtural address of kernel start
        RT-Thread Components  --->
        RT-Thread Utestcases  --->
        RT-Thread online packages  --->
        General Drivers Configuration  --->
    (8192) stack size for interrupt
        Board Type (milkv-duo256m)  --->
    	rootfs type (Disk FileSystems, e.g. ext4, fat ...)  --->
    
  2. 选择对应的开发板
    cv18xx_risc-v 默认开发板为 milkv-duo256m sd卡版本,可通过 menuconfig 进入 Board Type 修改选择。

    $ scons --menuconfig
        RT-Thread Kernel  --->
    (0xFFFFFFC000200000) The virtural address of kernel start
        RT-Thread Components  --->
        RT-Thread Utestcases  --->
        RT-Thread online packages  --->
        General Drivers Configuration  --->
    (8192) stack size for interrupt
        Board Type (milkv-duo256m)  --->
            ( ) milkv-duo
            ( ) milkv-duo-spinor
            (X) milkv-duo256m
            ( ) milkv-duo256m-spinor
            ( ) milkv-duos
    	rootfs type (Disk FileSystems, e.g. ext4, fat ...)  --->
    

    同时也可以在修改 RT-Thread Kernel、Components 等相关配置选择。修改完成后,保存并退出。

  3. 编译
    使用如下命令编译:

     $ scons
    # 或
    $ scons --exec-path=/opt/riscv64-linux-musleabi_for_x86_64-pc-linux-gnu/bin
    

编译完成

编译成功后,会在 bsp/cvitek/output 对应开发板型号目录下自动生成 fip.binboot.sd 文件,其中大核运行文件在 boot.sd 中,小核的运行文件在 fip.bin 中。

- fip.bin:fsbl、opensbi、uboot、小核运行文件打包后的 bin 文件
- boot.sd:大核打包后的 bin 文件

运行

  1. 将 SD 卡分为 2 个分区,第 1 个分区用于存放 bin 文件,分区格式为 FAT32。第 2 个分区可用于作为根文件系统,具体分区格式需与对应根文件系统移植(见 #根文件系统挂载 )。

  2. bsp/cvitek 目录下 output 对应开发板下的 fip.binboot.sd 复制到 SD 卡第一个分区中。两个固件文件可以独立修改更新,譬如后续只需要更新大核,只需要重新编译 “cv18xx_risc-v” 并复制 boot.sd 文件即可。

  3. 更新完固件文件后,重新上电可以看到串口的输出信息。
    大小核串口输出分别为:

  • 大核: uart0(GP12/GP13)

    U-Boot 2021.10 (Nov 02 2024 - 11:47:01 +0000) cvitek_cv181x
    
    DRAM:  254 MiB
    gd->relocaddr=0x8b0c8000. offset=0xaec8000
    MMC:   cv-sd@4310000: 0
    Loading Environment from nowhere... OK
    In:    serial
    Out:   serial
    Err:   serial
    Net:   
    Warning: ethernet@4070000 (eth0) using random MAC address - 82:f7:c4:27:98:16
    eth0: ethernet@4070000
    Hit any key to stop autoboot:  0 
    Boot from SD ...
    switch to partitions #0, OK
    mmc0 is current device
    170180 bytes read in 11 ms (14.8 MiB/s)
    
    ## Loading kernel from FIT Image at 81800000 ...
    
       Using 'config-cv1812cp_milkv_duo256m_sd' configuration
       Trying 'kernel-1' kernel subimage
         Description:  cvitek kernel
         Type:         Kernel Image
         Compression:  lzma compressed
         Data Start:   0x818000d8
         Data Size:    143686 Bytes = 140.3 KiB
         Architecture: RISC-V
         OS:           Linux
         Load Address: 0x80200000
         Entry Point:  0x80200000
         Hash algo:    crc32
         Hash value:   194b3f87
       Verifying Hash Integrity ... crc32+ OK
    
    ## Loading fdt from FIT Image at 81800000 ...
    
       Using 'config-cv1812cp_milkv_duo256m_sd' configuration
       Trying 'fdt-cv1812cp_milkv_duo256m_sd' fdt subimage
         Description:  cvitek device tree - cv1812cp_milkv_duo256m_sd
         Type:         Flat Device Tree
         Compression:  uncompressed
         Data Start:   0x8182333c
         Data Size:    24599 Bytes = 24 KiB
         Architecture: RISC-V
         Hash algo:    sha256
         Hash value:   fca09bd9678df89606a7d31d37d033745f23ef47701ba482f4637fc0ddbb0715
       Verifying Hash Integrity ... sha256+ OK
       Booting using the fdt blob at 0x8182333c
       Uncompressing Kernel Image
       Decompressing 398856 bytes used 44ms
       Loading Device Tree to 000000008a777000, end 000000008a780016 ... OK
    
    Starting kernel ...
    
    [I/drv.pinmux] Pin Name = "UART0_RX", Func Type = 281, selected Func [0]
    
    [I/drv.pinmux] Pin Name = "UART0_TX", Func Type = 282, selected Func [0]
    
    heap: [0x80292618 - 0x81200000]
    
     \ | /
    
    - RT -     Thread Smart Operating System
      / | \     5.2.0 build Nov  2 2024 12:02:13
       2006 - 2024 Copyright by RT-Thread team
      lwIP-2.1.2 initialized!
      [I/sal.skt] Socket Abstraction Layer initialize success.
      Hello RT-Smart!
      msh />
    
  • 小核: uart1(GP0/GP1)
    运行日志如下:

RT_HW_HEAP_BEGIN:8fe33900 RT_HW_HEAP_END:90000000 size: 1885952

 \ | /
- RT -     Thread Operating System
 / | \     5.2.0 build Nov  2 2024 11:37:57
 2006 - 2024 Copyright by RT-Thread team
Hello, RISC-V!
msh >
```

波特率均为:115200

可通过大核串口日志确认运行的是 rt-smart 系统,小核运行 rt-thread系统。

根文件系统挂载

大核启用 rt-smart 后可以在启动阶段挂载根文件系统。目前支持 ext4, fat 文件格式,内核默认支持 fat,下面介绍 fat 格式文件系统流程。

驱动配置

  1. 根文件系统依赖 SD 卡驱动和 RTC 驱动,可通过 menuconfig 进入 General Drivers Configuration 修改开启。
  2. 使能 BSP_ROOTFS_TYPE_DISKFS ,可通过 menuconfig 进入 rootfs type (Disk FileSystems, e.g. ext4, fat ...) 开启
$ scons --menuconfig
	RT-Thread Kernel  --->
(0xFFFFFFC000200000) The virtural address of kernel start
    RT-Thread Components  --->
    RT-Thread Utestcases  --->
    RT-Thread online packages  --->
    	[X] Enable RTC
		[X] Enable Secure Digital Host Controller
    General Drivers Configuration  --->
(8192) stack size for interrupt
    Board Type (milkv-duo256m)  --->
    rootfs type (Disk FileSystems, e.g. ext4, fat ...)  --->
    	(X) Disk FileSystems, e.g. ext4, fat ...

根文件系统构建

RT-Thread 官方的 userapps 工具制作文件系统。

userapps 仓库地址: https://github.com/RT-Thread/userapps。具体操作参考 《介绍与快速入门》

制作根文件系统步骤如下,供参考:

  1. xmake的安装
$ sudo add-apt-repository ppa:xmake-io/xmake
$ sudo apt update
$ sudo apt install xmake
  1. 根文件系统编译
$ git clone https://github.com/RT-Thread/userapps.git
$ cd userapps
$ source ./env.sh
$ cd apps
$ xmake f -a riscv64gc
$ xmake -j$(nproc)
$ xmake smart-rootfs
$ xmake smart-image -f fat -s 512M

userapps/apps/build 路径下生成根文件系统镜像文件 fat.img

如果是制作 ext4 格式的文件系统 image,则最后一步换成:

$ xmake smart-image -f ext4

生成根文件系统镜像文件 ext4.img

根文件系统写入 SD 卡

将已经打包完成的 fat.img 文件写入上诉 SD 卡第二个分区,请确认当前 SD 卡第二分区为 FAT32,当前步骤在 Ubuntu下进行。

  1. 将 SD 卡插入 PC ,通过 fdisk 命令获取设备号。

    $ sudo fdisk -l
    Disk /dev/sdb: 7.21 GiB, 7742685184 bytes, 15122432 sectors
    Disk model: Storage Device  
    Units: sectors of 1 * 512 = 512 bytes
    Sector size (logical/physical): 512 bytes / 512 bytes
    I/O size (minimum/optimal): 512 bytes / 512 bytes
    Disklabel type: dos
    Disk identifier: 0xc2aebb63
    
    Device     Boot  Start      End  Sectors  Size Id Type
    /dev/sdb1  *         1   262144   262144  128M  c W95 FAT32 (LBA)
    /dev/sdb2       264192 15120383 14856192  7.1G  c W95 FAT32 (LBA)
    

    SD 卡第二分区为 /dev/sdb2

  2. 将 SD卡第二分区挂载到 ~/sd-card

    $ mkdir ~/sd-card
    $ sudo mount /dev/sdb2 ~/sd-card
    
  3. fat.img

    $ mkdir ~/tmp
    $ sudo mount -o loop fat.img ~/tmp
    $ cd ~/tmp
    $ ls
    bin  dev  etc  lib  mnt  proc  root  run  sbin  services  tc  tmp  usr  var
    
  4. 复制 ~/tmp 下的所有文件至 ~/sd-card

    $ cp -a ~/tmp/* ~/sd-card
    
  5. 卸载 SD 卡分区

    $ sudo umount ~/sd-card
    $ sudo umount ~/tmp
    

根文件系统运行

将写入根文件系统的 SD 卡插入开发板,上电运行后,会显示如下日志

Starting kernel ...

[I/drv.pinmux] Pin Name = "UART0_RX", Func Type = 281, selected Func [0]

[I/drv.pinmux] Pin Name = "UART0_TX", Func Type = 282, selected Func [0]

heap: [0x0xffffffc000300110 - 0x0xffffffc000b00110]

 \ | /
- RT -     Thread Smart Operating System
 / | \     5.2.0 build Dec 28 2024 14:06:17
 2006 - 2024 Copyright by RT-Thread team
lwIP-2.1.2 initialized!
[I/sal.skt] Socket Abstraction Layer initialize success.
[I/drivers.serial] Using /dev/ttyS0 as default console
[I/SDIO] SD card capacity 7561216 KB.
[I/SDIO] sd: switch to High Speed / SDR25 mode 

found part[0], begin: 512, size: 128.0MB
found part[1], begin: 135266304, size: 7.86GB
[I/app.filesystem] device 'sd1' is mounted to '/' as FAT
Hello RT-Smart!
msh />
/ # 
/ # 

可以看到日志中显示 / #,表示文件系统已经启动,输入 help 命令可以查看当前支持的命令:

/ # help
Built-in commands:
------------------
        . : [ [[ alias bg break cd chdir command continue echo eval exec
        exit export false fg getopts hash help history jobs kill let
        local printf pwd read readonly return set shift source test times
        trap true type ulimit umask unalias unset wait [ [[ add-shell
        addgroup adduser adjtimex ar arch arp arping ascii ash awk base32
        base64 basename bash bbconfig bc blkid bootchartd bunzip2 busybox
        bzcat bzip2 cal cat chat chattr chgrp chmod chown chpasswd chpst
        chroot chrt chvt cksum clear cmp comm cp cpio crc32 crond crontab
        cryptpw cttyhack cut date dc dd deallocvt delgroup deluser depmod
        devfsd devmem devmem2 df dhcprelay diff dirname dmesg dnsd dnsdomainname
        dos2unix dpkg dpkg-deb du dumpkmap dumpleases echo ed egrep env
        envdir envuidgid expand expr factor fakeidentd fallocate false
        fatattr fbset fdflush fdformat fdisk fgconsole fgrep find findfs
        flock fold free freeramdisk fsck fsck.minix fsfreeze fstrim fsync
        ftpd ftpget ftpput fuser getopt getty grep groups gunzip gzip
        halt hd head hexdump hexedit hostid hostname httpd hush hwclock
        id ifconfig ifdown ifup inetd init inotifyd insmod install iostat
        ipcalc ipcrm ipcs kill killall killall5 klogd less link linux32
        linux64 ln loadkmap logger login logname logread losetup lpd
        lpq lpr ls lsattr lsmod lsof lspci lsscsi lsusb lzcat lzma lzop
        lzopcat makedevs makemime man md5sum mesg microcom mim minips
        mkdir mkfifo mkfs.minix mknod mkpasswd mkswap mktemp modinfo
        modprobe more mount mountpoint mpstat mt mv nc netcat netstat
        nice nl nmeter nohup nologin nproc nsenter nslookup ntpd nuke
        od partprobe passwd paste patch pgrep pidof ping pipe_progress
        pivot_root pkill pmap popmaildir poweroff powertop printenv printf
        ps pscan pstree pwd pwdx rdate rdev readahead readlink readprofile
        realpath reboot reformime remove-shell renice reset resize resume
        rev rm rmdir rmmod route rpm rpm2cpio rtcwake run-parts runsv
        runsvdir rx script scriptreplay sed sendmail seq setarch setconsole
        setfattr setkeycodes setlogcons setserial setsid setuidgid sh
        sha1sum sha256sum sha3sum sha512sum shred shuf sleep smemcap
        softlimit sort split ssl_client start-stop-daemon stat strings
        stty su sulogin sum sv svc svlogd svok swapoff swapon switch_root
        sync sysctl syslogd tac tail tar taskset tcpsvd tee telnet telnetd
        test tftp tftpd time timeout top touch tr traceroute true truncate
        ts tty ttysize tune2fs udhcpd udpsvd umount uname uncompress
        unexpand uniq unix2dos unlink unlzma unlzop unshare unxz unzip
        uptime usleep uudecode uuencode vconfig vi volname watch wc wget
        which whoami whois xargs xxd xz xzcat yes zcat

FAQ

  1. 如遇到不能正常编译,请先使用 scons --menuconfig 重新生成配置。

  2. 错误:./mkimage: error while loading shared libraries: libssl.so.1.1: cannot open shared object file: No such file or directory

    可在 http://security.ubuntu.com/ubuntu/pool/main/o/openssl 下载 libssl1.1_1.1.1f-1ubuntu2_amd64.deb 文件后安装即可解决。
    或使用以下命令下载安装:

    $ wget http://security.ubuntu.com/ubuntu/pool/main/o/openssl/libssl1.1_1.1.1f-1ubuntu2_amd64.deb
    $ sudo dpkg -i libssl1.1_1.1.1f-1ubuntu2_amd64.deb
    
  3. 如发现切换开发板编译正常,但无法正常打包,请切换至自动下载的 cvi_bootloader 目录,并手工运行 git pull 更新,或删除该目录后重新自动下载。

参考文档