Playing around Arty A7 boards with FOSS toolchain

Ruinland Maskman
28 min readAug 31, 2020

--

Including flashing configuration SPI flash. No Vivado needed.

Flashing bitstream to Artrix 7's flash with bscan_spi - -Vivado Free !!!

上一篇文章我們淺談了SymbiFlow,但是我沒提到確切上該怎麼玩XD

於是變寫了這篇小補遺,來讓想動手的人可以快速測試 — —

首先,請您先clone這個SymbiFlow官方的測試專案repo:

git clone https://github.com/SymbiFlow/fpga-tool-perf

接下來,請使用此專案的Makefile來bootstrap所有的dependencies (注意:此步驟將會使用約20Gb的空間)

cd fpga-tool-perf; make env

拿杯咖啡、吃塊餅乾、拿本書等它一下 😉

安裝後完成,請您讓環境設定生效:

cd .. ; source env.sh

好的,您有一個裝好的SymbiFlow環境了🎉 如此地簡單、沒有要您填email、申請帳號之類無意義、不知所云的事情。所有安裝都封裝一個資料夾底下,以Python的Anaconda佈署管理、不需要時 rm -rf 一下馬上乾淨溜溜。

那我們打鐵趁熱,來挑個專案試試看:

python3 fpgaperf.py --verbose --toolchain vpr --project oneblink --board arty

這邊的option意義很簡單,verbose輸出額外的除錯訊息、toolchain使用vpr此一開源的APR而非Vivado,project則是在專案資料夾project裡面提供的測試專案,如現在在測試的oneblink(閃Led燈)、PicoSoC此一基於PicoRV32的小RISC-V SoC ...... 等等。

此時您會看到,這支script在專案底下的build資料夾開了一個新的資料夾

build/oneblink_vpr_xc7_ac45tcsg234-1_arty_pcf_carry-n

此時,我們便可偷偷cd進去該資料夾來一探究竟,便從Makefile開始吧— —

TOP := top 
VERILOG := /home/ruinland/fpga-tool-perf/src/oneblink/oneblink.v
PARTNAME:= xc7a35tcsg324-1
DEVICE := xc7a50t_test
BITSTREAM_DEVICE := artix7
SDC :=
PCF := /home/ruinland/fpga-tool-perf/src/oneblink/constr/arty.pcf
XDC :=
BUILDDIR:= .
PCF_OPTS = -p ${PCF}
PCF_FASM2BELS_OPTS = --pcf ${PCF}
all: ${TOP}.bit${BUILDDIR}:
mkdir ${BUILDDIR}
${TOP}.eblif: | ${BUILDDIR}
cd ${BUILDDIR} && symbiflow_synth -t ${TOP} -v ${VERILOG} -d ${BITSTREAM_DEVICE} -p ${PARTNAME} ${XDC_OPTS}
${TOP}.net: ${TOP}.eblif
cd ${BUILDDIR} && ${VPR_OPTIONS} symbiflow_pack -e ${TOP}.eblif -d ${DEVICE} ${SDC_OPTS}
${TOP}.place: ${TOP}.net
cd ${BUILDDIR} && ${VPR_SEED} ${VPR_OPTIONS} symbiflow_place -e ${TOP}.eblif -d ${DEVICE} -n ${TOP}.net -P ${PARTNAME} ${SDC_OPTS} ${PCF_OPTS}
${TOP}.route: ${TOP}.place
cd ${BUILDDIR} && ${VPR_OPTIONS} symbiflow_route -e ${TOP}.eblif -d ${DEVICE} ${SDC_OPTS}
${TOP}.fasm: ${TOP}.route
cd ${BUILDDIR} && ${VPR_OPTIONS} symbiflow_write_fasm -e ${TOP}.eblif -d ${DEVICE} ${SDC_OPTS}
${TOP}.bit: ${TOP}.fasm
cd ${BUILDDIR} && symbiflow_write_bitstream -d ${BITSTREAM_DEVICE} -f ${TOP}.fasm -p ${PARTNAME} -b ${TOP}.bit
clean:
rm -rf ${BUILDDIR}

我們可以很清楚的看出來SymbiFlow的運作流程: all的target便是生成top.bit此一最終的bitstream。

