硬件行为的实现离不开条件语句和其他控制逻辑流的方式。Verilog有一套控制流块和机制来实现。
它包括:
这个条件语句用来决定是否应该执行某些语句。这与C语言中的if-else-if语句非常相似。如果表达式的值为真,那么第一条语句将被执行。如果表达式的值为false,并且如果存在一个else部分,那么else部分将被执行。
如果需要在if或者else部分内放置多条语句,则需要用 begin和end括起来。
// if statement without else part
if (expression)
[statement]
// Use "begin" and "end" blocks for more than 1 statements
if ([expression]) begin
Multiple statements
end
// if statment with an else part
if (expression)
[statement]
else
[statement]
// if else for multiple statements should be
// enclosed within "begin" and "end"
if (expression) begin
[multiple statements]
end
else begin
[multiple statements]
end
// if-else-if statement
if (expression)
[statement]
else if (expression)
[statement]
else
[statement]
// if-else-if style to check for more expressions if the previous one doesn't match
if ([expression 1])
Single statement
else if ([expression 2]) begin
Multiple Statements
end
else
Single statement
** 没有else的if**
if没有else部分,意味着对于任何不满足if内部表达式的条件,其值保持不变。
保持不变的硬件对应位锁存器,例:
module des ( input en,
input d,
output reg q);
always @ (*)
if (en)
q = d;
endmodule
vivado生成的RTL原理图为:
带有else的if
如果rstn为高电平,输出q将在时钟的上升沿得到输入d的值,并描述了D触发器的行为。
module dff ( input clk,
input rstn,
input d,
output reg q);
always @ (posedge clk) begin
if (! rstn)
q <= 0;
else
q <= d;
end
endmodule
这个例子中存在一个同步低电平复位,但从上图RTL原理图看出,仍然像是一个异步复位,因为复位信号的输出接到了触发器的rst端,这是什么情况呢?
在刚学习FPGA的时候我就遇到过这个一个问题,也就是综合的原理图(综合的情况应该和RTL原理图一致,只不过使用了某些器件的替换)和硬件语言描述的不一致问题。
这个问题,我看到过一个回答是:
这个回答貌似解决了这个疑问,仅供参考。
为了验证确实是同步复位,也可以通过行为仿真的方式进行验证,这个交给大家去做,由于篇幅有点大,所以赶时间。
if-else-fi
在下面的例子中,设计模块有一个4位输出q,当模式为1时递增,当模式为2时递减,使用if else结构。请注意,描述中没有说明如果模式为0或3时要做什么,这些都是2位变量的有效值。假设当模式为1和3时,电路什么都不做,但保持q的退出值,不建议在实际设计代码中留下这种含糊不清的地方,但在这里是为了强调这种可能性。
module des (
input [1:0] mode,
input clk,
input rstn,
output reg [3:0] q);
always @ (posedge clk) begin
if (! rstn)
q <= 0;
else begin
if (mode == 1)
q <= q + 1;
else if (mode == 2)
q <= q - 1;
end
end
endmodule
其原理图为:
如果综合库中有这些元件的话,上面显示的可以认为是综合的输出,值得注意的是,q被实现为一个4位的flop,它有一个CE引脚来使能触发器。值得注意的是,只有当模式为1或2时,这个flop才会被启用,而对于其他值则不会。输出q通过加减器块反馈到同一flop的输入端,通过mux,这也是由模式控制的。
注:综合的输出可能会随着给定技术库中器件的可用性而有所不同。
考虑与上面相同的设计,采用1位模式。
module des ( input mode,
input clk,
input rstn,
output reg [3:0] q);
always @ (posedge clk) begin
if (! rstn)
q <= 0;
else begin
if (mode)
q <= q + 1;
else
q <= q - 1;
end
end
endmodule
在这种情况下,一个没有CE引脚的普通触发器与几个多路复用器一起使用,根据模式的值来选择正确的信号。
RTL原理图为:
module tb;
integer a = 10;
initial begin
if (a == 10) // if block can have only one statement in it
$display ("a is found to be 10");
$display ("Always executed regardless of value of a"); // This statement is outside if block because
end
endmodule
仿真结果:
a is found to be 10
Always executed regardless of value of a
可见if执行后,继续执行if下面的语句。
其他的不再讨论。
循环提供了在一个块中执行单个或多个语句一次或多次的方法。在Verilog中,有四种不同类型的循环语句。
这将连续执行块内的语句,不可综合。
forever
[statement]
forever begin
[multiple statements]
end
module my_design;
initial begin
forever begin
$display ("This will be printed forever, simulation can hang ...");
end
end
endmodule
仿真结果:
This will be printed forever, simulation can hang ...
This will be printed forever, simulation can hang ...
...
...
This will be printed forever, simulation can hang ...
This will be printed forever, simulation can hang ...
This will be printed forever, simulation can hang ...
This will be printed forever, simulation can hang ...
Result reached the maximum of 5000 lines. Killing process.
这条语句常用于仿真中生成时钟信号,例如:
initial begin
clk = 0;
end
forever begin
#5 clk = ~clk;
end
这将执行固定次数的语句。如果表达式的值是X或Z,那么它将被视为零,根本不会被执行。不可综合。
repeat ([num_of_times]) begin
[statements]
end
repeat ([num_of_times]) @ ([some_event]) begin
[statements]
end
module my_design;
initial begin
repeat(4) begin
$display("This is a new iteration ...");
end
end
endmodule
仿真结果:
This is a new iteration ...
This is a new iteration ...
This is a new iteration ...
This is a new iteration ...
ncsim: *W,RNQUIE: Simulation is complete.
只要表达式为真,就会执行语句,一旦条件为假,就会退出。如果条件从一开始就是假的,则根本不会执行语句。不可综合。
while (expression) begin
[statements]
end
module my_design;
integer i = 5;
initial begin
while (i > 0) begin
$display ("Iteration#%0d", i);
i = i - 1;
end
end
endmodule
仿真结果:
Iteration#5
Iteration#4
Iteration#3
Iteration#2
Iteration#1
ncsim: *W,RNQUIE: Simulation is complete.
for循环是软件中使用最广泛的循环,但它主要用于复制Verilog中的硬件逻辑。for循环背后的思想是,只要给定的条件为真,就对循环内给定的一组语句进行迭代。这与while循环非常相似,但更多的是用在有迭代器的情况下,条件取决于这个迭代器的值。
for (; ; ) begin
// Statements
end
这将使用一个三步走的过程来控制语句。
或者:
注:这里的增加循环计算器变量不能受到C语言影响,使用i++或者++i之类的,要使用i = i + 1等。
初始条件和控制变量的更新都包含在for循环中,与while循环不同,不需要单独指定。while循环是更通用的,大多数情况下,只有当给定条件下的语句需要重复使用时才会使用。然而for循环的开头和结尾都是明确的,由步骤变量控制。
module my_design;
integer i = 5;
initial begin
for (i = 0; i < 5; i = i + 1) begin
$display ("Loop #%0d", i);
end
end
endmodule
仿真结果:
Loop #0
Loop #1
Loop #2
Loop #3
Loop #4
ncsim: *W,RNQUIE: Simulation is complete.
注:for循环使用得当,是可以综合的。
让我们看看在Verilog中如何在没有for循环的情况下实现8位左移位寄存器,然后与使用for循环的代码进行比较,以体会循环结构的效用。
module lshift_reg (input clk, // Clock input
input rstn, // Active low reset input
input [7:0] load_val, // Load value
input load_en, // Load enable
output reg [7:0] op); // Output register value
// At posedge of clock, if reset is low set output to 0
// If reset is high, load new value to op if load_en=1
// If reset is high, and load_en=0 shift register to left
always @ (posedge clk) begin
if (!rstn) begin
op <= 0;
end
else begin
if (load_en) begin
op <= load_val;
end
else begin
op[0] <= op[7];
op[1] <= op[0];
op[2] <= op[1];
op[3] <= op[2];
op[4] <= op[3];
op[5] <= op[4];
op[6] <= op[5];
op[7] <= op[6];
end
end
end
endmodule
同样的行为可以使用for循环来实现,这将减少代码,并使其对不同的寄存器宽度具有可扩展性。如果将寄存器的宽度作为Verilog参数,设计模块将变得可扩展,同样的参数可以在for循环里面使用。
module lshift_reg (input clk, // Clock input
input rstn, // Active low reset input
input [7:0] load_val, // Load value
input load_en, // Load enable
output reg [7:0] op); // Output register value
integer i;
// At posedge of clock, if reset is low set output to 0
// If reset is high, load new value to op if load_en=1
// If reset is high, and load_en=0 shift register to left
always @ (posedge clk) begin
if (!rstn) begin
op <= 0;
end
else begin
// If load_en is 1, load the value to op
// else keep shifting for every clock
if (load_en) begin
op <= load_val;
end
else begin
for (i = 0; i < 7; i = i + 1) begin
op[i+1] <= op[i];
end
op[0] <= op[7];
end
end
end
endmodule
可见,针对移位寄存器,使用for循环方便很多,但是下面这种写法会不会更好理解呢?
module lshift_reg (input clk, // Clock input
input rstn, // Active low reset input
input [7:0] load_val, // Load value
input load_en, // Load enable
output reg [7:0] op); // Output register value
integer i;
// At posedge of clock, if reset is low set output to 0
// If reset is high, load new value to op if load_en=1
// If reset is high, and load_en=0 shift register to left
always @ (posedge clk) begin
if (!rstn) begin
op <= 0;
end
else begin
// If load_en is 1, load the value to op
// else keep shifting for every clock
if (load_en) begin
op <= load_val;
end
else begin
op[0] <= op[7];
op[7:1] <= op[6:0];
/*
op <= {op[6:0],op[7]};
/*
end
end
end
endmodule
Verilog初级教程(15)Verilog中的阻塞与非阻塞语句
Verilog初级教程(14)Verilog中的赋值语句
Verilog初级教程(13)Verilog中的块语句
Verilog初级教程(12)Verilog中的generate块
Verilog初级教程(11)Verilog中的initial块
Verilog初级教程(10)Verilog的always块
Verilog初级教程(9)Verilog的运算符
Verilog初级教程(8)Verilog中的assign语句
Verilog初级教程(7)Verilog模块例化以及悬空端口的处理
Verilog初级教程(6)Verilog模块与端口
Verilog初级教程(5)Verilog中的多维数组和存储器
Verilog初级教程(4)Verilog中的标量与向量
Verilog初级教程(3)Verilog 数据类型
Verilog初级教程(2)Verilog HDL的初级语法
Verilog初级教程(1)认识 Verilog HDL
芯片设计抽象层及其设计风格
Verilog以及VHDL所倡导的的代码准则
FPGA/ASIC初学者应该学习Verilog还是VHDL?
ii-else-if
for-loop
verilog-control-block
移位寄存器
个人微信公众号: FPGA LAB
交个朋友