Is there a way to make signals in Chisel not defined at module scope visible in waveforms? - chisel

If we take for example the following code excerpt (at the top of a module):
val write_indices = WireInit(VecInit(Seq.fill(wordsPerBeat)(0.U((log2Ceil(nWays)+log2Ceil(nSets)+log2Ceil(cacheBlockBytes/wordBytes)).W))))
val write_line_indices = WireInit(VecInit(Seq.fill(wordsPerBeat)(0.U(log2Ceil(cacheBlockBytes/wordBytes).W))))
dontTouch(write_indices)
dontTouch(write_line_indices)
// refill logic
when(mem_response_present) {
for (i <- 0 until wordsPerBeat) {
val beatIdx = i.U(log2Ceil(wordsPerBeat).W)
val write_line_index = Cat(d_refill_cnt(log2Ceil(cacheBlockBytes/wordsPerBeat/wordBytes)-1, 0), beatIdx)
val write_idx = Cat(refill_way, refill_set, write_line_index)
write_indices(i) := write_idx
write_line_indices(i) := write_line_index
cache_line(write_idx) := tl_out.d.bits.data((i + 1) * wordBits - 1, i * wordBits)
}
}
The only reason for the two top level signals is to get lower signals visible in waveforms.
Is there any way to achieve the same effect without having to manually create those signals?
In this example half the code is used just to get the ability to debug.
That seems a bit excessive.

That seems a bit excessive
Completely agreed, fortunately there is a solution. For implementation reasons, Chisel by default is only able to name public fields of the Module class. That is, only the values at the the top-level scope of your Module. However, there is a nifty macro chisel3.experimental.chiselName that can name these vals inside of the for loop. Try annotating your Module like so:
import chisel3._
import chisel3.experimental.chiselName
#chiselName
class MyModule extends Module {
...
}
Please check out this earlier answer discussing naming, it has more information than is relevant to answer this question alone, but it has other useful information about how naming works in Chisel.

Related

Chisel: How the "read" function (or macro) is implemented for the SyncReadMem class?

