在C語言中,經常用到for循環語句,但在硬件描述語言中for語句的使用較C語言等軟件描述語言有較大的區別。
在Verilog中除了在Testbench(仿真測試激勵)中使用for循環語句外,在Testbench中for語句在生成激勵信號等方面使用較普遍,但在RTL級編碼中卻很少使用for循環語句。主要原因就是for循環會被綜合器展開為所有變量情況的執行語句,每個變量獨立占用寄存器資源,每條執行語句並不能有效地復用硬件邏輯資源,造成巨大的資源浪費。簡單的說就是:for語句循環幾次,就是將相同的電路復制幾次,因此循環次數越多,占用面積越大,綜合就越慢。
在RTL硬件描述中,遇到類似的算法,推薦的方法是先搞清楚設計的時序要求,做一個reg型計數器。在每個時鍾沿累加,並在每個時鍾沿判斷計數器情況,做相應的處理,能復用的處理模塊盡量復用,即使所有的操作不能復用,也采用case語句展開處理。
對於下面的for循環語句:
1 for(i=0;i<16;i++) 2 DoSomething();
可以采用如下代碼實現:
reg [3:0] counter; always @(posedge clk) if(syn_rst) counter<=4'b0;
else counter<=counter+1; always @(posedge clk) begin case(counter) 4'b0000:
4'b0001:
...... default: endcase end
另外,有幾個語法的細節需要注意一下。for(i=0;i<16;i=i+1)中的i既可以是reg型的變量也可以是integer類型的變量,但是當i是reg型的變量時,需要注意因為判斷語句i<16的緣故,i應定義為reg[4:0] i而不是reg[3:0] i 。由於verilog中沒有自增運算符,文中提到的for語句不能寫成for(i=0;i<16; i++)的形式。
下面簡單的列舉幾個用for實現的程序代碼:
示例一:
仿真結果如下:
仿真后的結果,由於采用了非阻塞賦值語句,所以每次在always借宿后才把值付給左邊的寄存器。
不過在使用了阻塞賦值語句后,得到了目的,但是由於for語句的綜合效率不高,且在時序邏輯中一般采用非阻塞賦值,因此最好不能這樣寫 ----轉自特權同學《深入淺出玩轉FPGA》
示例二:
for用在純組合邏輯中
舉例:4位左移器(將低4位輸入的數移到高4位)
1 //Leftshift for 4 bits
2 module For_Leftshift( 3 input wire [3:0]inp, 4 input wire L_EN, 5 output reg [7:0]result 6 ); 7
8 integer i; 9 always@(inp or L_EN) 10 begin 11 result[7:4] = 0; 12 result[3:0] = inp; 13 if(L_EN == 1) 14 begin 15 for(i=4;i<=7;i=i+1) 16 begin 17 result[i] = result[i-4]; 18 end 19 result[3:0] = 0; 20 end 21 end 22
23 endmodule
綜合結果(RTL視圖,實際是一個4位選擇器)
示例三:for不僅可以用在組合邏輯中,而且還可以用在時序邏輯中,用於在1個周期類完成整個for循環。
舉例:在一個周期類完成對輸入總線中高電平位的計數,則利用for循環實現加法器
1 module For_Counter( 2 input wire clk, 3 input wire rst_n, 4 input wire [12:0] data, 5 output wire [3:0] numout 6 ); 7 integer i; 8 reg[3:0] num; 9
10 always @(posedge clk) 11 begin 12 if(!rst_n) 13 num = 0; 14 else
15 begin 16 for(i=0;i<13;i=i+1) 17 if(data[i]) num = num + 1; 18 end 19 end 20
21 assign numout = num; 22
23 endmodule
綜合結果(RTL視圖,加法器+觸發器)
綜上,可以看出for循環是可以綜合的,而且效率很高。但所消耗的邏輯資源較大。在對速度(時鍾周期數)要求不是很高的情況下,可以多用幾個時鍾周期完成任務,而沒有必要用for循環來做。