2.3 描述一个过程的准备
assign
全加器(例子)
时延
惯性时延
initial语句
always 语句
阻塞赋值
非阻塞赋值
使用非阻塞赋值避免竞争冒险(暂时不看-数电没学完)
2.4 去描述一个过程吧
时延控制
边沿触发事件控制
电平敏感事件控制
三、不同抽象级别的verilog模型
Verilog模型可以是实际电路不同级别的抽象。这些抽象的级别和它们对应的模型
类型共有以下五种:
系统级(system)
算法级(algorithmic)
RTL级(RegisterTransferLevel):
门级(gate-level):
开关级(switch-level)
对于数字系统的逻辑设计工程师而言,熟练地掌握门级、RTL级、算法级、系统级是非
常重要的。暂时可以不用了解开关级别描述。
2.1门级结构描述
2.1.1.与非门、或门和反向器等及其说明语法
Verilog HDL中有关门类型的关键字共有26个之多,在本教材中我们只介绍最基本的八个。
and 与门 nand 与非门 nor 或非门 or 或门 xor 异或门 xnor 异或非门 buf 缓冲器 not 非门
门声明语句的格式,如下:
<门的类型>[<驱动能力><延时>]<门实例1>[,<门实例2>,…<门实例n>]; /*门的类型是门声明语句所必需的,它可以是Verilog HDL语法规定的26种门类型中的任意一种。驱动能 力和延时是可选项,可根据不同的情况选不同的值或不选。门实例1是在本模块中引用的第一个这种类 型的门,而门实例n是引用的第n个这种类型的门。有关驱动能力的选项我们在以后的章节里再详细加以 介绍.最后我们用一个具体的例子来说明门类型的引用: */ nand #10 nd1(a,data,clock,clear); //引用了一个与非门,名字叫nd1,输入为data,clock,clear,输出为a,输出与输入的延时为10个时间单位。 基本的程序结构: module<模块名>(<端口列表>) <定义> <模块条目> endmodule
2.1.2 (补充)数电上的行为描述和结构描述介绍
数电书本上内容:
两种描述方式,其一是行为描述方式,其二是结构描述方式
行为描述方式:通过行为语句来描述电路要实现的功能,表示输出与输入之间转换的行为,不涉及具体的结构,是一种行为建模的描述方式。对下面二选一数据选择器进行行为描述。
module mux_2_to_1(a,b,out,outbar,sel); input a,b,sel; output out,outbar; assign out =sel?a:b; assign outbar =-out; endmodule
结构描述方式:通过对组合电路的各个子模块间相互连接关系的描述来说明电路的组成。各个模块还可以对其他模块进行调用,也就是模块的实例化。其中调用模块成为层次结构中的上级模块,被调用模块成为下级模块。任何硬件电路都是一个一级一级不同层次的若干单元组成。
module muxgate(a,b,out,outbar,sel); input a,b,sel; output out,outbar; wire out1,out2,selb;//定义内部三个连接点 and a1(out1,a,sel); not i1(selb,sel); and a2(out,out1,out2); or o1(out,out1,out2); assign outbar =-out; endmodule
2.1.3 用门级结构描述D触发器
module flop(data,clock,clear,q,qb); input data,clock,clear; output q.qb; nand #10 nd1(a,data,clock,clear), nd2(b,ndata,clock), nd4(d,c,b,clear), nd5(e,c,nclock), nd6(f,d,nclock), nd8(qb,q,f,clear); nand #9 nd3(c,a,d), nd7(q,e,qb); not #10 ivl(ndata,data), iv2(nclock,clock); endmodule //综上,门级描述有点像结构描述。
2.1.4 由已经设计成的模块来构成更高一层的模块
引用的方法:只需在前面写上已编的模块名,紧跟着写上引用的实例名,按顺序写上
实例的端口名即可,见下面的两个例子:
1) flop f1op_d( d1, clk, clrb, q, qn); 2) flop flop_d (.clock(clk),.q(q),.clear(clrb),.qb(qn),.data(d1));
这两个例子都表示实例f1op_d引用已编模块flop。从上面的两个例子可以看出引用时f1op_d的端口信号与flop的端口对应有两种不同的表示方法。模块的端口名可以按序排列也可以不必按序排列,如果模块的端口名按序排列,只需按序列出实例的端口名。(见例1)。如果模块的端口名不按序排列,则实例的端口信号和被引用模块的端口信号必需一一列出(见例2)。
举个例子:用上面构成的触发器。来描述一个四位的寄存器。
module hardreg(d,clk,clrb,q); input clk,clrb; input[3:0] d; output[3:0] q; flop f1(d[0],clk,clrb,q[0],), f2(d[1],clk,clrb,q[1],), f3(d[2],clk,clrb,q[2],), f4(d[3],clk,clrb,q[3],); endmodule /*在上面这个结构描述的模块中,hardreg定义了模块名;f1,f2,f3,f4分别为图5中的各个基本部件,而 其后面括号中的参数分别为图5中各基本部件的输入输出信号。请注意当f1到f4实例引用已编模块flop 时,由于不需要flop端口中的qb口,故在引用时把它省去,但逗号仍需要留着。 */
2.1.5 用户定义的原语(UDP)
用户定义的原语是从英语User Defined Primitives直接翻译过来的,在Verilog HDL 中我们常用它的缩写UDP来表示。
利用UDP用户可以定义自己设计的基本逻辑元件的功能,也就是说,可以利用UDP来定义有自己特色的用于仿真的基本逻辑元件模块并建立相应的原语库。这样,我们就可以与调用Verilog HDL基本逻辑元件同样的方法来调用原语库中相应的元件模块来进行仿真。由于UDP是用查表的方法来确定其输出的,用仿真器进行仿真时,对它的处理速度较对一般用户编写的模块快得多。
与一般的用户模块比较,UDP更为基本,它只能描述简单的能用真值表表示的组合或时序逻辑。
定义UDP的语法:
UDP模块的结构与一般模块类似,只是不用module而改用primitive关键词开始,不用endmodule而改用endprimitive关键词结束。 //例子: primitive 元件名(输出端口名,输入端口名1,输入端口名2,…) output 输出端口名; input 输入端口名1, 输入端口名2,…; reg 输出端口名; initial begin 输出端口寄存器或时序逻辑内部寄存器赋初值(0,1,或 X); end table //输入1 输入2 输入3 … : 输出 逻辑值 逻辑值 逻辑值 … : 逻辑值 ; 逻辑值 逻辑值 逻辑值 … : 逻辑值 ; 逻辑值 逻辑值 逻辑值 … : 逻辑值 ; … … … … : … ; #怎么说这个table endtable endprimitive
注意点:
1) UDP只能有一个输出端,而且必定是端口说明列表的第一项。
2) UDP可以有多个输入端,最多允许有10个输入端。
3) UDP所有端口变量必须是标量,也就是必须是1位的。 /#标量是什么意思?
4) 在UDP的真值表项中,只允许出现0、1、X三种逻辑值,高阻值状态Z是不允许出现的。
5) 只有输出端才可以被定义为寄存器类型变量。
6) initial语句用于为时序电路内部寄存器赋初值,只允许赋0、1、X三种逻辑值,缺省值为X。
上面多多少少会有一些不理解,但是不着急,后面会慢慢深入的学习。
2.2 Verilog HDL的行为描述建模
2.2.1 仅用于产生仿真测试信号的verilog HDL行为级描述建模
为了对已设计的模块进行检验往往需要产生一系列信号作为输出,输入到已设计的模块,并检查已设计模块的输出,看它们是否符合设计要求。这就要求我们编写测试模块,也称作测试文件,常用带.tf扩展名的文件来描述测试模块。
ps:数电的时序逻辑电路不要落下
下面的Verilog HDL行为描述模型用于产生时钟信号,以验证电路功能。其输出的仿真信号共有2个,分别是时钟clk、复位信号reset。初始状态时,clk置为低电平,reset为高电平。reset信号输出一个复位信号之后,维持在高电平。这一功能可利用下面的语句来实现: module gen_clk ( clk, reset); output clk; output reset; reg clk, reset; initial begin reset = 1; //initial state clk=0; #3 reset = 0; //仿真时刻延迟第3秒的时候reset被置为0 #5 reset = 1; //仿真时刻延迟第5秒的时候reset被置为1 end always #5 clk = ~clk; //以后每隔5个时间单位,时钟就翻转一次,这一功能可利用下面的语句来实现:从而该模块所产生的时钟的周期为10个时间单位。 endmodule
用这种方法所建立的模型主要用于产生仿真时测试下一级电路所需的信号,如下一级电路有输出反馈到上一级电路,并对上一级电路有影响时,也可以在这个模型中再加入输入信号,用于接收下一级电路的反馈信号。可以利用这个反馈信号再在这个模块中编制相应的输出信号,这样就比用简单的波形描述信号能更好地仿真实际电路。
举例:对上面的思维寄存器进行行为级别的测试仿真
用门级别描述D触发器 module flop(data,clock,clear,q,qb); input data,clock,clear; output q.qb; nand #10 nd1(a,data,clock,clear), nd2(b,ndata,clock), nd4(d,c,b,clear), nd5(e,c,nclock), nd6(f,d,nclock), nd8(qb,q,f,clear); nand #9 nd3(c,a,d), nd7(q,e,qb); not #10 ivl(ndata,data), iv2(nclock,clock); endmodule //引用上面已设计的模块flop,用它构成一个四位寄存器 module hardreg(d,clk,clrb,q); input clk,clrb; input[3:0] d; output[3:0] q; flop f1(d[0],clk,clrb,q[0],), f2(d[1],clk,clrb,q[1],), f3(d[2],clk,clrb,q[2],), f4(d[3],clk,clrb,q[3],); endmodule /*我们再举一个简单的例子,即编制上面完成的设计(即hardreg模块)的测试文件。这个测试文件 不仅要包括时钟信号(clock)、数据(data[3:0])、清零信号(clearb)的变化,还需引用四位寄存器.(hardreg)模块,以观测各种组合信号输入到该四位寄存器(hardreg)模块后,它的输出(q[3:0])的变 化。这个测试文件完整的源程序如下: */ module hardreg_top; reg clock, clearb; reg [3:0] data; wire [3:0] qout; `define stim #100 data=4'b //宏定义 stim,可使源程序简洁 event end_first_pass; //定义事件end_first_pass hardreg reg_4bit (.d(data), .clk(clock), .clrb(clearb), .q(qout)); /********************************************************************************** 把本模块中产生的测试信号data、clock、clearb输入实例reg_4bit以观察输出信号qout.实例 reg_4bit引用了hardreg **********************************************************************************/ initial begin clock = 0; clearb = 1; end always #50 clock = ~clock; always @(end_first_pass) clearb = ~clearb; always @(posedge clock) $display("at time %0d clearb= %b data= %d qout= %d", $time, clearb, data, qout); /***************************************************** 类似于C语言的 printf 语句,可打印不同时刻的信号值 ******************************************************/ initial begin repeat(2) //重复两次产生下面的data变化 begin data=4'b0000; `stim0001; /*************************************************************** 宏定义stim引用,等同于 #100 data=4'b0001;。注意引用时要用 `符号。 ****************************************************************/ `stim0010; `stim0011; `stim0100; `stim0101; . . . `stim1110; `stim1111; end #200 -> end_first_pass; /********************* ************************** 延迟200个单位时间,触发事件end_first_pass ************************************************/ $finish; //结束仿真 end endmodule