6
votes

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.

3

3 Answers

6
votes

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) 
}
0
votes

This is also the question that I am interested in. I think "to construct a hardware AST" is programed and compiled to be a *.class. When the classes are generated, so as the firrtl. After you execute those class, the rtl is generated, feels like a tool chain embedded. But I cannot tell how. Wish anybody to tell more about.

0
votes

Chisel is not something about "code transformation" or "code reflection". Chisel itself is a RTL. Verilog, vhdl and firrtl are different types of languages describing RTL. RTL means Register Transfer Level. Any language that directly describe register operation could be categorized into RTL.

In Chisel, regisgters are defined as Reg, wires are defined as Wires, programmer directly manipulate these registers and wires just the same as in verilog. So no wonder why chisel could be directly interpreted into verilog rtl.

What chisel did better than verilog is the diplomacy pattern design, which means, instead of declare every details of circuits, like the bit width or register delays, chisel does this automatically, it can automatically infers the interface connection and thus "generate" your desired circuit.