Note: usb0: HOST MAC and MAC are set randomly and change after each (re)boot.
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.
/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 unsed 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. 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
DISK IMAGE | FEATURES | INCLUDED | MISSING |
---|---|---|---|
milkv-duo-archlinux-riscv64-2023-10-09-2.5gb-v0.0.1-spiritdude.img md5:2ff352bcb7a9d855dfe50b4d3176f69c 2023/10/21 | 55MB RAM (no camera support),256MB swap enabled, rndis (connect via usb virtual ether), minimum 4GB SD card recommended, 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 | no quickjs/qjs (not available install mujs instead), no micropython (not available) |
milkv-duo-archlinux-riscv64-2023-10-09-1.5gb-v0.0.1-spiritdude.img md5:ecc2ab2076ad9fbec795c5dba73a291e 2023/10/21 | 55MB RAM (no camera support), 256MB swap enabled, rndis (connect via usb virtual ether), minimum 2GB SD card recommended, 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 | no quickjs/qjs (not available install mujs instead), no micropython (not available), no gcc/llvm (install if you need it, resize rootfs) |
Notes:
pacman -Fy 'term'
or-Q 'term'
fails for me (too much memory needed), instead rungzip -d -c /var/lib/pacman/sync/*.files | grep -ai 'term'
(-F
equivalent) or*.db
for (-Q
equivalent)
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 GitHub - vitiral/gpio: python gpio module for linux using the sysfs file access (/sys/class/gpio). Mimics similar Raspberry Pi IO libraries 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 |