1.35k likes | 1.86k Vues
第五章 仿真验证与 Testbench 编写. 5.1 Verilog HDL 电路仿真和验证概述. 仿真,也叫模拟,是通过使用 EDA 仿真工具,通过输入测试信号,比对输出信号(波形、文本或者 VCD 文件)和期望值,来确认是否得到与期望所一致的正确的设计结果,验证设计的正确性。 验证是一个证明设计思路如何实现,保证设计在功能上正确的一个过程。 验证在 Verilog HDL 设计的整个流程中分为 4 个阶段: 阶段 1: 功能验证 ; 阶段 2: 综合后验证 ; 阶段 3: 时序验证 ; 阶段 4: 板级验证。.
E N D
第五章仿真验证与Testbench编写 Microelectronics School Xidian University
5.1 Verilog HDL电路仿真和验证概述 仿真,也叫模拟,是通过使用EDA仿真工具,通过输入测试信号,比对输出信号(波形、文本或者VCD文件)和期望值,来确认是否得到与期望所一致的正确的设计结果,验证设计的正确性。 验证是一个证明设计思路如何实现,保证设计在功能上正确的一个过程。 验证在Verilog HDL设计的整个流程中分为4个阶段: • 阶段1: 功能验证; • 阶段2: 综合后验证; • 阶段3: 时序验证; • 阶段4: 板级验证。 Microelectronics School Xidian University
5.2 Verilog HDL测试程序设计基础 • 5.2.1 Testbench及其结构 在仿真的时候Testbench用来产生测试激励给待验证设计(Design Under Verification,DUV),或者称为待测设计(Design Under Test,DUT)。 Testbench平台结构
测试程序的一般结构 由于Testbench是一个测试平台,信号集成在模块内部,没有输入输出。 在Testbench模块内,例化待测设计的顶层模块,并把测试行为的代码封装在内,直接对待测系统提供测试激励。
例5.2-1 T触发器测试程序示例 module Tflipflop_tb; //数据类型声明 reg clk, rst_n,T; wire data_out; TFF U1 (.data_out(data_out),.T(T),.clk(clk),.rst_n(rst_n)); / /对被测模块实例化 always //产生测试激励 #5 clk=~clk; Initial begin clk=0; #3 rst_n=0; #5 rst_n=1; T=1; #30 T=0; #20 T=1; end Initial //对输出响应进行收集 begin $monitor($time, "T= %b, clk= %b, rst_n= %b, data_out= %b", T, clk, rst_n, data_out); end endmodule
T触发器的仿真波形和部分文本输出结果: 部分文本输出结果: 0T= x, clk= 0, rst_n= x, data_out= x 3T= x, clk= 0, rst_n= 0, data_out= 0 5T= x, clk= 1, rst_n= 0, data_out= 0 8T= 1, clk= 1, rst_n= 1, data_out= 1 10T= 1, clk= 0, rst_n= 1, data_out= 1
从图中可以清晰地看出Testbench的主要功能: (1)为DUT提供激励信号。 (2)正确实例化DUT。 (3)将仿真数据显示在终端或者存为文件,也可以显示在波形窗口中以供分析检查。 (4)复杂设计可以使用EDA工具,或者通过用户接口自动比较仿真结果与理想值,实现结果的自动检查。
在编写Testbench时需要注意的问题: (1)testbench代码不需要可综合 Testbench代码只是硬件行为描述不是硬件设计 。 (2)行为级描述效率高 Verilog HDL语言具备5个描述层次,分别为开关级、门级、RTL级、算法级和系统级。 (3)掌握结构化、程式化的描述方式 结构化的描述有利于设计维护,可通过initial、always以及assign语句将不同的测试激励划分开来。 一般不要将所有的测试都放在一个语句块中。
5.2.2测试平台举例 测试平台需要产生时钟信号、复位信号和一系列的仿真向量,观察DUT的响应,确认仿真结果。 DUT的仿真平台
module adder1(a,b,ci,so,co); input a,b,ci; output so,co; assign{co,so}=a+b+ci; endmodule 根据全加器的真值表(表5.2-1)编写的全加器测试程序如下: module adder1_tb; wire so,co; reg a,b,ci; adder1 U1(a,b,ci,so,co); //模块例化 initial // 测试信号产生 begin a=0;b=0;ci=0; #20 a=0;b=0;ci=1; #20 a=0;b=1;ci=0; #20 a=0;b=1;ci=1; #20 a=1;b=0;ci=0; #20 a=1;b=0;ci=1; #20 a=1;b=1;ci=0; #20 a=1;b=1;ci=1; #200 $finish; end endmodule (1)组合逻辑电路仿真环境的搭建
全加器的输入a、b和ci定义为reg型变量;把输出so和co定义为wire型变量 用模块例化语句“adder1 U1(a,b,ci,so,co);”把全加器设计电路例化到测试仿真环境中; 用initial块语句改变输入的变化并生成测试条件,输入的变化语句完全根据全加器的真值表编写 仿真结果 :
(2)时序逻辑电路仿真环境的搭建 在于时序逻辑电路仿真环境中,需要考虑时序、定时信息和全局复位、置位等信号要求,并定义这些信号。 用Verilog HDL编写的十进制加法计数器源程序代码是: module cnt10(clk,rst,ena,q,cout); input clk,rst,ena; output [3:0] q; output cout; reg [3:0] q; always @ (posedge clk or posedge rst) begin if(rst)q=4'b0000; else if(ena) begin if(q<9)q=q+1; else q=0; end end assign cout=q[3]&q[0]; endmodule Verilog HDL测试程序代码是: module cnt10_tb; reg clk,rst,ena; wire [3:0] q; wire cout; cnt10 U1(clk,rst,ena,q,cout); //模块实例化 always #50 clk=~clk; //时钟信号产生 initial begin clk=0;rst=0;ena=1; //控制信号产生 #1200 rst=1; #120 rst=0; #2000 ena=0; #200 ena=1; #20000 $finish; end endmodule
实例化语句“cnt10 U1(clk,rst,ena,q,cout);”把十进制计数模块例化到仿真环境中; 在always中用语句“#50 clk=~clk;”产生周期为100(标准时间单位)的时钟方波; 用initial块生成复位信号rst和使能控制信号ena的测试条件。 测试结果如图:
5.2.3 Verilog HDL仿真结果确认 (1)直接观察波形 通过直接观察各信号波形的输出,比较测试值和期望值的大小,来确定仿真结果的正确性。
module adder1_tb; wire so,co; reg a,b,ci; adder1 U1(a,b,ci,so,co);//模块例化 initial // 测试信号产生 begin a=0;b=0;ci=0; #20 a=0;b=0;ci=1; #20 a=0;b=1;ci=0; #20 a=0;b=1;ci=1; #20 a=1;b=0;ci=0; #20 a=1;b=0;ci=1; #20 a=1;b=1;ci=0; #20 a=1;b=1;ci=1; #200 $finish; end $monitor($time, "%b %b %b -> %b %b",a, b, ci, so, co); endmodule (2)打印文本输出法 系统任务打印任务 : $display,直接输出到标准输出设备; $monitor,监控参数的变化;$fdisplay,输出到文件等 其输出的结果是: 0 0 0 0 -> 0 0 20 0 0 1 -> 1 0 40 0 1 0 -> 1 0 60 0 1 1 -> 0 1 80 1 0 0 -> 1 0
(3)自动检查仿真结果 自动检查仿真结果是通过在设计代码中的关键节点添加断言监控器,形成对电路逻辑综合的注释或是对设计特点的说明,以提高设计模块的观察性。 (4)使用VCD文件 Verilog HDL提供一系列系统任务用于记录信号值变化保存到标准的VCD(Value Change Dump)格式数据库中。 VCD文件是一种标准格式的波形记录文件,只记录发生变化的波形。 VCD文件将在第5.3.7小节中详细讲述。
5.2.4 Verilog HDL仿真效率 因为要通过串行软件代码完成并行语义的转化, Verilog HDL行为级仿真代码的执行时间比较长。 提高Verilog HDL代码的仿真代码执行时间: (1)减小层次结构 仿真代码的层次越少,执行时间就越短。 (2)减少门级代码的使用 由于门级建模属于结构级建模,建议仿真代码尽量使用行为级语句,建模层次越抽象,执行时间就越短。
(3)仿真精度越高,效率越低 计时单位值与计时精度值的差距越大,则模拟时间越长。 `timescale仿真时间标度将在第5.9.3小节中详细讲述。 (4)进程越少,效率越高 代码中的语句块越少仿真越快,这是因为仿真器在不同进程之间进行切换也需要时间。 (5)减少仿真器的输出显示 Verilog HDL语言包含一些系统任务,可以在仿真器的控制台显示窗口输出一些提示信息 ,但会降低仿真器的执行效率。
5.3与仿真相关的系统任务 • 5.3.1 $display和$write 语法格式如下: $display(“<format_specifiers>”, <signal1, signal2,...,signaln>); $write(“<format_specifiers>”, <signal1, signal2,...,signaln>); “<format_specifiers>”通常称为“格式控制” “<signal1,signal2,……,signaln>”则为“信号输出列表” $display自动地在输出后进行换行 $write输出特定信息时不自动换行
输出格式说明,由“%”和格式字符组成,其作用是将输出的数据转换成指定的格式输出。输出格式说明,由“%”和格式字符组成,其作用是将输出的数据转换成指定的格式输出。 常用的几种输出格式如右表。
一些特殊的字符可以通过表中的转换序列来输出。一些特殊的字符可以通过表中的转换序列来输出。
例5.3-1:$display和$write语句 其输出结果为: \% "S rval=00000065 hex 101 decimal rval=00000000145 octal 00000000000000000000000001100101 binary rval has e ascii character value pd strength value is StX current scope is disp e is ascii value for 101 simulation time is 0 module disp_tb; reg[31:0] rval; pulldown(pd); initial begin rval=101; $display("\\\t%%\n\"\123"); $display("rval=%h hex %d decimal", rval, rval); $display("rval=%o otal %b binary", rval, rval); $display("rval has %c ascii character value",rval); $display("pd strength value is %v",pd); $display("current scope is %m"); $display("%s is ascii value for 101",101); $write(“simulation time is”); $write(“%t\n”, $time); end endmodule 在$display中,输出列表中数据的显示宽度是自动按照输出格式进行调整的,总是用表达式的最大可能值所占的位数来显示表达式的当前值。
5.3.2 $monitor和$strobe $monitor与$stobe都提供了监控和输出参数列表中字符或变量的值的功能 (1)$monitor语法格式: $monitor(<“format_specifiers>”, <signal1, signal2,...,signaln>); 任务$monitor提供了监控和输出参数列表中的表达式或变量值的功能。每当参数列表中变量或表达式的值发生变化时,整个参数列表中变量或表达式的值都将输出显示。 例如:$monitor($time,,"rxd=%b txd=%b",rxd,txd); 注意在上面的语句中,“,,"代表一个空参数。空参数在输出时显示为空格。
$monitoron和$monitoroff任务的作用是通过打开和关闭监控标志来控制监控任务$monitor的启动和停止,这样使得程序员可以很容易的控制$monitor何时发生 。 $monitor与$display的不同处在于$monitor往往在initial块中调用,只要不调用$monitoroff,$monitor便不间断地对所设定的信号进行监视。 例5.3-2:$monitor系统任务的应用实例 module monitor_tb; integer a, b; initial begin a = 2; b = 4; forever begin #5 a= a + b; #5 b= a - 1; end end initial #40 $finish; initial $monitor($time, "a = %d, b = %d", a, b); endmodule 输出结果为: 0 a = 2, b = 4 5 a = 6, b = 4 10 a = 6, b = 5 15 a = 11, b = 5 20 a = 11, b = 10 25 a = 21, b = 10 30 a = 21, b = 20 35 a = 41, b = 20
(2)$strobe语法格式: $strobe(<functions_or_signals>); $strobe(“<string_and/or_variables>”,<functions_or_signals>); 探测任务用于在某时刻所有时间处理完后,在这个时间步的结尾输出一行格式化的文本。常用的系统任务如下: $strobe: 在所有时间处理完后,以十进制格式输出一行格式化的文本; $strobeb: 在所有时间处理完后,以二进制格式输出一行格式化的文本; $strobeo: 在所有时间处理完后,以八进制格式输出一行格式化的文本; $strobeh: 在所有时间处理完后,以十六进制格式输出一行格式化的文本。
$strobe任务在被调用的时刻所有的赋值语句都完成了,才输出相应的文字信息。 $strobe任务提供了另一种数据显示机制,可以保证数据只在所有赋值语句被执行完毕后才被显示。 例5.3-3:$strobe系统任务的应用实例 module strobe_tb; reg a,b; initial begin a=0; $display(“a by display is:”,a); $strobe(“a by strobe is:”,a); a=1; end initial begin b<=0; $display(“b by display is:”,b); $strobe (“b by strobe is:”,b); #5; $display(“#5 b by display is:”,b); $display(“#5 b by strobe is:”,b); b<=1; end Endmodule 显示结果是: a by display is:0 b by display is:x a by strobe is:1 b by strobe is:0 #5 b by display is:0 #5 b by strobe is:0
5.3.3 $time和$realtime • 用这两个时间系统函数可以得到当前的仿真时刻 ,所不同的是,$time函数以64位整数值的形式返回仿真时间,而$realtime函数则以实数型数据返回仿真时间。 (1)系统函数$time 例5.3-4:$time系统任务的应用实例 `timescale 1ns/1ns module time_tb; reg ts; parameter delay = 2; initial begin #delay ts = 1; #delay ts = 0; #delay ts = 1; #delay ts = 0; end initial $monitor($time,,,"ts = %b", ts);//使用函数$time endmodule 输出结果为: 0 ts = x 3 ts = 1 5 ts = 0 8 ts = 1 10 ts = 0
(2)$realtime系统函数 $realtime返回的时间数字是一个实型数,该数字也是以时间尺度为基准的。 例5.3-5:$realtime系统任务的应用实例 `timescale1ns/1ns module realtime_tb; reg set; parameter p=2; initial begin $monitor($realtime,,"set=b%",set); //使用函数$realtime #p set=0; #p set=1; end endmodule 输出结果为: 0 set=x 2 set=0 4 set=1
5.3.4 $finish和$stop 系统任务$finish和$stop是用于对仿真过程进行控制,分别表示结束仿真和中断仿真。其语法格式: $finish; $finish(n); $stop; $stop(n); 其中,n是$finish和$stop的参数,n可以取0、1或2几个值,分别表示如下含义,如下表所示。
$finish的作用是退出仿真器,返回主操作系统,也就是结束仿真过程。任务$finish可以带参数,根据参数的值输出不同的特征信息。如果不带参数,默认$finish的参数值为1。$finish的作用是退出仿真器,返回主操作系统,也就是结束仿真过程。任务$finish可以带参数,根据参数的值输出不同的特征信息。如果不带参数,默认$finish的参数值为1。 $stop任务的作用是把EDA工具(例如仿真器)置成暂停模式,在仿真环境下给出一个交互式的命令提示符,将控制权交给用户。这个任务可以带有参数表达式。根据参数值(0,1或2)的不同,输出不同的信息。参数值越大,输出的信息越多。
$finish和$stop实例 例5.3-6:$finish系统任务的应用实例 module finish_tb; integer a, b; initial begin a = 2; b = 4; forever begin #5 a= a + b; #5 b= a - 1; end end initial #40 $finish; initial begin $monitor($time, "a = %d, b = %d", a, b); end endmodule 在上例中,程序执行到40个时间单位时退出仿真器。 例5.3-7:$stop系统任务的应用实例 module stop_tb; integer a, b; initial begin a = 2; b = 4; forever begin #5 a= a + b; #5 b= a - 1; end end initial #40 $stop; initial begin $monitor($time, "a = %d, b = %d", a, b); end endmodule 在上例中,程序执行到40个时间单位时停止仿真,将EDA仿真器设置为暂停模式 。
5.3.5 $readmemh和$readmem 在Verilog HDL程序中有两个系统任务$readmemb和$readmemh用来从文件中读取数据到存储器中。这两个系统任务可以在仿真的任何时刻被执行使用,其语法格式共有以下六种: (1)$readmemb("<file_name>",<memory_name>); (2)$readmemb("<file_name >",< memory_name >,<start_addr>); (3)$readmemb("<file_name >",< memory_name>,<start_addr>,<finish_addr>); (4)$readmemh("<file_name >",< memory_name >); (5)$readmemh("<file_name >",< memory_name >,<start_addr>); (6)$readmemh("<file_name >",< memory_name >,<start_addr>,< finish_addr >); Microelectronics School Xidian University
例$readmemh和$readmemb系统任务的应用实例 module read_mem_tb; reg [7:0] memory_b [0:7]; reg [31:0] memory_h [0:31]; integer i; initial begin //把数据文件init_b.txt读入存储器中的给定地址 $readmemb("init_b.txt",memory_b); //把数据文件init_h.txt读入存储器中的给定地址 $readmemb("init_h.txt",memory_h); //显示初始化后的存储器内容 for(i=0; i<8; i=i+1) begin $display("memory_b [%0d] = %b", i, memory_b[i]); $display("memory_h [%0h] = %h", i, memory_h[i]); end end endmodule Microelectronics School Xidian University
文件init_b.txt和init_h.txt包含初始化数据。用@<address>在数据文件中指定地址。其中,“init_b.txt”指定二进制数据从第二位地址开始写入;而“init_h.txt”指定十六进制数据从地一位地址写入。样本文件如下所示。文件init_b.txt和init_h.txt包含初始化数据。用@<address>在数据文件中指定地址。其中,“init_b.txt”指定二进制数据从第二位地址开始写入;而“init_h.txt”指定十六进制数据从地一位地址写入。样本文件如下所示。 “init_b.txt”文件: @002 11111111 01010101 00000000 10101010 @006 1111zzzz 00001111 “init_h.txt”文件: @001 00000000000000000000000000000011 00000000000000000000000000000111 00000000000000000000000000001111 00000000000000000000000000011111 Microelectronics School Xidian University
5.3.6 $random $random是产生随机数的系统函数,每次调用该函数将返回一个32位的随机数,该随机数是一个带符号的整数。语法格式: $random%<number>; 这个系统函数提供了一个产生随机数的手段。当函数被调用时返回一个32bit的随机数。它是一个带符号的整形数。 $random一般的用法是:$ramdom % b ,其中b>0,它给出了一个范围在(-b+1):(b-1)中的随机数。 Microelectronics School Xidian University
例$random系统任务的应用实例 `timescale 1ns/1ns module random_pulse( dout ); output [9:0] dout; reg dout; integer delay1,delay2,k; initial begin #10 dout=0; for (k=0; k< 100; k=k+1) begin delay1 = 20 * ( {$random} % 6); // delay1 在0到100ns间变化 delay2 = 20 * ( 1 + {$random} % 3); // delay2 在20到60ns间变化 #delay1 dout = 1 << ({$random} %10); //dout的0--9位中随机出现1,并出现的时间在0-100ns间变化 #delay2 dout = 0; //脉冲的宽度在在20到60ns间变化 end end endmodule Microelectronics School Xidian University
5.4信号时间赋值语句 Microelectronics School Xidian University
5.4.1时间延迟的语法说明 延迟语句用于对各条语句的执行时间进行控制,从而快速满足用户的时序要求。Verilog HDL语言中延时控制的语法格式有两类: (1)#<延迟时间>行为语句; (2)#<延迟时间>; 其中,符号“#”是延迟控制的关键字符,<延迟时间>可以是直接指定的延迟时间量,并以多少个仿真时间单位的形式给出。在仿真过程中,所有时延都根据时间单位定义。 下面是带时延的赋值语句示例。 #2 Sum = A ^ B; //#2指定2个时间单位后,将A异或B的值赋值给Sum。 Microelectronics School Xidian University
根据时间控制部分在过程赋值语句中出现的位置,可以把过程赋值语句中的时间控制方式分为外部时间控制方式和内部时间控制方式。根据时间控制部分在过程赋值语句中出现的位置,可以把过程赋值语句中的时间控制方式分为外部时间控制方式和内部时间控制方式。 (1)外部时间控制方式是时间控制出现在整个过程赋值语句的最左端,也就是出现赋值目标变量的左边的时间控制方式,其语法结构如下例所示: #5 a=b; 在仿真执行时就相当于如下几条语句的执行: initial begin #5; a=b; end Microelectronics School Xidian University
(2)内部时间控制方式是过程赋值语句中的时间控制部分还可以出现在“赋值操作符”和“赋值表达式”之间的时间控制方式。其语法结构如下例所示:(2)内部时间控制方式是过程赋值语句中的时间控制部分还可以出现在“赋值操作符”和“赋值表达式”之间的时间控制方式。其语法结构如下例所示: a=#5b; 其中时间控制部分“#5”就出现在赋值操作符“=”和赋值表达式“b”的中间,因此在这条过程赋值语句内带有内部时间控制方式的时间控制。它在执行时就相当于如下几条语句的执行: initial begin temp=b; //先求b的值 #5; a=temp; end Microelectronics School Xidian University
5.4.2时间延迟的描述形式 此处时间延迟的描述形式是指延时控制的描述形式,其分为串行延迟控制、并行延迟控制、阻塞式延迟控制和非阻塞式延迟控制四种形式。以实现两组不同波形的信号为例(如图所示q0_out和q1_out),说明四种不同时间延迟的描述形式。 Microelectronics School Xidian University
(1)串行延迟控制 串行延迟控制是最为常见的信号延迟控制,它是由begin-end过程块加上延迟赋值语句构成,其中延迟赋值语句可以为外部时间控制方式,也可以为内部时间控制方式。在<延迟时间>之后也可根据情况来确定是否执行相应的行为语句。 在<延迟时间>后面有相应的行为语句,则仿真进程遇到这条带有延迟控制的行为语句后并不立即执行行为语句指定的操作,而是要延迟等待到“<延迟时间>”所指定的时间量过去后才真正开始执行行为语句指定的操作。 Microelectronics School Xidian University
例Verilog HDL串行延迟控制方式设计图示信号 `timescale 1ns/1ns module serial_delay (q0_out,q1_out); output q0_out,q1_out; reg q0_out,q1_out; initial begin q0_out=1'b0; #50 q0_out=1'b1; #100 q0_out=1'b0; #100 q0_out=1'b1; #50 q0_out=1'b0; #100 q0_out=1'b1; #50 q0_out=1'b0; #50 q0_out=1'b1; #50 q0_out=1'b0; end initial begin q1_out=1'b0; #100 q1_out=1'b1; #100 q1_out=1'b0; #50 q1_out=1'b1; #100 q1_out=1'b0; #50 q1_out=1'b1; #100 q1_out=1'b0; #50 q1_out=1'b1; #50 q1_out=1'b0; end endmodule Microelectronics School Xidian University
(2)并行延迟控制 并行延迟控制方式是通过fork-join过程块加上延迟赋值语句构成,其中延迟赋值语句同串行延迟控制方式一样,既可以是外部时间控制方式,也可以是内部时间控制方式。在<延迟时间>之后也可根据情况来确定是否执行相应的行为语句。 在<延迟时间>后面有相应的行为语句,则仿真进程遇到这条带有延迟控制的行为语句后并不立即执行行为语句指定的操作,而是要延迟等待到“<延迟时间>”所指定的时间量过去后才真正开始执行行为语句指定的操作。但并行延迟控制方式与串行延迟控制方式不同在于并行延迟控制方式中的多条延迟语句时并行执行的,并不需要等待上一条语句的执行完成才开始执行当前的语句。 Microelectronics School Xidian University
例Verilog HDL并行延迟控制方式设计图示信号 `timescale 1ns/1ns module parallel_delay (q0_out,q1_out); output q0_out,q1_out; reg q0_out,q1_out; initial fork q0_out=1'b0; #50 q0_out=1'b1; #150 q0_out=1'b0; #250 q0_out=1'b1; #300 q0_out=1'b0; #400 q0_out=1'b1; #450 q0_out=1'b0; #500 q0_out=1'b1; #600 q0_out=1'b0; join initial fork q1_out=1'b0; #100 q1_out=1'b1; #200 q1_out=1'b0; #250 q1_out=1'b1; #350 q1_out=1'b0; #400 q1_out=1'b1; #500 q1_out=1'b0; #550 q1_out=1'b1; #600 q1_out=1'b0; join endmodule Microelectronics School Xidian University
(3)阻塞式延迟控制 以赋值操作符“=”来标识的赋值操作称为“阻塞式过程赋值”,阻塞式过程赋值在之前已经介绍过,在此介绍阻塞式延迟控制。阻塞式延迟控制是在阻塞式过程赋值基础上带有延时控制的情况,例如 initial begin a = 0; a = #5 1; a = #10 0; a = #15 1; end Microelectronics School Xidian University
各条阻塞式赋值语句将依次得到执行,并且在第一条语句所指定的赋值操作没有完成之前第二条语句不会开始执行。因此在仿真进程开始时刻将“0”值赋给a,此条赋值语句完成之后才开始执行第二条赋值语句;在完成第一条赋值语句之后,延迟5个时间单位将“1”赋值给a;同理第三条赋值语句是在第二条赋值语句完成之后延迟10个时间单位才开始执行,将“0”赋值给a;最后一条赋值语句是在前三条语句都完成的时刻,延迟15个时间单位,将“1”赋值给a。下图给出了上例中信号a的波形。上述两例都采用的是阻塞式赋值语句。各条阻塞式赋值语句将依次得到执行,并且在第一条语句所指定的赋值操作没有完成之前第二条语句不会开始执行。因此在仿真进程开始时刻将“0”值赋给a,此条赋值语句完成之后才开始执行第二条赋值语句;在完成第一条赋值语句之后,延迟5个时间单位将“1”赋值给a;同理第三条赋值语句是在第二条赋值语句完成之后延迟10个时间单位才开始执行,将“0”赋值给a;最后一条赋值语句是在前三条语句都完成的时刻,延迟15个时间单位,将“1”赋值给a。下图给出了上例中信号a的波形。上述两例都采用的是阻塞式赋值语句。 Microelectronics School Xidian University
(4)非阻塞式延迟控制 以赋值操作符“<=”来标识的赋值操作称为“非阻塞式过程赋值”,非阻塞式过程赋值也在之前讲述过,在此主要介绍非阻塞式延迟控制。非阻塞式延迟控制是在非阻塞式过程赋值基础上带有延时控制的情况。如下例所示: initial begin a <= 0; a <= #5 1; a <= #10 0; a <= #15 1; end Microelectronics School Xidian University
在上例中各条非阻塞式赋值语句均以并行方式执行,虽然执行语句在begin-end串行块中,但其执行方式与并行延迟控制方式一致,在仿真进程开始时刻同时执行四条延迟赋值语句。在仿真进程开始时,将“0”值赋值给a;在离仿真开始时刻5个时间单位时,将“1”值赋值给a;在离仿真开始时刻10个时间单位时,将“0”值赋值给a;最后在离仿真开始时刻15个时间单位时,将“1”值赋值给a。下图给出了上例中信号a的波形。在上例中各条非阻塞式赋值语句均以并行方式执行,虽然执行语句在begin-end串行块中,但其执行方式与并行延迟控制方式一致,在仿真进程开始时刻同时执行四条延迟赋值语句。在仿真进程开始时,将“0”值赋值给a;在离仿真开始时刻5个时间单位时,将“1”值赋值给a;在离仿真开始时刻10个时间单位时,将“0”值赋值给a;最后在离仿真开始时刻15个时间单位时,将“1”值赋值给a。下图给出了上例中信号a的波形。 Microelectronics School Xidian University
例Verilog HDL非阻塞延迟控制方式设计 `timescale 1ns/1ns module non_blocking_delay(q0_out,q1_out); output q0_out,q1_out; reg q0_out,q1_out; initial begin q0_out<= 1'b0; q0_out<=#50 1'b1; q0_out<=#150 1'b0; q0_out<=#250 1'b1; q0_out<=#300 1'b0; q0_out<=#400 1'b1; q0_out<=#450 1'b0; q0_out<=#500 1'b1; q0_out<=#600 1'b0; end initial begin q1_out<= 1'b0; q1_out<=#100 1'b1; q1_out<=#200 1'b0; q1_out<=#250 1'b1; q1_out<=#350 1'b0; q1_out<=#400 1'b1; q1_out<=#500 1'b0; q1_out<=#550 1'b1; q1_out<=#600 1'b0; end endmodule Microelectronics School Xidian University