Chisel/FIRRTL Hardware Compiler Framework
chisel-lang.org
chisel-lang.org
For starters, it doesn’t seem to scale well. For a moderately complex design, it takes on the order of several minutes to generate Verilog code. When trying to work in a tight iteration debug loop this very much hinders productivity. In addition, it tends to eat up memory, too.
This is more of a scala/sbt thing, but typically the Chisel verilog generator is run via sbt which seems to only be able to run one thing at a time, so generating multiple (independent) modules described in the same project is not parallelizable without some finagling.
One thing I’ve noticed is, in an effort to support stable flattening and unflattening bundles to bit-vectors, the Chisel frontend will spend a large amount of its time doing reflection to get (bundle) class member names and sorting them (This I found after running it through a profiler – I apologize I don’t have those results handy).
The standard config environment for Chisel, CDE, makes things hard to understand because configuration for a design isn’t all kept in one place, but might be partially modified at any point in the code. This negatively impacts readability because one has to dive through and essentially understand the run-time call-stack in order to properly assemble the configuration in their head to diagnose why a particular value is set a certain way. (Reminds me of CSS, actually).
Also, CDE is essentially a layered switch case statement, which means it has O(n) lookup time which is also a big perf hit. In addition, each layer that does not contain a value throws an exception which also impacts performance when a large number of config lookups are happening in a design.
(Coming from a Chisel/FIRRTL dev...) Couple of clarifying comments:
CDE[^1] is really a Rocket Chip[^2] specific design decision meant to suit the configuration needs of system-on-chip generation (or: it's a Scala library which actually doesn't depend on Chisel and could be useful in unrelated projects). Also, SiFive should have fixed the issues with exceptions a long while ago (stack traces are still a thing, though).
Chisel gets a lot of criticism due to the (necessary) complexities of Rocket Chip. However, don't let that dissuade you from using it for a separate digital design project. None of the Rocket Chip complexity comes into play there.
Compilation time is something that we really care about and are seeking to make better. Minute-long Verilog generation times are something you would only see for mammoth design like Rocket Chip (generating MBs of Verilog). Nonetheless, we have some ideas on how to improve this via actual optimizations or meta-optimizations like grouping compiler transformations together that are safe to do so.
We also are really trying to make the Verification easier. One of these is effort on better testing via "Testers2"[^3] (name is TBD). The second is on improving the readability of the generated Verilog, e.g., emission of case statements [^4], emission of else-if[^5] (now supported!), etc. If you have suggestions we'd be happy to take a look (code examples of "I got this, but I'd like that" would be a huge plus, too!).
Regardless, thanks for taking the time to check it out.
[^1]: https://github.com/chipsalliance/api-config-chipsalliance
[^2]: https://github.com/chipsalliance/rocket-chip
[^3]: https://github.com/ucb-bar/chisel-testers2
[^4]: https://github.com/freechipsproject/chisel3/issues/1198
One of my favorite components of the ecosystem is https://github.com/ucb-bar/chisel-testers2, which replaces the peekpoke tester and makes it easier to test asynchronous IO.
The documentation isn't great, but Scala has a very flexible macro system (https://docs.scala-lang.org/overviews/macros/overview.html).
Great question! However, this is an incredibly difficult question to answer as it relies on comparing language power. Paul Graham's "Beating the Averages" essay gets into this with the "Blub Paradox" [^1]. (tl;dr: a programmer versed in one programming language can evaluate less powerful languages, but not more powerful languages.)
The FIR filter example shown is parametric. Verilog is also parametric. However, Chisel, because it's embedded in Scala, gives you other programming paradigms/features that Verilog/VHDL do not have (or vendor tools do not support): object oriented programming, functional programming, parametric polymorphism, first class function support, etc.
The example of the front page could, instead of taking a `Seq[UInt]`, take a type parameter and be parametric for an arbitrary Chisel datatype (`UInt`, `SInt`, `FixedPoint`, etc.). Something like this just isn't possible with Verilog/VHDL. Analogously, Python and assembly both can describe for loops, but there are better dimensions for comparison.
Problematically, if shown advanced features like parametric polymorphism too early (e.g., on the front page of the website) potential users will get confused and turn away. If shown an example too simple, but recognizable, users will see no benefits. Alternatively, Chisel does address SystemVerilog pain points. Michael Taylor does a nice job of elucidating these in Section 5 of [^2].
As this question comes up a lot, I've tried to iterate on an answer on SO which may also be a useful read [^3].
If you have any suggestions on better ways to get this across, any feedback is welcome, too.
[^1]: http://www.paulgraham.com/avg.html
[^2]: http://cseweb.ucsd.edu/~mbtaylor/papers/Taylor_DAC_BaseJump_...
[^3]: https://stackoverflow.com/questions/53007782/what-benefits-d...
I decided to give it a serious chance, allocated a few weeks to learn Scala and chisel and had some test projects to try it on.
In the end I gave up not really having seen the benefits of chisel over systemverilog or even vhdl. The riscv design that was the posterchild of chisel efficiency was coded in pure verilog by someone who knew his trade and the verilog version looks so much cleaner.
If I were to describe chisel in one word it would be "arrogance".
tl;dr: Chisel/FIRRTL is trying to be "LLVM for circuits".
Concretely, Chisel is a domain specific language for describing hardware in Scala. The Chisel front-end generates circuit IR (Flexible Intermediate Representation for RTL/FIRRTL), FIRRTL IR is then transformed/customized by the middle-end FIRRTL compiler, and finally emitted via a Verilog backend (and then fed into FPGA/ASIC tooling).
It's not High Level Synthesis, just powerful hardware description. You get all the features of Scala (object-oriented programming, functional programming, first class functions, parametric polymorphism) to write programs that generate circuits. Sadly, the last 50 years of software engineering is traditionally not available to hardware designers in classic languages like Verilog or VHDL. Chisel/FIRRTL is one solution to that.
We just released Chisel 3.2 and FIRRTL 1.2 which was a massive update. Notable additions include:
- Asynchronous Reset/Reset Inference: https://github.com/freechipsproject/chisel3/pull/1011
- BoringUtils (synthesizable cross module references): https://github.com/freechipsproject/chisel3/pull/718
- Strongly Typed Enums: https://github.com/freechipsproject/chisel3/pull/892
- Refactor of Chisel and FIRRTL as Stages of Phases: https://github.com/freechipsproject/firrtl/pull/1005 (and others)
- Aspect Oriented Programming: https://github.com/freechipsproject/chisel3/pull/1077
- Lots of documentation improvements: new website, dramatically increased Scaladoc
Going forward, we're trying to get the release cadence up and are pushing to make the verification problem easier. Others have mentioned Testers2 which is one effort in that area. A code coverage/assertion library is another effort.
I have built my own HDL, which is basically "cleaner Verilog" and not a generator-style approach like Chisel, and gets compiled to Verilog. One big problem I had was how to describe RAM blocks with synchronous read/write ports, or even worse, RAM blocks with two independent ports which are both synchronous read/write (i.e. BlockRAM in Xilinx FPGAs).
My intention was to make the HDL more powerful than just providing BlockRAM as a built-in module that can be instantiated, because this way the "assignment" of the read/write port, which determines clock/enable logic, can be merged into other synchronous blocks using the same clock and enable signals (nested if-statements), thus be more readable. But I never found an easy way to "extract" the BlockRAM's output register and enable out of a statement block, short of writing a full primitive inference system myself (which I tried to avoid).
Without such extraction, I could only convert the statement to a Verilog statement and rely on the inference from Xilinx's tools. I ended up with "patterns" that the hardware developer has to use, very similar to Xilinx's primitive instantiation templates, which would be compiled to the appropriate patterns in Verilog. Naturally this takes several tries until Xilinx's tools recognize what I intended to build. In retrospect, providing built-in submodules would have been easier.
On the FIRRTL side, there's a dedicated pass `ReplSeqMems` that will replace sequential memories with a blackbox and configuration file. The intent here is that the configuration file (describing info like width, depth, number of ports, etc.) can then be used as input to an SRAM generator.
For FPGAS, (and I may be wrong here), I think that without `ReplSeqMems`, when using `SyncReadMem`, things will automatically get inferred to BRAM for Xilinx tools. However, the same approach to `ReplSeqMems` could be used to replace memories with the correct template for a specific FPGA vendor tool.
There are some different design decisions between Chisel and Clash (and one or the other may work better for you!). Chisel is an embedded domain specific language in Scala that uses added hardware primitives to build a circuit graph and emit FIRRTL IR. Clash is a Haskell derivative that (in my limited understanding) reads a Haskell program, but interprets it using hardware semantics, eventually emitting Verilog. They are both hardware description languages and not HLS (c-to-gates).
The Chisel ecosystem is larger, currently. This is primarily due to UC Berkeley/SiFive projects (Rocket Chip RISC-V SoC generator, BOOM out-of-order processor, Hwacha vector accelerator, etc.).
One meta-difference is FIRRTL. Chisel/FIRRTL is trying to be a hardware compiler framework, analogous to LLVM, but for circuits. Chisel is only the front-end. You can interface other front-ends with or write your own front-end for FIRRTL (currently Yosys can target FIRRTL IT for Verilog -> FIRRTL IR). You then have a simple circuit IR to Target, but get access to all the existing FIRRTL transforms and have a path to Verilog for FPGA/ASIC. Relatedly, you can inject custom FIRRTL transforms into the FIRRTL compiler (one example being to add run-time fault injectors: https://github.com/IBM/chiffre).
All that said... Host language matters. If you're comfortable with Haskell (or Python), use what works! And for new/existing languages, targeting FIRRTL IR is an option.
More resources: