Updates:
- 2024/02/18: added AlpineLinux for Milk-V Duo 256M
- 2024/02/15: adding Ubuntu-22.04 & ArchLinux disk images for Milk-V Duo 256M version, and new photos of 256M version as well
- 2023/11/20: Ubuntu 22.04 v0.0.2/0.0.3 images released
- 2023/11/02: ArchLinux 2023-10-09 v0.0.3 image released
- 2023/10/31: distro update on Debian RISCV64, Fedora 38, Alpine Linux; description how to set static IP for host when using RNDIS
- 2023/10/21: released ArchLinux disk image with RNDIS support
- 2023/10/16: creating custom disk images, releasing my own disk image tagged “spiritdude”
- 2023/10/11: 2nd option for swap space, OLED SSD1306 example with TinyCC
- 2023/10/10: adding Resizing Disk, Start Script at Boot, TinyCC, GPIO, Pinpong, Software State update
- 2023/10/05: published
- 2023/10/03: adding printable case and guide to add multiple boards on a host
- 2023/09/21: starting writeup
Table of Contents
Introduction
In 2023/09 I purchased a couple of Milk-V Duo boards at USD 5.00 / piece, which supposed are able to boot and run Linux – a very competitive option.
Within 3D printing context it can or could serve as:
- real-time motor controller
- using 2nd core for RTOS (see these threads), not yet tested
- using DMA (see this repo), not yet tested
- tiny AI component to detect failed prints via camera (check these threads on ‘yolo’ and MilkV Duo: TPU)
- running Klipper on it, likely requires expansion board with additional USB ports, not yet tested; ArchLinux repo provides it though
- any kind of quick experimental setup when Linux is a requirement, and Raspberry Pi perhaps overkill already, and ESP32 not powerful enough to run Linux*)
*) as of 2023/09 a few people working on Linux for ESP32-S3
A couple of years ago I used ESP8266 with Lua, and ESP32 with Lua and MicroPython, but the functionality was very limited, although it had WiFi built-in, but barely ran anything more complex or serious – the Milk-V Duo changes this, at the same pricing of USD 5.00-9.00; and I really looking forward to have just a small device running Linux, and competing with Raspberry Pi’s which either hardly were available or sold at 2-3x the announced price.
and the newer 256MB RAM variant (released 2023/12):
Specification & Features have been partially copied from Milkv.io (2023/09 & 2024/02):
Specification
Milkv-DUO (64M) | MILKV-DUO 256M 1) | MILKV-DUO 512M 2) | |
---|---|---|---|
Processor | CVITEK CV1800B (C906@1GHz + C906@700MHz) + 8051@8KB SRAM + TPU | SG 2002 (C906@1GHz + C906@700MHz) + (Cortex-A53@1GHz) + 8051@6KB SRAM + TPU | SG 2000 |
Memory | DDR2 64MB | DDR2 256MB | DDR2 512MB |
Storage | 1x Mirco SD slot 1x SD NAND solder pad | 1x Mirco SD slot 1x SD NAND solder pad | |
USB | 1x USB-C for data and power 1x USB2 solder pad | 1x USB-C for data and power | |
Camera | 1x 16P FPC connector (MIPI CSI 2-lane) | 1x 16P FPC connector (MIPI CSI 2-lane) | |
GPIO | up to 26 Pins available for general purpose I/O(GPIO) | up to 26 Pins available for general purpose I/O(GPIO) | |
Size | 21mm * 51mm (same as Raspberry Pi Pico) | 21mm * 51mm (same as Raspberry Pi Pico) | |
Price | EUR 5.00-7.00 | EUR 7.00-9.00 |
- available since 2024/01
- not yet announced
Features
Processor
- 1GHz and 700MHz RISC-V C906 processors
- 8051with 8/6KB SRAM
- Integrated CVITEK TPU for smart detection
- Supports H.264/H.265 video encoding, up to 2880×1620@20fps
- Compatible with high-definition CMOS sensors
- Programmable frequency output for sensor clock
- 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
- Flexible network configurations with 1 Ethernet PHY
CSI-2 (MIPI serial camera)
- Features a 16-pin FPC interface for 2-lane MIPI camera input
- Operates I2C, CLK, and RST signals at a 1.8V voltage level
Ethernet
- Milk-V Duo includes CV1800B chip with a 100Mbps PHY
- PHY is linked to a 5-pin solder pad
- External transformer and RJ45 socket are needed for Ethernet use
USB
- USB 2.0 compliant, backward compatible with USB 1.1
- Supports various speed modes, Host/Device functionality, and transfer protocols
- Expandable interfaces via USB Hub (up to 127 devices)
- Power-saving mode, supports HID devices
- Functions as USB slave device with configurable software
- USB Type-C for storage media access
Micro SD
- SDIO0 is compatible with Secure Digital Memory (SD 3.0) protocol
No WiFi (but Ethernet over USB)
- it has no WiFi (either use ESP8266 or ESP32 at WiFi gateway via UART, but this kind contradicts the entire single board concept)
- USB(-C) connector can be used in double feature mode: as power supply and pseudo network called RNDIS (virtual Ethernet over USB)
No HDMI / Video Out
- it has no video out, at best I2C or SPI-based LCD can be connected, and function as framebuffer (see this post as a start)
Pinout
- Up to 26 GPIO pins on the MilkV-Duo 40-pin header provide access to internal peripherals such as SDIO, I2C, PWM, SPI, J-TAG, and UART
- Up to 3x I2C
- Up to 5x UART
- Up to 1x SDIO1
- Up to 1x SPI
- Up to 2x ADC
- Up to 7x PWM
- Up to 1x RUN
- Up to 1x JTAG
Distributions for Milk-V Duo 256M
Status 2024/02
DISTRIBUTION | Maturity | Capability | Advantages | disadvantages |
---|---|---|---|---|
Ubuntu 22.04 RISCV64 | ★★☆☆☆ | ★★★☆☆ | – rndis / usb network – package management (apt) works with 240MB available RAM – rootfs 8GB, 600MB used | |
ArchLinux RISCV64 | ★★☆☆☆ | ★★★☆☆ | – package management (pacman) – rootfs 8GB, 960MB used – rndis / usb network | |
AlpineLinux RISCV64 | ★★☆☆☆ | ★★★☆☆ | – package management (apk) – rndis / usb network (but random MAC addresses) – rootfs 1GB, 150MB used |
Distributions for Milk-V Duo (64MB RAM)
Status 2023/11
Distribution | Maturity | Capability | Advantages | disadvantages |
---|---|---|---|---|
Duo BuildRoot SDK | ★★★☆☆ | ★★☆☆☆ | – works – compact – rndis / usb network | – cumbersome bootstrap (make menuconfig) – no easy install of apps on live system |
ArchLinux RISCV64 | ★★☆☆☆ | ★★★☆☆ | – package management (pacman) – rootfs 2/4/8GB, 960MB used – rndis / usb network | |
AlpineLinux RISCV64 | ★★☆☆☆ | ★★★☆☆ | – package management (apk) – rndis / usb network – rootfs 1GB, 150MB used | – use apk [add|update] –no-check-certificate |
Ubuntu 22.04 RISCV64 | ★☆☆☆☆ | ★★★☆☆ | – package management (apt) – rootfs 8GB, 600MB used | – apt is awfully slow due 50+MB RAM requirement |
Debian RISCV64 | ★☆☆☆☆ | ★★★☆☆ | package management (apt/dpkg) | – no rndis (no virtual ether over usb) – very limited amount of packages – 4GB SD card minimum |
Gentoo RISCV | ☆☆☆☆☆ | ★★★☆☆ | – no disk image yet released | |
Fedora RISCV Builder | ☆☆☆☆☆ | ★★★☆☆ | package management (rpm) | – boots, but then fails with systemd (coredump) – no login possible |
Linux BuildRoot Disk Image
The BuildRoot is a minimal custom Linux release which is meant for IoT developers who know what they want and need and select the features at building time and then get a disk image which contains then a set of features and applications. It’s not very user friendly, e.g. there is no package manager which allows to add new packages afterwards once the system is running.
Download
- MilkV-Duo BuildRoot SDK (Releases): contains .img.zip to download direct
(login:root
passwd:milkv
)
Install
Attach a SD card to your host, find out which device it became (Linux):
% lsblk
CAUTION: make sure /dev/sdX
(replace X with proper letter) is the SD card and not your other disk(s) as copying the disk image will erase and replace the content of the device you choose with the following command:
% sudo dd if=milkv-duo-v1.0.4-2023-0908.img of=/dev/sdX bs=1M; sync
then remove SD card from the host*), and insert it to the MilkV-Duo board, and power it on via USB-C.
*) in case you have the wrong /dev/sdX, or write to it if it’s not plugged in, you might struggle to write there again, simply do sudo rm /dev/sdX
and then pull out and plugin the SD card again, and you should be able to write again to it.
automatically starts it USB Ethernet
After ~15 seconds you should be able to login with ssh root@192.168.42.1
into your MilkV-Duo with passwd: milkv
or you attach a UART to USB bridge like that:
- Pin 16/TX: RX/white UART-USB
- Pin 17/RX: TX/green UART-USB
- Pin 18/GND: GND/black UART-USB
- don’t connect 5V/red UART-USB
and use tio /dev/ttyUSB0
(or another number) under Linux to connect via serial port; also useful in case bootup is stuck after doing changes, and ssh
isn’t possible anymore.
Note: you won’t need to solder the male connectors, I usually just insert them loosely and the cable bending gives sufficient connectivity for a brief login to fix things and then remove UART-USB cable again.
Boot dmesg
[ 0.000000] Linux version 5.10.4-tag- (ubuntu@linux) (riscv64-unknown-linux-musl-gcc (Xuantie-900 linux-5.10.4 musl gcc Toolchain V2.6.1 B-20220906) 10.2.0, GNU ld (GNU Binutils) 2.35) #1 PREEMPT Fri Sep 8 17:23:15 CST 2023
[ 0.000000] earlycon: sbi0 at I/O port 0x0 (options '')
[ 0.000000] printk: bootconsole [sbi0] enabled
[ 0.000000] efi: UEFI not found.
[ 0.000000] Ion: Ion memory setup at 0x0000000082473000 size 26 MiB
[ 0.000000] OF: reserved mem: initialized node ion, compatible id ion-region
[ 0.000000] Zone ranges:
[ 0.000000] DMA32 [mem 0x0000000080000000-0x0000000083f3ffff]
[ 0.000000] Normal empty
[ 0.000000] Movable zone start for each node
[ 0.000000] Early memory node ranges
[ 0.000000] node 0: [mem 0x0000000080000000-0x0000000083f3ffff]
[ 0.000000] Initmem setup node 0 [mem 0x0000000080000000-0x0000000083f3ffff]
[ 0.000000] On node 0 totalpages: 16192
[ 0.000000] DMA32 zone: 222 pages used for memmap
[ 0.000000] DMA32 zone: 0 pages reserved
[ 0.000000] DMA32 zone: 16192 pages, LIFO batch:3
[ 0.000000] SBI specification v0.3 detected
[ 0.000000] SBI implementation ID=0x1 Version=0x9
[ 0.000000] SBI v0.2 TIME extension detected
[ 0.000000] SBI v0.2 IPI extension detected
[ 0.000000] SBI v0.2 RFENCE extension detected
[ 0.000000] riscv: ISA extensions acdfimsuv
[ 0.000000] riscv: ELF capabilities acdfimv
[ 0.000000] pcpu-alloc: s0 r0 d32768 u32768 alloc=1*32768
[ 0.000000] pcpu-alloc: [0] 0
[ 0.000000] Built 1 zonelists, mobility grouping on. Total pages: 15970
[ 0.000000] Kernel command line: root=/dev/mmcblk0p2 rootwait rw console=ttyS0,115200 earlycon=sbi loglevel=9 riscv.fwsz=0x80000
[ 0.000000] Dentry cache hash table entries: 8192 (order: 4, 65536 bytes, linear)
[ 0.000000] Inode-cache hash table entries: 4096 (order: 3, 32768 bytes, linear)
[ 0.000000] Sorting __ex_table...
[ 0.000000] mem auto-init: stack:off, heap alloc:off, heap free:off
[ 0.000000] Memory: 29360K/64768K available (3671K kernel code, 457K rwdata, 1651K rodata, 144K init, 195K bss, 35408K reserved, 0K cma-reserved)
[ 0.000000] SLUB: HWalign=64, Order=0-3, MinObjects=0, CPUs=1, Nodes=1
[ 0.000000] rcu: Preemptible hierarchical RCU implementation.
[ 0.000000] rcu: RCU event tracing is enabled.
[ 0.000000] Trampoline variant of Tasks RCU enabled.
[ 0.000000] rcu: RCU calculated value of scheduler-enlistment delay is 25 jiffies.
[ 0.000000] NR_IRQS: 64, nr_irqs: 64, preallocated irqs: 0
[ 0.000000] riscv-intc: 64 local interrupts mapped
[ 0.000000] plic: interrupt-controller@70000000: mapped 101 interrupts with 1 handlers for 2 contexts.
[ 0.000000] random: get_random_bytes called from start_kernel+0x2e0/0x41c with crng_init=0
[ 0.000000] riscv_timer_init_dt: Registering clocksource cpuid [0] hartid [0]
[ 0.000000] clocksource: riscv_clocksource: mask: 0xffffffffffffffff max_cycles: 0x5c40939b5, max_idle_ns: 440795202646 ns
[ 0.000009] sched_clock: 64 bits at 25MHz, resolution 40ns, wraps every 4398046511100ns
[ 0.008425] Calibrating delay loop (skipped), value calculated using timer frequency.. 50.00 BogoMIPS (lpj=100000)
[ 0.019128] pid_max: default: 4096 minimum: 301
[ 0.024025] Mount-cache hash table entries: 512 (order: 0, 4096 bytes, linear)
[ 0.031444] Mountpoint-cache hash table entries: 512 (order: 0, 4096 bytes, linear)
[ 0.041119] ASID allocator initialised with 65536 entries
[ 0.046849] rcu: Hierarchical SRCU implementation.
[ 0.052223] EFI services will not be available.
[ 0.057313] devtmpfs: initialized
[ 0.066807] early_time_log: do_initcalls: 4548979us
[ 0.072472] clocksource: jiffies: mask: 0xffffffff max_cycles: 0xffffffff, max_idle_ns: 7645041785100000 ns
[ 0.082528] futex hash table entries: 16 (order: -4, 384 bytes, linear)
[ 0.089496] pinctrl core: initialized pinctrl subsystem
[ 0.095414] NET: Registered protocol family 16
[ 0.100480] DMA: preallocated 128 KiB GFP_KERNEL pool for atomic allocations
[ 0.107813] DMA: preallocated 128 KiB GFP_KERNEL|GFP_DMA32 pool for atomic allocations
[ 0.116713] thermal_sys: Registered thermal governor 'step_wise'
[ 0.132538] OF: /gpio@03020000/gpio-controller@0: could not find phandle
[ 0.145753] OF: /gpio@03021000/gpio-controller@1: could not find phandle
[ 0.152748] OF: /gpio@03022000/gpio-controller@2: could not find phandle
[ 0.159737] OF: /gpio@03023000/gpio-controller@3: could not find phandle
[ 0.166722] OF: /gpio@05021000/gpio-controller@4: could not find phandle
[ 0.175540] clk reset: nr_reset=64 resource_size=8
[ 0.181130] get audio clk=24576000
[ 0.184672] cvitek-i2s-subsys 4108000.i2s_subsys: Set clk_sdma_aud0~3 to 24576000
[ 0.205405] dw_dmac 4330000.dma: CVITEK DMA Controller, 8 channels, probe done!
[ 0.213805] SCSI subsystem initialized
[ 0.218237] usbcore: registered new interface driver usbfs
[ 0.224017] usbcore: registered new interface driver hub
[ 0.229634] usbcore: registered new device driver usb
[ 0.238395] Ion: ion_parse_dt_heap_common: id 0 type 2 name carveout align 1000
[ 0.246344] Ion: rmem_ion_device_init: heap carveout base 0x0000000082473000 size 0x0000000001acd000 dev (____ptrval____)
[ 0.257624] ion_carveout_heap_create, size=0x1acd000
[ 0.262937] cvi_get_rtos_ion_size, rtos ion_size get:0x0
[ 0.398272] platform carveout: [ion] add heap id 0, type 2, base 0x82473000, size 0x1acd000
[ 0.407276] Advanced Linux Sound Architecture Driver Initialized.
[ 0.414938] clocksource: Switched to clocksource riscv_clocksource
[ 0.423536] NET: Registered protocol family 2
[ 0.429180] tcp_listen_portaddr_hash hash table entries: 256 (order: 0, 4096 bytes, linear)
[ 0.437895] TCP established hash table entries: 512 (order: 0, 4096 bytes, linear)
[ 0.445680] TCP bind hash table entries: 512 (order: 0, 4096 bytes, linear)
[ 0.452950] TCP: Hash tables configured (established 512 bind 512)
[ 0.459557] UDP hash table entries: 128 (order: 0, 4096 bytes, linear)
[ 0.466324] UDP-Lite hash table entries: 128 (order: 0, 4096 bytes, linear)
[ 0.473794] NET: Registered protocol family 1
[ 0.479012] RPC: Registered named UNIX socket transport module.
[ 0.485127] RPC: Registered udp transport module.
[ 0.490010] RPC: Registered tcp transport module.
[ 0.494934] RPC: Registered tcp NFSv4.1 backchannel transport module.
[ 0.504220] Initialise system trusted keyrings
[ 0.509141] workingset: timestamp_bits=62 max_order=13 bucket_order=0
[ 0.524041] squashfs: version 4.0 (2009/01/31) Phillip Lougher
[ 0.531324] jffs2: version 2.2. (NAND) © 2001-2006 Red Hat, Inc.
[ 0.538516] Key type asymmetric registered
[ 0.542733] Asymmetric key parser 'x509' registered
[ 0.554087] Serial: 8250/16550 driver, 5 ports, IRQ sharing disabled
[ 0.562710] printk: console [ttyS0] disabled
[ 0.567239] 4140000.serial: ttyS0 at MMIO 0x4140000 (irq = 15, base_baud = 1562500) is a 16550A
[ 0.576276] printk: console [ttyS0] enabled
[ 0.584875] printk: bootconsole [sbi0] disabled
[ 0.595241] 41c0000.serial: ttyS4 at MMIO 0x41c0000 (irq = 16, base_baud = 1562500) is a 16550A
[ 0.607812] cvi-spif 10000000.cvi-spif: unrecognized JEDEC id bytes: 00 00 00 00 00 00
[ 0.616044] cvi-spif 10000000.cvi-spif: device scan failed
[ 0.621769] cvi-spif 10000000.cvi-spif: unable to setup flash chip
[ 0.635092] libphy: Fixed MDIO Bus: probed
[ 0.639938] bm-dwmac 4070000.ethernet: IRQ eth_wake_irq not found
[ 0.646304] bm-dwmac 4070000.ethernet: IRQ eth_lpi not found
[ 0.652286] bm-dwmac 4070000.ethernet: Hash table entries set to unexpected value 0
[ 0.660361] bm-dwmac 4070000.ethernet: no reset control found
[ 0.666605] bm-dwmac 4070000.ethernet: User ID: 0x10, Synopsys ID: 0x37
[ 0.673525] bm-dwmac 4070000.ethernet: DWMAC1000
[ 0.678424] bm-dwmac 4070000.ethernet: DMA HW capability register supported
[ 0.685641] bm-dwmac 4070000.ethernet: RX Checksum Offload Engine supported
[ 0.692858] bm-dwmac 4070000.ethernet: COE Type 2
[ 0.697743] bm-dwmac 4070000.ethernet: TX Checksum insertion supported
[ 0.704512] bm-dwmac 4070000.ethernet: Normal descriptors
[ 0.710115] bm-dwmac 4070000.ethernet: Ring mode enabled
[ 0.715630] bm-dwmac 4070000.ethernet: Enable RX Mitigation via HW Watchdog Timer
[ 0.723389] bm-dwmac 4070000.ethernet: device MAC address 96:8d:89:ed:5b:ca
[ 0.758200] libphy: stmmac: probed
[ 0.763554] bm-dwmac 4070000.ethernet: Cannot get clk_500m_eth!
[ 0.769815] bm-dwmac 4070000.ethernet: Cannot get gate_clk_axi4!
[ 0.777191] dwc2 4340000.usb: axi clk installed
[ 0.781982] dwc2 4340000.usb: apb clk installed
[ 0.786726] dwc2 4340000.usb: 125m clk installed
[ 0.791530] dwc2 4340000.usb: 33k clk installed
[ 0.796239] dwc2 4340000.usb: 12m clk installed
[ 0.801042] dwc2 4340000.usb: EPs: 8, dedicated fifos, 3072 entries in SPRAM
[ 0.808950] dwc2 4340000.usb: DWC OTG Controller
[ 0.813824] dwc2 4340000.usb: new USB bus registered, assigned bus number 1
[ 0.821096] dwc2 4340000.usb: irq 36, io mem 0x04340000
[ 0.827545] hub 1-0:1.0: USB hub found
[ 0.831570] hub 1-0:1.0: 1 port detected
[ 0.837416] usbcore: registered new interface driver usb-storage
[ 0.844106] i2c /dev entries driver
[ 0.849865] sdhci: Secure Digital Host Controller Interface driver
[ 0.856316] sdhci: Copyright(c) Pierre Ossman
[ 0.860841] sdhci-pltfm: SDHCI platform and OF driver helper
[ 0.867007] cvi:sdhci_cvi_probe
[ 0.914963] mmc0: SDHCI controller on 4310000.cv-sd [4310000.cv-sd] using ADMA 64-bit
[ 0.923131] cvi_proc_init cvi_host 0x(____ptrval____)
[ 0.929146] usbcore: registered new interface driver usbhid
[ 0.938965] usbhid: USB HID core driver
[ 0.944921] cvitek-i2s 4100000.i2s: cvi_i2s_probe
[ 0.955198] cvitek-i2s 4130000.i2s: cvi_i2s_probe
[ 0.960922] cviteka-adc sound_adc: cviteka_adc_probe, dev name=sound_adc
[ 0.968044] cviteka-adc sound_adc: cviteka_adc_probe start devm_snd_soc_register_card
[ 0.976650] cvitekaadc 300a100.adc: cvitekaadc_probe
[ 0.987619] cviteka-dac sound_dac: cviteka_dac_probe, dev name=sound_dac
[ 0.995091] cvitekadac 300a000.dac: cvitekadac_probe
[ 1.000685] cvitekadac_probe gpio_is_valid mute_pin_l
[ 1.006585] NET: Registered protocol family 17
[ 1.011477] Loading compiled-in X.509 certificates
[ 1.039015] mmc0: new SDHC card at address 0001
[ 1.051483] mmcblk0: mmc0:0001 MSD20 14.6 GiB
[ 1.061126] cviteka-adc sound_adc: cviteka_adc_probe, dev name=sound_adc
[ 1.068302] cviteka-adc sound_adc: cviteka_adc_probe start devm_snd_soc_register_card
[ 1.080491] mmcblk0: p1 p2 p3
[ 1.088431] cviteka-dac sound_dac: cviteka_dac_probe, dev name=sound_dac
[ 1.101557] cfg80211: Loading compiled-in X.509 certificates for regulatory database
[ 1.112515] cfg80211: Loaded X.509 cert 'sforshee: 00b28ddf47aef9cea7'
[ 1.119630] cfg80211: failed to load regulatory.db
[ 1.124824] ALSA device list:
[ 1.128383] dw-apb-uart 4140000.serial: forbid DMA for kernel console
[ 1.148546] EXT4-fs (mmcblk0p2): mounted filesystem with ordered data mode. Opts: (null)
[ 1.157087] VFS: Mounted root (ext4 filesystem) on device 179:2.
[ 1.166212] devtmpfs: mounted
[ 1.169547] Freeing unused kernel memory: 144K
[ 1.174226] Kernel memory protection not selected by kernel config.
[ 1.180757] Run /sbin/init as init process
[ 1.185024] with arguments:
[ 1.188116] /sbin/init
[ 1.190917] with environment:
[ 1.194189] HOME=/
[ 1.196652] TERM=linux
[ 1.199477] early_time_log: run_init_process: 5681657us
[ 1.307708] EXT4-fs (mmcblk0p2): re-mounted. Opts: errors=remount-ro
[ 1.395862] random: fast init done
[ 1.511578] random: dd: uninitialized urandom read (512 bytes read)
[ 1.725888] random: dhcpcd: uninitialized urandom read (112 bytes read)
[ 1.817530] bm-dwmac 4070000.ethernet eth0: PHY [stmmac-0:00] driver [Generic PHY] (irq=POLL)
[ 1.839025] dwmac1000: Master AXI performs any burst length
[ 1.845702] bm-dwmac 4070000.ethernet eth0: No Safety Features support found
[ 1.853923] bm-dwmac 4070000.ethernet eth0: IEEE 1588-2002 Timestamp supported
[ 1.862474] bm-dwmac 4070000.ethernet eth0: configuring for phy/rmii link mode
[ 7.195361] random: dnsmasq: uninitialized urandom read (128 bytes read)
[ 7.202528] random: dnsmasq: uninitialized urandom read (48 bytes read)
[ 7.250085] cv180x_sys: bad vermagic: kernel tainted.
[ 7.255425] Disabling lock debugging due to kernel taint
[ 7.261336] cv180x_sys: loading out-of-tree module taints kernel.
[ 7.294527] res-reg: start: 0xa0c8000, end: 0xa0c801f, virt-addr(ffffffd0040c9000).
[ 7.303316] CVITEK CHIP ID = 22
[ 7.318433] cvi_rtos_cmdqu_probe start ---
[ 7.322806] name=1900000.rtos_cmdqu
[ 7.327194] res-reg: start: 0x1900000, end: 0x1900fff, virt-addr(ffffffd004228000).
[ 7.335254] cvi_rtos_cmdqu_probe DONE
[ 7.339691] [cvi_spinlock_init] success
[ 7.377509] cif a0c2000.cif: cam0 clk installed
[ 7.382321] cif a0c2000.cif: cam1 clk installed
[ 7.387481] cif a0c2000.cif: vip_sys_2 clk installed
[ 7.392945] cif a0c2000.cif: clk_mipimpll clk installed (____ptrval____)
[ 7.400203] cif a0c2000.cif: clk_disppll clk installed (____ptrval____)
[ 7.407370] cif a0c2000.cif: clk_fpll clk installed (____ptrval____)
[ 7.414298] cif a0c2000.cif: (0) res-reg: start: 0xa0c2000, end: 0xa0c3fff.
[ 7.421797] cif a0c2000.cif: virt-addr((____ptrval____))
[ 7.427690] cif a0c2000.cif: (1) res-reg: start: 0xa0d0000, end: 0xa0d0fff.
[ 7.435183] cif a0c2000.cif: virt-addr((____ptrval____))
[ 7.441074] cif a0c2000.cif: (2) res-reg: start: 0xa0c4000, end: 0xa0c5fff.
[ 7.448559] cif a0c2000.cif: virt-addr((____ptrval____))
[ 7.454448] cif a0c2000.cif: (3) res-reg: start: 0x3001c30, end: 0x3001c5f.
[ 7.461933] cif a0c2000.cif: virt-addr((____ptrval____))
[ 7.467824] cif a0c2000.cif: no pad_ctrl for cif
[ 7.472974] cif a0c2000.cif: request irq-26 as cif-irq0
[ 7.478759] cif a0c2000.cif: request irq-27 as cif-irq1
[ 7.484514] cif a0c2000.cif: rst_pin = 424, pol = 1
[ 7.499342] snsr_i2c snsr_i2c: i2c:-------hook 0
[ 7.504362] snsr_i2c snsr_i2c: i2c:-------hook 1
[ 7.509771] snsr_i2c snsr_i2c: i2c:-------hook 2
[ 7.515073] snsr_i2c snsr_i2c: i2c:-------hook 3
[ 7.519993] snsr_i2c snsr_i2c: i2c:-------hook 4
[ 7.596322] vi_core_probe:203(): res-reg: start: 0xa000000, end: 0xa07ffff, virt-addr(ffffffd004400000).
[ 7.606219] vi_core_probe:216(): irq(28) for isp get from platform driver.
[ 7.614260] vi_tuning_buf_setup:253(): tuning fe_addr[0]=0x8183f490, be_addr[0]=0x81837290, post_addr[0]=0x81820000
[ 7.625482] vi_tuning_buf_setup:253(): tuning fe_addr[1]=0x8193f490, be_addr[1]=0x81937290, post_addr[1]=0x81920000
[ 7.636654] vi_tuning_buf_setup:253(): tuning fe_addr[2]=0x8185f490, be_addr[2]=0x81857290, post_addr[2]=0x81840000
[ 7.647773] sync_task_init:177(): sync_task_init vi_pipe 0
[ 7.653739] sync_task_init:177(): sync_task_init vi_pipe 1
[ 7.659725] sync_task_init:177(): sync_task_init vi_pipe 2
[ 7.666173] vi_core_probe:252(): isp registered as cvi-vi
[ 7.750302] cvi_dwa_probe:487(): done with rc(0).
[ 7.791388] cv180x-cooling cv180x_cooling: elems of dev-freqs=6
[ 7.797661] cv180x-cooling cv180x_cooling: dev_freqs[0]: 850000000 500000000
[ 7.805487] cv180x-cooling cv180x_cooling: dev_freqs[1]: 425000000 375000000
[ 7.813089] cv180x-cooling cv180x_cooling: dev_freqs[2]: 425000000 300000000
[ 7.820782] cv180x-cooling cv180x_cooling: Cooling device registered: cv180x_cooling
[ 7.866285] jpu ctrl reg pa = 0xb030000, va = (____ptrval____), size = 256
[ 7.873985] end jpu_init result = 0x0
[ 8.065890] cvi_vc_drv_init result = 0x0
[ 8.080795] sh (172): drop_caches: 3
[ 8.261909] using random self ethernet address
[ 8.261928] using random host ethernet address
[ 8.309722] usb0: HOST MAC 6e:54:6b:61:a9:f5
[ 8.312146] usb0: MAC ce:b2:db:da:82:7b
[ 8.312220] dwc2 4340000.usb: bound driver configfs-gadget
[ 8.346187] dwc2 4340000.usb: new device is high-speed
[ 8.391171] dwc2 4340000.usb: new device is high-speed
[ 8.682141] dwc2 4340000.usb: new device is high-speed
[ 8.750359] dwc2 4340000.usb: new address 14
[ 57.067015] random: crng init done
Note: usb0: HOST MAC and MAC are set randomly and change after each (re)boot.
/proc/cpuinfo
[root@milkv-duo]~# more /proc/cpuinfo
processor : 0
hart : 0
isa : rv64imafdvcsu
mmu : sv39
df
[root@milkv-duo]~# df
Filesystem 1K-blocks Used Available Use% Mounted on
/dev/root 763327 157774 562136 22% /
devtmpfs 14680 0 14680 0% /dev
tmpfs 14752 0 14752 0% /dev/shm
tmpfs 14752 52 14700 0% /tmp
tmpfs 14752 28 14724 0% /run
[root@milkv-duo]~# df -h
Filesystem Size Used Available Use% Mounted on
/dev/root 745.4M 154.1M 549.0M 22% /
devtmpfs 14.3M 0 14.3M 0% /dev
tmpfs 14.4M 0 14.4M 0% /dev/shm
tmpfs 14.4M 52.0K 14.4M 0% /tmp
tmpfs 14.4M 28.0K 14.4M 0% /run
ps
PID USER COMMAND
1 root init
2 root [kthreadd]
3 root [rcu_gp]
4 root [rcu_par_gp]
5 root [kworker/0:0-eve]
7 root [kworker/u2:0-ev]
8 root [mm_percpu_wq]
9 root [ksoftirqd/0]
10 root [rcu_preempt]
11 root [kdevtmpfs]
12 root [rcu_tasks_kthre]
13 root [oom_reaper]
14 root [writeback]
15 root [kcompactd0]
24 root [kblockd]
25 root [watchdogd]
27 root [kworker/0:1H-mm]
28 root [rpciod]
29 root [kworker/u3:0]
30 root [xprtiod]
31 root [cfg80211]
32 root [kswapd0]
33 root [nfsiod]
34 root [spi0]
35 root [spi1]
36 root [stmmac_wq]
37 root [kworker/u2:1]
70 root [irq/45-cviusb-o]
71 root [irq/46-cd-gpio-]
72 root [sdhci]
73 root [irq/23-mmc0]
75 root [ion_system_heap]
76 root [mmc_complete]
81 root [jbd2/mmcblk0p2-]
82 root [ext4-rsv-conver]
97 root /sbin/syslogd -n
101 root /sbin/klogd -n
131 dhcpcd dhcpcd: [master] [ip4]
132 root dhcpcd: [privileged actioneer]
133 dhcpcd dhcpcd: [network proxy]
134 dhcpcd dhcpcd: [control proxy]
147 root [kworker/0:3-eve]
156 root /usr/sbin/ntpd -g -p /var/run/ntpd.pid
165 root /usr/sbin/dropbear -R
170 nobody /usr/sbin/dnsmasq
180 root [cvitask_isp_pre]
181 root [cvitask_isp_bla]
182 root [cvitask_isp_err]
184 root [cvitask_vpss_0]
185 root [cvitask_vpss_1]
187 root [gdc_work]
192 root [cvitask_tpu_wor]
198 root {S99user} /bin/sh /etc/init.d/S99user start
199 root [kworker/0:2H]
211 root -sh
1297 root /usr/sbin/dropbear -R
1302 root -sh
1331 root sleep 0.5
1332 root ps
/bin
[root@milkv-duo]~# ls /bin
arch dmesg linux64 nuke sleep
ash dnsdomainname ln pidof stty
base32 dumpkmap login ping su
base64 echo ls pipe_progress sync
busybox egrep lsattr printenv tar
cat false mk_cmds ps touch
chattr fdflush mkdir pwd true
chgrp fgrep mknod resume umount
chmod getopt mktemp rm uname
chown grep more rmdir usleep
compile_et gunzip mount run-parts vi
cp gzip mountpoint sed watch
cpio hostname mt setarch zcat
date kill mv setpriv
dd link netstat setserial
df linux32 nice sh
/usr/bin
[root@milkv-duo]~# ls /usr/bin/
[ fold od tee
[[ free openvt telnet
ar fuser passwd test
ascii gcore paste tftp
awk gdb patch time
basename gdb-add-index pip top
bc head pip3 tr
bunzip2 hexdump pip3.9 traceroute
bzcat hexedit printf truncate
chrt hostid pyserial-miniterm ts
chvt htop pyserial-ports tty
cksum id python uniq
clear install python3 unix2dos
cmp ipcrm python3.9 unlink
crc32 ipcs readlink unlzma
crontab killall realpath unlzop
cut last renice unxz
cvi_pinmux less reset unzip
dbclient logger resize uptime
dc logname scp uudecode
deallocvt lsof seq uuencode
diff lspci setfattr vlock
dirname lsscsi setkeycodes w
dos2unix lsusb setsid wc
dropbearconvert lzcat sha1sum wget
dropbearkey lzma sha256sum which
du lzopcat sha3sum who
easy_install md5sum sha512sum whoami
easy_install-3.9 mesg shred xargs
eject microcom smtpd.py.9 xmlcatalog
env mkfifo sort xmllint
event_rpcgen.py mkpasswd ssh xmlwf
evtest nl strace xsltproc
expr nohup strace-log-merge xxd
factor nproc strings xz
fallocate nslookup svc xzcat
find ntpdate svok yes
flock ntptime tail
[root@milkv-duo]~# ls -1 /usr/bin/ | wc -l
151
Multiple Milk-V Duos / Alternative IPs
In order to support multiple Milk-V Duos on the same host via USB-C, you assign for each board its own network:
- board 1: 192.168.51.0
- board 2: 192.168.52.0
- board 3: 192.168.53.0
Edit on each board two files:
/mnt/system/usb-rndis.sh
(buildroot-based) or /etc/usb-rndis.sh
(other systems):
ifconfig usb0 192.168.51.1
/etc/dnsmasq.conf
:
dhcp-range=192.168.51.2,192.168.51.242,1h
In order to add a new board, you login into 192.168.42.1 as usual, and change it to the 192.168.52.1 and so on.
Resizing Disk
By default the entire available space of the SD card is only 1GB (or 2GB in case you use another distro), but you can make the rest of the SD card available to /data
for example – part of the guide was taken from a post in the forum but updated it:
% mkdir /data
% fdisk /dev/mmcblk0
n (new partition)
p (primary partition)
4
<RETURN> (confirm start selection)
<RETURN> (confirm end selection)
w
q
% reboot
and login again, continue with:
% mkfs.ext4 /dev/mmcblk0p4
% echo "/dev/mmcblk0p4 /data ext4 defaults 0 0" >> /etc/fstab
% reboot
once you login again, you see the new available space:
% df -h
Filesystem Size Used Available Use% Mounted on
/dev/root 745.4M 154.1M 549.0M 22% /
devtmpfs 14.3M 0 14.3M 0% /dev
tmpfs 14.4M 0 14.4M 0% /dev/shm
tmpfs 14.4M 52.0K 14.4M 0% /tmp
tmpfs 14.4M 28.0K 14.4M 0% /run
/dev/mmcblk0p4 13.4G 24.0K 12.7G 0% /data
Making Swap Space
pip
won’t work by default, as there is too little memory to work – so you can make swap space in two ways:
mmcblk0p3: unused 256M partition
As of system image v1.0.4 there is an unused partition you can activate:
% mkswap /dev/mmcblk0p3
% swapon /dev/mmcblk0p3
% echo "/dev/mmcblk0p3 swap swap defaults 0 0" >> /etc/fstab
Swapfile
Or you can create a 256M swapfile to increase available memory, given we claimed the rest of the SD card as /data
as previously shown:
% cd /data
% fallocate -l 256M swapfile
% chmod 600 swapfile
% mkswap swapfile
% swapon swapfile
and to make it permanent:
% echo "/data/swapfile swap swap defaults 0 0" >> /etc/fstab
ArchLinux Disk Image
I followed this guide to get ArchLinux working – thanks to Judehahh doing the main work – and added RNDIS support (Virtual Ethernet over USB) and made a disk image to use, the date e.g. “2023-10-09
” references the riscv64 rootfs date which was unpacked as a base, the “x.xgb
” describes the size of disk or rootfs and “vX.X.X
” the actual release version. Unzip downloaded image first before writing on the SD card.
ssh root@192.168.42.1
passwd: milkv
Note: these are very experimental disk images.
Milk-V Duo 256M
Disk Image | Features | Included |
---|---|---|
milkv-duo-256m-archlinux-riscv64-2023-10-09-7.0gb-v0.0.3-spiritdude.img md5:218c1fb27addb4d2a7d455a5c9b8ea7a BuildRoot v1.0.4 2024/02/15 | – 240MB RAM (no camera support) – 250MB swap enabled – RNDIS (connect via usb virtual ether) – minimum 8GB SD card – 5.7GB free in rootfs – persistent MAC addresses for RNDIS (internet routing ready) | – pacman (pkg mgr) – ssh (dropbear v2022.83) – python 3.11 – make – tinycc/tcc – lua/luac – lighttpd |
For the 256m
-version disk image I downloaded the kernel from this thread and just copied boot.sd
and fip.bin
into the first partition, otherwise no changes toward the milkv-duo
disk-image (below) was made
Milk-V Duo (64MB)
Disk Image | Features | Included |
---|---|---|
milkv-duo-archlinux-riscv64-2023-10-09-7.0gb-v0.0.3-spiritdude.img md5:8c217f01dac7689f1f61ad9e1674913d BuildRoot v1.0.4 2023/11/02 | – 55MB RAM (no camera support) – 250MB swap enabled – RNDIS (connect via usb virtual ether) – minimum 8GB SD card – 5.7GB free in rootfs – persistent MAC addresses for RNDIS (internet routing ready) | – pacman (pkg mgr) – ssh (dropbear v2022.83) – python 3.11 – make – tinycc/tcc – lua/luac – lighttpd |
milkv-duo-archlinux-riscv64-2023-10-09-7.0gb-v0.0.2-spiritdude.img md5:b9ed3f8fb036f74733e2806dff9582bb BuildRoot v1.0.4 2023/10/25 | – 55MB RAM (no camera support) – 250MB swap enabled – RNDIS (connect via usb virtual ether) – minimum 8GB SD card – 5.7GB free in rootfs | – pacman (pkg mgr) – ssh (dropbear v2022.83) – python 3.11 – make – tinycc/tcc – lua/luac – lighttpd |
milkv-duo-archlinux-riscv64-2023-10-09-7.0gb-v0.0.1-spiritdude.img md5:a137a86a0dad572014afe2f2eda78a2c BuildRoot v1.0.4 2023/10/24 | – 55MB RAM (no camera support) – 250MB swap enabled – RNDIS (connect via usb virtual ether) – minimum 8GB SD card – 5.7GB free in rootfs | – pacman (pkg mgr) – ssh (dropbear v2022.83) – python 3.11 – make – tinycc/tcc – lua/luac – lighttpd |
milkv-duo-archlinux-riscv64-2023-10-09-2.5gb-v0.0.1-spiritdude.img md5:2ff352bcb7a9d855dfe50b4d3176f69c BuildRoot v1.0.4 2023/10/21 | – 55MB RAM (no camera support) – 250MB swap enabled – RNDIS (connect via usb virtual ether) – minimum 4GB SD card – 960 apps in /usr/bin/ – 1GB free in rootfs | – pacman (pkg mgr) – ssh (dropbear v2022.83) – python 3.11 – make – tinycc/tcc – lua/luac – lighttpd |
milkv-duo-archlinux-riscv64-2023-10-09-1.5gb-v0.0.1-spiritdude.img md5:ecc2ab2076ad9fbec795c5dba73a291e BuildRoot v1.0.4 2023/10/21 | – 55MB RAM (no camera support) – 250MB swap enabled – RNDIS (connect via usb virtual ether) – minimum 2GB SD card – 960 apps in /usr/bin/ – only 40MB free in rootfs (!!) | – pacman (pkg mgr) – ssh (dropbear v2022.83) – python 3.11 – make – tinycc/tcc – lua/luac – lighttpd |
You have a full Linux system with 55MB RAM available . . .
Notes:
pacman -Fy 'term'
fails for me (too much memory needed), instead rungzip -d -c /var/lib/pacman/sync/*.files | grep -ai 'term'
AlpineLinux Disk Image
As this discussion thread, Chaiwat Suttipongsakul did the disk image hosted on github, I made a mirror and added date of his ‘cwt’ handle to distinct it from future releases:
Milk-V Duo 256M
DISK IMAGE | Features | Included |
---|---|---|
milkv-duo-256m-alpinelinux-cwt-2023-10-28.img md5: 057d0304d958de7b7323d6d963d801e2 2024-02-18 | – package management (apk) – rndis / usb network (but random MAC addresses) – rootfs 1GB, 150MB used | – dropbear v2022.83 – use apk [add|update] –no-check-certificate |
For the 256m
-version disk image I downloaded the kernel from this thread and just copied boot.sd
and fip.bin
into the first partition, otherwise no changes toward the milkv-duo
disk-image (below) was made.
Milk-V Duo (64MB)
Disk Image | Features | included |
---|---|---|
milkv-duo-alpinelinux-cwt-2023-10-28.img md5: dbc23ca7b372ce7718924067ac846693 2023-10-28 | – package management (apk) – rndis / usb network (but random MAC addresses) – rootfs 1GB, 150MB used | – dropbear v2022.83 – use apk [add|update] –no-check-certificate |
Notes:
- use
apk [add|update] --no-check-certificate
to install new packages, otherwise installs or update fail
Ubuntu Disk Image
I followed this guide (see discussion thread as well) – thanks to Bassusteur – and added RNDIS related services so you can login with ssh root@192.168.42.1
(passwd milkv
) via virtual Ethernet over USB; unzip disk image before you write on the SD card.
Notes:
apt
/apt-get
are awfully slow on Milk-V Duo at step “Building dependency tree...
“, takes 4+mins for eachapt install
call asapt
requires 50+MB RAM to build that dependency tree, which goes hard on all available RAM + swap- these are very experimental disk images
Note: milkv-duo
(64MB RAM) vs milkv-duo-256m
(256MB RAM)
Milk-V Duo 256M
Disk Image | Features | Included |
---|---|---|
milkv-duo-256m-ubuntu-22.04-riscv64-v0.0.4-spiritdude.img md5: 0a80c70d9a6b2e763f49b7dca7aba59f BuildRoot v1.0.5 2024/02/15 | – 240MB RAM (no camera support) – RNDIS (connect via usb virtual ether) – persistent MAC addresses for RNDIS (internet routing ready) – apt works now smoothly – minimum 8GB SD card – 6.0GB free in rootfs | – dropbear (v2020.81) – python 3.10.12 |
For the 256m
-version disk image I downloaded the kernel from this thread and just copied boot.sd
and fip.bin
into the first partition, otherwise no changes toward the milkv-duo
disk-image (below) was made.
Milk-V Duo (64MB)
Disk Image | Features | Included |
---|---|---|
milkv-duo-ubuntu-22.04-riscv64-v0.0.4-spiritdude.img md5: 21833b5041e4f6a1706e910c491ba2d3 BuildRoot v1.0.5 2023/11/22 | – 55MB RAM (no camera support) – 250MB swap enabled – RNDIS (connect via usb virtual ether) – persistent MAC addresses for RNDIS (internet routing ready) – minimum 8GB SD card – 6.0GB free in rootfs | – dropbear (v2020.81) – python 3.10.12 – zram enabled in kernel, and works now, but apt still very slow |
milkv-duo-ubuntu-22.04-riscv64-v0.0.3-spiritdude.img md5: 9f8d3ab61a7ea328b8da5217f020c2b2 BuildRoot v1.0.5 2023/11/21 | – 55MB RAM (no camera support) – 250MB swap enabled – RNDIS (connect via usb virtual ether) – persistent MAC addresses for RNDIS (internet routing ready) – minimum 8GB SD card – 6.0GB free in rootfs | – dropbear (v2020.81) – python 3.10.12 – zram not enabled in kernel yet |
milkv-duo-ubuntu-22.04-riscv64-v0.0.2-spiritdude.img md5: bb05d70a0e81169ef2af05b68ef82ced BuildRoot v1.0.4 2023/11/20 | – 55MB RAM (no camera support) – 250MB swap enabled – RNDIS (connect via usb virtual ether) – persistent MAC addresses for RNDIS (internet routing ready) – minimum 8GB SD card – 6.0GB free in rootfs | – dropbear (v2020.81) – python 3.10.12 |
Changes done compared to this guide:
- removed
dhcpcd
as is clashes withdnsmasq
- added RNDIS related scripts, incl.
dnsmasq
andrndis.service
- disabled
systemd-resolved
service due clash withdnsmasq
- added
/etc/resolv.conf.tail
with default DNS servers - installed
dropbear
(lightweight sshd), removed the auto generated keys, added in/etc/default/dropbear
the-R
switch so new keys are generated at first boot
Tips & Examples
Most of the examples relate to the Duo BuildRoot SDK setup, but should be easily adaptable to ArchLinux or other distros.
blink.py with sysfs GPIO
There is a way to control GPIO via sysfs with Python:
- download https://github.com/vitiral/gpio release: gpio-1.0.0.zip
- copy it on the board
- unzip it
Note: you require more memory to run pip
, use guide Make Swap Space (previous section) then proceed:
% cd gpio-1.0.0
% pip install .
% chmod +r /sys/class/gpio/export
then use this script blink.py
:
import time
import gpio as GPIO
pin = 440
GPIO.setup(pin, GPIO.OUT)
while True:
GPIO.output(pin, GPIO.HIGH)
time.sleep(1.0)
GPIO.output(pin, GPIO.LOW)
time.sleep(1.0)
and run it:
% python blink.py
See this table for GPIO names, pins and numbers, a copy (2023/10/10):
GPIO NAME | GPIO PIN | GPIO Number | Notes |
---|---|---|---|
GPIOA14 | 19 | 494 | |
GPIOA15 | 20 | 495 | |
GPIOA16 | 16 | 496 | |
GPIOA17 | 17 | 497 | |
GPIOA22 | 24 | 502 | |
GPIOA23 | 21 | 503 | |
GPIOA24 | 22 | 504 | |
GPIOA25 | 25 | 505 | |
GPIOA26 | 27 | 506 | |
GPIOA27 | 26 | 507 | |
GPIOA28 | 1 | 508 | |
GPIOA29 | 2 | 509 | |
GPIOC9 | 14 | 425 | 1.8V |
GPIOC10 | 15 | 426 | 1.8V |
PWR_GPIO4 | 29 | 356 | 1.8V |
PWR_GPIO18 | 12 | 370 | |
PWR_GPIO19 | 6 | 371 | |
PWR_GPIO20 | 7 | 372 | |
PWR_GPIO21 | 11 | 373 | |
PWR_GPIO22 | 10 | 374 | |
PWR_GPIO23 | 9 | 375 | |
PWR_GPIO25 | 5 | 377 | |
PWR_GPIO26 | 4 | 378 |
Pinpong
Follow the guide to install pinpong.zip
(my mirror), only for V1.0.4 system image, and then:
blink2.py
import time
from pinpong.board import Board,Pin
Board("MILKV-DUO").begin()
led = Pin(Pin.D0, Pin.OUT)
while True:
led.value(1)
time.sleep(1)
led.value(0)
time.sleep(1)
The pinpong library covers quite a lot of functionality, and useful examples:
root@milkv-duo2]~# ls /usr/lib/python3.9/site-packages/pinpong/examples/milkv-Duo/
__init__.py gravityPM2.5.py oled2864.pyc
__init__.pyc i2c.py ozone.py
adc.py i2c.pyc ozone.pyc
adc.pyc i2c_scan.py paj7620.py
as7341.py i2c_scan.pyc paj7620.pyc
as7341.pyc iic_to_serial.py ph1.py
blink.py iic_to_serial.pyc ph1.pyc
blink.pyc ir_recv.py ph2.py
bme280.py ir_recv.pyc ph2.pyc
bme280.pyc ir_send.py pwm.py
bme680.py ir_send.pyc pwm.pyc
bme680.pyc irq.py rgb_panel.py
bmi160_acc.py irq.pyc rgb_panel.pyc
bmi160_acc.pyc lcd1602.py sen0483.py
bmi160_step.py lcd1602.pyc sen0483.pyc
bmi160_step.pyc lis2dh.py servo.py
bmp280.py lis2dh.pyc servo.pyc
bmp280.pyc max30103.py sht31.py
bmp388.py max30103.pyc sht31.pyc
bmp388.pyc mics_enable_power.py speech_synthesis.py
button.py mics_enable_power.pyc speech_synthesis.pyc
button.pyc mics_get_adc_data.py spi.py
buzzer.py mics_get_adc_data.pyc spi.pyc
buzzer.pyc mics_get_gas_exist.py sr04_urm10.py
ccs811_read_baseline.py mics_get_gas_exist.pyc sr04_urm10.pyc
ccs811_read_baseline.pyc mics_get_gas_ppm.py st7789-as7341.py
ccs811_read_data.py mics_get_gas_ppm.pyc st7789.py
ccs811_read_data.pyc mlx90614.py st7789.pyc
dht.py mlx90614.pyc tcs34725.py
dht.pyc mp3.py tcs34725.pyc
dht20.py mp3.pyc tds.py
dht20.pyc neopixel.py tds.pyc
ds0469.py neopixel.pyc tone.py
ds0469.pyc nfc.py tone.pyc
ds1307.py nfc.pyc uart.py
ds1307.pyc nfc_card_info.py uart.pyc
ds18b20.py nfc_card_info.pyc urm09.py
ds18b20.pyc nfc_uart.py urm09.pyc
ens160.py nfc_uart.pyc vl53l0.py
ens160.pyc nfc_uart_card.py vl53l0.pyc
gp2y1010au0f.py nfc_uart_card.pyc
gp2y1010au0f.pyc oled2864.py
I will explore those examples.
Start Script at Boot
As of V1.0.4 system image, the /etc/init.d/S99user
executes /mnt/data/auto.sh
if it exists:
% mkdir /mnt/data
% vi /mnt/data/auto.sh
and list each of the processes:
/path/to/my/script.sh &
python /path/to/another/script.py &
TinyCC
According this post, tinycc
has been ported as well – C compiler and C interpreter in one – download the .zip (my mirror) and run its install.sh
, and then fix missing executable bit:
% chmod +x /usr/local/bin/tcc
and start playing with test.c
:
#!/usr/local/bin/tcc -run
#include <stdio.h>
int main(int argc, char **argv) {
printf("%s---\n", "hello milk-v!");
return 0;
}
% tcc -o test test.c
% ./test
hello milk-v!---
% chmod +x test.c
% ./test.c
hello milk-v!---
Thanks to Yang who ported and provided the download.
I2C: SSD1306 OLED
I followed the example, and compiled the sources on the board itself with tcc
:
tcc -r ssd1306.c -I .
tcc -r linux_i2c.c -I .
tcc -o ssd1306 main.c linux_i2c.o ssd1306.o -I .
./ssd1306 -I 128x64
./ssd1306 -c
./ssd1306 -m "Hello world!\nMilk-V Duo"
ssd1306
from ssd1306_linuxSoftware State
On-Board | BuildRoot v1.0.4-2023-0908 | BUILDROOT V1.0.4-2023-1017-spiritdude | ArchLinux 2023-10-09 v0.0.1 spiritdude |
---|---|---|---|
available memory (RAM) | 48MB | 55MB | 55MB |
ssh server | ok (dropbear v2020.81) | ok (dropbear v2020.81) | ok (dropbear v2022.83) |
ssh client | ok (dropbear v2020.81) | ok (dropbear v2020.81) | ok (dropbear v2022.83) |
python | ok (python 3.9.5) | ok (python 3.9.5) | ok (python 3.11) |
py web-server (with socket ) | ok | ok | ok |
py gpio (with pinpong or gpio ) | ok | ok | |
py spi (with built-in spidev ) | available, not yet tested | available, not yet tested | |
py i2c (with pinpong ) | available, not yet tested with pinpong ok with .c (see above section) | available, not yet tested with pinpong ok with .c (see above section) | |
py pwm (with pinpong ) | not found, /sys/class/pwm/* is empty | not found, /sys/class/pwm/* is empty | |
pip | failed (hangs), enable swap space to use it | barely works, enable swap space | works (swap space enabled) |
cc/gcc/clang | not found | not included | not included |
tinycc/tcc | ok, see this post how to install | ok, see this post how to install | ok |
rsync | not found | included | included |
wget | ok, but no https (only http, ftp) | ok, but no https (only http, ftp) | not included, installable |
lua/luac | lua (5.4.6): segmentation fault, luac (5.4.6): seems to work (both compiled with tinycc) | lua & luac (5.3.6) works | lua & luac (5.4.6) works |
extras | quickjs/qjs, micropython, nano, screen, git, make (no gcc/cc, use tinycc), thttpd, nginx, lighttpd, php-cgi, file, which, sudo | pacman (package mgr), lighttpd, file, which, sudo, make |
Internet Access for Milk-V Duo
RNDIS (Virtual Ethernet over USB)
The host has to run Ethernet over USB and also operate as transparent router and let the connected board(s) reach the internet, see this guide (use google translate to english), here the brief description:
On The Host
The outgoing_if is the outgoing interface, either eth0
or wpl0s0
or something, check with ifconfig
of the proper name, and then as root
perform:
% sysctl net.ipv4.ip_forward=1
% iptables -P FORWARD ACCEPT
% iptables -t nat -A POSTROUTING -o outgoing_if -j MASQUERADE
Also, find out which IP your host got (ip_of_host) from the connected board, e.g. 192.168.42.120
, also check with ifconfig
.
On The Board
% ip r add default via ip_of_host
% echo "nameserver 8.8.8.8" >> /etc/resolv.conf
Static IP for Host with RNDIS
What may look simple actually isn’t that easy as RNDIS itself is the culprit:
- one can limit the IP range in
dnsmasq.conf
to from/to be the same IP, but - as RNDIS assigns random MAC addresses to the RNDIS host (the board) and the RNDIS client (the host the board is connected to) treat it as new device at every boot – if you force it to have the same IP again, the host will not get a new IP via DHCP as it has the IP remembered for another MAC address . . .
- it’s a mess
So, a working solution is:
/etc/rndis-macs.sh
which generates two random MAC addresses but keeps them persistent then:
#!/bin/bash
RNDIS_USB="/tmp/usb/usb_gadget/cvitek/functions/rndis.usb0"
MAC_FILE="/etc/rndis-macs.conf"
generate_random_mac() {
printf "02:%02x:%02x:%02x:%02x:%02x\n" $((RANDOM%256)) $((RANDOM%256)) $((RANDOM%256)) $((RANDOM%256)) $((RANDOM%256))
}
# check if the MAC address file exists and has exactly two lines
if [[ -f "$MAC_FILE" ]] && [[ $(wc -l < "$MAC_FILE") -eq 2 ]]; then
# read the two MAC addresses from the file
IFS=$'\n' read -d '' -r -a macs < "$MAC_FILE"
dev="${macs[0]}"
host="${macs[1]}"
echo "using existing MAC addresses:"
else
# generate two new MAC addresses and store them in the file
echo "generating new MAC addresses:"
dev=$(generate_random_mac)
host=$(generate_random_mac)
echo "$dev" > "$MAC_FILE"
echo "$host" >> "$MAC_FILE"
fi
echo "dev_addr: $dev"
echo "host_addr: $host"
echo "$dev" > "$RNDIS_USB"/dev_addr
echo "$host" > "$RNDIS_USB"/host_addr
/etc/usb-rndis.sh
:
/etc/uhubon.sh device >> /tmp/rndis.log 2>&1
/etc/run_usb.sh probe rndis >> /tmp/rndis.log 2>&1
/etc/rndis-macs.sh >> /tmp/rndis.log 2>&1
/etc/run_usb.sh start rndis >> /tmp/rndis.log 2>&1
sleep 0.5
ip link set dev usb0 up
ip a add 192.168.42.1/24 dev usb0
ip r add default via 192.168.42.2
sleep 0.5
systemctl start dnsmasq
/etc/dnsmasq.conf
interface=usb0
dhcp-range=192.168.42.2,192.168.42.2,1h
dhcp-option=3
dhcp-option=6
Note: if you had 192.168.42.2 once assigned to the host, choose another “static” IP in your range, as your host has remembered the IP to a particular MAC address and won’t accept it again, e.g. 192.168.42.200 or so.
As of milkv-duo-archlinux-*-v0.0.3-spiritdude.img
the above solution is included, see list of ArchLinux disk images.
Custom BuildRoot Milk-V Duo Disk Image
Here my brief guide – see also this guide (use google translate) – how to customize packages included in the base distribution of the image for the SD card:
% cd duo-buildroot-sdk/buildroot-2021.05
% make menuconfig
then go into the “Target packages”, and then walk through:
- Audio and video applications
- Compressors and decompressors
- Debugging, profiling and benchmark
- Development tools
- Filesystem and flash utilities
- Fonts, cursors, icons, sounds and themes
- Games
- Graphic libraries and applications (graphic/text)
- Hardware handling
- Interpreter languages and scripting
- Libraries
- Miscellaneous
- Networking applications
- Package managers
- Real-Time
- Security
- Shell and utilities
- System tools
- Text editors and viewers
once you selected the packages you like to have included, choose “Save” and confirm as ‘.config’ and then “Exit”.
% cp .config configs/milkv_duo_musl_riscv64_defconfig
% cd ..
% ./build_milkv.sh
and after while, depending on how many packages you selected, you find in out/
folder your new disk image you can copy on the SD card.
Note: buildroot
is quite a quirky package, e.g. when you select a package and make a build, later deselect a package, it will still be included – worse, if you commit a clean slate in output/
, some packages might not fully build anymore – you have to go back to an earlier state of fewer packages, remake the build, and restart re-selecting new packages.
Postfixing missing .so file
As of 2023/10 v1.0.4 buildroot-2021.05 environment, there seems a problem regarding a missing shared library for some of the compiled apps (like qjs
), you can fix this:
% cd /lib
% ln -s ld-musl-riscv64v0p7_xthead.so.1 ld-musl-riscv64.so.1
My BuildRoot Custom Disk Images
disk Image | Notes |
---|---|
milkv-duo-v1.0.4-20231017-spiritdude-64mb_ram-nocam.img milkv-duo-v1.0.4-20231017-spiritdude-64mb_ram-nocam-fixed.img | – lua, quickjs/qjs, micropython, nano, screen, git, make (no gcc/cc, use tinycc), thttpd2), nginx, lighttpd, php-cgi – 55MB RAM1) available – “fixed” image has .so lib-fix included |
milkv-duo-v1.0.4-20231016-spiritdude-64mb_ram-nocam.img | – lua, quickjs/qjs, micropython, nano, screen, git, make (no gcc/cc, use tinycc), lighttpd (doesn’t work yet out of the box) – 55MB RAM1) available |
- apprx. 55MB actual available RAM, no camera support, INO_SIZE=0
- thttpd, nginx and lighttpd are all http-server, only use one, see
/etc/init.d/
where those are started.
Ethernet Add-On
The easier way is to get proper networking is to add a real ethernet port and properly wire it to the ethernet router. There are several options & sources (2023/09):
- SpotPear: RJ45 for MilkV-Duo (EUR 2.50/pc)
- Installation Guide: insert the RJ45 board from underneath (very tight fit) and push up, solder, and then cut the pins so you can insert SD card again
- SpotPear: IO Expansion for MilkV-Duo 4x USB 1x Ethernet
- Aliexpress: SpotPear (USD 9.50/pc)
- Arace: USB & Ethernet Board (USD 5.00/pc – discounted)
Printable Case
I did a small case for the bare board without Ethernet or Extension board to be 3D printed:
bottom case & lid
Download
- Printables: Milk-V Duo Simple Case (STL, SCAD): very basic case
As the Milk-V Duo has the same width and depth (X/Y) as the Raspberry Pico but it’s a bit thicker so the some of the existing cases work only partially:
- Raspberry Pi Pico Case by Botvinnik
works perfectly, recommended, bottom/lid with 2 options: closed or with holes - Raspberry Pi Pico Case w Slider 3D Print Model by AndysTechGarage
bottom part works, but lid doesn’t fit due the camera connector; you need to disconnect board from power in order to put in or out from the case Raspberry Pi Pico Case by Kuma0055: has pins for holes which Milk-V Duo PCB doesn’t have, not recommended
As soon my Ethernet connectors and Extension boards arrived I will provide case variants (2023/10/05).
References
- Milkv.io: Milk-V Duo, original web-site
- Milk-V User Forum with Duo related posts, highly recommended
- Sophgo: CV1800B (Duo English Forum), another forum
- Github: Milk-V Duo Buildroot, build your own Linux image
- Github: Milk-V Duo Experiments
- Github: CV1800B CPU Datasheet (English) (700 pages PDF)
- YouTube: Platima Tinkers: Testing out the Milk-V Duo – The new $9 RISC-V eSBC that runs Linux! (2023/09)