3

I need to convert a Float32 into a Chisel FixedPoint, perform some computation and convert back FixedPoint to Float32.

For example, I need the following:

val a = 3.1F
val b = 2.2F
val res = a * b // REPL returns res: Float 6.82

Now, I do this:

import chisel3.experimental.FixedPoint

val fp_tpe = FixedPoint(6.W, 2.BP)
val a_fix = a.Something (fp_tpe) // convert a to FixPoint
val b_fix = b.Something (fp_tpe) // convert b to FixPoint
val res_fix = a_fix * b_fix
val res0 = res_fix.Something (fp_tpe) // convert back to Float

As a result, I'd expect the delta to be in a range of , e.g

val eps = 1e-4
assert ( abs(res - res0) < eps, "The error is too big")

Who can provide a working example for Chisel3 FixedPoint class for the pseudocode above?

Tampler
  • 385
  • 1
  • 9

3 Answers3

3

Take a look at the following code:

import chisel3._
import chisel3.core.FixedPoint
import dsptools._


class FPMultiplier extends Module {
  val io = IO(new Bundle {
    val a = Input(FixedPoint(6.W, binaryPoint = 2.BP))
    val b = Input(FixedPoint(6.W, binaryPoint = 2.BP))
    val c = Output(FixedPoint(12.W, binaryPoint = 4.BP))
  })

  io.c := io.a * io.b
}

class FPMultiplierTester(c: FPMultiplier) extends DspTester(c) {
  //
  // This will PASS, there is sufficient precision to model the inputs
  //
  poke(c.io.a, 3.25)
  poke(c.io.b, 2.5)

  step(1)
  expect(c.io.c, 8.125)

  //
  // This will FAIL, there is not sufficient precision to model the inputs
  // But this is only caught on output, this is likely the right approach
  // because you can't really pass in wrong precision data in hardware.
  //
  poke(c.io.a, 3.1)
  poke(c.io.b, 2.2)

  step(1)
  expect(c.io.c, 6.82)
}


object FPMultiplierMain {
  def main(args: Array[String]): Unit = {
    iotesters.Driver.execute(Array("-fiv"), () => new FPMultiplier) { c =>
      new FPMultiplierTester(c)
    }
  }
}

I'd also suggest looking at ParameterizedAdder in dsptools, that gives you a feel of how to write hardware modules that you pass different types. Generally you start with DspReals, confirm the model then start experimenting/calculating with FixedPoint sizes that return results with the desired precision.

Chick Markley
  • 4,023
  • 16
  • 17
  • I can't compile this code as well as the Parametrized Adder in the dsptools home folder. If I do: sbt "test:runMain examples.ParametrizedAdderSpec" or sbt "test:runMain my_pkg.FPMultiplierMain", I get Unresolved dependencies: Note: Unresolved dependencies path: [warn] edu.berkeley.cs:chisel3_2.12:3.2-SNAPSHOT (/home/bku/work/cores/bar/dsptools/build.sbt#L99) [warn] +- edu.berkeley.cs:dsptools_2.12:1.3-SNAPSHOT [warn] edu.berkeley.cs:chisel-iotesters_2.12:1.3-SNAPSHOT (/home/bku/work/cores/bar/dsptools/build.sbt#L99) – Tampler Sep 24 '18 at 09:43
  • The dsptools is still in the process of being formally released and has some problems in the master (development) branch. Try changing to release 1.1.3 by running "git checkout 1.1.3" and then running the test – Chick Markley Sep 24 '18 at 16:43
  • Excellent, that worked! Now, how do I set peek tolerance ? I tried DspTester.setTol(1e-4, 8) and this.setTol(1e-4, 8), but those won't compile – Tampler Sep 24 '18 at 17:55
  • I think the controls you are looking for are in src/main/scala/dsptools/tester/DspTesterOptions.scala. You pass these controls in by adding flags to the dspTestersOptions instance in DspTesterOptionsManager – Chick Markley Sep 26 '18 at 16:01
  • Yeah, that's exactly what I did. I found solution for this, but I can't figure out how to pass complex numbers to my module. No examples in your dsptools project for complex stuff! Pls check my latest code in the question. I posted a parametrized implementation and need to update it to support a Complex Ring as well. Thanks! – Tampler Sep 26 '18 at 17:30
  • Hi @ChickMarkley I was trying to use the FixedPoint Format that is mentioned here, but when I pass this value say 4.BP I get an error saying -> value BP is not a member of Int. Can you help me fix this error? Is there a new way of representing FixedPoint numbers in chisel that I am unaware of? – CV_Ruddha Jul 22 '20 at 23:50
