手动运行VTR流程
首先创建工程目录,位置任选,然后需要使用三个工具集来实现整个流程:
Odin II 运行综合 过程,将行为级描述转换为电路网表(.blif),该电路网表由基本逻辑门、除法器以及加法器等单元组成。
ABC 运行逻辑优化 过程,用于简化逻辑电路,然后运行工艺映射 过程,将由基本逻辑门实现的电路转换为由LUT所实现的电路。
VPR 运行打包 、布局 以及布线 过程,将用户电路通过目标FPGA架构实现。
使用ODIN II进行综合
blink.v 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 module blink( input clk, input i_reset, output o_led); reg [4 :0 ] r_counter; always @(posedge clk) begin if (i_reset) begin r_counter <= 5'd0 ; end else begin r_counter <= r_counter + 1'b1 ; end end always @(*) begin if (r_counter < 5'd16 ) begin o_led <= 1'b1 ; end else begin o_led <= 1'b0 ; end end endmodule
首先,我们将在我们的 Verilog 文件上运行 ODIN II 以将其合成为电路网表,并提供以下选项:
-a $VTR_ROOT/vtr_flow/arch/timing/EArch.xml
它指定了我们所针对的 FPGA 架构,
-V $VTR_ROOT/doc/src/quickstart/blink.v
它指定了我们要合成的verilog文件,以及
-o blink.odin.blif
它指定生成的.blif
电路网表的名称。
1 2 3 4 $ VTR_ROOT/ODIN_II/odin_II \ -a $VTR_ROOT /vtr_flow/arch/timing/EArch.xml \ -V $VTR_ROOT /doc/src/quickstart/blink.v \ -o blink.odin.blif
然后可以观看所生成的网表文件,其内容如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 .latch blink^nMUX~0 ^MUX_2~23 blink^r_counter~0_FF re blink^clk 3 .latch blink^nMUX~0 ^MUX_2~27 blink^r_counter~4_FF re blink^clk 3 .subckt adder a[0 ]=blink^r_counter~0_FF b[0 ]=vcc cin[0 ]=blink^ADD~2 -0 [0 ]\ cout[0 ]=blink^ADD~2 -1 [0 ] sumout[0 ]=blink^ADD~2 -1 [1 ] .subckt adder a[0 ]=blink^r_counter~1_FF b[0 ]=gnd cin[0 ]=blink^ADD~2 -1 [0 ]\ cout[0 ]=blink^ADD~2 -2 [0 ] sumout[0 ]=blink^ADD~2 -2 [1 ] .names blink^LT~4 ^GT~10 blink^LT~4 ^GT~12 blink^LT~4 ^GT~14 blink^LT~4 ^GT~16 blink^LT~4 ^GT~18 blink^LT~4 ^lOR~9 1 ---- 1 -1 --- 1 --1 -- 1 ---1 - 1 ----1 1
使用ABC进行优化以及技术映射
ABC提供以下选项:
-c <script>
,其中<script>
是一组命令,用于高速ABC如何处理我们的电路,一个示例如下所示。
1 2 3 4 5 6 $ VTR_ROOT/abc/abc \ -c 'read blink.odin.blif; if -K 6; write_hie blink.odin.blif blink.abc_no_clock.blif' # read blink.odin.blif; # if -K 6; # write_hie blink.odin.blif blink.abc_no_clock.blif
Note
Usually you should use a more complicated script (such as that used by run_vtr_flow ) to ensure ABC optitmizes your circuit well.
如果我们现在检查生成的 BLIF 文件 ( blink.abc_no_clock.blif
),我们会看到 ABC 能够显着简化和优化电路的逻辑(与blink.odin.blif
相比):
blink.abc_no_clock.blif 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 # Benchmark "blink" written by ABC on Tue May 19 15 :42 :50 2020 .model blink .inputs blink^clk blink^i_reset .outputs blink^o_led .latch n19 blink^r_counter~0 _FF 2 .latch n24 blink^r_counter~4 _FF 2 .latch n29 blink^r_counter~3 _FF 2 .latch n34 blink^r_counter~2 _FF 2 .latch n39 blink^r_counter~1 _FF 2 .subckt adder a[0 ]=blink^r_counter~0 _FF b[0 ]=vcc cin[0 ]=blink^ADD~2 -0 [0 ] cout[0 ]=blink^ADD~2 -1 [0 ] sumout[0 ]=blink^ADD~2 -1 [1 ] .subckt adder a[0 ]=blink^r_counter~1 _FF b[0 ]=gnd cin[0 ]=blink^ADD~2 -1 [0 ] cout[0 ]=blink^ADD~2 -2 [0 ] sumout[0 ]=blink^ADD~2 -2 [1 ] .subckt adder a[0 ]=blink^r_counter~2 _FF b[0 ]=gnd cin[0 ]=blink^ADD~2 -2 [0 ] cout[0 ]=blink^ADD~2 -3 [0 ] sumout[0 ]=blink^ADD~2 -3 [1 ] .subckt adder a[0 ]=blink^r_counter~3 _FF b[0 ]=gnd cin[0 ]=blink^ADD~2 -3 [0 ] cout[0 ]=blink^ADD~2 -4 [0 ] sumout[0 ]=blink^ADD~2 -4 [1 ] .subckt adder a[0 ]=blink^r_counter~4 _FF b[0 ]=gnd cin[0 ]=blink^ADD~2 -4 [0 ] cout[0 ]=blink^ADD~2 -5 [0 ] sumout[0 ]=blink^ADD~2 -5 [1 ] .subckt adder a[0 ]=gnd b[0 ]=gnd cin[0 ]=unconn cout[0 ]=blink^ADD~2 -0 [0 ] sumout[0 ]=blink^ADD~2 -0 ~dummy_output~0 ~1 .names blink^i_reset blink^ADD~2 -1 [1 ] n19 01 1 .names blink^i_reset blink^ADD~2 -5 [1 ] n24 01 1 .names blink^i_reset blink^ADD~2 -4 [1 ] n29 01 1 .names blink^i_reset blink^ADD~2 -3 [1 ] n34 01 1 .names blink^i_reset blink^ADD~2 -2 [1 ] n39 01 1 .names vcc 1 .names gnd 0 .names unconn 0 .names blink^r_counter~4 _FF blink^o_led 0 1 .end .model adder .inputs a[0 ] b[0 ] cin[0 ] .outputs cout[0 ] sumout[0 ] .blackbox .end
ABC保留了.latch
和.sunckt adder
基元,但是确实极大的简化了其他逻辑(.names
)。但是经过ABC处理之后的BLIF文件存在一个问题:锁存器(上升沿FF)没有指定任何时钟或者边沿敏感,但是这是VPR所需要的信息。
重新插入时钟
可以通过运行一个脚本来恢复时钟信息,该脚本将从原始 ODIN BLIF 文件中传输该信息(将其写入新文件blink.pre-vpr.blif
):
1 2 3 4 $ VTR_ROOT/vtr_flow/scripts/restore_multiclock_latch.pl \ blink.odin.blif \ blink.abc_no_clock.blif \ blink.pre-vpr.blif
然后再检查blink.pre-vpr.blif
,可以看到时钟 ( blink^clk
) 已恢复到触发器:
1 2 3 4 5 6 7 > grep 'latch' blink.pre-vpr.blif .latch n19 blink^r_counter~0_FF re blink^clk 3 .latch n24 blink^r_counter~4_FF re blink^clk 3 .latch n29 blink^r_counter~3_FF re blink^clk 3 .latch n34 blink^r_counter~2_FF re blink^clk 3 .latch n39 blink^r_counter~1_FF re blink^clk 3
使用VPR实现电路
现在我们已经有了优化和技术映射的网blink.pre-vpr.blif
表(但是,由于我们的 BLIF 文件与我们明确指定的设计名称不匹配。
blink
作为电路名称,和输入电路文件--circuit_file
。以确保生成的.net
,.place
和.route
文件将具有正确的名称。因此执行的命令以及执行的结果为:
1 2 3 4 5 6 7 > $VTR_ROOT /vpr/vpr \ $VTR_ROOT /vtr_flow/arch/timing/EArch.xml \ blink --circuit_file blink.pre-vpr.blif \ --route_chan_width 100 > ls *.net *.place *.routeblink.net blink.place blink.route
然后可以通过添加 --analysis --disp on
来查看布局:
1 2 3 4 5 > $VTR_ROOT /vpr/vpr \ $VTR_ROOT /vtr_flow/arch/timing/EArch.xml \ blink --circuit_file blink.pre-vpr.blif \ --route_chan_width 100 \ --analysis --disp on
Note
如果不使用--analysis
,那么VPR将重新布局布线。并且如果开启了--disp on
,那么就可以看到VPR运行时如何修改布局布线。默认情况下,会再关键阶段停止以使得用户可以观察和探索布局。用户需要按GUI上的Proceed
按钮以使得VPR继续运行。
自动运行VTR流程
手动运行流程的每个阶段非常耗时(并且可能容易出错)。为方便起见,VTR 提供了一个脚本 ( run_vtr_flow ) 来自动执行此过程。
首先,确保已激活在本教程开始时创建的 Python 虚拟环境(我为efpga环境),然后运行以下命令:
1 2 3 4 5 6 7 8 9 10 # 创建工作目录 > mkdir -p ~/vtr_work/quickstart/blink_run_flow> cd ~/vtr_work/quickstart/blink_run_flow# 脚本名称 HDL文件 目标FPGA架构文件 指定运行目录为当前文件夹 FPGA架构的路由宽度为100 > $VTR_ROOT /vtr_flow/scripts/run_vtr_flow.py \ $VTR_ROOT /doc/src/quickstart/blink.v \ $VTR_ROOT /vtr_flow/arch/timing/EArch.xml \ -temp_dir . \ --route_chan_width 100
接下来便可以查看运行的日志文件(script_name.out),其中感兴趣的主要日志文件包括 ODIN 日志文件 ( odin.out
)、ABC 生成的日志文件 (例如abc0.out
) 和 VPR 日志文件 ( vpr.out
)。
1 2 3 4 > ls *.out0_blackboxing_latch.out odin.out report_clocks.abc.out vanilla_restore_clocks.out abc0.out report_clk.out restore_latch0.out vpr.out
Note
如果电路具有多个时钟域,则 ABC 可能会被多次调用,从而产生多个日志文件 ( abc0.out
, abc1.out
, ...)
除此之外,还可以看到生成的BLIF文件,其中感兴趣的主要文件是blink.odin.blif
(ODIN 产生的网表),blink.abc.blif
(时钟恢复后 ABC 产生的最终网表),blink.pre-vpr.blif
VPR 使用的网表(通常与 相同blink.abc.blif
)。
1 2 3 4 > ls *.blif0_blink.abc.blif 0_blink.raw.abc.blif blink.odin.blif 0_blink.odin.blif blink.abc.blif blink.pre-vpr.blif
和之前一样,也可以看到VPR生成的实现文件:
1 2 3 4 5 6 7 8 9 10 11 > ls *.net *.place *.routeblink.net blink.place blink.route # 可视化方式 > $VTR_ROOT /vpr/vpr \ $VTR_ROOT /vtr_flow/arch/timing/EArch.xml \ blink --circuit_file blink.pre-vpr.blif \ --route_chan_width 100 \ --analysis --disp on
水调歌头
苏轼
丙辰中秋,欢饮达旦,大醉,作此篇,兼怀子由。
明月几时有?把酒问青天。
不知天上宫阙,今夕是何年?
我欲乘风归去,又恐琼楼玉宇,高处不胜寒。
起舞弄清影,何似在人间?
转朱阁,低绮户,照无眠。
不应有恨,何事长向别时圆?
人有悲欢离合,月有阴晴圆缺,此事古难全。
但愿人长久,千里共婵娟。