MilkV Board DuoS - johnosbb/Automation GitHub Wiki

MilkV Board Information

image

Introduction of SG2000

SG2000 is a high-performance, low-power chip designed for various product fields such as edge intelligent surveillance IP cameras, local facial recognition attendance machines, and smart home devices. It integrates H.264/H.265 video compression and decoding and ISP capabilities. It supports various image enhancement and correction algorithms like HDR wide dynamic range, 3D noise reduction, defogging, and lens distortion correction, providing customers with professional-grade video image quality.

The chip also integrates an in-house TPU, delivering approximately 0.5TOPS of computing power under INT8 operations. The specially designed TPU scheduling engine efficiently provides high-bandwidth data flow for tensor processing unit cores. It also offers users a powerful deep learning model compiler and software SDK development kit. Mainstream deep learning frameworks such as Caffe, Pytorch, ONNX, MXNet, and TensorFlow (Lite) can be easily ported to this platform.

image

image

Milk-V-Duo S 512MB SG2000 RISC V Linux Board

SG2000 (Duo S)

  • 1GHz and 700MHz RISC-V C906 processors
  • Optional T-Head C906@1GHz or Cortex-A53@1GHz
  • Integrated 0.5TOPS@INT8 TPU for smart detection.
  • SIP DRAM 512MB
  • Supports H.264/H.265 video encoding, up to 5M@30fps.
  • Compatible with high-definition CMOS sensors.
  • Support Multiple storage devices via SPI-NOR, SPI-NAND, eMMC5.0, 2 x SDIO3.0 interfaces
  • Comprehensive ISP features for image optimization.
  • Partial OpenCV library support with CV hardware acceleration.
  • 16-bit audio codec with built-in mic input and output functions.
  • 2L MIPI DSI 5M@30fps
  • 4L or 2L+2L MIPI CSI 5M@30fps
  • Flexible network configurations with 1 Ethernet PHY.
  • LFBGA

-Getting Started

MilkV References

Buildroot

Note: The V1 version of SDK does not support the ARM core of Duo256M and DuoS. If you need to use the ARM core, please use the V2 version of SDK. The V2 version SDK adds support for Duo256M and DuoS ARM cores, and the compilation method is basically the same as the V1 version SDK.

Building the SDCard Image

Note: Before building, deactivate any conda environments.

conda deactivate
./build.sh milkv-duos-glibc-arm64-sd &> build.log

The image can be found in

/mnt/500GB/MilkVDuoS/duo-buildroot-sdk-v2/out/milkv-duos-glibc-arm64-sd_2025-0309-1356.img

The individual components can be found in

/mnt/500GB/MilkVDuoS/duo-buildroot-sdk-v2/install/soc_sg2000_milkv_duos_glibc_arm64_sd/

The image can be copied to the SD Card with

First determine the card location

sudo dmesg

[3825492.511979] sd 6:0:0:1: [sde] Mode Sense: 2f 00 00 00
[3825492.512264] sd 6:0:0:1: [sde] Write cache: disabled, read cache: enabled, doesn't support DPO or FUA
[3825492.518291] sd 6:0:0:1: [sde] Attached SCSI removable disk
cd /mnt/500GB/MilkVDuoS/duo-buildroot-sdk-v2/out/

Then using dd:

sudo dd if=milkv-duos-glibc-arm64-sd_2025-0309-1356.img of=/dev/sde bs=4M status=progress

It should complete with a messsage similar to that below:

224+1 records in
224+1 records out
941621760 bytes (942 MB, 898 MiB) copied, 783.51 s, 1.2 MB/s

To view available targets

cd /mnt/500GB/MilkVDuoS/duo-buildroot-sdk-v2/buildroot-2024.02/output/milkv-duos-glibc-arm64-sd
make show-targets

