Überspringen und zum Inhalt gehen →

Milk-V Duo – 5$ Linux Computer mit 1Ghz

Milk-V Duo ist eine ultrakompakte Embedded-Entwicklungsplattform auf der Basis des CV1800B-Chips. Sie kann Linux und RTOS ausführen und bietet eine zuverlässige, kostengünstige und leistungsstarke Plattform für Profis, industrielle ODMs, IoT-Enthusiasten, Heimwerker und Entwickler.

Spezifikation

Milk-V DuoSpecification
ProzessorCVITEK CV1800B (C906@1Ghz + C906@700MHz)
RAMDDR2 64MB
Flash1x Mirco SD slot,1x SD NAND solder pad
USB1x Type-C for data and Power,1x USB2 solder pad
Kamera1x 16P FPC connector (MIPI CSI 2-lane)
GPIOup to 26 Pins available for general purpose I/O(GPIO)
Größe21mm*51mm

Details:
https://milkv.io/docs/duo/overview

Größe

Hier mal ein Größenvergleich des Milk-V & Cam mit meiner Logitech Maus.

Shop

https://arace.tech/search?q=Milk-V+Duo&type=product

https://milkv.io/

Ich habe mal direkt zugeschlagen und mich mal eingedeckt.

Ein erster Test zeigt, dass der Energiebedarf mit ca. 0,5W sehr übersichtlich ist.

Die Installation ist denkbar übersichtlich. Einfach das Image unter https://milkv.io/docs/duo/getting-started/boot herunterladen und z.B. mit balenaEtcher auf einer SD-Karte installieren.

Besonders toll ist die Möglichkeit, den Milk-V Duo über ein USB-C Kabel an den PC anzuschließen und wie unter https://milkv.io/docs/duo/getting-started/setup beschrieben per SSH darauf zugreifen zu können. Das default root Passwort ist milkv.

Nach dem Start des Milk-V beginnt die blaue Onboard-LED direkt an, zu blinken. Das liegt daran, dass beim Start des Milk-V das /mnt/system/blink.sh Shell-Skript ausgeführt wird.

#!/bin/sh

LED_GPIO=/sys/class/gpio/gpio440

if test -d $LED_GPIO; then
    echo "GPIO440 already exported"
else
    echo 440 > /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

Wie man sieht, kann man die GPIOs des Milk-V einfach aus einem Shell-Skript heraus ansprechen kann und der Milk-V hat viele davon.

  • 26 GPIO Pins – MilkV-Duo 40-pin (SDIO, I2C, PWM, SPI, J-TAG, und UART)
  • 3x I2C
  • 5x UART
  • 1x SDIO1
  • 1x SPI
  • 2x ADC
  • 7x PWM
  • 1x RUN
  • 1x JTAG

Man kann die GPIO-Pins des Milk-V aber auch aus einem C-Programm ansprechen. Dazu muss man das milkv-duo/duo-app-sdk unter https://github.com/milkv-duo/duo-app-sdk/releases herunterladen und auf einem Linux PC installieren. Wer gerade kein Linux zur Hand hat, kann auch eine Ubuntu VM nutzen.

Die einfachste Art ist es, das https://github.com/milkv-duo/duo-examples Repo zu klonen und das enthaltene envsetup.sh Skript auszuführen.

#!/bin/bash

SDK_DIR=$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" &> /dev/null && pwd)
echo "SDK_DIR: ${SDK_DIR}"

MILKV_DUO_SDK=${SDK_DIR}/duo-sdk
TOOLCHAIN_DIR=${MILKV_DUO_SDK}/riscv64-linux-musl-x86_64

SDK_URL="https://github.com/milkv-duo/duo-app-sdk/releases/download/duo-app-sdk-v1.1/duo-sdk-v1.1.tar.gz"

