カテゴリー: VisionFive2

VisionFive2, 電子工作

A/Dコンバータの値を取得する

水位センサを買うのと一緒にスイッチサイエンスで”GROVE – ADS1115搭載 16bit ADC – 4チャンネル“を購入したので、こちらにも対応します。こちらの商品は Grove やスクリューターミナルがついてて便利なものの1760円とそこそこのお値段がするので、同じチップを搭載したもうちょっと安いものを探してきて使った方が良かったかな~とおもいます。

ADS1115 自体に I2C の機能も搭載されていて、このボードは単にチップと端子を繋いでいるだけのようです。Linuxは標準で ADS1115 に対応しており、VisionFive2 SDK でも VisionFive2/linux/drivers/iio/adc/ti-ads1015.c があります。これとデータシートを参考にしながら作業を進めます。また、”[電子工作]ADS1115 16bit ADCを使ってみた“で ADS1115 の使い方が説明されているのでここのコードも参考にしました。

ちなみに、Linux で標準対応しているならカーネルモジュールを用意すれば Linux 方式で使えるのでは!?!?!?と思ってやってみたのですが、カーネルモジュールのビルドとロードはできたもののこれを使うには device tree への登録が必要で、そのためには u-boot の更新が必要で、オリジナルの u-boot を使うためには自前で用意した sdcard.img を使うようにしなければいけないけど VisionFive2 SDK から作ることができるのは最低限の環境なのでどーする!?!?というところで詰まってしまいました。。。Linux よくわからんちん。。。

コード

でまあ何やかんやと四苦八苦しながら書いたのが以下のコードです。基本は動画で説明されている内容そのままで、VisionFive2 が対応していない Arduino の Wire.h 関連を”水位センサの値を取得する“でも参考にした”LinuxのC言語でI2Cデバイスと通信する“を参考に書いています。細かい説明は。。。精も根も尽き果てたのでご勘弁を_(:3 」∠ )_

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <stdint.h>

#include <fcntl.h>
#include <sys/ioctl.h>
#include <linux/types.h>
#include <linux/i2c.h>
#include <linux/i2c-dev.h>

//i2c address
#define I2C_DEVICE         "/dev/i2c-0"
#define ADS1115_I2C_ADDRESS 0x48

//BIT[1:0] Register address
#define CONVERSION_REGISTER 0
#define CONFIG_REGISTER     1
#define LO_THRESH_REGISTER  2
#define HI_THRESH_REGISTER  3

//BIT[15]
#define NO_EFFECT              (0 << 15)
#define START_SINGLE_CONVERSION (1 << 15)

//BIT[14:12] Input multiplexer configuration
#define P_AIN0_N_AIN1 (0 << 12)
#define P_AIN0_N_AIN3 (1 << 12)
#define P_AIN1_N_AIN1 (2 << 12)
#define P_AIN2_N_AIN1 (3 << 12)
#define P_AIN0_N_GND  (4 << 12)
#define P_AIN1_N_GND  (5 << 12)
#define P_AIN2_N_GND  (6 << 12)
#define P_AIN3_N_GND  (7 << 12)

//BIT[11:9] Programmable gain amplifier configuration
#define FSR_6144MV (0 << 9)
#define FSR_4096MV (1 << 9)
#define FSR_2048MV (2 << 9)
#define FSR_1024MV (3 << 9)
#define FSR_0512MV (4 << 9)
#define FSR_0256MV (5 << 9)
//#define FSR_0256MV (6 << 9)
//#define FSR_0256MV (7 << 9)

//BIT[8] Device operating mode
#define CONTINUOUS_CONVERSION (0 << 8)
#define SINGLE_SHOT           (1 << 8)