alsa-lib alsa-utils bluez-tools bluez5_utils bluez5_utils-headers busybox bzip2 c-periphery ca-certificates coreutils cvi-pinmux dbus dbus-glib dhcpcd dnsmasq dropbear duo-pinmux duo-wiringx e2fsprogs ethtool evtest expat file fio gdb gmp gzip host-acl host-attr host-autoconf host-automake host-e2fsprogs host-fakeroot host-genimage host-libtool host-libzlib host-m4 host-makedevs host-mkpasswd host-patchelf host-pkgconf host-skeleton host-tar host-util-linux host-xz host-zlib htop i2c-tools ifupdown-scripts initscripts iperf3 iw json-c kmod libcurl libevent libffi libglib2 libical libnl libopenssl libusb libzlib lsof ncurses netcat ntp openssl p7zip parted pcre2 pv python-certifi python-charset-normalizer python-idna python-pip python-requests python-setuptools python-urllib3 python3 readline skeleton skeleton-init-common skeleton-init-sysv spi-tools spidev_test sqlite strace stress-ng tar toolchain toolchain-external toolchain-external-custom tree tzdata unrar unzip urandom-scripts util-linux wget wpa_supplicant xz zip zlib zstd rootfs-ext2 rootfs-tar

We can also configure buildroot in that directory

Configuring Buildroot

The buildroot overlay can be found in

board/milkv/milkv-duos-glibc-arm64-sd/overlay

I changed this to

/mnt/500GB/MilkVDuoS/duo-buildroot-sdk-v2/overlay

because the default build.sh script removes everything under the buildroot directory

Network Configuration

One can add an interfaces file to the buildroot rootfs overlay

board/milkv/milkv-duos-glibc-arm64-sd/overlay/etc/network/interfaces

You can create a static IP configuration

auto lo
iface lo inet loopback
auto eth0
iface eth0 inet static
    address 192.168.1.205
    netmask 255.255.255.0
    gateway 192.168.1.254

Configuring Busybox

make busybox-menuconfig

Adding Packages

cd /mnt/500GB/MilkVDuoS/duo-buildroot-sdk-v2/buildroot-2024.02/output/milkv-duos-glibc-arm64-sd
make menuconfig

The saved config gets written to

/mnt/500GB/MilkVDuoS/duo-buildroot-sdk-v2/buildroot-2024.02/output/milkv-duos-glibc-arm64-sd/.config

To save our changes

make savedefconfig

Rebuilding the Rootfs

cd /mnt/500GB/MilkVDuoS/duo-buildroot-sdk-v2/buildroot-2024.02/output/milkv-duos-glibc-arm64-sd
make rootfs-tar

We can check the target file system in:

ls /mnt/500GB/MilkVDuoS/duo-buildroot-sdk-v2/buildroot-2024.02/output/milkv-duos-glibc-arm64-sd/target

Build Directory

build files for individual packages can be found in :

output/milkv-duos-glibc-arm64-sd/build/

and the corresponding source

buildroot-2024.02/output/milkv-duos-glibc-arm64-sd/build/duo-wiringx-1.0.3/src

Running Scripts at Startup

Scripts can be called from

/mnt/system/auto.sh

Example

# cat /mnt/system/auto.sh
#!/bin/sh

echo "auto.sh started at $(date)" >> /tmp/auto.log
echo "auto: waiting for usb0..." > /dev/kmsg

# Put the programs you want to run automatically here

# To enable wlan, run this
/usr/bin/start_wlan.sh


# To enable usb0 lan, run this
# /usr/bin/start_usb0_lan.sh

Setting up USB-NCM

You can use any private IP range as long as it doesn’t conflict with your existing network.

On your windows machine you will see an USB-NCM device entry under Network Adapters when you plugin your Duos

image

On Windows:

  • Open Network Connections (Win + R → ncpa.cpl → Enter).
  • Right-click USB-NCM Network Adapter → Properties.
  • Select Internet Protocol Version 4 (TCP/IPv4) → Properties.

Set:

  • IP Address: 192.168.41.2
  • Subnet Mask: 255.255.255.0
  • Default Gateway: (Leave blank or set to 192.168.41.2 if needed)

image

On the Duos

ip addr add 192.168.41.1/24 dev usb0
[root@milkv-duo]~# ip link set usb0 up

You should see

4: usb0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast qlen 1000
    link/ether 06:8c:94:fd:f4:13 brd ff:ff:ff:ff:ff:ff
    inet 192.168.42.1/24 brd 192.168.42.255 scope global usb0
       valid_lft forever preferred_lft forever
    inet 192.168.41.1/24 scope global usb0
       valid_lft forever preferred_lft forever
    inet6 fe80::48c:94ff:fefd:f413/64 scope link
       valid_lft forever preferred_lft forever

Setting USB0 address

You can create a script in: mnt/system/auto.sh. This will run on boot up

