Functional Programming on FPGA: How Clash Works
Reading time: 5
When using software tools to design FPGA hardware, there’s two mainstream choices: on one side are the classic hardware description languages – VHDL, Verilog and SystemVerilog. On the other side, there’s the recent shift towards high-level synthesis, where a developer describes hardware in languages that are much further removed from the hardware itself. Clash, the compiler we built and use at QBayLogic, sits in between. Let’s explore what that means.
The Clash hardware description language (HDL) is not a high-level synthesis tool, but it is a high-level language with strong abstraction capabilities. And for the Haskell community, it comes with a remarkable property: a Clash program is not written in a language that merely resembles Haskell. It is Haskell.
Clash is an open-source compiler that translates the functional programming language Haskell into VHDL and Verilog, the languages that the software from FPGA manufacturers accepts to configure their chips. The first version was built in 2009 as a graduation project at the University of Twente, and the project has been in worldwide use since at least 2014. Today, the compiler is maintained here at QBayLogic, the company we founded in 2016 to keep Clash alive, apply it to real-world projects and contribute to the lively Clash community.
Your Haskell program is the circuit
The defining idea behind Clash is direct translation. The Clash team looked at Haskell and asked: doesn’t this look a lot like hardware? Can we express this Haskell program as hardware, essentially one-to-one?
Take the simplest possible example: x + y is an addition in Haskell, as it is in any language you know. You can also do that in hardware: take a signal as x, take a signal as y, and put an adder behind them. Clash translates Haskell at that level. Every piece of Haskell becoming an equivalent piece of hardware.
That may sound obvious, but it is not how most alternatives work. There are other languages that use Haskell or another functional language to describe hardware. But they all add an extra compiler step and, consequently, a logical distance between your code and your FPGA. You write a program, and that program produces a recipe, which is then executed to build the circuit for you. In Clash, you write a circuit as if it were an ordinary Haskell program, and that exact program is what gets translated.
Functional programming and the FPGA: why Haskell fits
Why Haskell, of all languages? Because synchronous hardware – which is what an FPGA is – behaves like a functional language.
Synchronous hardware is hardware driven by a clock signal: a chip that needs a clock to trigger each compute step. At an abstract level, ignoring the physical details of silicon chip hardware, every wire in an FPGA carries a stream of values: each clock tick, the next value appears. This abstraction is something you can describe perfectly in Haskell. Every component of a chip can be seen as a Haskell function: streams of bits go in, streams of bits come out. Not everything in Haskell can be translated to hardware though, so Clash uses a subset of Haskell.
From Haskell to Verilog: what the compiler does
This is exactly what “Clash code” is: the subset of Haskell that the compiler can translate into VHDL or Verilog. From there, the standard FPGA toolchains take over.
Compare that with converting a language like C to hardware, which is what high-level synthesis tools do. There, it is not really the C code being translated. The tool first analyzes what the C code does and then works out how that behavior might be achieved in hardware. That is a much more indirect path and much more error prone.
The trade-off: control over convenience
Direct translation keeps you in control. But, unfortunately, this cuts both ways. Working with Clash, you have to be concrete about what you want to build. You cannot tell Clash “I want the result ready within ten clock cycles, work out how”. In Clash, you describe what happens at every clock tick yourself. That’s a burden, but it’s also exactly the point, because it gives you the predictability that the classic languages offer too. Which raises the fair question: why Haskell? Don’t VHDL and Verilog already do this?
The answer is in the concepts of abstraction and reuse. In Haskell, you can build a small component, think hard about its details once, and then stop thinking about them forever. When you reuse it in the future, it simply does what you (or some other smart engineer) designed. Out of these small, battle-tested components you compose larger ones, and out of those, larger ones still. The classic hardware languages struggle here: the human designer eventually gets stuck on the cognitive load of coding everything from the ground up. Haskell’s abstraction mechanisms make scaling FPGA design possible.
The standard library is not optional
The above suggests that you could, in theory, write an FPGA configuration from scratch in Clash. You can’t. Not even in theory. Clash comes with a standard library that every design uses and needs to use.
The components in the library range from the truly minuscule to the more intelligent. Some of them, the crucial bits, help the compiler lay the groundwork for turning a Haskell program dealing with some stream of values into synthesizable HDL. Without these components, Clash won’t work. But with these components, it’s possible to use many normal Haskell packages in hardware design, even though they were not designed with hardware in mind.
The library also contains something less visible but at least as important: the simulation environment we use to examine how a design behaves in Haskell itself, before any hardware is involved. It also contains solutions for cases where something is easy to express on the HDL side but awkward to get working well from the Haskell side.
A final category of standard components is used by the compiler itself: HDL code that the compiler just adds when a certain component is called for. This is also why there is no hard boundary between the library and the compiler.
Simulate in Haskell, then build the chip
Clash is ‘just Haskell’, so you can simulate pieces of Clash code in Haskell directly, without generating any HDL first, and immediately see the result. Although, generally, you will want to add some sort of visualization solution to quickly verify your results.
To demo how this works, we implemented the N-body problem from mathematics: N bodies attracting each other through gravity. In this case, our solar system. On an FPGA development board with a small LCD attached, you can see the sun in the middle, the planets orbiting around it, and the moon orbiting the earth. The planets and the moon are comically large, but the distances are to scale.
The N-body simulation running on an Intel Cyclone IV (Terasic DE0-Nano) with an LT24 LCD. Four specialised processors, each computing gravitational attraction, with the bodies divided between them in turns. Planet sizes are exaggerated; the distances are to scale.
Inside the FPGA, we implemented the problem as multiple highly specific processors executing a machine language designed exclusively to compute gravitational attractions, with the bodies divided over the four processors in turns.
The same design also ran as a Clash simulation on an ordinary computer: no hardware, not even any HDL involved. So we needn’t stare at rows of coordinates to verify our calculations, the simulation was combined with classic Haskell code that did nothing more than receive the planet coordinates and draw the circles to visualize the orbiting planets in a browser window. ‘Hard part’ and visualization, all in one language.
From equations to gates, provably
This demo also shows what a Clash design process looks like at its best. It begins with the mathematics: a set of interlocking equations and some state, in this case a matrix of the current positions and velocities of the bodies. It ends with processors executing machine language. The input and output of every step are mathematically equivalent or, in some steps, at least any numerical error introduced by the transformation can be proven to be within a known acceptable bound.
To solidify this even further, work is now under way on incorporating formal proofs in Clash programs and thus continuously proving that the before and after of each step are equal.
We’re not saying nothing can go wrong. Any piece of software, including the Clash compiler, can contain bugs. This could mean that your perfect Clash program produces incorrect HDL. But the probability of errors does decrease dramatically, compared to other ways of developing FPGAs. Plus, for most steps in the design process you can make visible, or even mathematically prove, that the step taken was a correct one. With languages like C, that is much harder. And the same goes for VHDL and Verilog.
Clash HDL: myth vs. fact
Is an FPGA hard to program?
With the classic languages used for FPGA development, the designer eventually gets stuck on the cognitive load of coding everything from scratch. Clash prevents this by using an extensive library of battle-tested components. This does not make hardware design effortless, since you still describe behavior clock tick by clock tick, but it changes what is hard. Details are handled once, inside a component, and abstraction takes over from there.
Is Clash high-level synthesis?
No. High-level synthesis analyses what your code does and then works out how to achieve that behavior in hardware. Clash translates your Haskell directly: for each construct it supports, the compiler knows what the corresponding hardware is.
Does Clash output VHDL or Verilog?
Both. Clash compiles Haskell to basic, RTL-like VHDL or Verilog, the languages supported by the FPGA manufacturers’ own software. Which one you generate will depend on the rest of your toolchain.
Can any Haskell program become hardware?
No. Clash translates a subset of Haskell, together with its standard library. Many Haskell constructs have no worked-out hardware counterpart. The reverse, however, largely holds: essentially everything synchronous hardware does can be written in Haskell.
Is Clash a research prototype that could be abandoned any day?
No. The first version dates from 2009, the project has been in worldwide use since at least 2014, and it is open source with a reasonably active community. Crucially, it did not stop when its academic origins ended: QBayLogic was founded in 2016 specifically to keep Clash alive, maintain the compiler and keep improving how hardware is designed.
Where to find Clash
Clash is open source. The project lives at clash-lang.org and on GitHub, where the source code is available and where the Clash community comes together, filing bug reports and contributing improvements and new functionality. Current development focuses, among other things, on interaction with the existing hardware languages, making it easier to reuse components already written in VHDL and Verilog inside a Clash design. For a Haskell developer curious what their language looks like as silicon, the distance is smaller than it appears: the program you would write is, after all, just Haskell.
