FPGAを使ったRISC-V SoC(第1回)
環境構築~Linux起動
はじめに
RISC-Vの充実したOSSエコシステムを使えば、ソフトウェアエンジニアでも手軽に独自CPU(RISC-V softcore processor)/SoCを作ることができます。
ここでは、弊社がEdgeTech+ West 2023でデモ展示を行った、Litex(+Migen)によるFPGA SoCの合成、BuildrootによるLinuxディストリビューションの構築方法を解説します。
全体の流れ
- 開発環境の構築
- LitexでRisc-VSoCのbitstream生成とDeviceTree生成
- Linuxディストリビューションの生成
- RISC-V向けSBI(Supervisor Binary Interface)の生成
使用機材
- ubuntu 20.04(メモリ8G、ストレージ256G以上)
- Arty A7-100T
- SDカード
- Pmod MicroSD
開発環境の構築
必要なパッケージのインストール
$ sudo apt-get update
$ sudo apt-get install openocd dtc fakeroot perl-bignum json-c-devel verilator ¥
python3-devel python3-setuptools libevent-devel ¥
libmpc-devel mpfr-devel meson expat-devel ¥
gcc-10 texinfo bison flex expat libexpat-dev ¥
g++ m4 zlib1g-dev make p7zip libgmp-dev python3-pip curl
$ pip install migen
$ pip install -U meson
Xilinx開発ツール(Vivado ML エディション 2022.2)のインストール
AMDのダウンロードページよりダンロードしてインストールする。
環境変数を登録
$ export PATH=$PATH:/tools/Xilinx/Vivado/2022.2/bin
※˜/.bashrcへ記載することで、起動時に自動的にPATH設定を行うことが可能
RISC-V GNU Compiler Toolchainのインストール
インストール先のディレクトリを作成
$ cd /opt/
$ sudo mkdir riscv
$ sudo chmod 777 riscv
$ cd ˜
作成したディレクトリにToolchainをインストール
$ git clone --recursive https://github.com/riscv/riscv-gnu-toolchain
$ pushd riscv-gnu-toolchain
$ ./configure --prefix=/opt/riscv --enable-multilib
$ make newlib linux
$ popd
$ export PATH=$PATH:/opt/riscv/bin
※˜/.bashrcへ記載することで、開発環境起動時に自動的にPATH設定を行うことが可能
linux-on-litex-vexriscvとLitexの開発環境のインストール
linux-on-litex-vexriscvのインストール
$ mkdir litex-vexriscv
$ cd litex-vexriscv
$ git clone https://github.com/litex-hub/linux-on-litex-vexriscv
$ cd linux-on-litex-vexriscv
Litexのインストール
$ wget https://raw.githubusercontent.com/enjoy-digital/litex/master/litex_setup.py
$ chmod +x litex_setup.py
$ ./litex_setup.py --init --install --user --tag 2023.04
※issues 1717(https://github.com/enjoy-digital/litex/issues/1717)によりbitstreamの生成時にエラーとなるためtagを固定
bitstreamの生成
Arty A7 bitstreamの生成
$ ./make.py --board=arty_a7 --variant=a7-100 --build --cpu-count 2
以下のファイルが生成されたら成功
生成物 | ファイル |
デバイスツリー | build/arty_a7/arty_a7.dts build/arty_a7/arty_a7.dtb |
FPGA bitstream | build/arty_a7/gateware/arty_a7.bit |
bitstreamをFPGA書き込み用のファイルフォーマット(mcs)に変換
$ cd build/arty_a7/gateware/
$ cat > arty_a7_mcs.tcl <<EOF
write_cfgmem -force -format mcs -interface spix4 -size 16 -loadbit "up 0x0 arty_a7.bit" -file arty_a7.mcs
EOF
$ vivado -mode batch -source arty_a7_mcs.tcl
Linuxディストリビューションの生成
buildrootセットアップ
Buildroot - Making Embedded Linux Easyから取得し展開
$ cd ˜
$ wget https://buildroot.org/downloads/buildroot-2023.05.1.tar.gz
$ tar xzvf buildroot-2023.05.1.tar.gz
$ mv buildroot-2023.05.1 buildroot
$ cd buildroot
コンフィグレーションの読み込み
$ make BR2_EXTERNAL=<Litexの開発環境のインストールディレクトリ>/linux-on-litex-vexriscv/buildroot/ litex_vexriscv_defconfig
注)フォルダとファイル名の間のスペースは必須
Linuxカーネルとアプリ層で使用するヘッダファイルのバージョンに差異があるためコンフィグレーションを変更
$ make menuconfig
-> Toolchain
-> Custom kernel headers series[6.1.x]
ビルドの実行
$ make
以下のファイルが生成されたら成功
生成物 | ファイル |
Linux カーネル | output/images/Image |
rootファイル | output/images/rootfs.cpio output/images/rootfs.tar |
RISC-V向けSBI(Supervisor Binary Interface)の生成
opensbiを取得
$ cd ˜
$ git clone https://github.com/litex-hub/opensbi --branch 0.8-linux-on-litex-vexriscv
$ cd opensbi
環境変数の変更
platform/litex/vexriscv/config.mk
PLATFORM_RISCV_ISA = rv32ima_zicsr_zifencei
ビルドの実行
$ make CROSS_COMPILE=riscv64-unknown-elf- PLATFORM=litex/vexriscv
※習得したソースコードではmoddi3やdivdi3でコンパイルエラーが出るため、「./lib/sbi/sbi_console.c」に公開されている以下のコードをインクルードすることで対応した。
https://raw.githubusercontent.com/glitchub/arith64/master/arith64.c
以下のファイルが生成されたら成功
生成物 | ファイル |
fw_jump.bin | build/platform/litex/vexriscv/firmware/fw_jump.bin |
起動メディアの作成
SDカードに起動イメージを書き込み
$ sudo fdisk /dev/sdb
デバイス 開始位置 最後から セクタ サイズ タイプ
/dev/sdb1 2048 307199 305152 149M Linux ファイルシステム
/dev/sdb2 307200 61863902 61556703 29.4G Linux ファイルシステム
$ sudo mkfs.vfat /dev/sdb1
$ sudo mkfs.ext4 /dev/sdb2
$ sudo mount /dev/sdb1 /mnt
$ sudo cp ˜/buildroot/output/images/Image /mnt/.
$ sudo cp ˜/litex-vexriscv/linux-on-litex-vexriscv/build/arty_a7/arty_a7.dtb /mnt/.
$ sudo cp ˜/buildroot/output/images/rootfs.cpio /mnt/.
$ sudo cp ˜/opensbi/build/platform/litex/vexriscv/firmware/fw_jump.bin /mnt/.
$ cat > ˜/litex-vexriscv/linux-on-litex-vexriscv/images/boot.json <<EOF
{
"Image": "0x40000000",
"arty_a7.dtb": "0x40ef0000",
"rootfs.cpio": "0x41000000",
"fw_jump.bin": "0x40f00000"
}
EOF
$ sudo cp ˜/litex-vexriscv/linux-on-litex-vexriscv/images/boot.json /mnt/.
bitstreamの書き込み
Vivadoを起動し、USBケーブルで接続する。
以下の画面通りに設定
bitstreamの生成において準備したmcsをConfiguration fileとして指定し、bitstearmを書き込む。
起動確認
起動メディアの作成において準備したSDカードをPmod MicroSDに挿入し、ターゲットの12ピンコネクタ(JD) に指し、ターゲットとPCをUSBケーブルで接続する。
TeraTerm等でシリアルコンソールへアクセスすると、Linuxが起動していることが確認できる。
起動時のコンソール出力
/ / (_) /____ | |/_/
/ /__/ / __/ -_)> <
/____/_/¥__/¥__/_/|_|
Build your hardware, easily!
(c) Copyright 2012-2023 Enjoy-Digital
(c) Copyright 2007-2015 M-Labs
BIOS CRC passed (cb03754a)
LiteX git sha1: 3ab7ebe5
--=============== SoC ==================--
CPU: VexRiscv SMP-LINUX @ 100MHz
BUS: WISHBONE 32-bit @ 4GiB
CSR: 32-bit data
ROM: 64.0KiB
SRAM: 6.0KiB
FLASH: 16.0MiB
Couldn't read SDRAM size from the SPD, defaulting to 256 MB.
SDRAM: 256.0MiB 16-bit @ 800MT/s (CL-7 CWL-5)
MAIN-RAM: 256.0MiB
--========== Initialization ============--
Ethernet init...
Initializing SDRAM @0x40000000...
Switching SDRAM to software control.
Read leveling:
m0, b00: |00000000000000000000000000000000| delays: -
m0, b01: |00000000000000000000000000000000| delays: -
m0, b02: |11111111111110000000000000000000| delays: 06+-06
m0, b03: |00000000000000001111111111111000| delays: 21+-06
m0, b04: |00000000000000000000000000000000| delays: -
m0, b05: |00000000000000000000000000000000| delays: -
m0, b06: |00000000000000000000000000000000| delays: -
m0, b07: |00000000000000000000000000000000| delays: -
best: m0, b03 delays: 21+-06
m1, b00: |00000000000000000000000000000000| delays: -
m1, b01: |00000000000000000000000000000000| delays: -
m1, b02: |11111111111110000000000000000000| delays: 06+-06
m1, b03: |00000000000000001111111111111000| delays: 22+-06
m1, b04: |00000000000000000000000000000000| delays: -
m1, b05: |00000000000000000000000000000000| delays: -
m1, b06: |00000000000000000000000000000000| delays: -
m1, b07: |00000000000000000000000000000000| delays: -
best: m1, b03 delays: 22+-06
Switching SDRAM to hardware control.
Memtest at 0x40000000 (2.0MiB)...
Write: 0x40000000-0x40200000 2.0MiB
Read: 0x40000000-0x40200000 2.0MiB
Memtest OK
Memspeed at 0x40000000 (Sequential, 2.0MiB)...
Write speed: 169.4MiB/s
Read speed: 88.1MiB/s
Initializing S25FL128L SPI Flash @0x01000000...
Enabling Quad mode...
SPI Flash clk configured to 100 MHz
Memspeed at 0x1000000 (Sequential, 4.0KiB)...
Read speed: 22.1MiB/s
Memspeed at 0x1000000 (Random, 4.0KiB)...
Read speed: 4.6MiB/s
--============== Boot ==================--
Booting from serial...
Press Q or ESC to abort boot completely.
sL5DdSMmkekro
Timeout
Booting from SDCard in SD-Mode...
Booting from boot.json...
Copying Image to 0x40000000 (7726264 bytes)...
[########################################]
Copying arty_a7.dtb to 0x40ef0000 (5862 bytes)...
[########################################]
Copying rootfs.cpio to 0x41000000 (3566592 bytes)...
[########################################]
Copying fw_jump.bin to 0x40f00000 (49544 bytes)...
[########################################]
Executing booted program at 0x40f00000
--============= Liftoff! ===============--
OpenSBI v0.8-2-ga9ce3ad
____ _____ ____ _____
/ __ ¥ / ____| _ ¥_ _|
| | | |_ __ ___ _ __ | (___ | |_) || |
| | | | '_ ¥ / _ ¥ '_ ¥ ¥___ ¥| _ < | |
| | | | |_) | __/ | | |____) | |_) || |_
¥____/| .__/ ¥___|_| |_|_____/|____/_____|
| |
|_|
Platform Name : LiteX / VexRiscv-SMP
Platform Features : timer,mfdeleg
Platform HART Count : 8
Boot HART ID : 0
Boot HART ISA : rv32imas
BOOT HART Features : time
BOOT HART PMP Count : 0
Firmware Base : 0x40f00000
Firmware Size : 120 KB
Runtime SBI Version : 0.2
MIDELEG : 0x00000222
MEDELEG : 0x0000b101
[ 0.000000] Linux version 6.1.0-rc2 (developer@developer-VirtualBox) (riscv32-buildroot-linux-gnu-gcc.br_real (Buildroot 2023.02-267-g1ace31aec5) 11.3.0, GNU ld (GNU Binutils) 2.38) #1 SMP Wed Jul 12 17:52:12 JST 2023
[ 0.000000] earlycon: liteuart0 at I/O port 0x0 (options '')
[ 0.000000] Malformed early option 'console'
[ 0.000000] earlycon: liteuart0 at MMIO 0xf0001000 (options '')
[ 0.000000] printk: bootconsole [liteuart0] enabled
[ 0.000000] Zone ranges:
[ 0.000000] Normal [mem 0x0000000040000000-0x000000004fffffff]
[ 0.000000] Movable zone start for each node
[ 0.000000] Early memory node ranges
[ 0.000000] node 0: [mem 0x0000000040000000-0x000000004fffffff]
[ 0.000000] Initmem setup node 0 [mem 0x0000000040000000-0x000000004fffffff]
[ 0.000000] SBI specification v0.2 detected
[ 0.000000] SBI implementation ID=0x1 Version=0x8
[ 0.000000] SBI TIME extension detected
[ 0.000000] SBI IPI extension detected
[ 0.000000] SBI RFENCE extension detected
[ 0.000000] SBI HSM extension detected
[ 0.000000] riscv: base ISA extensions aim
[ 0.000000] riscv: ELF capabilities aim
[ 0.000000] percpu: Embedded 8 pages/cpu s11732 r0 d21036 u32768
[ 0.000000] Built 1 zonelists, mobility grouping on. Total pages: 65024
[ 0.000000] Kernel command line: console=liteuart earlycon=liteuart,0xf0001000 rootwait root=/dev/ram0
[ 0.000000] Dentry cache hash table entries: 32768 (order: 5, 131072 bytes, linear)
[ 0.000000] Inode-cache hash table entries: 16384 (order: 4, 65536 bytes, linear)
[ 0.000000] mem auto-init: stack:off, heap alloc:off, heap free:off
[ 0.000000] Memory: 243304K/262144K available (5848K kernel code, 571K rwdata, 906K rodata, 215K init, 254K bss, 18840K reserved, 0K cma-reserved)
[ 0.000000] SLUB: HWalign=64, Order=0-3, MinObjects=0, CPUs=2, Nodes=1
[ 0.000000] rcu: Hierarchical RCU implementation.
[ 0.000000] rcu: RCU restricting CPUs from NR_CPUS=32 to nr_cpu_ids=2.
[ 0.000000] rcu: RCU calculated value of scheduler-enlistment delay is 25 jiffies.
[ 0.000000] rcu: Adjusting geometry for rcu_fanout_leaf=16, nr_cpu_ids=2
[ 0.000000] NR_IRQS: 64, nr_irqs: 64, preallocated irqs: 0
[ 0.000000] riscv-intc: 32 local interrupts mapped
[ 0.000000] plic: interrupt-controller@f0c00000: mapped 32 interrupts with 2 handlers for 4 contexts.
[ 0.000000] rcu: srcu_init: Setting srcu_struct sizes based on contention.
[ 0.000000] riscv-timer: riscv_timer_init_dt: Registering clocksource cpuid [0] hartid [0]
[ 0.000000] clocksource: riscv_clocksource: mask: 0xffffffffffffffff max_cycles: 0x171024e7e0, max_idle_ns: 440795205315 ns
[ 0.000015] sched_clock: 64 bits at 100MHz, resolution 10ns, wraps every 4398046511100ns
[ 0.009906] Console: colour dummy device 80x25
[ 0.013610] Calibrating delay loop (skipped), value calculated using timer frequency.. 200.00 BogoMIPS (lpj=400000)
[ 0.023765] pid_max: default: 32768 minimum: 301
[ 0.031331] Mount-cache hash table entries: 1024 (order: 0, 4096 bytes, linear)
[ 0.037697] Mountpoint-cache hash table entries: 1024 (order: 0, 4096 bytes, linear)
[ 0.069453] ASID allocator using 9 bits (512 entries)
[ 0.075391] rcu: Hierarchical SRCU implementation.
[ 0.079151] rcu: Max phase no-delay instances is 1000.
[ 0.090641] smp: Bringing up secondary CPUs ...
[ 0.105314] smp: Brought up 1 node, 2 CPUs
[ 0.115009] devtmpfs: initialized
[ 0.153306] clocksource: jiffies: mask: 0xffffffff max_cycles: 0xffffffff, max_idle_ns: 7645041785100000 ns
[ 0.162225] futex hash table entries: 512 (order: 3, 32768 bytes, linear)
[ 0.187286] NET: Registered PF_NETLINK/PF_ROUTE protocol family
[ 0.348629] pps_core: LinuxPPS API ver. 1 registered
[ 0.352547] pps_core: Software ver. 5.3.6 - Copyright 2005-2007 Rodolfo Giometti <giometti@linux.it>
[ 0.361937] PTP clock support registered
[ 0.367876] FPGA manager framework
[ 0.379906] clocksource: Switched to clocksource riscv_clocksource
[ 0.562231] NET: Registered PF_INET protocol family
[ 0.569375] IP idents hash table entries: 4096 (order: 3, 32768 bytes, linear)
[ 0.587348] tcp_listen_portaddr_hash hash table entries: 512 (order: 0, 4096 bytes, linear)
[ 0.595120] Table-perturb hash table entries: 65536 (order: 6, 262144 bytes, linear)
[ 0.602710] TCP established hash table entries: 2048 (order: 1, 8192 bytes, linear)
[ 0.610548] TCP bind hash table entries: 2048 (order: 3, 32768 bytes, linear)
[ 0.617866] TCP: Hash tables configured (established 2048 bind 2048)
[ 0.624573] UDP hash table entries: 256 (order: 1, 8192 bytes, linear)
[ 0.630317] UDP-Lite hash table entries: 256 (order: 1, 8192 bytes, linear)
[ 0.651349] Unpacking initramfs...
[ 0.656516] workingset: timestamp_bits=30 max_order=16 bucket_order=0
[ 0.819202] io scheduler mq-deadline registered
[ 0.822765] io scheduler kyber registered
[ 0.866989] No litex,nclkout entry in the dts file
[ 0.872690] LiteX SoC Controller driver initialized
[ 1.243069] Initramfs unpacking failed: invalid magic at start of compressed archive
[ 1.323144] Freeing initrd memory: 8192K
[ 1.966135] f0001000.serial: ttyLXU0 at MMIO 0x0 (irq = 0, base_baud = 0) is a liteuart
[ 1.973515] printk: console [liteuart0] enabled
[ 1.973515] printk: console [liteuart0] enabled
[ 1.982171] printk: bootconsole [liteuart0] disabled
[ 1.982171] printk: bootconsole [liteuart0] disabled
[ 2.028834] liteeth f0002000.mac eth0: irq 2 slots: tx 2 rx 2 size 2048
[ 2.037466] i2c_dev: i2c /dev entries driver
[ 2.045935] i2c i2c-0: Not I2C compliant: can't read SCL
[ 2.050310] i2c i2c-0: Bus may be unreliable
[ 2.096250] litex-mmc f0009000.mmc: LiteX MMC controller initialized.
[ 2.127548] NET: Registered PF_INET6 protocol family
[ 2.149452] Segment Routing with IPv6
[ 2.153212] In-situ OAM (IOAM) with IPv6
[ 2.157033] sit: IPv6, IPv4 and MPLS over IPv4 tunneling driver
[ 2.172247] NET: Registered PF_PACKET protocol family
[ 2.185301] Freeing unused kernel image (initmem) memory: 208K
[ 2.190128] Kernel memory protection not selected by kernel config.
[ 2.196533] Run /init as init process
[ 2.277931] mmc0: new SDHC card at address 59b4
[ 2.296847] mmcblk0: mmc0:59b4 USD 7.51 GiB
[ 2.320867] mmcblk0: p1 p2
Starting syslogd: OK
Starting klogd: OK
Running sysctl: OK
Saving 256 bits of non-creditable seed for next boot
Starting network: OK
Welcome to Buildroot
buildroot login:
おわり
次回は、Adafruit 2.8 TFT Touch Shield取り付けて液晶とタッチパネル操作にチャレンジします。
おまけ rootfsをinitrdからSDカードへ変更
SDカードにrootfsを展開
$ sudo mount /dev/sdb2 /mnt
$ sudo tar -vxf ~/buildroot/output/images/rootfs.tar -C /mnt/.
デバイスツリー(DTB)に格納されているLinuxの起動パラメータ(cmdline)をSDカードへ変更
$ cd ˜/litex-vexriscv/linux-on-litex-vexriscv/
$ ../litex/litex/tools/litex_json2dts_linux.py --initrd disabled --root-device mmcblk0p2 ¥
build/arty_a7/csr.json > build/arty_a7/arty_a7_mmc.dts
$ dtc -O dtb -o build/arty_a7/arty_a7_mmc.dtb build/arty_a7/arty_a7_mmc.dts
DTBを差し替える
$ sudo mount /dev/sdb1 /mnt
$ sudo cp ~/litex-vexriscv/linux-on-litex-vexriscv/build/arty_a7/arty_a7_mmc.dtb /mnt/.
SDカードのboot.jsonを以下のように書き換える
{
"Image": "0x40000000",
"arty_a7_mmc.dtb": "0x40ef0000",
"fw_jump.bin": "0x40f00000"
}