#!/bin/sh
echo "auto: proceeding with usb0 bring-up..." > /dev/kmsg
# Wait up to 10 seconds for usb0 to appear
COUNT=0
while ! ip link show usb0 >/dev/null 2>&1; do
    sleep 1
    COUNT=$((COUNT+1))
    if [ $COUNT -ge 10 ]; then
        echo "auto: usb0 not found after 10 seconds" > /dev/kmsg
        exit 1
    fi
done
echo "auto: usb0 detected, proceeding with IP configuration..." > /dev/kmsg
# Now, we can set the IP address
echo "auto: setting IP address of usb0 ..." > /dev/kmsg
ip link set usb0 up
ip addr flush dev usb0
ip addr add 192.168.42.250/24 dev usb0
echo "auto: Completed usb0 configuration." > /dev/kmsg

Booting the Board

The large core of DuoS can choose to use RISC-V or ARM processor, which can be set through the switch on the board. Before attempting to boot ensure you have set the architecture to match you firmware.

image

Setting up Wireless Lan wlan0

/usr/bin/start_wlan.sh
#!/bin/sh

interface="wlan0"
wifi_config="/etc/wpa_supplicant.conf"
static_ip="192.168.1.210"
netmask="255.255.255.0"
gateway="192.168.1.254"
log_file="/var/log/start_wlan.sh.log"

MAX_WAIT=60
WAIT_INTERVAL=5
elapsed=0

echo "$(date +'%Y-%m-%d %H:%M:%S') Running custom wlan startup script..." >> "$log_file"

# Bring down eth0, eth0 appears to interfere with the routing and I have not been able to resolve this.
echo "$(date +'%Y-%m-%d %H:%M:%S') Bringing down eth0..." >> "$log_file"
if ifdown eth0; then
    echo "$(date +'%Y-%m-%d %H:%M:%S') eth0 brought down successfully." >> "$log_file"
else
    echo "$(date +'%Y-%m-%d %H:%M:%S') Failed to bring down eth0." >> "$log_file"
fi

# Wait for wlan0 interface to appear, I notice on the Duos one may have to wait up to 10 seconds.
echo "$(date +'%Y-%m-%d %H:%M:%S') Waiting for wlan0 interface..." >> "$log_file"
while [ $elapsed -lt $MAX_WAIT ]; do
  if ip link show "$interface" > /dev/null 2>&1; then
    echo "$(date +'%Y-%m-%d %H:%M:%S') wlan0 interface found after $elapsed seconds." >> "$log_file"
    break
  fi
  echo "$(date +'%Y-%m-%d %H:%M:%S') wlan0 not found, waiting $WAIT_INTERVAL seconds..." >> "$log_file"
  sleep "$WAIT_INTERVAL"
  elapsed=$((elapsed + WAIT_INTERVAL))
done

if [ $elapsed -ge $MAX_WAIT ]; then
  echo "$(date +'%Y-%m-%d %H:%M:%S') Timeout: wlan0 interface not found after $MAX_WAIT seconds." >> "$log_file"
  exit 1
fi

# Start wpa_supplicant in the background
echo "$(date +'%Y-%m-%d %H:%M:%S') Starting wpa_supplicant..." >> "$log_file"
wpa_supplicant -B -i "$interface" -c "$wifi_config" >> "$log_file" 2>&1 &
sleep 5 # Give wpa_supplicant a few seconds to connect
# One may get a warning:
# nl80211: kernel reports: Authentication algorithm number required
# This can be ignored: see (https://www.linuxquestions.org/questions/slackware-14/new-message-at-boot-nl80211-kernel-reports-authentication-algorithm-number-required-4175687556/)
# Assign static IP address and bring up the interface
echo "$(date +'%Y-%m-%d %H:%M:%S') Assigning static IP address..." >> "$log_file"
ifconfig "$interface" "$static_ip" netmask "$netmask" up
if [ $? -eq 0 ]; then
    echo "$(date +'%Y-%m-%d %H:%M:%S') Static IP assigned to $interface." >> "$log_file"

    # Add default gateway
    echo "$(date +'%Y-%m-%d %H:%M:%S') Adding default gateway..." >> "$log_file"
    route add default gw "$gateway" "$interface"
    if [ $? -eq 0 ]; then
        echo "$(date +'%Y-%m-%d %H:%M:%S') Default gateway added." >> "$log_file"
    else
        echo "$(date +'%Y-%m-%d %H:%M:%S') Failed to add default gateway." >> "$log_file"
    fi
