verilog语法实例学习(2)
Verilog中的信号类型
线网类型
线网类型表示一个或多个门或者其它类型的信号源驱动的硬件连线。如果没有驱动源,则线网的默认值为z。verilog中定义的线网类型有以下几种: wire,tri,wor,trior,wand,triand,trireg,tri1,tri0,supply0,supply1。其中最主要的是wire/tri,其它的类型都是综合中用不到的线网。
wire线网用来连接线路中一个逻辑模块的输出和另一个逻辑模块的输入,通常用来表示单个门驱动或连续赋值语句驱动的连线型数据。下面是wire类型的声明例子。
wire x;
wire [3:0] s;
wire [2:1] s1; //范围可以不从0开始
tri类型线网表示电路的连接以三态方式进行。它和wire是等价的,它只是用来提高三态门代码的可读性。例如:
tri z;
tri [7:0] out;
比如下面的三态门代码,它们分析与综合后,是相同的逻辑电路。
module trigate1(in,en,out); input in; input en; output out; wire out; assign out = en?in:'bz; endmodule module trigate2(in,en,out); input in; input en; output out; wire out; bufif1(out,in,en);//或者可以用bufif1 mybufif1(out,in,en); endmodule module trigate3(in,en,out); input in; input en; output out; tri out; assign out = en?in:'bz; endmodule module trigate4(in,en,out); input in; input en; output out; tri out; bufif1(out,in,en);//或者可以用bufif1 mybufif1(out,in,en); endmodule
在quartus中分析与综合上面代码中的四个模块,用rtl view看到的逻辑电路如下所示:trigate1, trigate2,trigate3,trigate4,都是对应形同的三态门。
用下面的testbench代码,从波形中我们可以看到这四个模块都实现三态门的逻辑。
`timescale 1ns/1ns module trigate_tb; reg in1,in2,in3,in4; reg en; wire out1, out2; tri out3,out4; trigate1 trigate1_0(.in(in1),.en(en),.out(out1)); trigate2 trigate2_0(.in(in2),.en(en),.out(out2)); trigate3 trigate3_0(.in(in3),.en(en),.out(out3)); trigate4 trigate4_0(.in(in4),.en(en),.out(out4)); initial begin en = 1'b0; in1 = 1'b1; #10 in2 = 1'b0; #10 in3 = 1'b1; #10 in4 = 1'b0; //en =0, out1/out2/out3/out4 都为高阻态 #10 en = 1'b1; //此时,out1=in1, ...,out4=in4 #10; $stop; end endmodule
当一个线网有多个驱动源时,即对一个线网有多个赋值时,不同的线网产生不同的行为。例如:
wire r;
assign r = a & b;
assign r = c | d;
r有两个驱动源,由于它是线网,r的有效值由使用驱动源的值的线表决定(下面的wire/tri表),如果如上图所示,则r为x。
如果有多个驱动源,线网的有效值如下表:可以看到只要输入端有x,输出端一定为x,而z状态是最弱的,只要有其它状态都能改变输出值。输入为0和1,输出为x。
wire/tri | 0 | 1 | x | z |
0 | 0 | x | x | 0 |
1 | x | 1 | x | 1 |
x | x | x | x | x |
z | 0 | 1 | x | z |
备注:在quartus+modelsim中,这样代码不能通过编译的,所以应该是不可综合的,只能在testbench中使用。
Error (10028): Can't resolve multiple constant drivers for net "r" at mulassign.v(7),。
module mulassign(a,b,r); input a,b; output r; wire r; assign r = a; assign r = b; endmodule
直接把module包含在testbench中,用vcs编译的。然后run simv,得到下面结果,和上面表中的值是匹配的:
Compiler version M-2017.03-SP2-11; Runtime version M-2017.03-SP2-11; Dec 27 16:10 2018
0 0 0 final result 0}
10 1 1 final result 1}
20 0 1 final result x}
30 1 0 final result x}
40 z z final result z}
50 x 1 final result x}
60 x z final result x}
70 0 x final result x}
`timescale 1ns/1ns `include "mulassign.v" module mulassign_tb; reg a,b,c,d; wire r; mulassign mulassign_0(.a(a),.b(b),.c(c),.d(d),.r(r)); initial begin a = 1'b0;b=1'b0; c=1'b0; d= 1'b0; // 0 0 #10 a = 1'b1;b=1'b1; c=1'b0; d= 1'b1; //1,1 #10 a = 1'b0;b=1'b1; c=1'b0; d= 1'b1;//0 1 #10 a = 1'b1;b=1'b1; c=1'b0; d= 1'b0;//1,0 #10 a = 1'bz;b=1'bz; c=1'bz; d= 1'bz; #10 a = 1'bx;b=1'b1; c=1'b0; d= 1'b1; #10 a = 1'b0;b=1'b1; c=1'bx; d= 1'b1; #10 a = 1'b0;b=1'b1; c=1'bz; d= 1'b1; end initial begin $monitor($time,,,"%b & %b, %b | %b, final result %b}", a, b, c,d,r); #400 $finish; end initial begin $dumpfile("dump.vcd"); $dumpvars; end endmodule;
wor和trior线网:对于wor如果某个驱动源为1,那么线网的值也是1,wor和三态或(trior)在语法和功能上是一致的。如果多个驱动源驱动这类网,网的有效值由下表决定:
wor/trior | 0 | 1 | x | z |
0 | 0 | 1 | x | 0 |
1 | 1 | 1 | 1 | 1 |
x | x | 1 | x | x |
z | 0 | 1 | x | z |
module mulassign1(a,b,r);
input a,b;
output r;
wor r;
assign r = a;
assign r = b;
endmodule
`timescale 1ns/1ns `include "mulassign1.v" module mulassign_tb; reg a,b; wire r; mulassign1 mulassign1_0(.a(a),.b(b),.r(r)); initial begin a = 1'b0;b=1'b0; #10 a = 1'b1;b=1'b1; #10 a = 1'b0;b=1'b1; #10 a = 1'b1;b=1'b0; #10 a = 1'bz;b=1'bz; #10 a = 1'bx;b=1'b1; #10 a = 1'bx;b=1'bz; #10 a = 1'b0;b=1'bx; end initial begin $monitor($time,,,"%b %b final result %b}", a, b,r); #400 $finish; end initial begin $dumpfile("dump.vcd"); $dumpvars; end endmodule;
用上面的代码,用vcs编译后,运行simv可以得到如下的结果,可见是要某个信号源为1,则结果为1:
Compiler version M-2017.03-SP2-11; Runtime version M-2017.03-SP2-11; Dec 27 17:37 2018
0 0 0 final result 0}
10 1 1 final result 1}
20 0 1 final result 1}
30 1 0 final result 1}
40 z z final result z}
50 x 1 final result 1}
60 x z final result x}
70 0 x final result x}
$finish called from file "mulassign1_tb.v", line 31.
wand和triand线网,线与(wand)如果某个驱动源为0,那么线网的值为0。线与和三态线与(triand)网在语法和功能上是一致的。
wand/triand | 0 | 1 | x | z |
0 | 0 | 0 | 0 | 0 |
1 | 0 | 1 | x | 1 |
x | 0 | x | x | x |
z | 0 | 1 | x | z |
module mulassign2(a,b,r);
input a,b;
output r;
wand r;
assign r = a;
assign r = b;
endmodule
`timescale 1ns/1ns `include "mulassign2.v" module mulassign_tb; reg a,b; wire r; mulassign2 mulassign2_0(.a(a),.b(b),.r(r)); initial begin a = 1'b0;b=1'b0; #10 a = 1'b1;b=1'b1; #10 a = 1'b0;b=1'b1; #10 a = 1'b1;b=1'b0; #10 a = 1'bz;b=1'bz; #10 a = 1'bx;b=1'b1; #10 a = 1'bx;b=1'bz; #10 a = 1'b0;b=1'bx; end initial begin $monitor($time,,,"%b %b final result %b}", a, b,r); #400 $finish; end initial begin $dumpfile("dump.vcd"); $dumpvars; end endmodule;
用vcs编译后,运行simv,得到以下结果,可见只要有信号源为0,则结果为0:
Compiler version M-2017.03-SP2-11; Runtime version M-2017.03-SP2-11; Dec 27 17:47 2018
0 0 0 final result 0}
10 1 1 final result 1}
20 0 1 final result 0}
30 1 0 final result 0}
40 z z final result z}
50 x 1 final result x}
60 x z final result x}
70 0 x final result 0}
$finish called from file "mulassign2_tb.v", line 31.
trireg线网,此线网存储数值,并且用于电容节点建模。当三态寄存器(trireg)的所有驱动源都处于高阻态,即值为z时,三态寄存器线网保存以前的值。三态寄存器线网的缺省初始值为x。
module mulassign3(a,b,r);
input a,b;
output r;
trireg r;
assign r = a;
assign r = b;
endmodule
`timescale 1ns/1ns `include "mulassign3.v" module mulassign_tb; reg a,b; wire r; mulassign3 mulassign3_0(.a(a),.b(b),.r(r)); initial begin a = 1'b0;b=1'b0; #10 a = 1'b1;b=1'b1; #10 a = 1'bz;b=1'bz; #10 a = 1'b0;b=1'b0; #10 a = 1'bz;b=1'bz; #10 a = 1'b1;b=1'b0; #10 a = 1'bz;b=1'bz; #10 a = 1'b0;b=1'bx; end initial begin $monitor($time,,,"%b %b final result %b}", a, b,r); #400 $finish; end initial begin $dumpfile("dump.vcd"); $dumpvars; end endmodule;
用上面的代码,vcs编译后,运行simv,得到以下结果:
Compiler version M-2017.03-SP2-11; Runtime version M-2017.03-SP2-11; Dec 27 18:46 2018 0 0 0 final result 0} 10 1 1 final result 1} 20 z z final result 1} 30 0 0 final result 0} 40 z z final result 0} 50 1 0 final result x} 60 z z final result 0} 70 0 x final result x}
tri0和tri1线网,分别用来建模下拉电阻和上拉电阻。这类线网可用于线逻辑的建模,即线网有多于一个驱动源,tri0线网的特征是,若无驱动源驱动,它的值为0,tri1与其相反。
tri0/tri1 | 0 | 1 | x | z |
0 | 0 | x | x | 0 |
1 | x | 1 | x | 1 |
x | x | x | x | x |
z | 0 | 1 | x | 0(1) |
module mulassign(a,b,r); input a,b; output r; tri0 r; assign r = a; assign r = b; endmodule module mulassign1(a,b,r); input a,b; output r; tri1 r; assign r = a; assign r = b; endmodule
`timescale 1ns/1ns `include "mulassign4.v" module mulassign_tb; reg a,b; wire r,r1; mulassign mulassign_0(.a(a),.b(b),.r(r)); mulassign1 mulassign1_0(.a(a),.b(b),.r(r1)); initial begin a = 1'b0;b=1'b0; #10 a = 1'b1;b=1'b1; #10 a = 1'b0;b=1'b1; #10 a = 1'b1;b=1'b0; #10 a = 1'bz;b=1'bz; #10 a = 1'bx;b=1'b1; #10 a = 1'bx;b=1'bz; #10 a = 1'b0;b=1'bx; end initial begin $monitor($time,,,"%b %b final result %b %b}", a, b,r,r1); #400 $finish; end initial begin $dumpfile("dump.vcd"); $dumpvars; end endmodule;
上面的代码vcs编译后,运行simv得到以下结果:
Compiler version M-2017.03-SP2-11; Runtime version M-2017.03-SP2-11; Dec 27 19:09 2018
0 0 0 final result 0 0}
10 1 1 final result 1 1}
20 0 1 final result x x}
30 1 0 final result x x}
40 z z final result 0 1}
50 x 1 final result x x}
60 x z final result x x}
70 0 x final result x x}
supply0和supply1线网,supply0用于对地建模,即低电平0,supply1用于对电源建模,即高电平1。
如下例所示:
supply1 vdd;
supply0 gnd;
assign a=vdd; //connect to vdd
assign b=gnd; //Connectb to gnn
`timescale 1ns/1ns module mulassign_tb; wire a,b; supply1 vdd; supply0 gnd; assign a=vdd; //connect to vdd assign b=gnd; //Connectb to gnn initial begin $monitor($time,,,"%b %b }", a, b); #400 $finish; end initial begin $dumpfile("dump.vcd"); $dumpvars; end endmodule;
Compiler version M-2017.03-SP2-11; Runtime version M-2017.03-SP2-11; Dec 27 19:29 2018
0 1 0 }
$finish called from file "supply_tb.v", line 13.
未说明的线网,在Verilog HDL中,有可能不必声明某种线网类型,在这样的情况下,缺省线网类型为1位wire类型线网。可以使用`default_nettype编译器指令改变隐式线网说明,例如:`default_nettype wand,但是default_nettype不能是supply0/supply1。
向量和标量线网:在定义向量线网时可选用关键词scalared或vectored。如果一个线网定义时使用了关键词vectored,那么就不允许位选操作和部分选择该线网。如果没有定义关键词,缺省值为scalared。
下面的代码可以用vcs编译,运行simv,得到结果,好像vcs和modelsim中,使用vectored定义wire,这个不允许位选操作和部分选择该线网并不成立。
Compiler version M-2017.03-SP2-11; Runtime version M-2017.03-SP2-11; Dec 27 20:53 2018
0 10101010 010z }
$finish called from file "wirev_tb.v", line 12
`timescale 1ns/1ns module mulassign_tb; wire vectored [7:0] a; wire vectored [3:0] b; assign a=8'b10101010; assign b[3:1]=a[4:2]; initial begin $monitor($time,,,"%b %b }", a, b); #400 $finish; end initial begin $dumpfile("dump.vcd"); $dumpvars; end endmodule;
变量类型
线网提供了一种逻辑元件互联的方式,但线网不能以行为的方式来描述电路,保持电路中间的信号值。为了达到这个目的,verilog提供了变量,可以在一条verilog语句中给一个变量赋值,这个变量会一直保持这一个值,直到被随后的赋值语句覆盖。变量的类型以下几种:reg和integer,time, real/realtime
integer i;
reg[2:0] count;
…
count=0;
for(i=0;i<4;i=i+1)
count = count +1
上面的代码中count是电路行为级模型,所以不能用wire,只能用reg。注意:integer主要用来控制循环变量,本身并不对应电路的结点。
integer可以作为普通reg类型使用,典型应用为高层次行为建模。使用整数型形式说明如下:
integer integer1,integer2,...,integerN[msb:lsb];
例如:
integer A,B,C; //表示三个integer类型变量
integer Hist [3:6]; //一组四个integer类型变量。
一个整数型寄存器可存储有符号数,并且算术操作符提供2的补码运算结果。整数不能作为位向量访问。一种截取位值的方法是将整数赋值给一般的reg类型变量,然后从中选取相应的位。
================
reg [31:0] Breg;
integer Bint;
Breg = Bint; //直接访问Bint[6]和Bint[20:10]是不允许的,但是Breg[6]和Breg[20:10]是允许的了。
time类型的寄存器用于存储和处理时间, time是一个无符号整数变量,位宽是64位。例如:time Events [0:31]; //时间值数组
time CurrTime; //currtime存储一个时间值。
real和realtime类型,real是有符号的浮点数,双精度。real寄存器使用如下:
real real_reg 1, real_reg2, . . ., real_regN;
real和realtime类型和使用完全相同。real说明的变量的缺省值为0,不允许对real声明值域、位界限或字节界限。real类型和reg类型没有一一对应的关系。
下面是time和real/real time的实例代码,从结果中可以看出time和real/realtime的区别,time只显示10ns倍数的整数,而realtime和real则可以显示精确的时间。
Compiler version M-2017.03-SP2-11; Runtime version M-2017.03-SP2-11; Dec 26 09:14 2018
t1 = 0, t2 = 0.000000, t3 = 0.000000, set = 0
t1 = 2, t2 = 1.600000, t3 = 1.600000, set = 1
t1 = 3, t2 = 3.200000, t3 = 3.200000, set = 2
V C S S i m u l a t i o n R e p o r t
`timescale 10ns/1ns module test; reg [7:0] set; time t1; realtime t2; real t3; parameter p=1.6; initial begin set = 0; t1 = $time; t2 = $realtime; t3 = $realtime; $display("t1 = %d, t2 = %f, t3 = %f, set = %d",t1,t2,t3,set); #p set=1; t1 = $time; t2 = $realtime; t3 = $realtime; $display("t1 = %d, t2 = %f, t3 = %f, set = %d",t1,t2,t3,set); #p set=2; t1 = $time; t2 = $realtime; t3 = $realtime; $display("t1 = %d, t2 = %f, t3 = %f, set = %d",t1,t2,t3,set); end endmodule
有符号数
Verilog中,线网和变量都可以声明成有符号数(signed),其中integer类型总是有符号的。例如:reg signed [15:0] a; 我们来看看下面的代码:
reg signed [7:0] a, b;
reg signed [3:0] c;
reg signed [7:0] sum1, sum4;
// same width. can be applied to signed and unsigned
sum1 = a + b;
// automatic sign extension
sum4 = a + c;
第一条语句将引用一个常规的加法器,因为a、b和sum1具有相同的位宽。
第二条语句,所有的右手边变量都具有signed数据类型,c被自动扩展符号位到8位。因此,无需再手动添加符号位。
在小型的数字系统中,我们通常可以选用有符号数或者无符号数。然而,在一些大型的系统中,会包括不同形式的子系统。Verilog是一种弱类型语言。无符号变量和有符号变量可以在同一表达式中混用。根据Verilog的标准,只有当所有右手边的变量具有signed数据类型属性的时候,扩展符号位才被执行。否则,所有的变量都只扩展0。考虑下面的代码片段:
reg signed [7:0] a, sum;
reg signed [3:0] b;
reg [3:0] c;
sum = a + b + c;
由于c不具有signed数据类型属性,因此右手边的变量b和c的扩展位为0。
Verilog有两个系统函数,$signed和$unsigned(),用以将括号内的表达式转换为signed和unsigned数据类型。比方说,我们可以转换c的数据类型,
sum = a + b + $signed(c);
现在,右手边的所有变量都具有signed数据类型属性,因此b和c将扩展符号位。
在复杂的表达式中,混用signed和unsigned数据类型将引入一些微妙的错误,因此应当避免混用。如果真的很有必要,那么表达式需要保持简单,同时通用转换函数,以确保数据类型的一致性。
数组
Verilog中,wire和reg类型都可以声明为多维数组,形式和c语言多维数组很像。例如:
reg [7:0] a[0:1023][0:511];
wire和reg类型的区别
wire表示直通,即输入有变化,输出马上无条件地反映(如与、非门的简单连接)。reg表示一定要有触发,输出才会反映输入的状态。reg相当于存储单元,wire相当于物理连线。
reg表示一定要有触发,没有输入的时候可以保持原来的值,但不直接和实际的硬件电路对应。两者的区别是:寄存器型数据保持最后一次的赋值,而线网型数据需要持续的驱动。wire使用在连续赋值语句中,而reg使用在过程赋值语句(initial ,always)中。wire若无驱动器连接,其值为z,reg默认初始值为不定值 x 。