Open FPGA toolchain (F4PGA) を 使ってみる。
Open FPGA toolchain (F4PGA) を仕事で使っているメインの M1 Mac にインストール(正確にはソースから build した)できたので、少し深掘りしてみます。以下のページを参考にします。
ツールのM1 Mac での build と install に関しては以下を参照。
なお、今回は FPGA board として Arty-A7 100T を使って実験します。
ファイル準備
counter_test.v
実装する Verilog file を用意します。今回は f4pga-example に同梱されている counter_test 用の counter_test.v を使います。
module top (
input clk,
output [3:0] led
);
localparam BITS = 4;
localparam LOG2DELAY = 24;
wire bufg;
BUFG bufgctrl (
.I(clk),
.O(bufg)
);
reg [BITS+LOG2DELAY-1:0] counter = 0;
always @(posedge bufg) begin
counter <= counter + 1;
end
assign led[3:0] = counter >> LOG2DELAY;
endmodule
arty.xdc
実装する Arty-A7 用の xdc file (Xilinx Design Constraint=合成制約ファイル)を用意します。今回は f4pga-example に同梱されている counter_test 用の arty.v を使います。
# Clock pin
set_property PACKAGE_PIN E3 [get_ports {clk}]
set_property IOSTANDARD LVCMOS33 [get_ports {clk}]
# LEDs
set_property PACKAGE_PIN H5 [get_ports {led[0]}]
set_property PACKAGE_PIN J5 [get_ports {led[1]}]
set_property PACKAGE_PIN T9 [get_ports {led[2]}]
set_property PACKAGE_PIN T10 [get_ports {led[3]}]
set_property IOSTANDARD LVCMOS33 [get_ports {led[0]}]
set_property IOSTANDARD LVCMOS33 [get_ports {led[1]}]
set_property IOSTANDARD LVCMOS33 [get_ports {led[2]}]
set_property IOSTANDARD LVCMOS33 [get_ports {led[3]}]
# Clock constraints
create_clock -period 10.0 [get_ports {clk}]
ちなみに、Master xdc file は以下にあります。
Toolchain
F4PGA (元Symbiflow) toolchain は、Python script で諸々のツールを一つの command になるようにまとめている。
また、f4pga-example では、以下のコマンド群を make コマンドで一気にbuild できるように Makefile を記述している。
論理合成
Verilog から top.eblif ファイルが作られます。
$ symbiflow_synth -t top -v counter.v -d artix7 -p xc7a100tcsg324-1 -x arty.xdc 2>&1 > /dev/null
実体は …dist-packages/f4pga/wrappers/sh/__init__.py から synth モジュールが呼ばれた後に、…dist-packages/f4pga/wrappers/sh/xc7/synth.f4pga.sh から yosys が実行される。
なお、eblif (extended BLIF format)に関しては以下を参照
https://people.eecs.berkeley.edu/~alanmi/publications/other/boxes01.pdf
パック
top.eblif ファイルからFPGAの物理タイルに対応した top.net ファイルを作成する。
$ symbiflow_pack -e top.eblif -d xc7a100t_test 2>&1 > /dev/null
実体は …dist-packages/f4pga/wrappers/sh/__init__.py から pack モジュールが呼ばれた後に、vpr が実行される。
def pack():
print("[F4PGA] Running (deprecated) pack")
extra_args = ["--write_block_usage", "block_usage.json"] if isQuickLogic else []
p_vpr_run(["--pack"] + extra_args, env=p_vpr_env_from_args("pack"))
Path("vpr_stdout.log").rename("pack.log")
配置
top.eblif と top.net ファイルから xc7a100tcsg324-1 デバイスに配置するための top.place, top.ioplace ファイルが作られる。
$ symbiflow_place -e top.eblif -d xc7a100t_test -n top.net -P xc7a100tcsg324-1 2>&1 > /dev/null
実体は …dist-packages/f4pga/wrappers/sh/__init__.py から place モジュールが呼ばれた後に、vpr が実行される。
def place():
print("[F4PGA] Running (deprecated) place")
place_cmds += 'python3 -m f4pga.wrappers.sh.vpr_run --fix_clusters "${VPR_PLACE_FILE}" --place'
p_run_bash_cmds(place_cmds, env=p_vpr_env_from_args("place"))
Path("vpr_stdout.log").rename("place.log")
配線
top.route ファイルが作られる。
$ symbiflow_route -e top.eblif -d xc7a100t_test 2>&1 > /dev/null
実体は …dist-packages/f4pga/wrappers/sh/__init__.py から route モジュールが呼ばれた後に、vpr が実行される。
def route():
print("[F4PGA] Running (deprecated) route")
extra_args = ["--write_timing_summary", "timing_summary.json"] if isQuickLogic else []
p_vpr_run(["--route"] + extra_args, env=p_vpr_env_from_args("pack"))
Path("vpr_stdout.log").rename("route.log")
FASM (FPGA Assembler)
top.fasm ファイルが作られる(プラットフォーム固有の記述)
$ symbiflow_write_fasm -e top.eblif -d xc7a100t_test 2>&1 > /dev/null
実体は …dist-packages/f4pga/wrappers/sh/__init__.py から write_fasm モジュールが呼ばれる。
ビットファイル生成
top.fasm ファイルから top.bit を作る。
$ symbiflow_write_bitstream -d artix7 -f top.fasm -p xc7a100tcsg324-1 -b top.bit 2>&1 > /dev/null
実体は …dist-packages/f4pga/wrappers/sh/__init__.py から write_bitstream モジュールが呼ばれる。
書き込み
top.bit ファイルを FDTI を介してFPGAボードに書き込む
$ openFPGALoader -b arty_a7_100t top.bit
感想
Vivado を起動せずに FPGA board に Verilog file を合成して書き込むことができる Open 環境が構築できることは素晴らしい。
まだまだ、沢山の課題があるとしても、コマンドラインだけでFPGA のツールチェーンが動くことはソフトエンジニアには、割ととっつき易いかと感じました。