Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to use chisel dsptools with floats

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?

like image 357
Tampler Avatar asked Dec 22 '25 03:12

Tampler


2 Answers

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.

like image 116
Chick Markley Avatar answered Dec 23 '25 20:12

Chick Markley


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)
  }
}
like image 37
Tampler Avatar answered Dec 23 '25 20:12

Tampler