else
    echo "$(date +'%Y-%m-%d %H:%M:%S') Failed to assign static IP address to $interface." >> "$log_file"
fi

echo "$(date +'%Y-%m-%d %H:%M:%S') Custom wlan startup script finished." >> "$log_file"

This assumes you have created a file in

/etc/wpa_supplicant.conf

With the following contents

ctrl_interface=/var/run/wpa_supplicant
ap_scan=1
update_config=1
network={
        ssid="yourssid"
        psk="yourkey"
        key_mgmt=WPA-PSK
}

Trouble-shooting the Wlan0 Interface

iw dev wlan0 link
Connected to 20:23:51:73:5c:0f (on wlan0)
        SSID: ssid_name_appears_herez
        freq: 5220
        RX: 411475 bytes (2290 packets)
        TX: 13108 bytes (168 packets)
        signal: -47 dBm
        rx bitrate: (unknown)
        tx bitrate: 180.0 MBit/s VHT-MCS 9 40MHz VHT-NSS 1

Serial Communication

Connect to the designated serial pins A16 and A17 on the DuoS as shown

image

Use an FTDI converter to connect the board to a usb port.

image

image

image

GPIO Mappings

image

image

image

image

Mapping Input

#!/bin/sh
# A18 (Pin 22): Can be configured as XGPIOA (SG2000 NUM 498)
INPUT_PIN=498
INPUT_GPIO=/sys/class/gpio/gpio${INPUT_PIN}
# Export the GPIO if not already exported
if [ ! -d "${INPUT_GPIO}" ]; then
    echo ${INPUT_PIN} > /sys/class/gpio/export
    sleep 1  # Give some time for export to complete
fi
# Set the direction to input
echo in > ${INPUT_GPIO}/direction
# Enable internal pull-up resistor (if supported)
# echo high > ${INPUT_GPIO}/direction
# Loop to read input value
while true; do
    VALUE=$(cat ${INPUT_GPIO}/value)
    echo "GPIO ${INPUT_PIN} Value: ${VALUE}"
    sleep 1
done

Output Example

#!/bin/sh
# A20 Pin 16 - SG2000 Num 500
LED_PIN=500
LED_GPIO=/sys/class/gpio/gpio${LED_PIN}
if test -d ${LED_GPIO}; then
    echo "PIN ${LED_PIN} already exported"
else
    echo ${LED_PIN} > /sys/class/gpio/export
fi
echo out > ${LED_GPIO}/direction
while true; do
    echo 0 > ${LED_GPIO}/value
    sleep 0.5
    echo 1 > ${LED_GPIO}/value
    sleep 0.5
done

Pinmux

Many pin functions of the Duo series are multiplexed. When using applications (such as wiringX, pinpong) to control the functions of each pin, it is important to confirm the current state of the pin to ensure it matches the desired functionality. If it doesn't, you can use the duo-pinmux command to switch it to the desired function.

Sample TDL SDK Applications

General Setup

The code can be found in

git clone https://github.com/milkv-duo/duo-tdl-examples.git

Get the code and update

cd duo-tdl-examples
git pull

Setup the Tools and Build Environment

source envsetup.sh

Select the board type (2) in the case of the DuoS

Select Product:
1. Duo (CV1800B)
2. Duo256M (SG2002) or DuoS (SG2000)

Select your prefered architecture to mathc your buildroot image (I amusing ARM64)

Select Arch:
1. ARM64
2. RISCV64
Which would you like:

Face Recognition

This requires the camera which you can attach as shown in the image below

image

image

cd sample_vi_fd
make

Copy this to the board The models can be found in

mnt/cvimodel/

To execute this example run

 ./sample_vi_fd /mnt/cvimodel/scrfd_768_432_int8_1x.cvimodel

Then open VLC and run:

rtsp://192.168.1.205/h264

Where 192.168.1.205 is the ip address of the board.

The output will look like this:

RTSP client connected from: 192.168.1.5
face count: 1
face count: 0
face count: 1
face count: 0
face count: 1
face count: 0
face count: 1

image

Object Detection

cd sample_vi_od
make

Copy this to the board The models can be found in

mnt/cvimodel/

To execute this example run

./sample_vi_od mobiledetv2-pedestrian mobiledetv2-pedestrian-d0-ls-448.cvimodel