将 x(无关)分配给组合输出的寄存器复位值以提高面积效率

Assigning x (dont care) to a register reset value or combinatorical output to improve area efficiency

我的问题是关于 FPGA 设计的——如果我的设计中有一些我不关心它们的复位值是多少的寄存器,我可以将复位值设置为 x 吗?这会提高面积效率吗(合成器是否能够利用它并以更有效的方式生成设计?) 例如,

always @(posedge clk or negedge reset_n) begin
    if(~reset_n) begin
        reg_1 <= 'x
    end
...
end

编辑: 另一个类似主题的问题——假设我有一个状态机,例如我不关心某些输出在某些状态下会是什么——将它们设置为“x”会提高面积效率吗? 例如,如果我有一个具有两个状态 STATE_1、STATE_2 和两个输出的 fsm,综合工具将能够利用以下代码:

always_comb begin
   case(state):
   STATE_1:begin
      out_1 = 1;
      out_2 = x;
   end
   STATE_2:begin
      out_1 = x;
      out_2 = 0;
   end
end

比这个更好:

always_comb begin
   case(state):
   STATE_1:begin
      out_1 = 1;
      out_2 = 0;
   end
   STATE_2:begin
      out_1 = 0;
      out_2 = 0;
   end
end

(假设我不关心 STATE_1 中的 out_2 和 STATE_2 中的 out_1 是什么)。 谢谢

在顺序逻辑中使用 'x

是的,在 Verilog 方面,您可以使用此语法。但是,在您的特定示例中,这样做并没有真正意义,您可以认为这是一种糟糕的编码实践。除了显式分配 'x,您还可以省略异步重置。

以下两个进程将综合到同一个触发器。我个人会推荐使用后一种风格。

// Assigning 'x to tell synthesis tool that there is no reset value
always @(posedge clk or negedge reset_n)
   if(~reset_n)
      reg_1 <= 'x;
   else
      reg_1 <= reg_1_next;


// Omitting the asynchronous reset from the sensitivity list to tell
// synthesis tool that there is no reset
always @(posedge clk)
   reg_1 <= reg_1_next;
    

一般来说:如果您有不同的变量,这些变量必须是可重置的或不可重置的,您应该将它们的赋值分成不同的 always 块。这通常会使您的代码更具可读性。请参阅以下示例:

// Do NOT do this
always @(posedge clk or negedge reset_n)
   if(~reset_n) 
   begin
      vld   <= 1'b0;
      reg_1 <= 'x;
   end
   else
   begin
      vld   <= vld_next;
      reg_1 <= reg_1_next;
   end


// Do this
always @(posedge clk or negedge reset_n)
   if(~reset_n) 
      vld   <= 1'b0;
   else
      vld   <= vld_next;

always @(posedge clk)
    reg_1 <= reg_1_next;

奖金

话虽如此,在某些情况下,在复位条件中分配 'x 以告诉综合工具 为特定的生成可复位触发器是有意义的变量。请看看这个答案:

让我们根据这个答案创建一个示例。假设您有一个包含 1 个有效信号 (vld) 和 2 个数据信号(data_adata_b)的结构。该数据仅在vld1'b1时有效。换句话说,我们可以通过只重置vld而不重置data_adata_b来节省空间。

现在,我们想充分发挥结构的潜力,并简单地分配完整的结构而不是单独的成员(参见 struct_example_q <= struct_example_next;)。这意味着我们 不能 将这个 always-block 分成两个单独的进程(就像我之前推荐的那样)。在这种情况下,我们必须明确告诉综合工具不要重置数据信号。

查看下面的代码:

   typedef struct {
      logic        vld;
      logic [31:0] data_a;
      logic [31:0] data_b;
  } struct_example_t;

  struct_example_t struct_example_next;
  struct_example_t struct_example_q;

  always @(posedge clk or negedge reset_n)
     if (!reset_n)
     begin
         /**
          * Only reset the valid-bit
          * We could use '{default:'x} to simplify this even further
          **/
         struct_example_q.data_a <= 'x;
         struct_example_q.data_b <= 'x;
         struct_example_q.vld    <= 1'b0;
     end
     else
     begin
         struct_example_q <= struct_example_next;
     end

在组合逻辑中使用'x

让我们先看看您的 RTL:

always_comb begin
   case(state):
   STATE_1:begin
      out_1 = 1;
      out_2 = x;
   end
   STATE_2:begin
      out_1 = x;
      out_2 = 0;
   end
end

我想指出,这并不是最好的例子。假设 FSM 是满的——即 STATE_1STATE_2 是仅有的两个状态 state 可以采用——你将使用下面的代码实现完全相同的效果,假设你不这样做无论如何,关于其他州的 out_1out_2 的情况。

always_comb begin
    out_1 = 1;
    out_2 = 0;
end

现在,为了示例起见,假设我们无法重写它。在这种情况下,您应该在案例陈述之前设置默认值。这可以防止综合逻辑在您不关心的状态下推断锁存器,但它也可以帮助您 not 运行 一旦开始就遇到 'x 的问题进行门级仿真 (GLS)。使用您的示例,您的 RTL 将类似于下面的代码。 (再次注意这里的案例有点多余。)

always_comb begin
   out_1 = 1;
   out_2 = 0;

   case(state):
   STATE_1:begin
      out_1 = 1;
   end
   STATE_2:begin
      out_2 = 0;
   end
end

一旦你有了更精细的 FSM,你就会发现这个策略是有意义的。


奖金

我想举一个例子,其中使用 uniquepriority 是有意义的(而不是使用 'x 作为默认值)。看看下面的 RTL,假设 select == 3'b0 永远不会发生:

always_comb
begin
    out_1 = 'x;

    case (1'b1)
        select[0]: out_1 = a & b;
        select[1]: out_1 = a ^ b;
        select[2]: out_1 = a | b;
    endcase
end

out_1 设置默认值将阻止逻辑推断锁存器(因为它不知道 select == 3'b0 永远不会发生)。此外,这里的'x会帮助综合工具优化这个逻辑(不一定w.r.t.area!)。然而,正如我们之前所讨论的,使用 'x 通常被认为是不好的做法。

您可以使用 priority 关键字来告诉综合工具已列出所有有效案例,并且该工具必须按顺序评估您的案例,而不是使用默认值。因此,以下情况也将被视为已满:

always_comb
    priority case (1'b1)
        select[0]: out_1 = a & b;
        select[1]: out_1 = a ^ b;
        select[2]: out_1 = a | b;
    endcase

另外,如果您可以确定 select 是单热信号 ($countones(select) == 1),则可以使用 unique 关键字。这将告诉综合工具这是完全并行的情况

always_comb
    unique case (1'b1)
        select[0]: out_1 = a & b;
        select[1]: out_1 = a ^ b;
        select[2]: out_1 = a | b;
    endcase

请注意,如果您违反使用 priorityunique.

所必需的假设,模拟器将尝试通过向您抛出错误来强制执行这些假设。