在always块中使用Verilog的generate/genvar语句

22

我正在尝试让一个模块通过ISE 12.4的语法检查,并且它给了我一个我不理解的错误。首先是代码片段:

parameter ROWBITS = 4;

reg [ROWBITS-1:0] temp;

genvar c;
generate
    always @(posedge sysclk) begin
        for (c = 0; c < ROWBITS; c = c + 1) begin: test
            temp[c] <= 1'b0;
        end
    end
endgenerate
当我尝试进行语法检查时,出现以下错误消息:

ERROR:HDLCompiler:731 - "test.v" 第46行:不允许对非寄存器 <c> 进行过程赋值。

我真的不明白为什么会出现这种情况。"c" 不是一个 wire,而是一个 genvar。这应该相当于完全合法的语法:

reg [3:0] temp;

always @(posedge sysclk) begin
    temp[0] <= 1'b0;
    temp[1] <= 1'b0;
    temp[2] <= 1'b0;
    temp[3] <= 1'b0;
end

请不要评论说不使用generate会更容易编写,这只是一个更复杂的代码片段的缩小示例,涉及多个if语句和对“temp”进行非阻塞赋值。此外,请不要仅告诉我有更新版本的ISE,我已经知道了。另一方面,如果你知道它在后续版本的ISE中已经修复,请告诉我你知道哪个版本可以正常工作。

6个回答

34

您需要在生成块内部反转嵌套:

genvar c;
generate
    for (c = 0; c < ROWBITS; c = c + 1) begin: test
        always @(posedge sysclk) begin
            temp[c] <= 1'b0;
        end
    end
endgenerate

从技术上讲,这将生成四个始终块:

always @(posedge sysclk) temp[0] <= 1'b0;
always @(posedge sysclk) temp[1] <= 1'b0;
always @(posedge sysclk) temp[2] <= 1'b0;
always @(posedge sysclk) temp[3] <= 1'b0;
在这个简单的例子中,四个always块和一个包含四个赋值语句的单个always块之间的行为没有区别,但在其他情况下可能会有所不同。
genvar相关的操作需要在构建设计的内存表示(模拟器的情况下)或映射到逻辑门(综合工具的情况下)时进行解析。只有当设计开始运行时,always @posedge 才有意义。
受到某些限制,你可以将for循环放置在always块内,即使是可综合代码也可以。对于综合,循环将被展开。但在这种情况下,for循环需要使用 reginteger 或类似的变量类型,而不能使用 genvar,因为在always块内部使用for循环描述的是在每个时钟边沿都会发生的操作,而不是在设计的实例化过程中可以静态扩展的操作。

感谢您的快速回复。我担心会是这样的情况。问题的一部分是我想用for循环生成一系列if语句或case选择器,但无法在单独的always块中完成。 - user1684538
2
有没有办法在单个always块中生成所有代码,或者genvar无法做到这一点? - user2569332

7

如果您希望在同一个always块中分配temp的所有位,那么您不需要生成一个块。

parameter ROWBITS = 4;
reg [ROWBITS-1:0] temp;
always @(posedge sysclk) begin
    for (integer c=0; c<ROWBITS; c=c+1) begin: test
        temp[c] <= 1'b0;
    end
end

另外,如果您的模拟器支持IEEE 1800(SystemVerilog),那么

parameter ROWBITS = 4;
reg [ROWBITS-1:0] temp;
always @(posedge sysclk) begin
        temp <= '0; // fill with 0
    end
end

4
如果您不介意需要编译/生成文件,那么可以使用预处理技术。这样可以获得生成的能力,同时结果是一个干净的Verilog文件,通常更容易调试,并且会导致更少的模拟器问题。
我使用RubyIt来使用ERB(嵌入式Ruby)从模板生成Verilog文件。
parameter ROWBITS = <%= ROWBITS %> ;
always @(posedge sysclk) begin
  <% (0...ROWBITS).each do |addr| -%>
    temp[<%= addr %>] <= 1'b0;
  <% end -%>
end

生成 module_name.v 文件,使用:
$ ruby_it --parameter ROWBITS=4 --outpath ./ --file ./module_name.rv

生成的 module_name.v
parameter ROWBITS = 4 ;
always @(posedge sysclk) begin
  temp[0] <= 1'b0;
  temp[1] <= 1'b0;
  temp[2] <= 1'b0;
  temp[3] <= 1'b0;
end

2
在Verilog模块中,基本上包含两种结构:项和语句。语句总是出现在过程性上下文中,这包括begin..end之间的任何内容、函数、任务、始终块和初始块。诸如生成构造之类的项直接列在模块中。for循环和大多数变量/常量声明可以存在于两个上下文中。
在您的代码中,似乎您希望for循环被评估为生成项,但该循环实际上是始终块的过程性上下文的一部分。要将for循环视为生成循环,它必须在模块上下文中。generate..endgenerate关键字完全是可选的(某些工具需要它们),并且没有影响。请参见this answer,了解如何评估生成循环的示例。
//Compiler sees this
parameter ROWBITS = 4;
reg [ROWBITS-1:0] temp;
genvar c;

    always @(posedge sysclk) //Procedural context starts here
    begin
        for (c = 0; c < ROWBITS; c = c + 1) begin: test
            temp[c] <= 1'b0; //Still a genvar
        end
    end

0

对于Verilog,只需执行以下操作

parameter ROWBITS = 4;
reg [ROWBITS-1:0] temp;
always @(posedge sysclk) begin
  temp <= {ROWBITS{1'b0}}; // fill with 0
end

-1
简单来说,在 always 进程中不使用 generate,而是使用 generate 创建参数化进程或实例化特定模块,其中可以组合 if-else 或 case。因此,您可以将此 generate 移动并创建一个特定的进程或实例化,例如:
module #(
parameter XLEN = 64,
parameter USEIP = 0
)
(
 input clk,
input rstn,
input [XLEN-1:0] opA,
input [XLEN-1:0] opB,
input [XLEN-1:0] opR,
input en
);

generate 
case(USEIP)
0:begin
always @(posedge clk or negedge rstn)
begin
if(!rstn)
begin
 opR <= '{default:0};
end
else
begin
if(en)
 opR <= opA+opB;
else
opR <= '{default:0};
end
end
end
1:begin
  superAdder #(.XLEN(XLEN)) _adder(.clk(clk),.rstm(rstn), .opA(opA), .opB(opB), .opR(opR), .en(en));
end
endcase

endmodule

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接