From ae1aa31fce04db79789bfe8a9a61071ce588e436 Mon Sep 17 00:00:00 2001 From: Paul Rigge Date: Mon, 25 May 2020 20:23:19 +0000 Subject: [PATCH] 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)) })