Verilog for 循环语句在Verilog 语法中,定义了多种循环语句,其中for是应用最广泛的一种语句,不仅可以在顺序语句中使用,在并发语句中也有相应的模型(generate)。for 循环语句可以用在实体模块中,也可以用在仿真模块中。在实体模块中for循环语句是可综合的。
- 顺序语句中使用for 循环操作
- for循环语句的格式
for(表达式1 ; 表达式2 ; 表达式3)
begin
语句1;
语句2;
...
语句n;
end
说明:如果只有一条语句,begin和end关键字可以省略。
一般在for循环中循环变量都使用integer类型。例如:
integer i; reg [15:0] a,b; for(i=0; i < 16; i = i + 1) a[ i ] = b [ i ];
for循环执行步骤如下:
step1: 求解表达式1;
step2: 求解表达式2,若其值为真(非0),则执行for语句中指定的内嵌语句,然后执行下面的第3步。若为假(0),则结束循环,转到第5步。
step3: 执行,在执行指定的语句后,求解表达式3。
step4: 返回,返回上面的第2步骤继续执行。
step5: 结束循环,执行for循环之后的语句。
例1 :利用for循环实现二进制到格雷码转换
十进制,二进制和格雷码的对照表:
| 十进制 | 二进制 | 格雷码 | 十进制格雷码 |
|---|---|---|---|
| 0 | 0000 | 0000 | 0 |
| 1 | 0001 | 0001 | 1 |
| 2 | 0010 | 0011 | 3 |
| 3 | 0011 | 0010 | 2 |
| 4 | 0100 | 0110 | 6 |
| 5 | 0101 | 0111 | 7 |
| 6 | 0110 | 0101 | 5 |
| 7 | 0111 | 0100 | 4 |
| 8 | 1000 | 1100 | 12 |
| 9 | 1001 | 1101 | 13 |
| 10 | 1010 | 1111 | 15 |
| 11 | 1011 | 1110 | 14 |
| 12 | 1100 | 1010 | 10 |
| 13 | 1101 | 1011 | 11 |
| 14 | 1110 | 1001 | 9 |
| 15 | 1111 | 1000 | 8 |
module binary2gray
(
input [3:0] a,
output [3:0] g
);
integer i;
function [3:0] gray(input [3:0] b);
begin
gray[3] = b[3];
for(i = 0; i < 3; i = i + 1)
gray[ i ] = b[ i ] ^ b[ i+1 ];//在function中使用for循环
end
endfunction
assign g = gray(a);
endmodule
仿真程序:
`timescale 1ns/1ps
module tb();
reg [3:0] ta;
wire [3:0] tg;
integer i;
initial begin
ta = 0;
#10
for(i = 0; i < 16; i = i + 1) begin
ta = i;
#10;
end
end
binary2gray binary2gray_inst
(
.a (ta),
.g (tg)
);
endmodule
Modelsim的仿真波形如图1:

图1
for循环也可以用在always过程中,甚至在时钟边沿驱动的always过程中。
例2:利用for循环实现4 x 4乘法器
module for_mult
(
input inclk,
input [3:0] a,b,
output reg [7:0] p
);
integer i;
always@(posedge inclk)
begin
p = 0;
for(i = 0; i < 4; i = i + 1)
begin
if(b[ i ])
p = p + (a << i);
end
end
endmodule
在时钟边沿驱动的always过程中,每次有上升沿的时候,for循环执行一遍。for循环的这种用法也是可综合的。当循环次数较多时,程序的编码效率较高。
仿真程序如下:
`timescale 1 ns / 1 ps
module tb_test
(
);
reg clk;
parameter PERIOD = 20;
initial
begin
clk = 1'b0;
#(PERIOD/2);
forever
#(PERIOD/2) clk = ~clk;
end
reg [3:0] ta = 0;
reg [3:0] tb = 0;
wire [7:0] tp;
always@(posedge clk)
begin
ta <= ta + 1;
if(ta == 15)
tb <= tb + 1;
end
for_mult for_mult_inst
(
.inclk (clk),
.a (ta),
.b (tb),
.p (tp)
);
endmodule
Modelsim仿真波形如图1:

