[Firtool] chisel for loop to verilog

I have the following loop in Chisel, that counts the number of ones in a register:

 val word = RegInit(0.U(32.W))
 // Register for data input
 word := Mux(io.optn === 1.U, io.dataIn, word)

 counter := 0.U
  for (i <- 31 to 0 by -1) {
    when(word(i)) {
      counter := counter + 1.U
    } .otherwise {
      counter := counter
    }
  }

Using Firtool to generate the equivalent verilog I get this:

 reg [31:0] word; 
 reg [5:0] counter; 
 wire [5:0] _counter_T_1 = counter + 6'h1; 
  always @(posedge clock) begin
    if (reset) begin 
      word <= 32'h0; 
    end else if (io_optn == 2'h1) begin 
      word <= io_dataIn;
    end
    if (reset) begin 
      counter <= 6'h0; 
    end else if (word[0]) begin 
      counter <= _counter_T_1; 
    end

I am a beginner with hardware description languages, and I don’t understand why word is indexed with 0 at each cycle, in the generated verilog ?

The canonicaliser in Firtool appears to collapse all the bit accesses of the unrolled loop at word[i] into word[0]. Is there something I am missing about the properties of registers ? (e.g. word is shifted each cycle)

This is Chisel and FIRRTL’s last connect semantics coming into play. What’s going on is that the code above is the same as:

when(word(31)) {
  counter := counter + 1.U
} .otherwise {
  counter := counter
}
when(word(30)) {
  counter := counter + 1.U
} .otherwise {
  counter := counter
}
// ...
when(word(0)) {
  counter := counter + 1.U
} .otherwise {
  counter := counter
}

firtool recognizes that the last when block is the only one that matters and it optimizes away all the dead previous ones.

You’re likely looking for a population count utility ( chisel3.util.PopCount / PopCount$ - chisel_2.13 6.0.0-M3 javadoc) and can then update the counter with the number of ones in word.

1 Like

Adding to what @seldridge said: if you wanted to implement this yourself, you’d have to make counter be a Scala variable that you keep updating. Using the := connect operator will override all previous assignments, which isn’t what you want. For example:

val word = RegInit(0.U(32.W))
word := Mux(io.optn === 1.U, io.dataIn, word)

var currentCount = 0.U
for (i <- 31 to 0 by -1) {
  currentCount = currentCount + word(i).asUInt
}
counter := currentCount

With := you are redefining the value of counter 32 times, and only the last definition will stick around. And note that after counter := ... in the previous loop happened, the following loop iteration will not read that previously assigned value for counter, it will read what counter will eventually be assigned to after all assignments have been resolved.

Instead, you want to use a Scala var and build up a nesting of 32 adders that add each word bit to the count of the previous iteration. Then at the very end just assign that as the counter value.

In practice you’ll want to create a more optimized tree structure for this, which is what @seldridge’s suggestion will give you.

2 Likes

Brilliant explanations, thank you! It makes a lot more sense now :slight_smile: