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 。




免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2026 CODEPRJ.COM