-
Notifications
You must be signed in to change notification settings - Fork 0
Changes in Chisel
As shown in the Chisel simulation pipeline section, a circuit needs be compiled to Verilog in order to be simulated. This compilation process consists of two main steps:
- Chisel elaboration: the Chisel code is executed and elaborated to an equivalent firrtl circuit (flexible intermediate representation).
- Firrtl compilation: the firrtl circuit is lowered to Verilog by firtool.
Thus, the missing debug information must be first generated during the first step, emitted together with firrtl and
propagated further through firtool
.
This section focuses on the Chisel elaboration step. First, a short analysis of the process to get firrtl from chisel is presented. Secondly, the phases and phase manager, core engine for the transformation, are explained. Finally, an updated phase pipeline is proposed for the emission of the Chisel type information encoded in firrtl in the form of an annotation.
The translation to firrtl is managed within the Chisel library by
ChiselStage
,
which acts as an entry point and bridge between Chisel and the CIRCT/MLIR compiler.
Specifically, this component is responsible for analyzing and preparing the source scala code for firrtl-verilog compilation.
Internally, before calling firtool, ChiselStage
executes several steps
(or Phase
s)
to elaborate Chisel source to firrtl.
These phases are handled by the
PhaseManager
,
which is responsible for storing, executing, and checking the dependencies and order between multiple elaboration steps.
The elaboration process is driven by the annotation-transformation mechanism, where the annotations are scala case classes storing generic information for the compiler within Chisel. Each phase implements mathematical transformation of a sequence of annotations as shown in Fig. 1.
Fig. 1 - Mathematical transformation implemented by the phase trait: transform(A) -> A'
|
ChiselStage implements an execute
method which is used to run the full compilation process from scala.
The method does 3 main operations:
- It first collects a set of input string arguments (CLI-like) and converts them to the corresponding annotation case classes;
- Then, it checks the validity of the sequence, if so the phases are guaranteed to succeed;
- Finally, it executes a sequence of transformation on those annotations through the phase manager.
The snippet below shows an example of calling this function to generate Verilog where the argument --target
specifies
the target language and
ChiselGeneratorAnnotation
is used to pass the circuit to the underlying compiler.
val chiselStage = new circt.stage.ChiselStage
val target = "verilog" // OPTIONS: --target {chirrtl|firrtl|hw|verilog|systemverilog}
chiselStage.execute(
// CLI-like arguments
args = Array("--target", target),
// Annotations
annotations = Seq(chisel3.stage.ChiselGeneratorAnnotation(() => new MyChiselModule()))
)
The PhaseManager
executes is the core of the elaboration since it performs the transformations.
In the sequence of phases specified by ChiselStage there are 2 phases of particular interest for the translation to firrtl:
the Elaborate
and Convert
phases. The body of a module is executed and elaborated into a circuit hardware graph (stored in
a ChiselCircuitAnnotation
)
which is subsequently converted into a firrtl hardware graph (stored in a
FirrtlCircuitAnnotation
).
This output firrtl representation is finally passed to the firtool through the
CIRCT
phase.
Currently, this pipeline does not support the generation of "chisel" debug information. Indeed, during the convert phase, any additional non-relevant information for a firrtl representation is skipped, such as the scala type information.
Fig. 2 - Highlighting the phases responsible for the transformation from Chisel to firrtl in the PhaseManager |
With the pipeline above, the firtool would have only the information intrisic to a firrtl representation, missing details about the source language that generated it. Firrtl is an intermediate representation (IR) used to standardize circuits produced by Chisel. Although firrtl is able to preserve the structure of a circuit and signal hierarchies, it cannot express or associate its elements with original Scala types. For instance, user defined bundles are always converted to anonymous bundles, making difficult their identification. This limitation arises because firrtl is generated once the scala meta-programming is executed, and there is no support for reconstructing it from firrtl. For more information about firrtl, please refer to its specification.
Therefore, the type information needs to be passed to firrtl without changing the behaviour while keeping compatibility with the existing pipeline.
In order to do so, I propose the following update to the existing phase manager of figure 2.
A new phase, AddTywavesAnnotation
(figure 3), is added to the pipeline between the Elaborate
and Convert
phases.
It parses the circuit graph emitted by the elaborate phase and annotates each component with its respective type
information stored in the form of a FIRRTL annotation.
The FIRRTL annotations are a mechanism to associate arbitrary metadata with zero or more target objects (i.e. signals,
modules etc...) of a firrtl circuit. The resulting scala annotation to encode the extra debug information is the
TywavesAnnotation
case class reported below. The type name and fields of constructor parameters are implemented as strings in order to
cover any possible type. In addition the annotation is declared as private to keep its usage internal only, since it is
something that the compiler should automatically generate and not the user.
case class ClassParam(name: String, typeName: String, value: Option[String])
private[chisel3] case class TywavesAnnotation[T <: IsMember](
target: T,
typeName: String,
// encode params as an option so if the class has no parameters, there is no field in the FIRRTL
params: Option[Seq[ClassParam]])
extends SingleTargetAnnotation[T] {
def duplicate(n: T) = this.copy(n)
}
The annotated circuit is then passed to the Convert
phase which will serialize the TywavesAnnotation
to a JSON string
during the conversion to firrtl. From here the firtool can consume it and operate specific debug transformations.
Fig. 3 - The updated PhaseManager for passing type information to firtool |
The only drawback of annotations is that they must be supported by all firrtl compilers, this means that if an
annotation is present in a firrtl output, the firrtl compiler used must handle it or abort. So, if other compilers of
firrtl are used, they may not work properly. However, I proposed and implemented
updates of firtool
to handle that.
Finally, in the updated phase pipeline this information is generated only when a debug flag is set, indicating that the circuit should be elaborated in a "debug compilation mode". In fact, debug information is not always necessary, for instance, when the logic is compiled for synthesis. Hence, this addition will not affect the compilation time if the flag is not set.
The TywavesAnnotation
stores the type and constructor parameters
of a Chisel component. Here an example of its serialization to JSON:
{
"class":"chisel3.tywaves.TywavesAnnotation",
"target":"~MyModule|MyModule",
"typeName":"MyModule"
},
{
"class":"chisel3.tywaves.TywavesAnnotation",
"target":"~MyModule|MyModule>inBundle",
"typeName":"IO[MyBundle]",
"params":[
{
"name":"size",
"typeName":"Int",
"value":"10"
}
]
},
{
"class":"chisel3.tywaves.TywavesAnnotation",
"target":"~MyModule|MyModule>wireBundle.a",
"typeName":"Wire[Bool]"
},
{
"class":"chisel3.tywaves.TywavesAnnotation",
"target":"~MyModule|MyModule>outBundle.a",
"typeName":"IO[SInt<9>]"
},