【问题标题】:Priority encoder in verilogverilog中的优先级编码器
【发布时间】:2015-06-14 07:13:51
【问题描述】:

我对verilog有点陌生,我尝试运行这段代码,但它给了我一个错误:

module enc(in,out);
  input [7:0] in;
  output [3:0] out;
  reg i;
  reg [3:0] out;

  always @*
    begin
      for (i=0;i<7;i=i+1)
        begin
          if ((in[i]==1) && (in[7:i+1]==0))
            out = i;
          else
            out = 0;
        end
    end
endmodule

我认为它抱怨 in[7:i+1] 但我不明白为什么? 有人可以请教..

编辑 好的,所以我不愿意使用 X,因为它们有很多问题。我正在考虑将代码修改为这样的:

module enc(in,out);
  input [7:0] in;
  output [2:0] out;
  reg i;
  reg [2:0] out,temp;

  always @*
    begin
      temp = 0;
      for (i=0;i<8;i=i+1)
        begin
          if (in[i]==1)
            temp = i;
        end
      out = temp;
    end
endmodule

你认为这会成功吗?我目前无法访问模拟器..

【问题讨论】:

  • 您需要生成它吗?由于输入和输出的大小是固定的,您可以选择非生成版本
  • 我怀疑你想将 i 声明为一个整数,而不是一个单位 reg。

标签: verilog encoder


【解决方案1】:

优先级编码器意味着如果两个或更多位满足标准,则将优先级赋予一位。查看您的代码,您似乎想在使用向上计数器时优先考虑 LSB。 out 分配在每一个look中,所以即使你可以编译,最终的结果也会是6或0。

对于 LSB 优先级编码器,首先从 out 的默认值开始并使用递减计数器:

module enc (
    input wire [7:0] in,
    output reg [2:0] out
  );
  integer i;
  always @* begin
    out = 0; // default value if 'in' is all 0's
    for (i=7; i>=0; i=i-1)
        if (in[i]) out = i;
  end
endmodule

【讨论】:

  • 谢谢,如果我们在 out = i; 之后在代码中添加“break”会怎样?那将是 MSB 优先编码器吗?它会被合成吗
  • 有些合成器确实支持break,所以你必须尝试一下。我没有读过任何推荐它的论文。
  • 如果您想要 MSB 优先级编码器,请在 for 循环中使用递增计数器。在 verilog 中总是阻塞,最后的分配获胜。因此,最高优先级必须要么阻止其他人分配,要么最后分配
  • 正如其他答案中提到的,一些工具链将很难从这个循环中生成有效的网表。
【解决方案2】:

如果你只对模拟感兴趣,那么你的线性循环方法应该没问题,比如

    out = 0;
    for (i = W - 1; i > 0; i = i - 1) begin
      if (in[i] && !out)
        out = i;
    end

如果您还关心性能,那么这个问题就会变得更有趣。我曾经尝试过不同的方法来编写参数化优先级编码器here。事实证明,即使从上面的脑死循环,Synopsys 也可以生成有效的实现,但其他工具链需要显式生成魔法。以下是链接的摘录:

    output [WIDTH_LOG - 1:0] msb;

    wire [WIDTH_LOG*WIDTH - 1:0] ors;
    assign ors[WIDTH_LOG*WIDTH - 1:(WIDTH_LOG - 1)*WIDTH] = x;

    genvar w, i;
    integer j;

    generate
      for (w = WIDTH_LOG - 1; w >= 0; w = w - 1) begin
        assign msb[w] = |ors[w*WIDTH + 2*(1 << w) - 1:w*WIDTH + (1 << w)];
        if (w > 0) begin
          assign ors[(w - 1)*WIDTH + (1 << w) - 1:(w - 1)*WIDTH] = msb[w] ? ors[w*WIDTH + 2*(1 << w) - 1:w*WIDTH + (1 << w)] : ors[w*WIDTH + (1 << w) - 1:w*WIDTH];
        end
      end
    endgenerate