//BIT[7:5] Data rate
#define DATA_RATE_8_SPS   (0 << 5)
#define DATA_RATE_16_SPS  (1 << 5)
#define DATA_RATE_32_SPS  (2 << 5)
#define DATA_RATE_64_SPS  (3 << 5)
#define DATA_RATE_128_SPS (4 << 5)
#define DATA_RATE_250_SPS (5 << 5)
#define DATA_RATE_475_SPS (6 << 5)
#define DATA_RATE_860_SPS (7 << 5)

//BIT[4] Comparator mode
#define TRADITIONAL_COMPARATE (0 << 4)
#define WINDOW_COMPARATE      (1 << 4)

//BIT[3] Comparator polarity
#define ACTIVE_LOW  (0 << 3)
#define ACTIVE_HIGH (1 << 3)

//BIT[2] Latching comparator
#define NONLATCHING_COMPARATOR (0 << 2)
#define LATCHING_COMPARATOR    (1 << 2)

//BIT[1:0] Comparator queue and disable
#define ASSERT_AFTER_1_CONVERSION 0
#define ASSERT_AFTER_2_CONVERSION 1
#define ASSERT_AFTER_4_CONVERSION 2
#define DISABLE_COMPARATOR        3

#define LSB_SIZE_6144MV (187.5    * 1E-6)
#define LSB_SIZE_4096MV (125.0    * 1E-6)
#define LSB_SIZE_2048MV ( 62.5    * 1E-6)
#define LSB_SIZE_1024MV ( 31.25   * 1E-6)
#define LSB_SIZE_0512MV ( 15.625  * 1E-6)
#define LSB_SIZE_0256MV (  5.8125 * 1E-6)

void printb(unsigned int v) {
  int buf[16];
  for(int i=0;i<16;i++) {
    buf[15-i] = (v >> i) & 1;
  }

  for(int i=0;i<16;i++) {
    printf("%d", buf[i]);
  }
}

int32_t i2c_open(char *i2c_device) {
  int32_t fd = open(I2C_DEVICE, O_RDWR);
  if (fd == -1) {
    fprintf(stderr, "i2c_read: failed to open: %s\n", strerror(errno));
    return -1;
  }

  return fd;
}

void i2c_close(int32_t i2c_dev) {
  close(i2c_dev);
}

int8_t i2c_read(int32_t i2c_dev, uint8_t dev_addr, uint8_t reg_addr, uint8_t* data, uint16_t length) {
  struct i2c_msg messages[] = {
      { dev_addr, 0, 1, &reg_addr},
      { dev_addr, I2C_M_RD, length, data },
  };
  struct i2c_rdwr_ioctl_data ioctl_data = { messages, 2 };

  if (ioctl(i2c_dev, I2C_RDWR, &ioctl_data) != 2) {
    fprintf(stderr, "i2c_read: failed to ioctl: %s\n", strerror(errno));
    return -1;
  }

  return 0;
}

int8_t i2c_write(int32_t i2c_dev, uint8_t dev_addr, uint8_t reg_addr, const uint8_t* data, uint16_t length) {
  uint8_t* buf = (uint8_t*)malloc(length + 1);
  if (buf == NULL) {
    fprintf(stderr, "i2c_write: failed to memory allocate\n");
    return -1;
  }
  buf[0] = reg_addr;
  memcpy(&buf[1], data, length);
  struct i2c_msg messages[] = {
    { dev_addr, 0, length+1, buf}
  };
  struct i2c_rdwr_ioctl_data ioctl_data = { messages, 1 };

  if (ioctl(i2c_dev, I2C_RDWR, &ioctl_data) != 1) {
    fprintf(stderr, "i2c_write: failed to ioctl: %s\n", strerror(errno));
    free(buf);
    return -1;
  }

  free(buf);
  return 0;
}

uint16_t ads1115_read_register(int32_t i2c_dev) {
  uint8_t buf[2];
  int16_t data;

  i2c_read(i2c_dev, ADS1115_I2C_ADDRESS, CONVERSION_REGISTER, buf, 2);

  data = (buf[0] << 8) | buf[1];
  return data;
}

