chisel Vec() in Bundle class cannot be implemented in Module class - chisel

The chisel version I'm using is 3.2.2.
When I try to create a Vec of member in a Bundle class, code is like,
class myIOBundle extends GenericParameterizedBundle {
val en = Vec(4, Output(Bool()))
val aa = Input(Vec(4, Bool())) // Used in Debug.scala of rocket-chip
}
then I implement this Bundle class and pass it to IO of a Module class, the code is like,
class myModuleClass extends Module{
val io = IO(new myIOBundle)
...
}
Then I got the error says :
[error] chisel3.package$RebindingException: Attempted reassignment of binding to UInt<3>(4)
But I see the same use of Vec in Debug.scala of rocket code. Is this error encoutered in old chisel version, not the chisel 3.4.3, since the rocket is using chisel 3.4.3?

Related

Implementing a diplomatic AXI Stream interface in Chisel - BundleMap.cloneType error

I am trying to build a minimal example, of how to generate an AXI4Stream interface using Chisel and diplomacy. I am using the diplomatic interface already available in rocket-chip (freechips.rocketchip.amba.axis). I have some experience with Chisel, but I am still trying to learn diplomacy.
Anyway, I've managed to create a small APB example using the answer provided here: IP block generation/testing when using diplomacy. Possible to give dummy node?
Following that, I tried to create a similar, simple AXI Stream example, but I keep getting errors. Concretely, I get the following error:
[error] (Compile / run) java.lang.Exception: Unable to use BundleMap.cloneType on class freechips.rocketchip.amba.axis.AXISBundleBits, probably because class freechips.rocketchip.amba.axis.AXISBundleBits does not have a constructor accepting BundleFields. Consider overriding cloneType() on class freechips.rocketchip.amba.axis.AXISBundleBits
The code:
package chipyard.example
import chisel3._
import chisel3.internal.sourceinfo.SourceInfo
import chisel3.stage.ChiselStage
import freechips.rocketchip.config.{Config, Parameters}
import freechips.rocketchip.amba.axis._
import freechips.rocketchip.diplomacy.{SimpleNodeImp, ValName, SourceNode, NexusNode,
SinkNode, LazyModule, LazyModuleImp, TransferSizes,
SimpleDevice, AddressSet}
class MyAxisController(implicit p: Parameters) extends LazyModule {
val device = new SimpleDevice("my-device", Seq("tutorial,my-device0"))
val axisParams = AXISSlaveParameters.v1(name = "axisSlave", supportsSizes = TransferSizes(8,8))
val axisPortParams = AXISSlavePortParameters.v1(slaves = Seq(axisParams))
val node = AXISSlaveNode(portParams = Seq(axisPortParams))
lazy val module = new LazyModuleImp(this) {
val ins = node.in.unzip._1
val register = RegInit(UInt(8.W), 0.U)
register := register + ins(0).bits.data
}
}
class AXISMaster()(implicit p: Parameters) extends LazyModule {
val axisMasterParams = AXISMasterParameters.v1(
name = "axisMaster", emitsSizes = TransferSizes(8, 8)
)
val axisMasterPortParams = AXISMasterPortParameters.v1(
masters = Seq(axisMasterParams),
beatBytes = Option(8)
)
val node = AXISMasterNode(
portParams = Seq(axisMasterPortParams)
)
lazy val module = new LazyModuleImp(this) {
//The dontTouch here preserves the interface so logic is generated
dontTouch(node.out.head._1)
}
}
class MyAxisWrapper()(implicit p: Parameters) extends LazyModule {
val master = LazyModule(new AXISMaster)
val slave = LazyModule(new MyAxisController()(Parameters.empty))
slave.node := master.node
lazy val module = new LazyModuleImp(this) {
//nothing???
}
}
and Main.scala:
package chipyard.example
import chisel3._
import freechips.rocketchip.config.Parameters
import freechips.rocketchip.diplomacy._
import java.io.File
import java.io.FileWriter
/**
* An object extending App to generate the Verilog code.
*/
object Main {
def main(args: Array[String]): Unit = {
//(new chisel3.stage.ChiselStage).execute(args, Seq(ChiselGeneratorAnnotation(() => LazyModule(new MyWrapper()(Parameters.empty)).module)))
val verilog = (new chisel3.stage.ChiselStage).emitVerilog(
LazyModule(new MyAxisWrapper()(Parameters.empty)).module
)
//println(s"```verilog\n$verilog```")
val fileWriter = new FileWriter(new File("./gen/gen.v"))
fileWriter.write(verilog)
fileWriter.close()
}
}
The code is also available at https://github.com/jurevreca12/temp_dspblock_example/tree/axistream2/scala/main.
My question is. Why do I get this error? Or am I doing something wrong in the first place, and is there an easier way to create an AXIStream module?
I appreciate any feedback.
This looks to be an issue with Rocket-Chip's changes to bump to Chisel 3.5. During those changes, AXISBundleBits had its cloneType removed even though it extends off BundleMap (and therefore requires cloneType due to extending off Record).
I don't have all the details of cloneType at this time, but essentially:
Records require cloneType
Bundles used to require cloneType, but since the compiler plugin was implemented, as of 3.5 they no longer require cloneType.
BundleMap is a confusing case because it is a custom Bundle type extending directly off Record and isn't of type Bundle. Therefore, it shouldn't have had its cloneType method removed during the 3.5 Chisel bump and that will need to be added back for AXIS in RC's main branch to start working again.
Edit: the cloneType exception issue is now fixed for 3.5 on the main branch :)

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.

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 generate code to RTL with blackbox?