中間會經歷:

  1. symbiflow_synth:合成,可以觀察到參數需要帶上FPGA晶片的part name,因為中間流程會做techmap,故需要給予真正的FPGA資訊。
  2. symbiflow_pack:VPR特有的AAPack phase(將techmap過得extented BLIF netlist做更進一步的邏輯優化)
  3. symbiflow_place :布局,此時可以看見他參數帶有pcf檔案,也就是physical constraints file,內含pinout的規劃。
  4. symbiflow_route:繞線。
  5. symbiflow_write_fasm:此為產生目前Xilinx開源bitgen工具PrjXray自訂的中介語言,FPGA Assembly,以供後續的bitgen方便。
  6. symbiflow_write_bitstream:將最終的bitstream產生出來。

到這邊,我們已經產生了一個可以使用的bitstream:top.bit。

可以順便看一看fpgaperf.py告訴我們FPGA資源的狀況,諸如clock有沒有達標啦,LUT、BRAM 、D-FlipFlop的使用情形等等:

===============================
Clocks
===============================
+--------------+-------------+----------------+---------+-----------------+----------------+
| Clock domain | Actual freq | Requested freq | Met? | Setup violation | Hold violation |
+--------------+-------------+----------------+---------+-----------------+----------------+
| clk | 164.237 MHz | 0.000 MHz | unknown | 0.000 ns | 0.000 ns |
+--------------+-------------+----------------+---------+-----------------+----------------+
===============================
Toolchain Run-Times
===============================
+-----------------+--------------------+
| Stage | Run Time (seconds) |
+-----------------+--------------------+
| prepare | 0.209 |
| synthesis | 7.750 |
| optimization | N/A |
| packing | 0.240 |
| placement | 1.290 |
| routing | 1.250 |
| fasm | 30.756 |
| overhead | 47.989 |
| checkpoint | N/A |
| bitstream | 1.397 |
| reports | N/A |
| total | 94.965 |
| nop | 0.013 |
| fasm2bels | N/A |
| link design | N/A |
| phys opt design | N/A |
+-----------------+--------------------+
===============================
FPGA resource utilization
===============================
+----------+------+
| Resource | Used |
+----------+------+
| BRAM | 0 |
| CARRY | 6 |
| DFF | 24 |
| GLB | N/A |
| IOB | 2 |
| LUT | 1 |
| PLL | 0 |
+----------+------+

然而,要如何將檔案上傳的Arty A7上呢?

很簡單地,我們可以使用 xc3sprog 此一開源的FPGA programmer來使用:

xc3sprog -c nexys4 ./build/oneblink_vpr_xc7_ac45tcsg234-1_arty_pcf_carry-n/top.bit

此時,您便可看到Arty上面的LED燈正在閃爍著。

覺得亮個LED燈太無聊了,想來點special的嗎?那我們拿可以開Linux的LiteX/VexRiscv SoC吧,依樣畫葫蘆:

python3 fpgaperf.py --verbose --toolchain vpr --project baselitex--board arty

一樣泡個茶,吃個小點心,我們便能得到我們想要的bitstream:

build/baselitex_vpr_xc7_a35tcsg324-1_arty_pcf_sdc_xdc_carry-n/top.bit

可是呢,一如開頭副標題說的,很多時候,我們會想要燒到FPGA的configuration flash (通常是SPI flash)上面啊,不然這樣斷電就要side load到CRAM上很煩欸XD

好的,可以作到,但是開源工具總是會麻煩點:

一個很簡單的問題,請問這個SPI flash是怎麼跟FPGA的jtag串在一起的呢?其實真正的原理是Vivado/ISE會先side load一個bitstream上去、將flash的pin引入到JTAG上、再透過JTAG進行寫入。中間這樣的bitstream有很多種稱呼方式,例如JTAG-FLASH proxy 、proxy bitstream ...... 等等五花八門。

然而,也有人寫好了對應的開源bitstream實做:

https://github.com/quartiq/bscan_spi_bitstreams

將Arty A7需要的bscan_spi_xc7a35t.bit 下載下來,緊接著撰寫一個小小的openocd configuration file:

#load_proxy_and_flash.cfginterface ftdi
ftdi_vid_pid 0x0403 0x6010
ftdi_channel 0
ftdi_layout_init 0x00e8 0x60eb
reset_config none
source [find cpld/xilinx-xc7.cfg]
source [find cpld/jtagspi.cfg]
adapter_khz 25000
proc fpga_program {} {
global _CHIPNAME
xc7_program $_CHIPNAME.tap
}