void ads1115_write_register(int32_t i2c_dev, uint16_t data) {
  uint8_t buf[] = {
    data >> 8 & 0xff,
    data >> 0 & 0xff
  };
  i2c_write(i2c_dev, ADS1115_I2C_ADDRESS, CONFIG_REGISTER, buf, 2);

  return;
}

int init(int32_t i2c_dev) {
  ads1115_write_register(i2c_dev,
                         NO_EFFECT |
                         P_AIN0_N_GND |
                         FSR_6144MV |
                         CONTINUOUS_CONVERSION |
                         DATA_RATE_8_SPS |
                         WINDOW_COMPARATE |
                         ACTIVE_LOW |
                         LATCHING_COMPARATOR |
                         ASSERT_AFTER_1_CONVERSION);

  return 0;
}

int sense(int i2c_dev) {
  int16_t sensor_val;

  sensor_val = ads1115_read_register(i2c_dev);

  printf("sensor value: ");
  printb(sensor_val);
  printf("\n");

  printf("voltage = %f\n", sensor_val * LSB_SIZE_6144MV);
}

int main() {
  int32_t i2c_dev;

  i2c_dev = i2c_open(I2C_DEVICE);

  init(i2c_dev);

  while(1) {
    sense(i2c_dev);
    sleep(1);
  }

  i2c_close(i2c_dev);

  return 0;
}

動作確認

直流安定化電源の出力を入力して、電圧を変えながら様子を見ます。

それでは実行。

chiyama@starfive:~$ sudo ./ADS1115
sudo: unable to resolve host starfive: Name or service not known

voltage = 0.000188
sensor value: 0000010011100100
voltage = 0.234750
sensor value: 0001000111001010
voltage = 0.853875
sensor value: 0001110101111011
voltage = 1.415063
sensor value: 0010100100101011
voltage = 1.976063
sensor value: 0010110110000011
voltage = 2.184563
sensor value: 0011000110100101
voltage = 2.382938
sensor value: 0100000101011101
voltage = 3.137437
sensor value: 0100101100111100
voltage = 3.611250
sensor value: 0101000010110011
voltage = 3.873563
sensor value: 0101000011010001
voltage = 3.879188
sensor value: 0101000011010100
voltage = 3.879750
sensor value: 0101000011010101
voltage = 3.879938
sensor value: 0101000011010110
voltage = 3.880125
sensor value: 0101000011011000
voltage = 3.880500
sensor value: 0101000011010100
voltage = 3.879750


^C
chiyama@starfive:~$

