0

We are implementing a processor pipeline, and want an efficient way to stall it. If we had control over the circuit, then we would use latches that have an enable input. To stall, just disable the latches, and then they won't update on the next clock edge, they will just stay the same until enabled again.

But, how to do that in Chisel? It's not clear what a "when" statement will translate into. Help with the black magic behind the scenes, and hints on how to control the end circuit would be much appreciated.

seanhalle
  • 973
  • 7
  • 27

3 Answers3

0

when statements are essentially just shorthand ways to help construct mux trees. For example:

class SimpleWhen extends Module {
  val io = IO(new Bundle {
    val cond = Input(Bool())
    val a = Input(UInt(32.W))
    val b = Input(UInt(32.W))
    val z = Output(UInt(32.W))
  })

  when (io.cond) { io.z := io.a }
  .otherwise { io.z := io.b }
}

If we generate Verilog from this we get (in Chisel 3):

module SimpleWhen(
  input   clock,
  input   reset,
  input   io_cond,
  input  [31:0] io_a,
  input  [31:0] io_b,
  output [31:0] io_z
);
  wire  _T_11;
  wire [31:0] _GEN_1;
  assign io_z = _GEN_1;
  assign _T_11 = io_cond == 1'h0;
  assign _GEN_1 = _T_11 ? io_b : io_a;
endmodule

As one would expect, this module essentially just muxes between a and b based on the condition cond.

For your more interesting use case of registers with enable:

class RegEnableTest extends Module {
  val io = IO(new Bundle {
    val en = Input(Bool())
    val a = Input(Valid(UInt(32.W)))
    val b = Input(Valid(UInt(32.W)))
    val out = Output(UInt(32.W))
  })

  val regNext = Wire(UInt(32.W))
  val myReg = Reg(UInt(32.W))
  when (io.en) { myReg := regNext }
  regNext := myReg

  when (io.a.valid) { regNext := io.a.bits }
  when (io.b.valid) { regNext := io.b.bits }

  io.out := myReg
}

Here we have a register myReg that is only updated when the input en is high. The generated Verilog is:

module RegEnableTest(
  input   clock,
  input   reset,
  input   io_en,
  input   io_a_valid,
  input  [31:0] io_a_bits,
  input   io_b_valid,
  input  [31:0] io_b_bits,
  output [31:0] io_out
);
  wire [31:0] regNext;
  reg [31:0] myReg;
  reg [31:0] _GEN_1;
  wire [31:0] _GEN_0;
  wire [31:0] _GEN_2;
  assign io_out = myReg;
  assign regNext = _GEN_2;
  assign _GEN_0 = io_en ? regNext : myReg;
  assign _GEN_2 = io_b_valid ? io_b_bits : io_a_bits;
// ... Randomization removed for clarity ...
  always @(posedge clock) begin
    if (io_en) begin
      myReg <= regNext;
    end
  end
endmodule

myReg is only updated when io.en is high. This code can be made somewhat more concise by using RegEnable (https://chisel.eecs.berkeley.edu/api/index.html#chisel3.util.RegEnable$)

Jack Koenig
  • 5,840
  • 15
  • 21
  • These capture the functionality, but the question was about something else. There are native circuit implementations of flip-flops with enable. In these, the enable is implemented with transistors in a much more compact, fast, and energy efficient manner. The question was about forcing Chisel to cause these particular circuit elements to be chosen from the foundry's library during synthesis. If Chisel implements the functionality with muxes then there is no way for the synthesis tools to discover that they can use the much better native circuit version. – seanhalle Jan 25 '17 at 05:44
  • Chisel generates Verilog in such a way so that the synthesis tools **can** infer such things as flip flops with enable. In a recent run of [rocket-chip](https://github.com/ucb-bar/rocket-chip) (DefaultConfig), we found that 99% of registers were synthesized (in an educational process) as gated registers. – Jack Koenig Jan 25 '17 at 17:36
0

Here is how to do it using RegEnable, the helper object constructing enabled registers: https://github.com/ucb-bar/chisel3/blob/master/src/main/scala/chisel3/util/Reg.scala

import chisel3._
import chisel3.util._

class RegEnableTest extends Module {
  val io = IO(new Bundle {
    val en = Input(Bool())
    val a = Input(Valid(UInt(32.W)))
    val b = Input(Valid(UInt(32.W)))
    val out = Output(UInt(32.W))
  })

  val regNext = Wire(UInt(32.W))
  val myReg = RegEnable(regNext,io.en)

  when (io.a.valid) { regNext := io.a.bits }
  when (io.b.valid) { regNext := io.b.bits }

  io.out := myReg
}

This produces the following verilog (edited to remove unneeded macros)

module RegEnableTest(
  input   clock,
  input   reset,
  input   io_en,
  input   io_a_valid,
  input  [31:0] io_a_bits,
  input   io_b_valid,
  input  [31:0] io_b_bits,
  output [31:0] io_out
);
  wire [31:0] regNext;
  reg [31:0] myReg;
  reg [31:0] _GEN_1;
  wire [31:0] _GEN_0;
  wire [31:0] _GEN_2;
  assign io_out = myReg;
  assign regNext = _GEN_2;
  assign _GEN_0 = io_en ? regNext : myReg;
  assign _GEN_2 = io_b_valid ? io_b_bits : io_a_bits;

  always @(posedge clock) begin
    if (io_en) begin
      myReg <= regNext;
    end
  end
endmodule
Steve Burns
  • 309
  • 1
  • 8
0

Or you can do it this way:

  val regNext = Wire(UInt(32.W))

  when (io.a.valid) { regNext := io.a.bits }
  when (io.b.valid) { regNext := io.b.bits }

  io.out := RegEnable(regNext,io.en)

but then the state doesn't have a good name in the verilog code

  assign io_out = _T_30;
  assign regNext = _GEN_1;
  assign _GEN_1 = io_b_valid ? io_b_bits : io_a_bits;
  assign _GEN_2 = io_en ? regNext : _T_30;

  always @(posedge clock) begin
    if (io_en) begin
      _T_30 <= regNext;
    end
  end
  endmodule
Steve Burns
  • 309
  • 1
  • 8