【问题标题】:Quartus does not allow using a Generate block in VerilogQuartus 不允许在 Verilog 中使用 Generate 模块
【发布时间】:2020-06-02 15:09:45
【问题描述】:

很简单的问题。给定以下代码:

module main(
    output reg  [1:0][DATA_WIDTH-1:0] dOut,
    input  wire [1:0][DATA_WIDTH-1:0] dIn,
    input  wire [1:0][ADDR_WIDTH-1:0] addr,
    input  wire [1:0] wren,
    input  wire clk
);
    parameter DATA_WIDTH = 16;
    parameter ADDR_WIDTH = 6;

    reg [DATA_WIDTH-1:0] ram [2**ADDR_WIDTH-1:0];

    generate
        genvar k;
        for(k=0; k<2; k=k+1) begin: m
            always @(posedge clk) begin
                if(wren[k])
                    ram[addr[k]] <= dIn[k];
                dOut[k] <= ram[addr[k]];
            end
        end
    endgenerate
endmodule

quarus 13.0sp1 给出了这个错误(以及它的其他 20 个不正当的兄弟姐妹):

错误 (10028):无法在 main.v(42) 处解析网络“ram[63][14]”的多个常量驱动程序

但如果我手动展开生成循环:

module main(
    output reg  [1:0][DATA_WIDTH-1:0] dOut,
    input  wire [1:0][DATA_WIDTH-1:0] dIn,
    input  wire [1:0][ADDR_WIDTH-1:0] addr,
    input  wire [1:0] wren,
    input  wire clk
);
    parameter DATA_WIDTH = 16;
    parameter ADDR_WIDTH = 6;

    reg [DATA_WIDTH-1:0] ram [2**ADDR_WIDTH-1:0];

    always @(posedge clk) begin
        if(wren[0])
            ram[addr[0]] <= dIn[0];
        dOut[0] <= ram[addr[0]];
    end

    always @(posedge clk) begin
        if(wren[1])
            ram[addr[1]] <= dIn[1];
        dOut[1] <= ram[addr[1]];
    end
endmodule

分析和合成步骤一切顺利。

有什么办法可以让生成循环运行?

【问题讨论】:

  • 绝对正确。您生成多个驱动ram[addr[whatever]]always 语句; addr 是一个 var 数组,无法在编译时解析。所以,它是多重驱动的。
  • @Serge 我无法理解您的评论。
  • 我所说的“ram”在您的两种情况下都是多重驱动的。在任何情况下,综合都应该给出一些警告/错误,除非它可以将 'addr' 解析为常量。
  • Serge 和工具谢谢。我懂了。但是为什么展开的版本有效。此外,模板使用非常相似的代码来推断 Quartus 的真正双端口 RAM。那里看不到同一个地址写冲突吗?
  • @toolic,如果我强制更新条件为addr[0]!=addr[1] &amp;&amp; wren[k],则没有雪茄

标签: verilog fpga hdl quartus intel-fpga


【解决方案1】:

我认为正确的方法是在这个问题中解释的内容:Using a generate with for loop in verilog

这将被转移到您的代码中:

module main(
    output reg  [1:0][DATA_WIDTH-1:0] dOut,
    input  wire [1:0][DATA_WIDTH-1:0] dIn,
    input  wire [1:0][ADDR_WIDTH-1:0] addr,
    input  wire [1:0] wren,
    input  wire clk
);
    parameter DATA_WIDTH = 16;
    parameter ADDR_WIDTH = 6;

    reg [DATA_WIDTH-1:0] ram [2**ADDR_WIDTH-1:0];    
    integer k;

    always @(posedge clk) begin
      for(k=0; k<2; k=k+1) begin:
        if(wren[k])
          ram[addr[k]] <= dIn[k];
        dOut[k] <= ram[addr[k]];
      end
    end
endmodule

将对双端口 RAM 的所有访问保存在一个 always 块中很方便,因此合成器可以安全地检测到您正在有效地使用寄存器 ram 处的双端口 RAM。

【讨论】:

  • 没有雪茄。新错误:Error (10049): Verilog HDL error at main.v(42): value must not be assigned to nonvariable "k"
  • 应该是integer k
  • 从我的回答中阅读最后一段。您的方法生成了多个 always 块。一个好的设计风格(不易出错)要求所有对同一个信号(在本例中为 ram)的写入都应该在同一个 always 块中执行。对于模拟,您的方法很可能工作,但对于综合,很可能不会。
  • @Kraken 我解释了为什么你的方法在我的回答中不起作用。 mcleod_ideafix 单始终阻止解决方案被认为是这种情况的最佳实践。
【解决方案2】:

生成循环和展开版本都不应该通过综合。在这两种情况下,ram 中的相同地址都可以由两个 always 块分配。更糟糕的是,如果wren 的两个位都为高且两个地址相同而数据不同,则结果无法确定。 Verilog LRM 声明寄存器中的最后一个分配获胜,并且始终可以按任何顺序评估具有相同触发器的块。

综合要求对寄存器的分配是确定性的。两个(或更多)总是对同一位具有写访问权限的块是非法的,因为具有不确定性。如果展开的合成正确,那么这意味着在所示模块之外的wrenaddr 上有常量,这使得写冲突在逻辑上是不可能的;由于某种原因,生成循环版本没有得到相同的优化。允许优化以防止多始终阻止写入访问的约束示例:

  1. 一个wren 被硬编码为0。因此只有一个块具有独占访问权
  2. 地址具有不重叠的可能值集。例如addr[0] 只能是偶数,而addr[1] 只能是奇数,或者addr[0] &lt; 2**(ADDR_WIDTH/2)addr[1] &gt;= 2**(ADDR_WIDTH/2)

dOut 由两个 always 块分配,合成没问题,因为每个块对其目标位(可能的地址值的非重叠集)具有独占写入访问权限。

mcleod_ideafix 答案中的单个 always 块是首选解决方案。如果wren 的两个位都为高且两个地址相同,则wren[1] 将始终获胜。如果wren[0] 应该具有优先级,则使 for 循环倒计时。

【讨论】:

  • "那么这意味着 wren 和 addr 在所示模块之外有常量,这使得写冲突在逻辑上是不可能的"......不是这样,问题中显示的模块是我的顶级模块。此外,quartus 使用这两种方法来推断 ALT_SYNC_RAM,并且在 Altera Docs 中也有说明。我知道对同一个变量进行多重赋值是不好的,但我猜 Altera 在这种情况下做了一个“硬编码”异常。
  • @Kraken Altera 可能允许特殊条件。这也可以解释他们的生成循环不起作用(不适合模板)并且手册起作用(确实适合他们的模板)。如果两个写入发生在同一个地址上,我不确定 Altera 会发生什么。由于使用两个或多个 always 块写入同一个寄存器是非标准的,因此不要期望代码可以与另一个合成器一起使用。
  • 我更喜欢将我所有的写入放在单个块中的单个变量中。这样,在发生冲突的情况下,最后一条语句开始驱动网络。想知道为什么 Altera 不喜欢这样。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-09-02
  • 1970-01-01
相关资源
最近更新 更多