Zephyr @ Milk-V Duo S Test Report

System Version

4.1.99

Status

BASIC

Last Update

2026-09-21

Zephyr MilkV DuoS Test Report

Test Environment

Operating System Information

Hardware Information

  • Milk-V Duo S
  • A USB C to C cable
  • A USB to UART Debugger
  • Three DuPont wires
  • SD Card

Installation Steps

Create and Compile BuildRoot

Follow the official tutorial to obtain the source code:

git clone https://github.com/milkv-duo/duo-buildroot-sdk.git --depth=1

Fetch the RISC-V Toolchain

A freshly cloned SDK does not include host-tools/gcc/. Run the SDK’s bundled script to download it first:

cd ~/duo-buildroot-sdk
./build/scripts/download_toolchain.sh

After the download completes, verify the toolchain exists:

ls -lh ~/duo-buildroot-sdk/host-tools/gcc/riscv64-linux-musl-x86_64/bin/riscv64-unknown-linux-musl-gcc

Check that the toolchain runs correctly:

~/duo-buildroot-sdk/host-tools/gcc/riscv64-linux-musl-x86_64/bin/riscv64-unknown-linux-musl-gcc --version

Once the toolchain is confirmed, proceed to U-Boot configuration and compilation.

Modify U-Boot Board Initialization

Modify build/boards/cv181x/cv1813h_milkv_duos_sd/u-boot/cvi_board_init.c to remove all pre-mapped pins, keeping only the UART1 (A19/A18) configuration:

int cvi_board_init(void)
{
        PINMUX_CONFIG(JTAG_CPU_TMS, UART1_TX);  // A19
        PINMUX_CONFIG(JTAG_CPU_TCK, UART1_RX);  // A18

        set_rtc_register_for_power();

        return 0;
}

Sync Board Files into the U-Boot Source Tree

The SDK build script copies board-level files from build/boards/.../u-boot/ into the U-Boot source tree. When building manually, this step must be done explicitly:

# Copy the board defconfig into the U-Boot source tree
cp ~/duo-buildroot-sdk/build/boards/cv181x/cv1813h_milkv_duos_sd/u-boot/cvitek_cv1813h_milkv_duos_sd_defconfig \
   ~/duo-buildroot-sdk/u-boot-2021.10/configs/

# Copy cvi_board_init.c into U-Boot's board directory
mkdir -p ~/duo-buildroot-sdk/u-boot-2021.10/board/cvitek/cv1813h_milkv_duos_sd/
cp ~/duo-buildroot-sdk/build/boards/cv181x/cv1813h_milkv_duos_sd/u-boot/cvi_board_init.c \
   ~/duo-buildroot-sdk/u-boot-2021.10/board/cvitek/cv1813h_milkv_duos_sd/

Verify that the two copies of the board init file are identical:

diff -u \
  build/boards/cv181x/cv1813h_milkv_duos_sd/u-boot/cvi_board_init.c \
  u-boot-2021.10/board/cvitek/cv1813h_milkv_duos_sd/cvi_board_init.c

If the command produces no output and exits with status 0, the two files are identical.

Configure and Compile U-Boot

Configure U-Boot with the Duo S defconfig:

cd ~/duo-buildroot-sdk/u-boot-2021.10

export CROSS_COMPILE="$HOME/duo-buildroot-sdk/host-tools/gcc/riscv64-linux-musl-x86_64/bin/riscv64-unknown-linux-musl-"

make O=build/cv1813h_milkv_duos_sd \
    ARCH=riscv \
    CROSS_COMPILE="$CROSS_COMPILE" \
    cvitek_cv1813h_milkv_duos_sd_defconfig

After configuration, remove the stale object file and its dependency record for cvi_board_init.c, to avoid mistaking a previous build’s artifact for this build’s output:

rm -f build/cv1813h_milkv_duos_sd/board/cvitek/cv1813h_milkv_duos_sd/cvi_board_init.o

rm -f build/cv1813h_milkv_duos_sd/board/cvitek/cv1813h_milkv_duos_sd/.cvi_board_init.o.cmd

Compile U-Boot:

make O=build/cv1813h_milkv_duos_sd \
    CROSS_COMPILE="$CROSS_COMPILE" \
    ARCH=riscv \
    CHIP=cv1813h \
    CVIBOARD=milkv_duos_sd \
    -j"$(nproc)"

After compilation, verify that cvi_board_init.o has been generated:

ls -lh build/cv1813h_milkv_duos_sd/board/cvitek/cv1813h_milkv_duos_sd/cvi_board_init.o

Also check the build dependencies to confirm that cvi_board_init.c was used in this build:

find build/cv1813h_milkv_duos_sd \
    -name '*.cmd' \
    -exec grep -H "cvi_board_init.c" {} \;

