这些小活动你都参加了吗?快来围观一下吧!>>
电子产品世界 » 论坛首页 » 活动中心 » 板卡试用 » 【换取手持数字示波器】+FPGA基本语法语句Verilog代码优化之for语句分

共3条 1/1 1 跳转至

【换取手持数字示波器】+FPGA基本语法语句Verilog代码优化之for语句分享

菜鸟
2024-05-23 14:18:53   被打赏 37 分(兑奖)     打赏

Verilog代码优化之 for 语句

下面谈谈for语句,Verilog中的for语句虽然也是可综合的,但在RTL级的代码中基本不用。一方面是因为for语句的使用是很占用硬件资源的,另一方面是因为在设计中往往是采用时序逻辑设计,用到for循环的地方不多。

Verilog中,for循环确实是可以综合的,但通常不建议在描述硬件行为的寄存器传输级(RTL)代码中过度使用for循环,特别是在可综合的模块中。原因主要有以下几点:

  1. 硬件资源消耗:每个for循环迭代在硬件中可能需要其自己的逻辑来实现。这可能导致大量的重复逻辑,从而消耗大量的硬件资源。

  2. 并行与顺序Verilog描述的是并行的硬件行为,而for循环是顺序的。因此,使用for循环可能会导致设计的行为不如预期,特别是当循环体内部的操作依赖于之前的迭代结果时。

  3. 可读性:过度使用for循环可能会降低代码的可读性,使得其他工程师难以理解你的设计意图。

在一些情况下,使用for循环可能是合理的,比如初始化数组或生成查找表。但即使在这些情况下,也应该仔细考虑其对硬件资源的影响。

 

下面是一个使用for循环的示例,以及它可能的综合结果:

module for_loop_example(
   input wire clk,
   input wire reset,
   output reg [7:0] result
);

reg [7:0] array[0:7];

initial begin
   // 这是一个非综合的初始块,用于示例
   for(int i = 0; i < 8; i = i + 1) begin
       array[i] = i; // 初始化数组
   end
end

always @(posedge clk or posedge reset) begin
   if(reset) begin
       // 重置逻辑
       result <= 0;
   end else begin
       // 这里我们不会使用for循环,因为它在时序逻辑中不合适
       // 但如果我们真的用了,它可能会为每个迭代生成一个硬件块
   end
end

// 假设我们真的在时序逻辑中使用了for循环(这通常是不推荐的)
// always @(posedge clk) begin
//     for(int i = 0; i < 8; i = i + 1) begin
//         // 一些依赖于i的操作...
//         // 这将为每个i值生成一个硬件块
//     end
// end

Endmodule

 

在上面的示例中,initial块中的for循环用于初始化数组,但它是一个非综合的块,只在仿真中执行。在always块中,我们没有使用for循环,因为它在时序逻辑中通常是不合适的。但是,如果我们真的在时序逻辑中使用了for循环(如注释所示),那么它可能会为每个迭代生成一个硬件块。

 

这段代码的用意是在一个时钟周期内计算出13路脉冲信号为高电平的个数,一般人都会感觉这个任务交给for循环来做再合适不过了,但是for 循环能完成这个任务吗?来看看如图所示的仿真波形。

 blob.png


Verilog中,使用非阻塞赋值(<=)是在描述并行硬件行为时常见的做法。在always块中,当使用非阻塞赋值时,所有右边的表达式计算都将在单个时间单位(如时钟周期)内并行完成,然后这些值会在该时间单位的末尾同时更新到左侧的寄存器或变量。

然而,如果你试图在always块中使用阻塞赋值(=),你通常会遇到问题,因为阻塞赋值会阻止always块中的后续代码在当前赋值完成之前执行。在硬件描述语言中,这通常不是期望的行为,因为硬件是并行的,而不是顺序的。

但是,如果你确实需要在某个特定的上下文中使用阻塞赋值(例如,在初始化或测试平台代码中),你可以这样做,但你应该清楚这不是描述硬件并行性的标准方法。

下面是一个使用阻塞赋值在initial块中初始化变量的例子(这不会影响到硬件的综合,因为initial块是不可综合的,只用于仿真):

module test_blocking_assignment;
   reg [31:0] num;

   initial begin
       for (int i = 0; i < 10; i = i + 1) begin
           // 使用阻塞赋值初始化变量
           num = num + 1;
           // 在这里,num的值会在每次循环迭代中立即更新
           #1; // 添加一个时间延迟以便在仿真中看到变化(可选)
       end
       // 此时,num的值将是9(如果初始值为0)
   end

   // ... 其他硬件描述代码 ...

Endmodule


但是,在always块中,你应该避免使用阻塞赋值,因为它会改变always块中代码的执行顺序,这可能导致意外的行为或不可综合的代码。

如果你需要在每个时钟周期递增一个寄存器,你应该使用非阻塞赋值,如下所示:

module counter;
   input wire clk, reset;
   output reg [31:0] num;

   always @(posedge clk or posedge reset) begin
       if (reset) begin
           // 如果复位信号为高,则重置计数器
           num <= 0;
       end else begin
           // 否则,在每个时钟上升沿递增计数器
           num <= num + 1;
       end
   end

   // ... 其他硬件描述代码 ...

Endmodule


在这个例子中,num在每个时钟上升沿递增,而不管always块中的其他代码如何执行,因为所有非阻塞赋值都会在时钟边缘同时更新。



高工
2024-05-23 16:57:22     打赏
2楼

谢谢分享


院士
2024-05-24 18:08:50     打赏
3楼

楼主,您这个文章是使用AI生成的吗?


共3条 1/1 1 跳转至

回复

匿名不能发帖!请先 [ 登陆 注册 ]