So, I have a theoretical question about the Chisel code transformation.
I know Chisel is actually a set of Scala definitions, so it is compiled to Java bytecodes, which in turn run in the JVM and, just like a magic, it spits out Verilog equivalent description and even C++ description for older versions of Chisel.
The point is that I could not figure out how this "magic" works. My guess is that the code transformation from Chisel to Verilog/C++ is all based on Scala reflection. But I'm not sure about it as I could not find anything related to this topic.
So, is it about reflection? If so, is it compile time our runtime reflection? Can someone please give me a clue?
Thanks a lot.
Fundamentally, writing Chisel is writing a Scala program to generate a circuit. What you're describing sounds a bit like High-Level Synthesis which is quite different from Chisel. Rather than mapping Scala (or Java) primitives to hardware, Chisel executes Scala code to construct a hardware AST that is then compiled to Verilog.
I'll try to make this a little more clear with an annotated example.
// The body of a Scala class is the default constructor
// MyModule's default constructor has a single Int argument
// Superclass Module is a chisel3 Class that begins construction of a hardware module
// Implicit clock and reset inputs are added by the Module constructor
class MyModule(width: Int) extends Module {
// io is a required field for subclasses of Module
// new Bundle creates an instance of an anonymous subclass of Chisel's Bundle (like a struct)
// When executing the function IO(...), Chisel adds ports to the Module based on the Bundle object
val io = IO(new Bundle {
val in = Input(UInt(width.W)) // Input port with width defined by parameter
val out = Output(UInt()) // Output port with width inferred by Chisel
})
// A Scala println that will print at elaboration time each time this Module is instantiated
// This does NOT create a node in the Module AST
println(s"Constructing MyModule with width $width")
// Adds a register declaration node to the Module AST
// This counter register resets to the value of input port io.in
// The implicit clock and reset inputs feed into this node
val counter = RegInit(io.in)
// Adds an addition node to the hardware AST with operands counter and 1
val inc = counter + 1.U // + is overloaded, this is actually a Chisel function call
// Connects the output of the addition node to the "next" value of counter
counter := inc
// Adds a printf node to the Module AST that will print at simulation time
// The value of counter feeds into this node
printf("counter = %d\n", counter)
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With