图1
2.在并发过程中使用for循环
-
- 并发过程for循环语句的格式
genvar var; //定义循环变量 var
generate
for (var = 0; var < limit; var = var + 1)
begin: <label> //隐式例化
语句1;
…
语句 n;
end
endgenerate
其实在并发语句中的循环是由generate创建的,generate创建过程本身也是一个独立的语法结构,不仅能使用for循环,还能使用if,case等语法结构。可以认为在并发过程中,generate创建了顺序语句过程,因此if,case,for这些原本只能在顺序语句使用的语法结构,在这里也能使用。generate for还支持循环嵌套。
genvar var1, var2; //循环变量
generate
for (var1 = 0; var1 < limit; var1 = var1 + 1)
begin: <label_1>
for (var2 = 0; var2 < limit; var2 = var2 + 1)
begin: <label_2>
<code>
end
end
endgenerate
例:存储器初始化
reg [7:0] mem[0:255];
genvar i;
generate
for (i = 0; i < 256; i = i + 1) begin
initial begin
mem[ i ] = 8'b0;
end
end
endgenerate
generate for 与普通的for循环不同点
- generate for 每次循环都会生成一个例化,虽然没有明确指出例化过程,但例化结果是真实存在的。如果在generate 过成中使用always过程 ,generate 生成的for循环,always只能在for循环之内;而一般for循环,for在always之内。如:
reg [3:0] temp;
genvar i;
generate
for (i = 0; i < 3 ; i = i + 1)
begin: my_gen
// begin
always @(posedge sysclk)
begin
temp[ i ] <= 1'b0;
end
end
endgenerate
更详细的能容,在generate过程中描述。
作业题:在例2仿真程序中有如下语句:
always@(posedge clk)
begin
ta <= ta + 1;
if(ta == 15)
tb <= tb + 1;
end
如果替换成的语句如下:
always@(posedge clk)
begin
ta = ta + 1;
if(ta == 15)
tb = tb + 1;
end
请比较两段代码的结果有何不同?
练习题:
完善下面的代码,实现输入0,1位数的统计,并在输出端最高位加奇校验。b[7:0] 为a中1的个数统计,b[8] 为b[7:0] 的奇校验。
module statistic_parity
(
input [253:0] a,
output [8:0] b
)
endmodule
对应视频:
作业题:换完后的ta比换之前的ta晚一个clk
建议仿真观察结果,再思考为什么。
好的 谢谢老师
那个存储器初始化的例子在quartus编译不过,那个generate for后面的begin需要加个:名称,否则会报下面的错误:Error (10644): Verilog HDL error at test_generate.v(14): this block requires a name
这个取决于IDE软件(Vivado,Quartus等),但是最好每次都在generate for中加上 begin:label
老师好,我想问下initial 顺序块的语句是不是只执行一次,下面两个地方变量的初始化效果是不是一样的?
tb文件中:
……
reg clk;
reg [3:0] ta = 0; //1
reg [3:0] tb = 0;
wire [7:0] tp;
initial
begin
clk = 0;
// ta = 0; //2
// tb = 0;
#10
forever
#10 clk = ~clk;
end
initial块里的语句确实只执行一次。当前的两种写法没有区别。
但是initial块在仿真时可以加延时。加上延时后的initial块比如:
例1:
initial
begin
#10 ta = 0;
#20 tb = 0;
end
例1 中的ta 是赋值; 声明时候的 reg [3:0] ta = 0; 是赋初值。两者是有区别的。
老师好,那个乘法器算法,我试着用generate for写了下,仿真时候发现乘数和被乘数都正常循环,但乘法结果都是输出为0,不知道哪里问题?
设计文件:
module test_generate(
input [3:0] a,
input [3:0] b,
output [7:0] p
);
reg [7:0] p_temp[0:3];
genvar i;
generate
for(i = 0; i < 4; i = i + 1)
begin:test
initial begin
if(b[i])
p_temp[i] = a << i;
else
p_temp[i] = 8'b0;
end
end
endgenerate
assign p = p_temp[0] + p_temp[1] + p_temp[2] + p_temp[3];
endmodule
这里generate和initial用法没有熟练掌握,修改后的工程如下。具体修改会在视频中讲解。
module test_generate(
input [3:0] a,
input [3:0] b,
output [7:0] p
);
reg [7:0] p_temp[0:3] ;
initial
begin
p_temp[0] = 0;
p_temp[1] = 0;
p_temp[2] = 0;
p_temp[3] = 0;
end
integer i;
always @ (*)
for(i = 0; i < 4; i = i + 1) begin:test //initial begin if(b[i]) p_temp[i] = a << i; else p_temp[i] = 8'b0; //end end assign p = p_temp[0] + p_temp[1] + p_temp[2] + p_temp[3]; endmodule
老师,例2中,for(i=0;i<4;i=i+1)begin
if(b[i])
p=p+(a<<i);
end
a<<i,如果a是1010,i是2,a<<2得到的是1000,最高两位舍弃了,这样的到的结果理论上是不对的。就是这块的过程一直不明白,仿真结果是对的。求老师解答。
见视频解答Verilog问题答疑(14)RISC-V中有符号位扩展,异步复位,同步释放–视频教程