Search code examples
scalaloopshdlchisel

Reassign a variable multiple times within a clock cycle - Chisel


I would like to reassign the variable hit_bits multiple times during a single clock cycle. hit_bits will be increased whenever io.bits_perf.bits(i) is true. I'm getting "FOUND COMBINATIONAL PATH!" when I try to compile the code.

Any idea?

  val hit_bits = Bits()
  hit_bits := Bits(0)

  when(io.bits_perf.valid){
    for(i<- 0 until 3){
      when(io.bits_perf.bits(i)) { hit_bits := hit_bits + Bits(1) 
      }
    }
  }

Solution

  • For this example, it is important to keep in mind the difference between Chisel and Scala. More specifically, when is a Chisel construct that maps to conditional connections in Verilog, whereas for is a Scala construct that we can use to generate hardware (similar to generate in Verilog).

    Let's unroll this for loop and see what we get:

    when(io.bits_perf.valid){
      when(io.bits_perf.bits(0)) { hit_bits := hit_bits + Bits(1) }
      when(io.bits_perf.bits(1)) { hit_bits := hit_bits + Bits(1) }
      when(io.bits_perf.bits(2)) { hit_bits := hit_bits + Bits(1) }
    }
    

    Note that all of the connections are the same, when io.bits_perf.valid is high and any of the bits in io.bits_perf.bits is high, you will be connecting hit_bits to hit_bits + Bits(1). This is the combinational loop.

    Now, let's figure out how to express what you're really trying to do: how to connect hit_bits to the number of ones in io.bits_perf.bits when io.bits_perf.valid is high. This is also known as a popcount for which Chisel just so happens to have a utility. What you should do is use that:

      val hit_bits = Bits()
      hit_bits := Bits(0)
    
      when(io.bits_perf.valid) {
        hit_bits := PopCount(io.bits_perf.bits)
      }
    

    However, the code you wrote is close to correct so let's make it work anyway. What we want to do is use a Scala for loop to do some code generation. One way to do this is to use a Scala var (which allows reassignment) as kind of a "pointer" to Chisel nodes rather than a val which only allows single assignment (and thus can't be changed to point to a different Chisel node).

    var hit_bits = Bits(0, 2) // We set the width because + does not expand width
    when (io.bits_perf.valid) {
      for (i <- 0 until 3) {
        hit_bits = hit_bits + io.bits_perf.bits(i)
      }
    }
    // Now hit_bits is equal to the popcount of io.bits_perf.bits (or 0 if not valid)
    

    Note that I also dropped the inner when conditional since you can just add the bit directly rather than conditionally adding 1. What is happening here is that hit_bits is a reference to Chisel nodes, starting with 0. Then, for each index in the for loop, we change the node hit_bits refers to to be the Chisel node that is the output of addition of the previous node hit_bits referred to and a bit of io.bits_perf.bits.