緊接著將OpenOCD server建立起來:

openocd -f ./load_proxy_and_flash.cfg

telnet進去進行我們要的行為:

$ telnet localhost 4444

> init
> jtagspi_init 0 ./bscan_spi_xc7a35t.bit
> jtagspi_program ./top.bit 0x0
> fpga_program
> exit

中間您可看到它在進行SPI flash的re-programming:

Found flash device 'sp s25fl128s' (ID 0x00182001)
Found flash device 'sp s25fl128s' (ID 0x00182001)
Found flash device 'sp s25fl128s' (ID 0x00182001)
sector 0 took 1601 ms
sector 1 took 1619 ms
sector 2 took 93 ms
sector 3 took 105 ms
sector 4 took 98 ms
sector 5 took 94 ms
sector 6 took 95 ms
sector 7 took 99 ms
sector 8 took 95 ms
sector 9 took 90 ms
sector 10 took 94 ms
sector 11 took 93 ms
sector 12 took 96 ms
sector 13 took 98 ms
sector 14 took 94 ms
sector 15 took 98 ms
sector 16 took 105 ms
sector 17 took 98 ms
sector 18 took 98 ms
sector 19 took 98 ms
sector 20 took 102 ms
sector 21 took 98 ms
sector 22 took 96 ms
sector 23 took 102 ms
sector 24 took 100 ms
sector 25 took 97 ms
sector 26 took 100 ms
sector 27 took 94 ms
sector 28 took 97 ms
sector 29 took 100 ms
sector 30 took 95 ms
sector 31 took 101 ms
sector 32 took 100 ms
sector 33 took 96 ms
Found flash device 'sp s25fl128s' (ID 0x00182001)
read 2192119 bytes from file ./top.bit and flash bank 0 at offset 0x00000000 in 1.300241s (1646.419 KiB/s)
contents match

此時,該bitstream已經被我們燒錄到Arty的flash上面了,從此每次斷電重開,都會是LiteX/VexRiscv的核心在上面運作了 :-)

想測試 Linux的話,可以使用SymbiFlow已經gen好的images :

您需要下載資料夾emulator 以及 buildroot ,還有images.json ,並且透過PIP安裝LiteX的相關套件以供與SoC上的BIOS進行溝通:

安裝完成後,您可以使用LiteX專案專屬的terminal lxterm來進行Linux kernel/rootfs等等的傳輸:

lxterm --speed=115200 --serial-boot --images ./images.json --no-crc /dev/ttyUSB1

因為透過serial傳輸很慢,一樣的,請拿杯熱可可(被揍飛),好啦,如果你有興趣可以改用Ethernet的方式傳輸:

等一切順利傳輸上去,您便可看到Linux開機畫面:

