Skip to content

Commit

Permalink
Merge branch 'master' into testers2-first-step
Browse files Browse the repository at this point in the history
  • Loading branch information
chick committed May 14, 2019
2 parents 6b6bf3f + d529d7d commit dc83974
Show file tree
Hide file tree
Showing 2 changed files with 104 additions and 141 deletions.
238 changes: 97 additions & 141 deletions 2.5_exercise.ipynb
Expand Up @@ -213,32 +213,6 @@
"</pre></article></div></section></div>"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"---\n",
"# IPXact Example\n",
"\n",
"Your Chisel generator produces Verilog, but just handing someone else a piece of Verilog is insufficient to verify and integrate the design. What other information is needed to capture a design’s intent? IPXact solves this by providing an XML description of a design and its metadata, including the interfaces, parameters, address mapping, etc. So for this portion of the lab, we'll standardize the FIR interfaces and write out IPXact to ease implementation and verification. You'll need the results of this generator in later labs.\n",
"\n",
"## Setup\n",
"We have compiled the necessary depedencies, which are not all published on Maven, into a jar file. Contact the contributors if you want a copy. If you are not at the bootcamp, you can safely skip this section."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"// depdencies for ipxact\n",
"val path = System.getProperty(\"user.dir\") + \"/../rocket-dsp-utils-assembly-1.0.jar\"\n",
"interp.load.cp(ammonite.ops.Path(java.nio.file.FileSystems.getDefault().getPath(path)))"
]
},
{
"cell_type": "markdown",
"metadata": {},
Expand Down Expand Up @@ -296,17 +270,23 @@
"# DspBlock\n",
"\n",
"Integrating DSP components into a larger system can be challenging and error prone.\n",
"The [rocket-dsp-utils](https://github.com/ucb-art/rocket-dsp-utils) repository consists of useful generators that should help with such tasks.\n",
"The [rocket section of the dsptools repository](https://github.com/ucb-bar/dsptools/tree/master/rocket) consists of useful generators that should help with such tasks.\n",
"\n",
"One of the core abstractions is the notion of a DSPBlock.\n",
"A DSPBlock has:\n",
"One of the core abstractions is the notion of a `DspBlock`.\n",
"A `DspBlock` has:\n",
"* AXI-4 Stream input and output\n",
"* AXI-4 memory-mapped status and control\n",
"\n",
"* Memory-mapped status and control (in this example, AXI4)\n",
"\n",
"<img src=\"images/fir_filter.png\" style=\"width:800px;\"/>\n",
"\n",
"The following code wraps the FIR filter in AXI4 interfaces."
"`DspBlock`s use diplomatic interfaces from rocket.\n",
"[This site](https://www.lowrisc.org/docs/diplomacy/) has a good overview of the basic of diplomacy, but don't worry too much about how it's working for this example.\n",
"Diplomacy really shines when you're connecting a lot of different blocks together to form a complex SoC.\n",
"In this example, we're just making a single peripheral.\n",
"The `StandaloneBlock` traits are mixed in to make diplomatic interfaces work as top-level IOs.\n",
"You only need them when the `DspBlock` is being used as a top level interface without any diplomatic connections.\n",
"\n",
"The following code wraps the FIR filter in AXI4 interfaces.\n"
]
},
{
Expand All @@ -322,159 +302,136 @@
"import freechips.rocketchip.amba.axi4stream._\n",
"import freechips.rocketchip.config._\n",
"import freechips.rocketchip.diplomacy._\n",
"\n",
"case object NumFilters extends CSRField {\n",
" val name = \"firQueueDepth\"\n",
"}\n",
"\n",
"case class FIRTap(filterIdx: Int, tapIdx: Int) extends CSRField {\n",
" val name = s\"firTap${filterIdx}_$tapIdx\"\n",
"}\n",
"\n",
"class FIRBlock(nFilters: Int, nTaps: Int)(implicit p: Parameters) extends\n",
"AXI4DspBlock with AXI4HasCSR with HasIPXactParameters {\n",
" outer =>\n",
" addStatus(NumFilters)\n",
" \n",
" for (i <- 0 until nFilters) {\n",
" for (j <- 0 until nTaps) {\n",
" addControl(FIRTap(i, j))\n",
" }\n",
" }\n",
" makeCSRs()\n",
" \n",
"import freechips.rocketchip.regmapper._\n",
"\n",
"//\n",
"// Base class for all FIRBlocks.\n",
"// This can be extended to make TileLink, AXI4, APB, AHB, etc. flavors of the FIR filter\n",
"//\n",
"abstract class FIRBlock[D, U, EO, EI, B <: Data](val nFilters: Int, val nTaps: Int)(implicit p: Parameters)\n",
"// HasCSR means that the memory interface will be using the RegMapper API to define status and control registers\n",
"extends DspBlock[D, U, EO, EI, B] with HasCSR {\n",
" // diplomatic node for the streaming interface\n",
" // identity node means the output and input are parameterized to be the same\n",
" val streamNode = AXI4StreamIdentityNode()\n",
" \n",
" override def ipxactParameters = Map(\n",
" \"nFilters\" -> nFilters.toString,\n",
" \"nTaps\" -> nTaps.toString\n",
" )\n",
"\n",
" def beatBytes: Int = 8\n",
" def csrAddress = freechips.rocketchip.diplomacy.AddressSet(0x0, 0xffffL)\n",
" def csrBase: Int = 0\n",
" def csrSize: Int = 4096\n",
" \n",
" // define the what hardware will be elaborated\n",
" lazy val module = new LazyModuleImp(this) {\n",
" // get streaming input and output wires from diplomatic node\n",
" val (in, _) = streamNode.in(0)\n",
" val (out, _) = streamNode.out(0)\n",
" val mem = outer.mem.map { m => m.in(0) }\n",
" \n",
"\n",
" require(in.params.n >= nFilters,\n",
" s\"\"\"AXI-4 Stream port must be big enough for all \n",
" |the filters (need $nFilters,, only have ${in.params.n})\"\"\".stripMargin)\n",
"\n",
" status(NumFilters) := nFilters.U\n",
" \n",
" \n",
" val outs = (0 until nFilters).map(i => {\n",
" // make registers to store taps\n",
" val taps = Reg(Vec(nFilters, Vec(nTaps, UInt(8.W))))\n",
"\n",
" // memory map the taps, plus the first address is a read-only field that says how many filter lanes there are\n",
" val mmap = Seq(\n",
" RegField.r(64, nFilters.U, RegFieldDesc(\"nFilters\", \"Number of filter lanes\"))\n",
" ) ++ taps.flatMap(_.map(t => RegField(8, t, RegFieldDesc(\"tap\", \"Tap\"))))\n",
"\n",
" // generate the hardware for the memory interface\n",
" // in this class, regmap is abstract (unimplemented). mixing in something like AXI4HasCSR or TLHasCSR\n",
" // will define regmap for the particular memory interface\n",
" regmap(mmap.zipWithIndex.map({case (r, i) => i * 8 -> Seq(r)}): _*)\n",
"\n",
" // make the FIR lanes and connect inputs and taps\n",
" val outs = for (i <- 0 until nFilters) yield {\n",
" val fir = Module(new MyManyDynamicElementVecFir(nTaps))\n",
" \n",
" fir.io.in := in.bits.data((i+1)*8, i*8)\n",
" fir.io.valid := in.valid && out.ready\n",
" \n",
" for (j <- 0 until nTaps) {\n",
" fir.io.consts(j) := control(FIRTap(i, j))\n",
" }\n",
" \n",
" fir.io.consts := taps(i) \n",
" fir.io.out\n",
" })\n",
" \n",
" \n",
" }\n",
"\n",
" val output = if (outs.length == 1) {\n",
" outs(0)\n",
" outs.head\n",
" } else {\n",
" outs.reduce((x: UInt, y: UInt) => Cat(y, x))\n",
" }\n",
" \n",
"\n",
" out.bits.data := output\n",
" in.ready := out.ready\n",
" out.valid := in.valid\n",
" }\n",
"}"
"}\n",
"\n",
"// make AXI4 flavor of FIRBlock\n",
"abstract class AXI4FIRBlock(nFilters: Int, nTaps: Int)(implicit p: Parameters) extends FIRBlock[AXI4MasterPortParameters, AXI4SlavePortParameters, AXI4EdgeParameters, AXI4EdgeParameters, AXI4Bundle](nFilters, nTaps) with AXI4DspBlock with AXI4HasCSR {\n",
" override val mem = Some(AXI4RegisterNode(\n",
" AddressSet(0x0, 0xffffL), beatBytes = 8\n",
" ))\n",
"}\n",
"\n",
"// running the code below will show what firrtl is generated\n",
"// note that LazyModules aren't really chisel modules- you need to call \".module\" on them when invoking the chisel driver\n",
"// also note that AXI4StandaloneBlock is mixed in- if you forget it, you will get weird diplomacy errors because the memory\n",
"// interface expects a master and the streaming interface expects to be connected. AXI4StandaloneBlock will add top level IOs\n",
"// println(chisel3.Driver.emit(() => LazyModule(new AXI4FIRBlock(1, 8)(Parameters.empty) with AXI4StandaloneBlock).module))"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"---\n",
"# Invoking the Generator\n",
"\n",
"The following code invokes the generator.\n",
"It will produce a file called `BlindModule.fir` which contains the firrtl generated by our code, as well as a file called `BlindModule.v` which is the compiled verilog.\n",
"`BlindModule` is a wrapper for our `DspBlock`- it is necessary because of how [diplomacy](https://carrv.github.io/papers/cook-diplomacy-carrv2017.pdf) in rocket works.\n",
"You'll notice that `BlindModule` instantiates an `FIRBlock`.\n",
"\n",
"The line\n",
"```scala\n",
"AXI4StreamBundleParameters(n = 8)\n",
"```\n",
"sets the AXI-4 streams' data widths to 8 bytes."
"## Testing\n",
"\n",
"Testing `DspBlock`s is a little different.\n",
"Now we're dealing with memory interfaces and `LazyModule`s.\n",
"dsptools has some features that help test `DspBlock`s.\n",
"\n",
"One important feature is `MemMasterModel`.\n",
"The trait defines functions like `memReadWord` and `memWriteWord`- generic functions for generating memory traffic.\n",
"This allows you to write one generic test that can be specialized to the memory interface you are using- for example, you write one test and then specialize it for the TileLink and AXI4 interfaces.\n",
"\n",
"The code below tests the `FIRBlock` this way."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": false
},
"metadata": {},
"outputs": [],
"source": [
"object FIRGenerator extends ipxact.IPXactGeneratorApp with App {\n",
" override val verilogFilename: String = \"BlindModule.v\"\n",
" override val ipxactDir: String = \"./\"\n",
" implicit val p: Parameters = Parameters.root((new freechips.rocketchip.coreplex.BaseCoreplexConfig).toInstance)\n",
"\n",
" val blindNodes = DspBlockBlindNodes.apply(\n",
" AXI4StreamBundleParameters(n = 8),\n",
" () => AXI4MasterNode(Seq(AXI4MasterPortParameters(Seq(AXI4MasterParameters(\"fir\"))))))\n",
" \n",
" val dut = () => {\n",
" val lazyMod = LazyModule(DspBlock.blindWrapper(() => new FIRBlock(4, 8), blindNodes))\n",
" val m = lazyMod.module\n",
" IPXactComponents._ipxactComponents += DspIPXact.makeDspBlockComponent(lazyMod.internal)\n",
" m\n",
"import dsptools.tester.MemMasterModel\n",
"import freechips.rocketchip.amba.axi4\n",
"\n",
"abstract class FIRBlockTester[D, U, EO, EI, B <: Data](c: FIRBlock[D, U, EO, EI, B]) extends PeekPokeTester(c.module) with MemMasterModel {\n",
" // check that address 0 is the number of filters\n",
" require(memReadWord(0) == c.nFilters)\n",
" // write 1 to all the taps\n",
" for (i <- 0 until c.nFilters * c.nTaps) {\n",
" memWriteWord(8 + i * 8, 1)\n",
" }\n",
"}\n",
"\n",
" \n",
" chisel3.Driver.dumpFirrtl(chisel3.Driver.elaborate(dut), None)\n",
" chisel3.Driver.compileFirrtlToVerilog(\"BlindModule\", new java.io.File(System.getProperty(\"user.dir\")))\n",
" generateIPXact(IPXactComponents.ipxactComponents())\n",
"// specialize the generic tester for axi4\n",
"class AXI4FIRBlockTester(c: AXI4FIRBlock with AXI4StandaloneBlock) extends FIRBlockTester(c) with AXI4MasterModel {\n",
" def memAXI = c.ioMem.get\n",
"}\n",
"FIRGenerator.main(Array[String]())"
"\n",
"// invoking testers on lazymodules is a little strange.\n",
"// note that the firblocktester takes a lazymodule, not a module (it calls .module in \"extends PeekPokeTester()\").\n",
"val lm = LazyModule(new AXI4FIRBlock(1, 8)(Parameters.empty) with AXI4StandaloneBlock)\n",
"chisel3.iotesters.Driver(() => lm.module) { _ => new AXI4FIRBlockTester(lm) }"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"---\n",
"# IPXact\n",
"Look in the file browser for a file called `craft_BlindModule_edu.berkeley.cs_1.0.xml`.\n",
"This is the ipxact file.\n",
"It contains information about:\n",
"* Port mappings\n",
"* Interfaces\n",
"* Memory maps\n",
"* Generator parameters\n",
"\n",
"You'll notice that the parameters from the scala code\n",
"```scala\n",
"override def ipxactParameters = Map(\n",
" \"nFilters\" -> nFilters.toString,\n",
" \"nTaps\" -> nTaps.toString\n",
")\n",
"```\n",
"appears in the IPXact output.\n",
"This gives verification generators the information they need to generate appropriate test vectors for the given instance."
"<span style=\"color:red\">**Exercise: TileLink**</span><br>\n",
"\n",
"Add a version of `FIRBlock` that uses TileLink for its memory interconnect, and extend the `FIRBlockTester` to use TileLink."
]
},
{
"cell_type": "markdown",
"metadata": {
"collapsed": true
},
"metadata": {},
"source": [
"---\n",
"# You're done!\n",
Expand All @@ -493,12 +450,11 @@
"codemirror_mode": "text/x-scala",
"file_extension": ".scala",
"mimetype": "text/x-scala",
"name": "scala211",
"name": "scala",
"nbconvert_exporter": "script",
"pygments_lexer": "scala",
"version": "2.11.11"
"version": "2.12.8"
}
},
"nbformat": 4,
"nbformat_minor": 1
"nbformat_minor": 2
}
7 changes: 7 additions & 0 deletions source/load-ivy.sc
@@ -1,3 +1,9 @@
interp.repositories() ++= Seq(
coursier.maven.MavenRepository("https://oss.sonatype.org/content/repositories/snapshots")
)

@

interp.configureCompiler(x => x.settings.source.value = scala.tools.nsc.settings.ScalaVersion("2.11.12"))

// Uncomment and change to use proxy
Expand All @@ -9,6 +15,7 @@ import $ivy.`edu.berkeley.cs::chisel-iotesters:1.3-SNAPSHOT`
import $ivy.`edu.berkeley.cs::chisel-testers2:0.1-SNAPSHOT`
import $ivy.`edu.berkeley.cs::dsptools:1.1.0`
import $ivy.`org.scalanlp::breeze:0.13.2`
import $ivy.`edu.berkeley.cs::rocket-dsptools:1.2-020719-SNAPSHOT`

// Convenience function to invoke Chisel and grab emitted Verilog.
def getVerilog(dut: => chisel3.core.UserModule): String = {
Expand Down

0 comments on commit dc83974

Please sign in to comment.