凿子将启用添加到具有下一个字段的寄存器

Chisel adding enable to a register that has a next field

我有一个浮点加凿子模块,我想使用它有几个流水线阶段。我想让它停止,这样我就可以将它放入可能无法在任何给定时间使用输出数据的管道中,因此我希望将部分计算的添加存储在模块中。

我最初希望我可以只添加一个启用信号,然后将其添加为更新各种寄存器的另一个条件,但不幸的是,该模块包含很多 Reg(next=xxx) 形式的语句。我很好奇如果我只是将寄存器分配给它自己会发生什么,即使它的输入已经通过 next=xxx 分配,所以我制作了一个测试模块并得到了一些(在我看来)奇怪的结果。

这是 Scala:

package Hello

import Chisel._

class Hello extends Module {
  val io = new Bundle { 
    val in = UInt(INPUT, 8)
    val en = Bool(INPUT)
    val out = UInt(OUTPUT, 8)
  }
  val test_reg = Reg(next = io.in)
  io.out := test_reg
  when (!io.en) {
    test_reg := test_reg
  }
}

object Hello {
  def main(args: Array[String]): Unit = {
  }
}

这是生成的 verilog:

module Hello(input clk,
    input [7:0] io_in,
    input  io_en,
    output[7:0] io_out
);

  reg [7:0] test_reg;
  wire[7:0] T0;
  wire T1;

`ifndef SYNTHESIS
// synthesis translate_off
  integer initvar;
  initial begin
    #0.002;
    test_reg = {1{$random}};
  end
// synthesis translate_on
`endif

  assign io_out = test_reg;
  assign T0 = T1 ? test_reg : io_in;
  assign T1 = io_en ^ 1'h1;

  always @(posedge clk) begin
    if(T1) begin
      test_reg <= test_reg;
    end else begin
      test_reg <= io_in;
    end
  end
endmodule

令我感到好奇的是,verilog 似乎几乎以两种不同的方式实现了启用。它使用 T1 (!en) 在 test_reg 和 io_in 之间进行多路复用,并将输出标记为 T0。如果 T0 被无条件地作为 test_reg 的输入,我认为这将具有所需的功能。相反,T0 被完全忽略,T1 在 if else 块中用于 select 寄存器是否应该更新。

最终,这个例子似乎仍然可以正常工作,但现在我有点害怕在更复杂的浮点单元中使用它,如果它在简单情况下的行为有点出乎意料的话。

是否有更优雅的方法来停止浮点加法模块的流水线?我最初喜欢上面的方法,因为我可以在末尾添加一个 when(!en) 块,它只将所有状态的输出写入其输入。我认为另一种方法是用 Reg() 替换 Reg(next=xxx) 的任何实例,然后用 when(en) {reg := next} 块来更新寄存器。最终我想学习凿子,所以我想知道最干净的方法是什么。

供参考,我说的浮点加法模块是:https://github.com/zhemao/chisel-float/blob/master/src/main/scala/FPAdd.scala

我从来没有用过凿子,但我猜是这样的:

when (io.en) {
  test_reg := io.in
}

会产生这样的结果:

always @(posedge clk) begin
  if(io_en) begin
    test_reg <= io_in;
  end
end

我认为这与您现在拥有的相同,但更具可读性。

val test_reg = Reg(next = io.in)
io.out := test_reg
when (!io.en) {
  test_reg := test_reg
}

我认为这是错误的编码习惯 - 使用 Reg(next=...) 或通过 test_reg := ... 指定下一个值,但不要将两者混用!首先,reader 哪个 writer 应该优先(尽管答案是 "last writer wins")是模棱两可的。其次,当 reader 看到 Reg(next=...) 时,他可能不希望看到该作者在代码的其他地方被覆盖。

写成:

val test_reg = Reg(io.in.clone())
when (io.en) { 
   test_reg := io.in
}

它仍然会生成一些未使用的信号,但代码的意图更加清晰。

always @(posedge clk) begin
  if(io_en) begin
    test_reg <= io_in;
  end
end