From 90358a334cce56e271cf133f255275077213af5f Mon Sep 17 00:00:00 2001 From: Martin Schoeberl Date: Fri, 1 Nov 2024 16:00:29 -0700 Subject: [PATCH] Drop dsptools and Cordic components --- README.md | 4 +- build.sbt | 3 +- .../lib/cordic/iterative/Constants.scala | 26 -- .../chisel/lib/cordic/iterative/Cordic.scala | 223 ------------------ .../lib/cordic/iterative/CordicApp.scala | 66 ------ .../cordic/iterative/FixedCordicSpec.scala | 131 ---------- .../cordic/iterative/FixedCordicTester.scala | 97 -------- 7 files changed, 3 insertions(+), 547 deletions(-) delete mode 100644 src/main/scala/chisel/lib/cordic/iterative/Constants.scala delete mode 100644 src/main/scala/chisel/lib/cordic/iterative/Cordic.scala delete mode 100644 src/main/scala/chisel/lib/cordic/iterative/CordicApp.scala delete mode 100644 src/test/scala/chisel/lib/cordic/iterative/FixedCordicSpec.scala delete mode 100644 src/test/scala/chisel/lib/cordic/iterative/FixedCordicTester.scala diff --git a/README.md b/README.md index d1929c0..2e689f0 100644 --- a/README.md +++ b/README.md @@ -25,7 +25,7 @@ the community. This is the place to do it. line to your ```build.sbt``` ``` -libraryDependencies += "edu.berkeley.cs" % "ip-contributions" % "0.5.4" +libraryDependencies += "edu.berkeley.cs" % "ip-contributions" % "0.6.1" ``` ### Versions @@ -33,7 +33,7 @@ libraryDependencies += "edu.berkeley.cs" % "ip-contributions" % "0.5.4" | ip-contributions | Chisel | Scala | |------------------|--------|-------| -| 0.6.0 | 3.6.1 | 2.13 | +| 0.6.1 | 3.6.1 | 2.13 | | 0.5.4 | 3.5.6 | 2.13 | | 0.5.3 | 3.5.6 | 2.12 | | 0.5.1 | 3.5.5 | 2.12 | diff --git a/build.sbt b/build.sbt index 77efb90..3a86b3d 100644 --- a/build.sbt +++ b/build.sbt @@ -2,7 +2,7 @@ ThisBuild / scalaVersion := "2.13.14" // ThisBuild / crossScalaVersions := Seq("2.12.17", "2.13.10") -ThisBuild / version := "0.6.0" +ThisBuild / version := "0.6.1" lazy val publishSettings = Seq ( @@ -35,7 +35,6 @@ lazy val root = (project in file(".")) resolvers += Resolver.sonatypeRepo("snapshots"), libraryDependencies ++= Seq( "edu.berkeley.cs" %% "chisel3" % "3.6.1", - "edu.berkeley.cs" %% "dsptools" % "1.5.6", "edu.berkeley.cs" %% "chiseltest" % "0.6.2" % "test", ), scalacOptions ++= Seq( diff --git a/src/main/scala/chisel/lib/cordic/iterative/Constants.scala b/src/main/scala/chisel/lib/cordic/iterative/Constants.scala deleted file mode 100644 index 6ca168b..0000000 --- a/src/main/scala/chisel/lib/cordic/iterative/Constants.scala +++ /dev/null @@ -1,26 +0,0 @@ -// See README.md for license details. - -package chisel.lib.cordic.iterative - -import breeze.numerics.{atan, pow, sqrt} - -/** - * Object for computing useful constants - */ -object Constants { - - /** - * Get sequences of length n that go 1.0, 0.5, 0.25, ... - */ - def linear(n: Int): IndexedSeq[Double] = for (i <- 0 until n) yield pow(2.0, -i) - - /** - * Get gain for n-stage CORDIC - */ - def gain(n: Int): Double = linear(n).map(x => sqrt(1 + x * x)).reduce(_ * _) - - /** - * Get sequences of length n that go atan(1), atan(0.5), atan(0.25), ... - */ - def arctan(n: Int): IndexedSeq[Double] = linear(n).map(atan(_)) -} diff --git a/src/main/scala/chisel/lib/cordic/iterative/Cordic.scala b/src/main/scala/chisel/lib/cordic/iterative/Cordic.scala deleted file mode 100644 index e13c5a2..0000000 --- a/src/main/scala/chisel/lib/cordic/iterative/Cordic.scala +++ /dev/null @@ -1,223 +0,0 @@ -// See LICENSE for license details. - -package chisel.lib.cordic.iterative - -import chisel3._ -import chisel3.experimental.FixedPoint -import chisel3.util.Decoupled -import chisel3.util.log2Ceil - -import scala.math.{ceil, max} -import dsptools.numbers._ - -/** - * Base class for CORDIC parameters - * - * These are type generic - */ -trait CordicParams[T <: Data] { - val protoXY: T - val protoZ: T - val protoXYZ: T - val nStages: Int - val correctGain: Boolean - val stagesPerCycle: Int -} - -/** - * CORDIC parameters object for fixed-point CORDICs - */ -case class FixedCordicParams( - // width of X and Y - xyWidth: Int, - // width of Z - zWidth: Int, - // scale output by correction factor? - correctGain: Boolean = true, - // number of CORDIC stages to perform per clock cycle - stagesPerCycle: Int = 1) - extends CordicParams[FixedPoint] { - - // prototype for x and y - // binary point is (xyWidth-2) to represent 1.0 exactly - // need an extra digit for XY in the situation where we don't gain correct - val protoXY = FixedPoint(xyWidth.W, if (correctGain) (xyWidth - 2).BP else (xyWidth - 3).BP) - // prototype for z - // binary point is (xyWidth-3) to represent Pi/2 exactly - val protoZ = FixedPoint(zWidth.W, (zWidth - 3).BP) - // internal width needs to be n + log2(n) + 2 where n is output width - val totalWidth: Int = max(xyWidth, zWidth) + log2Ceil(max(xyWidth, zWidth)) + 2 - val protoXYZ = FixedPoint(totalWidth.W, (totalWidth - 3).BP) - // make nStages be output width precision, as integer multiple of stagesPerCycle - val nStages: Int = ceil(max(xyWidth, zWidth) / stagesPerCycle).toInt * stagesPerCycle -} - -/** - * Bundle type that describes the input, and output of CORDIC - */ -class CordicBundle[T <: Data](params: CordicParams[T]) extends Bundle { - val x: T = params.protoXY.cloneType - val y: T = params.protoXY.cloneType - val z: T = params.protoZ.cloneType - -} -object CordicBundle { - def apply[T <: Data](params: CordicParams[T]): CordicBundle[T] = new CordicBundle(params) -} - -/** - * To connect vectoring from AXI4 - */ -class CordicBundleWithVectoring[T <: Data](params: CordicParams[T]) extends CordicBundle[T](params) { - val vectoring: Bool = Bool() - -} -object CordicBundleWithVectoring { - def apply[T <: Data](params: CordicParams[T]): CordicBundleWithVectoring[T] = new CordicBundleWithVectoring(params) -} - -/** - * Bundle type that describes the internal state of CORDIC (needed for better fixed point accuracy - */ -class CordicInternalBundle[T <: Data](params: CordicParams[T]) extends Bundle { - val x: T = params.protoXYZ.cloneType - val y: T = params.protoXYZ.cloneType - val z: T = params.protoXYZ.cloneType - -} -object CordicInternalBundle { - def apply[T <: Data](params: CordicParams[T]): CordicInternalBundle[T] = new CordicInternalBundle(params) -} - -/** - * Bundle type as IO for iterative CORDIC modules - */ -class IterativeCordicIO[T <: Data](params: CordicParams[T]) extends Bundle { - val in = Flipped(Decoupled(new CordicBundleWithVectoring(params))) - val out = Decoupled(CordicBundle(params)) - - //val vectoring = Input(Bool()) - -} -object IterativeCordicIO { - def apply[T <: Data](params: CordicParams[T]): IterativeCordicIO[T] = - new IterativeCordicIO(params) -} - -object AddSub { - def apply[T <: Data: Ring](sel: Bool, a: T, b: T): T = { - Mux(sel, a + b, a - b) - } -} - -class IterativeCordic[T <: Data: Real: BinaryRepresentation](val params: CordicParams[T]) extends Module { - // Check parameters - require(params.nStages > 0) - require(params.stagesPerCycle > 0) - require(params.nStages >= params.stagesPerCycle) - require(params.nStages % params.stagesPerCycle == 0, "nStages % stagesPerCycle must equal 0") - - val io = IO(IterativeCordicIO(params)) - - // SETUP - regs, wires, constants - - // register to store vectoring or else it will grab the random value from the bus - val vecReg = Reg(Bool()) - - // registers to store xi, yi, and zi - val xyz = Reg(CordicInternalBundle(params)) - - // intermediate wires for loop unrolling - val xyzn = Wire(Vec(params.stagesPerCycle + 1, CordicInternalBundle(params))) - - // counter that will control for how many cycles we are computing & not ready to receive more data - val counter = RegInit(0.U(log2Ceil(params.nStages + 1).W)) - - // sequence of calculated arctans => vector called alpha - val alpha = VecInit(Constants.arctan(params.nStages).map(params.protoXYZ.fromDouble(_))) - - // perform range extension as well using an initial 90 degree rotation if necessary - // initial rotation by pi/2 if in quadrants II or III - val halfPi = params.protoXYZ.fromDouble(scala.math.Pi / 2) - val ext = Mux(io.in.bits.vectoring, io.in.bits.x.isSignNegative(), io.in.bits.z.abs() > halfPi) - val bottom = Mux(io.in.bits.vectoring, io.in.bits.y.isSignNegative(), io.in.bits.z.isSignNegative()) - val extReg, bottomReg = Reg(Bool()) - - // STATE MACHINE - - // we are ready to receive data by default and output data is not valid - val inReady = RegInit(true.B) - io.in.ready := inReady - val outValid = RegInit(false.B) - io.out.valid := outValid - - // when the input interface turns valid deassert ready & load the initial data (incl. initial rotation if necessary) - when(io.in.fire) { - inReady := false.B - vecReg := io.in.bits.vectoring - extReg := ext - bottomReg := bottom - xyz.x := Mux(ext, Mux(bottom, -1 * io.in.bits.y, io.in.bits.y), io.in.bits.x) - xyz.y := Mux(ext, Mux(bottom, io.in.bits.x, -1 * io.in.bits.x), io.in.bits.y) - xyz.z := Mux(ext, Mux(bottom, halfPi, -1 * halfPi) + io.in.bits.z, io.in.bits.z) - } - - // when we are no longer ready, start computing by incrementing counter - when(!inReady && counter < (params.nStages).U) { - counter := counter + params.stagesPerCycle.U - // and update register from previous clock cycle - xyz := xyzn(params.stagesPerCycle) - } - - // finally when the counter hits 0, and latch out data/valid (gain corrected if desired). - // reset counter and return to in ready/out invalid state only after the valid went high and output interface is ready - when(counter >= (params.nStages).U) { - outValid := true.B - - when(io.out.fire) { - counter := 0.U - inReady := true.B - outValid := false.B - } - } - - // COMBINATORIAL CORDIC COMPUTATION - - // calculate each stage's x,y,z - // 0th wire in Vec always equal to the register for easy indexing inside loop - xyzn(0) := xyz - for (j <- 0 until params.stagesPerCycle) { - val delta = Mux(vecReg, xyzn(j).y.signBit() ^ xyzn(j).x.signBit(), xyzn(j).z.isSignPositive()) - val shift = counter + j - xyzn(j + 1).x := AddSub(!delta, xyzn(j).x, xyzn(j).y >> shift) - xyzn(j + 1).y := AddSub(delta, xyzn(j).y, xyzn(j).x >> shift) - xyzn(j + 1).z := AddSub(!delta, xyzn(j).z, alpha(shift)) - } - - // OUTPUTS - - // gain correction - val gainCor = params.protoXYZ.fromDouble(1 / Constants.gain(params.nStages)) //always less than 1 - val xoutCor = Mux(params.correctGain.B, xyz.x * gainCor, xyz.x) - val youtCor = Mux(params.correctGain.B, xyz.y * gainCor, xyz.y) - - // if rotation, flip x & y if rotation was into left half plane - io.out.bits.x := Mux(!vecReg && extReg, -1 * xoutCor, xoutCor) - io.out.bits.y := Mux(!vecReg && extReg, -1 * youtCor, youtCor) - - // if vectoring, add/subtract pi to z if vectoring was from left half plane - val pi = params.protoXYZ.fromDouble(scala.math.Pi) - io.out.bits.z := Mux(vecReg && extReg, Mux(bottomReg, xyz.z - pi, xyz.z + pi), xyz.z) - -} - -/** - * Mixin for top-level rocket to add a PWM - */ -//trait HasPeripheryCordic extends BaseSubsystem { -// // instantiate cordic chain -// val cordicChain = LazyModule(new CordicThing(FixedCordicParams(8, 10))) -// // connect memory interfaces to pbus -// pbus.toVariableWidthSlave(Some("cordicWrite")) { cordicChain.writeQueue.mem.get } -// pbus.toVariableWidthSlave(Some("cordicRead")) { cordicChain.readQueue.mem.get } -//} diff --git a/src/main/scala/chisel/lib/cordic/iterative/CordicApp.scala b/src/main/scala/chisel/lib/cordic/iterative/CordicApp.scala deleted file mode 100644 index 1ca219d..0000000 --- a/src/main/scala/chisel/lib/cordic/iterative/CordicApp.scala +++ /dev/null @@ -1,66 +0,0 @@ -// See LICENSE for license details. - -package chisel.lib.cordic.iterative - -import chisel3._ -import chisel3.stage.ChiselStage - -/** - * Make an unapply function for the argument parser. - * It allows us to match on parameters that are integers - */ -object Int { - def unapply(v: String): Option[Int] = { - try { - Some(v.toInt) - } catch { - case _: NumberFormatException => None - } - } -} - -/** - * Define entry point for CORDIC generator - */ -object CordicApp extends App { - val usage = s"""Cordic arguments: - |--xy \t\tWidth of x and y - |--z \t\tWidth of z - |--correctGain\t\tCorrect gain - |--noCorrectGain\t\tDon't correct gain - |--stagesPerCycle \t\tStages to use per cycle - |""".stripMargin - - /** - * Parse arguments - * - * Some arguments are used by the cordic generator and are used to construct a FixedCordicParams object. - * The rest get returned as a List[String] to pass to the Chisel driver - */ - def argParse(args: List[String], params: FixedCordicParams): (List[String], FixedCordicParams) = { - args match { - case "--help" :: tail => - println(usage) - val (newArgs, newParams) = argParse(tail, params) - ("--help" +: newArgs, newParams) - case "--xy" :: Int(xy) :: tail => argParse(tail, params.copy(xyWidth = xy)) - case "--z" :: Int(z) :: tail => argParse(tail, params.copy(zWidth = z)) - case "--correctGain" :: tail => argParse(tail, params.copy(correctGain = true)) - case "--noCorrectGain" :: tail => argParse(tail, params.copy(correctGain = false)) - case "--stagesPerCycle" :: Int(spc) :: tail => argParse(tail, params.copy(stagesPerCycle = spc)) - case chiselOpt :: tail => { - val (newArgs, newParams) = argParse(tail, params) - (chiselOpt +: newArgs, newParams) - } - case Nil => (args, params) - } - } - val defaultParams = FixedCordicParams( - xyWidth = 12, - zWidth = 12, - stagesPerCycle = 1 - ) - val (chiselArgs, params) = argParse(args.toList, defaultParams) - // Run the Chisel driver to generate a cordic - emitVerilog(new IterativeCordic(params), chiselArgs.toArray) -} diff --git a/src/test/scala/chisel/lib/cordic/iterative/FixedCordicSpec.scala b/src/test/scala/chisel/lib/cordic/iterative/FixedCordicSpec.scala deleted file mode 100644 index 9be6710..0000000 --- a/src/test/scala/chisel/lib/cordic/iterative/FixedCordicSpec.scala +++ /dev/null @@ -1,131 +0,0 @@ -// See LICENSE for license details. - -package chisel.lib.cordic.iterative - -import dsptools.numbers._ -import org.scalatest.flatspec.AnyFlatSpec - -import scala.math.{ceil, max} - -/* does not work with Chisel 3.6 -class FixedCordicSpec extends AnyFlatSpec { - behavior.of("FixedIterativeCordic") - - val params = FixedCordicParams( - xyWidth = 16, - zWidth = 16, - correctGain = true, - stagesPerCycle = 1 - ) - it should "rotate" in { - val baseTrial = XYZ(xin = 1.0, yin = 0.0, zin = 0.0, vectoring = false) - val angles = Seq(-3, -2, -1, -0.5, 0, 0.25, 0.5, 1, 2, 3) - val trials = angles.map { phi => - baseTrial.copy(zin = phi, xout = Some(math.cos(phi)), yout = Some(math.sin(phi)), zout = Some(0)) - } - assert(FixedCordicTester(params, trials)) - } - it should "vector" in { - val baseTrial = XYZ(xin = 1.0, yin = 0.0, zin = 0.0, vectoring = true) - val angles = Seq(-3, -2, -1, -0.5, 0, 0.25, 0.5, 1, 2, 3) - val trials = angles.map { phi => - baseTrial.copy(xin = math.cos(phi), yin = math.sin(phi), xout = Some(1), yout = Some(0), zout = Some(phi)) - } - assert(FixedCordicTester(params, trials)) - } - - // No gain tests. - val paramsNoGain = FixedCordicParams( - xyWidth = params.xyWidth, - zWidth = params.zWidth, - correctGain = false, - stagesPerCycle = params.stagesPerCycle - ) - val nStages = ceil(max(params.xyWidth, params.zWidth) / params.stagesPerCycle).toInt * params.stagesPerCycle - var gainCor = Constants.gain(nStages) - - it should "rotate without gain correction" in { - val baseTrial = XYZ(xin = 1.0, yin = 0.0, zin = 0.0, vectoring = false) - val angles = Seq(-3, -2, -1, -0.5, 0, 0.25, 0.5, 1, 2, 3) - val trials = angles.map { phi => - baseTrial.copy( - zin = phi, - xout = Some(math.cos(phi) * gainCor), - yout = Some(math.sin(phi) * gainCor), - zout = Some(0) - ) - } - assert(FixedCordicTester(paramsNoGain, trials)) - } - it should "vector without gain correction" in { - val baseTrial = XYZ(xin = 1.0, yin = 0.0, zin = 0.0, vectoring = true) - val angles = Seq(-3, -2, -1, -0.5, 0, 0.25, 0.5, 1, 2, 3) - val trials = angles.map { phi => - baseTrial.copy(xin = math.cos(phi), yin = math.sin(phi), xout = Some(gainCor), yout = Some(0), zout = Some(phi)) - } - assert(FixedCordicTester(paramsNoGain, trials)) - } - - behavior.of("RealIterativeCordic") - - val realParams = new CordicParams[DspReal] { - val protoXY = DspReal() - val protoZ = DspReal() - val protoXYZ = DspReal() - val nStages = 30 - val correctGain = true - val stagesPerCycle = params.stagesPerCycle - } - it should "rotate" in { - val baseTrial = XYZ(xin = 1.0, yin = 0.0, zin = 0.0, vectoring = false) - val angles = Seq(-3, -2, -1, -0.5, 0, 0.25, 0.5, 1, 2, 3) - val trials = angles.map { phi => - baseTrial.copy(zin = phi, xout = Some(math.cos(phi)), yout = Some(math.sin(phi)), zout = Some(0)) - } - assert(RealCordicTester(realParams, trials)) - } - it should "vector" in { - val baseTrial = XYZ(xin = 1.0, yin = 0.0, zin = 0.0, vectoring = true) - val angles = Seq(-3, -2, -1, -0.5, 0, 0.25, 0.5, 1, 2, 3) - val trials = angles.map { phi => - baseTrial.copy(xin = math.cos(phi), yin = math.sin(phi), xout = Some(1), yout = Some(0), zout = Some(phi)) - } - assert(RealCordicTester(realParams, trials)) - } - - // No gain tests. - val realParamsNoGain = new CordicParams[DspReal] { - val protoXY = DspReal() - val protoZ = DspReal() - val protoXYZ = DspReal() - val nStages = realParams.nStages - val correctGain = false - val stagesPerCycle = params.stagesPerCycle - } - gainCor = Constants.gain(realParams.nStages) - - it should "rotate without gain correction" in { - val baseTrial = XYZ(xin = 1.0, yin = 0.0, zin = 0.0, vectoring = false) - val angles = Seq(-3, -2, -1, -0.5, 0, 0.25, 0.5, 1, 2, 3) - val trials = angles.map { phi => - baseTrial.copy( - zin = phi, - xout = Some(math.cos(phi) * gainCor), - yout = Some(math.sin(phi) * gainCor), - zout = Some(0) - ) - } - assert(RealCordicTester(realParamsNoGain, trials)) - } - it should "vector without gain correction" in { - val baseTrial = XYZ(xin = 1.0, yin = 0.0, zin = 0.0, vectoring = true) - val angles = Seq(-3, -2, -1, -0.5, 0, 0.25, 0.5, 1, 2, 3) - val trials = angles.map { phi => - baseTrial.copy(xin = math.cos(phi), yin = math.sin(phi), xout = Some(gainCor), yout = Some(0), zout = Some(phi)) - } - assert(RealCordicTester(realParamsNoGain, trials)) - } - -} - - */ diff --git a/src/test/scala/chisel/lib/cordic/iterative/FixedCordicTester.scala b/src/test/scala/chisel/lib/cordic/iterative/FixedCordicTester.scala deleted file mode 100644 index a55b9ba..0000000 --- a/src/test/scala/chisel/lib/cordic/iterative/FixedCordicTester.scala +++ /dev/null @@ -1,97 +0,0 @@ -// See LICENSE for license details. - -package chisel.lib.cordic.iterative - -import dsptools.DspTester - -/** - * Case class holding information needed to run an individual test - */ -case class XYZ( - // input x, y and z - xin: Double, - yin: Double, - zin: Double, - // mode - vectoring: Boolean, - // optional outputs - // if None, then don't check the result - // if Some(...), check that the result matches - xout: Option[Double] = None, - yout: Option[Double] = None, - zout: Option[Double] = None) - -/** - * DspTester for FixedIterativeCordic - * - * Run each trial in @trials - */ - -/* does not work with Chisel 3.6 -class CordicTester[T <: chisel3.Data](c: IterativeCordic[T], trials: Seq[XYZ], tolLSBs: Int = 2) extends DspTester(c) { - val maxCyclesWait = 50 - - poke(c.io.out.ready, 1) - poke(c.io.in.valid, 1) - - for (trial <- trials) { - poke(c.io.in.bits.x, trial.xin) - poke(c.io.in.bits.y, trial.yin) - poke(c.io.in.bits.z, trial.zin) - poke(c.io.in.bits.vectoring, trial.vectoring) - - // wait until input is accepted - var cyclesWaiting = 0 - while (!peek(c.io.in.ready) && cyclesWaiting < maxCyclesWait) { - cyclesWaiting += 1 - if (cyclesWaiting >= maxCyclesWait) { - expect(false, "waited for input too long") - } - step(1) - } - // wait until output is valid - cyclesWaiting = 0 - while (!peek(c.io.out.valid) && cyclesWaiting < maxCyclesWait) { - cyclesWaiting += 1 - if (cyclesWaiting >= maxCyclesWait) { - expect(false, "waited for output too long") - } - step(1) - } - // set desired tolerance - // in this case, it's pretty loose (2 bits) - // can you get tolerance of 1 bit? 0? what makes the most sense? - fixTolLSBs.withValue(tolLSBs) { - // check every output where we have an expected value - trial.xout.foreach { x => expect(c.io.out.bits.x, x) } - trial.yout.foreach { y => expect(c.io.out.bits.y, y) } - trial.zout.foreach { z => expect(c.io.out.bits.z, z) } - } - } -} - -/** - * Convenience function for running tests - */ - -object FixedCordicTester { - def apply(params: FixedCordicParams, trials: Seq[XYZ]): Boolean = { - dsptools.Driver.execute(() => new IterativeCordic(params), Array("-tbn", "firrtl", "-fiwv")) { c => - new CordicTester(c, trials) - } - } -} - -object RealCordicTester { - def apply(params: CordicParams[dsptools.numbers.DspReal], trials: Seq[XYZ]): Boolean = { - dsptools.Driver.execute( - () => new IterativeCordic(params), - Array("--backend-name", "verilator", "-fiwv", "-rtdec", "6") - ) { c => - new CordicTester(c, trials) - } - } -} - - - */