直流安定化電源の電圧を4Vまで上げたところ、3.88Vで計測されました。ちょっと低いですね。3.8Vくらいまではかなり正確な値が計測しているのですが、そこから先が誤差が広がっているようです。VDDに近くなるとダメなのか???と思いつつ、よみやさんの動画では4.18Vまで計測しているのでそういうわけでもなさそうです。データシートも目を通してみたものの、それっぽい制限事項を見つけることができなかったです。。。謎な。。。このあたりは、私の電子工作関連のスキルの低さが現れてしまっている感じです(涙

とはいえ、3.8Vまでであればいい感じに使えるようなので一旦はこれで良しとしておきます。

VisionFive2

水位センサの値を取得する

VisionFive2 の I2C 経由で水位センサに接続できたので、値を取得してみます。i2c-tools の時もそうでしたが VisionFive2 上で Linux が動いているおかげで Raspberry Pi など Linux で動いている SBC とやることは同じなので既存のノウハウが流用できて助かります。

値の取得

センサの使い方は製造元のドキュメントを参照します。また、I2C との通信は”LinuxのC言語でI2Cデバイスと通信する“を参考にしています。VisionFive2 が Arduino IDE に対応していれば、製造元のドキュメントに書いてあるサンプルがほぼそのまま使えるかもしれないですが Arduino IDE への対応状況は未調査です。

最終的にできたプログラムは以下の通りです:

#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <stdint.h>

#include <fcntl.h>
#include <sys/ioctl.h>
#include <linux/types.h>
#include <linux/i2c.h>
#include <linux/i2c-dev.h>


unsigned char low_data[8] = {0};
unsigned char high_data[12] = {0};

#define THRESHOLD      100

//i2c address
#define I2C_DEVICE         "/dev/i2c-0"
#define ATTINY1_HIGH_ADDR  0x78
#define ATTINY2_LOW_ADDR   0x77


int8_t i2c_read(uint8_t dev_addr, uint8_t reg_addr, uint8_t* data, uint16_t length) {
  int32_t fd = open(I2C_DEVICE, O_RDWR);
  if (fd == -1) {
    fprintf(stderr, "i2c_read: failed to open: %s\n", strerror(errno));
    return -1;
  }

  struct i2c_msg messages[] = {
      { dev_addr, 0, 1, &reg_addr },
      { dev_addr, I2C_M_RD, length, data },
  };
  struct i2c_rdwr_ioctl_data ioctl_data = { messages, 2 };

  if (ioctl(fd, I2C_RDWR, &ioctl_data) != 2) {
    fprintf(stderr, "i2c_read: failed to ioctl: %s\n", strerror(errno));
    close(fd);
    return -1;
  }

  close(fd);
  return 0;
}

void getHigh12SectionValue(void)
{
  memset(high_data, 0, sizeof(high_data));
  i2c_read(ATTINY1_HIGH_ADDR, 0, high_data, 12);
}

void getLow8SectionValue(void)
{
  memset(low_data, 0, sizeof(low_data));
  i2c_read(ATTINY2_LOW_ADDR, 0, low_data, 8);
}

int sense() {
  uint32_t touch_val = 0;
  uint8_t trig_section = 0;

  getHigh12SectionValue();
  getLow8SectionValue();

  printf("low_data :");
  for (int i = 0 ; i < 8; i++) {
    printf("%d ", low_data[i]);
    if (low_data[i] > THRESHOLD) {
      touch_val |= 1 << i;
    }
  }
  printf("\n");

  printf("high_data :");
  for (int i = 0 ; i < 12; i++) {
    printf("%d ", high_data[i]);
    if (high_data[i] > THRESHOLD) {
      touch_val |= (uint32_t)1 << (8 + i);
    }
  }

  printf("\n");

  printf("touch value= %x\n", touch_val);

  while (touch_val & 0x01) {
    trig_section++;
    touch_val >>= 1;
  }

  printf("water level = %d\n", trig_section * 5);
}

int main() {
  while(1) {
    sense();
    sleep(1);
  }

  return 0;
}

途中で気づいたんですが、Linux の機能を使うくらいなら Python からアクセスする方が楽かもしれないです。その場合、製造元が用意している grove.py にちょっと手を加えると使えそうな雰囲気でしたし、smbus2 モジュールを使用しても良いかもしれません。

動作確認

動作確認は、コップに入れた水の中でセンサを上下に動かしておこないます。

作成したプログラムをコンパイルして実行します。

chiyama@starfive:~$ make WaterLevel
chiyama@starfive:~$ sudo ./WaterLevel
sudo: unable to resolve host starfive: Name or service not known
low_data :249 249 249 249 250 249 249 249
high_data :248 248 248 248 246 0 0 0 0 0 0 0
touch value= 1fff
water level = 65
low_data :249 249 249 249 249 249 249 249
high_data :248 248 248 248 247 0 0 0 0 0 0 0
touch value= 1fff
water level = 65
low_data :249 249 249 249 249 249 249 249
high_data :248 248 248 248 240 0 0 0 0 0 0 0
touch value= 1fff
water level = 65
low_data :249 249 249 249 180 150 90 31
high_data :248 247 247 187 0 0 0 0 0 0 0 0
touch value= f3f
water level = 30
low_data :249 249 2 255 0 0 0 0
high_data :0 0 0 0 0 0 0 0 0 0 0 0
touch value= b
water level = 10
low_data :249 249 0 0 0 0 0 0
high_data :0 0 0 0 0 0 0 0 0 0 0 0
touch value= 3
water level = 10
low_data :249 249 249 249 249 249 249 249
high_data :0 0 0 0 0 0 0 0 0 0 0 0
touch value= ff
water level = 40
low_data :249 249 249 249 249 249 249 250
high_data :248 248 0 0 0 0 0 0 0 0 0 0
touch value= 3ff
water level = 50
low_data :249 249 249 249 249 249 249 249
high_data :248 248 248 248 0 0 0 0 0 0 0 0
touch value= fff
water level = 60
low_data :249 249 249 249 215 204 255 249
high_data :245 251 251 0 0 0 0 0 0 0 0 0
touch value= 7ff
water level = 55
low_data :225 0 0 0 0 0 0 0
high_data :0 0 0 0 0 0 0 0 0 0 0 0
touch value= 1
water level = 5
low_data :249 0 0 0 0 0 0 0
high_data :0 0 0 0 0 0 0 0 0 0 0 0
touch value= 1
water level = 5
low_data :249 249 249 249 249 249 249 249
high_data :248 248 248 247 0 0 0 0 0 0 0 0
touch value= fff
water level = 60
^C
chiyama@starfive:~$

センサを上下すると water level の値が変化しています。出ている値もセンサ上の水位と合致しているようです。

VisionFive2

StarFive VisionFive2 で I2C を使う

センサや A/D, D/A などは I2C 接続して使用すると市販のものがそのまま使えて便利なので、今回は VisionFive2 で I2C を使ってみます。

接続

VisionFive2 にも RaspberryPi のように GPIO ヘッダがあり、I2C 接続もできるようになっているのでドキュメントの通りに接続します。

今回はスイッチサイエンスで入手できる Grove 用水位センサを接続します。

シリアルやオシロスコープのプローブもあるのでちょっとゴチャゴチャしていますが、Grove 用コネクタからそのまま VisionFive2 のピンに繋げているだけです。

dtsファイルの編集

I2c 接続を使用できるように dts ファイルを設定します。dts ファイルは VisionFile SDK の /linux/arch/riscv/boot/dts/starfive/jh7110-visionfive-v2.dtsi です。今回は 3 番(GPIO58)と 5 番(GPIO57)ピンを使うので、デフォルトのまま i2c-0 として使用可能です。

I2C 情報の確認

はじめに i2c-tools をインストールします

chiyama@starfive:~$ sudo apt install i2c-tools

i2cdetect が使用可能になるので、I2C バスの情報を確認してみます。

chiyama@starfive:~$ sudo i2cdetect -l
sudo: unable to resolve host starfive: Name or service not known
i2c-0 i2c Synopsys DesignWare I2C adapter I2C adapter
i2c-2 i2c Synopsys DesignWare I2C adapter I2C adapter
i2c-5 i2c Synopsys DesignWare I2C adapter I2C adapter
i2c-6 i2c Synopsys DesignWare I2C adapter I2C adapter
i2c-7 i2c Inno HDMI I2C adapter
chiyama@starfive:~$

i2c-0 が認識されているので、続いてデバイスの確認をします。

chiyama@starfive:~$ sudo i2cdetect -y -r 0
sudo: unable to resolve host starfive: Name or service not known
0 1 2 3 4 5 6 7 8 9 a b c d e f
00: -- -- -- -- -- -- -- --
10: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
20: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
30: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
40: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
50: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
60: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
70: -- -- -- -- -- -- -- 77
chiyama@starfive:~$

製品ページによると I2C アドレスは 0x77 と 0x78 を使用しているとのことで、0x77 は認識できています。しかし、0x78 以上は I2C では 10 ビットアドレスモードでないと認識できないようです。man を見ても i2cdetect では 10 ビットアドレスモードに言及されていないので、対応していないのかもしれません。ひとまず 0x77 が認識できたので良しとしておきます。

データを取得する

i2cget でデータを取得してみます。

chiyama@starfive:~$ sudo i2cget 0 0x77
sudo: unable to resolve host starfive: Name or service not known
WARNING! This program can confuse your I2C bus, cause data loss and worse!
I will read from device file /dev/i2c-0, chip address 0x77, current data
address, using read byte.
Continue? [Y/n] y
0x00
chiyama@starfive:~$

このとき、オシロスコープの波形もみておきます。

それっぽい波形が出ているので、ちゃんと動いているようですね。ここまでくれば、プログラムからセンサデータの読み書きはできそうです。

VisionFive2

StarFive VisionFive2 で X を立ち上げる

StarFive VisionFive2 着弾からの続きです。公式から配布されている Image-69 を使うためには SPL と U-Boot を更新しなければいけないので、そのために VisionFive2 SDK から取得できる sdcard.img を使って Linux を立ち上げるところまでできました。

SPL と U-Boot の更新

sdcard.img がビルドできているなら、同じ場所に u-boot-spl.bin.normal.out と visionfive2_fw_payload.img ができているので、VisionFive2 に転送します。

※ここでネットワークが繋がっている必要があるので手持ちの USB Wifi ドングルを繋げてみたところ、そのままではネットワークインターフェースとして認識してくれませんでした。調べるのがめんどくさいので私は有線接続してしまいましたが、USB Wifi をサクッと動かしたい人はボードと一緒に公式から購入してしまうのが良いかもしれないです。

sdcard.img で立ち上げた環境では root での ssh 接続が許可されているので、scp を使用して転送できます。

chiyama@riscv:~/VisionFive2/VisionFive2/work$ scp u-boot-spl.bin.normal.out root@192.168.0.111:.
root@192.168.0.111's password:
u-boot-spl.bin.normal.out 100% 125KB 915.4KB/s 00:00
chiyama@riscv:~/VisionFive2/VisionFive2/work$ scp visionfive2_fw_payload.img root@192.168.0.111:.
root@192.168.0.111's password:
visionfive2_fw_payload.img 100% 2728KB 4.8MB/s 00:00
chiyama@riscv:~/VisionFive2/VisionFive2/work$

続いて Updating SPL and U-Boot の手順通りに SPL と U-Boot を更新します。

root@starfive:~# flashcp -v u-boot-spl.bin.normal.out /dev/mtd0
root@starfive:~# flashcp -v visionfive2_fw_payload.img  /dev/mtd1

X の起動

再起動して無事に立ち上がることが確認できたら SD カードに Image-69 を焼いてそれで立ち上げます。。。が、シリアル上では OS が立ち上がっているのに HDMI 側に X が表示されない。何でかー???と思たら、キーボードやマウスが繋がっていないと X が立ち上がらないようです。これ以外にも X 周りはまだ不安定なようで、突然死んだり色々起こるようです。

動作確認

gcc と make を入れてプログラムをコンパイルしてみます。

root@starfive:~# apt install gcc
root@starfive:~# gcc -v
Using built-in specs.
COLLECT_GCC=gcc
COLLECT_LTO_WRAPPER=/usr/lib/gcc/riscv64-linux-gnu/11/lto-wrapper
Target: riscv64-linux-gnu
Configured with: ../src/configure -v --with-pkgversion='Debian 11.3.0-3' --with-bugurl=file:///usr/share/doc/gcc-11/README.Bugs --enable-languages=c,ada,c++,go,d,fortran,objc,obj-c++,m2 --prefix=/usr --with-gcc-major-version-only --program-suffix=-11 --program-prefix=riscv64-linux-gnu- --enable-shared --enable-linker-build-id --libexecdir=/usr/lib --without-included-gettext --enable-threads=posix --libdir=/usr/lib --enable-nls --enable-clocale=gnu --enable-libstdcxx-debug --enable-libstdcxx-time=yes --with-default-libstdcxx-abi=new --enable-gnu-unique-object --disable-libitm --disable-libquadmath --disable-libquadmath-support --enable-plugin --enable-default-pie --with-system-zlib --enable-libphobos-checking=release --with-target-system-zlib=auto --enable-objc-gc=auto --enable-multiarch --disable-werror --disable-multilib --with-arch=rv64gc --with-abi=lp64d --enable-checking=release --build=riscv64-linux-gnu --host=riscv64-linux-gnu --target=riscv64-linux-gnu --with-build-config=bootstrap-lto-lean --enable-link-serialization=2
Thread model: posix
Supported LTO compression algorithms: zlib zstd
gcc version 11.3.0 (Debian 11.3.0-3)
root@starfive:~# apt install make
root@starfive:~# cat hello.c
#include <stdio.h>

int main(int argc, char **argv) {
  printf("Hello, StarFive2!!\n");

  return 0;
}

root@starfive:~# make hello
cc     hello.c   -o hello
root@starfive:~# ./hello
Hello, StarFive2!!
root@starfive:~#

いい感じですねっ!!これで RISC-V を手軽に試すことのできる環境が整いました。これまでは”作って学ぶコンピュータアーキテクチャ“を読むときにはシミュレータを使ってきたのですが、動作がかなり重いので実機があるのはとても嬉しいです。

処理能力について

普段、Raspberry Pi4 を使う時には GUI を使わないのであまり正確な比較はできないものの、X の立ち上がり時間なんかは RPi4 に比べて結構かかるかな?という気がします。コンソールで処理したり、hello をコンパイル・実行するくらいなら当然ですが全くストレスは感じないです。このあたりはベンチマークも取ってみたいですね。

VisionFive2

StarFive VisionFive2 着弾

StarFive が夏に Kickstarter でクラウドファンディングしていた VisionFile2 が年末に突然届きました。

RISC-V は組み込み用途としては既にかなりの実績があるものの、計算能力を高めることを目指したものはあまりなく、物足りなさを感じていたところでした。

そこに現れたのが StarFive JH7110 を搭載した VisionFive2 です。Quad core 1.5GHz の CPU で GPU も搭載していて、Raspberry pi4 よりもちょっと落ちるもののそれに匹敵する性能と GPIO や I2C といった各種インターフェース、DDR4, PCIe, M.2 NVMe SSD 対応と色々遊べそうな構成になっています。また、Linux が動いて手軽に実環境でプログラムを実行することができるのも嬉しいです。

内容物

届いた中身はとてもシンプルです。外箱とプラスチックケースに収められた VisionFive2 が入っているだけです。ドキュメント類は外箱の裏に書かれた URL からアクセスします。

資料

資料は外箱裏に載っている

の二か所を参照します。メーカーが提供しているイメージのダウンロード URL は Getting Started の Flasing with Linux or Windows のページにあるのが最初ちょっと不安感を煽ります。笑。確かに、必要な資料を読み進めると必要な時に出てくるので良いのですが。

あと、2022年12 月末時点では VisionFive2 を動かすにはいくつかハマり所があり、それを @osakanataro2 さんが懇切丁寧に整理されているので、この記事も参考にします。

StarFive Vision Five 2の2022年12月出荷分を使うには面倒くさい手順が必要

ちなみに、最近の中国製のこういうプロダクトは”翻訳すれば読めるでしょ?”という感じで中国語で書かれていることが結構あり、実際に翻訳されたものを読むだけで十分なので突然中国語が出てきても Google 様の力で翻訳してもらって読み進めます。VisionFive2 はかなりの部分が英語で書かれていますが、フォーラムなんかは中国語が多いですね。

作業

基本は前述の @osakanataro2 さんの記事に全て書いてあります。ただ、sdcard.img の作り方だけはサラッと流されていたので、補足しておきます。→元記事では releases にあるものを使う想定だったようです。たしかにこちらを使えばわざわざビルドする必要無いです。

sdcard.img を作るのは starfive-tech/VisionFive2 に書いてる手順に沿って行えば良いです。ただ、私の環境では開発者用環境のチェックアウトまで行わないとビルドが通らなかったので、その作業も行いました。

$ cd buildroot && git checkout --track origin/JH7110_VisionFive2_devel && cd ..
$ cd u-boot && git checkout --track origin/JH7110_VisionFive2_devel && cd ..
$ cd linux && git checkout --track origin/JH7110_VisionFive2_devel && cd ..
$ cd opensbi && git checkout master && cd ..
$ cd soft_3rdpart && git checkout JH7110_VisionFive2_devel && cd ..

ビルドの際、リポジトリには pre-build.sh, build.sh, post-build.sh があってこれを叩けばいいのではないかと思ってしまうのですが、ここはドキュメント通り make を実行するだけで 良いようです。ビルドは結構時間がかかり、私はビルドをしていたら年が明けてしまいました。

ビルドができたら Additional command to config buildroot, uboot, linux, busybox の項目を実行し、最後に genimage.sh を実行すると work/sdcard.img が作られます。このあたりの手順は刻々と状況が変わっていくと思うので、その時々試行錯誤することになるかと思います。

sdcard.img ができたら SDカードにイメージを焼いて VisionFive2 を起動すると Linux が立ち上がります。

U-Boot SPL 2021.10 (Oct 31 2022 - 12:11:37 +0800)
DDR version: dc2e84f0.
Trying to boot from SPI

OpenSBI v1.0
   ____                    _____ ____ _____
  / __ \                  / ____|  _ \_   _|
 | |  | |_ __   ___ _ __ | (___ | |_) || |
 | |  | | '_ \ / _ \ '_ \ \___ \|  _ < | |
 | |__| | |_) |  __/ | | |____) | |_) || |_
  \____/| .__/ \___|_| |_|_____/|____/_____|
        | |
        |_|

