思考题
-
什么是信号处理电路?它通常由哪两大部分组成?
信号处理电路是进行一些复杂的数字运算和数据处理,并且又有实时响应要求的电路。两大部分:高速数据通道接口、高速算法电路。
-
为什么要设计专用的信号处理电路?
部分数字信号处理对时间要求苛刻,通用微处理器芯片为一般目的设计,通用性不可能为某一特殊的算法设计一系列专用运算电路,内部总线宽度不能随意改变,算法速度受限。
-
什么是实时处理系统?
具有实时响应的处理系统。
-
为什么要用硬件描述语言来设计复杂的算法逻辑电路?
设计和制造能进行快速计算的硬线逻辑专用电路必须学习数字电路基本知识和硬件描述语言,现代复杂数字逻辑系统的设计都借助于EDA工具完成,无论电路系统的仿真和综合都需掌握HDL。
-
能不能完全用C语言替代硬件描述语言进行算法逻辑电路的设计?
不能,C语言做基础算法的描述和验证;设计专用电路进行有速度要求的实时数据处理,需编写HDL进行仿真,从电路结构上保证算法能满足要求,并能通过与前后端的设备接口正确无误交换数据。
-
为什么在算法逻辑电路的设计中需要用C语言和HDL配合使用来提高设计效率?
C语言灵活、差错功能强,还可以通过PLI(编程语言接口)编写自己的系统任务,并直接与硬件仿真器(如Verilog-XL)结合使用。此外,C语言有可靠的编译环境,语法完备,缺陷少,应用多领域;Verilog语言仅针对硬件描述,在别处(如算法表达)并不方便。Verilog的仿真、综合、差错工具等大部分软件是商业软件,缺乏长期大量使用,可靠性差,亦有很多缺陷。
既利用C语言的完整性,又结合Verilog对硬件描述的精确性。
第1章 Verilog的基本知识
硬件描述语言(HDL, Hardware Description Language):是电子系统硬件行为描述、结构描述、数据流描述的语言
思考题
-
什么是硬件描述语言?主要作用是什么?
是电子系统硬件行为描述、结构描述、数据流描述的语言。
作用:数字电路系统的设计可以从顶层到底层(从抽象到具体)逐层描述设计思想,用一系列风层次的模块来表示极其复杂的数字系统。
-
目前世界上符合IEEE标准的硬件描述语言有哪两种?它们各有什么特点?
Verilog HDL和VHDL两种。
共同特点:能够形式化地抽象表示电路的行为和结构;支持逻辑设计中层次与范围的描述;可借用高级语言的精巧结构简化电路行为的描述;具有电路仿真与验证机制以保证设计的正确性;支持电路描述由高层到底层的综合转换;硬件描述与实现工艺无关;便于文档管理;易于理解和设计重用。
不同:Verilog HDL易掌握,VHDL掌握较困难
-
什么情况下需要采用硬件描述语言的设计方法?
对逻辑电路及系统设计的时间要求很短时,需采用HDL的设计方法。
-
采用硬件描述语言设计方法的优点是什么?缺点?
优点:与工艺无关。工程师在功能设计、逻辑验证阶段可以不必过多考虑门级工艺实现的具体细节,只需利用系统设计时对芯片的要求,施加不同的约束条件,即可设计出实际电路。
缺点:需要相应的EDA工具,而EDA工具的稳定性需要进一步的在工程中提升。
-
简单叙述一下利用EDA工具并采用硬件描述语言的设计方法和流程?
自顶向下(Top_Down)设计方法:从系统级开始,划分为基本单元,再划分为下一层次基本单元,直到可以直接用EDA元件库中的基本元件实现为止。
基本流程由两大功能部分组成:
1)设计开发。即从编写设计文档->综合到布局布线->电路生成这样一系列步骤。
2)设计验证。进行各种仿真的一系列步骤,如果在仿真过程中发现问题就返回设计输入进行修改。
-
硬件描述语言可以用哪两种方式参与复杂数字电路的设计?
复杂数字电路的设计、复杂数字电路的仿真验证。
-
用硬件描述语言设计的数字系统需要经过哪些步骤才能与具体的电路相对应?
编写设计文件,功能仿真,优化、布局布线,布线后门及仿真。
-
为什么说用硬件描述语言设计的数字逻辑系统具有最大的灵活性并可以映射到任何工艺的电路上?
硬件描述语言的设计具有与工艺无关性。(见思考题4)
-
软核是什么?虚拟器件是什么?它们的作用是什么?
软核(Soft Core):功能经过验证的、可综合的、实现后电路结构总门数在5000门以上的Verilog HDL模型。
虚拟器件:由软核构成的器件。
新电路研制过程中,软核和虚拟器件可以很容易地借助EDA综合工具与其他外部逻辑结合为一体,重用性大大缩短设计周期,加快复杂电路的设计。
-
集成电路行业中IP的含义是什么?固核是什么?硬核是什么?与软核相比它们各有什么特点?各适合于什么场合?
知识产权(IP, Intellectual Property)。
固核(Firm Core):在某一种现场可编程门阵列(FPGA)器件上实现的、经验证是正确的、总门数在5000门以上的电路结构编码文件。
硬核(Hard Core):在某一种专用集成电路工艺的(ASIC)器件上实现的、经验证是正确的、总门数在5000门以上的电路结构版图掩膜。
在具体实现手段和工艺技术尚未确定的逻辑设计阶段,软核具有最大的灵活性,很容易借助EDA综合工具与其他外部逻辑结合为一体;相比之下固核和硬核与其他外部逻辑电路结合为一体的灵活性要差很多,特别是电路实现工艺技术改变时更是如此。
-
简述Top_Down设计方法和硬件描述语言的关系?
Top_Down设计方法首先从系统设计入手,从顶层进行功能划分和结构设计。系统总仿真时顶层进行功能划分的主要环节,而该过程需要采用硬件描述语言的方法。
-
System Verilog与Verilog有什么关系?适合于何种设计?
System Verilog是Verilog语言的扩展和延伸。
Verilog适合系统级、算法级、寄存器级、逻辑级、门级、电路开关级设计;System Verilog更适合于可重用的可综合IP和可重用的验证用IP设计,以及特大型(千万门级以上)基于IP的系统级设计和验证。
第2章 Verilog语法的基本概念
Verilog模型可以是实际电路的不同级别抽象,5种:
-
系统级(system-level):用语言提供的高级结构能够实现待设计模块的外部性能的模型。
-
算法级(algorithm-level):用语言提供的高级结构能够实现算法运行的模型。
-
RTL级(register transfer level):描述数据在寄存器之间的流动和如何处理、控制这些数据流动的模型。
以上三种属于行为描述,只有RTL级才与逻辑电路有明确的对应关系。
-
门级(gate-level):描述逻辑门以及逻辑门之间连接的模型。
与逻辑电路有确定的连接关系
-
开关级(switch-level):描述期间中三极管和存储节点以及它们之间连接的模型。
与具体的物理电路有对应关系
测试在功能级(即行为级)进行,称为前(RTL)仿真;
逻辑网表(逻辑布尔表达式),称为逻辑网表仿真;
门级结构级,称为门级仿真。
如果门级结构模块与具体的工艺技术对应起来,并加上布局布线引入的延迟模型,此时的仿真称为布线后仿真,与实际电路情况非常接近。
思考题
-
Verilog语言有什么作用?
-
可描述顺序执行或并行执行的程序结构;
-
用延迟表达式或事件表达式来明确的控制过程的启动时间;
-
通过命名的事件来触发其他过程里的激活行为或停止行为;
-
提供了条件如if-else、case等循环程序结构;
-
提供了可带参数且非零延续时间的任务(task)程序结构;
-
提供了可定义新的操作符的函数结构(function);
-
提供了用于建立表达式的算术运算符、逻辑运算符、位运算符;
-
Verilog HDL语言作为一种结构化的语言非常适用于门级和开关级的模型设计
-
提供了一套完整的表示组合逻辑的基本元件的原语(primitive);
-
提供了双向通路(总线)和电阻器件的原语;
-
可建立MOS器件的电荷分享和电荷衰减动态模型;
-
Verilog HDL的构造性语句可以精确地建立信号的模型。
-
-
构成模块的关键词是什么?
module、endmodule
-
为什么说可以用Verilog构成非常复杂的电路结构?
Verilog HDL程序由模块构成;模块可以进行层次嵌套,大型的数字电路设计可以分割成不同的小模块实现特定功能。
-
为什么可以用比较抽象的描述来设计具体的电路结构?
有抽象描述设计电路结构的语言,是适合数字系统设计的语言。
-
是否任意抽象的符合语法的Verilog模块都可以通过综合工具转变为电路结构?
不能。符合语法,还要符合一些基本规则。
-
什么叫综合?
通过综合工具把行为级描述的模块通过逻辑网表自动转化为门级形式的模块叫综合。
-
综合是由什么工具来完成的?
EDA工具完成综合。
-
通过综合产生的是什么?产生的结果有什么用处?
产生的是由与、或、非门组成的组合逻辑电路。产生的模块很容易与某种工艺的基本元件逐一对应,再通过布局布线工具自动转变为某种工具工艺的电路布线结构。
-
仿真是什么?为什么要仿真?
仿真是对电路模块进行动态的全面测试。通过观察测试模块的输出信号是否符合要求,可以调试和验证逻辑系统的设计和结构准确与否,并发现问题及时修改。
-
仿真可以在几层面上进行?每个层面的仿真有什么意义?
分别为前仿真、逻辑网表仿真、门级仿真和布线后仿真。
前三者可以调试和验证逻辑系统的设计和结构准确与否,并发现问题及时修改;布线后仿真分析设计的电路模块运行是否正常。
-
模块的端口是如何描述的?
-
在引用实例模块时,如何在主模块中连接信号线?
.()
“.”表示被引用模块的端口,“()”表示本模块中与之连接的模块。
-
如何产生连续的周期性测试时钟?
always #(period/2) clock = ~clock;
-
如果不用initial块,能否产生测试时钟?
不能,不用initial块,无时钟初始值。
-
从本讲的简单例子,是否能明白always块与initial块有什么不同?
initial执行一次,always执行无数次。
-
为什么说Verilog可以用来设计数字逻辑电路和系统?
同1. Verilog作用。
第3章 模块的结构、数据类型、变量和基本运算符号
x代表不定值;z代表高阻值,可写作?。
取模运算结果的符号位采用模运算式里第一个操作数的符号位。
逻辑运算符:&&,||,!。逻辑运算,输出为1位。
位运算符:~,|,^,&,^~。逐位逻辑运算,输出与输入位数一致。
缩位运算符:&,|,^。单目运算符,按位逻辑运算,输出为1位。
思考题
-
模块由几个部分组成?
描述接口、描述逻辑功能两部分组成。
-
端口分为几种?
3种,输入口、输出口、输入/输出口。
-
为什么端口要说明信号的位宽?
不说明信号位宽可能会在信号发生改变时发生错误,不容易看出接收信号的数据宽度,很难进行数据处理。
-
能否说模块相当于电路图中的功能模块,端口相当于功能模块的引脚?
可以,每个模块都有特定功能,功能的实现必须依靠具体电路,端口是信号传递通道,相当于功能模块引脚。
-
模块中的功能描述可以由哪几类语句或语句块组成?它们出现的顺序会不会影响功能的描述?
assign语句声明、实例元件、always块。出现顺序不影响功能描述。
-
这几类描述中的哪一种直接与电路结构有关?
实例元件直接与电路结构相关。
-
最基本的Verilog变量有哪几种类型?
wire、reg、memory。
-
reg型和wire型变量的差别是什么?
reg型是寄存器变量,相当于存储单元;wire型是线型变量,相当于物理连线。根本性差别在于reg型保持最后一次的赋值,wire型需要持续的驱动。
-
由连续赋值语句(assign)赋值的变量是否能是reg类型的?
不能,左侧必须是线网型数据(wire)。
-
在always模块中被赋值的变量能否是wire类型的?如果不能是wire类型的,那么必须是什么类型的?它们表示的一定是实际的寄存器吗?
always块中被赋值变量不能是wire型,必须是reg型,表示的不一定是实际的寄存器。
-
参数类型的变量有什么用处?
可以提高程序的可读性和可维护性。
-
Verilog语法规定的参数传递和重新定义功能有什么直接的应用价值?
可以用于定义延迟时间和变量宽度。
-
逻辑比较运算符小于等于“<=”和非阻塞赋值“<=”的表示是完全一样的,为什么Verilog在语句解释和编译时不会搞错?
逻辑比较时<=两边是两个操作数,是双目运算符;在非阻塞赋值时<=右边是操作数,是单目运算符。
-
是否可以说实例引用的描述实际就是严格意义上的电路结构描述?
不能,实例引用的描述是在门级电路上描述,和严格意义的电路结构仍有差距。
第4章 运算符、赋值语句和结构说明语句
==相等;!=不等;===相同;!==不同
=== | 0 | 1 | x | z | == | 0 | 1 | x | z |
---|---|---|---|---|---|---|---|---|---|
0 | 1 | 0 | 0 | 0 | 0 | 1 | 0 | x | x |
1 | 0 | 1 | 0 | 0 | 1 | 0 | 1 | x | x |
x | 0 | 0 | 1 | 0 | x | x | x | x | x |
z | 0 | 0 | 0 | 1 | z | x | x | x | x |
各运算符优先级(由高到低) |
---|
! ~ |
* / % |
+ - |
<< >> |
< <= > >= |
== != === !== |
& |
^ ^~ |
| |
&& |
|| |
?: |
思考题
-
逻辑运算符与按位逻辑运算符有什么不同,它们各在什么场合使用?
逻辑运算符输出1位,按位运算符输出与输入位数一致;逻辑运算符多用于条件判断,按位运算符用于信号的运算和检测。
-
指出两种逻辑等式运算符的不同点,解释书上的真值表。
区别:相等和相同。
-
拼接符的作用是什么?为什么说合理使用拼接符可以提高程序的可读性和可维护性?拼接符表示的操作其物理意义是什么?
拼接符可以把两个或多个信号的某些位拼接起来进行运算操作。借助拼接符可以用一个信号名表示由多位信号组成的复杂信号。物理意义:将多个信号结合成一个信号。
-
如果都不带时间延迟,阻塞和非阻塞赋值有什么不同?举例说明它们的不同点?
阻塞是顺序执行,非阻塞赋值是并行执行。
//非阻塞赋值 always @(posedge clk) begin b <= a; c <= b; end //阻塞赋值 always @(posedge clk) begin b = a; c = b; end
非阻塞赋值在always块结束后执行,描述功能为两个D触发器;
阻塞赋值先后执行,描述功能只用一个D触发器存储a值。
-
举例说明顺序块和并行块的不同。
//顺序块 begin #50 r = 'h35; #50 r = 'hE2; #50 r = 'h00; #50 ->end_wave; end //并行块 fork #50 r = 'h35; #100 r = 'hE2; #150 r = 'h00; #200 ->end_wave; join
-
如果在顺序块中,前面有一条语句是无限循环,下面的语句能否进行?
不能。
-
如果在并行块中,发生上述情况,会如何呢?
下面语句可以执行。
第5章 条件语句、循环语句、块语句与生成语句
循环语句:
-
forever:必须写在initial块中。
-
repeat:
-
while
-
for
//乘法器 parameter size = 8, longsize = 16; reg [size:1] opa, opb; reg [longsize:1] result; //repeat实现 begin: mult_repeat reg [longsize:1] shift_opa, shift_opb; shift_opa = opa; shift_opb = opb; result = 0; repeat(size) begin if (shift_opb[1]) result = result + shift_opa; shift_opa = shift_opa << 1; shift_opb = shift_opb >> 1; end end //for实现 begin: mult_for integer bindex; result = 0; for(bindex=1; bindex<=size; bindex=bindex+1) if(opb[bindex]) result = result + (opa << (bindex-1)); end
命名块:
-
可以声明局部变量;
-
是设计层次的一部分,可以通过层次名引用进行访问;
-
可以被禁用,例如停止执行(关键字disable)。
循环生成语句:
-
仿真器对生成块中的代码确立(展平),再仿真;
-
genvar用于声明生成变量,只能用在生成快中;确立后的代码中不存在生成变量;
-
生成变量的值只能由循环生成语句改变;
-
循环生成语句名字:or_loop,层次化引用:or_loop[0].g1、or_loop[1].g1。
本地参数localparam:不能用参数重定义(defparam)修改,也不能在实例引用时通过传递参数语句修改,即#(参数1,参数2,...)。
思考题
-
为什么建议在编写Verilog模块程序时,如果用到if语句,建议把配套的else情况也考虑在内?
如果没有配套的else语句,在不满足if条件语句时,将会保持原来的状态不变,从而在综合时产生一个锁存器,这并不是设计想要的结果。
-
用if(条件1)语句;else if(条件2)语句;else if(条件3)语句;…else 语句和用case endcase表示不同条件下的多个分支是完全相同的,还是有什么不同?
不完全相同。if_else_if条件表达式更为直观;case语句提供了对于不定值x和高阻值z的情况处理。
-
如果case语句的分支条件没有覆盖所有可能的组合条件,定义了default项和没有定义default项有什么不同?
定义default项的电路描述更清楚,综合时不会产生不想要的结果,未定义default项会在综合时产生一个锁存器。
-
仔细阐述case、casex和casez之间的不同。
case所有位均考虑,casez不考虑z位,casex不考虑z位和x位。
-
forever语句如果运行了,在它下面的语句能否运行?它位于begin end块和位于fork join块有什么不同?
不能运行。begin end顺序块,执行到forever后下面的语句不能执行;fork join并行块,执行forever还是能执行下面的语句。
-
forever语句、repeat语句能否独立于过程块而存在,即能否不在initial或always块中使用?
forever不能独立于过程块,repeat能够独立于过程块。
-
用for循环为存储器许多单元赋值时是否需要时间?如果不定义时间延迟,为什么它可以不需要时间就把不管多大的储存器赋值完毕?
定义时间延迟则需要时间,否则不需要。循环的边界是确定的,那么在综合时该循环语句被认为是重复的硬件结构。
-
for循环是否可以表示可以综合的组合逻辑?请举例说明。
可以。例如for循环实现的乘法器。
-
在编写测试模块时用什么方法可以使for循环按照时钟的节拍运行?请比较下图程序段。
//一 always @(posedge clk) begin for(i=0; i<=1024; i=i+1) mem[i] = i; end //二 initial begin for(i=0; i<=1024; i=i+1) begin mem[i] = i; @(posedge clk) end end
第一种不能,第二种可以。
-
声明一个名为oscillate的寄存器变量并将它初始化为0,使其每30个时间单位进行一次取反操作,使用forever循环。
reg oscillate; initial begin oscillate = 0; forever #30 oscillate = ~oscillate; end
-
设计一个周期为40个时间单位的时钟信号,其占空比为25%。使用always和initial块进行设计,将其在仿真0时刻的值初始化为0。
initial begin clk = 0; always begin #30 clk = ~clk; #10 clk = ~clk; end end
-
给定下面含有阻塞过程赋值语句的initial块,每条语句在什么仿真时刻开始执行?a、b、c和d在仿真过程中的中间值和仿真结束时的值是什么?
initial begin a = 1'b0; b = #10 1'b0; c = #5 1'b0; d = #20 {a, b, c}; end
仿真开始第一条语句执行,10个时钟单元后第二条执行,15个后第三条执行,35个后第四条执行。仿真中间a=0,b、c、d为x;仿真结束后a=1'b0,b=1'b0,c=1'b0,d=3'b000。
-
在12问中,如果initial块中包括的是非阻塞过程赋值语句,那么各个问题的答案是什么?
仿真开始第一条语句执行,10个时钟单元后第二条执行,5个后第三条执行,20个后第四条执行。仿真中间a=0,b、c、d为x;仿真结束后a=1'b0,b=1'b0,c=1'b0,d=3'b000。
-
d的最终值为?
initial begin b = 1'b1; c = 1'b0; #10 b = 1'b0; end initial begin d = #25 (b | c); end
d = 1‘b0。
-
使用带有同步清零端的D触发器(清零端高电平有效,在时钟下降沿执行清零操作)设计一个下降沿触发的D触发器,只能使用行为语句(D触发器的输出q应当声明为寄存器变量)。使用设计出的D触发器输出一个周期为10个时间单位的时钟信号。
module dff_syn(d, clk, clr, q); input d, clk, clr; output reg q; always @(negedge clk) if (clr) q <= 0; else q <= d; endmodule //testbench `timescale 1ns/1ns module dff_syn_tb(); reg d, clk, clr; wire q; initial begin d = 0; clr = 0; clk = 0; #10 clr = 1; #20 clr = 0; forever #5 d = ~d; end dff_syn u1(d, clk, clr, q); always #5 clk = ~clk; endmodule
-
使用带有异步清零端的D触发器设计第15题中要求的D触发器(在清零端变为高电平后立即执行清零操作,无须等待下一个时钟下降沿),并对这个D触发器进行测试。
module dff_asyn(d, clk, clr, q); input d, clk, clr; output reg q; always @(negedge clk, posedge clr) begin if (clr) q <= 0; else q <= d; end endmodule //testbench `timescale 1ns/1ns module dff_asyn_tb(); reg d, clk, clr; wire q; initial fork d = 0; clr = 0; clk = 0; #10 clr = 1; #20 clr = 0; forever #5 d = ~d; join dff_asyn u1(d, clk, clr, q); always #5 clk = ~clk; endmodule
-
使用wait语句设计一个电平敏感的锁存器,该锁存器的输入信号为d和clock,输出为q,其功能是当clock=1时q=d。
module L_FF(d, clock, q); input d, clock; output reg q; always @(*) begin wait(clock) q = d; end endmodule
-
使用条件语句设计四选一多路选择器。
module mux4_to_1(out, i0, i1, i2, i3, s1, s0); output reg out; input i0, i1, i2, i3; input s1, s0; always @(*) begin if (s1==0 && s0==0) out = i0; else if (s1==0 && s0==1) out = i1; else if (s1==1 && s0==0) out = i2; else if (s1==1 && s0==1) out = i3; else out = 1'bx; end endmodule
-
使用case语句设计八功能的算术运算单元(ALU),其输入信号a和b均为4位,功能选择信号select为3位,输出信号out为5位。ALU执行操作与select信号有关,具体关系如表所列(忽略输出结果中的上溢和下溢的位)。
select信号 功能 3'b000 out = a 3'b001 out = a+b 3'b010 out = a-b 3'b011 out = a/b 3'b100 out = a%b 3'b101 out = a<<1 3'b110 out = a>>1 3'b111 out = (a>b) module ALU(a, b, select, out); input [3:0] a, b; input [2:0] select; output reg [4:0] out; always @(select) begin case(select) 3'b000: out = a; 3'b001: out = a+b; 3'b010: out = a-b; 3'b011: out = a/b; 3'b100: out = a%b; 3'b101: out = a<<1; 3'b110: out = a>>1; 3'b111: out = a>b; default: out = 5'bx; endcase end endmodule
-
使用while循环设计一个时钟信号发生器。其时钟信号的初值为0,周期为10个时间单位。
initial begin clk = 0; while(1) #5 clk = ~clk; end
-
使用for循环对一个长度为1024(地址从0~1023)、位宽为4的寄存器类型数组cache_var进行初始化,把所有单元都设置为0。
begin reg [3:0] cache_var[1023:0]; integer i; for(i=0; i<1024; i=i+1) cache_var[i] = 0; end
-
使用forever循环设计一个时钟信号,周期为10,占空比为40%,初值为0。
initial begin clk = 0; forever begin #6 clk = ~clk; #4 clk = ~clk; end end
-
使用repeat将语句a=a+1延迟20个时钟上升沿之后再执行。
parameter delay = 20; reg a; begin repeat(delay) @(posedge clk); a = a + 1; end
-
下面是一个内嵌顺序块和并行块的块语句。该块的执行结束时间是多少?事件的顺序是怎样的?每条语句的仿真结束时间是多少?
initial begin //顺序 x = 1'b0; //0 #5 y = 1'b1; //5 fork //并行 #20 a = x; //25 #15 b = y; //20 join //顺序 #40 x = 1'b1; //65 fork //并行 #10 p = x; //75 begin //顺序 #10 a = y; //75 #30 b = x; //105 end #5 m = y; //70 join end
执行结束时间为105个时间单位。
-
用forever循环语句、命名块(named block)和禁用(disabling of)命名块来设计一个八位计数器。这个计数器从count=5开始计数,到count=67结束计数。每个时钟正跳变沿计数器加一,时钟的周期为10。计数器的计数只用了一次循环,然后禁用(使用disable语句)。
reg [7:0] count; initial begin count = 5; end begin: named block forever begin: disabling of @(posedge clk) count = count + 1; if (count == 67) disable: disabling of; end end
第6章 结构语句、系统任务、函数语局和显示系统任务
6.2 task和function
task和function的不同:
-
function只能与主模块共用同一个仿真时间单位;task可以定义自己的仿真时间单位,可以使用延迟、事件、时序控制结构;
-
function可以调用其他函数,不能调用task;task能调用其他task和function;
-
function至少有一个输入变量,task可以没有或有多个任何类型的变量;
-
function返回一个值,task不返回值。
自动(递归)函数:automatic,仿真器为每一次函数调用动态分配新地址空间;自动函数中的局部变量不能通过层次名访问,函数本身可以通过层次名调用。
function automatic func;
常量函数:integer;带符号函数:signed。
6.4 常用系统任务
6.4.1 $display和$write
$display输出后自动换行,$write不换行。
输出格式 | 说明 |
---|---|
%h或%H | 十六进制数形式输出 |
%d或%D | 十进制数形式输出 |
%o或%O | 八进制数形式输出 |
%b或%B | 二进制数形式输出 |
%c或%C | ASCII码字符形式输出 |
%v或%V | 输出网络型数据信号强度 |
%m或%M | 输出等级层次的名字 |
%s或%S | 字符串形式输出 |
%t或%T | 当前的时间格式输出 |
%e或%E | 指数形式输出实型数 |
%f或%F | 十进制数形式输出实型数 |
%g或%G | 指数/十进制数形式输出实型数,以较短的结果输出 |
-
十进制数格式输出时,结果的高位0值用空格代替;其他进制,保留0;可以使用 %0h 自动调整显示输出数据宽度。
-
十进制:所有位均为不定值/高阻值,结果为x/z;部分为不定值/高阻值,结果为X/Z。
-
十六进制和八进制:对应进制数的所有位为不定值/高阻值,该位进制数结果为x/z;部分为不定值/高阻值,结果为X/Z。
6.4.2 文件输出
返回一个称为多通道描述符(multichannel descriptor)的32位值,只有一位被设置为1。标准输出有一个多通道描述符,最低位(第0位)设置为1,一直开放。此后每次调用打开新通道,返回一个设置了第1位、第2位等,直到第30位。第31位是保留位。通道号与多通道描述符中被设置为1的位相对应。
文件描述符是一个多通道描述符,可以是一个文件句柄或多个文件句柄按位组合,会输出到与文件描述符中值为1的位相关联的所有文件中。
文件关闭不能再写,多通道描述符中相应位设置为0。
6.4.3 显示层次
$display、$write、$monitor、$strobe任务中的%m选项可以显示任何级别的层次,无需参数。
6.4.4 选通显示(Strobing)
与$display相似;其他语句和$display在同一个时间单位执行顺序不确定;$strobe在同时刻的其他赋值语句执行完成后才执行。
6.4.5 值变转储文件(VCD)
VCD是一个ACSII文件(.dmp),包含仿真时间、范围与信号的定义以及仿真运行中信号值变化等信息。
思考题
-
怎样理解initial语句只执行一次的概念?
仿真开始时,initial语句只执行一次,块内的语句可能不止执行一次。
-
在initial语句引导的过程块中是否可以有循环语句?如果可以,是否与思考题1互相矛盾?
可以有,不矛盾。initial语句执行一次,并不意味块内语句不能是循环语句。
-
怎样理解由always语句引导的过程块是不断活动的?
仿真过程中always块始终在循环的活动,检查always语句后面的信号是否发生相应改变,如果没有检查信号将会进入一个循环,使仿真器锁死。
-
不断活动与不断执行有什么不同?
不断活动是always语句不断检查是否满足条件;不断执行是always语句引导的过程中的语句不断执行。
-
怎样理解沿触发和电平触发的不同?
沿触发是信号上升沿或下降沿触发,电平触发是信号改变即触发。
-
是不是可以说沿触发是有间隔的,在一定的时间区间里只需要注意有限的点,而电平触发却需要注意无穷多个点?
不是,沿触发是边沿触发,电平触发是信号改变时触发。
-
沿触发的always块和电平触发的always块各表示什么类型的逻辑电路的行为?为什么?
沿触发表示时序逻辑电路,和时序有关;电平触发表示组合逻辑电路,只和电平有关。
-
简单叙述任务与函数的不同点。
见6.2。
-
简单叙述$display、$write和$strobe的不同点。
$display输出后自动换行,$write不能;$display和其他语句执行顺序不确定,$strobe在同时刻的其他赋值语句执行完成后执行。
-
简单叙述Verilog1364-2001版语法规定的电平敏感列表的简化写法。
“or”可以用“,”代替。
-
如何在Verilog测试模块中,利用文件的读写产生预定格式的信号,并记录有测试价值的信号?
提供系统任务,值变转储文件。
initial $dumpfile("myfile.dmp"); //仿真信息转储到myfile.dmp文件 initial $dumpvars; //未指定变量范围,设计中全部信号转储 initial $dumpvars(1, top); //转储模块实例top中的信号,1表示层次等级,只转储top下第一层的信号 //即转储top模块中的变量,而不转储在top中调用模块中的变量 initial $dumpvars(2, top.m1); //转储top.m1模块下两层的信号 initial $dumpvars(0, top.m1); //0表示转储top.m1模块下各层的所有信号 //启动和停止转储过程 initial begin $dumpon; //启动转储过程 #1000 $dumpoff; //过1000仿真时间单位后,停止转储过程 end //生成一个检查点,转储所有VCD变量的现行值 initial $dumpall;
第7章 调试用系统任务和常用编译预处理语句
7.1 $monitor
$monitor($time, , p1, p2, ..., pn);
", ,"代表一个空格,空参数输出时显示为空格。$monitoron和$monitoroff任务的作用是通过打开和关闭监控标志来控制监控任务$monitor的启动和停止。调用$monitoron启动$monitor时,立刻输出显示当前时刻参数列表的值。连续监视数据变化。
7.2 $time
$time返回一个64位整数表示当前仿真时刻值,受时间尺度比例影响,输出是时间尺度的倍数,再取整。
$realtime返回实型数时间数字,以时间尺度为基准。
常用在$monitor中,用来做时间标记。
7.3 $finish
$finish;
$finish(n);
退出仿真器,返回主操作系统,即结束仿真过程。
可带参数(不带默认为1):0:不输出任何信息;1:输出当前仿真时刻和位置;2:输出当前仿真时刻、位置和在仿真过程中所用memory及CPU时间的统计。
7.4 $stop
$stop;
$stop(n);
把EDA工具置成暂停模式,在仿真环境下给出一个交互式的命令提示符,将控制权交给用户。参数值越大,输出信息越多。
$stop和$finish常用在测试模块的initial模块中,配合时间延迟控制仿真的持续时间。
7.5 $readmemb和$readmemh
$readmemb("<数据文件名>", <存储器名>); $readmemb("<数据文件名>", <存储器名>, <起始地址>); $readmemb("<数据文件名>", <存储器名>, <起始地址>, <结束地址>); $readmemh("<数据文件名>", <存储器名>); $readmemh("<数据文件名>", <存储器名>, <起始地址>); $readmemh("<数据文件名>", <存储器名>, <起始地址>, <结束地址>);
被读取的数据文件内容只能包含:空白位置、注释行、二进制/十六进制数字。
数据文件中地址格式为:@十六进制数;例:@hhhh。
7.6 $random
$random % b; //b>0, (-b+1):(b-1) {$random} % b; // 0:(b-1)
产生一个32位的随机数,带符号的整形数。
//宽度随机脉冲序列 `timescale 1ns/1ns module random_pulse(dout); output reg [9:0] dout; integer delay1, delay2, k; initial begin #10 dout = 0; for(k=0; k<100; k=k+1) begin delay1 = 20*({$random}%6); //0-100ns delay2 = 20*(1 + {$random}%3); //20-60ns #delay1 dout = 1 << ({$random}%10); //dout随机位为1,出现时间在0-100ns #delay2 dout = 0; //脉冲宽度在20-60ns end end endmodule
7.7 编译预处理
宏定义:`define 标识符(宏名) 字符串(宏内容)。宏定义时可引用已定义的宏名。
文件包含处理:`include “文件名”。
时间尺度:`timescale <时间单位>/<时间精度>。
条件编译命令:
`ifdef 宏名(标识符) 程序段1 `elsif 程序段2 `else 程序段3 `endif `ifndef
条件执行:$test$plusargs("DISPLAY_VAR"),可以指定+DISPLAY_VAR选择在运行时设置标志。
$value$plusargs,测试调用选项的参数值,未找到返回0。使用带 + 的命令行启动仿真器。
思考题
-
为什么在多模块调试的情况下$monitor 需要配合$monitoron和$monitoroff来工作?
多模块调试时,调用$monitor模块数不仅一个,而任何时刻只能有一个$monitor起作用,因此需要配合$monitoron和$monitoroff使用。
-
请用$random配合求模运算编写:
(1)用于测试的跳变沿抖动为周期1/10的时钟波形。 (2)随机出现的脉宽随机的窄脉冲。
module random_pulse(dout); output reg [9:0] dout; integer delay, k; initial begin #10 dout = 0; for(k=0; k<100; k=k+1) begin delay = {$random}%10; #delay dout = 1; #delay dout = 0; end end endmodule
-
Verilog的编译预处理与C语言的编译预处理有什么不同?
Verilog编译预处理要以“`”开头。
-
请仔细阐述`timescale编译预处理的作用?
说明该命令后模块的时间单位和时间精度,可以在同一个设计里包含不同时间单位的模块。
-
不同`timescale定义的多模块仿真测试时需要注意什么?
-
`timescale命令声明本模块时间单位和时间精度;
-
$printtimescale输出一个模块的时间单位和时间精度;
-
$time和$realtime以及%t格式输出EDA工具记录的时间信息。
-
-
为什么说系统任务$readmem可以用来产生用于算法验证的极其复杂的测试用数据流?
复杂数据可以用C语言产生,存在文件中,用$readmem取出存入存储器,按节拍输出,可用于验证算法逻辑电路。
-
为什么说熟练地使用条件编译命令可以使源代码有更大的灵活性,可以适用于不同的实现对象,如不同工艺的ASIC或速度规模不同的FPGA或CPLD,从而为软核的商品化创造条件?
合理的使用条件编译和条件执行预处理可以使测试程序适应不同的编译环境,也可以把不同的测试过程编写到一个统一的测试程序中去,可以简化测试过程,对于复杂设计的验证模块的编写很有实用价值。
第8章 练习
(7)
//1) reg [7:0] A; A = 2'hFF; //answer A = 8'b0000_0011 A = 8'h03 //2) reg [7:0] B; B = 8'bZ0; //answer B = 8'bZZZZ_ZZZ0;
(15)以下模块综合后可能是哪一种硬件实现?
always @(posedge clock) if(a) C = B; //1. 不能综合 //2. 一个上升沿触发器和一个多路器 //3. 一个输入是A,B,clock的三输入与门 //4. 一个透明锁存器 //5. 一个带有clock有使能引脚的上升沿触发器 //answer:2和5。
(17)以下模块被综合后可能是哪一种硬件实现?
//1. 带异步复位端的触发器 //2. 不能综合或与预先设想不一致 //3. 组合逻辑 //4. 带逻辑的透明锁存器 //5. 带同步复位端的触发器 //1) 5 always @(posedge clk) begin A <= B; if(C) A <= 1'b0; end //2) 2 always @(A or B) case(A) 1'b0: F = B; 1'b1: G = B; endcase //3) 1 always @(posedge A or posedge B) if(A) C <= 1'b0; else C <= D; //4) 2 always @(posedge clk or negedge rst) if(rst) A <= 1'b0; else A <= B;
(23){1, 0}值为?
64'H0000_0001_0000_0000