[LXTERM] Starting....__   _ __      _  __
/ / (_) /____ | |/_/
/ /__/ / __/ -_)> <
/____/_/\__/\__/_/|_|
(c) Copyright 2012-2019 Enjoy-DigitalBIOS built on Feb 21 2020 15:04:55
BIOS CRC passed (a3c14d97)
Migen git sha1: d11565a
LiteX git sha1: 02bfda5e
--=============== SoC ==================--
CPU: VexRiscv @ 60MHz
ROM: 64KB
SRAM: 32KB
L2: 8KB
MAIN-RAM: 262144KB
--========== Initialization ============--
Ethernet init...
Initializing SDRAM...
SDRAM now under software control
Read leveling:
m0, b0: |00000000000000000000001111111111| delays: 27+-05
m0, b1: |00000000000000000000000000000000| delays: -
m0, b2: |00000000000000000000000000000000| delays: -
m0, b3: |00000000000000000000000000000000| delays: -
m0, b4: |00000000000000000000000000000000| delays: -
m0, b5: |00000000000000000000000000000000| delays: -
m0, b6: |00000000000000000000000000000000| delays: -
m0, b7: |00000000000000000000000000000000| delays: -
best: m0, b0 delays: 27+-05
m1, b0: |00000000000000000000001111111111| delays: 27+-05
m1, b1: |00000000000000000000000000000000| delays: -
m1, b2: |00000000000000000000000000000000| delays: -
m1, b3: |00000000000000000000000000000000| delays: -
m1, b4: |00000000000000000000000000000000| delays: -
m1, b5: |00000000000000000000000000000000| delays: -
m1, b6: |00000000000000000000000000000000| delays: -
m1, b7: |00000000000000000000000000000000| delays: -
best: m1, b0 delays: 27+-05
SDRAM now under hardware control
Memtest OK
--============== Boot ==================--
Booting from serial...
Press Q or ESC to abort boot completely.
sL5DdSMmkekro
[LXTERM] Received firmware download request from the device.
[LXTERM] Uploading buildroot/Image to 0xc0000000 (4652188 bytes)...
[LXTERM] Upload complete (9.9KB/s).
[LXTERM] Uploading buildroot/rootfs.cpio to 0xc0800000 (4062720 bytes)...
[LXTERM] Upload complete (9.9KB/s).
[LXTERM] Uploading buildroot/rv32.dtb to 0xc1000000 (1838 bytes)...
[LXTERM] Upload complete (10.1KB/s).
[LXTERM] Uploading emulator/emulator.bin to 0x50000000 (2992 bytes)...
[LXTERM] Upload complete (9.8KB/s).
[LXTERM] Booting the device.
[LXTERM] Done.
KExecuting booted program at 0x50000000
--============= Liftoff! ===============--
*** VexRiscv BIOS ***
*** Supervisor ***
[ 0.000000] No DTB passed to the kernel
[ 0.000000] Linux version 5.0.13 (tmichalak@localhost.localdomain) (gcc version 8.3.0 (Buildroot 2020.02-git-00142-ga4d38f029f)) #1 Tue Dec 3 08:55:41 CET 2019
[ 0.000000] earlycon: sbi0 at I/O port 0x0 (options '')
[ 0.000000] printk: bootconsole [sbi0] enabled
[ 0.000000] Initial ramdisk at: 0x(ptrval) (8388608 bytes)
[ 0.000000] Zone ranges:
[ 0.000000] Normal [mem 0x00000000c0000000-0x00000000cfffffff]
[ 0.000000] Movable zone start for each node
[ 0.000000] Early memory node ranges
[ 0.000000] node 0: [mem 0x00000000c0000000-0x00000000cfffffff]
[ 0.000000] Initmem setup node 0 [mem 0x00000000c0000000-0x00000000cfffffff]
[ 0.000000] elf_hwcap is 0x1101
[ 0.000000] Built 1 zonelists, mobility grouping on. Total pages: 65024
[ 0.000000] Kernel command line: mem=256M@0xc0000000 rootwait console=liteuart earlycon=sbi root=/dev/ram0 init=/sbin/init swiotlb=32
[ 0.000000] Dentry cache hash table entries: 32768 (order: 5, 131072 bytes)
[ 0.000000] Inode-cache hash table entries: 16384 (order: 4, 65536 bytes)
[ 0.000000] Sorting __ex_table...
[ 0.000000] Memory: 247124K/262144K available (3490K kernel code, 150K rwdata, 530K rodata, 148K init, 216K bss, 15020K reserved, 0K cma-reserved)
[ 0.000000] SLUB: HWalign=64, Order=0-3, MinObjects=0, CPUs=1, Nodes=1
[ 0.000000] NR_IRQS: 0, nr_irqs: 0, preallocated irqs: 0
[ 0.000000] clocksource: riscv_clocksource: mask: 0xffffffffffffffff max_cycles: 0x171024e7e0, max_idle_ns: 440795205315 ns
[ 0.000222] sched_clock: 64 bits at 100MHz, resolution 10ns, wraps every 4398046511100ns
[ 0.006386] Console: colour dummy device 80x25
[ 0.009331] Calibrating delay loop (skipped), value calculated using timer frequency.. 200.00 BogoMIPS (lpj=400000)
[ 0.015519] pid_max: default: 32768 minimum: 301
[ 0.025904] Mount-cache hash table entries: 1024 (order: 0, 4096 bytes)
[ 0.029913] Mountpoint-cache hash table entries: 1024 (order: 0, 4096 bytes)
[ 0.075275] devtmpfs: initialized
[ 0.105604] random: get_random_bytes called from setup_net+0x4c/0x188 with crng_init=0
[ 0.114941] clocksource: jiffies: mask: 0xffffffff max_cycles: 0xffffffff, max_idle_ns: 7645041785100000 ns
[ 0.120748] futex hash table entries: 256 (order: -1, 3072 bytes)
[ 0.131766] NET: Registered protocol family 16
[ 0.286082] FPGA manager framework
[ 0.303140] clocksource: Switched to clocksource riscv_clocksource
[ 0.548355] NET: Registered protocol family 2
[ 0.563927] tcp_listen_portaddr_hash hash table entries: 512 (order: 0, 4096 bytes)
[ 0.568795] TCP established hash table entries: 2048 (order: 1, 8192 bytes)
[ 0.573321] TCP bind hash table entries: 2048 (order: 1, 8192 bytes)
[ 0.577377] TCP: Hash tables configured (established 2048 bind 2048)
[ 0.583679] UDP hash table entries: 256 (order: 0, 4096 bytes)
[ 0.587338] UDP-Lite hash table entries: 256 (order: 0, 4096 bytes)
[ 0.604369] Unpacking initramfs...
[ 1.967426] Initramfs unpacking failed: junk in compressed archive
[ 1.988638] workingset: timestamp_bits=30 max_order=16 bucket_order=0
[ 2.412375] Block layer SCSI generic (bsg) driver version 0.4 loaded (major 253)
[ 2.416669] io scheduler mq-deadline registered
[ 2.419366] io scheduler kyber registered
[ 3.921486] f0001800.serial: ttyLXU0 at MMIO 0xf0001800 (irq = 0, base_baud = 0) is a liteuart
[ 3.927178] printk: console [liteuart0] enabled
[ 3.927178] printk: console [liteuart0] enabled
[ 3.932434] printk: bootconsole [sbi0] disabled
[ 3.932434] printk: bootconsole [sbi0] disabled
[ 3.958308] litex-spiflash f0005000.spiflash: unrecognized JEDEC id bytes: 00, 00, 00
[ 3.963163] litex-spiflash f0005000.spiflash: SPI_NOR_SCAN FAILED
[ 3.966582] litex-spiflash: probe of f0005000.spiflash failed with error -2
[ 3.979488] libphy: Fixed MDIO Bus: probed
[ 3.983314] liteeth f0007800.mac: Failed to get IRQ, using polling
[ 4.001320] liteeth f0007800.mac eth0: irq 0, mapped at a0004800
[ 4.009953] i2c /dev entries driver
[ 4.066196] NET: Registered protocol family 10
[ 4.089928] Segment Routing with IPv6
[ 4.093474] sit: IPv6, IPv4 and MPLS over IPv4 tunneling driver
[ 4.139489] Freeing unused kernel memory: 148K
[ 4.141538] This architecture does not have kernel memory protection.
[ 4.145829] Run /init as init process
mount: mounting tmpfs on /dev/shm failed: Invalid argument
mount: mounting tmpfs on /tmp failed: Invalid argument
mount: mounting tmpfs on /run failed: Invalid argument
Starting syslogd: OK
Starting klogd: OK
Running sysctl: OK
Saving random seed: [ 6.814260] random: dd: uninitialized urandom read (512 bytes read)
OK
Starting network: OK
Welcome to Buildroot
buildroot login: root
__ _
/ / (_)__ __ ____ __
/ /__/ / _ \/ // /\ \ /
/____/_/_//_/\_,_//_\_\
/ _ \/ _ \
__ _ __ _\___/_//_/ __ _
/ / (_) /____ | |/_/__| | / /____ __ ____(_)__ _____ __
/ /__/ / __/ -_)> </___/ |/ / -_) \ // __/ (_-</ __/ |/ /
/____/_/\__/\__/_/|_| |___/\__/_\_\/_/ /_/___/\__/|___/
32-bit VexRiscv CPU with MMU integrated in a LiteX SoClogin[75]: root login on 'console'
root@buildroot:~# cat /proc/cpuinfo
processor : 0
hart : 0
isa : rv32ima
mmu : sv32
uarch : spinalhdl,vexriscv
root@buildroot:~#

即便現在很粗糙,沒有辦法讓您一個按鈕按下去就什麼都弄好,但是開源的世界正在到來。

如果您也支持開源工具鏈、讓學生/教師們都能在不受限的情況下開發,也請在能力上、經濟上支持 SymbiFlow,讓開發得以持續下去 😎

Ref : This mailing list thread - -

https://lists.librecores.org/pipermail/symbiflow/2020-August/000029.html

--

--