Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Designing a filter using scala - For loop unrolling

Tags:

scala

chisel

I am trying to create a synthesizable FIR filter using Chisel. I am reading signed integers from a csv file which I then pass to the FIR filter class as coefficients. Just to give you all a flavor - this is what the coefficient look like:

-152651856 1233223 ...

A for loop is used to then do the MAC operation across the coefficients of the filter. I can't find the reason why the for loop is not unrolling properly. It is only synthesizing the last coefficient as a constant.

class FIR (val coefficients:Array[Int], val count:Int ) extends Module {
  val io = IO(new Bundle {
    val X        = Input(Vec(count,SInt(16.W)))
    val Y_out     = Output(SInt(64.W))
  })

  val sum  = Reg(SInt(64.W))
  sum := 0.S(64.W)
  val temp = Reg(Vec(count,SInt(64.W)))


  for(ct <- 0 until count ) {

   temp(ct) := new  fromIntToLiteral(coefficients(ct): Int).S
   sum := sum + temp(ct) * io.X(ct)

  }
  io.Y_out := sum

}

Here array coefficients are the coefficients of the filter passed to the class and count is the number of coefficients.

like image 297
shodan Avatar asked Mar 11 '26 11:03

shodan


1 Answers

The important thing to keep in mind is that Chisel is not behavioral synthesis. There is no blocking assignment (although it can be emulated). Chisel instead has last connect semantics such that the last connection wins. This is primarily useful when the later connections are predicated (ie. guarded by a when), but can also be useful for overriding defaults.

To see what's happening, let's pick a value for count and unroll the loop:

// Let count = 4
temp(0) := new  fromIntToLiteral(coefficients(0): Int).S                        
sum := sum + temp(0) * io.X(0)
temp(1) := new  fromIntToLiteral(coefficients(1): Int).S
sum := sum + temp(1) * io.X(1)
temp(2) := new  fromIntToLiteral(coefficients(2): Int).S
sum := sum + temp(2) * io.X(2)
temp(3) := new  fromIntToLiteral(coefficients(3): Int).S
sum := sum + temp(3) * io.X(3)

Last connect makes this the same as:

temp(0) := new  fromIntToLiteral(coefficients(0): Int).S                        
// sum := sum + temp(0) * io.X(0)
temp(1) := new  fromIntToLiteral(coefficients(1): Int).S
// sum := sum + temp(1) * io.X(1)
temp(2) := new  fromIntToLiteral(coefficients(2): Int).S
// sum := sum + temp(2) * io.X(2)
temp(3) := new  fromIntToLiteral(coefficients(3): Int).S
sum := sum + temp(3) * io.X(3) // <--- Winning connect to sum

What you're presumably trying to do is create the sum incrementally as the for loop executes as one might in Verilog using blocking assignment. We can emulate this behavior using a var (a variable that we can reassign to) to hold the intermediate values:

  val sum  = Reg(SInt(64.W))
  val temp = Reg(Vec(count,SInt(64.W)))

  var sumVar = 0.S(64.W)
  for(ct <- 0 until count ) {
   temp(ct) := coefficients(ct).S
   sumVar = sumVar + temp(ct) * io.X(ct)
  }
  sum := sumVar    // Connect the finished logic to the sum's "next" value.
  io.Y_out := sum  // Connect current value of sum register to output

I want to highlight a few things here:

  1. Note that we use = for sumVar, think about it like a pointer to a hardware node. We're updating the pointer to point to the new logic we've constructed each iteration

  2. I may have misunderstood your intent since my code doesn't look like an FIR I'm familiar with (EDIT: The code is correct, see comments on this answer). Note that in Chisel Reg corresponds to an actual, register or array of flip-flops. It is not like Verilog reg. Reg is described here: https://www.chisel-lang.org/chisel3/sequential-circuits.html. You can also take a look at the Chisel Bootcamp which uses FIR filters as a motivating example throughout.

  3. I noticed you're directly using fromIntToLiteral. This is an implicit class that isn't really supposed to be user facing. You can just write coefficients(ct).S, Scala makes it work without you noticing so long as you do import chisel3._. If you want to know more details, implicits are how one can implement Extension Methods in Scala. When you import chisel3._ the .S method comes from fromIntToLiteral implicitly and makes it look like S is a method defined on Int. Think about it as syntactic sugar to make Chisel code more convenient to write.

like image 103
Jack Koenig Avatar answered Mar 13 '26 06:03

Jack Koenig



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!