1

For others benefit, I provide an improved solution from @Chick, rewritten in a more abstract Scala with variable DSP tolerances.

package my_pkg

import chisel3._
import chisel3.core.{FixedPoint => FP}

import dsptools.{DspTester, DspTesterOptions, DspTesterOptionsManager}

class FPGenericIO (inType:FP, outType:FP) extends Bundle {
  val a = Input(inType)
  val b = Input(inType)
  val c = Output(outType)
}

class FPMul (inType:FP, outType:FP) extends Module {
  val io  = IO(new FPGenericIO(inType, outType))
  io.c := io.a * io.b
}

class FPMulTester(c: FPMul) extends DspTester(c) {

  val uut = c.io

  // This will PASS, there is sufficient precision to model the inputs
  poke(uut.a, 3.25)
  poke(uut.b, 2.5)

  step(1)
  expect(uut.c, 3.25*2.5)

  // This will FAIL, if you won't increase tolerance, which is eps = 0.0 by default
  poke(uut.a, 3.1)
  poke(uut.b, 2.2)

  step(1)
  expect(uut.c, 3.1*2.2)
}


object FPUMain extends App {

  val fpInType  = FP(8.W, 4.BP)
  val fpOutType = FP(12.W, 6.BP)

// Update default DspTester options and increase tolerance
  val opts = new DspTesterOptionsManager {

    dspTesterOptions = DspTesterOptions(
      fixTolLSBs = 2,
      genVerilogTb = false,
      isVerbose = true
    )
  }

  dsptools.Driver.execute (() => new FPMul(fpInType, fpOutType), opts) {
    c => new FPMulTester(c)
  }
}
Tampler
  • 385
  • 1
  • 9
1

Here's my ultimate DSP multiplier implementation, which should support both FixedPoint and DspComplex numbers. @ChickMarkley, how do I update this class to implement a Complex multiplication?

package my_pkg

import chisel3._

import dsptools.numbers.{Ring,DspComplex}
import dsptools.numbers.implicits._
import dsptools.{DspContext}

import chisel3.core.{FixedPoint => FP}
import dsptools.{DspTester, DspTesterOptions, DspTesterOptionsManager}


class FPGenericIO[A <: Data:Ring, B <: Data:Ring] (inType:A, outType:B) extends Bundle {
  val a = Input(inType.cloneType)
  val b = Input(inType.cloneType)
  val c = Output(outType.cloneType)

  override def cloneType = (new FPGenericIO(inType, outType)).asInstanceOf[this.type]

}

class FPMul[A <: Data:Ring, B <: Data:Ring] (inType:A, outType:B) extends Module {

  val io  = IO(new FPGenericIO(inType, outType))

  DspContext.withNumMulPipes(3) {
    io.c := io.a * io.b
  }
}

class FPMulTester[A <: Data:Ring, B <: Data:Ring](c: FPMul[A,B]) extends DspTester(c) {

  val uut = c.io

  //
  // This will PASS, there is sufficient precision to model the inputs
  //
  poke(uut.a, 3.25)
  poke(uut.b, 2.5)

  step(1)
  expect(uut.c, 3.25*2.5)

  //
  // This will FAIL, there is not sufficient precision to model the inputs
  // But this is only caught on output, this is likely the right approach
  // because you can't really pass in wrong precision data in hardware.
  //
  poke(uut.a, 3.1)
  poke(uut.b, 2.2)

  step(1)
  expect(uut.c, 3.1*2.2)
}


object FPUMain extends App {

  val fpInType  = FP(8.W, 4.BP)
  val fpOutType = FP(12.W, 6.BP)
  //val comp = DspComplex[Double] // How to declare a complex DSP type ?

  val opts = new DspTesterOptionsManager {

    dspTesterOptions = DspTesterOptions(
      fixTolLSBs = 0,
      genVerilogTb = false,
      isVerbose = true
    )
  }

  dsptools.Driver.execute (() => new FPMul(fpInType, fpOutType), opts) {
  //dsptools.Driver.execute (() => new FPMul(comp, comp), opts) { // <-- this won't compile
    c => new FPMulTester(c)
  }
}
Tampler
  • 385
  • 1
  • 9