Can anyone help me to understand how "read" macro is implemented? I have the feeling that "do_read" function below is actually called, but could not figure out how that is done. I'm intrigued by the "SourceInfoTransform" class. Can anyone give me a hint on its usage?
The "SyncReadMem" implementation is listed below.
Thanks in advance for any help!
Best regards,
-Fei
sealed class SyncReadMem[T <: Data] private (t: T, n: BigInt, val readUnderWrite: SyncReadMem.ReadUnderWrite) extends MemBase[T](t, n) {
def read(x: UInt, en: Bool): T = macro SourceInfoTransform.xEnArg
/** #group SourceInfoTransformMacro */
def do_read(addr: UInt, enable: Bool)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): T = {
val a = Wire(UInt())
a := DontCare
var port: Option[T] = None
when (enable) {
a := addr
port = Some(read(a))
}
port.get
}
}
The SourceInfoTransform is a scala macro that transforms the def read into the def do_read. The code for the macro is in src/main/scala/chisel3/internal/sourceinfo/SourceInfoTransform.scala of github.com/chipsalliance/chisel3. In that file there are a lot of transform classes for handling different chisel constructs with different numbers of arguments. The main use of the SourceInfoTransform is to get the line number of the Chisel/Scala source so it can be reported in Exceptions and in generated Firrtl and Verilog. Here is an article on macros, there are many more available.
Good luck.
Chick's answer is mostly correct--it's correct on the "how" and where to look, but slightly wrong on the "why":
The main use of the SourceInfoTransform is to get the line number of the Chisel/Scala source
This is not quite true, you can get source locators with merely having implicit sourceInfo: SourceInfo, and you could have that on the original def read if you wanted to. What the SourceInfoTransform macros do is resolve ambiguity when you want to do a bit extraction immediately following invoking the read method, eg.
myMem.read(addr, en)(16, 0)
If we had defined read with the implicits directly, the compiler would think you're trying to pass 16 and 0 as the implicit arguments, when in reality, you're trying to call .apply on the resulting T (if it's a subtype of Bits). The macro resolves the ambiguity so that the compiler knowns you're not trying to pass the implicits.
Here's a link to a talk where I describe this (warning while the little part about source locators and the macro are correct, much of this talk is out-of-date): https://youtu.be/2-ZiXNd9wbc?t=2756

how to override/extend chisel signal naming

It seems not an easy thing to do or even impossible, but we are using a naming convention that prefix or postfix signals with "i_" or "o_" for inputs/outputs in verilog.
Is there some method to mess with or override inside the chisel library to to that?
I saw that except for clock and reset, all signals have "io" prefix.
Is is possible to use just "i" for input and "o" for output?
The easiest way to do this is to probably use a MultiIOModule. However, you can also do it with suggestName. Both approaches are shown below.
MultiIOModule
This a more flexible Module that lets you call the IO method to add ports to a module more than once. (Module requires that you define an io member and only allows you to call IO once.)
Because MultiIOModule frees you from the constraints of val io = ... you can use the prefix/postfix naming that you want with the names of your vals. Reflective naming will then get these right in the generated Verilog.
Consider the following Chisel code:
import chisel3._
import chisel3.stage.{ChiselStage, ChiselGeneratorAnnotation}
class Foo extends MultiIOModule {
val i_bar = IO(Input(Bool()))
val o_baz = IO(Output(Bool()))
o_baz := ~i_bar
}
(new ChiselStage).execute(Array.empty, Seq(ChiselGeneratorAnnotation(() => new Foo)))
This produces the following Verilog:
module Foo(
input clock,
input reset,
input i_bar,
output o_baz
);
assign o_baz = ~ i_bar;
endmodule
SuggestName
As an alternative, you can use the suggestName method to change the name to be different from what reflective naming (getting the name from the name of the val) would use.
Using suggestName you can coerce the names to be whatever you want. The following Chisel produces the same Verilog as above:
class Foo extends MultiIOModule {
val a = IO(Input(Bool())).suggestName("i_bar")
val b = IO(Output(Bool())).suggestName("o_baz")
b := ~a
}

How to keep val names under withClock() or withClockAndReset() scopes

Val names under withClock() & withClockAndReset() scopes tend to lose their coded names in the generated Verilog file.
So far in order to maintain to original names I used suggestName() function to force the original name.
However I wonder if there is a smarter way do it ? is there a way to force all vals to keep their names without adding suggestName() to each val declaration ?
As Kamyar mentioned in his comment, you should use the #chiselName macro
import chisel3._
import chisel3.experimental.chiselName
#chiselName
class MyModule extends Module {
...
withClock(otherClock) {
val importantReg = Reg(...) // <- this will now get a name
}
}
The way #chiselName works is it will automatically add a .suggestName to each val.

no cloneType for Mem?

I'm wrapping a block of Mem in a fairly generic module called "bank" and instantiating it in a Vec as follows:
val rams = Vec.fill( 100 ) { Module( new bank ).io }
So far so good. I'm running into problems when I connect the signals. If I connect the vector of modules' signals directly to vectors of signals, like so:
rams(i).in := io.ins(i)
io.outs(i) := rams(i).out
...and so forth, I get no errors.
If I connect them in a non-trivial pattern, however, such as to a crossbar, I start getting a weird error that appears to refer to the Mem wrapper I call "bank":
"Parameterized Bundle class ascenium.bank$$anon$1 needs cloneType method."
This error is specifically a Chisel error. Can anybody tells me what it means and how to fix it?
I can provide source code if need be.
Errors like this can usually be addressed by adding a cloneType method to classes you define:
class MyModule extends Module
{
// Your class definition here
override def cloneType = new MyModule.asInstanceOf[this.type]
}

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.