Platform Name             : StarFive VisionFive V2
Platform Features         : medeleg
Platform HART Count       : 5
Platform IPI Device       : aclint-mswi
Platform Timer Device     : aclint-mtimer @ 4000000Hz
Platform Console Device   : uart8250
Platform HSM Device       : ---
Platform Reboot Device    : ---
Platform Shutdown Device  : ---
Firmware Base             : 0x40000000
Firmware Size             : 360 KB
Runtime SBI Version       : 0.3

(中略)

Starting NFS statd: OK
Starting NFS services: OK
Starting NFS daemon: rpc.nfsd: Unable to access /proc/fs/nfsd errno 2 (No such file or directory).
Please try, as root, 'mount -t nfsd nfsd /proc/fs/nfsd' and then restart rpc.nfsd to correct the problem
FAIL
Starting NFS mountd: rpc.mountd: svc_tli_create: could not open connection for udp6
rpc.mountd: svc_tli_create: could not open connection for tcp6
rpc.mountd: svc_tli_create: could not open connection for udp6
rpc.mountd: svc_tli_create: could not open connection for tcp6
rpc.mountd: svc_tli_create: could not open connection for udp6
rpc.mountd: svc_tli_create: could not open connection for tcp6
OK
Starting isp_ctrl_daemon.sh: OK
Starting DHCP server: FAIL

Welcome to Buildroot
buildroot login: [   38.976567] mipi_0p9: disabling
root
Password: [  174.736301] random: crng init done
[  174.739759] random: 7 urandom warning(s) missed due to ratelimiting

# uname -a
Linux buildroot 5.15.0 #1 SMP Sat Dec 31 23:18:30 JST 2022 riscv64 GNU/Linux
#

sdcard.img を使った Linux 環境が立ち上がったので、あとは @osakanataro2 さんの記事に沿って作業を進めればよいです。

ちょっと長くなったので今回はここまで。