手动运行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
//A simple cricuit which blinks an LED on and off periodically
module blink(
input clk, //Input clock
input i_reset, //Input active-high reset
output o_led); //Output to LED

//Sequential logic
//
//A reset-able counter which increments each clock cycle
reg[4:0] r_counter;
always @(posedge clk) begin
if (i_reset) begin //When reset is high, clear counter
r_counter <= 5'd0;
end else begin //Otherwise increment counter each clock (note that it will overflow back to zero)
r_counter <= r_counter + 1'b1;
end
end

//Combinational logic
//
//Drives o_led high if count is below a threshold
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
#r_counter implements
.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

# + operator implements
.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]

# < operator implements
.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

See also

For more information on the BLIF file format see BLIF Netlist (.blif).

使用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; #Read the circuit synthesized by ODIN
#if -K 6; #Technology map to 6 input LUTs (6-LUTs)
#write_hie blink.odin.blif blink.abc_no_clock.blif #Write new circuit to 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 *.route
blink.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 *.out

0_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.blifVPR 使用的网表(通常与 相同blink.abc.blif)。

1
2
3
4
> ls *.blif

0_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 *.route

blink.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
水调歌头
苏轼

丙辰中秋,欢饮达旦,大醉,作此篇,兼怀子由。
明月几时有?把酒问青天。
不知天上宫阙,今夕是何年?
我欲乘风归去,又恐琼楼玉宇,高处不胜寒。
起舞弄清影,何似在人间?

转朱阁,低绮户,照无眠。
不应有恨,何事长向别时圆?
人有悲欢离合,月有阴晴圆缺,此事古难全。
但愿人长久,千里共婵娟。