In addition, check the source file path in the build output to confirm the file that actually participated in the build is:

u-boot-2021.10/board/cvitek/cv1813h_milkv_duos_sd/cvi_board_init.c

Generate U-Boot Raw Image

Manually generate u-boot-raw.bin:

cp build/cv1813h_milkv_duos_sd/u-boot.bin \
   build/cv1813h_milkv_duos_sd/u-boot-raw.bin

ls -lh build/cv1813h_milkv_duos_sd/u-boot-raw.bin

Flash BuildRoot Image

Download the standard BuildRoot image from Milk-V’s official Releases:

wget https://github.com/milkv-duo/duo-buildroot-sdk/releases/download/v1.1.4/milkv-duos-sd-v1.1.4.img.zip
unzip milkv-duos-sd-v1.1.4.img.zip

Flash it:

sudo dd if=milkv-duos-sd-v1.1.4.img of=/dev/sdX bs=1M status=progress
sync

Install Zephyr

Create a virtual environment:

python3 -m venv ~/zephyrproject/.venv
source ~/zephyrproject/.venv/bin/activate
pip install west

Fetch the specified version of the Zephyr source

Since the Milk-V DuoS support code has not yet been merged into the Zephyr mainline, the source must be obtained from the corresponding development repository and pinned to the tested commit:

4788d883d554d015470eff11883af610aeb6c2cd

Initialize the workspace and fetch dependencies:

west init ~/zephyrproject \
    -m https://github.com/xingrz/zephyr.git \
    --mr 4788d883d554d015470eff11883af610aeb6c2cd

cd ~/zephyrproject
west update

Confirm the Zephyr source is at the specified commit:

cd ~/zephyrproject/zephyr 
git rev-parse HEAD

Configure the environment:

west zephyr-export
pip install -r ~/zephyrproject/zephyr/scripts/requirements.txt

Confirm the output is 4788d883d554d015470eff11883af610aeb6c2cd.

Code Compilation

Compile the code using west:

west build -p always -b milkv_duos/sg2000/c906_1 samples/hello_world

To make hello_world print in a loop for easier observation, modify samples/hello_world/src/main.c:

#include <zephyr/kernel.h>

int main(void)
{
        while (1) {
                printk("Hello World! %s\n", CONFIG_BOARD_TARGET);
                k_msleep(1000);
        }
        return 0;
}

Then rebuild:

west build -p always -b milkv_duos/sg2000/c906_1 samples/hello_world

After a successful build, verify that the Zephyr image has been generated:

ls -lh ~/zephyrproject/zephyr/build/zephyr/zephyr.bin

Merge fip.bin

Mount the SD card with the flashed BuildRoot image, and back up the original fip.bin:

sudo mount /dev/sdX1 /mnt/sdcard
sudo cp /mnt/sdcard/fip.bin /mnt/sdcard/fip.bin.bak

Incrementally replace U-Boot and the small-core firmware:

sudo python3 ~/duo-buildroot-sdk/fsbl/plat/cv181x/fiptool.py \
    -v genfip /mnt/sdcard/fip.bin \
    --OLD_FIP=/mnt/sdcard/fip.bin \
    --LOADER_2ND=$HOME/duo-buildroot-sdk/u-boot-2021.10/build/cv1813h_milkv_duos_sd/u-boot-raw.bin \
    --BLCP_2ND=$HOME/zephyrproject/zephyr/build/zephyr/zephyr.bin

sync
sudo umount /mnt/sdcard

Connect Serial Port

Zephyr uses UART1 :

  • A19 (Pin 18) — TX
  • A18 (Pin 22) — RX
  • GND (Pin 6/14/20/25)

Expected Results

The system should boot normally and information should be viewable via the onboard serial port.

Actual Results

The system booted successfully and information was viewable via the onboard serial port.

Boot Information

*** Booting Zephyr OS build 4788d883d554 ***
Hello World! milkv_duos/sg2000/c906_1
Hello World! milkv_duos/sg2000/c906_1
Hello World! milkv_duos/sg2000/c906_1
Hello World! milkv_duos/sg2000/c906_1
Hello World! milkv_duos/sg2000/c906_1
Hello World! milkv_duos/sg2000/c906_1
Hello World! milkv_duos/sg2000/c906_1
Hello World! milkv_duos/sg2000/c906_1
Hello World! milkv_duos/sg2000/c906_1
Hello World! milkv_duos/sg2000/c906_1
Hello World! milkv_duos/sg2000/c906_1
...

Screen recording :

asciicast

Test Criteria

Successful: The actual result matches the expected result.

Failed: The actual result does not match the expected result.

Test Conclusion

Test successful.