程式扎記: [ Verilog Tutorial ] 行為模型的敘述: always, if/else, case 與 for loop

標籤

2013年11月17日 星期日

[ Verilog Tutorial ] 行為模型的敘述: always, if/else, case 與 for loop

Preface: 
在這個階層中,我們只需考慮電路模組的功能,而不需考慮其硬體的詳細內容. Verilog 的時序控制為以事件為基礎的時序控制: 
* 接線或暫存器的值被改變。
* 模組的輸入埠接收到新的值
* 正規事件控制:正緣、負緣、訊號值改變
* 多事件或訊號控制

always 敘述: 
always 敘述的觀念有如監督程式一般,隨時監看著輸出入埠訊號的變化,然後告知模組內部 進行相關的處理. 語法如下: 
  1. // Style1  
  2. always  
  3.     statement  
  4. // Style2  
  5. always@(event_expression)  
  6.     statement  
  7. // Style3  
  8. always  
  9.     begin  
  10.         statements  
  11.     end  
  12. // Style4  
  13. always@(event_expression)  
  14.     begin  
  15.         statements   
  16.     end  
always@ 敘述 
always@(…) 括弧內的運算式稱之為事件運算式 (event expression),其可以是: 
* 單一訊號: 屬於準位觸發 (level trigger)。
* 多個訊號: 利用 or 關鍵字連接不同訊號, 也屬於屬於準位觸發.
* 邊緣觸發: 正緣觸發/posedge 或 負緣觸發/negedge

接著來看 單一訊號範例: 
  1. module test(in, out);  
  2.     input in;  
  3.     output out;  
  4.     reg out;  
  5.     always@(in)  
  6.         out = ~ in;  
  7. endmodule  
實事上上面的模組就是一個 Not gate: 
 

接著來看 多個訊號的範例: 
  1. module test(a, b, out);  
  2.     input a, b;  
  3.     output out;  
  4.     reg out;  
  5.     always@(a or b)  
  6.     begin  
  7.         out = a | b;  
  8.     end  
  9. endmodule  
上面是一個 OR gate: 
 

