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,都是对应形同的三态门。

imageimage

imageimage

用下面的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

image

当一个线网有多个驱动源时,即对一个线网有多个赋值时,不同的线网产生不同的行为。例如:

wire r;

assign r = a & b;

assign r = c | d;

clip_image002[6]

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 。



posted on 2018-12-27 09:49  迈克老狼2012  阅读(7798)  评论(0编辑  收藏  举报

导航