When I want to convert code chisel to verilog with black box, I have error. How can I fix it?
[error] /data/workspace/chisel/chisel3-3.1.8/src/main/scala/tap/dti_bypass_register.scala:45:18: overloaded method value execute with alternatives:
import chisel3._
import chisel3.util._
class dti_bypass_register extends BlackBox with HasBlackBoxResource {
val io = IO(new Bundle {
val clk_DR = Input (Clock())// Bypass register clock
val TDI = Input (UInt(1.W))// data in
val bypass_en = Input (Bool())// enable signal
val captureDR = Input (Bool())// captureDR signal
val TDO_bypass = Output (UInt(1.W))// Serial data out
})
setResource("/dti_bypass_register.v")
}
object dti_bypass_registerDriver extends App {
chisel3.Driver.execute(args, () => new dti_bypass_register)
}
Chisel does not accept BlackBoxes as the top Module. Since BlackBoxes are simply interfaces that we emit a Verilog instantiation for, there's not really anything for Chisel to do with them.

Serializing and unserializing case classes with lift-json

I'm attempting basic serialization/hydration with lift-json, but without success. As near as I can tell from the package readme, this should work. Help?
I'm using Scala 2.8.0 and Lift 2.2 cross-built for 2.8 with sbt ("net.liftweb" %% "lift-json" % "2.2").
import net.liftweb.json._
import net.liftweb.json.Serialization.{read, write}
implicit val formats = Serialization.formats(NoTypeHints)
case class Route(title: String)
val rt = new Route("x277a1")
val ser = write(rt)
// ser: String = {} ...
val deser = read[Route]("""{"title":"Some Title"}""")
// net.liftweb.json.MappingException: Parsed JSON values do not match with class constructor
Lift JSON's serialization does not work for case classes defined in REPL (paranamer can't find the bytecode to read the type metadata). Compile Route with scalac and then the above example works.
The same problem applies every time when the (de)serialuzed class is not on the classpath. In such case, paranamer can't read the parameter names. It is necessary to provide a custom ParameterNameReader.
Such problem applies for e.g.:
REPL (as mentioned) - unless you define the class outside the REPL and add via classpath.
Play Framework - unless you provide a simple custom ParameterNameReader (see below) or load the (de)serialized class as a Maven/Play/... dependency
Feel free to add another situation (you can edit this post).
The PlayParameterNameReader:
import net.liftweb.json.ParameterNameReader
import java.lang.reflect.Constructor
import play.classloading.enhancers.LocalvariablesNamesEnhancer
import scala.collection.JavaConversions._
object PlayParameterReader extends ParameterNameReader{
def lookupParameterNames(constructor: Constructor[_]) = LocalvariablesNamesEnhancer.lookupParameterNames(constructor)
}