接著來看 邊緣觸發之 "posedge" (在 clock cycle 的正緣觸發
  1. module test(a, ck, out);  
  2.     input a, ck;  
  3.     output out;  
  4.     reg out;  
  5.     always@(posedge ck)  
  6.     begin  
  7.         out = ~ a;  
  8.     end  
  9. endmodule  
上面的 NOT gate 只在 clock 正緣觸發: 
 

如果是負緣觸發, 則使用 always@(negedge ck), 輸出結果: 
 

if 敘述: 
可用來進行訊號值的判斷,後根據判斷結果執行相關處理. if 敘述能處理正準位與負準位觸發兩種訊號, 語法如下: 
  1. // Style1  
  2. if (expression)  
  3.     statement  
  4.   
  5. // Style2  
  6. if (expression)  
  7. begin  
  8.     statements  
  9. end  
接著來看 準位觸發 範例: 
  1. module test(reset, in, out);  
  2.     input reset, in;  
  3.     output out;  
  4.     reg out;  
  5.     always  
  6.     begin  
  7.         if (reset == 1’b1) // 正  
  8.             out = 0;  
  9.         if (reset == 1’b0) // 負  
  10.             out = in;  
  11.     end  
  12. endmodule  
當 reset 為 1, 則設置 out 為 0; 當 reset 為 0, 則 out 等於 in
 

if-else 敘述: 
語法如下: 
  1. // Style1  
  2. if (expression)  
  3.     statement  
  4. else statement  
  5.   
  6. // Style2  
  7. if (expression)  
  8. begin  
  9.     statements  
  10. end  
  11. else  
  12. begin  
  13.     statements  
  14. end  
接著來看一個範例: 優先權編碼器 (priority encoder) 部分代碼 
  1. always@(X, Y, Z)  
  2. begin  
  3.     if (Z) // highest  
  4.         out = result4;  
  5.     else if (Y)  
  6.         out = result3;  
  7.     else if (X)  
  8.         out = result2;  
  9.     else out = result1; // lowest  
  10. end  
case 敘述: 
case 敘述為一多路分支選擇的敘述, 如果電路中所有可能的分支判別條件都被指定了,則稱為 full case. 語法如下: 
  1. case (expression)  
  2.     alter_1, alter_2: stm_1;  
  3.     alter_3: stm_2;  
  4.     …  
  5.     default: default_stm;  
  6. endcase  
接著來看一個範例: 2X1 Multiplexor 
  1. module mux21(in1, in2, sl, out);  
  2.     input in1, in2, sl;  
  3.     output out;  
  4.     reg out;  
  5.     always@(in1 or in2 or sl)  
  6.     begin  
  7.         case (sl)  
  8.             1’b0: out = in1;  
  9.             1’b1: out = in2;  
  10.         endcase  
  11.     end  
  12. endmodule  
上面是一個 2x1 多工器, 輸出結果: 
 

接著來考慮一個優先編碼模組: 
  1. module priencoder(X, Y, Z, out);  
  2. input [1:0] X, Y, Z;  
  3. output [2:0] out;  
  4. reg [2:0] out;  
  5. always@(X or Y or Z)  
  6. begin  
  7.   case (2'b11)  
  8.     X: out = 3'b001;  
  9.     Y: out = 3'b010;  
  10.     Z: out = 3'b100;  
  11.     default: out = 3'b000;  
  12.   endcase  
  13. end  
  14. endmodule  
而它的一個 testbench 代碼如下: 
  1. `timescale 1ns / 1ns  
  2.   
  3. module priencoder_tb;  
  4.   reg  [1:0] X,Y,Z;  
  5.   wire [2:0] out;  
  6.   priencoder ENCODER(X,Y,Z,out);  
  7.     
  8.   initial  
  9.     begin  
  10.       X <= 2'b00;  
  11.       Y <= 2'b00;  
  12.       Z <= 2'b00;  
  13.       #1 $display("out=%b", out);  
  14.       X <= 2'b11;  
  15.       Y <= 2'b00;  
  16.       Z <= 2'b00;  
  17.       #5 $display("out=%b", out);  
  18.       X <= 2'b00;  
  19.       Y <= 2'b11;  
  20.       Z <= 2'b00;  
  21.       #5 $display("out=%b", out);  
  22.       X <= 2'b00;  
  23.       Y <= 2'b00;  
  24.       Z <= 2'b11;  
  25.       #5 $display("out=%b", out);  
  26.       X <= 2'b11;  
  27.       Y <= 2'b11;  
  28.       Z <= 2'b11;  
  29.       #5 $display("out=%b", out);  
  30.     end  
  31. endmodule  
執行結果為: 
# out=000 # 此時 X,Y,Z 沒一個是 2'b11, 故輸出 output 為 default
# out=001 # 此時 X 為 2'b11, 故輸出 001
# out=010 # 此時 Y 為 2'b11, 故輸出 010
# out=100 # 此時 Y 為 2'b11, 故輸出 100
# out=001 # 此時雖然 X,Y,Z 皆為 2'b11, 但是因為 case 順序 X 是第一個, 故輸出 001

Verilog 中還有 casex 與 casez 兩種 case 敘述, 更多可以參考 Case Statement

迴圈敘述 for: 
Verilog 提供有 for、while、repeat 和 forever 等迴圈敘述, 語法如下: 
  1. for (statement; expression; statement)  
  2. begin  
  3.     statements;  
  4. end  
Ps. 所有迴圈敘述僅能在 always 敘述中執行。 

接著來看一個範例: 
  1. module forloop(a, b, out);    
  2.   parameter size=2;  
  3.   input  [size-1:0] a, b;    
  4.   output [size-1:0] out;    
  5.   reg    [size-1:0] out;    
  6.   integer i;    
  7.   always @(a or b)    
  8.   begin    
  9.     for (i = 0;i < size; i = i + 1)    
  10.       out[i] = a[i] & b[i];    
  11.   end    
  12. endmodule   
這是一個 AND gate 的範例, 透過 for loop 對 a兩個 array 進行 AND 運算後輸出到 out. 底下是一個 testbench: 
  1. `timescale 1ns / 1ns  
  2.   
  3. module forloop_tb;  
  4.   reg  [1:0] a, b;  
  5.   wire [1:0] out;  
  6.   forloop FL(a, b, out);  
  7.     
  8.   initial  
  9.     begin  
  10.       a <= 2'b00;  
  11.       b <= 2'b00;  
  12.       #1 $display("a=%b; b=%b; out=%b", a, b, out);  
  13.       a <= 2'b01;  
  14.       b <= 2'b00;  
  15.       #1 $display("a=%b; b=%b; out=%b", a, b, out);  
  16.       a <= 2'b01;  
  17.       b <= 2'b01;  
  18.       #1 $display("a=%b; b=%b; out=%b", a, b, out);  
  19.       a <= 2'b10;  
  20.       b <= 2'b01;  
  21.       #1 $display("a=%b; b=%b; out=%b", a, b, out);  
  22.       a <= 2'b10;  
  23.       b <= 2'b10;  
  24.       #1 $display("a=%b; b=%b; out=%b", a, b, out);  
  25.       a <= 2'b11;  
  26.       b <= 2'b10;  
  27.       #1 $display("a=%b; b=%b; out=%b", a, b, out);  
  28.       a <= 2'b11;  
  29.       b <= 2'b11;  
  30.       #1 $display("a=%b; b=%b; out=%b", a, b, out);  
  31.     end  
  32. endmodule  
執行結果: 
 

Supplement: 
Verilog online help > Loop Statements 
Bioelectromagnetics Lab > Verilog tutorial

沒有留言:

張貼留言

網誌存檔

關於我自己

我的相片
Where there is a will, there is a way!