Remove Bundle prefixes for nested bundles? - chisel

I have a bunch of Verilog IPs that for various reasons we don't want to (or can't easily) convert to Chisel, but I want to take advantage of some of the configurability with Chisel for now until we can transition some of the designs.
My goal is to be able to instantiate my IPs as BlackBoxes, then use Chisels Bundle connection features. I have researched and played around with the BlackBox/ExtModule classes, and this seems to be what I need. The only issue I'm running into right now is that I would like to be able to create sub-Bundles of signal groups. See the image for a simple example:
And here is an example of what I want my BlackBox class to look like:
class BB1 extends BlackBox {
val io = IO(new Bundle{
val other_TOP = (new Bundle{
val in0 = Input (Bool())
val in1 = Input (Bool())
val d0 = Input (UInt(4.W))
val d1 = Input (UInt(4.W))
})
val apb = (new Bundle{
val apb_clk = Input (Bool())
val apb_paddr = Input (UInt(8.W))
val apb_penable = Input (Bool())
val apb_psel = Input (Bool())
val apb_pwrite = Input (Bool())
val apb_rddata = Output (UInt(32.W))
val apb_reset = Input (Bool())
val apb_wrdata = Input (UInt(32.W))
})
val other_BOTTOM = (new Bundle{
val out0 = Input (Bool())
val dout0 = Input (UInt(18.W))
})
})
}
This works as expected, however the issue I get is the sub-Bundle name is prefixed to the signal name. e.g. apb_clk has the name apb_apb_clk in the instantiation. I understand why this is happening, but is there anyway to disable this feature? I have tried some .suggestName, but it didn't seem to have any effect.
This issue on github (https://github.com/freechipsproject/chisel3/issues/612) seems to relate to the same issue, although the OP was asking for MultiIOModule. I saw where the last comment was a "Won't Fix". I'm wondering if additions have been made for this feature, or if there is any workaround outside of creating a wrapper. I'm actually looking to use Chisel to connect multiple BlackBoxes, so I'd prefer not to make wrappers just to connect these up.
Thank you for the help

Just to answer this. I ended up using Bundle names to "trick" the compiler to what I wanted the names and Bundle relationships to be (which basically turned out to be a wrapper). While it's not an entirely clean solution, I'll say that how Chisel/FIRRTL creates RTL in such a way that an additional wrapper is not that much extra work/hierarchy to allow BlackBox modules to be integrated easily.

Related

Create a generic Bundle in Chisel3

In Chisel3, I want to create a generic Bundle ParamsBus with parameterized type.
Then I follow the example on the Chisel3 website:
class ParamBus[T <: Data](gen: T) extends Bundle {
val dat1 = gen
val dat2 = gen
override def cloneType = (new ParamBus(gen)).asInstanceOf[this.type]
}
class TestMod[T <: Data](gen: T) extends Module {
val io = IO(new Bundle {
val o_out = Output(gen)
})
val reg_d = Reg(new ParamBus(gen))
io.o_out := 0.U
//io.o_out := reg_d.dat1 + reg_d.dat2
dontTouch(reg_d)
}
However, during code generation, I have the following error:
chisel3.AliasedAggregateFieldException: Aggregate ParamBus(Reg in TestMod) contains aliased fields List(UInt<8>)...
at fpga.examples.TestMod.<init>(test.scala:20)
Moreover, if I exchange the two lines to connect io.o_out, another error appears:
/home/escou64/Projects/fpga-io/src/main/scala/examples/test.scala:23:34: type mismatch;
found : T
required: String
io.o_out := reg_d.dat1 + reg_d.dat2
^
Any idea of the issue ?
Thanks for the help!
The issue you're running into is that the argument gen to ParamBus is a single object that is used for both dat1 and dat2. Scala (and thus Chisel) has reference semantics (like Java and Python), and thus dat1 and dat2 are both referring to the exact same object. Chisel needs the fields of Bundles to be different objects, thus the aliasing error you are seeing.
The easiest way to deal with this is to call .cloneType on gen when using it multiple times within a Bundle:
class ParamBus[T <: Data](gen: T) extends Bundle {
val dat1 = gen.cloneType
val dat2 = gen.cloneType
// Also note that you shouldn't need to implement cloneType yourself anymore
}
(Scastie link: https://scastie.scala-lang.org/mJmSdq8xSqayOceSjxHkRQ)
This is definitely a bit of a wart in the Chisel3 API because we try to hide the need to call .cloneType yourself, but least as of v3.4.3, this remains the case.
Alternatively, you could wrap the uses of gen in Output. It may seem weird to use a direction here but if all directions are Output, it's essentially the same as having no directions:
class ParamBus[T <: Data](gen: T) extends Bundle {
val dat1 = Output(gen)
val dat2 = Output(gen)
}
(Scastie link: https://scastie.scala-lang.org/TWajPNItRX6qOKDGDPnMmw)
A third (and slightly more advanced) technique is to make gen a 0-arity function (ie. a function that takes no arguments). Instead of gen being an object to use as a type template, it's instead a function that will create fresh types for you when called. Scala is a functional programming language so functions can be passed around as values just like objects can:
class ParamBus[T <: Data](gen: () => T) extends Bundle {
val dat1 = gen()
val dat2 = gen()
}
// You can call it like so:
// new ParamBus(() => UInt(8.W))
(Scastie link: https://scastie.scala-lang.org/JQ7D8VZsSCWP2i6DWJ4cLA)
I tend to prefer this final version, but I understand it can be more daunting for new users. Eventually I'd like to fix the issue you're seeing with a more direct use of gen, but these are ways to deal with the issue for the time being.

Connecting Individual Modules - Chisel

Hello I have a question regarding how to connect and map the ports between two modules, I will describe only the inputs and ouputs while excluding the control logic for each. The first module is a simple register whose output is the input to second module which is a demultiplexer
class simpleRegister extends Module {
val io = IO( new Bundle {
val enable = Input(UInt(1.W))
val in = Input(UInt(8.W))
val out = Output(UInt(8.W))
})
}
class demultiplexer extends Module {
val io = IO(new Bundle {
val datain = Input(UInt(8.W))
val dataout1 = Output(UInt(8.W))
val dataout2 = Output(UInt(8.W))
})
I am not sure if i should what method to use when I read " Interfaces & Bulk Connections" on github
You need to construct a top module that calls both the modules and perform the construction. This is pretty straight forward.
Your top module will look like this
class Top extends Module{
//You can expose a top level IO bundle if you will
val smplReg = Module(new simpleRegister)
val dmux = Module(new demultiplexer)
//connection of interest here is
dmux.io.datain := smplReg.io.out
//Make other connections as necessary
}
Interfaces and Bulk connections are when two IO bundles have similar fields instead of connecting each input/ output to the corresponding input/output you connect the bundle as a whole. This is just a way of optimizing your code.

Chisel Bundle connection and type Safety

Just noticed you can do something like:
class MyBundle1 extends Bundle {
val a = Bool()
val x = UInt(2.W)
val y = Bool()
}
class MyBundle2 extends Bundle {
val x = Bool()
val y = Bool()
}
class Foo extends Module {
val io = IO(new Bundle {
val in = Input(new MyBundle1)
val out = Output(new MyBundle2)
})
io.out := io.in
}
not get an error and actually get the correct or, at least, expected verilog. One would think that Chisel was type-safe wrt to Bundles in this sort of bulk connection. Is this intentional? If so, is there any particular reasons for it?
The semantics of := on Bundle allow the RHS to have fields that are not present on the LHS, but not vice-versa. The high-level description of x := y is "drive all fields of x with corresponding fields of y."
The bi-directional <> operator is stricter.
I can't speak precisely to the reasoning, but connection operators are often a matter of taste with respect to "do what I mean." The idea of including alternate forms of connection operators for future releases is an ongoing discussion, with the threat of "operator glut" being weighed against the ability of users to specify exactly what they'd like to do.

Chisel test - internal signals

I would like to test my code, so I'm doing a testbench. I wanted to know if it was possible to check the internal signals -like the value of the state register in this example- or if the peek was available only for the I/O
class MatrixMultiplier(matrixSize : UInt, cellSize : Int) extends Module {
val io = IO(new Bundle {
val writeEnable = Input(Bool())
val bufferSel = Input(Bool())
val writeAddress = Input(UInt(14.W)) //(matrixSize * matrixSize)
val writeData = Input(SInt(cellSize.W))
val readEnable = Input(Bool())
val readAddress = Input(UInt(14.W)) //(matrixSize * matrixSize)
val readReady = Output(Bool())
val readData = Output(SInt((2 * cellSize).W))
})
val s_idle :: s_writeMemA :: s_writeMemB :: s_multiplier :: s_ready :: s_readResult :: Nil = Enum(6)
val state = RegInit(s_idle)
...
and for the testbench :
class MatrixUnitTester(matrixMultiplier: MatrixMultiplier) extends PeekPokeTester(matrixMultiplier) { //(5.asUInt(), 32.asSInt())
println("State is: " + peek(matrixMultiplier.state).toString) // is it possible to have access to state ?
poke(matrixMultiplier.io.writeEnable, true.B)
poke(matrixMultiplier.io.bufferSel, false.B)
step(1)
...
EDIT : Ok, with VCD + GTKWave it is possible to graphically see these variables ;)
Good question. There's several parts to this answer
The Chisel supplied unit testing frameworks older chisel-testers and the newer chiseltest. Do not provide a mechanism to look into the wires directly.
Currently the chisel team is looking into ways of doing that.
Both provide indirect ways of doing it. Writing VCD output and using printf to see internal values
The Treadle firrtl simulator, which can directly simulate a firrtl (the direct output of the Chisel compiler) does allow for peek, and poking any signal directly. There are lots of examples of how its use in Treadle's unit tests. Treadle also provides a REPL shell which can be useful for exploring a circuit with manual peeks and pokes
The older chiseltesters (io-testers) and current chiseltest frameworks allow debugging the signal values with .peek() function that works well for the interface signals.
I haven't found a way to peek() an internal signal while debugging a testcase. However, Treadle simulator can dump the values of internal signals when it is running in verbose mode:
Add the annotation treadle.VerboseAnnotation to the test:
`test(new DecoupledGcd(16)).withAnnotations(Seq(WriteVcdAnnotation, treadle.VerboseAnnotation))`
When debugging in the IDEA and the test stops at breakpoint, the changes in the values of all internal signals up to this point are dumped to the Console.
This example will also generate the VCD wave file for further debugging.

how to extract from dispatch.json.JsObject

What do i need to do to extract the value for friends_count. i noticed that screen_name are already define in the Status object and case class. Do still require to extends Js or JsObject different
object TweetDetails extends Js { val friends_count = 'friends_count ? num }
and then pattern match it against each json object in the list of JsObjects as represented below. The symbols are confusing:
scala> val friends_count = 'friends_count ! num // I wish SO understood Scala's symbols
val twtJsonList = http(Status("username").timeline)
twtJsonList foreach {
js =>
val Status.user.screen_name(screen_name) = js
val Status.text(text) = js
val friends_counts(friends_count) = js //i cannot figure out how to extract this
println(friends_count)
println(screen_name)
println(text)
}
Normally, Scala symbols can be thought of as a unique identifier which will always be the same. Every symbol that is lexi-graphically identical refers to the exact same memory space. There's nothing else that's special about them from Scala's point of view.
However, Dispatch-Json pimps out symbols making them JSON property extractors. To see the code which is responsible for the pimping, check out the SymOp class and the rest of the JsonExtractor.scala code.
Let's write some code which solves the problem you are looking at and then analyze what's going on:
trait ExtUserProps extends UserProps with Js {
val friends_count = 'friends_count ! num
}
object ExtUser extends ExtUserProps with Js
val good_stuff = for {
item <- http(Status("username").timeline)
msg = Status.text(item)
user = Status.user(item)
screen_name = ExtUser.screen_name(user)
friend_count = ExtUser.friends_count(user)
} yield (screen_name, msg, friend_count)
The first thing that we're doing is extending the UserProps trait in the Dispatch-Twitter module to give it a friends_count extractor and then defining a ExtUser object which we can use to get access to that extractor. Because the ExtUserProps extends UserProps, which also extends Js, we get the method sym_add_operators in scope which turns our symbol 'friends_count into a SymOp case class. We then call the ! method on that SymOp which we then pass the Extractor num to, which creates an Extractor that looks for a property "friends_count" on a JSON object and then parses it as a number before returning. Quite a bit going on there for such a small bit of code.
The next part of the program is just a for-comprehension that calls out to the Twitter timeline for a user and parses it into JsObjects which represent each status item, them we apply the Status.text extractor to pull out the status message. Then we do the same to pull out the user. We then pull the screen_name and friend_count out of the user JsObject and finally we yield a Tuple3 back with all of the properties we were looking for. We're then left with a List[Tuple3[String,String,BigDecimal]] which you could then iterate on to print out or do whatever with.
I hope that clears some things up. The Dispatch library is very expressive but can be a little tough to wrap your head around as it uses a lot of Scala tricks which someone just learning Scala won't get right away. But keep plugging around and playing with, as well as looking at the tests and source code, and you'll see how to create powerful DSL's using Scala.