Verilog语法:for循环的使用心得_verilog for
在硬件设计中,往往会遇到大量相同电路的设计需求。在这种情况下,如果能够总结出这些电路的一般规律,使用for语句不仅可以减少代码量(如果代码量是工作量的话,不推荐使用for语句),并且在一定程度上提高可读性。
在硬件电路中,for循环的本质是电路的复制(for语句的理解,可以把辅助变量i代入,把所有情况列举出来)。当然,在仿真的时候也可以当成类似C语言的循环体来用。设计时,for语句是空间维度上的;仿真时,for语句是时间维度上的。
Verilog中的for循环有两种用法,第一种是放在always块外面,用generate for来生成大量的always块;第二种则是放在always块的里面,用来给信号进行大量的赋值。下面有一些常见的示意用法。
1 generate for用法
1.1 always用法,时序和组合皆适用
output [31:0]dout;reg [7:0]data[0:3];//索引为0~3的reg数组,元素为8bitgenvar i;//辅助电路生成的变量,32bitgenerate for(i=0;i<4;i=i+1) begin: inst_name always@(posedge clk or negedge rst_n) if(!rst_n) data[i] <= 8\'d0; else if(in_valid) data[i] <= data[i] + 8\'d1; endendgenerate assign dout = {data[3],data[2],data[1],data[0]};
该写法相当于产生了4组完全相同的电路,i只是起到了辅助的作用,生成的示意图如下。
1.2 assign用法
input [31:0]din; output [31:0]dout; wire [7:0]data[0:3]; //索引为0~3的wire数组,元素为8bitgenvar i;generate for(i=0;i<4;i=i+1) begin: inst_name assign data[i] = din[8*i+7 : 8*i] + 8\'d1; endendgenerate assign dout = {data[3],data[2],data[1],data[0]};
该写法同样对assign语句有效,即对任意组合逻辑电路同样生效。在verilog的语法规则中,类似于wire [7:0]data[0:3]或者reg [7:0]data[0:3]的这种申明不能出现在input和output的端口申明中。示意图如下。
1.3 模块的例化用法
input [31:0]a;input [31:0]b;output [35:0]dout;wire [8:0]a_b[0:3];//索引为0~3的wire数组,元素为8bit genvar i;generate for(i=0;i<4;i=i+1) begin: inst_name add add_inst( .clk(clk), .rst_n(rst_n), .din1(a[8*i+7 : 8*i]), //8bit .din2(b[8*i+7 : 8*i]), //8bit .dout(a_b[i])//9bit ); end assign dout = {a_b[3],a_b[2],a_b[1],a_b[0]};
generate for也可以用来进行大量有规律的模块例化,示意图如下。
2 放在always内部使用,适用于组合和时序逻辑
放在always内部使用时,辅助变量的i的申明不能用genvar,而应该使用integer i;。
input [7:0]din;output [7:0]dout3,dout2,dout1,dout0;reg [7:0]r_data[0:3];//索引为0~3的reg数组,元素为8bitinteger i;always@(posedge clk or negedge rst_n)if(!rst_n) for(i=0;i<4;i=i+1)begin r_data[i] <= 8\'d0; endelse begin r_data[0] <= din; for(i=1;i<4;i=i+1)begin r_data[i] <= r_data[i-1]; endendassign dout0 = r_data[0];assign dout1 = r_data[1];assign dout2 = r_data[2];assign dout3 = r_data[3];
用for语句生成了一个多bit信号的打拍器,一共打了4拍,示意图如下所示。
3 类似C语言的循环用法,主要用于仿真中
module add_tb; reg [7:0]din1; reg [7:0]din2; wire [8:0]dout; add add_inst( .din1(din1),//8bit .din2(din2),//8bit .dout(dout)//9bit ); integer seed; initial seed = $time; integer i=0; initial begin din1 = 0; din2 = 0; #20; for(i=0;i<16;i=i+1)begin din1 = i; //din1 = i din2 = {$random(seed)} % 256; //生成随机数(0~255) #10; end $stop; end endmodule
根据for循环生成了16组din1和din2数据,用于仿真中,仿真图如下图。
再举一个常见的for循环用于仿真的例子,生成串口的波形图的例子。
task Gen_Uart_Rx;input [7:0]byte;integer i;beginuart_rx = 1\'b0;#8680;//115200for(i=0;i<8;i=i+1)beginuart_rx = byte[i]; //利用for循环从低到高生成uart图形#8680;enduart_rx = 1\'b1;#8680;endendtaskinitial begin uart_rx = 1; Rx_Done = 0; #10; Gen_Uart_Rx(8\'h55); //调用task Rx_Done = 1\'b1; #10 Rx_Done = 1\'b0; Gen_Uart_Rx(8\'hb9); //调用task Rx_Done = 1\'b1; #10 Rx_Done = 1\'b0; #100; $stop;end
生成的图形如下所示。
这就是Verilog的for循环常见的用法,如果还有别的什么用法,请在评论区进行补充。