在这篇文章中,我们讨论了可以在verilog中使用的不同类型的循环 - for循环,while循环,forever循环和repeat循环。
正如我们在上一篇关于 verilog 顺序语句的文章中看到的那样,有许多语句只能在过程块中使用。我们使用这些语句来控制在 verilog 设计中数据赋值的方式。我们可以使用的四种不同类型的循环用于设计中分配赋值的顺序语句。
因此,我们只能在程序块(例如always块或initial块)中编写循环语句。在这篇文章的其余部分,我们将讨论如何在verilog中使用循环语句。然后,我们为每个结构考虑一个简短的示例,以展示我们如何在实践中使用它们。
Verilog中的循环
我们在verilog中使用循环来多次执行相同的代码。verilog 中最常用的循环是 for 循环。我们使用此循环来执行固定次数的代码块。 我们还可以在verilog中使用repeat关键字,它执行与for循环类似的功能。但是,我们通常更喜欢在verilog设计中使用for循环而不是repeat关键字。 我们在verilog中常用的另一种类型的循环是while循环。只要给定条件为真,我们就使用此循环来执行部分代码。 让我们仔细看看这些类型的循环。
Verilog forever循环
我们使用verilog中的forever循环来创建连续执行的代码块,就像其他编程语言中的无限循环一样。这与 verilog 中的其他类型的循环形成鲜明对比,例如 for 循环和while循环,它们只运行固定次数。forever循环最常见的用例之一是在verilog测试平台中生成时钟信号。forever循环不能综合,这意味着我们只能在测试台代码中使用它。 下面的代码片段显示了 verilog forever循环的一般语法。
1 | forever begin |
2 | // Code to be executed by the loop goes here |
3 | end |
forever循环示例
为了更好地演示我们如何在实践中使用永久循环,让我们考虑一个例子。在本例中,我们将生成一个频率为 10MHz 的时钟信号,我们可以在测试台内使用该信号。为此,我们首先将信号分配给初始值。然后,我们使用永久块定期反转信号。 下面的代码片段显示了我们如何在verilog中实现这个时钟示例。
1 | initial begin |
2 | clk = 1'b0; |
3 | forever begin |
4 | #500 clk = ~clk; |
5 | end |
6 | end |
关于这个例子,有两件重要的事情要说。首先,请注意,我们使用verilog initial块,这是过程语句的另一个示例。我们在初始块中编写的任何代码都会在模拟开始时执行一次。我们几乎总是在测试平台代码中使用初始块,而不是always块。原因是它们只执行一次,我们通常只需要运行一次测试。
这里要注意的另一件重要事情是使用 # 符号在 verilog 中对时间延迟进行建模。为了使此示例正常工作,我们需要在代码中包含 verilog 时间刻度编译器指令。我们使用时间刻度编译器指令来指定模拟的时间单位和分辨率。在这种情况下,我们需要将时间单位设置为 ns,如下面的代码片段所示。
1 | `timescale 1ns / 1ps |
Verilog repeat循环
我们使用repeat循环来执行给定的verilog代码块固定次数。我们指定代码块在repeat循环声明中执行的次数。虽然我们最常在verilog测试台中使用repeat循环,但我们也可以在可综合的代码中使用它。但是,我们在使用此结构综合成代码时必须小心,因为我们只能使用它来描述重复的结构。
下面的代码片段显示了verilog重复循环的一般语法
1 |
repeat ( |
2 | // Code to be executed in the loop |
3 | end |
我们使用<数字>字段来确定repeat循环的执行次数。repeat循环与verilog中的 for循环非常相似,因为它们都执行代码的次数固定。 这两种类型的循环之间的主要区别在于 for 循环包含一个局部变量,我们可以在循环中引用该变量。此变量的值在循环的每次迭代中更新。相比之下,repeat循环不包括此局部循环变量。因此,在我们不需要此变量的情况下,repeat循环实际上不如for循环那么冗长。
repeat循环示例
repeat循环是一个相对直接的结构。但是,让我们考虑一个基本示例,以更好地演示它是如何工作的。对于此示例,假设我们的设计中有一个信号,每当设计中另一个信号出现上升沿时,我们想要取反该信号,但是,我们只希望此取反操作总共有效六次。 下面的波形显示了我们试图在此示例循环中实现的功能。
我们可以轻松地在repeat块中实现这一点,如下面的代码片段所示。
1 | repeat (6) begin |
2 | @(posedge sig_a) |
3 | sig_b = ~sig_b; |
4 | end |
我们可以在这个例子中看到,我们已将<数字>字段设置为 6。因此,repeat循环在终止之前总共将运行六次。然后,我们使用我们在verilog always块的帖子中讨论的posedge宏。此宏告诉我们代码中sig_a信号何时出现上升沿。在verilog中,我们使用@符号来告诉我们的代码等待事件发生。这仅意味着代码将在此行暂停并等待括号中的条件评估为 true。一旦发生这种情况,代码将继续运行。 在此示例中,我们使用此运算符来阻止repeat循环的执行,直到在sig_a信号上检测到上升沿。 最后,我们可以使用非verilog位运算符(~)在检测到上升沿时反转sig_b信号。 下面的波形显示了该代码的仿真结果。
Verilog while循环
我们使用while循环来执行verilog代码的一部分,只要给定条件为真。在循环的每次迭代之前计算指定的条件。因此,块中的所有代码都将在每次有效的迭代中执行。 即使条件发生更改,在块中的代码运行时不再计算为true,也会发生这种情况。我们可以将 while循环视为重复执行的if语句。 由于循环通常不可综合,因此我们经常在测试平台中使用它们来产生激励。 下面的代码片段显示了verilog中while循环的一般语法。
1 |
while |
2 | // Code to execute |
3 | end |
我们使用上述构造中的 <条件> 字段来确定循环的执行何时停止。
while循环示例
为了更好地演示我们如何在verilog中使用while循环,让我们考虑一个基本示例。对于此示例,我们将创建一个从0增加到3的整数类型变量。然后,我们在循环的每次迭代中打印此变量的值。 虽然这是一个微不足道的示例,但它演示了while循环的基本原理。 下面的代码片段显示了我们将如何实现此示例。
1 | while (iter < 4) begin |
2 | $display("iter = %0d", iter); |
3 | iter = iter + 1; |
4 | end |
此示例假定已声明iter变量并为其分配初始值0。在循环的每次迭代中,循环体中的第二行代码都会递增iter变量。 在此示例中设置了 <条件> 字段,以便仅在iter变量小于4时执行循环。因此,迭代变量在此循环中从0递增到3。
Verilog For循环
在编写verilog代码时,我们使用for循环来执行固定次数的代码块。与while循环一样,只要给定条件为真,for循环就会执行。在循环的每次迭代之前计算指定的条件。我们将此条件指定为 for循环声明的一部分。此条件用于控制循环的执行次数。 虽然它通常用于测试平台,但我们也可以在可综合的verilog代码中使用for循环。 当我们在可综合代码中使用for循环时,我们通常使用它来复制硬件的各个部分。最常见的例子之一是移位寄存器。 正如我们前面提到的,for循环与rep循环非常相似。主要区别在于for循环使用可以在我们的循环代码中使用的局部变量。
下面的代码片段显示了我们在 verilog for循环中使用的语法。
1 |
for ( |
2 | // Code to execute |
3 | end |
我们使用
for循环示例
为了更好地演示我们如何在verilog中使用for循环,让我们考虑一个基本示例。在本例中,我们将使用verilog for循环编写一个简单的四位串行移位寄存器。实现移位寄存器实际上是for循环最常见的用例之一。 移位寄存器可以使用简单的verilog阵列来实现。然后,我们可以将移位寄存器的输入分配给数组的第一个元素。然后,我们使用for循环将数组的现有内容向左移动一个位置。
下面的 verilog 代码片段显示了我们将如何使用for循环实现此移位寄存器。
1 | // The circuit input goes into the first register |
2 | shift[0] <= circuit_in; |
3 |
4 | // A for loop to shift the contents of the register |
5 | for (i = 1; i < 4; i = i + 1) begin |
6 | shift[i] <= shift[i-1]; |
7 | end |
在此代码中要注意的第一件事是,我们使用循环变量(i)来引用循环中数组的元素。在代码中使用它之前,我们必须声明这个循环变量。 由于我们的移位数组有四个位,我们设置
全部0条评论
快来发表一下你的评论吧 !