function get_duo_sdk()
{
  pushd ${SDK_DIR}

  echo "SDK_URL: ${SDK_URL}"
  sdk_file=${SDK_URL##*/}
  echo "sdk_file: ${sdk_file}"

  wget ${SDK_URL} -O ${sdk_file}
  if [ $? -ne 0 ]; then
    echo "Failed to download ${SDK_URL} !"
    return 1
  fi

  if [ ! -f ${sdk_file} ]; then
    echo "${sdk_file} not found!"
    return 1
  fi

  echo "Extracting ${sdk_file}..."
  tar -xf ${sdk_file}
  if [ $? -ne 0 ]; then
    echo "Extract ${sdk_file} failed!"
    return 1
  fi

  [ -f ${sdk_file} ] && rm -rf ${sdk_file}

  popd
}

if [ ! -d ${MILKV_DUO_SDK} ]; then
  echo "SDK does not exist, download it now..."
  get_duo_sdk
  if [ $? -ne 0 ]; then
    echo "Get SDK failed!"
    return 1
  fi
fi

export TOOLCHAIN_PREFIX=${TOOLCHAIN_DIR}/bin/riscv64-unknown-linux-musl-
export SYSROOT=${MILKV_DUO_SDK}/rootfs

export LDFLAGS="-mcpu=c906fdv -march=rv64imafdcv0p7xthead -mcmodel=medany -mabi=lp64d"
# -Os
export CFLAGS="-D_LARGEFILE_SOURCE -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64"

echo "SDK environment is ready"

Das Skript installiert das duo-sdk und setzt die Umgebungsvariable TOOLCHAIN_PREFIX, die dann in den Makefiles der Beispiele verwendet wird, um den korrekten gcc zu verwenden.

Das Blink C-Programm kann man dann wie folgt kompilieren und per ssh auf den Milk-V Duo zu kopieren.

So weit so gut. Scheint alles zu funktionieren.

Allerdings ein Problem habe ich dann doch noch gefunden, als ich versucht habe den ADC des Milk-V zu nutzen. Für den Zugriff auf die GPIOs wird auf dem Milk-V die wiringX Library verwendet.

WiringX ist eine Library, mit der man die GPIOs verschiedener Plattformen mit generischen und einheitlichen Funktionen steuern kann. Durch die Verwendung von wiringX wird derselbe Code auf allen von wiringX unterstützten Plattformen nativ ausgeführt. Die wiringX Lib. hat auch eine analogRead Funktion, die aber leider in der Milk-V Version der wiringX Lib auskommentiert ist.

Schade, hätte gerne mal gesehen, was der ADC so kann… aber evtl. ist die analogRead Metode zur Verwendung des ADC in der nächsten Version des SDK erhalten. Ich habe jedenfalls mal ein GitHub Issue zu dem Thema erstellt (https://github.com/milkv-duo/duo-app-sdk/issues/1)

Alle Details zu den wiringX Funktionen des Milk-V Dou kann man unter https://milkv.io/zh/docs/duo/application-development/wiringx nachlesen.

Python

Da die Programmiersprache C nicht so einfach ist wie Python, ist auch ein Python Interpreter vorhanden. Das aktuelle Image unterstützt Python 3.9.5 und bringt für die Steuerung der GPIOs noch die pinpong Library mit.

Die pinpong-Lib ist eine Open-Source-Python-Bibliothek, die auf dem Firmata-Protokoll basiert und die Micropython-Syntax verwendet. Ihr Ziel ist es, ein Werkzeug zur Verfügung zu stellen, mit dem verschiedene Open-Source-Hardware-Steuerkarten direkt aus dem Python- Code heraus steuern kann.

Die Doku auf der Milk-V Seite https://milkv.io/docs/duo/application-development/pinpong ist noch recht dürftig und da wird hoffentlich in den nächsten Wochen noch etwas passieren.

Das blink Skript in Python sieht dann wie folgt aus:

import time

from pinpong.board import Board,Pin

Board("MILKV-DUO").begin()

led = Pin(Pin.D25, Pin.OUT)

while True:
    led.write_digital(1) #Output high level method 2
    print("1") #Terminal printing information.
    time.sleep(1) #Wait for 1 second and keep the state
    led.write_digital(0) #Output Low Level Method 1
    print("0") #Terminal printing information.
    time.sleep(1) #Wait for 1 second and keep the state

Ein Large language model auf dem Milk-V laufen lassen

Klingt erst mal etwas verrückt, aber es ist möglich, ein kleines LLM (Large language model) auf dem Milk-V laufen zu lassen. Dazu muss man das https://github.com/karpathy/llama2.c Repository von Andrej Karpathy klonen und mit dem gcc compiler für die RISC-Architektur übersetzen. Wichtig ist, dass man in der run.c Datei noch die Zeile „#include <stdint.h>“ ergänz werde, da sonst der Datentype int8_t unbekannt ist und der folgende Fehler auftritt:

run.c:453:39: error: unknown type name 'int8_t'; did you mean 'intptr_t'?
  453 | void encode(Tokenizer* t, char *text, int8_t bos, int8_t eos, int *tokens, int *n_tokens) {
      |                                       ^~~~~~
      |                                       intptr_t
run.c:453:51: error: unknown type name 'int8_t'; did you mean 'intptr_t'?
  453 | void encode(Tokenizer* t, char *text, int8_t bos, int8_t eos, int *tokens, int *n_tokens) {
      |                                                   ^~~~~~
      |                                                   intptr_t
run.c: In function 'generate':
run.c:737:5: warning: implicit declaration of function 'encode'; did you mean 'decode'? [-Wimplicit-function-declaration]
  737 |     encode(tokenizer, prompt, 1, 0, prompt_tokens, &num_prompt_tokens);
      |     ^~~~~~
      |     decode
run.c: In function 'chat':
run.c:816:5: error: unknown type name 'int8_t'; did you mean 'intptr_t'?
  816 |     int8_t user_turn = 1; // user starts
      |     ^~~~~~
      |     intptr_t

Wenn man alles korrekt eingerichtet hat, kann man ein RISC Binary mit dem Namen „run.rv64“ erzeugen.

Nachdem die Datei per scp auf den Milk-V kopiert wurde, kann man das run.rv64 ausführen.

Was noch fehlt ist ein Model. Eine Liste von kompatiblen Modellen findet man unter https://github.com/karpathy/llama2.c#models. Auf Grund des limitierten RAM von 64MB kann man max. das stories15M.bin Model verwenden, dass 15.000.000 Parameter besitzt und so gerade eben mit dem verfügbaren Speicher auskommt. Wenn man die Dateien stories15M.bin und tokenizer.bin aus dem GitHub Repository auf den Milk-V kopiert hat kann man von der KI eine kleine Geschichte schreiben lassen.

[root@milkv-duo]~/llm# ./run.rv64 stories15M.bin 
One day, a little boy named Tim went to play in the park. He saw a big pile of rocks. He wanted to make a big structure with the rocks. Tim started to add more and more rocks. He was having so much fun.
Then, Tim met a new friend, Sam. Sam was not wealthy, but he was very smart. Sam said, "Let's build a big structure together!" Tim agreed, and they started to add more and more rocks. They worked hard all day.
Soon, the structure was very tall. Tim and Sam were proud of their work. They learned that working together made their playtime more fun. The moral of the story is that teamwork makes everything better.
achieved tok/s: 0.299230
[root@milkv-duo]~/llm# 
Run karpathy/llama2.c on Milk-V

Natürlich ist es nicht unbedingt sinnvoll, das stories15 Model auf einer so limitierten Hardware laufen zu lassen, aber es ist sehr beeindruckend zu sehen, was die der CV1800B Chip auf dem Milk-V Board drauf hat.

Kamera Test

Der Duo verfügt über einen 16P FPC connector (MIPI CSI 2-lane) an dem man eine Kamera anschließen kann. Dafür dass die Kamera gerade mal 4$ kostet sind die Ergebnisse eigentlich ganz OK.

Milk-V Duo Camera Test 2
Milk-V Duo Kamera Test (Indoor)
Milk-V Duo Camera Test 1
Milk-V Duo Kamera Test (Outdoor)

Fazit:

Alles in allem ein recht beeindruckendes Stück Technik, wenn man bedenkt, dass man es für 5$ (Plus Zollgebühren) kaufen kann.

Als nächstes werde ich mal die anderen Möglichkeiten des Milk-V testen. Besonders gespannt bin ich auf die Qualität der Kamera, die auch nur 4$ kostet und auf die Fähigkeiten der Onboard-TPU (Tensor Processing Unit).

Veröffentlicht in Allgemein