3

我正在尝试在 Verilog 中创建一个可合成的、参数化的优先级编码器。具体来说,我想在向量中找到最不重要的 1 并返回一个仅包含该 1 的向量。例如:

IN[3:0] | OUT[4:0]
--------+---------
1010    | 00010
1111    | 00001
0100    | 00100
0000    | 10000   (special case)

因此,如果向量是四位宽,则代码为:

if (in[0]==1'b1) least_one = 1;
else if (in[1]==1'b1) least_one = 2;
else if (in[2]==1'b1) least_one = 4;
else if (in[3]==1'b1) least_one = 8;
else out = 16; // special case in==0, set carry bit

我需要一种通用的、可扩展的方法来执行此操作,因为输入/输出向量长度是参数化的。我目前的代码是:

module least_one_onehot
#(parameter ADDR_WIDTH=4)
(output reg [ADDR_WIDTH:0] least_one,
input [ADDR_WIDTH-1:0] in);

genvar i;

always @(in) begin
    if (in[0]==1'b1) least_one = 1;
    generate for (i=1; i<ADDR_WIDTH; i=i+1) begin : U 
        else if (in[i]==1'b1) least_one = 2**i; 
        end 
        endgenerate
    else least_one = 2**ADDR_WIDTH;
    end

endmodule

当我尝试编译它时,我收到以下错误:

file: least_one_onehot.v
        generate for (i=1; i<ADDR_WIDTH; i=i+1) begin : U
               |
ncvlog: *E,GIWSCP (least_one_onehot.v,10|8): Generated instantiation can only be valid within a module scope [12.1.3(IEEE 2001)].
                        else if (in[i]==1'b1) least_one = 2**i; 
                           |
ncvlog: *E,NOTSTT (least_one_onehot.v,11|6): expecting a statement [9(IEEE)].
                endgenerate
                          |
ncvlog: *E,GIWSCP (least_one_onehot.v,13|12): Generated instantiation can only be valid within a module scope [12.1.3(IEEE 2001)].
                else least_one = 2**ADDR_WIDTH;
                   |
ncvlog: *E,NOTSTT (least_one_onehot.v,14|5): expecting a statement [9(IEEE)]

我尝试了生成、if 和 always 语句的各种安排,但都没有成功。任何人都知道正确的语法吗?案例陈述实施或其他替代方案也可以。谢谢。

4

4 回答 4

2

我认为您误解了 generate 的工作原理。它不是一个文本预处理器,它通过适当的替换在 generate/endgenerate 对之间发出代码。你必须有完整的句法实体与对。我现在无法访问模拟器,但这可能对你有用(完全未经测试)

genvar i;
generate
    for (i = 1; i < ADDR_WIDTH; i = i + 1) begin : U
        least_one[i] = in[i] & ~|in[i - 1:0];
    end
endgenerate
least_one[0] = in[0];
least_one[ADDR_WIDTH] = ~|in;

通常 Verilog 会抱怨非恒定位切片宽度,但由于它在生成循环内,它可能会起作用。

如果像上面那样失败,您只需在 for 循环中测试第一个设置位,然后解码该结果。

于 2013-02-25T23:57:13.677 回答
1

不需要生成块。你可以使用:

integer i;
reg found;
always @(in) begin
  least_one = {(ADDR_WIDTH+1){1'b0}};
  found = 1'b0;
  for (i=0; i<ADDR_WIDTH; i=i+1) begin
    if (in[i]==1'b1 && found==1'b0) begin
      least_one[i] = 1'b1;
      found = 1'b1;
    end
  end
  least_one[ADDR_WIDTH] = (found==1'b0);
end

如果您真的想使用生成块,那么您需要分配每个位。

assign least_one[0] = in[0];
assign least_one[ADDR_WIDTH] = (in == {ADDR_WIDTH{1'b0}});
genvar i;
generate
  for (i=1; i<ADDR_WIDTH; i=i+1) begin : U
    assign least_one[i] = in[i] && (in[i - 1:0] == {i{1'b0}});
  end
endgenerate
于 2013-02-26T01:36:13.530 回答
1

这模拟了你想要的方式,但它是不可合成的(你没有指定这是否是一个要求):

module least_one_onehot #(parameter ADDR_WIDTH=4) (
    output reg [ADDR_WIDTH-1:0] least_one,
    input      [ADDR_WIDTH-1:0] in
);

always @* begin
    least_one = '0;
    for (int i=ADDR_WIDTH-1; i>=0; i--) begin
        if (in[i]) least_one = 2**i;
    end
end

endmodule

请注意,它使用 SystemVerilog 构造。

于 2013-02-26T01:48:16.420 回答
1

就个人而言,我喜欢以下代码块来满足您的需求:assign out = {1'b1,in} & ((~{1'b1,in})+1);

你可以试试这个(为了易读性而放弃额外的高位),但我喜欢明确地做这两个恭维以避免任何潜在的兼容性问题。

分配 = in & (-1*in);

于 2014-02-09T07:55:28.123 回答