How to generate Verilog code with parametized modules in Chisel? - chisel

The following module definition in chisel:
class Mux2 (width: Int = 4) extends Module
does not result in a Verilog module that is parametrized. The generated Verilog RTL will instead substitute the parameter value that the user instantiated the module with.
Is there a way to generate Verilog with actual parametrized module definitions.
module Mux2 #(parameter width = 4)
If there is no way to do this this would be a very useful feature to add.

Unfortunately this is probably an impossible feature to add. Chisel is really just a Scala library of hardware primitives that enables you to write a Scala program to elaborate a circuit. Parameterization of Chisel generators is arbitrary Scala code which would be impossible to map to Verilog constructs in the general case. In fact, the primary utility of Chisel comes from enabling designers to use these higher-level constructs that do not exist in [synthesizable] Verilog (eg. object-oriented programming, functional programming).

Related

Difference between PyObject and object? (and possibly other definitions)

I'm looking for a precise definition of the following for the purpose of programming in Cython:
PyObject
object
Python object (as in "cannot convert X to Python object")
Cython object
and the differences between these and a shared object compiled from C/C++ code that Cython generates from a .pxd and .pyx file defining an extension type.
Edit: I meant the code one writes in C/C++ and the code that Cython generates. Compiling regular C/C++ code and Cython generated code will produce a different binary, right? What does Cython do that makes it worth not writing everything in C/C++?
(bonus: definition of PyObject in CPython source code)
I've tried learning cython from the official tutorials and reading the documentation, but this confusion is a major obstacle to further development.
Everything you manipulate in Python code is ultimately a "Python object". These are implemented/represented in C by the PyObject structure that contains
A link to the another PyObject defining the type.
A reference count (to decide when it can be destroyed)
Some data, which can be basically anything, depends on the type of the object and is what makes it useful.
(Almost) all Python programs should work in Cython. Thus, if you compile a Python program in Cython you're still using Python objects. Cython generates some C code that manipulates these Python objects using the Python C API (i.e. using them as PyObject*). You mostly don't need to worry about what it's actually doing since it behaves the same as in Python - they're automatically reference counted etc.
In Cython you can then specify types (e.g. cdef int, cdef char* or perhaps a C struct). These are directly C types, and using them appropriately gives you extra speed. They aren't Python objects (and so may need to be converted to Python objects if you want to pass them back to pure Python code - Cython knows how to do this for some, but not all, C types). The general rule in Cython is that everything is Python Object unless you say otherwise.
The object keyword in Cython is a way of specifying a type to be a Python object. (It also has its usual meaning from Python where it represents the type of an "empty" object). You don't normally have to use it since Cython assumes things are a Python object by default unless you tell it otherwise.
A "Cython object" refers to an object with a type defined as cdef class Something. These are still Python objects, however the "useful data" is stored in a way that Cython knows about which makes it quick to access from Cython. Often this useful data is composed of basic C types (like int or char*).
If you really want you can use the C PyObject directly in Cython, calling the C API functions yourself. If you do this then Cython does not take care of the reference counting for you (as it would do if you'd declared the type as object or simply not declared it). For this reason you shouldn't usually do this.
There's no real difference between writing C code yourself and letting Cython generate it - you can do anything it can do. It does take care of lots of tedious and hard-to-get-right stuff for you, and this is the main advantage.

Practical question about SOLID programming practises

A practical question about Dependency Inversion Principle:
We want to build our systems in many libraries or DLLs.
If the components or classes of a lower level library should depend upon an abstraction, be it an Iinterface or pure abstract class and the callee executable or higher-level library should also depend upon that abstraction rather than the concrete class then into which library should the abstraction be compiled?
Yes of course, the concrete class is wired and provided by a factory...
Logically, it belongs in the executable or higher level library but perhaps for practical purposes it should be compiled into the lower level library.
The most flexible (modular) approach is to compile the abstraction into its own binary file (library). This allows anyone to use and/or extend the abstraction without inheriting any implementation details. Ideally, this means without inheriting any transitive dependencies.
If the abstraction will only be consumed by one client, you may safely compile it along with its consumer, to reduce the amount of binary files. This will keep the concrete implementation(s) decoupled from the client.
The one thing you should not do is compile the abstraction along with any of its implementations. That would couple every client to the implementation code as well as its transitive dependencies.

Chisel Output with SystemVerilog Interfaces/Structs

I'm finding when generating Verilog output from the Chisel framework, all of the 'structure' defined in the chisel framework is lost at the interface.
This is problematic for instantiating this work in larger SystemVerilog designs.
Are there any extensions or features in Chisel to support this better? For example, automatically converting Chisel "Bundle" objects into SystemVerilog 'struct' ports.
Or creating SV enums, when the Chisel code is written using the Enum class.
Currently, no. However, both suggestions sound like very good candidates for discussion for future implementation in Chisel/FIRRTL.
SystemVerilog Struct Generation
Most Chisel code instantiated inside Verilog/SystemVerilog will use some interface wrapper that deals with converting the necessary signal names that the instantiator wants to use into Chisel-friendly names. As one example of doing this see AcceleratorWrapper. That instantiates a specific accelerator and does the connections to the Verilog names the instantiator expects. You can't currently do this with SystemVerilog structs, but you could accomplish the same thing with a SystemVerilog wrapper that maps the SystemVerilog structs to deterministic Chisel names. This is the same type of problem/solution that most people encounter/solve when integrating external IP in their project.
Kludges aside, what you're talking about is possible in the future...
Some explanation is necessary as to why this is complex:
Chisel is converted to FIRRTL. FIRRTL is then lowered to a reduced subset of FIRRTL called "low" FIRRTL. Low FIRRTL is then mapped to Verilog. Part of this lowering process flattens all bundles using uniquely determined names (typically a.b.c will lower to a_b_c but will be uniquified if a namespace conflict due to the lowering would result). Verilog has no support for structs, so this has to happen. Additionally, and more critically, some optimizations happen at the Low FIRRTL level like Constant Propagation and Dead Code Elimination that are easier to write and handle there.
However, SystemVerilog or some other language that a FIRRTL backend is targeting that supports non-flat types benefits from using the features of that language to produce more human-readable output. There are two general approaches for rectifying this:
Lowered types retain information about how they were originally constructed via annotations and the SystemVerilog emitter reconstructs those. This seems inelegant due to lowering and then un-lowering.
The SystemVerilog emitter uses a different sequence of FIRRTL transforms that does not go all the way to Low FIRRTL. This would require some of the optimizing transforms run on Low FIRRTL to be rewritten to work on higher forms. This is tractable, but hard.
If you want some more information on what passes are run during each compiler phase, take a look at LoweringCompilers.scala
Enumerated Types
What you mention for Enum is planned for the Verilog backend. The idea here was to have Enums emit annotations describing what they are. The Verilog emitter would then generate localparams. The preliminary work for annotation generation was added as part of StrongEnum (chisel3#885/chisel3#892), but the annotations portion had to be later backed out. A solution to this is actively being worked on. A subsequent PR to FIRRTL will then augment the Verilog emitter to use these. So, look for this going forward.
On Contributions and Outreach
For questions like this with (currently) negative answers, feel free to file an issue on the respective Chisel3 or FIRRTL repository. And even better than that is an RFC followed by an implementation.

Chisel/Firrtl Verilog backend proof of work

Is there some built in test or tools for formal verification of chisel or firrtl design vs generated verilog? On which concepts verilog backend is build? Is there any bugs in it?
There is no built-in formal verification support in Chisel and FIRRTL. There is no proof of work for the compiler or backend. As with any traditional compiler, there are certainly bugs although we do our best to catch and fix them.
We are currently using Yosys to perform LEC on a few instances of FIRRTL circuits between any changes we make to the FIRRTL code base. I would like to expand the use of formal verification to ensure that various transformations in the compiler do not change the semantics of the circuits upon which they operate. We are also experimenting with model checking backends to improve integration with formal verification tools.
As of FIRRTL v1.4 and Chisel v3.4, there will be basic support for verification primitives.
If you import chisel3.experimental.verification you'll get assert, assume and cover, which generate their corresponding constructs in Verilog.
import chisel3.experimental.{verification => v}
class Foo extends Module {
val predicate: Bool
v.assert(predicate)
}
Note that this is a fairly low-level interface. I'm currently working on a helper library to make formal verification in Chisel more approachable: https://github.com/tdb-alcorn/chisel-formal

Chisel code translating into Verilog/C++

So, I have a theoretical question about the Chisel code transformation.
I already know that the Chisel code is compiled to Java bytecodes, it then runs in the JVM and it emits equivalent Verilog and C++ source codes (for older versions of Chisel).
But I'm having a lot of trouble in understanding that process.
For instance, in the Chisel source code, I can see that there is a Reg class, for example, that creates a definition of a register. I can then import and use this class in the design of the hardware. But I cannot understand where the separation between the description of the Reg class itself and the actual usage of it lies. It's so confusing.
For example, suppose I'm developing a project that USES a Reg object, where there's a source code called whatever.scala, and inside this source code there are Reg objects. As I understand it, the description of the register itself (the Reg.scala) and the source code that uses it (whatever.scala) are all compiled at the same time, and that's precisely the point a cannot get.
To make it short, in my point of view, there is a separation between describing a library, and actually using this library after it was built. You must first compile the library, then you import it into your project and use it. But in Chisel, these two steps seem to happen at the same time.
Is there any intermediate process between the JVM code emission and the creation of the Chisel AST?
Chisel is a high level highly parameterized embedded DSL for generating hardware design.
A chisel program typically consists of several steps:
A chisel3 program first constructs an internal representations of an idealized circuit as an abstact syntax tree (AST). At the end of generation, the AST is serialized in to FIRRTL (an intermediate representation) representation. See: chisel3
The firrtl transformation engine process the high level FIRRTL produced with some number of transformation passes. These passes can optimize the code, do width inferences, and finally emit verilog or low firrtl. See: firrtl
Typically during development the circuit is then unit tested. There are two simple ways to do this.
The verilog emitted can be converted into an executable simulation via verilator and a c++ compiler. The simulation can be executed with a test harness that validates the circuit. See: chisel-testers
Or, the emitted firrtl can be simulated using the firrtl-interpreter a lightweight scala program, capable of running the same unit tests used with the chisel-testers. See: firrtl-interpreter
These steps can be run together, using chisel-tester can execute all the above steps automatically. Or done individually, each step can produce output files for the user to add custom integration or to target the verilog for FPGA or a chip tape-out.
The JVM is simply the execution environment used to run scala programs and is not necessary to understand or interact with in order to build circuits using Chisel.
To address the Chisel vs. your project question:
Chisel is a Scala library that is compiled to JVM bytecode. A project that uses Chisel is a Scala program that links against Chisel. This project is also compiled to JVM bytecode, but includes calls to the separately compiled Chisel library*. This project using Chisel is then executed, running on the JVM. The execution of this program constructs a hardware AST that is ultimately emitted as Verilog.
* Many projects (like rocket-chip) do include the Chisel source code as a subproject. Chisel is usually compiled first and then linked against. However, it should make no difference if it were compiled all at once--it's just Scala code that other Scala code invokes.