经过数字逻辑电路课程的学习,大家已对多路选择器(数据选择器)有了一定的认识。本节将通过建模2选1的数据选择器,简单介绍Verilog的各级建模语言。
一、行为级建模
使用always块来对数据选择器进行描述,其后面的小括号为该always块的敏感列表(sensitive
list),只要sl或a或b其中有一个变化时,就执行其后的语句。需要注意的是,always块内的输出out,必须定义为reg型变量,因为该值在同一块内可能多次变化。
module muxtwo (out,a,b,sl); input a,b,sl; output out; reg out; always@ (sl or
a or b) if(!sl) out=a; else out=b; endmodule
RTL级语言为一种行为描述语言,只关心电路功能。
二、RTL建模
需要用到2选1数据选择器的门级电路图。
数据流建模,其标志多为并行的多个assign连续赋值语句,将对输入输出结点以及内部的各个结点的信号进行赋值定义操作。代码如下:
module muxtwo(out,a,b,sl); input a,b,sl; output out; wire
nsl,sela,selb;//定义内部结点 assign nsl= ~sl; assign sela= a & nsl; assign selb= b &
sl; assign out= sela | selb; endmodule
三、门级建模
该电路的门级图如上,两者相不同的是,数据流建模代码只需要电路图中的结点名称,门级建模代码不仅需要各结点名称,而且需要各个具体的门器件名称,以便例化出该名称的对应门,并且描述该所需门的输入输出。代码如下:
module muxtwo(out,a,b,sl); input a,b,sl; output out; wire nsl,sela,selb; not
u1(nsl,sl); //not表示的是Verilog语言中自带的非门,该语句表示例化一个叫u1的非门,其输入是sl,输出是nsl。 and #1
u2(sela,a,nsl); //#1表示的是该与门的输入到输出延迟1个时间单位。
//and表示的是例化调用Verilog语言中自带的与门,其输入是nsl和a,输出是sela。 and #1 u3(selb,b,sl); or #2
u4(out,sela,selb); //#2表示的是该或门的输入到输出延迟2个时间单位。
//or表示的是例化调用Verilog语言中自带的或门,其输入是sela和selb,输出是out。 endmodule
四、Verilog语法的主要特点之一——模块例化
模块例化是指,对低层次模块的调用。说白了就是在一个module......endmodule中需要使用其他的module......endmodule的功能,则通过调用模块名(调用方式见后)将该模块的功能移植到当前的主模块中。这个过程在主模块中只体现被调用(被例化)模块的输入和输出。使用原理就像C语言中的函数一样,调用时只关心函数的输入输出。同时,它其实是与C++中的
“类与对象”同出一辙。
1. 门的例化:在刚刚的门级建模语句中,已经出现了对与门,或门,非门的例化,在此先介绍各个门在Verilog库中的名称:
与门and
或门or
非门not
与非门nand
或非门nor
异或门xor
同或门xnor
缓冲器buf
具体的,当拥有这些门后,该如何在语句中运用呢?下面是门的声明使用格式:
<门类型> [延时] <门的例化名> (<输出1,输出2......>,<输入1,输入2......>);
如:xor g1 (out[j],i0[j],i1[j]); //延时可以省略。
2. 模块的例化:模块的例化方式主要有两种:
①顺序例化:
格式:<模块名> <例化名>(端口信号与被例化模块的定义内的端口顺序相接);
注:该种方式例化,若有不需连接的端口,需要跳过并空出该端口在模块定义中的位置,如:(<例化块的端口信号名1>,,<例化块的端口信号名3>),跳过了例化块的端口信号名2。
②名称例化:
格式:<模块名> <例化名>(.<被例化模块的端口名1>(<例化块的端口信号名1>),
.<被例化模块的端口名2>(<例化块的端口信号名2>),······);
例如,全加器 full_adder (a,b,c_out,sum); 的例化应用:
//顺序例化 full_adder m0 (a[0],b[0],c_out[0],sum[0]); full_adder m1
(a[1],b[1],c_out[1],sum[1]); full_adder m2 (a[2],b[2],c_out[2],sum[2]);
full_adder m3 (a[3],b[3],c_out[3],sum[3]); //名称例化 full_adder m0
(.a(a[0]),.b(b[0]),.c_out(c_out[0]),.sum(sum[0])); full_adder m1
(.a(a[1]),.b(b[1]),.c_out(c_out[1]),.sum(sum[1])); full_adder m2
(.a(a[2]),.b(b[2]),.c_out(c_out[2]),.sum(sum[2])); full_adder m3
(.a(a[3]),.b(b[3]),.c_out(c_out[3]),.sum(sum[3])); //两种例化方式结果等价
//若令a=[0110],即a[0]=0,a[1]=1,......,b=[1100],c_out=0,
//则最后的结果可推得为,sum[3]=0,sum[2]=0,sum[1]=1,sum[0]=0,c_out=1
五、testbench入门
testbench是一个模块的测试模块(激励块),就是给予所设计的模块一个或多个激励,测试所得到的结果是否满足所设定的功能。用来验证设计的模块的正确性。testbench为顶层模块,不会被其他模块例化,所以不需要有端口。
对于前面的二选一数据选择器,写一个testbench:
module test; reg a,b,sl; wire out; muxtwo mux(out,a,b,sl); //例化数据选择器模块 initial
begin a=0; b=1; sl=0; #5 b=0; #5 b=1; sl=1; #5 a=1; #5 $finish;//系统函数,表示结束当前仿真
end initial $monitor($time,"out=%b,a=%b,b=%b,sl=%b",out,a,b,sl);
//每次out,a,b,sl的值有变化时,打印out,a,b,sl的值。(系统函数monitor之后介绍) endmodule
其输出结果应该为:
timeoutabsl
00010
50000
101011
151111
20finishfinishfinishfinish