From 35cba5dfaef0486a10b7b8b683200bff25952b72 Mon Sep 17 00:00:00 2001 From: Ryan Lund Date: Mon, 20 Apr 2020 10:33:03 -0700 Subject: [PATCH 01/19] Dsptools examples (#457) * Add c test files for DSPTools example * Update tests Makefile to build DSPTools c tests * Add DSPTools example configs to ConfigMixins and RocketConfigs * Add dsptools and rocket-dsptools as dependancies for example * Add Scala implementations of DSPTools test blocks * Clean up GenericFIR scala * Modify dsptools blocks and mixins to match 'CanHave' when adding peripherial * Update documentation, will need reworking once FIR is characterized as fixed point * Update naming of Passthrough to Streaming Passthrough. Update naming of Thing to Chain and remove old Chain * Fix capitalization in docs (#419) * Add c test files for DSPTools example * Update tests Makefile to build DSPTools c tests * Add DSPTools example configs to ConfigMixins and RocketConfigs * Add dsptools and rocket-dsptools as dependancies for example * Add Scala implementations of DSPTools test blocks * Clean up GenericFIR scala * Modify dsptools blocks and mixins to match 'CanHave' when adding peripherial * Update documentation, will need reworking once FIR is characterized as fixed point * Update naming of Passthrough to Streaming Passthrough. Update naming of Thing to Chain and remove old Chain * Update docs/Customization/Dsptools-Blocks.rst Co-Authored-By: alonamid * Docummentation update for clarity and to explain how this can be applied to a generalized block * Some refactoring to get dsptools working with these examples * Oops, old files crept in Co-authored-by: Ryan Lund Co-authored-by: Sagar Karandikar Co-authored-by: alonamid Co-authored-by: Paul Rigge --- build.sbt | 15 +- docs/Customization/Dsptools-Blocks.rst | 105 ++++++++ docs/Customization/index.rst | 5 +- .../src/main/scala/config/RocketConfigs.scala | 12 + .../scala/example/dsptools/DspBlocks.scala | 146 ++++++++++++ .../scala/example/dsptools/GenericFIR.scala | 224 ++++++++++++++++++ .../scala/example/dsptools/Passthrough.scala | 156 ++++++++++++ tests/Makefile | 2 +- tests/fir.c | 51 ++++ tests/passthrough.c | 49 ++++ tools/chisel-testers | 2 +- tools/dsptools | 2 +- 12 files changed, 758 insertions(+), 11 deletions(-) create mode 100644 docs/Customization/Dsptools-Blocks.rst create mode 100644 generators/chipyard/src/main/scala/example/dsptools/DspBlocks.scala create mode 100644 generators/chipyard/src/main/scala/example/dsptools/GenericFIR.scala create mode 100644 generators/chipyard/src/main/scala/example/dsptools/Passthrough.scala create mode 100644 tests/fir.c create mode 100644 tests/passthrough.c diff --git a/build.sbt b/build.sbt index 05f05d7b..fdeed5a7 100644 --- a/build.sbt +++ b/build.sbt @@ -20,6 +20,8 @@ lazy val commonSettings = Seq( libraryDependencies += "com.github.scopt" %% "scopt" % "3.7.0", libraryDependencies += "org.scala-lang.modules" % "scala-jline" % "2.12.1", libraryDependencies += "com.typesafe.play" %% "play-json" % "2.6.10", + libraryDependencies += "org.typelevel" %% "spire" % "0.16.2", + libraryDependencies += "org.scalanlp" %% "breeze" % "1.0", addCompilerPlugin("org.scalamacros" % "paradise" % "2.1.0" cross CrossVersion.full), unmanagedBase := (chipyardRoot / unmanagedBase).value, allDependencies := allDependencies.value.filterNot(_.organization == "edu.berkeley.cs"), @@ -129,6 +131,7 @@ lazy val iocell = (project in file("./tools/barstools/iocell/")) lazy val chipyard = conditionalDependsOn(project in file("generators/chipyard")) .dependsOn(boom, hwacha, sifive_blocks, sifive_cache, utilities, iocell, sha3, // On separate line to allow for cleaner tutorial-setup patches + dsptools, `rocket-dsptools`, gemmini, icenet, tracegen, ariane) .settings(commonSettings) @@ -175,19 +178,17 @@ lazy val barstoolsMacros = (project in file("./tools/barstools/macros/")) .enablePlugins(sbtassembly.AssemblyPlugin) .settings(commonSettings) -lazy val dsptools = (project in file("./tools/dsptools")) +lazy val dsptools = freshProject("dsptools", file("./tools/dsptools")) .dependsOn(chisel, chisel_testers) .settings( commonSettings, libraryDependencies ++= Seq( - "org.typelevel" %% "spire" % "0.14.1", - "org.scalanlp" %% "breeze" % "0.13.2", - "junit" % "junit" % "4.12" % "test", - "org.scalatest" %% "scalatest" % "3.0.5" % "test", - "org.scalacheck" %% "scalacheck" % "1.14.0" % "test" + "junit" % "junit" % "4.13" % "test", + "org.scalatest" %% "scalatest" % "3.0.8", + "org.scalacheck" %% "scalacheck" % "1.14.3" % "test" )) -lazy val `rocket-dsptools` = (project in file("./tools/dsptools/rocket")) +lazy val `rocket-dsptools` = freshProject("rocket-dsptools", file("./tools/dsptools/rocket")) .dependsOn(rocketchip, dsptools) .settings(commonSettings) diff --git a/docs/Customization/Dsptools-Blocks.rst b/docs/Customization/Dsptools-Blocks.rst new file mode 100644 index 00000000..228f5f91 --- /dev/null +++ b/docs/Customization/Dsptools-Blocks.rst @@ -0,0 +1,105 @@ +.. _dsptools-blocks: + +Dsptools Blocks +=============== + +Another way to create a MMIO peripheral is to use the Dsptools library for Chisel. In this method, a memory interface is created by creating a "chain". This chain consists of a custom module placed inside a ``DspBlock``, which is then sandwiched between a ``ReadQueue`` and ``WriteQueue``. Those queues then act as memory mapped interfaces to the Rocket Chip SoCs. This section will again primarily focus on designing Tilelink-based peripherals. However, through the resources provided in Dsptools, one could also define an AXI4-based peripheral by following similar steps. + +For this example, we will show you how to connect a simple FIR filter created using Dsptools as an MMIO peripheral. The full code can be found in ``generators/example/src/main/scala/dsptools/GenericFIR.scala``. That being said, one could substitute any module with a ready valid interface in the place of the FIR and achieve the same results. As long as the read and valid signals of the module are attached to those of a corresponding ``DSPBlock`` wrapper, and that wrapper is placed in a chain with a ``ReadQueue`` and a ``WriteQueue``, following the general outline establised by these steps will allow you to interact with that block as a memory mapped IO + +The module ``GenericFIR`` is the overall wrapper of our FIR module. This module links together a variable number of ``GenericFIRDirectCell`` submodules, each of which performs the computations for one coefficient in a FIR direct form architecture. It is important to note that both modules are type generic, which means that they can be instantiated for any datatype that implements ``Ring`` operations per the specifications on ``T``. + +.. literalinclude:: ../../generators/example/src/main/scala/dsptools/GenericFIR.scala + :language: scala + :start-after: DOC include start: GenericFIR chisel + :end-before: DOC include end: GenericFIR chisel + +.. literalinclude:: ../../generators/example/src/main/scala/dsptools/GenericFIR.scala + :language: scala + :start-after: DOC include start: GenericFIRDirectCell chisel + :end-before: DOC include end: GenericFIRDirectCell chisel + +Creating a DspBlock Extension +----------------------------- + +The first step in attaching the FIR filter as a MMIO peripheral is to create an abstract extension of ``DspBlock`` the wraps around the ``GenericFIR`` module. The main steps of this process are as follows. + +1. Instantiate a ``GenericFIR`` within ``GenericFIRBlock``. +2. Attach the ready and valid signals from the in and out connections. +3. Cast the module input data to the input type of ``GenericFIR`` (``GenericFIRBundle``) and attach. +4. Cast the output of ``GenericFIR`` to ``UInt`` and attach to the module output. + +.. literalinclude:: ../../generators/example/src/main/scala/dsptools/GenericFIR.scala + :language: scala + :start-after: DOC include start: GenericFIRBlock chisel + :end-before: DOC include end: GenericFIRBlock chisel + +Connecting by TileLink +---------------------- +With these classes implemented, you can begin to construct the chain by extending ``GenericFIRBlock`` while using the ``TLDspBlock`` trait via mixin. + +.. literalinclude:: ../../generators/example/src/main/scala/dsptools/GenericFIR.scala + :language: scala + :start-after: DOC include start: TLGenericFIRBlock chisel + :end-before: DOC include end: TLGenericFIRBlock chisel + +We can then construct the final chain by utilizing the ``TLWriteQueue`` and ``TLReadeQueue`` modules found in ``generators/example/src/main/scala/dsptools/DspBlocks.scala``. Inside our chain, we construct an instance of each queue as well as our ``TLGenericFIRBlock``. We then take the ``steamnode`` from each module and wire them all together to link the chain. + +.. literalinclude:: ../../generators/example/src/main/scala/dsptools/GenericFIR.scala + :language: scala + :start-after: DOC include start: TLGenericFIRChain chisel + :end-before: DOC include end: TLGenericFIRChain chisel + +Top Level Traits +---------------- +As in the previous MMIO example, we use a cake pattern to hook up our module to our SoC. + +.. literalinclude:: ../../generators/example/src/main/scala/dsptools/GenericFIR.scala + :language: scala + :start-after: DOC include start: CanHavePeripheryUIntTestFIR chisel + :end-before: DOC include end: CanHavePeripheryUIntTestFIR chisel + +Note that this is the point at which we decide the datatype for our FIR. It is also possible with some reworking to push the datatype selection out to the top level. + +Our module does not need to be connected to concrete IOs or wires, so we do not need to create a concrete trait. + +Constructing the Top and Config +------------------------------- + +Once again following the path of the previous MMIO example, we now want to mix our traits into the system as a whole. The code is from ``generators/example/src/main/scala/Top.scala`` + +.. literalinclude:: ../../generators/example/src/main/scala/Top.scala + :language: scala + :start-after: DOC include start: Top + :end-before: DOC include end: Top + +Finally, we create the configuration class in ``generators/example/src/main/scala/RocketConfigs.scala`` that uses the ``WithUIntTestFIR`` mixin defined in ``generators/example/src/main/scala/ConfigMixins.scala``. + +.. literalinclude:: ../../generators/example/src/main/scala/ConfigMixins.scala + :language: scala + :start-after: DOC include start: WithTestFIR + :end-before: DOC include end: WithTestFIR + +.. literalinclude:: ../../generators/example/src/main/scala/RocketConfigs.scala + :language: scala + :start-after: DOC include start: FIRRocketConfig + :end-before: DOC include end: FIRRocketConfig + +Testing +------- + +We can now test that the FIR is working. The test program is found in ``tests/gcd.c``. + +.. literalinclude:: ../../tests/fir.c + :language: c + +The test feed a series of values into the fir and compares the output to a golden model of computation. The base of the module's MMIO write region is at 0x2000 and the base of the read region is at 0x2100 by default. + +Compiling this program with ``make`` produces a ``fir.riscv`` executable. + +Now we can run our simulation. + +.. code-block:: shell + + cd sims/verilator + make CONFIG=GCDTLRocketConfig BINARY=../../tests/fir.riscv run-binary diff --git a/docs/Customization/index.rst b/docs/Customization/index.rst index 90d36fda..9421b79a 100644 --- a/docs/Customization/index.rst +++ b/docs/Customization/index.rst @@ -11,7 +11,9 @@ These guides will walk you through customization of your system-on-chip: - Adding custom MMIO widgets to the Chipyard memory system by Tilelink or AXI4, with custom Top-level IOs -- Standard practices for using keys, traits, and configs to parameterize your design +- Adding custom Dsptools based blocks as MMIO widgets. + +- Standard practices for using Keys, Traits, and Configs to parameterize your design - Customizing the memory hierarchy @@ -36,6 +38,7 @@ We recommend reading all these pages in order. Hit next to get started! RoCC-or-MMIO RoCC-Accelerators MMIO-Peripherals + Dsptools-Blocks Keys-Traits-Configs DMA-Devices Incorporating-Verilog-Blocks diff --git a/generators/chipyard/src/main/scala/config/RocketConfigs.scala b/generators/chipyard/src/main/scala/config/RocketConfigs.scala index e25680ca..c72fb1f5 100644 --- a/generators/chipyard/src/main/scala/config/RocketConfigs.scala +++ b/generators/chipyard/src/main/scala/config/RocketConfigs.scala @@ -384,3 +384,15 @@ class RingSystemBusRocketConfig extends Config( new freechips.rocketchip.subsystem.WithNBigCores(1) ++ new freechips.rocketchip.system.BaseConfig) // DOC include end: RingSystemBusRocket + +class UIntStreamingPassthroughRocketConfig extends Config( + new chipyard.example.WithUIntStreamingPassthrough ++ // use top with tilelink-controlled passthrough + new RocketConfig +) + +// DOC include start: FIRRocketConfig +class UIntTestFIRRocketConfig extends Config ( + new chipyard.example.WithUIntTestFIR ++ // use top with tilelink-controlled FIR + new RocketConfig +) +// DOC include end: FIRRocketConfig diff --git a/generators/chipyard/src/main/scala/example/dsptools/DspBlocks.scala b/generators/chipyard/src/main/scala/example/dsptools/DspBlocks.scala new file mode 100644 index 00000000..ad4c585e --- /dev/null +++ b/generators/chipyard/src/main/scala/example/dsptools/DspBlocks.scala @@ -0,0 +1,146 @@ +package chipyard.example + +import chisel3._ +import chisel3.util._ +import dspblocks._ +import dsptools.numbers._ +import freechips.rocketchip.amba.axi4stream._ +import freechips.rocketchip.config.Parameters +import freechips.rocketchip.diplomacy._ +import freechips.rocketchip.regmapper._ +import freechips.rocketchip.tilelink._ +import freechips.rocketchip.subsystem._ + +/** + * The memory interface writes entries into the queue. + * They stream out the streaming interface + * @param depth number of entries in the queue + * @param streamParameters parameters for the stream node + * @param p + */ +abstract class WriteQueue +( + val depth: Int = 8, + val streamParameters: AXI4StreamMasterParameters = AXI4StreamMasterParameters() +)(implicit p: Parameters) extends LazyModule with HasCSR { + // stream node, output only + val streamNode = AXI4StreamMasterNode(streamParameters) + + lazy val module = new LazyModuleImp(this) { + require(streamNode.out.length == 1) + + // get the output bundle associated with the AXI4Stream node + val out = streamNode.out.head._1 + // width (in bits) of the output interface + val width = out.params.n * 8 + // instantiate a queue + val queue = Module(new Queue(UInt(out.params.dataBits.W), depth)) + // connect queue output to streaming output + out.valid := queue.io.deq.valid + out.bits.data := queue.io.deq.bits + // don't use last + out.bits.last := false.B + queue.io.deq.ready := out.ready + + regmap( + // each write adds an entry to the queue + 0x0 -> Seq(RegField.w(width, queue.io.enq)), + // read the number of entries in the queue + (width+7)/8 -> Seq(RegField.r(width, queue.io.count)), + ) + } +} + +/** + * TLDspBlock specialization of WriteQueue + * @param depth number of entries in the queue + * @param csrAddress address range for peripheral + * @param beatBytes beatBytes of TL interface + * @param p + */ +class TLWriteQueue +( + depth: Int = 8, + csrAddress: AddressSet = AddressSet(0x2000, 0xff), + beatBytes: Int = 8, +)(implicit p: Parameters) extends WriteQueue(depth) with TLHasCSR { + val devname = "tlQueueIn" + val devcompat = Seq("ucb-art", "dsptools") + val device = new SimpleDevice(devname, devcompat) { + override def describe(resources: ResourceBindings): Description = { + val Description(name, mapping) = super.describe(resources) + Description(name, mapping) + } + } + // make diplomatic TL node for regmap + override val mem = Some(TLRegisterNode(address = Seq(csrAddress), device = device, beatBytes = beatBytes)) +} + +/** + * The streaming interface adds elements into the queue. + * The memory interface can read elements out of the queue. + * @param depth number of entries in the queue + * @param streamParameters parameters for the stream node + * @param p + */ +abstract class ReadQueue +( + val depth: Int = 8, + val streamParameters: AXI4StreamSlaveParameters = AXI4StreamSlaveParameters() +)(implicit p: Parameters) extends LazyModule with HasCSR { + val streamNode = AXI4StreamSlaveNode(streamParameters) + + lazy val module = new LazyModuleImp(this) { + require(streamNode.in.length == 1) + + // get the input associated with the stream node + val in = streamNode.in.head._1 + // make a Decoupled[UInt] that RegReadFn can do something with + val out = Wire(Decoupled(UInt())) + // get width of streaming input interface + val width = in.params.n * 8 + // instantiate a queue + val queue = Module(new Queue(UInt(in.params.dataBits.W), depth)) + // connect input to the streaming interface + queue.io.enq.valid := in.valid + queue.io.enq.bits := in.bits.data + in.ready := queue.io.enq.ready + // connect output to wire + out.valid := queue.io.deq.valid + out.bits := queue.io.deq.bits + queue.io.deq.ready := out.ready + + regmap( + // map the output of the queue + 0x0 -> Seq(RegField.r(width, RegReadFn(out))), + // read the number of elements in the queue + (width+7)/8 -> Seq(RegField.r(width, queue.io.count)), + ) + } +} + +/** + * TLDspBlock specialization of ReadQueue + * @param depth number of entries in the queue + * @param csrAddress address range + * @param beatBytes beatBytes of TL interface + * @param p + */ +class TLReadQueue +( + depth: Int = 8, + csrAddress: AddressSet = AddressSet(0x2100, 0xff), + beatBytes: Int = 8 +)(implicit p: Parameters) extends ReadQueue(depth) with TLHasCSR { + val devname = "tlQueueOut" + val devcompat = Seq("ucb-art", "dsptools") + val device = new SimpleDevice(devname, devcompat) { + override def describe(resources: ResourceBindings): Description = { + val Description(name, mapping) = super.describe(resources) + Description(name, mapping) + } + } + // make diplomatic TL node for regmap + override val mem = Some(TLRegisterNode(address = Seq(csrAddress), device = device, beatBytes = beatBytes)) + +} diff --git a/generators/chipyard/src/main/scala/example/dsptools/GenericFIR.scala b/generators/chipyard/src/main/scala/example/dsptools/GenericFIR.scala new file mode 100644 index 00000000..5335eb42 --- /dev/null +++ b/generators/chipyard/src/main/scala/example/dsptools/GenericFIR.scala @@ -0,0 +1,224 @@ +//// See LICENSE for license details. +// +package chipyard.example + +import chisel3._ +import chisel3.{Bundle, Module} +import chisel3.util._ +import dspblocks._ +import dsptools.numbers._ +import freechips.rocketchip.amba.axi4stream._ +import freechips.rocketchip.config.{Parameters, Field, Config} +import freechips.rocketchip.diplomacy._ +import freechips.rocketchip.tilelink._ +import freechips.rocketchip.subsystem._ + +// FIR params +case class GenericFIRParams( + writeAddress: BigInt = 0x2000, + readAddress: BigInt = 0x2100, + depth: Int +) + +case object GenericFIRKey extends Field[Option[GenericFIRParams]](None) + +class GenericFIRCellBundle[T<:Data:Ring](genIn:T, genOut:T) extends Bundle { + val data: T = genIn.cloneType + val carry: T = genOut.cloneType + + override def cloneType: this.type = GenericFIRCellBundle(genIn, genOut).asInstanceOf[this.type] +} +object GenericFIRCellBundle { + def apply[T<:Data:Ring](genIn:T, genOut:T): GenericFIRCellBundle[T] = new GenericFIRCellBundle(genIn, genOut) +} + +class GenericFIRCellIO[T<:Data:Ring](genIn:T, genOut:T) extends Bundle { + val coeff = Input(genIn.cloneType) + val in = Flipped(Decoupled(GenericFIRCellBundle(genIn, genOut))) + val out = Decoupled(GenericFIRCellBundle(genIn, genOut)) +} +object GenericFIRCellIO { + def apply[T<:Data:Ring](genIn:T, genOut:T): GenericFIRCellIO[T] = new GenericFIRCellIO(genIn, genOut) +} + +class GenericFIRBundle[T<:Data:Ring](proto: T) extends Bundle { + val data: T = proto.cloneType + + override def cloneType: this.type = GenericFIRBundle(proto).asInstanceOf[this.type] +} +object GenericFIRBundle { + def apply[T<:Data:Ring](proto: T): GenericFIRBundle[T] = new GenericFIRBundle(proto) +} + +class GenericFIRIO[T<:Data:Ring](genIn:T, genOut:T) extends Bundle { + val in = Flipped(Decoupled(GenericFIRBundle(genIn))) + val out = Decoupled(GenericFIRBundle(genOut)) +} +object GenericFIRIO { + def apply[T<:Data:Ring](genIn:T, genOut:T): GenericFIRIO[T] = new GenericFIRIO(genIn, genOut) +} + +// A generic FIR filter +// DOC include start: GenericFIR chisel +class GenericFIR[T<:Data:Ring](genIn:T, genOut:T, coeffs: Seq[T]) extends Module { + val io = IO(GenericFIRIO(genIn, genOut)) + + // Construct a vector of genericFIRDirectCells + val directCells = Seq.fill(coeffs.length){ Module(new GenericFIRDirectCell(genIn, genOut)).io } + + // Construct the direct FIR chain + for ((cell, coeff) <- directCells.zip(coeffs)) { + cell.coeff := coeff + } + + // Connect input to first cell + directCells.head.in.bits.data := io.in.bits.data + directCells.head.in.bits.carry := Ring[T].zero + directCells.head.in.valid := io.in.valid + io.in.ready := directCells.head.in.ready + + // Connect adjacent cells + // Note that .tail() returns a collection that consists of all + // elements in the inital collection minus the first one. + // This means that we zip together directCells[0, n] and + // directCells[1, n]. However, since zip ignores unmatched elements, + // the resulting zip is (directCells[0], directCells[1]) ... + // (directCells[n-1], directCells[n]) + for ((current, next) <- directCells.zip(directCells.tail)) { + next.in.bits := current.out.bits + next.in.valid := current.out.valid + current.out.ready := next.in.ready + } + + // Connect output to last cell + io.out.bits.data := directCells.last.out.bits.carry + directCells.last.out.ready := io.out.ready + io.out.valid := directCells.last.out.valid + +} +// DOC include end: GenericFIR chisel + +// A generic FIR direct cell used to construct a larger direct FIR chain +// +// in ----- [z^-1]-- out +// | +// coeff ----[*] +// | +// carryIn --[+]-- carryOut +// +// DOC include start: GenericFIRDirectCell chisel +class GenericFIRDirectCell[T<:Data:Ring](genIn: T, genOut: T) extends Module { + val io = IO(GenericFIRCellIO(genIn, genOut)) + + // Registers to delay the input and the valid to propagate with calculations + val hasNewData = RegInit(0.U) + val inputReg = Reg(genIn.cloneType) + + // Passthrough ready + io.in.ready := io.out.ready + + // When a new transaction is ready on the input, we will have new data to output + // next cycle. Take this data in + when (io.in.fire()) { + hasNewData := 1.U + inputReg := io.in.bits.data + } + + // We should output data when our cell has new data to output and is ready to + // recieve new data. This insures that every cell in the chain passes its data + // on at the same time + io.out.valid := hasNewData & io.in.fire() + io.out.bits.data := inputReg + + // Compute carry + // This uses the ring implementation for + and *, i.e. + // (a * b) maps to (Ring[T].prod(a, b)) for whicever T you use + io.out.bits.carry := inputReg * io.coeff + io.in.bits.carry +} +// DOC include end: GenericFIRDirectCell chisel + + +// DOC include start: GenericFIRBlock chisel +abstract class GenericFIRBlock[D, U, EO, EI, B<:Data, T<:Data:Ring] +( + genIn: T, + genOut: T, + coeffs: Seq[T] +)(implicit p: Parameters) extends DspBlock[D, U, EO, EI, B] { + val streamNode = AXI4StreamIdentityNode() + val mem = None + + lazy val module = new LazyModuleImp(this) { + require(streamNode.in.length == 1) + require(streamNode.out.length == 1) + + val in = streamNode.in.head._1 + val out = streamNode.out.head._1 + + // instantiate generic fir + val fir = Module(new GenericFIR(genIn, genOut, coeffs)) + + // Attach ready and valid to outside interface + in.ready := fir.io.in.ready + fir.io.in.valid := in.valid + + fir.io.out.ready := out.ready + out.valid := fir.io.out.valid + + // cast UInt to T + fir.io.in.bits := in.bits.data.asTypeOf(GenericFIRBundle(genIn)) + + // cast T to UInt + out.bits.data := fir.io.out.bits.asUInt + } +} +// DOC include end: GenericFIRBlock chisel + +// DOC include start: TLGenericFIRBLock chisel +class TLGenericFIRBlock[T<:Data:Ring] +( + val genIn: T, + val genOut: T, + coeffs: Seq[T] +)(implicit p: Parameters) extends +GenericFIRBlock[TLClientPortParameters, TLManagerPortParameters, TLEdgeOut, TLEdgeIn, TLBundle, T]( + genIn, genOut, coeffs +) with TLDspBlock +// DOC include end: TLGenericFIRBlock chisel + +// DOC include start: TLGenericFIRChain chisel +class TLGenericFIRChain[T<:Data:Ring] (genIn: T, genOut: T, coeffs: Seq[T], params: GenericFIRParams)(implicit p: Parameters) + extends LazyModule { + val writeQueue = LazyModule(new TLWriteQueue(params.depth, AddressSet(params.writeAddress, 0xff))) + val fir = LazyModule(new TLGenericFIRBlock(genIn, genOut, coeffs)) + val readQueue = LazyModule(new TLReadQueue(params.depth, AddressSet(params.readAddress, 0xff))) + + // connect streamNodes of queues and FIR + readQueue.streamNode := fir.streamNode := writeQueue.streamNode + + lazy val module = new LazyModuleImp(this) +} +// DOC include end: TLGenericFIRChain chisel + +// DOC include start: CanHavePeripheryUIntTestFIR chisel +trait CanHavePeripheryUIntTestFIR extends BaseSubsystem { + val fir = p(GenericFIRKey) match { + case Some(params) => { + val fir = LazyModule(new TLGenericFIRChain(UInt(8.W), UInt(12.W), Seq(1.U, 2.U, 3.U), params)) + + pbus.toVariableWidthSlave(Some("firWrite")) { fir.writeQueue.mem.get } + pbus.toVariableWidthSlave(Some("firRead")) { fir.readQueue.mem.get } + } + case None => None + } +} +// DOC include end: CanHavePeripheryUIntTestFIR chisel + +/** + * Mixin to add FIR to rocket config + */ +// DOC include start: WithTestFIR +class WithUIntTestFIR extends Config((site, here, up) => { + case GenericFIRKey => Some(GenericFIRParams(depth = 8)) +}) +// DOC include end: WithTestFIR diff --git a/generators/chipyard/src/main/scala/example/dsptools/Passthrough.scala b/generators/chipyard/src/main/scala/example/dsptools/Passthrough.scala new file mode 100644 index 00000000..1d953b83 --- /dev/null +++ b/generators/chipyard/src/main/scala/example/dsptools/Passthrough.scala @@ -0,0 +1,156 @@ +//// See LICENSE for license details. +// +package chipyard.example + +import chisel3._ +import chisel3.{Bundle, Module} +import chisel3.util._ +import dspblocks._ +import dsptools.numbers._ +import freechips.rocketchip.amba.axi4stream._ +import freechips.rocketchip.config.{Parameters, Field, Config} +import freechips.rocketchip.diplomacy._ +import freechips.rocketchip.tilelink._ +import freechips.rocketchip.subsystem._ + +// Simple passthrough to use as testbed sanity check +// StreamingPassthrough params +case class StreamingPassthroughParams( + writeAddress: BigInt = 0x2000, + readAddress: BigInt = 0x2100, + depth: Int +) + +// StreamingPassthrough key +case object StreamingPassthroughKey extends Field[Option[StreamingPassthroughParams]](None) + +class StreamingPassthroughBundle[T<:Data:Ring](proto: T) extends Bundle { + val data: T = proto.cloneType + + override def cloneType: this.type = StreamingPassthroughBundle(proto).asInstanceOf[this.type] +} +object StreamingPassthroughBundle { + def apply[T<:Data:Ring](proto: T): StreamingPassthroughBundle[T] = new StreamingPassthroughBundle(proto) +} + +class StreamingPassthroughIO[T<:Data:Ring](proto: T) extends Bundle { + val in = Flipped(Decoupled(StreamingPassthroughBundle(proto))) + val out = Decoupled(StreamingPassthroughBundle(proto)) +} +object StreamingPassthroughIO { + def apply[T<:Data:Ring](proto: T): StreamingPassthroughIO[T] = new StreamingPassthroughIO(proto) +} + +class StreamingPassthrough[T<:Data:Ring](proto: T) extends Module { + val io = IO(StreamingPassthroughIO(proto)) + + io.in.ready := io.out.ready + io.out.bits.data := io.in.bits.data + io.out.valid := io.in.valid +} + +/** + * Make DspBlock wrapper for StreamingPassthrough + * @param cordicParams parameters for cordic + * @param ev$1 + * @param ev$2 + * @param ev$3 + * @param p + * @tparam D + * @tparam U + * @tparam EO + * @tparam EI + * @tparam B + * @tparam T Type parameter for passthrough, i.e. FixedPoint or DspReal + */ +abstract class StreamingPassthroughBlock[D, U, EO, EI, B<:Data, T<:Data:Ring] +( + proto: T +)(implicit p: Parameters) extends DspBlock[D, U, EO, EI, B] { + val streamNode = AXI4StreamIdentityNode() + val mem = None + + lazy val module = new LazyModuleImp(this) { + require(streamNode.in.length == 1) + require(streamNode.out.length == 1) + + val in = streamNode.in.head._1 + val out = streamNode.out.head._1 + + // instantiate passthrough + val passthrough = Module(new StreamingPassthrough(proto)) + + // Pass ready and valid from read queue to write queue + in.ready := passthrough.io.in.ready + passthrough.io.in.valid := in.valid + + // cast UInt to T + passthrough.io.in.bits := in.bits.data.asTypeOf(StreamingPassthroughBundle(proto)) + + passthrough.io.out.ready := out.ready + out.valid := passthrough.io.out.valid + + // cast T to UInt + out.bits.data := passthrough.io.out.bits.asUInt + } +} + +/** + * TLDspBlock specialization of StreamingPassthrough + * @param cordicParams parameters for passthrough + * @param ev$1 + * @param ev$2 + * @param ev$3 + * @param p + * @tparam T Type parameter for passthrough data type + */ +class TLStreamingPassthroughBlock[T<:Data:Ring] +( + val proto: T +)(implicit p: Parameters) extends +StreamingPassthroughBlock[TLClientPortParameters, TLManagerPortParameters, TLEdgeOut, TLEdgeIn, TLBundle, T](proto) +with TLDspBlock + +/** + * A chain of queues acting as our MMIOs with the passthrough module in between them. + * @param depth depth of queues + * @param ev$1 + * @param ev$2 + * @param ev$3 + * @param p + * @tparam T Type parameter for passthrough, i.e. FixedPoint or DspReal + */ +class TLStreamingPassthroughChain[T<:Data:Ring](params: StreamingPassthroughParams, proto: T)(implicit p: Parameters) + extends LazyModule { + // instantiate lazy modules + val writeQueue = LazyModule(new TLWriteQueue(params.depth, AddressSet(params.writeAddress, 0xff))) + val passthrough = LazyModule(new TLStreamingPassthroughBlock(proto)) + val readQueue = LazyModule(new TLReadQueue(params.depth, AddressSet(params.readAddress, 0xff))) + + // connect streamNodes of queues and passthrough + readQueue.streamNode := passthrough.streamNode := writeQueue.streamNode + + lazy val module = new LazyModuleImp(this) +} + +trait CanHavePeripheryUIntStreamingPassthrough { this: BaseSubsystem => + val passthrough = p(StreamingPassthroughKey) match { + case Some(params) => { + val passthrough = LazyModule(new TLStreamingPassthroughChain(params, UInt(32.W))) + + pbus.toVariableWidthSlave(Some("passthroughWrite")) { passthrough.writeQueue.mem.get } + pbus.toVariableWidthSlave(Some("passthroughRead")) { passthrough.readQueue.mem.get } + + Some(passthrough) + } + case None => None + } +} + +/** + * Mixin to add passthrough to rocket config + */ +class WithUIntStreamingPassthrough extends Config((site, here, up) => { + case StreamingPassthroughKey => Some(StreamingPassthroughParams(depth = 8)) +}) + diff --git a/tests/Makefile b/tests/Makefile index 6f247e70..6fff62ce 100644 --- a/tests/Makefile +++ b/tests/Makefile @@ -5,7 +5,7 @@ LDFLAGS= -static include libgloss.mk -PROGRAMS = pwm blkdev accum charcount nic-loopback big-blkdev pingd +PROGRAMS = pwm blkdev accum charcount nic-loopback big-blkdev pingd passthrough fir .DEFAULT_GOAL := default diff --git a/tests/fir.c b/tests/fir.c new file mode 100644 index 00000000..46313a35 --- /dev/null +++ b/tests/fir.c @@ -0,0 +1,51 @@ +#define PASSTHROUGH_WRITE 0x2000 +#define PASSTHROUGH_WRITE_COUNT 0x2008 +#define PASSTHROUGH_READ 0x2100 +#define PASSTHROUGH_READ_COUNT 0x2108 + +#include "mmio.h" + +#include +#include +#include +#include + +int main(void) +{ + printf("Starting writing\n"); + uint32_t num_tests = 15; + uint32_t test_vector[15] = {1, 2, 3, 4, 5, 4, 3, 2, 1, 5, 4, 3, 2, 1, 2}; + + for (int i = 0; i < num_tests; i++) { + reg_write64(PASSTHROUGH_WRITE, test_vector[i]); + } + + printf("Done writing\n"); + uint32_t rcnt = reg_read32(PASSTHROUGH_READ_COUNT); + printf("Write count: %d\n", reg_read32(PASSTHROUGH_WRITE_COUNT)); + printf("Read count: %d\n", rcnt); + + int failed = 0; + if (rcnt != 0) { + for (int i = 0; i < num_tests - 3; i++) { + uint32_t res = reg_read32(PASSTHROUGH_READ); + uint32_t expected = 3*test_vector[i] + 2*test_vector[i+1] + test_vector[i+2]; + if (res == expected) { + printf("\n\nPass: Got %d Expected %d\n\n", res, expected); + } else { + failed = 1; + printf("\n\nFail: Got %d Expected %d\n\n", res, expected); + } + } + } else { + failed = 1; + } + + if (failed) { + printf("\n\nSome tests failed\n\n"); + } else { + printf("\n\nAll tests passed\n\n"); + } + + return 0; +} diff --git a/tests/passthrough.c b/tests/passthrough.c new file mode 100644 index 00000000..a25e367b --- /dev/null +++ b/tests/passthrough.c @@ -0,0 +1,49 @@ +#define PASSTHROUGH_WRITE 0x2000 +#define PASSTHROUGH_WRITE_COUNT 0x2008 +#define PASSTHROUGH_READ 0x2100 +#define PASSTHROUGH_READ_COUNT 0x2108 + +#include "mmio.h" + +#include +#include +#include +#include + +int main(void) +{ + printf("Starting writing\n"); + uint32_t test_vector[7] = {3, 2, 1, 0, -1, -2, -3} ; + for (int i = 0; i < 7; i++) { + reg_write64(PASSTHROUGH_WRITE, test_vector[i]); + } + + printf("Done writing\n"); + uint32_t rcnt = reg_read32(PASSTHROUGH_READ_COUNT); + printf("Write count: %d\n", reg_read32(PASSTHROUGH_WRITE_COUNT)); + printf("Read count: %d\n", rcnt); + + int failed = 0; + if (rcnt != 0) { + for (int i = 0; i < 7; i++) { + uint32_t res = reg_read32(PASSTHROUGH_READ); + uint32_t expected = test_vector[i]; + if (res == expected) { + printf("\n\nPass: Got %d Expected %d\n\n", res, test_vector[i]); + } else { + failed = 1; + printf("\n\nFail: Got %d Expected %d\n\n", res, test_vector[i]); + } + } + } else { + failed = 1; + } + + if (failed) { + printf("\n\nSome tests failed\n\n"); + } else { + printf("\n\nAll tests passed\n\n"); + } + + return 0; +} diff --git a/tools/chisel-testers b/tools/chisel-testers index f410c593..1aa906fe 160000 --- a/tools/chisel-testers +++ b/tools/chisel-testers @@ -1 +1 @@ -Subproject commit f410c59316e5c43bac96411889aba8c5ab9a8fc0 +Subproject commit 1aa906fe168eb5ddca705ec955b27cf5c8856e4d diff --git a/tools/dsptools b/tools/dsptools index 15145ab6..63ac4b94 160000 --- a/tools/dsptools +++ b/tools/dsptools @@ -1 +1 @@ -Subproject commit 15145ab6230f869676de7eb730b4267fff7b11e8 +Subproject commit 63ac4b941bc711a0c7efd48b6418c86756b403ad From 0cc643bfdbeef594e68bc7fb0c2a9e0f1cacf9e0 Mon Sep 17 00:00:00 2001 From: Paul Rigge Date: Thu, 30 Apr 2020 10:51:32 -0700 Subject: [PATCH 02/19] Switch FIR from UInt -> FixedPoint --- .../chipyard/src/main/scala/DigitalTop.scala | 2 ++ .../scala/example/dsptools/GenericFIR.scala | 26 +++++++++-------- tests/fir.c | 28 ++++++++++++++----- 3 files changed, 38 insertions(+), 18 deletions(-) diff --git a/generators/chipyard/src/main/scala/DigitalTop.scala b/generators/chipyard/src/main/scala/DigitalTop.scala index c7b3d497..75262a99 100644 --- a/generators/chipyard/src/main/scala/DigitalTop.scala +++ b/generators/chipyard/src/main/scala/DigitalTop.scala @@ -22,6 +22,8 @@ class DigitalTop(implicit p: Parameters) extends System with icenet.CanHavePeripheryIceNIC // Enables optionally adding the IceNIC for FireSim with chipyard.example.CanHavePeripheryInitZero // Enables optionally adding the initzero example widget with chipyard.example.CanHavePeripheryGCD // Enables optionally adding the GCD example widget + with chipyard.example.CanHavePeripheryUIntTestFIR // Enables optionally adding the FIR example widget + with chipyard.example.CanHavePeripheryUIntStreamingPassthrough // Enables optionally adding the passthrough example widget { override lazy val module = new DigitalTopModule(this) } diff --git a/generators/chipyard/src/main/scala/example/dsptools/GenericFIR.scala b/generators/chipyard/src/main/scala/example/dsptools/GenericFIR.scala index 5335eb42..45093f9a 100644 --- a/generators/chipyard/src/main/scala/example/dsptools/GenericFIR.scala +++ b/generators/chipyard/src/main/scala/example/dsptools/GenericFIR.scala @@ -3,7 +3,7 @@ package chipyard.example import chisel3._ -import chisel3.{Bundle, Module} +import chisel3.experimental.FixedPoint import chisel3.util._ import dspblocks._ import dsptools.numbers._ @@ -80,9 +80,9 @@ class GenericFIR[T<:Data:Ring](genIn:T, genOut:T, coeffs: Seq[T]) extends Module // Connect adjacent cells // Note that .tail() returns a collection that consists of all // elements in the inital collection minus the first one. - // This means that we zip together directCells[0, n] and + // This means that we zip together directCells[0, n] and // directCells[1, n]. However, since zip ignores unmatched elements, - // the resulting zip is (directCells[0], directCells[1]) ... + // the resulting zip is (directCells[0], directCells[1]) ... // (directCells[n-1], directCells[n]) for ((current, next) <- directCells.zip(directCells.tail)) { next.in.bits := current.out.bits @@ -108,22 +108,22 @@ class GenericFIR[T<:Data:Ring](genIn:T, genOut:T, coeffs: Seq[T]) extends Module // // DOC include start: GenericFIRDirectCell chisel class GenericFIRDirectCell[T<:Data:Ring](genIn: T, genOut: T) extends Module { - val io = IO(GenericFIRCellIO(genIn, genOut)) - + val io = IO(GenericFIRCellIO(genIn, genOut)) + // Registers to delay the input and the valid to propagate with calculations val hasNewData = RegInit(0.U) val inputReg = Reg(genIn.cloneType) // Passthrough ready io.in.ready := io.out.ready - + // When a new transaction is ready on the input, we will have new data to output // next cycle. Take this data in when (io.in.fire()) { hasNewData := 1.U - inputReg := io.in.bits.data + inputReg := io.in.bits.data } - + // We should output data when our cell has new data to output and is ready to // recieve new data. This insures that every cell in the chain passes its data // on at the same time @@ -133,7 +133,7 @@ class GenericFIRDirectCell[T<:Data:Ring](genIn: T, genOut: T) extends Module { // Compute carry // This uses the ring implementation for + and *, i.e. // (a * b) maps to (Ring[T].prod(a, b)) for whicever T you use - io.out.bits.carry := inputReg * io.coeff + io.in.bits.carry + io.out.bits.carry := inputReg * io.coeff + io.in.bits.carry } // DOC include end: GenericFIRDirectCell chisel @@ -204,8 +204,12 @@ class TLGenericFIRChain[T<:Data:Ring] (genIn: T, genOut: T, coeffs: Seq[T], para trait CanHavePeripheryUIntTestFIR extends BaseSubsystem { val fir = p(GenericFIRKey) match { case Some(params) => { - val fir = LazyModule(new TLGenericFIRChain(UInt(8.W), UInt(12.W), Seq(1.U, 2.U, 3.U), params)) - + val fir = LazyModule(new TLGenericFIRChain( + genIn = FixedPoint(8.W, 3.BP), + genOut = FixedPoint(8.W, 3.BP), + coeffs = Seq(1.F(0.BP), 2.F(0.BP), 3.F(0.BP)), + params = params)) + pbus.toVariableWidthSlave(Some("firWrite")) { fir.writeQueue.mem.get } pbus.toVariableWidthSlave(Some("firRead")) { fir.readQueue.mem.get } } diff --git a/tests/fir.c b/tests/fir.c index 46313a35..be61534e 100644 --- a/tests/fir.c +++ b/tests/fir.c @@ -3,6 +3,9 @@ #define PASSTHROUGH_READ 0x2100 #define PASSTHROUGH_READ_COUNT 0x2108 +#define BP 3 +#define BP_SCALE ((double)(1 << BP)) + #include "mmio.h" #include @@ -10,14 +13,23 @@ #include #include +uint64_t roundi(double x) +{ + if (x < 0.0) { + return (uint64_t)(x - 0.5); + } else { + return (uint64_t)(x + 0.5); + } +} + int main(void) { - printf("Starting writing\n"); - uint32_t num_tests = 15; - uint32_t test_vector[15] = {1, 2, 3, 4, 5, 4, 3, 2, 1, 5, 4, 3, 2, 1, 2}; + double test_vector[15] = {1.0, 2.0, 3.0, 4.0, 5.0, 4.0, 3.0, 2.0, 1.0, 0.5, 0.25, 0.125, 0.125}; + uint32_t num_tests = sizeof(test_vector) / sizeof(double); + printf("Starting writing %d inputs\n", num_tests); for (int i = 0; i < num_tests; i++) { - reg_write64(PASSTHROUGH_WRITE, test_vector[i]); + reg_write64(PASSTHROUGH_WRITE, roundi(test_vector[i] * BP_SCALE)); } printf("Done writing\n"); @@ -29,12 +41,14 @@ int main(void) if (rcnt != 0) { for (int i = 0; i < num_tests - 3; i++) { uint32_t res = reg_read32(PASSTHROUGH_READ); - uint32_t expected = 3*test_vector[i] + 2*test_vector[i+1] + test_vector[i+2]; + // double res = ((double)reg_read32(PASSTHROUGH_READ)) / BP_SCALE; + double expected_double = 3*test_vector[i] + 2*test_vector[i+1] + test_vector[i+2]; + uint32_t expected = ((uint32_t)(expected_double * BP_SCALE + 0.5)) & 0xFF; if (res == expected) { - printf("\n\nPass: Got %d Expected %d\n\n", res, expected); + printf("\n\nPass: Got %u Expected %u\n\n", res, expected); } else { failed = 1; - printf("\n\nFail: Got %d Expected %d\n\n", res, expected); + printf("\n\nFail: Got %u Expected %u\n\n", res, expected); } } } else { From ae1aa31fce04db79789bfe8a9a61071ce588e436 Mon Sep 17 00:00:00 2001 From: Paul Rigge Date: Mon, 25 May 2020 20:23:19 +0000 Subject: [PATCH 03/19] Incorporate feedback --- docs/Customization/Dsptools-Blocks.rst | 31 +++++++++---- .../chipyard/src/main/scala/DigitalTop.scala | 4 +- .../src/main/scala/config/RocketConfigs.scala | 44 +++++++++++++++---- .../scala/example/dsptools/GenericFIR.scala | 12 ++--- ...rough.scala => StreamingPassthrough.scala} | 4 +- 5 files changed, 68 insertions(+), 27 deletions(-) rename generators/chipyard/src/main/scala/example/dsptools/{Passthrough.scala => StreamingPassthrough.scala} (97%) diff --git a/docs/Customization/Dsptools-Blocks.rst b/docs/Customization/Dsptools-Blocks.rst index 228f5f91..ba189ac5 100644 --- a/docs/Customization/Dsptools-Blocks.rst +++ b/docs/Customization/Dsptools-Blocks.rst @@ -1,11 +1,24 @@ .. _dsptools-blocks: +Dsptools is a Chisel library that aids in writing custom signal processing accelerators. It does this by: +* Giving types and helpers that allow you to express mathematical operations more directly. +* Typeclasses that let you write polymorphic generators, for example an FIR filter generator that works for both real- and complex-valued filters. +* Structures for packaging DSP blocks and integrating them into a rocketchip-based SoC. +* Test harnesses for testing DSP circuits, as well as VIP-style drivers and monitors for DSP blocks. + +The `Dsptools `_ repository has more documentation. + + Dsptools Blocks =============== +A ``DspBlock`` is the basic unit of signal processing functionality that can be integrated into an SoC. +It has a AXI4-stream interface and an optional memory interface. +The idea idea is that these ``DspBlocks`` can be easily designed, unit tested, and assembled lego-style to build complex functionality. +A ``DspChain`` is one example of how to assemble ``DspBlocks``, in which case the streaming interfaces are connected serially into a pipeline, and a bus is instatiated and connected to every block with a memory interface. -Another way to create a MMIO peripheral is to use the Dsptools library for Chisel. In this method, a memory interface is created by creating a "chain". This chain consists of a custom module placed inside a ``DspBlock``, which is then sandwiched between a ``ReadQueue`` and ``WriteQueue``. Those queues then act as memory mapped interfaces to the Rocket Chip SoCs. This section will again primarily focus on designing Tilelink-based peripherals. However, through the resources provided in Dsptools, one could also define an AXI4-based peripheral by following similar steps. +This project has example designs that integrate a ``DspBlock`` to a rocketchip-based SoC as an MMIO peripheral. The custom ``DspBlock`` has a ``ReadQueue`` before it and a ``WriteQueue`` after it, which allow memory mapped access to the streaming interfaces so the rocket core can interact with the ``DspBlock``. This section will primarily focus on designing Tilelink-based peripherals. However, through the resources provided in Dsptools, one could also define an AXI4-based peripheral by following similar steps. Furthermore, the examples here are simple, but can be extended to implement more complex accelerators, for example an `OFDM baseband `_ or a `spectrometer `_. -For this example, we will show you how to connect a simple FIR filter created using Dsptools as an MMIO peripheral. The full code can be found in ``generators/example/src/main/scala/dsptools/GenericFIR.scala``. That being said, one could substitute any module with a ready valid interface in the place of the FIR and achieve the same results. As long as the read and valid signals of the module are attached to those of a corresponding ``DSPBlock`` wrapper, and that wrapper is placed in a chain with a ``ReadQueue`` and a ``WriteQueue``, following the general outline establised by these steps will allow you to interact with that block as a memory mapped IO +For this example, we will show you how to connect a simple FIR filter created using Dsptools as an MMIO peripheral. The full code can be found in ``generators/example/src/main/scala/dsptools/GenericFIR.scala``. That being said, one could substitute any module with a ready valid interface in the place of the FIR and achieve the same results. As long as the read and valid signals of the module are attached to those of a corresponding ``DSPBlock`` wrapper, and that wrapper is placed in a chain with a ``ReadQueue`` and a ``WriteQueue``, following the general outline establised by these steps will allow you to interact with that block as a memory mapped IO. The module ``GenericFIR`` is the overall wrapper of our FIR module. This module links together a variable number of ``GenericFIRDirectCell`` submodules, each of which performs the computations for one coefficient in a FIR direct form architecture. It is important to note that both modules are type generic, which means that they can be instantiated for any datatype that implements ``Ring`` operations per the specifications on ``T``. @@ -56,10 +69,10 @@ As in the previous MMIO example, we use a cake pattern to hook up our module to .. literalinclude:: ../../generators/example/src/main/scala/dsptools/GenericFIR.scala :language: scala - :start-after: DOC include start: CanHavePeripheryUIntTestFIR chisel - :end-before: DOC include end: CanHavePeripheryUIntTestFIR chisel + :start-after: DOC include start: CanHavePeripheryFIR chisel + :end-before: DOC include end: CanHavePeripheryFIR chisel -Note that this is the point at which we decide the datatype for our FIR. It is also possible with some reworking to push the datatype selection out to the top level. +Note that this is the point at which we decide the datatype for our FIR. Our module does not need to be connected to concrete IOs or wires, so we do not need to create a concrete trait. @@ -73,12 +86,12 @@ Once again following the path of the previous MMIO example, we now want to mix o :start-after: DOC include start: Top :end-before: DOC include end: Top -Finally, we create the configuration class in ``generators/example/src/main/scala/RocketConfigs.scala`` that uses the ``WithUIntTestFIR`` mixin defined in ``generators/example/src/main/scala/ConfigMixins.scala``. +Finally, we create the configuration class in ``generators/example/src/main/scala/RocketConfigs.scala`` that uses the ``WithFIR`` mixin defined in ``generators/example/src/main/scala/ConfigMixins.scala``. .. literalinclude:: ../../generators/example/src/main/scala/ConfigMixins.scala :language: scala - :start-after: DOC include start: WithTestFIR - :end-before: DOC include end: WithTestFIR + :start-after: DOC include start: WithFIR + :end-before: DOC include end: WithFIR .. literalinclude:: ../../generators/example/src/main/scala/RocketConfigs.scala :language: scala @@ -88,7 +101,7 @@ Finally, we create the configuration class in ``generators/example/src/main/scal Testing ------- -We can now test that the FIR is working. The test program is found in ``tests/gcd.c``. +We can now test that the FIR is working. The test program is found in ``tests/fir.c``. .. literalinclude:: ../../tests/fir.c :language: c diff --git a/generators/chipyard/src/main/scala/DigitalTop.scala b/generators/chipyard/src/main/scala/DigitalTop.scala index a31079e6..0866676b 100644 --- a/generators/chipyard/src/main/scala/DigitalTop.scala +++ b/generators/chipyard/src/main/scala/DigitalTop.scala @@ -23,8 +23,8 @@ class DigitalTop(implicit p: Parameters) extends System with icenet.CanHavePeripheryIceNIC // Enables optionally adding the IceNIC for FireSim with chipyard.example.CanHavePeripheryInitZero // Enables optionally adding the initzero example widget with chipyard.example.CanHavePeripheryGCD // Enables optionally adding the GCD example widget - with chipyard.example.CanHavePeripheryUIntTestFIR // Enables optionally adding the FIR example widget - with chipyard.example.CanHavePeripheryUIntStreamingPassthrough // Enables optionally adding the passthrough example widget + with chipyard.example.CanHavePeripheryFIR // Enables optionally adding the FIR example widget + with chipyard.example.CanHavePeripheryStreamingPassthrough // Enables optionally adding the passthrough example widget with nvidia.blocks.dla.CanHavePeripheryNVDLA // Enables optionally having an NVDLA { override lazy val module = new DigitalTopModule(this) diff --git a/generators/chipyard/src/main/scala/config/RocketConfigs.scala b/generators/chipyard/src/main/scala/config/RocketConfigs.scala index 4bc3a6d5..a55718ff 100644 --- a/generators/chipyard/src/main/scala/config/RocketConfigs.scala +++ b/generators/chipyard/src/main/scala/config/RocketConfigs.scala @@ -426,16 +426,44 @@ class RingSystemBusRocketConfig extends Config( new freechips.rocketchip.system.BaseConfig) // DOC include end: RingSystemBusRocket -class UIntStreamingPassthroughRocketConfig extends Config( - new chipyard.example.WithUIntStreamingPassthrough ++ // use top with tilelink-controlled passthrough - new RocketConfig -) +class StreamingPassthroughRocketConfig extends Config( + new chipyard.example.WithStreamingPassthrough ++ // use top with tilelink-controlled passthrough + new chipyard.iobinders.WithUARTAdapter ++ // display UART with a SimUARTAdapter + new chipyard.iobinders.WithTieOffInterrupts ++ // tie off top-level interrupts + new chipyard.iobinders.WithBlackBoxSimMem ++ // drive the master AXI4 memory with a SimAXIMem + new chipyard.iobinders.WithTiedOffDebug ++ // tie off debug (since we are using SimSerial for testing) + new chipyard.iobinders.WithSimSerial ++ // drive TSI with SimSerial for testing + new testchipip.WithTSI ++ // use testchipip serial offchip link + new chipyard.config.WithBootROM ++ // use default bootrom + new chipyard.config.WithUART ++ // add a UART + new chipyard.config.WithL2TLBs(1024) ++ // use L2 TLBs + new freechips.rocketchip.subsystem.WithNoMMIOPort ++ // no top-level MMIO master port (overrides default set in rocketchip) + new freechips.rocketchip.subsystem.WithNoSlavePort ++ // no top-level MMIO slave port (overrides default set in rocketchip) + new freechips.rocketchip.subsystem.WithInclusiveCache ++ // use Sifive L2 cache + new freechips.rocketchip.subsystem.WithNExtTopInterrupts(0) ++ // no external interrupts + new freechips.rocketchip.subsystem.WithNBigCores(1) ++ // single rocket-core + new freechips.rocketchip.subsystem.WithCoherentBusTopology ++ // hierarchical buses including mbus+l2 + new freechips.rocketchip.system.BaseConfig) // "base" rocketchip system // DOC include start: FIRRocketConfig -class UIntTestFIRRocketConfig extends Config ( - new chipyard.example.WithUIntTestFIR ++ // use top with tilelink-controlled FIR - new RocketConfig -) +class FIRRocketConfig extends Config ( + new chipyard.example.WithFIR ++ // use top with tilelink-controlled FIR + new chipyard.iobinders.WithUARTAdapter ++ // display UART with a SimUARTAdapter + new chipyard.iobinders.WithTieOffInterrupts ++ // tie off top-level interrupts + new chipyard.iobinders.WithBlackBoxSimMem ++ // drive the master AXI4 memory with a SimAXIMem + new chipyard.iobinders.WithTiedOffDebug ++ // tie off debug (since we are using SimSerial for testing) + new chipyard.iobinders.WithSimSerial ++ // drive TSI with SimSerial for testing + new testchipip.WithTSI ++ // use testchipip serial offchip link + new chipyard.config.WithBootROM ++ // use default bootrom + new chipyard.config.WithUART ++ // add a UART + new chipyard.config.WithL2TLBs(1024) ++ // use L2 TLBs + new freechips.rocketchip.subsystem.WithNoMMIOPort ++ // no top-level MMIO master port (overrides default set in rocketchip) + new freechips.rocketchip.subsystem.WithNoSlavePort ++ // no top-level MMIO slave port (overrides default set in rocketchip) + new freechips.rocketchip.subsystem.WithInclusiveCache ++ // use Sifive L2 cache + new freechips.rocketchip.subsystem.WithNExtTopInterrupts(0) ++ // no external interrupts + new freechips.rocketchip.subsystem.WithNBigCores(1) ++ // single rocket-core + new freechips.rocketchip.subsystem.WithCoherentBusTopology ++ // hierarchical buses including mbus+l2 + new freechips.rocketchip.system.BaseConfig) // "base" rocketchip system // DOC include end: FIRRocketConfig class SmallNVDLARocketConfig extends Config( diff --git a/generators/chipyard/src/main/scala/example/dsptools/GenericFIR.scala b/generators/chipyard/src/main/scala/example/dsptools/GenericFIR.scala index 45093f9a..6a18af1a 100644 --- a/generators/chipyard/src/main/scala/example/dsptools/GenericFIR.scala +++ b/generators/chipyard/src/main/scala/example/dsptools/GenericFIR.scala @@ -200,8 +200,8 @@ class TLGenericFIRChain[T<:Data:Ring] (genIn: T, genOut: T, coeffs: Seq[T], para } // DOC include end: TLGenericFIRChain chisel -// DOC include start: CanHavePeripheryUIntTestFIR chisel -trait CanHavePeripheryUIntTestFIR extends BaseSubsystem { +// DOC include start: CanHavePeripheryFIR chisel +trait CanHavePeripheryFIR extends BaseSubsystem { val fir = p(GenericFIRKey) match { case Some(params) => { val fir = LazyModule(new TLGenericFIRChain( @@ -216,13 +216,13 @@ trait CanHavePeripheryUIntTestFIR extends BaseSubsystem { case None => None } } -// DOC include end: CanHavePeripheryUIntTestFIR chisel +// DOC include end: CanHavePeripheryFIR chisel /** * Mixin to add FIR to rocket config */ -// DOC include start: WithTestFIR -class WithUIntTestFIR extends Config((site, here, up) => { +// DOC include start: WithFIR +class WithFIR extends Config((site, here, up) => { case GenericFIRKey => Some(GenericFIRParams(depth = 8)) }) -// DOC include end: WithTestFIR +// DOC include end: WithFIR diff --git a/generators/chipyard/src/main/scala/example/dsptools/Passthrough.scala b/generators/chipyard/src/main/scala/example/dsptools/StreamingPassthrough.scala similarity index 97% rename from generators/chipyard/src/main/scala/example/dsptools/Passthrough.scala rename to generators/chipyard/src/main/scala/example/dsptools/StreamingPassthrough.scala index 1d953b83..87f97760 100644 --- a/generators/chipyard/src/main/scala/example/dsptools/Passthrough.scala +++ b/generators/chipyard/src/main/scala/example/dsptools/StreamingPassthrough.scala @@ -133,7 +133,7 @@ class TLStreamingPassthroughChain[T<:Data:Ring](params: StreamingPassthroughPara lazy val module = new LazyModuleImp(this) } -trait CanHavePeripheryUIntStreamingPassthrough { this: BaseSubsystem => +trait CanHavePeripheryStreamingPassthrough { this: BaseSubsystem => val passthrough = p(StreamingPassthroughKey) match { case Some(params) => { val passthrough = LazyModule(new TLStreamingPassthroughChain(params, UInt(32.W))) @@ -150,7 +150,7 @@ trait CanHavePeripheryUIntStreamingPassthrough { this: BaseSubsystem => /** * Mixin to add passthrough to rocket config */ -class WithUIntStreamingPassthrough extends Config((site, here, up) => { +class WithStreamingPassthrough extends Config((site, here, up) => { case StreamingPassthroughKey => Some(StreamingPassthroughParams(depth = 8)) }) From e678e01c0a865c1774f2705f5a519ed84036e74d Mon Sep 17 00:00:00 2001 From: Paul Rigge Date: Mon, 25 May 2020 20:26:18 +0000 Subject: [PATCH 04/19] Bump dsptools --- tools/dsptools | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/dsptools b/tools/dsptools index 63ac4b94..7c408297 160000 --- a/tools/dsptools +++ b/tools/dsptools @@ -1 +1 @@ -Subproject commit 63ac4b941bc711a0c7efd48b6418c86756b403ad +Subproject commit 7c40829718592fc99b011d53595f01b00a3d68cf From 7c074661d5a832105a07b3cc1dc3217336eadb54 Mon Sep 17 00:00:00 2001 From: Paul Rigge Date: Mon, 25 May 2020 20:27:58 +0000 Subject: [PATCH 05/19] Add CI for dsptools examples --- .circleci/config.yml | 38 ++++++++++++++++++++++++++++++++++++++ .circleci/defaults.sh | 2 ++ .circleci/run-tests.sh | 8 ++++++++ 3 files changed, 48 insertions(+) diff --git a/.circleci/config.yml b/.circleci/config.yml index 03943fa8..1d205ee9 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -204,6 +204,16 @@ jobs: steps: - prepare-rtl: project-key: "chipyard-sha3" + prepare-chipyard-fir: + executor: main-env + steps: + - prepare-rtl: + project-key: "chipyard-fir" + prepare-chipyard-passthrough: + executor: main-env + steps: + - prepare-rtl: + project-key: "chipyard-passthrough" prepare-chipyard-hetero: executor: main-env steps: @@ -287,6 +297,16 @@ jobs: steps: - run-tests: project-key: "chipyard-sha3" + chipyard-fir-run-tests: + executor: main-env + steps: + - run-tests: + project-key: "chipyard-fir" + chipyard-passthrough-run-tests: + executor: main-env + steps: + - run-tests: + project-key: "chipyard-passthrough" chipyard-hetero-run-tests: executor: main-env steps: @@ -439,6 +459,16 @@ workflows: - install-riscv-toolchain - install-verilator + - prepare-chipyard-fir: + requires: + - install-riscv-toolchain + - install-verilator + + - prepare-chipyard-passthrough: + requires: + - install-riscv-toolchain + - install-verilator + - prepare-chipyard-hetero: requires: - install-riscv-toolchain @@ -525,6 +555,14 @@ workflows: requires: - prepare-chipyard-sha3 + - chipyard-fir-run-tests: + requires: + - prepare-chipyard-fir + + - chipyard-passthrough-run-tests: + requires: + - prepare-chipyard-passthrough + - chipyard-hetero-run-tests: requires: - prepare-chipyard-hetero diff --git a/.circleci/defaults.sh b/.circleci/defaults.sh index d9699df8..6225ad95 100755 --- a/.circleci/defaults.sh +++ b/.circleci/defaults.sh @@ -46,6 +46,8 @@ LOCAL_FIRESIM_DIR=$LOCAL_CHIPYARD_DIR/sims/firesim/sim declare -A mapping mapping["chipyard-rocket"]="SUB_PROJECT=chipyard" mapping["chipyard-sha3"]="SUB_PROJECT=chipyard CONFIG=Sha3RocketConfig" +mapping["chipyard-fir"]="SUB_PROJECT=chipyard CONFIG=FIRRocketConfig" +mapping["chipyard-passthrough"]="SUB_PROJECT=chipyard CONFIG=StreamingPassthroughRocketConfig" mapping["chipyard-hetero"]="SUB_PROJECT=chipyard CONFIG=LargeBoomAndRocketConfig" mapping["chipyard-boom"]="SUB_PROJECT=chipyard CONFIG=SmallBoomConfig" mapping["chipyard-blkdev"]="SUB_PROJECT=chipyard CONFIG=SimBlockDeviceRocketConfig" diff --git a/.circleci/run-tests.sh b/.circleci/run-tests.sh index 19a0e566..f319880b 100755 --- a/.circleci/run-tests.sh +++ b/.circleci/run-tests.sh @@ -62,6 +62,14 @@ case $1 in (cd $LOCAL_CHIPYARD_DIR/generators/sha3/software && ./build.sh) $LOCAL_SIM_DIR/simulator-chipyard-Sha3RocketConfig $LOCAL_CHIPYARD_DIR/generators/sha3/software/benchmarks/bare/sha3-rocc.riscv ;; + chipyard-passthrough) + make -C $LOCAL_CHIPYARD_DIR/tests + $LOCAL_SIM_DIR/simulator-chipyard-StreamingPassthroughRocketConfig $LOCAL_CHIPYARD_DIR/tests/passthrough.riscv + ;; + chipyard-fir) + make -C $LOCAL_CHIPYARD_DIR/tests + $LOCAL_SIM_DIR/simulator-chipyard-FIRRocketConfig $LOCAL_CHIPYARD_DIR/tests/fir.riscv + ;; chipyard-spiflashread) make -C $LOCAL_CHIPYARD_DIR/tests make -C $LOCAL_SIM_DIR ${mapping[$1]} BINARY=$LOCAL_CHIPYARD_DIR/tests/spiflashread.riscv SIM_FLAGS="+spiflash0=${LOCAL_CHIPYARD_DIR}/tests/spiflash.img" run-binary From 863b3a7bc324548ac09806fa79515b98ac56eadf Mon Sep 17 00:00:00 2001 From: Paul Rigge Date: Mon, 25 May 2020 14:08:30 -0700 Subject: [PATCH 06/19] Fix doc references --- docs/Customization/Dsptools-Blocks.rst | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/docs/Customization/Dsptools-Blocks.rst b/docs/Customization/Dsptools-Blocks.rst index ba189ac5..45053bfa 100644 --- a/docs/Customization/Dsptools-Blocks.rst +++ b/docs/Customization/Dsptools-Blocks.rst @@ -18,16 +18,16 @@ A ``DspChain`` is one example of how to assemble ``DspBlocks``, in which case th This project has example designs that integrate a ``DspBlock`` to a rocketchip-based SoC as an MMIO peripheral. The custom ``DspBlock`` has a ``ReadQueue`` before it and a ``WriteQueue`` after it, which allow memory mapped access to the streaming interfaces so the rocket core can interact with the ``DspBlock``. This section will primarily focus on designing Tilelink-based peripherals. However, through the resources provided in Dsptools, one could also define an AXI4-based peripheral by following similar steps. Furthermore, the examples here are simple, but can be extended to implement more complex accelerators, for example an `OFDM baseband `_ or a `spectrometer `_. -For this example, we will show you how to connect a simple FIR filter created using Dsptools as an MMIO peripheral. The full code can be found in ``generators/example/src/main/scala/dsptools/GenericFIR.scala``. That being said, one could substitute any module with a ready valid interface in the place of the FIR and achieve the same results. As long as the read and valid signals of the module are attached to those of a corresponding ``DSPBlock`` wrapper, and that wrapper is placed in a chain with a ``ReadQueue`` and a ``WriteQueue``, following the general outline establised by these steps will allow you to interact with that block as a memory mapped IO. +For this example, we will show you how to connect a simple FIR filter created using Dsptools as an MMIO peripheral. The full code can be found in ``generators/chipyard/src/main/scala/example/dsptools/GenericFIR.scala``. That being said, one could substitute any module with a ready valid interface in the place of the FIR and achieve the same results. As long as the read and valid signals of the module are attached to those of a corresponding ``DSPBlock`` wrapper, and that wrapper is placed in a chain with a ``ReadQueue`` and a ``WriteQueue``, following the general outline establised by these steps will allow you to interact with that block as a memory mapped IO. The module ``GenericFIR`` is the overall wrapper of our FIR module. This module links together a variable number of ``GenericFIRDirectCell`` submodules, each of which performs the computations for one coefficient in a FIR direct form architecture. It is important to note that both modules are type generic, which means that they can be instantiated for any datatype that implements ``Ring`` operations per the specifications on ``T``. -.. literalinclude:: ../../generators/example/src/main/scala/dsptools/GenericFIR.scala +.. literalinclude:: ../../generators/chipyard/src/main/scala/example/dsptools/GenericFIR.scala :language: scala :start-after: DOC include start: GenericFIR chisel :end-before: DOC include end: GenericFIR chisel -.. literalinclude:: ../../generators/example/src/main/scala/dsptools/GenericFIR.scala +.. literalinclude:: ../../generators/chipyard/src/main/scala/example/dsptools/GenericFIR.scala :language: scala :start-after: DOC include start: GenericFIRDirectCell chisel :end-before: DOC include end: GenericFIRDirectCell chisel @@ -42,7 +42,7 @@ The first step in attaching the FIR filter as a MMIO peripheral is to create an 3. Cast the module input data to the input type of ``GenericFIR`` (``GenericFIRBundle``) and attach. 4. Cast the output of ``GenericFIR`` to ``UInt`` and attach to the module output. -.. literalinclude:: ../../generators/example/src/main/scala/dsptools/GenericFIR.scala +.. literalinclude:: ../../generators/chipyard/src/main/scala/example/dsptools/GenericFIR.scala :language: scala :start-after: DOC include start: GenericFIRBlock chisel :end-before: DOC include end: GenericFIRBlock chisel @@ -51,14 +51,14 @@ Connecting by TileLink ---------------------- With these classes implemented, you can begin to construct the chain by extending ``GenericFIRBlock`` while using the ``TLDspBlock`` trait via mixin. -.. literalinclude:: ../../generators/example/src/main/scala/dsptools/GenericFIR.scala +.. literalinclude:: ../../generators/chipyard/src/main/scala/example/dsptools/GenericFIR.scala :language: scala :start-after: DOC include start: TLGenericFIRBlock chisel :end-before: DOC include end: TLGenericFIRBlock chisel -We can then construct the final chain by utilizing the ``TLWriteQueue`` and ``TLReadeQueue`` modules found in ``generators/example/src/main/scala/dsptools/DspBlocks.scala``. Inside our chain, we construct an instance of each queue as well as our ``TLGenericFIRBlock``. We then take the ``steamnode`` from each module and wire them all together to link the chain. +We can then construct the final chain by utilizing the ``TLWriteQueue`` and ``TLReadeQueue`` modules found in ``generators/chipyard/src/main/scala/example/dsptools/DspBlocks.scala``. Inside our chain, we construct an instance of each queue as well as our ``TLGenericFIRBlock``. We then take the ``steamnode`` from each module and wire them all together to link the chain. -.. literalinclude:: ../../generators/example/src/main/scala/dsptools/GenericFIR.scala +.. literalinclude:: ../../generators/chipyard/src/main/scala/example/dsptools/GenericFIR.scala :language: scala :start-after: DOC include start: TLGenericFIRChain chisel :end-before: DOC include end: TLGenericFIRChain chisel @@ -67,7 +67,7 @@ Top Level Traits ---------------- As in the previous MMIO example, we use a cake pattern to hook up our module to our SoC. -.. literalinclude:: ../../generators/example/src/main/scala/dsptools/GenericFIR.scala +.. literalinclude:: ../../generators/chipyard/src/main/scala/example/dsptools/GenericFIR.scala :language: scala :start-after: DOC include start: CanHavePeripheryFIR chisel :end-before: DOC include end: CanHavePeripheryFIR chisel @@ -79,21 +79,21 @@ Our module does not need to be connected to concrete IOs or wires, so we do not Constructing the Top and Config ------------------------------- -Once again following the path of the previous MMIO example, we now want to mix our traits into the system as a whole. The code is from ``generators/example/src/main/scala/Top.scala`` +Once again following the path of the previous MMIO example, we now want to mix our traits into the system as a whole. The code is from ``generators/chipyard/src/main/scala/DigitalTop.scala`` -.. literalinclude:: ../../generators/example/src/main/scala/Top.scala +.. literalinclude:: ../../generators/chipyard/src/main/scala/DigitalTop.scala :language: scala :start-after: DOC include start: Top :end-before: DOC include end: Top -Finally, we create the configuration class in ``generators/example/src/main/scala/RocketConfigs.scala`` that uses the ``WithFIR`` mixin defined in ``generators/example/src/main/scala/ConfigMixins.scala``. +Finally, we create the configuration class in ``generators/chipyard/src/main/scala/config/RocketConfigs.scala`` that uses the ``WithFIR`` mixin defined in ``generators/chipyard/src/main/scala/example/dsptools/GenericFIR.scala``. -.. literalinclude:: ../../generators/example/src/main/scala/ConfigMixins.scala +.. literalinclude:: ../../generators/chipyard/src/main/scala/example/dsptools/GenericFIR.scala :language: scala :start-after: DOC include start: WithFIR :end-before: DOC include end: WithFIR -.. literalinclude:: ../../generators/example/src/main/scala/RocketConfigs.scala +.. literalinclude:: ../../generators/chipyard/src/main/scala/config/RocketConfigs.scala :language: scala :start-after: DOC include start: FIRRocketConfig :end-before: DOC include end: FIRRocketConfig From f619b69e330e9751fac6cb373d5b866d8beeec12 Mon Sep 17 00:00:00 2001 From: Paul Rigge Date: Mon, 25 May 2020 14:11:02 -0700 Subject: [PATCH 07/19] Update docs/Customization/Dsptools-Blocks.rst Co-authored-by: alonamid --- docs/Customization/Dsptools-Blocks.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/Customization/Dsptools-Blocks.rst b/docs/Customization/Dsptools-Blocks.rst index 45053bfa..1f7dcfde 100644 --- a/docs/Customization/Dsptools-Blocks.rst +++ b/docs/Customization/Dsptools-Blocks.rst @@ -13,7 +13,7 @@ Dsptools Blocks =============== A ``DspBlock`` is the basic unit of signal processing functionality that can be integrated into an SoC. It has a AXI4-stream interface and an optional memory interface. -The idea idea is that these ``DspBlocks`` can be easily designed, unit tested, and assembled lego-style to build complex functionality. +The idea is that these ``DspBlocks`` can be easily designed, unit tested, and assembled lego-style to build complex functionality. A ``DspChain`` is one example of how to assemble ``DspBlocks``, in which case the streaming interfaces are connected serially into a pipeline, and a bus is instatiated and connected to every block with a memory interface. This project has example designs that integrate a ``DspBlock`` to a rocketchip-based SoC as an MMIO peripheral. The custom ``DspBlock`` has a ``ReadQueue`` before it and a ``WriteQueue`` after it, which allow memory mapped access to the streaming interfaces so the rocket core can interact with the ``DspBlock``. This section will primarily focus on designing Tilelink-based peripherals. However, through the resources provided in Dsptools, one could also define an AXI4-based peripheral by following similar steps. Furthermore, the examples here are simple, but can be extended to implement more complex accelerators, for example an `OFDM baseband `_ or a `spectrometer `_. From 0cdc8fe244a507ac7fe886572f3a6e2c23415a2e Mon Sep 17 00:00:00 2001 From: Paul Rigge Date: Mon, 25 May 2020 14:13:15 -0700 Subject: [PATCH 08/19] Remove comments for non-unique portions of config fragment --- .../src/main/scala/config/RocketConfigs.scala | 66 +++++++++---------- 1 file changed, 33 insertions(+), 33 deletions(-) diff --git a/generators/chipyard/src/main/scala/config/RocketConfigs.scala b/generators/chipyard/src/main/scala/config/RocketConfigs.scala index a55718ff..76ed912f 100644 --- a/generators/chipyard/src/main/scala/config/RocketConfigs.scala +++ b/generators/chipyard/src/main/scala/config/RocketConfigs.scala @@ -427,43 +427,43 @@ class RingSystemBusRocketConfig extends Config( // DOC include end: RingSystemBusRocket class StreamingPassthroughRocketConfig extends Config( - new chipyard.example.WithStreamingPassthrough ++ // use top with tilelink-controlled passthrough - new chipyard.iobinders.WithUARTAdapter ++ // display UART with a SimUARTAdapter - new chipyard.iobinders.WithTieOffInterrupts ++ // tie off top-level interrupts - new chipyard.iobinders.WithBlackBoxSimMem ++ // drive the master AXI4 memory with a SimAXIMem - new chipyard.iobinders.WithTiedOffDebug ++ // tie off debug (since we are using SimSerial for testing) - new chipyard.iobinders.WithSimSerial ++ // drive TSI with SimSerial for testing - new testchipip.WithTSI ++ // use testchipip serial offchip link - new chipyard.config.WithBootROM ++ // use default bootrom - new chipyard.config.WithUART ++ // add a UART - new chipyard.config.WithL2TLBs(1024) ++ // use L2 TLBs - new freechips.rocketchip.subsystem.WithNoMMIOPort ++ // no top-level MMIO master port (overrides default set in rocketchip) - new freechips.rocketchip.subsystem.WithNoSlavePort ++ // no top-level MMIO slave port (overrides default set in rocketchip) - new freechips.rocketchip.subsystem.WithInclusiveCache ++ // use Sifive L2 cache - new freechips.rocketchip.subsystem.WithNExtTopInterrupts(0) ++ // no external interrupts - new freechips.rocketchip.subsystem.WithNBigCores(1) ++ // single rocket-core - new freechips.rocketchip.subsystem.WithCoherentBusTopology ++ // hierarchical buses including mbus+l2 - new freechips.rocketchip.system.BaseConfig) // "base" rocketchip system + new chipyard.example.WithStreamingPassthrough ++ // use top with tilelink-controlled passthrough + new chipyard.iobinders.WithUARTAdapter ++ + new chipyard.iobinders.WithTieOffInterrupts ++ + new chipyard.iobinders.WithBlackBoxSimMem ++ + new chipyard.iobinders.WithTiedOffDebug ++ + new chipyard.iobinders.WithSimSerial ++ + new testchipip.WithTSI ++ + new chipyard.config.WithBootROM ++ + new chipyard.config.WithUART ++ + new chipyard.config.WithL2TLBs(1024) ++ + new freechips.rocketchip.subsystem.WithNoMMIOPort ++ + new freechips.rocketchip.subsystem.WithNoSlavePort ++ + new freechips.rocketchip.subsystem.WithInclusiveCache ++ + new freechips.rocketchip.subsystem.WithNExtTopInterrupts(0) ++ + new freechips.rocketchip.subsystem.WithNBigCores(1) ++ + new freechips.rocketchip.subsystem.WithCoherentBusTopology ++ + new freechips.rocketchip.system.BaseConfig) // DOC include start: FIRRocketConfig class FIRRocketConfig extends Config ( new chipyard.example.WithFIR ++ // use top with tilelink-controlled FIR - new chipyard.iobinders.WithUARTAdapter ++ // display UART with a SimUARTAdapter - new chipyard.iobinders.WithTieOffInterrupts ++ // tie off top-level interrupts - new chipyard.iobinders.WithBlackBoxSimMem ++ // drive the master AXI4 memory with a SimAXIMem - new chipyard.iobinders.WithTiedOffDebug ++ // tie off debug (since we are using SimSerial for testing) - new chipyard.iobinders.WithSimSerial ++ // drive TSI with SimSerial for testing - new testchipip.WithTSI ++ // use testchipip serial offchip link - new chipyard.config.WithBootROM ++ // use default bootrom - new chipyard.config.WithUART ++ // add a UART - new chipyard.config.WithL2TLBs(1024) ++ // use L2 TLBs - new freechips.rocketchip.subsystem.WithNoMMIOPort ++ // no top-level MMIO master port (overrides default set in rocketchip) - new freechips.rocketchip.subsystem.WithNoSlavePort ++ // no top-level MMIO slave port (overrides default set in rocketchip) - new freechips.rocketchip.subsystem.WithInclusiveCache ++ // use Sifive L2 cache - new freechips.rocketchip.subsystem.WithNExtTopInterrupts(0) ++ // no external interrupts - new freechips.rocketchip.subsystem.WithNBigCores(1) ++ // single rocket-core - new freechips.rocketchip.subsystem.WithCoherentBusTopology ++ // hierarchical buses including mbus+l2 - new freechips.rocketchip.system.BaseConfig) // "base" rocketchip system + new chipyard.iobinders.WithUARTAdapter ++ + new chipyard.iobinders.WithTieOffInterrupts ++ + new chipyard.iobinders.WithBlackBoxSimMem ++ + new chipyard.iobinders.WithTiedOffDebug ++ + new chipyard.iobinders.WithSimSerial ++ + new testchipip.WithTSI ++ + new chipyard.config.WithBootROM ++ + new chipyard.config.WithUART ++ + new chipyard.config.WithL2TLBs(1024) ++ + new freechips.rocketchip.subsystem.WithNoMMIOPort ++ + new freechips.rocketchip.subsystem.WithNoSlavePort ++ + new freechips.rocketchip.subsystem.WithInclusiveCache ++ + new freechips.rocketchip.subsystem.WithNExtTopInterrupts(0) ++ + new freechips.rocketchip.subsystem.WithNBigCores(1) ++ + new freechips.rocketchip.subsystem.WithCoherentBusTopology ++ + new freechips.rocketchip.system.BaseConfig) // DOC include end: FIRRocketConfig class SmallNVDLARocketConfig extends Config( From 77a624f4883eef706ee03aba57793c7b560d0f66 Mon Sep 17 00:00:00 2001 From: Paul Rigge Date: Mon, 25 May 2020 14:19:52 -0700 Subject: [PATCH 09/19] Fix bad capitalization in doc reference --- .../chipyard/src/main/scala/example/dsptools/GenericFIR.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/generators/chipyard/src/main/scala/example/dsptools/GenericFIR.scala b/generators/chipyard/src/main/scala/example/dsptools/GenericFIR.scala index 6a18af1a..a2d2ad05 100644 --- a/generators/chipyard/src/main/scala/example/dsptools/GenericFIR.scala +++ b/generators/chipyard/src/main/scala/example/dsptools/GenericFIR.scala @@ -174,7 +174,7 @@ abstract class GenericFIRBlock[D, U, EO, EI, B<:Data, T<:Data:Ring] } // DOC include end: GenericFIRBlock chisel -// DOC include start: TLGenericFIRBLock chisel +// DOC include start: TLGenericFIRBlock chisel class TLGenericFIRBlock[T<:Data:Ring] ( val genIn: T, From c4d791d3239f1fe305bd8898b7214f9ec8472cb2 Mon Sep 17 00:00:00 2001 From: Paul Rigge Date: Mon, 25 May 2020 14:22:39 -0700 Subject: [PATCH 10/19] Update reference to DigitalTop in doc --- docs/Customization/Dsptools-Blocks.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/Customization/Dsptools-Blocks.rst b/docs/Customization/Dsptools-Blocks.rst index 1f7dcfde..edad9ae0 100644 --- a/docs/Customization/Dsptools-Blocks.rst +++ b/docs/Customization/Dsptools-Blocks.rst @@ -83,8 +83,8 @@ Once again following the path of the previous MMIO example, we now want to mix o .. literalinclude:: ../../generators/chipyard/src/main/scala/DigitalTop.scala :language: scala - :start-after: DOC include start: Top - :end-before: DOC include end: Top + :start-after: DOC include start: DigitalTop + :end-before: DOC include end: DigitalTop Finally, we create the configuration class in ``generators/chipyard/src/main/scala/config/RocketConfigs.scala`` that uses the ``WithFIR`` mixin defined in ``generators/chipyard/src/main/scala/example/dsptools/GenericFIR.scala``. From 1157c6f93dae747335c6a59cc0d0088c1a302738 Mon Sep 17 00:00:00 2001 From: Paul Rigge Date: Mon, 25 May 2020 14:26:17 -0700 Subject: [PATCH 11/19] Resolve duplicate label --- docs/Customization/Dsptools-Blocks.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/Customization/Dsptools-Blocks.rst b/docs/Customization/Dsptools-Blocks.rst index edad9ae0..28f4ced1 100644 --- a/docs/Customization/Dsptools-Blocks.rst +++ b/docs/Customization/Dsptools-Blocks.rst @@ -47,7 +47,7 @@ The first step in attaching the FIR filter as a MMIO peripheral is to create an :start-after: DOC include start: GenericFIRBlock chisel :end-before: DOC include end: GenericFIRBlock chisel -Connecting by TileLink +Connecting DspBlock by TileLink ---------------------- With these classes implemented, you can begin to construct the chain by extending ``GenericFIRBlock`` while using the ``TLDspBlock`` trait via mixin. From 3d4a6d4ea672e2d6495117b27fbc95bb70f4c69a Mon Sep 17 00:00:00 2001 From: Paul Rigge Date: Mon, 25 May 2020 14:28:32 -0700 Subject: [PATCH 12/19] Ugh restructured text sucks --- docs/Customization/Dsptools-Blocks.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/Customization/Dsptools-Blocks.rst b/docs/Customization/Dsptools-Blocks.rst index 28f4ced1..f8808cd3 100644 --- a/docs/Customization/Dsptools-Blocks.rst +++ b/docs/Customization/Dsptools-Blocks.rst @@ -48,7 +48,7 @@ The first step in attaching the FIR filter as a MMIO peripheral is to create an :end-before: DOC include end: GenericFIRBlock chisel Connecting DspBlock by TileLink ----------------------- +------------------------------- With these classes implemented, you can begin to construct the chain by extending ``GenericFIRBlock`` while using the ``TLDspBlock`` trait via mixin. .. literalinclude:: ../../generators/chipyard/src/main/scala/example/dsptools/GenericFIR.scala From 541d037c5d6931640ff868a1f9255c081d0c9497 Mon Sep 17 00:00:00 2001 From: Paul Rigge Date: Mon, 25 May 2020 14:33:00 -0700 Subject: [PATCH 13/19] Remove another duplicate label --- docs/Customization/Dsptools-Blocks.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/Customization/Dsptools-Blocks.rst b/docs/Customization/Dsptools-Blocks.rst index f8808cd3..ebebd12b 100644 --- a/docs/Customization/Dsptools-Blocks.rst +++ b/docs/Customization/Dsptools-Blocks.rst @@ -98,8 +98,8 @@ Finally, we create the configuration class in ``generators/chipyard/src/main/sca :start-after: DOC include start: FIRRocketConfig :end-before: DOC include end: FIRRocketConfig -Testing -------- +FIR Testing +----------- We can now test that the FIR is working. The test program is found in ``tests/fir.c``. From 7a0d8ea772f6e3e4f1a6fd246f901c6f569f73b7 Mon Sep 17 00:00:00 2001 From: Paul Rigge Date: Mon, 25 May 2020 17:19:48 -0700 Subject: [PATCH 14/19] Update generators/chipyard/src/main/scala/DigitalTop.scala Co-authored-by: alonamid --- generators/chipyard/src/main/scala/DigitalTop.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/generators/chipyard/src/main/scala/DigitalTop.scala b/generators/chipyard/src/main/scala/DigitalTop.scala index 0866676b..0824b6a9 100644 --- a/generators/chipyard/src/main/scala/DigitalTop.scala +++ b/generators/chipyard/src/main/scala/DigitalTop.scala @@ -24,7 +24,7 @@ class DigitalTop(implicit p: Parameters) extends System with chipyard.example.CanHavePeripheryInitZero // Enables optionally adding the initzero example widget with chipyard.example.CanHavePeripheryGCD // Enables optionally adding the GCD example widget with chipyard.example.CanHavePeripheryFIR // Enables optionally adding the FIR example widget - with chipyard.example.CanHavePeripheryStreamingPassthrough // Enables optionally adding the passthrough example widget + with chipyard.example.CanHavePeripheryStreamingPassthrough // Enables optionally adding the DSPTools streaming-passthrough example widget with nvidia.blocks.dla.CanHavePeripheryNVDLA // Enables optionally having an NVDLA { override lazy val module = new DigitalTopModule(this) From a6e96b649685bd3266d47841511d0f73d1a15ee1 Mon Sep 17 00:00:00 2001 From: Paul Rigge Date: Mon, 25 May 2020 17:19:56 -0700 Subject: [PATCH 15/19] Update generators/chipyard/src/main/scala/DigitalTop.scala Co-authored-by: alonamid --- generators/chipyard/src/main/scala/DigitalTop.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/generators/chipyard/src/main/scala/DigitalTop.scala b/generators/chipyard/src/main/scala/DigitalTop.scala index 0824b6a9..7db9f7f3 100644 --- a/generators/chipyard/src/main/scala/DigitalTop.scala +++ b/generators/chipyard/src/main/scala/DigitalTop.scala @@ -23,7 +23,7 @@ class DigitalTop(implicit p: Parameters) extends System with icenet.CanHavePeripheryIceNIC // Enables optionally adding the IceNIC for FireSim with chipyard.example.CanHavePeripheryInitZero // Enables optionally adding the initzero example widget with chipyard.example.CanHavePeripheryGCD // Enables optionally adding the GCD example widget - with chipyard.example.CanHavePeripheryFIR // Enables optionally adding the FIR example widget + with chipyard.example.CanHavePeripheryFIR // Enables optionally adding the DSPTools FIR example widget with chipyard.example.CanHavePeripheryStreamingPassthrough // Enables optionally adding the DSPTools streaming-passthrough example widget with nvidia.blocks.dla.CanHavePeripheryNVDLA // Enables optionally having an NVDLA { From e6984e412b136e9a5b9fea6a088045c15a03180e Mon Sep 17 00:00:00 2001 From: Paul Rigge Date: Tue, 26 May 2020 23:00:37 +0000 Subject: [PATCH 16/19] Use Chain for dsptools example. Rename examples, bump dsptools to master, and incorporate feedback. --- .circleci/config.yml | 28 +++++----- .circleci/defaults.sh | 4 +- .circleci/run-tests.sh | 8 +-- docs/Customization/Dsptools-Blocks.rst | 26 ++++----- .../chipyard/src/main/scala/DigitalTop.scala | 2 +- .../src/main/scala/config/RocketConfigs.scala | 8 +-- .../scala/example/dsptools/DspBlocks.scala | 54 ++++++++++++------- .../scala/example/dsptools/GenericFIR.scala | 31 +++++------ .../dsptools/StreamingPassthrough.scala | 28 ++++------ scripts/tutorial-patches/build.sbt.patch | 8 +-- tests/Makefile | 2 +- tests/{fir.c => streaming-fir.c} | 0 ...{passthrough.c => streaming-passthrough.c} | 0 tools/dsptools | 2 +- 14 files changed, 104 insertions(+), 97 deletions(-) rename tests/{fir.c => streaming-fir.c} (100%) rename tests/{passthrough.c => streaming-passthrough.c} (100%) diff --git a/.circleci/config.yml b/.circleci/config.yml index 1d205ee9..ab7006e1 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -204,16 +204,16 @@ jobs: steps: - prepare-rtl: project-key: "chipyard-sha3" - prepare-chipyard-fir: + prepare-chipyard-streaming-fir: executor: main-env steps: - prepare-rtl: - project-key: "chipyard-fir" - prepare-chipyard-passthrough: + project-key: "chipyard-streaming-fir" + prepare-chipyard-streaming-passthrough: executor: main-env steps: - prepare-rtl: - project-key: "chipyard-passthrough" + project-key: "chipyard-streaming-passthrough" prepare-chipyard-hetero: executor: main-env steps: @@ -297,16 +297,16 @@ jobs: steps: - run-tests: project-key: "chipyard-sha3" - chipyard-fir-run-tests: + chipyard-streaming-fir-run-tests: executor: main-env steps: - run-tests: - project-key: "chipyard-fir" - chipyard-passthrough-run-tests: + project-key: "chipyard-streaming-fir" + chipyard-streaming-passthrough-run-tests: executor: main-env steps: - run-tests: - project-key: "chipyard-passthrough" + project-key: "chipyard-streaming-passthrough" chipyard-hetero-run-tests: executor: main-env steps: @@ -459,12 +459,12 @@ workflows: - install-riscv-toolchain - install-verilator - - prepare-chipyard-fir: + - prepare-chipyard-streaming-fir: requires: - install-riscv-toolchain - install-verilator - - prepare-chipyard-passthrough: + - prepare-chipyard-streaming-passthrough: requires: - install-riscv-toolchain - install-verilator @@ -555,13 +555,13 @@ workflows: requires: - prepare-chipyard-sha3 - - chipyard-fir-run-tests: + - chipyard-streaming-fir-run-tests: requires: - - prepare-chipyard-fir + - prepare-chipyard-streaming-fir - - chipyard-passthrough-run-tests: + - chipyard-streaming-passthrough-run-tests: requires: - - prepare-chipyard-passthrough + - prepare-chipyard-streaming-passthrough - chipyard-hetero-run-tests: requires: diff --git a/.circleci/defaults.sh b/.circleci/defaults.sh index 6225ad95..dcdd1e7b 100755 --- a/.circleci/defaults.sh +++ b/.circleci/defaults.sh @@ -46,8 +46,8 @@ LOCAL_FIRESIM_DIR=$LOCAL_CHIPYARD_DIR/sims/firesim/sim declare -A mapping mapping["chipyard-rocket"]="SUB_PROJECT=chipyard" mapping["chipyard-sha3"]="SUB_PROJECT=chipyard CONFIG=Sha3RocketConfig" -mapping["chipyard-fir"]="SUB_PROJECT=chipyard CONFIG=FIRRocketConfig" -mapping["chipyard-passthrough"]="SUB_PROJECT=chipyard CONFIG=StreamingPassthroughRocketConfig" +mapping["chipyard-streaming-fir"]="SUB_PROJECT=chipyard CONFIG=StreamingFIRRocketConfig" +mapping["chipyard-streaming-passthrough"]="SUB_PROJECT=chipyard CONFIG=StreamingPassthroughRocketConfig" mapping["chipyard-hetero"]="SUB_PROJECT=chipyard CONFIG=LargeBoomAndRocketConfig" mapping["chipyard-boom"]="SUB_PROJECT=chipyard CONFIG=SmallBoomConfig" mapping["chipyard-blkdev"]="SUB_PROJECT=chipyard CONFIG=SimBlockDeviceRocketConfig" diff --git a/.circleci/run-tests.sh b/.circleci/run-tests.sh index f319880b..1dc67b16 100755 --- a/.circleci/run-tests.sh +++ b/.circleci/run-tests.sh @@ -62,13 +62,13 @@ case $1 in (cd $LOCAL_CHIPYARD_DIR/generators/sha3/software && ./build.sh) $LOCAL_SIM_DIR/simulator-chipyard-Sha3RocketConfig $LOCAL_CHIPYARD_DIR/generators/sha3/software/benchmarks/bare/sha3-rocc.riscv ;; - chipyard-passthrough) + chipyard-streaming-passthrough) make -C $LOCAL_CHIPYARD_DIR/tests - $LOCAL_SIM_DIR/simulator-chipyard-StreamingPassthroughRocketConfig $LOCAL_CHIPYARD_DIR/tests/passthrough.riscv + $LOCAL_SIM_DIR/simulator-chipyard-StreamingPassthroughRocketConfig $LOCAL_CHIPYARD_DIR/tests/streaming-passthrough.riscv ;; - chipyard-fir) + chipyard-streaming-fir) make -C $LOCAL_CHIPYARD_DIR/tests - $LOCAL_SIM_DIR/simulator-chipyard-FIRRocketConfig $LOCAL_CHIPYARD_DIR/tests/fir.riscv + $LOCAL_SIM_DIR/simulator-chipyard-StreamingFIRRocketConfig $LOCAL_CHIPYARD_DIR/tests/streaming-fir.riscv ;; chipyard-spiflashread) make -C $LOCAL_CHIPYARD_DIR/tests diff --git a/docs/Customization/Dsptools-Blocks.rst b/docs/Customization/Dsptools-Blocks.rst index ebebd12b..8f8d2d2d 100644 --- a/docs/Customization/Dsptools-Blocks.rst +++ b/docs/Customization/Dsptools-Blocks.rst @@ -15,8 +15,8 @@ A ``DspBlock`` is the basic unit of signal processing functionality that can be It has a AXI4-stream interface and an optional memory interface. The idea is that these ``DspBlocks`` can be easily designed, unit tested, and assembled lego-style to build complex functionality. A ``DspChain`` is one example of how to assemble ``DspBlocks``, in which case the streaming interfaces are connected serially into a pipeline, and a bus is instatiated and connected to every block with a memory interface. - -This project has example designs that integrate a ``DspBlock`` to a rocketchip-based SoC as an MMIO peripheral. The custom ``DspBlock`` has a ``ReadQueue`` before it and a ``WriteQueue`` after it, which allow memory mapped access to the streaming interfaces so the rocket core can interact with the ``DspBlock``. This section will primarily focus on designing Tilelink-based peripherals. However, through the resources provided in Dsptools, one could also define an AXI4-based peripheral by following similar steps. Furthermore, the examples here are simple, but can be extended to implement more complex accelerators, for example an `OFDM baseband `_ or a `spectrometer `_. + +Chipyard has example designs that integrate a ``DspBlock`` to a rocketchip-based SoC as an MMIO peripheral. The custom ``DspBlock`` has a ``ReadQueue`` before it and a ``WriteQueue`` after it, which allow memory mapped access to the streaming interfaces so the rocket core can interact with the ``DspBlock``. This section will primarily focus on designing Tilelink-based peripherals. However, through the resources provided in Dsptools, one could also define an AXI4-based peripheral by following similar steps. Furthermore, the examples here are simple, but can be extended to implement more complex accelerators, for example an `OFDM baseband `_ or a `spectrometer `_. For this example, we will show you how to connect a simple FIR filter created using Dsptools as an MMIO peripheral. The full code can be found in ``generators/chipyard/src/main/scala/example/dsptools/GenericFIR.scala``. That being said, one could substitute any module with a ready valid interface in the place of the FIR and achieve the same results. As long as the read and valid signals of the module are attached to those of a corresponding ``DSPBlock`` wrapper, and that wrapper is placed in a chain with a ``ReadQueue`` and a ``WriteQueue``, following the general outline establised by these steps will allow you to interact with that block as a memory mapped IO. @@ -49,7 +49,7 @@ The first step in attaching the FIR filter as a MMIO peripheral is to create an Connecting DspBlock by TileLink ------------------------------- -With these classes implemented, you can begin to construct the chain by extending ``GenericFIRBlock`` while using the ``TLDspBlock`` trait via mixin. +With these classes implemented, you can begin to construct the chain by extending ``GenericFIRBlock`` while using the ``TLDspBlock`` trait via mixin. .. literalinclude:: ../../generators/chipyard/src/main/scala/example/dsptools/GenericFIR.scala :language: scala @@ -69,8 +69,8 @@ As in the previous MMIO example, we use a cake pattern to hook up our module to .. literalinclude:: ../../generators/chipyard/src/main/scala/example/dsptools/GenericFIR.scala :language: scala - :start-after: DOC include start: CanHavePeripheryFIR chisel - :end-before: DOC include end: CanHavePeripheryFIR chisel + :start-after: DOC include start: CanHavePeripheryStreamingFIR chisel + :end-before: DOC include end: CanHavePeripheryStreamingFIR chisel Note that this is the point at which we decide the datatype for our FIR. @@ -90,29 +90,29 @@ Finally, we create the configuration class in ``generators/chipyard/src/main/sca .. literalinclude:: ../../generators/chipyard/src/main/scala/example/dsptools/GenericFIR.scala :language: scala - :start-after: DOC include start: WithFIR - :end-before: DOC include end: WithFIR + :start-after: DOC include start: WithStreamingFIR + :end-before: DOC include end: WithStreamingFIR .. literalinclude:: ../../generators/chipyard/src/main/scala/config/RocketConfigs.scala :language: scala - :start-after: DOC include start: FIRRocketConfig - :end-before: DOC include end: FIRRocketConfig + :start-after: DOC include start: StreamingFIRRocketConfig + :end-before: DOC include end: StreamingFIRRocketConfig FIR Testing ----------- -We can now test that the FIR is working. The test program is found in ``tests/fir.c``. +We can now test that the FIR is working. The test program is found in ``tests/streaming-fir.c``. -.. literalinclude:: ../../tests/fir.c +.. literalinclude:: ../../tests/streaming-fir.c :language: c The test feed a series of values into the fir and compares the output to a golden model of computation. The base of the module's MMIO write region is at 0x2000 and the base of the read region is at 0x2100 by default. -Compiling this program with ``make`` produces a ``fir.riscv`` executable. +Compiling this program with ``make`` produces a ``streaming-fir.riscv`` executable. Now we can run our simulation. .. code-block:: shell cd sims/verilator - make CONFIG=GCDTLRocketConfig BINARY=../../tests/fir.riscv run-binary + make CONFIG=StreamingFIRRocketConfig BINARY=../../tests/streaming-fir.riscv run-binary diff --git a/generators/chipyard/src/main/scala/DigitalTop.scala b/generators/chipyard/src/main/scala/DigitalTop.scala index 7db9f7f3..ae363539 100644 --- a/generators/chipyard/src/main/scala/DigitalTop.scala +++ b/generators/chipyard/src/main/scala/DigitalTop.scala @@ -23,7 +23,7 @@ class DigitalTop(implicit p: Parameters) extends System with icenet.CanHavePeripheryIceNIC // Enables optionally adding the IceNIC for FireSim with chipyard.example.CanHavePeripheryInitZero // Enables optionally adding the initzero example widget with chipyard.example.CanHavePeripheryGCD // Enables optionally adding the GCD example widget - with chipyard.example.CanHavePeripheryFIR // Enables optionally adding the DSPTools FIR example widget + with chipyard.example.CanHavePeripheryStreamingFIR // Enables optionally adding the DSPTools FIR example widget with chipyard.example.CanHavePeripheryStreamingPassthrough // Enables optionally adding the DSPTools streaming-passthrough example widget with nvidia.blocks.dla.CanHavePeripheryNVDLA // Enables optionally having an NVDLA { diff --git a/generators/chipyard/src/main/scala/config/RocketConfigs.scala b/generators/chipyard/src/main/scala/config/RocketConfigs.scala index 76ed912f..05559662 100644 --- a/generators/chipyard/src/main/scala/config/RocketConfigs.scala +++ b/generators/chipyard/src/main/scala/config/RocketConfigs.scala @@ -427,7 +427,7 @@ class RingSystemBusRocketConfig extends Config( // DOC include end: RingSystemBusRocket class StreamingPassthroughRocketConfig extends Config( - new chipyard.example.WithStreamingPassthrough ++ // use top with tilelink-controlled passthrough + new chipyard.example.WithStreamingPassthrough ++ // use top with tilelink-controlled streaming passthrough new chipyard.iobinders.WithUARTAdapter ++ new chipyard.iobinders.WithTieOffInterrupts ++ new chipyard.iobinders.WithBlackBoxSimMem ++ @@ -445,9 +445,9 @@ class StreamingPassthroughRocketConfig extends Config( new freechips.rocketchip.subsystem.WithCoherentBusTopology ++ new freechips.rocketchip.system.BaseConfig) -// DOC include start: FIRRocketConfig -class FIRRocketConfig extends Config ( - new chipyard.example.WithFIR ++ // use top with tilelink-controlled FIR +// DOC include start: StreamingFIRRocketConfig +class StreamingFIRRocketConfig extends Config ( + new chipyard.example.WithStreamingFIR ++ // use top with tilelink-controlled streaming FIR new chipyard.iobinders.WithUARTAdapter ++ new chipyard.iobinders.WithTieOffInterrupts ++ new chipyard.iobinders.WithBlackBoxSimMem ++ diff --git a/generators/chipyard/src/main/scala/example/dsptools/DspBlocks.scala b/generators/chipyard/src/main/scala/example/dsptools/DspBlocks.scala index ad4c585e..b51e2223 100644 --- a/generators/chipyard/src/main/scala/example/dsptools/DspBlocks.scala +++ b/generators/chipyard/src/main/scala/example/dsptools/DspBlocks.scala @@ -18,11 +18,11 @@ import freechips.rocketchip.subsystem._ * @param streamParameters parameters for the stream node * @param p */ -abstract class WriteQueue +abstract class WriteQueue[D, U, E, O, B <: Data] ( - val depth: Int = 8, + val depth: Int, val streamParameters: AXI4StreamMasterParameters = AXI4StreamMasterParameters() -)(implicit p: Parameters) extends LazyModule with HasCSR { +)(implicit p: Parameters) extends DspBlock[D, U, E, O, B] with HasCSR { // stream node, output only val streamNode = AXI4StreamMasterNode(streamParameters) @@ -58,12 +58,10 @@ abstract class WriteQueue * @param beatBytes beatBytes of TL interface * @param p */ -class TLWriteQueue -( - depth: Int = 8, - csrAddress: AddressSet = AddressSet(0x2000, 0xff), - beatBytes: Int = 8, -)(implicit p: Parameters) extends WriteQueue(depth) with TLHasCSR { +class TLWriteQueue (depth: Int, csrAddress: AddressSet, beatBytes: Int) +(implicit p: Parameters) extends WriteQueue[ + TLClientPortParameters, TLManagerPortParameters, TLEdgeOut, TLEdgeIn, TLBundle +](depth) with TLHasCSR { val devname = "tlQueueIn" val devcompat = Seq("ucb-art", "dsptools") val device = new SimpleDevice(devname, devcompat) { @@ -76,6 +74,17 @@ class TLWriteQueue override val mem = Some(TLRegisterNode(address = Seq(csrAddress), device = device, beatBytes = beatBytes)) } +object TLWriteQueue { + def apply( + depth: Int = 8, + csrAddress: AddressSet = AddressSet(0x2000, 0xff), + beatBytes: Int = 8, + )(implicit p: Parameters) = { + val writeQueue = LazyModule(new TLWriteQueue(depth = depth, csrAddress = csrAddress, beatBytes = beatBytes)) + writeQueue + } +} + /** * The streaming interface adds elements into the queue. * The memory interface can read elements out of the queue. @@ -83,11 +92,11 @@ class TLWriteQueue * @param streamParameters parameters for the stream node * @param p */ -abstract class ReadQueue +abstract class ReadQueue[D, U, E, O, B <: Data] ( - val depth: Int = 8, + val depth: Int, val streamParameters: AXI4StreamSlaveParameters = AXI4StreamSlaveParameters() -)(implicit p: Parameters) extends LazyModule with HasCSR { +)(implicit p: Parameters) extends DspBlock[D, U, E, O, B] with HasCSR { val streamNode = AXI4StreamSlaveNode(streamParameters) lazy val module = new LazyModuleImp(this) { @@ -126,12 +135,10 @@ abstract class ReadQueue * @param beatBytes beatBytes of TL interface * @param p */ -class TLReadQueue -( - depth: Int = 8, - csrAddress: AddressSet = AddressSet(0x2100, 0xff), - beatBytes: Int = 8 -)(implicit p: Parameters) extends ReadQueue(depth) with TLHasCSR { +class TLReadQueue( depth: Int, csrAddress: AddressSet, beatBytes: Int) +(implicit p: Parameters) extends ReadQueue[ + TLClientPortParameters, TLManagerPortParameters, TLEdgeOut, TLEdgeIn, TLBundle +](depth) with TLHasCSR { val devname = "tlQueueOut" val devcompat = Seq("ucb-art", "dsptools") val device = new SimpleDevice(devname, devcompat) { @@ -142,5 +149,14 @@ class TLReadQueue } // make diplomatic TL node for regmap override val mem = Some(TLRegisterNode(address = Seq(csrAddress), device = device, beatBytes = beatBytes)) - +} + +object TLReadQueue { + def apply( + depth: Int = 8, + csrAddress: AddressSet = AddressSet(0x2100, 0xff), + beatBytes: Int = 8)(implicit p: Parameters) = { + val readQueue = LazyModule(new TLReadQueue(depth = depth, csrAddress = csrAddress, beatBytes = beatBytes)) + readQueue + } } diff --git a/generators/chipyard/src/main/scala/example/dsptools/GenericFIR.scala b/generators/chipyard/src/main/scala/example/dsptools/GenericFIR.scala index a2d2ad05..e0041192 100644 --- a/generators/chipyard/src/main/scala/example/dsptools/GenericFIR.scala +++ b/generators/chipyard/src/main/scala/example/dsptools/GenericFIR.scala @@ -188,30 +188,27 @@ GenericFIRBlock[TLClientPortParameters, TLManagerPortParameters, TLEdgeOut, TLEd // DOC include start: TLGenericFIRChain chisel class TLGenericFIRChain[T<:Data:Ring] (genIn: T, genOut: T, coeffs: Seq[T], params: GenericFIRParams)(implicit p: Parameters) - extends LazyModule { - val writeQueue = LazyModule(new TLWriteQueue(params.depth, AddressSet(params.writeAddress, 0xff))) - val fir = LazyModule(new TLGenericFIRBlock(genIn, genOut, coeffs)) - val readQueue = LazyModule(new TLReadQueue(params.depth, AddressSet(params.readAddress, 0xff))) - - // connect streamNodes of queues and FIR - readQueue.streamNode := fir.streamNode := writeQueue.streamNode - - lazy val module = new LazyModuleImp(this) -} + extends TLChain(Seq( + TLWriteQueue(params.depth, AddressSet(params.writeAddress, 0xff))(_), + { implicit p: Parameters => + val fir = LazyModule(new TLGenericFIRBlock(genIn, genOut, coeffs)) + fir + }, + TLReadQueue(params.depth, AddressSet(params.readAddress, 0xff))(_) + )) // DOC include end: TLGenericFIRChain chisel // DOC include start: CanHavePeripheryFIR chisel -trait CanHavePeripheryFIR extends BaseSubsystem { - val fir = p(GenericFIRKey) match { +trait CanHavePeripheryStreamingFIR extends BaseSubsystem { + val streamingFIR = p(GenericFIRKey) match { case Some(params) => { - val fir = LazyModule(new TLGenericFIRChain( + val streamingFIR = LazyModule(new TLGenericFIRChain( genIn = FixedPoint(8.W, 3.BP), genOut = FixedPoint(8.W, 3.BP), coeffs = Seq(1.F(0.BP), 2.F(0.BP), 3.F(0.BP)), params = params)) - - pbus.toVariableWidthSlave(Some("firWrite")) { fir.writeQueue.mem.get } - pbus.toVariableWidthSlave(Some("firRead")) { fir.readQueue.mem.get } + pbus.toVariableWidthSlave(Some("streamingFIR")) { streamingFIR.mem.get := TLFIFOFixer() } + Some(streamingFIR) } case None => None } @@ -222,7 +219,7 @@ trait CanHavePeripheryFIR extends BaseSubsystem { * Mixin to add FIR to rocket config */ // DOC include start: WithFIR -class WithFIR extends Config((site, here, up) => { +class WithStreamingFIR extends Config((site, here, up) => { case GenericFIRKey => Some(GenericFIRParams(depth = 8)) }) // DOC include end: WithFIR diff --git a/generators/chipyard/src/main/scala/example/dsptools/StreamingPassthrough.scala b/generators/chipyard/src/main/scala/example/dsptools/StreamingPassthrough.scala index 87f97760..923f12e2 100644 --- a/generators/chipyard/src/main/scala/example/dsptools/StreamingPassthrough.scala +++ b/generators/chipyard/src/main/scala/example/dsptools/StreamingPassthrough.scala @@ -121,27 +121,21 @@ with TLDspBlock * @tparam T Type parameter for passthrough, i.e. FixedPoint or DspReal */ class TLStreamingPassthroughChain[T<:Data:Ring](params: StreamingPassthroughParams, proto: T)(implicit p: Parameters) - extends LazyModule { - // instantiate lazy modules - val writeQueue = LazyModule(new TLWriteQueue(params.depth, AddressSet(params.writeAddress, 0xff))) - val passthrough = LazyModule(new TLStreamingPassthroughBlock(proto)) - val readQueue = LazyModule(new TLReadQueue(params.depth, AddressSet(params.readAddress, 0xff))) - - // connect streamNodes of queues and passthrough - readQueue.streamNode := passthrough.streamNode := writeQueue.streamNode - - lazy val module = new LazyModuleImp(this) -} + extends TLChain(Seq( + TLWriteQueue(params.depth, AddressSet(params.writeAddress, 0xff))(_), + { implicit p: Parameters => { + val streamingPassthrough = LazyModule(new TLStreamingPassthroughBlock(proto)) + streamingPassthrough + }}, + TLReadQueue(params.depth, AddressSet(params.readAddress, 0xff))(_) + )) trait CanHavePeripheryStreamingPassthrough { this: BaseSubsystem => val passthrough = p(StreamingPassthroughKey) match { case Some(params) => { - val passthrough = LazyModule(new TLStreamingPassthroughChain(params, UInt(32.W))) - - pbus.toVariableWidthSlave(Some("passthroughWrite")) { passthrough.writeQueue.mem.get } - pbus.toVariableWidthSlave(Some("passthroughRead")) { passthrough.readQueue.mem.get } - - Some(passthrough) + val streamingPassthroughChain = LazyModule(new TLStreamingPassthroughChain(params, UInt(32.W))) + pbus.toVariableWidthSlave(Some("streamingPassthrough")) { streamingPassthroughChain.mem.get := TLFIFOFixer() } + Some(streamingPassthroughChain) } case None => None } diff --git a/scripts/tutorial-patches/build.sbt.patch b/scripts/tutorial-patches/build.sbt.patch index 40e773fe..6c1e3007 100644 --- a/scripts/tutorial-patches/build.sbt.patch +++ b/scripts/tutorial-patches/build.sbt.patch @@ -1,17 +1,17 @@ diff --git a/build.sbt b/build.sbt -index 0c4581f..ff0597c 100644 +index 5d642c1..56f6fda 100644 --- a/build.sbt +++ b/build.sbt -@@ -128,7 +128,7 @@ lazy val iocell = (project in file("./tools/barstools/iocell/")) +@@ -130,7 +130,7 @@ lazy val iocell = (project in file("./tools/barstools/iocell/")) lazy val chipyard = conditionalDependsOn(project in file("generators/chipyard")) .dependsOn(boom, hwacha, sifive_blocks, sifive_cache, utilities, iocell, - sha3, // On separate line to allow for cleaner tutorial-setup patches +// sha3, // On separate line to allow for cleaner tutorial-setup patches + dsptools, `rocket-dsptools`, gemmini, icenet, tracegen, ariane, nvdla) .settings(commonSettings) - -@@ -155,9 +155,9 @@ lazy val ariane = (project in file("generators/ariane")) +@@ -158,9 +158,9 @@ lazy val ariane = (project in file("generators/ariane")) .dependsOn(rocketchip) .settings(commonSettings) diff --git a/tests/Makefile b/tests/Makefile index 299b7c02..ca8268ea 100644 --- a/tests/Makefile +++ b/tests/Makefile @@ -6,7 +6,7 @@ LDFLAGS= -static include libgloss.mk PROGRAMS = pwm blkdev accum charcount nic-loopback big-blkdev pingd \ - passthrough fir nvdla spiflashread spiflashwrite + streaming-passthrough streaming-fir nvdla spiflashread spiflashwrite spiflash.img: spiflash.py python3 $< diff --git a/tests/fir.c b/tests/streaming-fir.c similarity index 100% rename from tests/fir.c rename to tests/streaming-fir.c diff --git a/tests/passthrough.c b/tests/streaming-passthrough.c similarity index 100% rename from tests/passthrough.c rename to tests/streaming-passthrough.c diff --git a/tools/dsptools b/tools/dsptools index 7c408297..211166e6 160000 --- a/tools/dsptools +++ b/tools/dsptools @@ -1 +1 @@ -Subproject commit 7c40829718592fc99b011d53595f01b00a3d68cf +Subproject commit 211166e635861fb1937828aee38c166baf0840b5 From 312700e9da1e0780f1b157a5bd14477beba5ac6d Mon Sep 17 00:00:00 2001 From: Paul Rigge Date: Tue, 26 May 2020 18:24:46 -0700 Subject: [PATCH 17/19] Fix some doc references --- .../src/main/scala/example/dsptools/GenericFIR.scala | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/generators/chipyard/src/main/scala/example/dsptools/GenericFIR.scala b/generators/chipyard/src/main/scala/example/dsptools/GenericFIR.scala index e0041192..ed16b25d 100644 --- a/generators/chipyard/src/main/scala/example/dsptools/GenericFIR.scala +++ b/generators/chipyard/src/main/scala/example/dsptools/GenericFIR.scala @@ -198,7 +198,7 @@ class TLGenericFIRChain[T<:Data:Ring] (genIn: T, genOut: T, coeffs: Seq[T], para )) // DOC include end: TLGenericFIRChain chisel -// DOC include start: CanHavePeripheryFIR chisel +// DOC include start: CanHavePeripheryStreamingFIR chisel trait CanHavePeripheryStreamingFIR extends BaseSubsystem { val streamingFIR = p(GenericFIRKey) match { case Some(params) => { @@ -213,13 +213,13 @@ trait CanHavePeripheryStreamingFIR extends BaseSubsystem { case None => None } } -// DOC include end: CanHavePeripheryFIR chisel +// DOC include end: CanHavePeripheryStreamingFIR chisel /** * Mixin to add FIR to rocket config */ -// DOC include start: WithFIR +// DOC include start: WithStreamingFIR class WithStreamingFIR extends Config((site, here, up) => { case GenericFIRKey => Some(GenericFIRParams(depth = 8)) }) -// DOC include end: WithFIR +// DOC include end: WithStreamingFIR From cdcaf5c57471f59f5e430e65209038fb2aafe7ee Mon Sep 17 00:00:00 2001 From: Paul Rigge Date: Tue, 26 May 2020 22:29:50 -0700 Subject: [PATCH 18/19] Missed an include end --- generators/chipyard/src/main/scala/config/RocketConfigs.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/generators/chipyard/src/main/scala/config/RocketConfigs.scala b/generators/chipyard/src/main/scala/config/RocketConfigs.scala index 05559662..b1001c59 100644 --- a/generators/chipyard/src/main/scala/config/RocketConfigs.scala +++ b/generators/chipyard/src/main/scala/config/RocketConfigs.scala @@ -464,7 +464,7 @@ class StreamingFIRRocketConfig extends Config ( new freechips.rocketchip.subsystem.WithNBigCores(1) ++ new freechips.rocketchip.subsystem.WithCoherentBusTopology ++ new freechips.rocketchip.system.BaseConfig) -// DOC include end: FIRRocketConfig +// DOC include end: StreamingFIRRocketConfig class SmallNVDLARocketConfig extends Config( new chipyard.iobinders.WithUARTAdapter ++ From 4e696b6a0a16bd50863a6205e17f6d656017ca9c Mon Sep 17 00:00:00 2001 From: Paul Rigge Date: Wed, 27 May 2020 11:21:39 -0700 Subject: [PATCH 19/19] Add block diagram, clean up docs more --- docs/Customization/Dsptools-Blocks.rst | 29 ++++++++++++++--------- docs/_static/images/fir-block-diagram.svg | 1 + 2 files changed, 19 insertions(+), 11 deletions(-) create mode 100644 docs/_static/images/fir-block-diagram.svg diff --git a/docs/Customization/Dsptools-Blocks.rst b/docs/Customization/Dsptools-Blocks.rst index 8f8d2d2d..bdf68fc1 100644 --- a/docs/Customization/Dsptools-Blocks.rst +++ b/docs/Customization/Dsptools-Blocks.rst @@ -6,7 +6,7 @@ Dsptools is a Chisel library that aids in writing custom signal processing accel * Structures for packaging DSP blocks and integrating them into a rocketchip-based SoC. * Test harnesses for testing DSP circuits, as well as VIP-style drivers and monitors for DSP blocks. -The `Dsptools `_ repository has more documentation. +The `Dsptools repository `_ has more documentation. Dsptools Blocks @@ -16,11 +16,16 @@ It has a AXI4-stream interface and an optional memory interface. The idea is that these ``DspBlocks`` can be easily designed, unit tested, and assembled lego-style to build complex functionality. A ``DspChain`` is one example of how to assemble ``DspBlocks``, in which case the streaming interfaces are connected serially into a pipeline, and a bus is instatiated and connected to every block with a memory interface. -Chipyard has example designs that integrate a ``DspBlock`` to a rocketchip-based SoC as an MMIO peripheral. The custom ``DspBlock`` has a ``ReadQueue`` before it and a ``WriteQueue`` after it, which allow memory mapped access to the streaming interfaces so the rocket core can interact with the ``DspBlock``. This section will primarily focus on designing Tilelink-based peripherals. However, through the resources provided in Dsptools, one could also define an AXI4-based peripheral by following similar steps. Furthermore, the examples here are simple, but can be extended to implement more complex accelerators, for example an `OFDM baseband `_ or a `spectrometer `_. +Chipyard has example designs that integrate a ``DspBlock`` to a rocketchip-based SoC as an MMIO peripheral. The custom ``DspBlock`` has a ``ReadQueue`` before it and a ``WriteQueue`` after it, which allow memory mapped access to the streaming interfaces so the rocket core can interact with the ``DspBlock`` [#]_. This section will primarily focus on designing Tilelink-based peripherals. However, through the resources provided in Dsptools, one could also define an AXI4-based peripheral by following similar steps. Furthermore, the examples here are simple, but can be extended to implement more complex accelerators, for example an `OFDM baseband `_ or a `spectrometer `_. -For this example, we will show you how to connect a simple FIR filter created using Dsptools as an MMIO peripheral. The full code can be found in ``generators/chipyard/src/main/scala/example/dsptools/GenericFIR.scala``. That being said, one could substitute any module with a ready valid interface in the place of the FIR and achieve the same results. As long as the read and valid signals of the module are attached to those of a corresponding ``DSPBlock`` wrapper, and that wrapper is placed in a chain with a ``ReadQueue`` and a ``WriteQueue``, following the general outline establised by these steps will allow you to interact with that block as a memory mapped IO. +.. figure:: ../_static/images/fir-block-diagram.svg + :align: center + :alt: Block diagram showing how FIR is integrated with rocket. + :width: 400px -The module ``GenericFIR`` is the overall wrapper of our FIR module. This module links together a variable number of ``GenericFIRDirectCell`` submodules, each of which performs the computations for one coefficient in a FIR direct form architecture. It is important to note that both modules are type generic, which means that they can be instantiated for any datatype that implements ``Ring`` operations per the specifications on ``T``. +For this example, we will show you how to connect a simple FIR filter created using Dsptools as an MMIO peripheral as shown in the figure above. The full code can be found in ``generators/chipyard/src/main/scala/example/dsptools/GenericFIR.scala``. That being said, one could substitute any module with a ready valid interface in the place of the FIR and achieve the same results. As long as the read and valid signals of the module are attached to those of a corresponding ``DSPBlock`` wrapper, and that wrapper is placed in a chain with a ``ReadQueue`` and a ``WriteQueue``, following the general outline establised by these steps will allow you to interact with that block as a memory mapped IO. + +The module ``GenericFIR`` is the overall wrapper of our FIR module. This module links together a variable number of ``GenericFIRDirectCell`` submodules, each of which performs the computations for one coefficient in a FIR direct form architecture. It is important to note that both modules are type-generic, which means that they can be instantiated for any datatype ``T`` that implements ``Ring`` operations (e.g. addition, multiplication, identities). .. literalinclude:: ../../generators/chipyard/src/main/scala/example/dsptools/GenericFIR.scala :language: scala @@ -32,10 +37,10 @@ The module ``GenericFIR`` is the overall wrapper of our FIR module. This module :start-after: DOC include start: GenericFIRDirectCell chisel :end-before: DOC include end: GenericFIRDirectCell chisel -Creating a DspBlock Extension ------------------------------ +Creating a DspBlock +------------------- -The first step in attaching the FIR filter as a MMIO peripheral is to create an abstract extension of ``DspBlock`` the wraps around the ``GenericFIR`` module. The main steps of this process are as follows. +The first step in attaching the FIR filter as a MMIO peripheral is to create an abstract subclass of ``DspBlock`` the wraps around the ``GenericFIR`` module. Streaming outputs and inputs are packed and unpacked into ``UInt`` s. If there were control signals, this is where they'd go from raw IOs to memory mapped. The main steps of this process are as follows. 1. Instantiate a ``GenericFIR`` within ``GenericFIRBlock``. 2. Attach the ready and valid signals from the in and out connections. @@ -47,6 +52,8 @@ The first step in attaching the FIR filter as a MMIO peripheral is to create an :start-after: DOC include start: GenericFIRBlock chisel :end-before: DOC include end: GenericFIRBlock chisel +Note that at this point the ``GenericFIRBlock`` does not have a type of memory interface specified. This abstract class can be used to create different flavors that use AXI-4, TileLink, AHB, or whatever other memory interface you like like. + Connecting DspBlock by TileLink ------------------------------- With these classes implemented, you can begin to construct the chain by extending ``GenericFIRBlock`` while using the ``TLDspBlock`` trait via mixin. @@ -56,7 +63,7 @@ With these classes implemented, you can begin to construct the chain by extendin :start-after: DOC include start: TLGenericFIRBlock chisel :end-before: DOC include end: TLGenericFIRBlock chisel -We can then construct the final chain by utilizing the ``TLWriteQueue`` and ``TLReadeQueue`` modules found in ``generators/chipyard/src/main/scala/example/dsptools/DspBlocks.scala``. Inside our chain, we construct an instance of each queue as well as our ``TLGenericFIRBlock``. We then take the ``steamnode`` from each module and wire them all together to link the chain. +We can then construct the final chain by utilizing the ``TLWriteQueue`` and ``TLReadeQueue`` modules found in ``generators/chipyard/src/main/scala/example/dsptools/DspBlocks.scala``. The chain is created by passing a list of factory functions to the constructor of ``TLChain``. The constructor then automatically instantiates these ``DspBlocks``, connects their stream nodes in order, creates a bus, and connects any ``DspBlocks`` that have memory interfaces to the bus. .. literalinclude:: ../../generators/chipyard/src/main/scala/example/dsptools/GenericFIR.scala :language: scala @@ -72,9 +79,7 @@ As in the previous MMIO example, we use a cake pattern to hook up our module to :start-after: DOC include start: CanHavePeripheryStreamingFIR chisel :end-before: DOC include end: CanHavePeripheryStreamingFIR chisel -Note that this is the point at which we decide the datatype for our FIR. - -Our module does not need to be connected to concrete IOs or wires, so we do not need to create a concrete trait. +Note that this is the point at which we decide the datatype for our FIR. You could create different configs that use different types for the FIR, for example a config that instantiates a complex-valued FIR filter. Constructing the Top and Config ------------------------------- @@ -116,3 +121,5 @@ Now we can run our simulation. cd sims/verilator make CONFIG=StreamingFIRRocketConfig BINARY=../../tests/streaming-fir.riscv run-binary + +.. [#] ``ReadQueue`` and ``WriteQueue`` are good illustrations of how to write a ``DspBlock`` and how they can be integrated into rocket, but in a real design a DMA engine would be preferred. ``ReadQueue`` will stall the processor if you try to read an empty queue, and ``WriteQueue`` will stall if you try to write to a full queue, which a DMA engine can more elegantly avoid. Furthermore, a DMA engine can do the work of moving data, freeing the processor to do other useful work (or sleep). diff --git a/docs/_static/images/fir-block-diagram.svg b/docs/_static/images/fir-block-diagram.svg new file mode 100644 index 00000000..c56379e5 --- /dev/null +++ b/docs/_static/images/fir-block-diagram.svg @@ -0,0 +1 @@ + \ No newline at end of file