Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Chisel code transformation

Tags:

chisel

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.

like image 350
Rafael Avatar asked Jun 14 '17 14:06

Rafael


1 Answers

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) 
}
like image 99
Jack Koenig Avatar answered Nov 14 '22 11:11

Jack Koenig