verilog語法實例學習(1)


    本文檔中通過verilog實例來學習verilog語法。Verilog是一種硬件描述語言,它具有並發性和時序性。並發性是指不同硬件模塊的同時操作,時序性是指信號的賦值或操作在時鍾的邊沿進行。由於作者本身也是一個初學者,所以盡量用簡單明了的例子介紹Verilog語法。

Verilog中的注釋

      Verilog代碼中的注釋和c++語言相同,分為短注釋(//)和長注釋(/* … */)。短注釋通常放在每行代碼的后面或上面,用來注釋這行代碼的功能。長注釋一般在module的開始處,用來說明模塊的功能。比如下面四位全加器代碼中的注釋。

/*
  通過實例化全加器模塊實現四位加法的功能。
  輸入:cin,進位
       x, y 被加數和加數
       s 和
       cout 進位
*/
module adder4(cin, x, y,s,cout);

  input cin;
  input [3:0] x;
  input [3:0] y;

  output [3:0] s;
  output cout;
  wire [3:1] c; //內部線網類型信號c,用來存儲串行進位

  fulladd stage0(.cin(cin),.x(x[0]),.y(y[0]),.s(s[0]),.cout(c[1]));
  fulladd stage1(.cin(c[1]),.x(x[1]),.y(y[1]),.s(s[1]),.cout(c[2]));
  fulladd stage2(.cin(c[2]),.x(x[2]),.y(y[2]),.s(s[2]),.cout(c[3]));
  fulladd stage3(.cin(c[3]),.x(x[3]),.y(y[3]),.s(s[3]),.cout(cout));

endmodule

Verilog中的信號

      Verilog中,電路里面的一個信號就代表一個特定類型的線網(net)或變量。這里線網指的兩個或更多電路結點的相互連接。

      一個線網或變量的聲明格式如下:

      type [range] signal_name{,signal_name};

      方括號中range(范圍)是可選的,如果沒有指定范圍,默認情況下表示該信號是標量,是只有一位的單位信號。大括號中表示允許加入的條目,也就是說一個在一行里面可以聲明多個線網或變量。

      范圍表示為[Ra:Rb]的形式,它定義了矢量信號的范圍。范圍[Ra:Rb]可以增大或減小。在任何情況下,Ra都表示一個矢量信號的最高有效位(最左邊),Rb表示一個矢量信號的最低有效位(最右邊)。Ra和Rb可以是正整數和負整數。

比如:

   wire [7:0] x1,x2;

   wire [3:1] Array;

   reg y1,y2;   //y1和y2沒有指定范圍,只有一位。

標識符

      在verilog中,線網或變量名字是通過標識符來表示的。標識符是由一些字母,數字,下划線或美元符號組成。但必須注意兩點:標識符不能以數字開頭,也不能使用verilog中的關鍵字。Verilog標識符中也可以出現轉義字符,比如 \abc,如果用轉義字符,則可以用特殊字符,比如\*cd,但是通常寫代碼過程中,不要用轉義字符來定義變量,這種轉義字符通常是其它語言用工具翻譯成verilog語言時自動插入的。另外注意的一點就是verilog是區分大小寫的,m和M是兩個不同的變量

合法的標識符:

f, x, x1, x_y, Byte

非法的標識符:

1x, 100, x*y,default(verilog關鍵字

      Verilog中一些系統任務或函數以$開頭,比如$display, $fopen, $monitor等等。Verilog中還定義一些編譯指示,以`開頭,比如:`timescale 1ns/1ns,定義時間單位和時間精度。

信號的值

Verilog支持單位信號表示的標量(一位信號)或者矢量(多位信號),每個單位信號有四種可能的值:0,1,z(或者Z),x(或者X)。

0, 邏輯值0

1, 邏輯值1,

在正邏輯系統中,邏輯值0表示低電平,邏輯值1表示高電平。負邏輯系統中正好相反。在教程中,我們總是用正邏輯系統。


clip_image002

      我們用閾值電壓來表示電平的邏輯值。 在上圖中,最低電壓為 Vss,表示接地。VDD表示最高電壓,表示接電源,通常電源在5v到1v之間。VDD~V1,min之間的范圍對應邏輯值1,V0,max~Vss之間的范圍對應邏輯值0。典型的V1,min 是60%Vdd,V0,max是40%Vdd

    X, 通常表示多驅動值的沖突,為未初始化的值或者兩個反相信號(如0和1)的短接。Verilog中線網和變量的初始值都為X。

clip_image002[6]

Z, 通常表示未驅動的高阻值,此時在電源和地之間沒有電流流動。比如在三態門中,E=0,則輸出為Z。

clip_image002[8]

Verilog中的數字

信號值如果為標量,則為一位的0,1,x或z。例如:

a = 1’b0; b = 1’b1;

c = x; d = z;

      對於矢量,通常表示為:[size]’[radix]constant, 標量也可以當作size=1的矢量。其中size表示常數的位數,radix為常數的基數。如果沒有指定基數,則默認為十進制。Verilog支持的基數有:d(十進制),b(二進制),h(十六進制),o(八進制)。如果定義的位數大於所需要的位數時,通常在前面補0,但是如果最高位是x或z時候,就用x和z來填充。在常量表示中間,可以任意插入下划線,把數字隔開,以方便閱讀。可以在基數前面加s(或者S)以表示這是一個有符號數,若未指定一個整數的進制類型,則默認是有符號數,若指定了進制類型,僅當基數前有s,則其為有符號數,否則為無符號數。對於有符號數,如果定義的位數大於所需要的位數時,前面補最高有效位(MSB),如下表中的8’sha9。

下面是一些實例:

數字表示

二進制

注釋

reg [31:0] a = 8'sha9

‘11111111111111111111111110101001

8位有符號數,所以進行MSB位擴展,

reg [31:0] b = 4'd5

’00000000000000000000000000000101

4位無符號數,高位補0

reg [31:0] c = 12'h5b_3

‘00000000000000000000010110110011

忽略下划線,12位的無符號數,高位補0

reg [31:0] d = -8'b101

’11111111111111111111111111111011

負數轉為補碼表示,MSB位擴展到32位

reg [31:0] e= 10'o752

‘00000000000000000000000111101010

8進制無符號數,高位補0

reg [31:0] f = 8'hf

’00000000000000000000000000001111

16進制無符號數,高位補0

reg [31:0] g = 12'hxa

‘00000000000000000000xxxxxxxx1010

左邊補x直到12位,然后高位補0

reg [31:0] h = -8'sha9

’00000000000000000000000001010111

補碼表示負數,擴展MSB位,

reg [31:0] i = 8'ha9

‘00000000000000000000000010101001

無符號數,高位補0

reg [31:0] j = -4'sha

’00000000000000000000000000000110

補碼表示負數,擴展MSB位。

實現這些實例的verilog代碼為:

`timescale 1ns/1ns
module verilogdisnum;
  reg [31:0] a = 8'sha9;
  initial $displayb("a=",a);
  reg [31:0] b = 4'd5;
  initial $displayb("b=",b);
  reg [31:0] c = 4'h5b_3;
  initial $displayb("c=",c);
  reg [31:0] d = -8'b101;
  initial $displayb("d=",d);
  reg [31:0] e= 10'o752;
  initial $displayb("e=",e);
  reg [31:0] f = 8'hf;
  initial $displayb("f=",f);
  reg [31:0] g = 12'hxa;
  initial $displayb("g=",g);
  reg [31:0] h = -8'sha9;
  initial $displayb("h=",h);
  reg [31:0] i = 8'ha9;
  initial $displayb("i=",i);
  reg [31:0] j = -4'sha;
  initial  $displayb("j=",j);
endmodule

Verilog中的參數

參數由一個標識符和一個常數組成。比如:

parameter n=4;

parameter S0=2’b00; S1=2’b01;S2=2’b10;S3=2’b11;

      標識符n可以在代碼中替換表示數字4的地方,S0則可以替換數值2’b00。參數的主要作用是指定參數化的子電路。比如下面的代碼中,可以看到代碼通過parameter定義了一個常量n,實例化時候通過addern #(.n(32)) addern_0(…)的形式把32傳入到參數n中,從而實現32位的加法操作。另外一種調用參數的方法是用defparam,比如下面的例子:

addern addern_0(…)

defparam addern_0.n=32;

但這種方法是不可綜合的,通常只是用在testbench當中

module addern(x, y, s, cout);
  parameter n=8;
  input [n-1:0] x;
  input [n-1:0] y;
  output reg[n-1:0] s;
  output reg cout;
  reg [n:0] c;
  integer k;
  always @(x,y) begin
    c[0] = 1'b0;
	 for(k = 0; k < n; k = k + 1) begin
	   s[k] = x[k]^y[k]^c[k];
		c[k+1] = (x[k]&y[k])|(x[k]&c[k])|(y[k]&c[k]);
	 end
	 cout = c[n];
    end
endmodule
`timescale 1ns/1ns
`define clock_period 20
module addern_tb;
  reg [7:0] x,y;
  wire cout;
  wire [7:0] s;
  reg clk;
  addern #(.n(32)) addern_0(
						.x(x),
						.y(y),
						.s(s),
						.cout(cout)
                  );
  initial clk = 0;
  always #(`clock_period/2) clk = ~clk;
  initial begin
     x = 0;
     repeat(20)
	    #(`clock_period) x = $random;
   end
  initial begin
     y = 0;
     repeat(20)
	    #(`clock_period) y = $random;
   end
  initial begin
     #(`clock_period*20)
	  $stop;
  end
endmodule


免責聲明!

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



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