通过Vivado软件进行人体反应测试仪设计,并通过下板实现进行展示。我一开始其实并不是很熟悉整个测试应该如何设计,不会的东西也有很多,比如如何验证,下板实现等等。通过ASIC课程的学习,我学到了很多内容,也掌握了数字设计的流程,有了一个从“0”到“1”,“不会”到“逐渐学会”的过程,令我受益匪浅。
![](https://img-blog.csdnimg.cn/img_convert/b0f7afd4a4df3e28adfe5aa868b87e05.png)
一、设计规划
Top-down设计方法是从系统级的高层次开始,逐步向下分解和细化设计,直到达到最底层的电路实现。它的基本原理是从整体到部分,从确定电路系统的性能指标开始,自系统级、寄存器传输级、逻辑级直到物理级逐级细化并逐级验证其功能和性能,直到可以进行具体的电路设计。这种方法强调对整体结构和功能的把握,然后逐步细化和优化设计,使得设计过程更加有序和可控。
Bottom-up设计方法是从逻辑级开始,采用逻辑单元和少数行为级模块构成层次式模型进行层次设计,从门级开始逐级向上组成RTL级模块,再由若干RTL模块构成电路系统。它的基本原理是从部分到整体,首先设计最基本的电路组件,然后将它们组合成更复杂的组件,再进一步组合成更高层次的组件,直到最终构建出整个系统。这种方法强调先解决底层组件的设计和实现,然后逐步组合和集成,使得设计过程更加模块化和可重用。
![](https://img-blog.csdnimg.cn/img_convert/6816e1c5055770c6349c9f6ff40d5494.png)
两种方法的区别在于设计的起点和思维方式。Top-down方法从系统级的高层次开始设计,将整体功能进行分解和细化,逐步进行设计和优化。Bottom-up方法从最底层的基本组件开始设计,逐步构建更复杂的电路,最终得到系统级的设计。Top-down方法更注重对整体结构和功能的把握,而Bottom-up方法更注重对底层组件的设计和构建。
Bottom-up流程是集成电路和PCB板的传统设计方法,该方法盛行于七、八十年对于集成度在一万门以内的ASIC设计是行之有效的,无法完成十万门以上的设计设计效率低、周期长,一次设计成功率低。
Top-down流程在EDA工具支持下逐步成为IC主要的设计方法,设计从行为到结构再到物理级,每一步部进都进行验证,提高了一次设计的成功率,提高了设计效率,缩短了ASIC的开发周期,降低了产品的开发成本。
不过在实际应用中,常常结合使用这两种方法。例如,可以使用Top-down方法确定整体的系统结构和功能需求,然后使用Bottom-up方法设计和实现底层的组件,最后再使用Top-down方法进行整体的细化和优化。这样可以在保持整体把握的同时,充分利用底层组件的模块化和可重用性,提高设计的效率和可靠性。
本次人体反应测试仪的实现主要基于Top-down方法进行设计,完成对应功能。
1.1 设计要求
使用TOP-DOWN流程设计一个人体反应测试仪,用4个8段LED作为显示,2个按键分别表示启动Start和反应按键React。
Start键表示开始测试,内部计数器开始计数,三个8段LED显示为四条横线“----”,接着随机时间(500ms到5S不等)之后变为四道竖线“||||”,被测试的人看到后快速按反应按键react,测试仪显示从开始变为“||||”到被测试人按反应按键的时间,即为被测试人的反应时间,该反应时间的分辨率为1ms。所以最大可测试时间为9999ms。但是注意,因为人体反应速度极限,如果此时间为负或小于人体最小反应时间(100ms),则测试为Fail(显示FAIL)。
1.2 设计思路
本实验采用TOP-DOWN的设计方法。自上向下的设计方法就是先分析需求,先明确电路需要实现的功能,再将总体功能进行分割,将复杂的问题分解为较为简单、易于实现分块功能,以方便将较为复杂的数字电路分为不同的模块来实现。应先编写顶层模块主体部分,最后在顶层模块中调用各个子模块,将其进行实例化连接各个端口,最终完成设计。相比自下而上的方法,自上而下的设计能够更系统性完成超大规模集成电路的设计。
根据自上而下的设计方法,软件级层面上,本实验首先将功能分为状态机转换模块、随机时间生成模块、反应时间计时器模块(ms级)和八段数码管显示模块等,并分别对其进行了RTL级设计与验证。然后在顶层模块中分别对其进行实例化调用,并对其进行了测试,包括综合、功能仿真与时序仿真。最后使用 FPGA开发板进行了硬件级物理实现。
1.3 TOP-DOWN设计流程图
![](https://img-blog.csdnimg.cn/img_convert/9660d52a108d66addc142377b6bf8192.png)
数字电路的设计流程如图3所示,首先根据功能定义编写RTL级的描述文件,其中包含了HDL代码、测试文件、约束文件以及IP核,之后利用行为级仿真初步验证功能正确性。其次,通过综合工具引入一些标准单元库进行综合,之后利用综合后仿真验证功能和时序的正确性。然后,通过实现完成设计优化和布局布线,进一步利用实现后仿真完成进一步的时序和功能仿真。最后,生成比特流文件用于下板实现。值得注意的是,在综合和实现过程中均涉及对时序、功耗和资源的分析,更有利于设计者掌控电路的各项性能指标,方便设计优化。
1.3.1 设计平台
-
RTL 设计平台:Vs Code v1.89.1(已配置Verilog相关插件)
-
综合:Vivado v2023.2
-
功能验证:Vivado v2023.2
-
时序仿真:Vivado v2023.2
-
目标FPGA:Xilinx公司的ARTIX-7系列的FPGA芯片,型号为XC7A35T-2FGG484I
-
开发板:AX7035B
本次实验采用的是Visual Studio Code v1.89.1(64-bit)版本以及Vivado v2023.2(64-bit)版本进行设计和仿真,对应的板子是正点原子的达·芬奇Xilinx公司ARTIX-7系列的FPGA芯片开发板AX7035B,型号为XC7A35T-2FGG484I,电脑基于WIN64平台,计算机的CPU采用英特尔第11代i7-1165G7,速度为2.80GHz,系统的RAM为16GB.
1.3.2 EDA使用说明
-
在RTL设计阶段,我主要使用了VS Code,在其中加载了关于Verilog的扩展包。VS code具有良好的coding语法检查功能,可以很好地降低代码编写的难度与错误率。
-
在仿真阶段,运用了Vivado自带的软件,它可以通过testbench.v对RTL设计进行仿真和功能验证,并且可以较为方便地在布局布线后进行时序仿真,可以通过观察波形图方便地查找设计中逻辑的缺陷。
-
在物理实现阶段,在Vivado中生成bit文件,连接开发板,通过JTAG导入开发板进行编译实现。
二、设计实现
2.1 框图介绍
2.1.1 实验时间线
![](https://img-blog.csdnimg.cn/img_convert/72aae057829c9f4e187326049a006a6c.png)
2.1.2 设计框图
![](https://img-blog.csdnimg.cn/img_convert/ef24c90fba8ed7be6ade316f71af73ee.png)
首先按下rst_n进行复位,数码管不显示,状态机进入等待start状态(即IDLE);
当按下start给予一段低电平(使得start_flg = 1以启动随机数生成模块以及record_flg = 1以启动随机时间生成模块),数码管显示“ - - - - ”,当random_flg从0变为1表示随机时间生成成功,开始人体反应测试,这时候的数码管将会显示“ | | | | ”,状态机进入等待react状态(即ST1);
随后当按下react给予一段低电平(使得react_flg = 1)表示测试结束,从反应时间计时器中传出fail_flg判断是否测试失败(1为失败,0为成功),随后状态机控制数码管输出对应图像。
![](https://img-blog.csdnimg.cn/img_convert/2c6f3e2cbb510964a4384315d476a618.png)
本次设计的人体反应测试仪系统的主模块是human_react_demo,它包含了各个子模块和接口的实例化。以下是系统中涉及的各个模块和接口的简要描述:
- Human_react_module:
-
该模块用于实现人体反应测试原理顶层逻辑状态机的实现,主要使用了三种状态,分别对应未按start、按下start、按下react三种情况;
- Rand_num_module:
-
该模块用于随机时间(500ms~5000ms)的生成,是基于斐波拉契LFSR(线性反馈移位寄存器)原理实现的伪随机数生成器;
- Count_module:
-
该模块用于记录反应时间,分辨率为毫秒级;
- Smg_disp_module:
-
该模块用于动态控制八段数码管显示器,根据输入的时钟和复位信号,以及输入的显示数值以及状态,控制八段数码管显示器的段选和位选,实现反应时间的显示。
2.2 各个模块设计和验证(仿真激励和结果波形说明)
2.2.1 状态机转换模块(human_react_module.v)
2.2.1.1 状态图:
![](https://img-blog.csdnimg.cn/img_convert/d5a7f2d562384431d127dcd5d09fbdfb.png)
如上图所示,本实验状态机分为3个状态:IDLE(4'b0001)、ST1(4'b0010)、ST2(4'b0100)。
-
处于IDLE状态时,表示人体反应测试仪并未启动,此时数码管应该不显示,随即等待start信号;
-
处于ST1状态时,表示人体反应测试仪正式启动,此时数码管将会开始显示“ - - - - ”,并开始随机时间的生成,一旦随机时间生成完成,数码管将会显示“ | | | | ”,随机等待react信号;
-
处于ST2状态时,表示反应时间计时器开始启动,若测试者反应时间符合要求,则八段数码管显示反应时间,否则显示FAIL。
注意事项:经思考以及民间高手的讨论,本实验可以使用3~5个状态实现,而根据个人喜好,我倾向于三状态机的实现,感觉顶层逻辑更简洁明。
2.2.1.2 具体代码:
module human_react_module (
input start,
input react,
input sys_clk,
input rst_n,
input [15:0] disp_time,
input random_flg, // 随机时间结束后变 1
input fail_flg, // 未按规定按 react 后变 1
output reg start_flg = 0, // 检测到 start 变 1
output reg record_flg1 = 0,
output reg react_flg = 0, // 检测到 react 变 1
output reg record_flg2 = 0,
//数码管
output reg [15:0] disp_data,
output reg [ 3:0] disp_state,
output [3:0] Led //与LED灯引脚在一起,方便显示状态以调试
);
// 检测 start 和 react 按键信号的下降沿
always @(posedge sys_clk or negedge rst_n) begin
if (!rst_n) begin
start_flg <= 0;
react_flg <= 0;
end else begin
if (!start) start_flg <= 1;
else start_flg <= start_flg;
if (!react && start_flg == 1) react_flg <= 1;
else react_flg <= react_flg;
end
end
// 状态定义 对于数码管而言:
parameter IDLE = 4'b0001; // IDLE 显示数字
parameter ST1 = 4'b0010; // ST1 显示----,
parameter ST2 = 4'b0100; // ST2 显示1111,
parameter ST3 = 4'b1000; // ST3 显示FAIL
// 对于反应模型而言:
// IDLE 代表未按 start ,数码管不显示
// ST1 代表按下 start , 数码管立即显示 ---- ,启动 rand_num 模块产生随机时间后数码管显示 1111 ,随即开始启动 count 模块计时反应时间
// ST2 代表按下 react ,数码管显示 FAIL 或者反应时间
// PS : 1. 在未按下 start 时,按 react 不会有任何反应,此时再按 start 仍然可以正常运行
// 2. 超时仍旧未按下 react ,此时并不会显示 FAIL ,只有按下 react 才会显示FAIL
// 三段式状态机模型
reg [3:0] curr_state;
reg [3:0] next_state;
// 描述状态转移(时序逻辑)
always @(posedge sys_clk or negedge rst_n) begin
if (!rst_n) curr_state <= IDLE;
else curr_state <= next_state;
end
// 链接LED灯显示当前状态
assign Led = curr_state;
// 描述状态转移的条件和规律(组合逻辑)
always @(posedge sys_clk or negedge rst_n) begin
if (!rst_n) begin
next_state <= IDLE;
end else begin
case (curr_state)
IDLE: begin
if (start_flg == 1 && react_flg == 0) begin
next_state <= ST1;
end else begin
next_state <= IDLE;
end
end
ST1: begin
if (react_flg == 1) begin
next_state <= ST2;
end else begin
next_state <= ST1;
end
end
ST2: begin
next_state <= ST2;
end
default: next_state = next_state;
endcase
end
end
// 描述状态输出(组合/时序逻辑)
always @(posedge sys_clk or negedge rst_n) begin
if (!rst_n) begin
record_flg1 <= 0;
record_flg2 <= 0;
disp_data <= 16'hffff;
disp_state <= ST1;
end else begin
case (curr_state)
IDLE: begin
record_flg1 <= 0;
record_flg2 <= 0;
disp_data <= 16'hffff;
disp_state <= ST1;
end
ST1: begin
if (random_flg == 0) begin
record_flg1 <= 1;
record_flg2 <= 0;
disp_data <= 16'haaaa;
disp_state <= ST1;
end else if (random_flg == 1) begin
record_flg1 <= 0;
record_flg2 <= 1;
disp_data <= 16'h1111;
disp_state <= ST2;
end
end
ST2: begin
record_flg1 <= 0;
record_flg2 <= 0;
if (fail_flg == 1) begin
disp_data <= 16'hdb1c;
disp_state <= ST3;
end else if (fail_flg == 0) begin
disp_data <= disp_time;
disp_state <= IDLE;
end
end
endcase
end
end
endmodule
2.2.1.3 仿真验证:
对比下面两种结果,我们只需关注react、disp_time[15:0]与最后四组变量值,在实际运行时是不会出现第一种情况的,原因是仿真时disp_time[15:0]是自己直接赋值的,在实际程序中它应该在react = 0后紧接的一个系统时钟上升沿立刻改变,正如第二种情况的发生,而第一种情况很显然是在react = 0后的很多个系统时钟上升沿才开始改变,所以才会导致disp_data[15:0]即数码管显示的数据多出现一个0000状态。
![](https://img-blog.csdnimg.cn/img_convert/f4f418fff6fc215b0bac3f661bbc8a77.png)
![](https://img-blog.csdnimg.cn/img_convert/273942b06506f379068cafbd24cbe9b8.png)
2.2.2 (伪)随机时间生成模块(rand_num_module.v)
2.2.2.1 本模块第一部分:
2.2.2.2 本模块第二部分:
2.2.2.3 具体代码:
2.2.2.4 仿真验证:
详情请阅:
2.2.3 反应时间计时器模块(ms级)(count_module.v)
2.2.3.1 模块描述:
此处的计数器,需要判断是否随机时间结束(record_flg2 = 1 表示结束)然后开始计时,以及需要通过react_flg知道是否按下react按键,如果在计时开始前(record_flg2 = 0)或者100ms内按下,则显示FAIL;超出9999ms再按下也显示FAIL;只有开始计时,并且计时时间符合范围,才能在react_flg = 1即按下react按键时输出反应时间。
注意事项:关于超时有两种写法,第一种是一检测到超时立即显示FAIL;另一种是检测到超时并且检测到按下react按键才会显示FAIL;以下代码采取的是第二种,因为顶层逻辑状态机是三种状态,只能采用第二种写法,如果采用第一种写法,则需要再添加一个状态,此处不做展示。
2.2.3.2 具体代码:
// 记录 1111 到 react_time 的状态
module count_module (
input sys_clk,
input rst_n,
input react_flg,
input record_flg2,
output reg [15:0] disp_data,
output reg fail_flg
);
// 计时模块 ms 级
parameter T1MS = 16'd49999; //1ms count
reg [15:0] time_cnt; //1ms计数器
// 1ms计数器逻辑
always @(posedge sys_clk or negedge rst_n)
if (!rst_n) time_cnt <= 16'd0;
else if (time_cnt == T1MS) time_cnt <= 16'd0;
else time_cnt <= time_cnt + 1'b1;
// 反应时间计时器 ms 级,即记录 1111 到按下 react 的时间(单位 ms )
parameter T100MS = 16'd100; // 最小的正常人类反应时间
parameter T9999MS = 16'd9999; // 最大的正常人类反应时间
reg [15:0] react_time; // 反应时间 ms 级
always @(posedge sys_clk or negedge rst_n) begin
if (!rst_n) react_time <= 0;
else if (time_cnt == T1MS) begin
if (record_flg2 == 1) react_time <= react_time + 1;
else react_time <= react_time;
end
end
// react_time 传递给数码管显示数据 disp_data
parameter FAIL = 16'hdb1c;
always @(posedge sys_clk or negedge rst_n)
if (!rst_n) fail_flg <= 0;
else begin
// 收到反应信号且计数值在正常范围内(100ms到9999ms)
if (react_flg == 1 && react_time > T100MS && react_time < T9999MS) begin
fail_flg <= 0; // 失败标志为零
disp_data <= react_time; // 保存当前计数值
end
// 收到反应信号但计数值太小(小于100ms)或计数值过大(超过9999ms)
else if ((react_flg == 1 && react_time < T100MS) || react_time > T9999MS) begin
fail_flg <= 1; // 设置失败标志
disp_data <= 16'b0; // 重置反应时间计数值
end else begin
fail_flg <= 0; // 失败标志清零
disp_data <= 16'b0; // 重置反应时间计数值
end
end
endmodule
2.2.3.3 仿真验证:
如下图,验证复位信号,功能正确。
![](https://i-blog.csdnimg.cn/direct/861f0ebb751c4ed3868c74a86feb321f.png)
如下图,验证反应时间计数时序逻辑,功能正确。可以看出当按下 react 按键时,将会进入状态机的ST2状态,此时record_flg2将立即变成0,意味着反应时间计数完毕,可以传递赋值给disp_data用于数码管显示反应时间。
![](https://i-blog.csdnimg.cn/direct/402d41695e354ddcb10c40e5b792c06b.png)
2.2.4 八段数码管显示模块(smg_disp_module.v)
2.2.4.1 模块描述:
2.2.4.2 为什么八段数码管需要动态扫描
2.2.4.3 具体代码:
2.2.4.4 仿真验证:
详情请阅:
2.2.5 顶层模型(human_react_demo.v)
2.2.5.1 模块描述:
在各模块RTL完成并验证通过后,顶层模块进行连接各个子模块,所有子模块中的输入输出变量都需要在顶层模块中声明。同时,连接多个子模块的变量需要是wire类型,而不能是reg类型。
2.2.5.2 原理图
![](https://img-blog.csdnimg.cn/img_convert/c5a644815e4d8f5a083cac4a00d147d6.png)
2.2.5.3 具体代码:
module top (
input sys_clk,
input rst_n,
input start,
input react,
output [7:0] Disp_Seg,
output [5:0] Scan_Sig,
output [3:0] Led
);
// from U1 to U2
wire start_flg;
wire record_flg1;
// from U1 to U3
wire react_flg;
wire record_flg2;
// from U1 to U4
wire [15:0] disp_data;
wire [ 3:0] disp_state;
// from U2 to U1
wire random_flg;
// from U3 to U1
wire fail_flg;
wire [15:0] cnt_out;
human_react_module U1 (
.start (start), //from top
.react (react), //from top
.sys_clk(sys_clk),
.rst_n (rst_n),
.disp_time(cnt_out), //from U3
.fail_flg (fail_flg), //from U3
.random_flg(random_flg), //from U2
.start_flg (start_flg), //to U2
.record_flg1(record_flg1), //to U2
.react_flg (react_flg), //to U3
.record_flg2(record_flg2), //to U3
.disp_data (disp_data), //to U4
.disp_state(disp_state), //to U4
.Led(Led) //to top
);
rand_num_module U2 (
.sys_clk (sys_clk),
.rst_n (rst_n),
.start_flg (start_flg), //from U1
.record_flg1(record_flg1), //from U1
.random_flg(random_flg) //to U1
);
count_module U3 (
.sys_clk (sys_clk),
.rst_n (rst_n),
.react_flg (react_flg), //from U1
.record_flg2(record_flg2), //from U1
.disp_data(cnt_out), //to U1
.fail_flg (fail_flg) //to U1
);
smg_disp_module U4 (
.sys_clk (sys_clk),
.rst_n (rst_n),
.disp_data (disp_data), //from U1
.disp_state(disp_state), //from U1
.Disp_Seg(Disp_Seg), //to top
.Scan_Sig(Scan_Sig) //to top
);
endmodule
2.2.5.4 约束条件:
############## NET - IOSTANDARD ######################
set_property CFGBVS VCCO [current_design]
set_property CONFIG_VOLTAGE 3.3 [current_design]
#############SPI Configurate Setting##################
set_property BITSTREAM.CONFIG.SPI_BUSWIDTH 4 [current_design]
set_property CONFIG_MODE SPIx4 [current_design]
set_property BITSTREAM.CONFIG.CONFIGRATE 50 [current_design]
############## clock and reset define##################
create_clock -period 20.000 [get_ports sys_clk]
set_property IOSTANDARD LVCMOS33 [get_ports sys_clk]
set_property PACKAGE_PIN Y18 [get_ports sys_clk]
set_property IOSTANDARD LVCMOS33 [get_ports rst_n]
set_property PACKAGE_PIN F20 [get_ports rst_n]
#######################digital tube setting#############
set_property PACKAGE_PIN J5 [get_ports {Disp_Seg[0]}]
set_property PACKAGE_PIN M3 [get_ports {Disp_Seg[1]}]
set_property PACKAGE_PIN J6 [get_ports {Disp_Seg[2]}]
set_property PACKAGE_PIN H5 [get_ports {Disp_Seg[3]}]
set_property PACKAGE_PIN G4 [get_ports {Disp_Seg[4]}]
set_property PACKAGE_PIN K6 [get_ports {Disp_Seg[5]}]
set_property PACKAGE_PIN K3 [get_ports {Disp_Seg[6]}]
set_property PACKAGE_PIN H4 [get_ports {Disp_Seg[7]}]
set_property PACKAGE_PIN M2 [get_ports {Scan_Sig[0]}]
set_property PACKAGE_PIN N4 [get_ports {Scan_Sig[1]}]
set_property PACKAGE_PIN L5 [get_ports {Scan_Sig[2]}]
set_property PACKAGE_PIN L4 [get_ports {Scan_Sig[3]}]
set_property PACKAGE_PIN M16 [get_ports {Scan_Sig[4]}]
set_property PACKAGE_PIN M17 [get_ports {Scan_Sig[5]}]
set_property PACKAGE_PIN F19 [get_ports {Led[0]}]
set_property PACKAGE_PIN E21 [get_ports {Led[1]}]
set_property PACKAGE_PIN D20 [get_ports {Led[2]}]
set_property PACKAGE_PIN C20 [get_ports {Led[3]}]
set_property IOSTANDARD LVCMOS33 [get_ports {Disp_Seg[*]}]
set_property IOSTANDARD LVCMOS33 [get_ports {Scan_Sig[*]}]
set_property IOSTANDARD LVCMOS33 [get_ports {Led[*]}]
set_property PACKAGE_PIN M13 [get_ports start]
set_property PACKAGE_PIN K14 [get_ports react]
set_property IOSTANDARD LVCMOS33 [get_ports react]
set_property IOSTANDARD LVCMOS33 [get_ports start]
set_property DRIVE 12 [get_ports {Disp_Seg[7]}]
set_property DRIVE 12 [get_ports {Disp_Seg[6]}]
set_property DRIVE 12 [get_ports {Disp_Seg[5]}]
set_property DRIVE 12 [get_ports {Disp_Seg[4]}]
set_property DRIVE 12 [get_ports {Disp_Seg[3]}]
set_property DRIVE 12 [get_ports {Disp_Seg[2]}]
set_property DRIVE 12 [get_ports {Disp_Seg[1]}]
set_property DRIVE 12 [get_ports {Disp_Seg[0]}]
2.3 综合与实现(Synthesis & Implement)
2.3.1 综合与实现的原理和步骤
综合(Synthesis)和实现(Implement)是数字电路设计流程中的两个重要步骤。下面是它们的原理和具体步骤的详细说明:
(1)综合(Synthesis)的原理和步骤:
-
原理:
-
综合是将高级描述语言(如Verilog或VHDL)表示的设计转换为低级综合库元素(门级、触发器等)的过程。综合工具会根据设计描述中的逻辑功能和约束条件,生成等效的电路结构。
-
步骤:
-
编写设计描述:使用高级描述语言(如Verilog或VHDL)编写设计的功能描述和约束条件。
-
设置综合工具选项:配置综合工具的选项,包括综合库文件、目标技术库、时钟频率等。
-
运行综合工具:运行综合工具,将设计描述作为输入。
-
综合优化:综合工具会对设计进行优化,包括逻辑优化、时序优化和面积优化等。优化的目标是提高电路的性能、减少功耗和面积。
-
时序分析和约束:综合工具会进行时序分析,并根据约束条件生成时序报告,包括时序路径、时钟限制和时序违规等信息。
-
生成综合网表:综合工具将优化后的设计转换为综合网表,其中包含了门级电路结构和逻辑功能。
-
验证综合结果:对生成的综合网表进行功能仿真和时序仿真,验证综合后的电路与设计描述的一致性。
(2)实现(Implement)的原理和步骤:
-
原理:
-
实现是将综合后的网表映射到目标技术库中的物理元件(如门、触发器等)的过程。实现工具会根据目标技术库和约束条件,生成布局(Layout)和路由(Routing)信息。
-
步骤:
-
设置实现工具选项:配置实现工具的选项,包括目标技术库、约束条件、物理布局规则等。
-
运行布局工具:运行布局工具,生成初始布局。布局工具会根据约束条件和物理布局规则,将综合网表映射到目标技术库中的物理元件,并进行逻辑区域划分和布局。
-
进行布局优化:布局工具会对初始布局进行优化,以满足约束条件和性能要求。优化的目标包括减少布线长度、降低时延和功耗等。
-
进行布线:运行布线工具,将布局后的电路连接起来,并生成完整的路由信息。布线工具会根据约束条件和布局结果,生成有效的连线路径,并解决时序和布局冲突。
-
进行时序分析和优化:运行时序分析工具,对布线后的电路进行时序分析,并根据时序约束进行优化。优化的目标是满足时序要求,包括最小化时延、最大化工作频率等。
-
生成最终布局和路由:最终布局和路由生成后,形成了实现后的电路结构。布局包括物理元件的位置和布线规则,而路由包括物理元件之间的连线路径。
-
进行后仿真和验证:对生成的布局和路由进行后仿真和验证,验证实现后的电路与设计描述的一致性,并进行性能评估。
综合和实现是数字电路设计流程中的关键步骤,通过综合将高级描述语言转换为低级综合库元素,再通过实现将综合网表映射到目标技术库中的物理元件,最终得到可制造的电路布局和路由。这样的流程能够确保设计的功能正确性和时序可靠性,并满足面积、功耗和性能等要求。
2.3.2 功能验证:
如下图,在综合Synthesis design成功后,我们可以进入功能仿真;在布局布线Implement design成功后,我们可以进入时序仿真。
![](https://img-blog.csdnimg.cn/img_convert/46c2156a38bdeb8317994a36ac7a79c3.png)
2.3.2.1 测试成功:
如下图,这是全过程波形图,功能正确。
先看Disp_Seg由bf(即显示 - - - - )转变成f9(即显示 | | | | )再转变成反应具体时间(90 - 99 - 80 -c0即9 - 4 - 8 - 0)实际上就是4'd0849等价于4'h0351,与波形图中disp_time数据相同;
再看波形图中蓝色标签时间差距即人眼看到数码管显示 | | | | 到按下react按键的时间,具体大致为1900 - 1051 = 849ms,也刚好等于上面所述,说明整体上人体反应测试仪模型制作成功。
![](https://img-blog.csdnimg.cn/img_convert/c83549882de8fd47a548fe9782c12dfe.png)
![](https://img-blog.csdnimg.cn/img_convert/3c96a617747d28dcdbe32aa9fd01542f.png)
再看第一处状态转换交界处,random_flg = 1说明随机时间生成成功,状态机此时立刻进入ST2状态等待 react 信号的到来,并且实现record_flg1 = 0 & record_flg2 = 1的转化以及反应时间(react_time)的计时。
![](https://img-blog.csdnimg.cn/img_convert/f7d848df0e19faae3d8e276050861433.png)
最后看第二处状态转换交界处,当按下react按键时,反应时间数据disp_time比record_flg2先改变,这也帮助我印证了状态机转换模块仿真所思所想。
![](https://img-blog.csdnimg.cn/img_convert/3cfad99874b70b75e32da02a4f0d203f.png)
2.3.2.2 测试失败:
如下图,这是一个在数码管显示“ | | | | ”之前就按下react按键的失败实例化,很显然随机时间4'h11a6等价于4'd4518,即至少等待4518ms,数码管才会显示“ | | | | ”,然而我们在800ms左右就开始按下react按键了,所以会显示c7 - f9 - 88 - 8e即L - I - A -F,实际上就是FAIL的显化。
![](https://img-blog.csdnimg.cn/img_convert/24647b6ca0ecd71fe90940371259baea.png)
2.3.3 时序验证:
2.3.3.1 波形分析:
在完成implement design流程也就是布局布线之后,我们进行时序仿真。
以下展示时序仿真结果,可以从波形看出,时序仿真中,功能基本正确,从时钟上升沿到输出变化的延时普遍在10ns级别,意味着时序仿真中会出现一定时间的不确定抖动,是电路中冒险竞争的结果,但是不影响整体测试。此处波形解释与前面功能验证时大致相同,不再赘述。
![](https://img-blog.csdnimg.cn/img_convert/6e3b283327f5a2ca1f3eddafca99d485.png)
![](https://img-blog.csdnimg.cn/img_convert/666ae66801d49ce3f4ad33a60755eb73.png)
![](https://img-blog.csdnimg.cn/img_convert/e28ec142f780f2f30f0c9bd98be11aa6.png)
2.3.3.2 时序分析:
2.3.3.2.1 Design timing analysis
![](https://img-blog.csdnimg.cn/img_convert/da7a3120bdd221f6c948912509140217.png)
![](https://img-blog.csdnimg.cn/img_convert/ea5046b3e1dbd242ca0c099c519111b5.png)
从设计时序分析中可以看到综合与实现过程WNS最差负时序裕量 (Worst Negative Slack)分别为7.978ns/6.348ns,WHS最差保持时序裕量 (Worst Hold Slack)分别为0.130/0.129ns,这些参数都保持正值,没有问题。从报告最后一句话也可以看出所有时序约束都是满足的。
2.3.3.2.2 Clock analysis
![](https://img-blog.csdnimg.cn/img_convert/1c8f990314020791c0089b5d9949670c.png)
从时钟分析中可以看到,系统时钟可以达到周期20ns,频率50MHz的级别,与实际板子的时钟晶振频率相匹配。
2.3.3.2.3 Max Delay Path(关键路径延时)
在时序分析的报告中,Vivado挑选了若干条较长的路径进行分析,本文挑选了最长的一条路径延时报告如下所示,也就是电路的关键路径:
![](https://img-blog.csdnimg.cn/img_convert/712021004f0d767cf818028da28ad4c0.png)
可以看到,最长路径来自八段数码管显示模块smg_disp_module的内部电路,延时为13.632ns,时钟偏移为0.015ns,时钟不确定量为0.035ns,整个系统的抖动为0.071ns.
2.3.3.2.4 Min Delay Path
![](https://img-blog.csdnimg.cn/img_convert/d021860a12ffa13bba277294c5eb0f97.png)
可以看到,最短的路径存在于状态机转换模块human_react_module,延时为0.239ns,时钟偏移为0.016ns.
2.3.3.3 Vivado中的资源分析:
2.3.3.3.1 综合过程的资源利用率
![](https://img-blog.csdnimg.cn/img_convert/fe1b2379111e7c66a8c06c898f6da527.png)
![](https://img-blog.csdnimg.cn/img_convert/f6aab0c4072c47f495bcced8a2ea34f5.png)
![](https://i-blog.csdnimg.cn/direct/7c086c45320a4780bd1d2bfb7884e6dd.png)
可以看到,在综合Synthesis过后的资源利用率中,用于选择器的利用率最高,为8.80%,用于逻辑功能块的利用率为1.68%排名第二,用于Flip Flop的register利用率最低,为0.43%。
2.3.3.3.2 实现(布局布线)过程的资源利用率
![](https://i-blog.csdnimg.cn/direct/fbbe6f2a497c4230938af4977d21983a.png)
![](https://i-blog.csdnimg.cn/direct/7544f9b21eb94259a721732314087afe.png)
![](https://i-blog.csdnimg.cn/direct/811b21fa07a9465d8e81aef59d9a5049.png)
可以看到,在实现implementation(布局布线)过后的资源利用率LUT的资源利用率略微下降了0.03%,这是由于在实际的布局布线的过程中,U1(human_react_module Slice Luts 减少了2)与U4(smg_disp_module Slice Luts 减少了3)模块可能与实际的物理结构有关,不需要更多的资源来实现整个人体反应时间测试。
2.3.3.4 Vivado中的功耗分析:
2.3.3.4.1 整体功耗统计
综合过程与实现(布局布线)过程的整体功耗统计对比分析
![](https://i-blog.csdnimg.cn/direct/9581e5e97bfb4138aad35b9c9b6c0190.png)
![](https://i-blog.csdnimg.cn/direct/11164709b79f40e3b1e68aa0dc623236.png)
![](https://i-blog.csdnimg.cn/direct/3a2d828095644139be23b496c88aab2f.png)
![](https://i-blog.csdnimg.cn/direct/520dc02aa83b49f8b0ab2898d9f58d6c.png)
可以看到Synthesis综合过程总功耗为0.079W,Junction温度为25.0℃,处于正常范围。热工裕量Thermal Margin为59.8℃(21.2W),裕度较好。
Implement实现过程总功耗比上述低0.001W,Junction温度却高0.2℃,也处于正常范围。热工裕量Thermal Margin与上述相同,裕度相对更好。
2.3.3.4.2 各模块功耗统计
综合过程与实现(布局布线)过程的各模块功耗统计对比分析
可以看到各个模块的功耗统计而言:
![](https://i-blog.csdnimg.cn/direct/fe21cc58dcdd4f26a72b59c3aabd1efc.png)
Synthesis综合过程动态功耗为0.08W,其中设备静态功耗最大,为0.072W,其次是IO模块的功耗为0.003W,其他方面功耗为0.001W左右。
![](https://i-blog.csdnimg.cn/direct/6a27ed541f084a12ba5aa769d3a03afe.png)
Implement实现过程动态功耗比上述低0.01W,其中设备静态功耗最大,与上述相同,其次是IO模块的功耗也与上述相同,其他方面功耗与上述相差无几。
可以看到各个模块的功耗统计而言:
![](https://i-blog.csdnimg.cn/direct/8d7fb24def774e94870c31b5d68e44f9.png)
Synthesis综合过程U1(human_react_module)模块的功耗为0.02W,其中U4(smg_disp_module)与U2(rand_num_module)模块的功耗均为0.001W,U3(count_module)模块的功耗最小,低于0.001W。
![](https://i-blog.csdnimg.cn/direct/8be9b4c9cec7487a9d63ebc1bdf7de4d.png)
Implement实现过程U1(human_react_module)模块经过布局布线后功耗相比上述下降0.001W,其中U4(smg_disp_module)与U2(rand_num_module)模块的功耗均为0.001W,U3(count_module)模块的功耗最小,低于0.001W。
2.3.4 物理实现:
上板试验成功部分展示:
![](https://i-blog.csdnimg.cn/direct/dd1e8153ccc2413194ad8a66d8738148.png)
具体视频在设计资料附件已附上,此处是其上传至B站的链接:
2.4 编译工具
Visual Studio Code v1.89.1
Vivado v2023.2
三、设计总结
3.1 注意事项与编程技巧
-
Vs code是一个不可多得的极好的代码编辑软件,我们可以在其上面首先配置好Verilog语言环境,从而可以实现实时的代码拼写与语法检查,降低了代码编写难度,但是偶尔还是会写错,这就需要我们胆大心细地认真规范化编写;
-
在描写状态机的代码时,比如本次实验中的状态机转换模块以及八段数码管显示模块,严格按照三段式代码编写规范可以很大程度提高代码的可读性,包括实时模块化跟进注释,即有助于自己一周速成Verilog语言,也有助于民间高手帮忙看代码答疑解惑;
-
在开始写代码前结合TOP-DOWN自上而下的逻辑思维,从系统层面先想好应该写几个模块,到底是哪几个模块,比如本例中实际上也可以先实现系统时钟分频模块,直接将20ns FPGA本征时钟周期转化成1ms本地所需分辨率,当然也可以在具体模块中需要用1ms周期再临时编写,两种方法都不复杂,可以根据个人喜爱自行选择。
-
使用IP核:Vivado提供了许多现成的IP核,可以快速实现常见的功能模块,如时钟管理、存储器控制器、高级接口等。使用IP核可以加速设计过程并提高可重用性。
-
适当划分设计模块:将设计模块划分为适当的层次和功能单元可以提高设计的模块化和可维护性。使用设计层次结构可以简化布局和布线,减少时延和资源使用。
-
使用参数化:Verilog允许使用参数化来创建可配置的模块。通过使用参数化,可以轻松地修改和重新使用模块,以适应不同的设计要求。
-
写完一个版本的之后一定要备份一下,之后在改进的时候就不会因为修改之后找不到之前已经做好的完整版本。
-
很重要的一点!自学这些知识时一定要有质疑意识,要有自己的想法,哪怕有难免有点不太正常,也要去一点一点将自己的写法改入例程代码里验证是否正确,只有这样自己才能对代码以及FPGA的理解越来越透彻。
-
当然还有额外的一个重点!在利用Verilog进行编程的时候,不应该只想着逻辑是否正确,因为每一块代码都会映射成为一个硬件结构,所以在写代码的时候还应当想一下这个代码的硬件结构是否节省面积,延时是不是更小等等,这样对自己的提升很大。
3.2 体会心得
我一开始其实并不是很熟悉整个测试应该如何设计,不会的东西也有很多,比如如何验证,下板实现等等。通过专用集成电路设计方法这门课,我学到了很多内容,也掌握了数字设计的流程,有了一个从“0”到“1”,“不会”到“逐渐学会”的过程,令我受益匪浅。同时,在编程的过程中,也让我养成了良好的编程习惯,这对今后的科研和工作来说也是十分重要的。此外,我也深刻了解了Top-down的设计模式的具体内涵,这种设计方法其实也可以融会贯通到生活和其他的学习任务中,在做一件事情的时候,也是需要从顶层开始考虑如何规划,分成每一个部分,之后在每一个部分中再去细分各个细节。小到做一件事情,大到公司管理,团队合作,都需要用到Top-down的设计思想。
本次实验存在的最大挑战首先是对FPGA及其编译软件的使用不熟练,尤其是之前我从来没有接触过FPGA开发板,更别提完整地在FPGA开发板上实现过一个测试或者模型。所以本次先是在自己购买的FPGA 开发板教程学习最简单的Vivado使用流程、流水灯实验、按键控制实验、数码管计时器实验等,随后又自己按照教程实现了仿真,以及实例化开发板,才对软件摸索的大差不差。所幸在于一个礼拜内便学会了简单的Verilog以及对模块进行的实例化测试。
在TOP-DOWN设计流程下,一开始就需要对总体设计方案、各个功能模块设计方案,各模块间怎么联系有所把握,不然在写Verilog时就会很痛苦,不知道如何下手。然后,在写Verilog的时候,因为在同步时序电路里面,都是并行的模块,尤其是在状态机中,需要对每个状态转换的约束条件明确清楚,避免出现冲突和意外情况。其次,Verilog综合时对于语法规范性的要求较高,需要及时处理error,同时warning中哪些问题不能忽略,哪些能接受也需要及时处理,以免对后续过程造成阻碍。在综合完成后,需要仿真进行检测功能,可以跟着代码流程找到可能的功能错误。
此外,仅仅进行仿真,不烧录到FPGA开发板进行测试,始终会有空中楼阁之感。很高兴京东快递及时送来了我的开发板,并让我在一周内成功上板成功完成整个实验。
3.3 总结
本设计在TOP-DOWN的设计流程下,完成了一个简易人体反应测试仪设计,两计数器与状态机均正常运行,可以实现多个状态之间的正常切换,并且通过译码显示模块对于不同情况的输出正确显示。
本次实验仍有许多值得改进的地方:
-
比如LFSR每一次的初始种子是设计好的,因此每一次开始游戏的时候,产生的随机数序列是一样的,在此基础上还可以对随机数的生成进行改进,采用其他更为高级的算法,让伪随机数更加逼近真实的随机数。
-
最后,在布局布线后的仿真中可以发现,leds有关的电路会发生竞争和冒险,导致有一段时间内leds信号在不停地变化抖动,可以通过进一步的电路改进来消除中间的竞争冒险状态。
本次实验熟悉了数字电路设计的具体流程,熟练了verilog的代码编写,例如不同的always块不能同时驱动同一个变量,理解时序分析的具体原理,为今后的学习打下了坚实的基础。