【讨论】:

    【解决方案3】:

    为了能够在部分切片后缀中使用可变索引,您必须将 for 块包含在生成块中,如下所示:

    gen var i;
    generate
    for (i=0;i<7;i=i+1) begin :gen_slices
      always @* begin
        ... do whatever with in[7:i+1]
      end
    end
    

    问题在于,将其应用到您的模块中,它的编写方式会导致其他错误。你重写的模块看起来像这样(警告:这也不起作用)

    module enc (
      input wire [7:0] in,
      output reg [2:0] out  // I believe you wanted this to be 3 bits width, not 4.
      );
    
      genvar i; //a generate block needs a genvar
      generate
        for (i=0;i<7;i=i+1) begin :gen_block
          always @* begin
            if (in[i]==1'b1 && in[7:i+1]=='b0) // now this IS allowed :)
              out = i;
            else
              out = 3'b0;
          end
        end
      endgenerate
    endmodule
    

    这将引发关于out 由多个来源驱动的综合错误。这意味着分配给out 的值同时来自多个来源,这是不允许的。

    这是因为for 块展开为如下内容:

    always @* begin
      if (in[0]==1'b1 && in[7:1]=='b0)
        out = 0;
      else
        out = 3'b0;
    end
    always @* begin
      if (in[1]==1'b1 && in[7:2]=='b0)
        out = 1;
      else
        out = 3'b0;
    end
    always @* begin
      if (in[2]==1'b1 && in[7:3]=='b0)
        out = 2;
      else
        out = 3'b0;
    end
    .... and so on...
    

    所以现在您有多个组合块 (always @*) 尝试将值设置为 out。它们都将同时工作,并且无论if 块评估为true 还是false,它们都将尝试为out 赋予特定值。回想一下,每个 if 语句的条件相对于其他 if 条件是互斥的(即只有一个 if 必须评估为 true)。

    因此,避免这种多源情况的一种快速而肮脏的方法(我相信有更优雅的方法可以解决这个问题)是如果if 块不打算为其分配值,则将其设为高阻抗.像这样的:

    module enc (
      input wire [7:0] in,
      output reg [2:0] out  // I believe you wanted this to be 3 bits width, not 4.
      );
    
      genvar i; //a generate block needs a genvar
      generate
        for (i=0;i<7;i=i+1) begin :gen_block
          always @* begin
            if (in[i]==1'b1 && in[7:i+1]=='b0) // now this IS allowed :)
              out = i;
            else
              out = 3'bZZZ;
          end
        end
      endgenerate
      always @* begin
        if (in[7])  // you missed the case in which in[7] is high
          out = 3'd7;
        else
          out = 3'bZZZ;
      end
    endmodule
    

    另一方面,如果您只需要一个优先级编码器,并且您的设计使用固定和小宽度的输入和输出,您可以这样编写编码器:

    module enc (
      input wire [7:0] in,
      output reg [2:0] out
      );
    
      always @* begin
        casex (in)
          8'b1xxxxxxx : out = 3'd7;
          8'b01xxxxxx : out = 3'd6;
          8'b001xxxxx : out = 3'd5;
          8'b0001xxxx : out = 3'd4;
          8'b00001xxx : out = 3'd3;
          8'b000001xx : out = 3'd2;
          8'b0000001x : out = 3'd1;
          8'b00000001 : out = 3'd0;
          default     : out = 3'd0;
        endcase
      end
    endmodule
    

    (尽管似乎有理由不在设计中使用casex。阅读@Tim 在另一个问题中发布的评论:How can I assign a "don't care" value to an output in a combinational module in Verilog

    总结:恐怕我没有满足您要求的防弹设计(如果我们考虑到 Tim 在他的评论中链接的论文的内容),但至少,您现在知道为什么 @987654344 @ 在部分切片后缀中是不允许的。


    另一方面,您可以通过研究我提供的作为另一个 SO 问题答案的代码来完成一半的工作。在这种情况下,模块像优先级编码器一样工作,参数化并且没有casex 语句,只有输出不是二进制的,而是单热编码的。 How to parameterize a case statement with don't cares?

    【讨论】:

    • 不要在 RTL 中使用casex,而是使用casez(谨慎)。请参阅this paper 中的第 4.3 和 4.3 节。另一种选择是case(1'b1)。我更喜欢 casecase(1'b1) 与 SystemVerilog 指令 ​​uniqueunique0priority
    • 感谢 mcleod 澄清了切片索引的问题和生成的需要。
    【解决方案4】:

    所以我的编辑解决方案奏效了......多么愚蠢!我忘了声明 reg [2:0] i;而是写了 reg i; 谢谢大家

    【讨论】:

      【解决方案5】:

      伙计们,我必须告诉你,你所有的解决方案要么过于复杂,要么无法综合,要么实施到慢速多路复用器中。 OpenCores 的 Alexej Bolshakov 于 2015 年 8 月 23 日上传了一个基于 OR 元素的出色的参数化编码器。没有多路复用器,100% 可合成。他的代码(我的小格式):

      module encoder #(
        parameter LINES = 16,
        parameter WIDTH = $clog2(LINES)
      )(
        input      [LINES-1:0] unitary_in,
        output wor [WIDTH-1:0] binary_out
      );
      
      genvar i, j;
      
      generate
      for (i = 0; i < LINES; i = i + 1)
        begin: loop_i
          for (j = 0; j < WIDTH; j = j + 1)
          begin: loop_j
          if (i[j])
            assign binary_out[j] = unitary_in[i];
          end 
        end
      endgenerate
      
      endmodule
      

      RTL viewer screenshot, Model-Sim screenshot

      【讨论】:

        【解决方案6】:

        此解决方案将输入分成四个块并检查第一个非零块。以相同的方式进一步细分该块。它相当有效。

        // find position of most significant 1 bit in 64 bits input
        // (system verilog)
        module bitscan(
            input  logic [63:0] in,  // number input
            output logic [5:0]  out, // bit position output
            output logic zeroout     // indicates if input is zero
        );
        
        logic [63:0] m0;  // intermediates
        logic [15:0] m1;
        logic [3:0]  m2;
        logic [5:0]  r;
        
        always_comb begin
            m0 = in;
            // choose between four 16-bit blocks
            if (|m0[63:48]) begin
                m1 = m0[63:48];
                r[5:4] = 3;
            end else if (|m0[47:32]) begin
                m1 = m0[47:32];
                r[5:4] = 2;
            end else if (|m0[31:16]) begin
                m1 = m0[31:16];
                r[5:4] = 1;
            end else begin
                m1 = m0[15:0];
                r[5:4] = 0;
            end
        
            // choose between four 4-bit blocks
            if (|m1[15:12]) begin
                m2 = m1[15:12];
                r[3:2] = 3;
            end else if (|m0[11:8]) begin
                m2 = m1[11:8];
                r[3:2] = 2;
            end else if (|m0[7:4]) begin
                m2 = m1[7:4];
                r[3:2] = 1;
            end else begin
                m2 = m1[3:0];
                r[3:2] = 0;
            end
        
            // choose between four remaining bits    
            if      (m2[3]) r[1:0] = 3;
            else if (m2[2]) r[1:0] = 2;
            else if (m2[1]) r[1:0] = 1;
            else            r[1:0] = 0;
        
            out = r;
            zeroout = ~|m2;
        end 
        endmodule
        

        这是另一种使用资源略少的解决方案:

        module bitscan4 (
            input logic [63:0] in,
            output logic  [5:0] out,
            output logic  zout
        );
        logic [63:0] m0;
        logic [3:0]  m1;
        logic [3:0]  m2;
        logic [5:0]  r;
        
        always_comb begin
            r = 0;
            m0 = in;
            if (|m0[63:48]) begin
                r[5:4] = 3;
                m1[3] = |m0[63:60];
                m1[2] = |m0[59:56];
                m1[1] = |m0[55:53];
                m1[0] = |m0[51:48];        
            end else if (|m0[47:32]) begin
                r[5:4] = 2;
                m1[3] = |m0[47:44];
                m1[2] = |m0[43:40];
                m1[1] = |m0[39:36];
                m1[0] = |m0[35:32];
            end else if (|m0[31:16]) begin
                r[5:4] = 1;
                m1[3] = |m0[31:28];
                m1[2] = |m0[27:24];
                m1[1] = |m0[23:20];
                m1[0] = |m0[19:16];
            end else begin
                r[5:4] = 0;
                m1[3] = |m0[15:12];
                m1[2] = |m0[11:8];
                m1[1] = |m0[7:4];
                m1[0] = |m0[3:0]; 
            end
        
            if (m1[3]) begin
                r[3:2] = 3;
            end else if (m1[2]) begin
                r[3:2] = 2;
            end else if (m1[1]) begin
                r[3:2] = 1;
            end else begin
                r[3:2] = 0;
            end
        
            m2 = m0[{r[5:2],2'b0}+: 4];
        
            if      (m2[3]) r[1:0] = 3;
            else if (m2[2]) r[1:0] = 2;
            else if (m2[1]) r[1:0] = 1;
            else            r[1:0] = 0;
        
            zout = ~|m2;
            out = r;
        end 
        
        endmodule
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2023-03-31
          • 1970-01-01
          • 2012-12-16
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2011-12-20
          • 1970-01-01
          相关资源
          最近更新 更多