In case anyone stumbles on this blog, this image describes what I'm doing ..
As does this ..
Monday, May 8, 2017
Play and learn
How to test a parameterized module in Chisel
Quick update today. Busy with real work. This quick post is written because how to test these modules in not adequately covered in the documentation for chisel-testers.
Consider the case for a parameterizable LSFR module.
Below I show a test containing two examples. First using PokeTester and the second using PeekPokeTester. See if you can spot the difference ...
In the PokeTester case, the first argument to test is new LSFR(new DDRCommand). For PeekPokeTester, you pass a function to generate the module () => new LSFR(new DDRCommand). Not immediately obvious. Now, look at the function prototypes for PokeTester and PeekPokeTester respectively ...
The definition of dutGen is different.
Wise to keep in mind that PokeTester is under iotesters.experimental.
Consider the case for a parameterizable LSFR module.
class LSFR[T <: Bundle](gen: T = UInt(4.W)) extends Module { val io = IO(new Bundle { val rand = Output(gen) }) val width = gen.getWidth val regLFSR = Reg(UInt(width.W), init = UInt(0)) when(true.B) { regLFSR := Cat(regLFSR(0) ^ regLFSR(1), regLFSR(width - 1, 1)) } io.rand := gen.fromBits(regLFSR) }
Below I show a test containing two examples. First using PokeTester and the second using PeekPokeTester. See if you can spot the difference ...
class LSFRPeekPokeTester[T <: Bundle](gen: T, c: LSFR[T]) extends PeekPokeTester(c) { println("peekpoke test") } class LSFRSpec extends ChiselFlatSpec with PokeTester { behavior of "LSFR" it should "elaborate with peek test" in { val options = new TesterOptionsManager options.setTargetDirName("test_run_dir/LSFRSpec") test(new LSFR(new DDRCommand), testerBackend = FirrtlInterpreterBackend, options = options) { (t, c) => { println("poke test") } } } it should "elaborate with peekpoke test" in { Driver(() => new LSFR(new DDRCommand)) { c => new LSFRPeekPokeTester(new DDRCommand, c) } } }
In the PokeTester case, the first argument to test is new LSFR(new DDRCommand). For PeekPokeTester, you pass a function to generate the module () => new LSFR(new DDRCommand). Not immediately obvious. Now, look at the function prototypes for PokeTester and PeekPokeTester respectively ...
// PokeTester source def test[T <: Module](dutGen: => T, testerBackend: TesterBackend, options: TesterOptionsManager)(block: (InnerTester, T) => Unit) { //[..] // PeekPokeTester source def apply[T <: Module]( dutGen: () => T, backendType: String = "firrtl", verbose: Boolean = false, testerSeed: Long = System.currentTimeMillis())( testerGen: T => PeekPokeTester[T]): Boolean = { //[..]
The definition of dutGen is different.
Wise to keep in mind that PokeTester is under iotesters.experimental.
Saturday, May 6, 2017
Use of HellaQueue in Chisel, secondary constructor
This post is an expanded update to an entry in my personal "NOTES" file. At first glance, I found the following code perplxing, therefore I decided to break it down.
What does the :< operator do in [T <: Data>]?
It is obvious (to me at least) that this is for type parameterization. The <: operator indicates that the type supplied to the class instance must have chisel3.core.Data as an ancestor.
Why does (data: => T) do?
No suprise that (val entries: Int) is the primary constructor. The fat arrow (=>) implies a function. The first argument to the constructor lets you specific the number of entries. The second argument data helps to parameterize the class.
Why is a class definion and a object definition of the same name?
The object is known as a companion class, consult stackoverflow.com for more info
What does the apply function do in the object?
The apply function acts as an auxillary constructor. Allows alternative syntax for creating HellaQueue.
class HellaQueue[T <: Data](val entries: Int)(data: => T) extends Module { val io = new QueueIO(data, entries) val fq = Module(new HellaFlowQueue(entries)(data)) fq.io.enq <> io.enq io.deq <> Queue(fq.io.deq, 1, pipe = true) } object HellaQueue { def apply[T <: Data](enq: DecoupledIO[T], entries: Int) = { val q = Module((new HellaQueue(entries)) { enq.bits }) q.io.enq.valid := enq.valid // not using <> so that override is allowed q.io.enq.bits := enq.bits enq.ready := q.io.enq.ready q.io.deq } }
What does the :< operator do in [T <: Data>]?
It is obvious (to me at least) that this is for type parameterization. The <: operator indicates that the type supplied to the class instance must have chisel3.core.Data as an ancestor.
Why does (data: => T) do?
No suprise that (val entries: Int) is the primary constructor. The fat arrow (=>) implies a function. The first argument to the constructor lets you specific the number of entries. The second argument data helps to parameterize the class.
// example usage val xy: Data val hq1 = (new HellaQueue(entries = 90)){xy} // equivalent to above, without (data: => T) in the class constructor val hq2 = (new HellaQueue[SInt](entries = 90))
Why is a class definion and a object definition of the same name?
The object is known as a companion class, consult stackoverflow.com for more info
What does the apply function do in the object?
The apply function acts as an auxillary constructor. Allows alternative syntax for creating HellaQueue.
Wednesday, May 3, 2017
Using IDEA Scala debugger for Chisel
This a quick follow up on an older post where I set up IntelliJ IDEA and Scala. I wrote this to make sure my steps for getting the debugger up and running were reproducable. The steps assume you have a working Chisel project that compiles using sbt.
Check if sbt compile works from the command line for your Chisel project ...
Check if sbt compile works from the command line for your Chisel project ...
[plus]:~/gitrepos/plus/arbiterdemo$ sbt compile [info] Loading global plugins from /home/edc/.sbt/0.13/plugins [info] Loading project definition from /home/edc/gitrepos/plus/arbiterdemo/project [info] Set current project to memctrldemo (in build file:/home/edc/gitrepos/plus/arbiterdemo/) [info] Updating {file:/home/edc/gitrepos/plus/arbiterdemo/}arbiterdemo... [..] [info] Done updating. [info] Compiling 3 Scala sources to /home/edc/gitrepos/plus/arbiterdemo/target/scala-2.11/classes... [success] Total time: 4 s, completed May 3, 2017 8:26:36 PM
Use the default project settings ...
Right click on your class that contains call to chisel3.Driver.execute, hit Run ...
Right click on your class that contains call to chisel3.Driver.execute, hit Run ...
Follow up by checking that that Verilog code is generated as expected (yes)
Finally, try setting a breakpoint by right clicking to the left of the source window and hit shift-F9 to begin debug. I was 100% successfull following only these steps on IDEA 2017.1 with the project-specific IDEA settings (i.e. .idea/ diretory) wiped out prior.
Finally, try setting a breakpoint by right clicking to the left of the source window and hit shift-F9 to begin debug. I was 100% successfull following only these steps on IDEA 2017.1 with the project-specific IDEA settings (i.e. .idea/ diretory) wiped out prior.
Gentle introduction to diplomacy
In a previous post, I was looking at how to use the diplomacy and uncore libraries (in Rocket Chip Generator) to hook up a simple AHB master module to AHB slave module. To my knowledge, none of the library code is documented. IDEA's debugger came in handy to grow my understanding of the code base. I can confirm the wiring portion now works. May come back to this in the future.
... there might be a bug in naming of ports for hrdata.
Master module
class Master()(implicit p: Parameters) extends LazyModule { val ahbMasterParameters = AHBMasterParameters( // nothing for now .. ) val ahbMasterPortParameters = AHBMasterPortParameters( masters = Seq(ahbMasterParameters) ) val node = AHBMasterNode( portParams = Seq(ahbMasterPortParameters) ) lazy val module = new LazyModuleImp(this) { val io = new Bundle { val out = node.bundleOut } } }
Slave module
class Slave()(implicit p: Parameters) extends LazyModule { val ahbSlaveParameters = AHBSlaveParameters( address = Seq(AddressSet(0x0, 0xFFFF)), regionType = RegionType.UNCACHED, executable = true, supportsRead = TransferSizes(1, 4), supportsWrite = TransferSizes(1, 4) ) val ahbSlavePortParameters = AHBSlavePortParameters( slaves = Seq(ahbSlaveParameters), beatBytes = 4 ) val node = AHBSlaveNode( portParams = Seq(ahbSlavePortParameters) ) lazy val module = new LazyModuleImp(this) { val io = new Bundle { val in = node.bundleIn } } }
Connect master to slave
class Slave()(implicit p: Parameters) extends LazyModule { val ahbSlaveParameters = AHBSlaveParameters( address = Seq(AddressSet(0x0, 0xFFFF)), regionType = RegionType.UNCACHED, executable = true, supportsRead = TransferSizes(1, 4), supportsWrite = TransferSizes(1, 4) ) val ahbSlavePortParameters = AHBSlavePortParameters( slaves = Seq(ahbSlaveParameters), beatBytes = 4 ) val node = AHBSlaveNode( portParams = Seq(ahbSlavePortParameters) ) lazy val module = new LazyModuleImp(this) { val io = new Bundle { val in = node.bundleIn } } }
Top level hookup
class ChiselTopTest extends ChiselFlatSpec { class ChiselTopTester(c: ChiselTopWrapper) extends PeekPokeTester(c) { println("Hello World!") } // https://github.com/ucb-bar/rocket-chip/issues/359 class ChiselTopWrapper(p: Parameters) extends Module { // must wrap Module into Module() .. val top = Module(LazyModule(new ChiselTop()(p)).module) // must wrap IO into IO(), thought it wasn't necessary with Chisel3 .. val io = IO(top.io.cloneType) io <> top.io } implicit val p: Parameters = new BlankConfig chisel3.iotesters.Driver(() => new ChiselTopWrapper(p)) { c => new ChiselTopTester(c) } }
Generated verilog (abridged), woo!
module ChiselTop( input clock, input reset, input io_ddrClock ); Master_master master ( .io_out_0_hwdata(master_io_out_0_hwdata), .io_out_0_hrdata(master_io_out_0_hrdata) //--TODO: check naming ); Slave_slave slave ( .io_in_0_hwdata(slave_io_in_0_hwdata), .io_in_0_hrdata(slave_io_in_0_hrdata) ); // slave to master for read data assign master_io_out_0_hrdata = slave_io_in_0_hrdata; // master to slave for write data assign slave_io_in_0_hwdata = master_io_out_0_hwdata; endmodule
... there might be a bug in naming of ports for hrdata.
Tuesday, May 2, 2017
One simple command and voila ...
I have been trying to get IntelliJ IDEA's debugger working with Scala (support via plugin) for the better part of this past week. Each time the debugger was launched, I hit an issue that presented itself as a runtime exception: ClassNotFoundException for: org.jetbrains.plugins.scala.testingSupport.scalaTest.ScalaTestRunner.
My initial guess was "JAR Hell". The best trick I found for debugging library dependency issues is using sbt-depdency-graph plugin to visualize the dependency tree using sbt. To make use of the plugin you you need to add the following line to your plugins.sbt file. Use command sbt dependencyTree to view the output (library dependency tree) of the plugin.
addSbtPlugin("net.virtual-void" % "sbt-dependency-graph" % "0.8.2")
Connected to the target VM, address: '127.0.0.1:36512', transport: 'socket'
Luckily enough, I knew that something was up with my firewall as my Jenkins instance was no longer accepting connections. I had yet to debug this. On further inspection, I found my workstation's firewall rules had been completely wiped. The effect was no local TCP connections werepossible. The simple solution was to re-add the loopback network interface (lo) to firewalld and apply the trusted firewall zone to it. The not-so-simple solution, aka. the one that I first tried, was to identify the ports that IDEA's Scala plugin uses for local remote debugging. It turns out the port choice is random, thus the simple solution.
% firewall-cmd --zone=trusted --add-interface=lo --permanent
The debugger works fine now.
Subscribe to:
Posts (Atom)