Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Clash Compiler Language User Guide

Welcome to the Clash Compiler User Guide, the official documentation of the Clash Compiler. Clash is an open-source functional hardware description language (HDL) that borrows syntax and semantics from the Haskell programming language. To learn more, we suggest reading the introduction to Clash.

The table of contents in the sidebar (which can be opened in the menu on the top left) allows easy access to different pages in the documentation. You can also use the search function in the top-left corner.

Note

The Clash Compiler and Clash Language User Guide are open-source efforts developed by QBayLogic B.V., volunteers and students. The Clash Team always appreciates feedback and contributions to the project to help improve the development experience.

If you don't understand something, or think something is missing or incorrect in the documentation you can open an issue or pull request in the GitHub repository.

Installing Clash

Check out clash-lang.org/install to install the latest stable release of Clash, or to setup a Clash project.

Introduction to Clash

Functional Hardware

Clash is an open-source functional hardware description language (HDL) that closely mirrors the syntax and semantics of the Haskell programming language. It is used for creating hardware designs, typically for running on field programmable gate arrays (FPGAs) or application-specific integrated circuits (ASICs).

Clash is both a compiler, and a set of libraries for circuit design, that transform high level Haskell descriptions of synchronous, sequential logic into low-level VHDL, Verilog, or SystemVerilog. It provides a unique approach to design of sequential circuits, with a high amount of abstraction power that blurs the line between strictly behavioral and structural synthesis approaches.

Clash aims to modernize the hardware development experience, making it easier to quickly and correctly develop complex circuit designs. This is achieved by making Clash:

Expressive

Clash uses the Haskell type system to its full potential — including modern extensions and techniques — to bring a high level of type safety and expressiveness to hardware design.

This expressive typing makes it easier to develop safe, maintainable hardware. Combinational and sequential logic is separated by type, and global safety invariants such as separating incompatible clock domains are enforced in the type system.

Intuitive

Clash makes it easy to express circuit designs in an intuitive manner, allowing high level structural components to be connected easily in designs. Moreover, unlike most "high level synthesis" tools, this extends to precise control over register placement and pipelining.

Interactive

Unlike traditional HDL tools, Clash has a fully interactive read-eval-print loop (REPL), allowing circuits to be interactively designed and tested.

Performant

Clash reuses parts of the Glasgow Haskell Compiler to provide fast simulation of circuits for development and testing.

Efficient

Clash uses a "whole program synthesis" approach in order to view the entire circuit at once, and optimizes this design before translating to a specific target. This allows meaningful optimizations to be performed on the entire design.

Extensible

Additional primitives and black boxes can be added to Clash in the language of your choice, allowing you to use your own vendor or IP library within projects.

Clash allows seamless interoperability with libraries written in Haskell, including mtl, lens and hedgehog. This makes it even easier to quickly prototype complex designs.

Intended Audience

Clash is ideal for developers from different backgrounds, although the main intended audiences are

Hardware Engineers

You are a hardware engineer, used to using tools like VHDL and Verilog to implement circuit designs. Clash offers the familiar mixed simulation/synthesis capabilities of these tools, while providing a language with powerful abstractions.

Haskell Programmers

You are a Haskell programmer, looking to start developing hardware. Clash offers the ability to start prototyping and simulating designs in a familiar environment — lowering the learning curve significantly.

Maturity and Support

Clash is a continually evolving tool, having been actively developed since 2009. With the release of Clash 1.0 there has been an increased focus on maintaining API stability between releases, meaning circuit designs written in Clash should continue to work between minor releases. Today, the Clash Compiler is actively developed by QBayLogic B.V., volunteers and students.

Several companies and enthusiasts are already using Clash to develop circuit designs, ranging from small designs on hobbyist boards to larger designs on modern FPGA and ASIC architectures.

While care is taken to thoroughly test the Clash compiler, some bugs may exist. We encourage users to file issues, or contribute pull requests on our GitHub repository.

Meta-information: Web Sites, Mailing Lists, etc.

Mailing list: for updates and questions join the mailing list clash-language+subscribe@googlegroups.com or read the forum

Slack: Invite yourself at fpchat-invite.herokuapp.com. To join #clash, click on "Channels" and search for "clash".

IRC: freenode#clash-lang

Clash Version Numbering Policy

Clash follows the Haskell PVP Specification for its version numbers, for all packages. The main libraries that make up the Clash compiler maintain the same version numbers, making it easy to identify which versions are compatible.

Note

Due to the Clash's tight integration with GHC, updates to the GHC version that Clash uses result in changes to the Clash version. As GHC's internals change frequently, even for minor bumps, it cannot be guaranteed that these changes will not result in Clash changes.

It is recommended (but not required) that downstream Clash packages and published Clash code also follow the PVP specification.

Release Notes

Up to date release notes of the clash compiler can be found on the Github releases page.

Frequently Asked Questions

Basic Questions

  • Q: How do I install Clash?

    A: Check out the installing page in the Getting Started section of the manual.


  • Q: Is the name "Clash", "CLaSH", or "CλaSH"?

    A: It's Clash.

    In its research stages Clash was called "CλaSH", an acronym for the CAES Language for Synchronous Hardware. CAES is a group of the Faculty of Electrical Engineering, Mathematics and Computer Science at the University of Twente. Clash was originally developed by Christaan Baaij and supervisor Jan Kuper. The stylization "CλaSH" is an homage to Haskell, whose official logo has long been the venerable Greek lambda character.


  • Q: Is Clash a "high level synthesis" tool?

    A: While clash provides a high level language features, hardware descriptions written in Clash are not decoupled from clock-level timing. Clash does therefore not offer what is generally understood as "high level synthesis". Compared to the big three hardware description languages, VHDL, Verilog, and SystemVerilog, Clash arguably is high-level. It offers many of the powerful abstractions that modern software programming languages offer. In fact, it inherits many of the software industry's bleeding-edge features by virtue of basing its implementation on Haskell.

Clash Support

  • Q: Is Clash production ready?

    A: Clash is constantly evolving, and since the 1.0 release there is a focus on maintaining API backwards compatibility. Clash is used successfully in real-world scenarios, and QBayLogic Clash support can help with education and implementation of Clash projects.


  • Q: Will Clash work with my EDA tools?

    A: In general, Clash should work well with Xilinx and Intel FPAGs and their EDA tools — as development typically focuses on these vendors. Clash has also been successfully used on Microsemi (formerly Actel) SmartFusion 2 and Lattice Semiconductor iCE40 FPGAs, and some basic IP for these exist.

    For most toolchains, the default primitives supplied by Clash should work with minimal effort. If not, it is possible to call your vendor's library manually, or use a tool like Yosys to do mapping. It is also possible to consult QBayLogic Clash support for more assistance.


  • Q: Does Clash support Project IceStorm?

    A: The Verilog backend for Clash emits Verilog 2001, which is supported by Yosys. This means it can be placed and packed with arachne-pnr and icestorm. Additionally, Clash has some support for the Lattice Semiconductor iCE40 FPGA.


  • Q: Can Clash be used for ASIC designs, as well as FPGA designs?

    Clash can be used for ASIC designs, however the RTL produced by Clash may not be immediately suitable as it is largely platform agnostic. While this is not a problem for FPGAs, it can make developing ASICs more complicated as many ASIC vendors have different proprietary tool flows, with limited information available about their workings.

    If you are using Clash to develop for ASIC, and need assistance with getting your toolchain to work, you can contact QBayLogic Clash support for assistance.

Clash and Haskell

  • Q: Is Clash its own programming language, or is it Haskell?

    A: Clash is a programming language in its own right, complete with its own executable and standard library. Clash is also related to the Haskell programming language, and may be thought of as a dialect of Haskell for developing hardware. While the surface syntax and typing rules are the same, the semantics change as code progresses through the compilation pipeline.

    Due to the shared behavior in the early stages of the compiler, components from GHC (the most common Haskell compiler) are reused in the Clash compiler. This is how Clash achieves such high interoperability with existing Haskell projects.


  • Q: Clash has better inference for type level natural numbers than GHC. How is this possible?

    A: Clash's enhanced type checking functionality is due to the use of GHC compiler plugins, which can be used in any Haskell project. To enable these plugins, pass the following compiler flags to GHC:

    {-# OPTIONS_GHC -fplugin GHC.TypeLits.Normalise       #-}
    {-# OPTIONS_GHC -fplugin GHC.TypeLits.Extra.Solver    #-}
    {-# OPTIONS_GHC -fplugin GHC.TypeLits.KnownNat.Solver #-}
    

    These plugins come from the ghc-typelits-natnormalise, ghc-typelits-extra, and ghc-typelits-knownnat packages respectively, which are all available from Hackage and Stackage.


  • Q: Do I need to know Haskell in order to use Clash?

    A: As Clash is deeply integrated with Haskell, it is recommended that users have some familiarity with Haskell, or functional programming in general. Clash uses some advanced features of Haskell, and real-world designs will often want to leverage the existing Haskell ecosystem.

    For developers who are particularly familiar with either Haskell or hardware design, Clash should be relatively intuitive to use. Additionally, obvious mistakes with designs will be identified and reported due to the strong type system identifying mistakes at compile-time.

Clash and other HDLs

  • Q: Do I need to know existing RTL/HDL languages in order to use Clash?

    A: Clash currently outputs VHDL, Verilog, and SystemVerilog. While it's not necessary to understand these descriptions, you will need to some understanding of vendor tools to actually deploy it.


  • Q: What's the difference between Clash and Lava?

    A: Lava dialects (including the modern variant Blarney) are all embedded domain specific languages (EDSLs) inside Haskell. On top of that they use a so-called deep embedding to be able to transform a circuit description into a netlist (to subsequently output that as a VHDL/Verilog file). Clash on the other hand uses "standard" compiler techniques to create a netlist from the Haskell abstract syntax tree (AST). This "standard" compiler technique enables the following features not available in (Haskell-based) EDSLs:

    1. Clash allows the use of normal Haskell operations such as (==) on both the meta-level (how the program is structured/generated), and the object-level (the functionality of the program).
    2. Clash allows the use of regular Haskell syntax to model the concept of 'choice' at the object-level (the functionality of the program): if-expressions, guards, case, etc.
    3. Clash allows programmers to use native Haskell pattern matching.

    Basically, with Clash you can use regular Haskell to describe the behavior of the circuit, most importantly all of it's choice-constructs (case-expressions, guards, etc.). With an EDSL you are "limited" by the constructs of the DSL, making your circuit descriptions look less like regular Haskell functions.


  • Q: What's the difference between Clash and Chisel/Spinal/Migen/Hardcaml?

    A: The biggest difference between these toolchains and Clash is that Clash exists as a Haskell derivative, with a full synthesizing compiler to RTL — while Chisel/Spinal/Migen/Hardcaml exists as an embedding of hardware semantics inside Scala/Scala/Python/OCaml. Aside from the "host language" differences, this means that Chisel/Spinal/Migen/Hardcaml are conceptually closer to something like Lava/Blarney than to Clash. So within these languages you can only use the host language constructs to structure and compose the constructs of the EDSL, and you can't use host language constructs to describe the behavior of the circuit; i.e. you cannot use the host language's regular if-expression to model the concept of choice, but you have to use e.g. Chisel's when-function.

    Aside from the above, there is also a varying degree of native simulation and interactivity. In Clash you can evaluate/simulate any (sub-)component in the interactive interpreter for an immediate and localized design feedback loop. The only EDSLs that have a similar interactive interpreter for fast design feedback are the older variants of Lava. They used a so-called dual-embedding, where the EDSL primitives also contained a normal Haskell function which described their behavior, and so the composition of these primitives could be evaluated as a regular Haskell function.

    The other EDSLs all offer simulation, but there is a higher latency to get from a design to a simulation of a design, and they are not as interactive. Blarney emits Verilog, and you can then use a Verilog simulator to simulate the Blarney design. Spinal also emits Verilog, but it then uses Verilator to compile it to an object-file which is loaded back into Scala, allowing you to interact with your Spinal design from within Scala. Chisel is also not interpreted directly, instead, a Chisel description is "lowered" to FIRRTL where that FIRRTL description is then executed inside Scala by the FIRRTL interpreter. Migen works similarly to Chisel as far as the approach to simulation goes, although perhaps more direct: it directly interprets its own deep embedding data structure (its IR) to enable native simulation.

    All of this influences the style in which you write circuits and the creative process by which you come to a solution; the effects of this on the quality of results (QoR) and development time are, however, both hard to qualify and hard to quantify. That is, although all of these languages, both the EDSLs and Clash, enable full control over the QoR (i.e. you can get as many registers and as much logic as you intended), the way in which you get there can vary from problem domain to problem domain and person to person. If you have enough time, we encourage to try several of them and see which style is the most natural fit for you; if you're limited on time, we of course recommend that you just go with Clash ;-)

License

Copyright (c) 2012-2016, University of Twente, 2016-2019, Myrtle Software Ltd, 2017-2019, QBayLogic B.V., Google Inc. All rights reserved.

Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:

  1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
  2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

Clash as a Language

As Clash reuses parts of the GHC compiler for its frontend, the syntax and semantics should be familiar to Haskell programmers. For people unfamiliar with Haskell, there are many resources to learn the language, such as

Clash does make some use of more advanced features of GHC Haskell, which are exposed by GHC as language extensions. The extensions used by Clash are

Warning

Since GHC 8.6, the StarIsType extension is defined. This extension is explicitly turned off by Clash, meaning Data.Kind.Type must be used to refer to Haskell types.

Clash also enables some GHC plugins by default which improve the type inference for type level numbers. The plugins enabled by default are

Users are free to control the language extensions and GHC options with the normal OPTIONS_GHC and LANGUAGE pragmas in source files. For more information, see the GHC User's Guide.

Clash Prelude

Basic Types

The Clash prelude includes many different numeric types, which are used to safely define other types/functions. These include, but may not be limited to

  • Type level natural numbers (Nat), which allow numbers to be used in types. Conceptually, this is similar to const generics in C++.

    It is possible to have term level values which refer to a type level number. This is called SNat n (for singleton natural number). These are defined up to 1024 with the prefix "d" (e.g. d256).

  • Unsigned n and Signed n numbers with an arbitrary width (given as a type level natural number). These allow fixed-width arithmetic to be used on arbitrary numbers.

  • Index n provides natural numbers up to an arbitrary value (given as a type level natural number). These allow indexing into fixed width structures like Vec n a.

Another commonly used type is BitVector n. This provides a fixed size vector of Bit values which can be indexed, and used to perform unsigned integer arithmetic. Any type that can be marshaled to/from a BitVector n implements the BitPack class, which defines the conversion.

Note

It is also possible to derive instances of BitPack using Generic, by writing deriving (Generic, BitPack) in the type definition. This automatically determines how to do the conversion at compile-time.

More generally, there is a Vec n a type which allows collections of arbitrary values to be used. These vectors are tagged with their length, to prevent out of bounds access at compile-time.

Warning

The Vec n a type exports pattern synonyms for inserting at the left and right of a vector. The types of the Cons constructor and (:>) pattern are slightly different, and may behave differently in practice.

The Cons constructor has a more general type, allowing it to be used in some cases where the pattern cannot be used. However, this additional power comes at the cost of type inference. It is recommended that users use the (:>) pattern by default, and only use Cons when necessary.

Synthesis Domains

Synchronous circuits have a synthesis domain, which determines the behavior of things which can affect signals in the domain. Domains consist of

  • a name, which uniquely refers to the domain
  • the clock period in ps
  • the active edge of the clock
  • whether resets are synchronous (edge-sensitive) or not
  • whether the initial (power up) behavior is defined
  • whether resets are high or low polarity

The prelude provides some common domains, namely XilinxSystem and IntelSystem for the standard configurations of each vendor. There is also a generic domain, System, which can be used for vendor-agnostic purposes (e.g., writing a generic test bench). It is possible to define new synthesis domains for custom hardware using the createDomain function, which also defines the necessary instances for domains.

A value in a synchronous circuit is wrapped in the Signal dom a type, which specifies the synthesis domain and the type of value. Any function which needs access to a domain can use the constraint KnownDomain to extract configuration.

The default API exposed by the prelude is implicit with regards to clocks, reset lines and enable lines, as these can be determined at compile time. However, if they are needed the Clash.Explicit module contains explicit versions of the API which expose these directly in function arguments. It is also possible to use functions like exposeClockResetEnable to turn an implicitly defined function to an explicitly defined function.

State Machines

The Clash prelude contains combinators for two classical finite state machines which can be used to define synchronous circuits. The first of these is mealy, which encodes a Mealy machine. This is a machine specified by

  • A transition function of type state -> input -> (state, output)
  • An initial state
  • An input signal which can change at each cycle

Note

The Mealy machine is similar to the State monad, which Haskell programmers may already be familiar with. Practically speaking, the only difference is that this machine also has an input signal which is changed externally to the definition of the machine.

It is also possible to define a Moore machine using the moore function in the Clash prelude. This differs from the Mealy machine by providing output based on the previous state (as opposed to the newly calculated state), and is specified by

  • A transition function of type state -> input -> state
  • An output function of type state -> output
  • An initial state
  • An input signal which can change at each cycle

Sometimes, there may be multiple inputs/outputs needed for a machine. As machines only input and output a single signal, there is a way to combine and separate multiple signals. The Bundle class specifies how to convert between some type which is a signal of a product, and some type which is a product of signals, e.g.

bundle   :: (Signal dom a, Signal dom b) -> Signal dom (a, b)
unbundle :: Signal dom (a, b) -> (Signal dom a, Signal dom b)

There are combinators which can automatically perform this bundling and unbundling for you as required, called mealyB and mooreB. The Bundle class is already defined for many types, including tuples (up to 62 elements), Maybe a, Either a b and Vec n a.

RAM and ROM

The Clash prelude provides the ability to work with synchronous and asynchronous ROM, asynchronous RAM and synchronous Block RAM. The simplest of these are ROMs, which only allow indexing into a Vec n a of elements. ROM is defined using the functions in Clash.Prelude.ROM.

RAM is more complex, as it allows both reading and writing. The function to define a RAM takes in a signal for the address to read, and a signal for an optional address to update (bundled with the new value). At each cycle it outputs the value of the memory address read in the previous cycle. Asynchronous RAM is defined in Clash.Prelude.RAM.

An FPGA may include a block RAM, which is a larger memory structure and more suitable for some applications. Block RAM also has a synchronous read port, allowing memory access to be synchronized to a clock. Block RAM is used the same way as async RAM, allowing the two to be compared quickly. Block RAM is defined in Clash.Prelude.BlockRam.

Undefined Values

When working with hardware designs, there are times when undefined values may be encountered in simulation. Clash provides a custom exception type, XException, for cases when an undefined value is encountered. There are also many utility functions for working with exceptions, such as

  • errorX, which throws an XException
  • isX and hasX, which check for XExceptions when evaluating
  • maybeIsX and maybeHasX, which discard information about exceptions

There are also implementations of typical classes in Haskell which have been changed to work with undefined values. Currently these are

  • ShowX, which works like the Show class in Haskell. When an undefined value is encountered an "X" is printed. Show can still be used, but will throw an exception if an undefined value is encountered.
  • NFDataX, which works like the NFData class in the deepseq library. This allows evaluating values to normal form in code when XException may be present. NFData can still be used, but will bubble up exceptions if XException is encountered.

Clash Compiler Flags

--vhdl
Use the VHDL backend for code generation. This currently emits VHDL 1993 source which can be consumed by other tools.

--verilog
Use the Verilog backend for code generation. This currently emits Verilog 2001 source which can be consumed by other tools.

--systemverilog
Use the SystemVerilog backend for code generation. This currently emits SystemVerilog 2012 source which can be consumed by other tools.

-fclash-debug
Set the debugging mode for the compiler, exposing additional output. The available options are

  • DebugNone to show no debug messages
  • DebugSilent to test invariants and error if any are violated. This is implicitly enabled by any debug flag.
  • DebugFinal to show expressions after they have been completely normalized
  • DebugCount to count how often each transformation is applied
  • DebugName to show the names of transformations as they are applied
  • DebugTry to show names of tried and applied transformations
  • DebugApplied to show sub-expressions after they are rewritten
  • DebugAll to show all sub-expressions when a rewrite is attempted

Default: DebugNone

Note

This flag exists for backwards compatibility. It is now possible to set debugging flags individually with -fclash-debug-invariants, -fclash-debug-info and -fclash-debug-count-transformations.

-fclash-debug-invariants
Check invariants while debugging and print warnings/errors which may be useful, such as alerting when unexpected changes occur or when a transformation introduces free variables / shadowing.

-fclash-debug-info
Specify the information to show about individual transformations while debugging. From least to most information, these are

  • None to show no information
  • FinalTerm to show the final result of normalization
  • AppliedName to show the names of applied transformations
  • AppliedTerm to show the result of applied transformations
  • TryName to show the names of attempted transformations, as well as the result of any transformations which are applied
  • TryTerm to show the names and results of all transformations attempted whether they were applied or not

Default: None

-fclash-debug-count-transformations
Count the transformations that are applied and print a summary at the end of the normalization phase.

-fclash-debug-history[=FILENAME]
Saves all applied rewrites into FILENAME, for later analysis with the clash-term tool. When no filename is given it defaults to history.dat.

-fclash-debug-transformations
List the transformations that are to be debugged. This is given as a comma-separated list of transformations, e.g.

clash -fclash-debug-transformations inlineNonRep,topLet,appProp

Default: []

-fclash-debug-transformations-from=N
Only print debug output from applied transformation N and onwards.

clash -fclash-debug-transformations-from=21570

Default: 0

-fclash-debug-transformations-limit=N
Only print debug output for N applied transformations.

clash -fclash-debug-transformations-limit=12

Default: MAX_INT

-fclash-hdldir
Specify the directory that generated HDL is written into. For example

clash -fclash-hdldir build/hdl

will create a directory build/hdl

Default: Either vhdl, verilog, or systemverilog depending on the synthesis target.

-fclash-hdlsyn
Specify the HDL synthesis tool which will be used. Available options are Vivado, Quartus and Other, but some synonyms for these exist (Xilinx and ISE are synonyms for Vivado, Altera and Intel are synonyms for Quartus).

Default: Other

-fclash-no-cache
Don't reuse previously generated output from Clash, instead generating HDL from a clean state. While this leads to longer builds, it can be useful in development.

Warning

Previously this flag was called -fclash-nocache, however this is now deprecated.

Default: Cache generated HDL

-fclash-no-check-inaccessible-idirs
Check that all include directories (containing primitives) exist when running Clash. If any directory does not exist, an error is thrown.

Default: Check directories

-fclash-clear
Remove HDL directories before writing to them (if cache can't be used). By default, Clash will only write to non-empty directories if it can prove all files in it are generated by a previous run. This option applies to directories of the various top entities, i.e., the subdirectories made in the directory passed in with -fclash-hdldir.

Default: Clean before build

-fclash-no-prim-warn
Disable warnings for primitives that are annotated with warnAlways. This means warnings from annotations like

{-# ANN f (warnAlways "This primitive is dangerous") #-}

will not be shown when compiling.

Default: Show warnings

-fclash-spec-limit
Change the number of times a function can undergo specialization.

Default: 20

-fclash-inline-limit
Change the number of times a function f can undergo inlining inside some other function g. This prevents the size of g growing dramatically.

Default: 20

-fclash-inline-function-limit
Set the threshold for function size. Below this threshold a function is always inlined (if it is not recursive).

Default: 15

-fclash-inline-constant-limit
Set the threshold for constant size. Below this threshold constants are always inlined. A value of 0 inlines all constants.

Default: 0

-fclash-evaluator-fuel-limit
Set the threshold for unfolding potentially non-terminating bindings in the evaluator. A value of 0 only unfolds terminating bindings.

Default: 20

-fclash-intwidth
Set the bit width for the Int/Word/Integer types in the generated HDL. Clash simulation is not affected, and neither are BitPack instances. The only allowed values are 32 or 64.

Default: Machine word size (WORD_SIZE_IN_BITS)

-fclash-error-extra
Print additional information with compiler errors if it as available. If there is extra information and this flag is not enabled, a message will be printed suggesting this flag.

Default: False

-fclash-float-support
Enable support for floating point numbers. If this is disabled, Clash will not attempt to convert Float and Double values for hardware.

Default: False

-fclash-component-prefix
Prefix the names of generated HDl components with a string. For example a component foo would be called xcorp_foo if run with

clash -fclash-component-prefix "xcorp"

Default: ""

-fclash-old-inline-strategy
The new inlining strategy for Clash inlines all functions which are not marked with NOINLINE or a synthesize attribute. The old inlining strategy differed, attempting only to inline functions which were deemed "cheap". The old inlining strategy may be quicker in practice for some circuits.

Default: False

-fclash-no-escaped-identifiers
Disable extended identifiers, as used in some HDLs like VHDL to allow more flexibility with names. Clash will only generate basic identifiers if this is used.

Default: Escaped identifiers are allowed

-fclash-lower-case-basic-identifiers
Clash will only generate lower case basic identifiers if this is used. This affects places where the various HDLs only allow basic identifiers to be used, most notably module and file names.

Default: Disabled

-fclash-compile-ultra
Aggressively run the normalizer, potentially gaining much better runtime performance at the expense of compile time.

Default: False

-fclash-force-undefined{,0,1}
Set the value to use when an undefined value is inserted into generated HDL. This flag can be suffixed with either 0 or 1 to force use of that bit, or left without a suffix to use a HDL-specific default (e.g. x in Verilog).

Default: Disabled

-fclash-aggressive-x-optimization
Remove all undefined branches from case expressions, replacing them with another defined value in the expression. If only one branch is defined, the case expression is elided completely. If no branches are defined the entire expression is replaced with a call to errorX.

Implies: -fclash-aggressive-x-optimization-blackboxes

Default: False

-fclash-aggressive-x-optimization-blackboxes
Allow blackboxes to detect undefined values and change their behavior accordingly. For example, if register is used in combination with an undefined reset value, it will leave out the reset logic entirely. This flag is enabled when using -fclash-aggressive-x-optimization.

Default: False

-fclash-edalize
Generate metadata for use with Edalize. This generates edam.py files in all top entities with the configuration for building that entity. Users still need to edit this file to specify the EDA tool to use, and if necessary the device to target (for Quartus, Vivado etc.).

Default: False

-main-is
When using one of --vhdl, --verilog, or --systemverilog, this flag refers to the synthesis target. For example, running Clash with clash My.Module -main-is top --vhdl would synthesize My.Module.top.

-fclash-timescale-precision
Sets the second part of Verilog's timescale 100fs/100fs. E.g., setting this flag to 1fs would make Clash generate Verilog files with timescale 100fs/1fs as their header.

Default: 100fs

Synthesize annotations: controlling VHDL/(System)Verilog generation.

Synthesize annotations allow us to control hierarchy and naming aspects of the Clash compiler, specifically, they allow us to:

  • Assign names to entities (VHDL) / modules ((System)Verilog), and their ports.
  • Put generated HDL files of a logical (sub)entity in their own directory.
  • Use cached versions of generated HDL, i.e., prevent recompilation of (sub)entities that have not changed since the last run. Caching is based on a .manifest which is generated alongside the HDL; deleting this file means deleting the cache; changing this file will result in undefined behavior.

Functions with a Synthesize annotation must adhere to the following restrictions:

  • Although functions with a Synthesize annotation can of course depend on functions with another Synthesize annotation, they must not be mutually recursive.
  • Functions with a Synthesize annotation must be completely monomorphic and first-order, and cannot have any non-representable arguments or result.

Also take the following into account when using Synthesize annotations.

  • The Clash compiler is based on the GHC Haskell compiler, and the GHC machinery does not understand Synthesize annotations and it might subsequently decide to inline those functions. You should therefor also add a {-# OPAQUE f #-} pragma to the functions which you give a Synthesize functions.
  • Functions with a Synthesize annotation will not be specialized on constants.

Finally, the root module, the module which you pass as an argument to the Clash compiler must either have:

  • A function with a Synthesize annotation.
  • A function called topEntity.

You apply Synthesize annotations to functions using an ANN pragma:

{-# OPAQUE topEntity #-}
{-# ANN topEntity (Synthesize {t_name = ..., ...  }) #-}
topEntity x = ...

For example, given the following specification:

module Blinker where

import Clash.Signal
import Clash.Prelude
import Clash.Intel.ClockGen

createDomain vSystem{vName="DomInput", vPeriod=20000, vResetPolarity=ActiveLow}
createDomain vSystem{vName="Dom100", vPeriod=10000}

topEntity
  :: Clock DomInput
  -> Reset DomInput
  -> Signal Dom100 Bit
  -> Signal Dom100 (BitVector 8)
topEntity clk rst =
  exposeClockResetEnable (mealy blinkerT (1,False,0) . isRising 1) pllOut pllRst enableGen
 where
  (pllOut,pllRst) = altpllSync clk rst

blinkerT (leds,mode,cntr) key1R = ((ledsN,modeN,cntrN),leds)
 where
  -- clock frequency = 100e6  (100 MHz)
  -- led update rate = 333e-3 (every 333ms)
  cnt_max = maxBound :: Index 33300000 -- 100e6 * 333e-3

  cntrN | cntr == cnt_max = 0
        | otherwise       = cntr + 1

  modeN | key1R     = not mode
        | otherwise = mode

  ledsN | cntr == 0 = if mode then complement leds
                              else rotateL leds 1
        | otherwise = leds

The Clash compiler will normally generate the following topentity.vhdl file:

-- Automatically generated VHDL-93
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.NUMERIC_STD.ALL;
use IEEE.MATH_REAL.ALL;
use std.textio.all;
use work.all;
use work.Blinker_topEntity_types.all;

entity topEntity is
  port(-- clock
       clk    : in Blinker_topEntity_types.clk_DomInput;
       -- reset
       rst    : in Blinker_topEntity_types.rst_DomInput;
       eta    : in std_logic;
       result : out std_logic_vector(7 downto 0));
end;

architecture structural of topEntity is
 ...
end;

However, if we add the following Synthesize annotation in the file:

{-# OPAQUE topEntity #-}
{-# ANN topEntity
  (Synthesize
    { t_name   = "blinker"
    , t_inputs = [PortName "CLOCK_50", PortName "KEY0", PortName "KEY1"]
    , t_output = PortName "LED"
    }) #-}

The Clash compiler will generate the following blinker.vhdl file instead:

-- Automatically generated VHDL-93
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.NUMERIC_STD.ALL;
use IEEE.MATH_REAL.ALL;
use std.textio.all;
use work.all;
use work.blinker_types.all;

entity blinker is
  port(-- clock
       CLOCK_50 : in blinker_types.clk_DomInput;
       -- reset
       KEY0     : in blinker_types.rst_DomInput;
       KEY1     : in std_logic;
       LED      : out std_logic_vector(7 downto 0));
end;

architecture structural of blinker is
 ...
end;

Where we now have:

  • A top-level component that is called blinker.
  • Inputs and outputs that have a user-chosen name: CLOCK_50, KEY0, KEY1, LED, etc.

See the documentation of Synthesize for the meaning of all its fields.

User-defined primitives

There are times when you already have an existing piece of IP, or there are times where you need the HDL to have a specific shape so that the HDL synthesis tool can infer a specific component. In these specific cases you can resort to defining your own HDL primitives. Actually, most of the primitives in Clash are specified in the same way as you will read about in this section. There are perhaps 10 (at most) functions which are truly hard-coded into the Clash compiler. You can take a look at the files in https://github.com/clash-lang/clash-compiler/tree/master/clash-lib/prims/vhdl (or https://github.com/clash-lang/clash-compiler/tree/master/clash-lib/prims/verilog for the Verilog primitives or https://github.com/clash-lang/clash-compiler/tree/master/clash-lib/prims/systemverilog for the SystemVerilog primitives) if you want to know which functions are defined as "regular" primitives. The compiler looks for primitives in four locations:

  • The official install location: e.g.

    • $HOME/.stack/snapshots/x86_64-linux/<HASH>/share/<GHC_VERSION>/clash-lib-<VERSION>/prims/common
    • $HOME/.stack/snapshots/x86_64-linux/<HASH>/share/<GHC_VERSION>/clash-lib-<VERSION>/prims/commonverilog
    • $HOME/.stack/snapshots/x86_64-linux/<HASH>/share/<GHC_VERSION>/clash-lib-<VERSION>/prims/systemverilog
    • $HOME/.stack/snapshots/x86_64-linux/<HASH>/share/<GHC_VERSION>/clash-lib-<VERSION>/prims/verilog
    • $HOME/.stack/snapshots/x86_64-linux/<HASH>/share/<GHC_VERSION>/clash-lib-<VERSION>/prims/vhdl
  • Directories indicated by a Clash.Annotations.Primitive.Primitive annotation

  • The current directory (the location given by pwd)

  • The include directories specified on the command-line: -i<DIR>

Where redefined primitives in the current directory or include directories will overwrite those in the official install location. For now, files containing primitive definitions must have a .primitives.yaml file-extension.

Clash differentiates between two types of primitives, expression primitives and declaration primitives, corresponding to whether the primitive is an HDL expression or an HDL declaration. We will first explore expression primitives, using Signed multiplication (*) as an example. The Clash.Sized.Internal.Signed module specifies multiplication as follows:

(*#) :: KnownNat n => Signed n -> Signed n -> Signed n
(S a) *# (S b) = fromInteger_INLINE (a * b)
{-# OPAQUE (*#) #-}

For which the VHDL expression primitive is:

BlackBox:
  name: Clash.Sized.Internal.Signed.*#
  kind: Expression
  type: '(*#) :: KnownNat n => Signed n -> Signed n -> Signed n'
  template: resize(~ARG[1] * ~ARG[2], ~LIT[0])

The name of the primitive is the fully qualified name of the function you are creating the primitive for. Because we are creating an expression primitive the kind must be set to Expression. As the name suggest, it is a VHDL template, meaning that the compiler must fill in the holes heralded by the tilde (~). Here:

  • ~ARG[1] denotes the second argument given to the (*#) function, which corresponds to the LHS of the (*) operator.
  • ~ARG[2] denotes the third argument given to the (*#) function, which corresponds to the RHS of the (*) operator.
  • ~LIT[0] denotes the first argument given to the (*#) function, with the extra condition that it must be a LITeral. If for some reason this first argument does not turn out to be a literal then the compiler will raise an error. This first arguments corresponds to the KnownNat n class constraint.

An extensive list with all of the template holes will be given the end of this section. What we immediately notice is that class constraints are counted as normal arguments in the primitive definition. This is because these class constraints are actually represented by ordinary record types, with fields corresponding to the methods of the type class. In the above case, KnownNat is actually just like a newtype wrapper for Natural.

The second kind of primitive that we will explore is the declaration primitive. We will use blockRam# as an example, for which the Haskell/Clash code is:

{-# LANGUAGE BangPatterns #-}

module BlockRam where

import Clash.Explicit.Prelude
import Clash.Annotations.Primitive (hasBlackBox)
import Clash.Signal.Internal (Clock, Signal (..), (.&&.))
import Clash.Sized.Vector (Vec, toList)
import Clash.XException (defaultSeqX)

import qualified Data.Vector as V
import GHC.Stack (HasCallStack, withFrozenCallStack)

blockRam#
  :: ( KnownDomain dom
     , HasCallStack
     , NFDataX a )
  => Clock dom           -- ^ Clock to synchronize to
  -> Enable dom          -- ^ Global enable
  -> Vec n a             -- ^ Initial content of the BRAM, also
                         -- determines the size, @n@, of the BRAM.
                         --
                         -- __NB__: __MUST__ be a constant.
  -> Signal dom Int      -- ^ Read address @r@
  -> Signal dom Bool     -- ^ Write enable
  -> Signal dom Int      -- ^ Write address @w@
  -> Signal dom a        -- ^ Value to write (at address @w@)
  -> Signal dom a        -- ^ Value of the BRAM at address @r@ from
                         -- the previous clock cycle
blockRam# (Clock _) gen content rd wen =
  go
    (V.fromList (toList content))
    (withFrozenCallStack (deepErrorX "blockRam: intial value undefined"))
    (fromEnable gen)
    rd
    (fromEnable gen .&&. wen)
 where
  go !ram o ret@(~(re :- res)) rt@(~(r :- rs)) et@(~(e :- en)) wt@(~(w :- wr)) dt@(~(d :- din)) =
    let ram' = d `defaultSeqX` upd ram e (fromEnum w) d
        o'   = if re then ram V.! r else o
    in  o `seqX` o :- (ret `seq` rt `seq` et `seq` wt `seq` dt `seq` go ram' o' res rs en wr din)

  upd ram we waddr d = case maybeIsX we of
    Nothing -> case maybeIsX waddr of
      Nothing -> V.map (const (seq waddr d)) ram
      Just wa -> ram V.// [(wa,d)]
    Just True -> case maybeIsX waddr of
      Nothing -> V.map (const (seq waddr d)) ram
      Just wa -> ram V.// [(wa,d)]
    _ -> ram
{-# OPAQUE blockRam# #-}
{-# ANN blockRam# hasBlackBox #-}

And for which the declaration primitive is:

BlackBox:
  name: Clash.Explicit.BlockRam.blockRam#
  kind: Declaration
  type: |-
    blockRam#
      :: ( KnownDomain dom        ARG[0]
         , HasCallStack  --       ARG[1]
         , NFDataX a )   --       ARG[2]
      => Clock dom       -- clk,  ARG[3]
      -> Enable dom      -- en,   ARG[4]
      -> Vec n a         -- init, ARG[5]
      -> Signal dom Int  -- rd,   ARG[6]
      -> Signal dom Bool -- wren, ARG[7]
      -> Signal dom Int  -- wr,   ARG[8]
      -> Signal dom a    -- din,  ARG[9]
      -> Signal dom a
  template: |-
    -- blockRam begin
    ~GENSYM[~RESULT_blockRam][1] : block
      signal ~GENSYM[~RESULT_RAM][2] : ~TYP[5] := ~CONST[5];
      signal ~GENSYM[rd][4]  : integer range 0 to ~LENGTH[~TYP[5]] - 1;
      signal ~GENSYM[wr][5]  : integer range 0 to ~LENGTH[~TYP[5]] - 1;
    begin
      ~SYM[4] <= to_integer(~ARG[6])
      -- pragma translate_off
                    mod ~LENGTH[~TYP[5]]
      -- pragma translate_on
                    ;
      ~SYM[5] <= to_integer(~ARG[8])
      -- pragma translate_off
                    mod ~LENGTH[~TYP[5]]
      -- pragma translate_on
                    ;
    ~IF ~VIVADO ~THEN
      ~SYM[6] : process(~ARG[3])
      begin
        if ~IF~ACTIVEEDGE[Rising][0]~THENrising_edge~ELSEfalling_edge~FI(~ARG[3]) then
          if ~ARG[7] ~IF ~ISACTIVEENABLE[4] ~THEN and ~ARG[4] ~ELSE ~FI then
            ~SYM[2](~SYM[5]) <= ~TOBV[~ARG[9]][~TYP[9]];
          end if;
          ~RESULT <= fromSLV(~SYM[2](~SYM[4]))
          -- pragma translate_off
          after 1 ps
          -- pragma translate_on
          ;
        end if;
      end process; ~ELSE
      ~SYM[6] : process(~ARG[3])
      begin
        if ~IF~ACTIVEEDGE[Rising][0]~THENrising_edge~ELSEfalling_edge~FI(~ARG[3]) then
          if ~ARG[7] ~IF ~ISACTIVEENABLE[4] ~THEN and ~ARG[4] ~ELSE ~FI then
            ~SYM[2](~SYM[5]) <= ~ARG[9];
          end if;
          ~RESULT <= ~SYM[2](~SYM[4])
          -- pragma translate_off
          after 1 ps
          -- pragma translate_on
          ;
        end if;
      end process; ~FI
    end block;
    --end blockRam

Again, the name of the primitive is the fully qualified name of the function you are creating the primitive for. Because we are creating a declaration primitive the kind must be set to Declaration. Instead of discussing what the individual template holes mean in the above context, we will instead just give a general listing of the available template holes:

  • ~RESULT: Signal to which the result of a primitive must be assigned to. NB: Only used in a declaration primitive.
  • ~ARG[N]: (N+1)'th argument to the function.
  • ~CONST[N]: (N+1)'th argument to the function. Like ~ARG, but Clash will try to reduce this to a literal, even if it would otherwise consider it too expensive. And if Clash fails to reduce this argument to a literal it will produce an error.
  • ~LIT[N]: (N+1)'th argument to the function. Like ~CONST but values are rendered as a bare literals, without any size or type annotations. This only works for numeric types, and not for BitVector.
  • ~TYP[N]: VHDL type of the (N+1)'th argument.
  • ~TYPO: VHDL type of the result.
  • ~TYPM[N]: VHDL typename of the (N+1)'th argument; used in type qualification.
  • ~TYPMO: VHDL typename of the result; used in type qualification.
  • ~ERROR[N]: Error value for the VHDL type of the (N+1)'th argument.
  • ~ERRORO: Error value for the VHDL type of the result.
  • ~GENSYM[<NAME>][N]: Create a unique name, trying to stay as close to the given <NAME> as possible. This unique symbol can be referred to in other places using ~SYM[N].
  • ~SYM[N]: a reference to the unique symbol created by ~GENSYM[<NAME>][N].
  • ~SIGD[<HOLE>][N]: Create a signal declaration, using <HOLE> as the name of the signal, and the type of the (N+1)'th argument.
  • ~SIGDO[<HOLE>]: Create a signal declaration, using <HOLE> as the name of the signal, and the type of the result.
  • ~TYPEL[<HOLE>]: The element type of the vector type represented by <HOLE>. The content of <HOLE> must either be: ~TYP[N], ~TYPO, or ~TYPEL[<HOLE>].
  • ~COMPNAME: The name of the component in which the primitive is instantiated.
  • ~LENGTH[<HOLE>]: The vector length of the type represented by <HOLE>.
  • ~DEPTH[<HOLE>]: The tree depth of the type represented by <HOLE>. The content of <HOLE> must either be: ~TYP[N], ~TYPO, or ~TYPEL[<HOLE>].
  • ~SIZE[<HOLE>]: The number of bits needed to encode the type represented by <HOLE>. The content of <HOLE> must either be: ~TYP[N], ~TYPO, or ~TYPEL[<HOLE>].
  • ~IF <CONDITION> ~THEN <THEN> ~ELSE <ELSE> ~FI: renders the <ELSE> part when <CONDITION> evaluates to 0, and renders the <THEN> in all other cases. Valid <CONDITION>s are ~LENGTH[<HOLE>], ~SIZE[<HOLE>], ~CMPLE[<HOLE1>][<HOLE2>], ~DEPTH[<HOLE>], ~VIVADO, ~IW64, ~ISLIT[N], ~ISVAR[N], ~ISACTIVEENABLE[N], ~ISSYNC[N], and ~AND[<HOLE1>,<HOLE2>,..].
  • ~VIVADO: 1 when Clash compiler is invoked with the -fclash-hdlsyn Vivado (or Xilinx or ISE) flag. To be used with in an ~IF .. ~THEN .. ~ELSE .. ~FI statement.
  • ~CMPLE[<HOLE1>][<HOLE2>]: 1 when <HOLE1> <= <HOLE2>, otherwise 0
  • ~IW64: 1 when Int/Word/Integer types are represented with 64 bits in HDL. 0 when they're represented by 32 bits.
  • ~TOBV[<HOLE>][<TYPE>]: create conversion code that so that the expression in <HOLE> is converted to a bit vector (std_logic_vector). The <TYPE> hole indicates the type of the expression and must be either ~TYP[N], ~TYPO, or ~TYPEL[<HOLE>].
  • ~FROMBV[<HOLE>][<TYPE>]: create conversion code that so that the expression in <HOLE>, which has a bit vector (std_logic_vector) type, is converted to type indicated by <TYPE>. The <TYPE> hole must be either ~TYP[N], ~TYPO, or ~TYPEL[<HOLE>].
  • ~INCLUDENAME[N]: the generated name of the N'th included component.
  • ~FILE[<HOLE>]: The argument mentioned in <HOLE> is a file which must be copied to the location of the generated HDL.
  • ~GENERATE: Verilog: create a generate statement, except when already in a generate context.
  • ~ENDGENERATE: Verilog: create an endgenerate statement, except when already in a generate context.
  • ~ISLIT[N]: Is the (N+1)'th argument to the function a literal.
  • ~ISVAR[N]: Is the (N+1)'th argument to the function explicitly not a literal.
  • ~ISSCALAR[N]: Is the (N+1)'th argument to the function a scalar. Note that this means different things for different HDLs. In (System)Verilog only Bit and Bool are considered scalar. In VHDL, in addition to those two, enumeration types and integers are considered scalar.
  • ~TAG[N]: Name of given domain. Errors when called on an argument which is not a KnownDomain, Reset, or Clock.
  • ~PERIOD[N]: Clock period of given domain. Errors when called on an argument which is not a Clock, Reset, KnownDomain or KnownConf.
  • ~ISACTIVEENABLE[N]: Is the (N+1)'th argument an Enable line not set to a constant True.
  • ~ISSYNC[N]: Does synthesis domain at the (N+1)'th argument have synchronous resets. Errors when called on an argument which is not a Reset, Clock, Enable, KnownDomain or KnownConf.
  • ~ISINITDEFINED[N]: Does synthesis domain at the (N+1)'th argument have defined initial values. Errors when called on an argument which is not a Clock, Reset, Enable, KnownDomain or KnownConf.
  • ~ACTIVEEDGE[edge][N]: Does synthesis domain at the (N+1)'th argument respond to edge. edge must be one of Falling or Rising. Errors when called on an argument which is not a Clock, Reset, Enable, KnownDomain or KnownConf.
  • ~AND[<HOLE1>,<HOLE2>,..]: Logically and the conditions in the <HOLE>'s
  • ~VAR[<NAME>][N]: Like ~ARG[N] but binds the argument to a variable named NAME. The <NAME> can be left blank, then Clash will come up with a (unique) name.
  • ~VARS[N]: VHDL: Return the variables at the (N+1)'th argument.
  • ~NAME[N]: Render the (N+1)'th string literal argument as an identifier instead of a string literal. Fails when the (N+1)'th argument is not a string literal.
  • ~DEVNULL[<HOLE>]: Render all dependencies of <HOLE>, but disregard direct output.
  • ~REPEAT[<HOLE>][N]: Repeat literal value of <HOLE> a total of N times.
  • ~TEMPLATE[<HOLE1>][<HOLE2>]: Render a file <HOLE1> with contents <HOLE2>.

Some final remarks to end this section: HDL primitives are there to instruct the Clash compiler to use the given HDL template, instead of trying to do normal synthesis. As a consequence you can use constructs inside the Haskell definitions that are normally not synthesizable by the Clash compiler. However, VHDL primitives do not give us co-simulation: where you would be able to simulate VHDL and Haskell in a single environment. If you still want to simulate your design in Haskell, you will have to describe, in a cycle- and bit-accurate way, the behavior of that (potentially complex) IP you are trying to include in your design.

Verilog examples

For those who are interested, the equivalent Verilog primitives are:

BlackBox:
  name: Clash.Sized.Internal.Signed.*#
  kind: Expression
  type: '(*#) :: KnownNat n => Signed n -> Signed n -> Signed n'
  template: ~ARG[1] * ~ARG[2]

and

BlackBox:
  name: Clash.Explicit.BlockRam.blockRam#
  kind: Declaration
  outputUsage: NonBlocking
  type: |-
    blockRam#
      :: ( KnownDomain dom        ARG[0]
         , HasCallStack  --       ARG[1]
         , NFDataX a )   --       ARG[2]
      => Clock dom       -- clk,  ARG[3]
      => Enable dom      -- en,   ARG[4]
      -> Vec n a         -- init, ARG[5]
      -> Signal dom Int  -- rd,   ARG[6]
      -> Signal dom Bool -- wren, ARG[7]
      -> Signal dom Int  -- wr,   ARG[8]
      -> Signal dom a    -- din,  ARG[9]
      -> Signal dom a
  template: |-
    // blockRam begin
    reg ~TYPO ~GENSYM[~RESULT_RAM][1] [0:~LENGTH[~TYP[5]]-1];
    reg ~TYP[5] ~GENSYM[ram_init][3];
    integer ~GENSYM[i][4];
    initial begin
      ~SYM[3] = ~CONST[5];
      for (~SYM[4]=0; ~SYM[4] < ~LENGTH[~TYP[5]]; ~SYM[4] = ~SYM[4] + 1) begin
        ~SYM[1][~LENGTH[~TYP[5]]-1-~SYM[4]] = ~SYM[3][~SYM[4]*~SIZE[~TYPO]+:~SIZE[~TYPO]];
      end
    end
    ~IF ~ISACTIVEENABLE[4] ~THEN
    always @(~IF~ACTIVEEDGE[Rising][0]~THENposedge~ELSEnegedge~FI ~ARG[3]) begin : ~GENSYM[~RESULT_blockRam][5]~IF ~VIVADO ~THEN
      if (~ARG[4]) begin
        if (~ARG[7]) begin
          ~SYM[1][~ARG[8]] <= ~ARG[9];
        end
        ~RESULT <= ~SYM[1][~ARG[6]];
      end~ELSE
      if (~ARG[7] & ~ARG[4]) begin
        ~SYM[1][~ARG[8]] <= ~ARG[9];
      end
      if (~ARG[4]) begin
        ~RESULT <= ~SYM[1][~ARG[6]];
      end~FI
    end~ELSE
    always @(~IF~ACTIVEEDGE[Rising][0]~THENposedge~ELSEnegedge~FI ~ARG[3]) begin : ~SYM[5]
      if (~ARG[7]) begin
        ~SYM[1][~ARG[8]] <= ~ARG[9];
      end
      ~RESULT <= ~SYM[1][~ARG[6]];
    end~FI
    // blockRam end

SystemVerilog examples

And the equivalent SystemVerilog primitives are:

BlackBox:
  name: Clash.Sized.Internal.Signed.*#
  kind: Expression
  type: '(*#) :: KnownNat n => Signed n -> Signed n -> Signed n'
  template: ~ARG[1] * ~ARG[2]

and

BlackBox:
  name: Clash.Explicit.BlockRam.blockRam#
  kind: Declaration
  type: |-
    blockRam#
      :: ( KnownDomain dom        ARG[0]
         , HasCallStack  --       ARG[1]
         , NFDataX a )   --       ARG[2]
      => Clock dom       -- clk,  ARG[3]
      -> Enable dom      -- en,   ARG[4]
      -> Vec n a         -- init, ARG[5]
      -> Signal dom Int  -- rd,   ARG[6]
      -> Signal dom Bool -- wren, ARG[7]
      -> Signal dom Int  -- wr,   ARG[8]
      -> Signal dom a    -- din,  ARG[9]
      -> Signal dom a
  template: |-
    // blockRam begin
    ~SIGD[~GENSYM[RAM][1]][5];
    logic [~SIZE[~TYP[9]]-1:0] ~GENSYM[~RESULT_q][2];
    initial begin
      ~SYM[1] = ~CONST[5];
    end~IF ~ISACTIVEENABLE[4] ~THEN
    always @(~IF~ACTIVEEDGE[Rising][0]~THENposedge~ELSEnegedge~FI ~ARG[3]) begin : ~GENSYM[~COMPNAME_blockRam][3]~IF ~VIVADO ~THEN
      if (~ARG[4]) begin
        if (~ARG[7]) begin
          ~SYM[1][~ARG[8]] <= ~TOBV[~ARG[9]][~TYP[9]];
        end
        ~SYM[2] <= ~SYM[1][~ARG[6]];
      end~ELSE
      if (~ARG[7] & ~ARG[4]) begin
        ~SYM[1][~ARG[8]] <= ~TOBV[~ARG[9]][~TYP[9]];
      end
      if (~ARG[4]) begin
        ~SYM[2] <= ~SYM[1][~ARG[6]];
      end~FI
    end~ELSE
    always @(~IF~ACTIVEEDGE[Rising][0]~THENposedge~ELSEnegedge~FI ~ARG[3]) begin : ~SYM[3]
      if (~ARG[7]) begin
        ~SYM[1][~ARG[8]] <= ~TOBV[~ARG[9]][~TYP[9]];
      end
      ~SYM[2] <= ~SYM[1][~ARG[6]];
    end~FI
    assign ~RESULT = ~FROMBV[~SYM[2]][~TYP[9]];
    // blockRam end

Troubleshooting

A list of often encountered errors and their solutions:

  • Type error: Couldn't match expected type Signal dom (a,b) with actual type (Signal dom a, Signal dom b):

    Signals of product types and product types of signals are isomorphic due to synchronisity principle, but are not (structurally) equal. Tuples are a product type. Use the bundle function to convert from a product type to the signal type. So if your code which gives the error looks like:

    ... = f a b (c,d)
    

    add the bundle function like so:

    ... = f a b (bundle (c,d))
    

    Product types supported by bundle are:

    • All tuples up to and including 62-tuples (GHC limit)
    • The Vector type
  • Type error: Couldn't match expected type (Signal dom a, Signal dom b) with actual type Signal dom (a,b):

    Product types of signals and signals of product types are isomorphic due to synchronicity principle, but are not (structurally) equal. Tuples are a product type. Use the unbundle function to convert from a signal type to the product type. So if your code which gives the error looks like:

    (c,d) = f a b
    

    add the unbundle function like so:

    (c,d) = unbundle (f a b)
    

    Product types supported by unbundle are:

    • All tuples up to and including 62-tuples (GHC limit)
    • The Vector type
  • Clash.Netlist(..): Not in normal form: <REASON>: <EXPR>:

    A function could not be transformed into the expected normal form. This usually means one of the following:

    • The topEntity has higher-order arguments, or a higher-order result.
    • You are using types which cannot be represented in hardware.

    The solution for all the above listed reasons is quite simple: remove them. That is, make sure that the topEntity is completely monomorphic and first-order. Also remove any variables and constants/literals that have a non-representable type; see Limitations of Clash to find out which types are not representable.

  • Clash.Normalize(..): Clash can only normalize monomorphic functions, but this is polymorphic:

    If this happens for a topEntity or something with a Synthesize annotation, add a monomorphic type signature. Non topEntites should be type-specialized by clash automatically, if not please report this as a bug. But adding a monomorphic type signature should still help (when possible).

  • Clash.Normalize(..): Expr belonging to bndr: <FUNCTION> remains recursive after normalization:

    • If you actually wrote a recursive function, rewrite it to a non-recursive one using e.g. one of the higher-order functions in Clash.Sized.Vector

    • You defined a recursively defined value, but left it polymorphic:

    topEntity x y = acc
      where
        acc = register 3 (acc + x * y)
    

    The above function, works for any number-like type. This means that acc is a recursively defined polymorphic value. Adding a monomorphic type annotation makes the error go away:

    topEntity
      :: SystemClockResetEnable
      => Signal System (Signed 8)
      -> Signal System (Signed 8)
      -> Signal System (Signed 8)
    topEntity x y = acc
      where
        acc = register 3 (acc + x * y)
    
  • Clash.Normalize.Transformations(..): InlineNonRep: <FUNCTION> already inlined 100 times in:<FUNCTION>, <TYPE>:

    You left the topEntity function polymorphic or higher-order: use :i topEntity to check if the type is indeed polymorphic or higher-order. If it is, add a monomorphic type signature, and/or supply higher-order arguments.

  • <*** Exception: <<loop>> or "blinking cursor"

    You are using value-recursion, but one of the Vector functions that you are using is too strict in one of the recursive arguments. For example:

    -- Bubble sort for 1 iteration
    sortV xs = map fst sorted :< (snd (last sorted))
     where
       lefts  = head xs :> map snd (init sorted)
       rights = tail xs
       sorted = zipWith compareSwapL lefts rights
    
    -- Compare and swap
    compareSwapL a b = if a < b then (a,b) else (b,a)
    

    Will not terminate because zipWith is too strict in its second argument.

    In this case, adding lazyV on zipWiths second argument:

    sortVL xs = map fst sorted :< (snd (last sorted))
     where
       lefts  = head xs :> map snd (init sorted)
       rights = tail xs
       sorted = zipWith compareSwapL (lazyV lefts) rights
    

    Results in a successful computation:

    clashi> sortVL (4 :> 1 :> 2 :> 3 :> Nil)
    1 :> 2 :> 3 :> 4 :> Nil
    

Limitations of the compiler

Here is a list of Haskell features for which the Clash compiler has only limited support (for now):

  • Recursively defined functions

    At first hand, it seems rather bad that a compiler for a functional language cannot synthesize recursively defined functions to circuits. However, when viewing your functions as a structural specification of a circuit, this feature of the Clash compiler makes sense. Also, only certain types of recursion are considered non-synthesizable; recursively defined values are for example synthesizable: they are (often) synthesized to feedback loops.

    Let us distinguish between three variants of recursion:

    • Dynamic data-dependent recursion

      As demonstrated in this definition of a function that calculates the n'th Fibbonacci number:

      fibR 0 = 0
      fibR 1 = 1
      fibR n = fibR (n-1) + fibR (n-2)
      

      To get the first 10 numbers, we do the following:

      >>> import qualified Data.List as L
      >>> L.map fibR [0..9]
      [0,1,1,2,3,5,8,13,21,34]
      

      The fibR function is not synthesizable by the Clash compiler, because, when we take a structural view, fibR describes an infinitely deep structure.

      In principle, descriptions like the above could be synthesized to a circuit, but it would have to be a sequential circuit. Where the most general synthesis would then require a stack. Such a synthesis approach is also known as behavioral synthesis, something which the Clash compiler simply does not do. One reason that Clash does not do this is because it does not fit the paradigm that only functions working on values of type Signal result in sequential circuits, and all other (non higher-order) functions result in combinational circuits. This paradigm gives the designer the most straightforward mapping from the original Haskell description to generated circuit, and thus the greatest control over the eventual size of the circuit and longest propagation delay.

    • Value-recursion

      As demonstrated in this definition of a function that calculates the n'th Fibbonaci number on the n'th clock cycle:

      fibS :: SystemClockResetEnable => Signal System (Unsigned 64)
      fibS = r
          where r = register 0 r + register 0 (register 1 r)
      

      To get the first 10 numbers, we do the following:

      >>> sampleN @System 11 fibS
      [0,0,1,1,2,3,5,8,13,21,34]
      

      Unlike the fibR function, the above fibS function is synthesizable by the Clash compiler. Where the recursively defined (non-function) value r is synthesized to a feedback loop containing three registers and one adder.

      Note that not all recursively defined values result in a feedback loop. An example that uses recursively defined values which does not result in a feedback loop is the following function that performs one iteration of bubble sort:

      sortV xs = map fst sorted :< (snd (last sorted))
        where
          lefts  = head xs :> map snd (init sorted)
          rights = tail xs
          sorted = zipWith compareAndSwap (lazyV lefts) rights
      
      compareAndSwap a b = if a < b then (a,b) else (b,a)
      

      Where we can clearly see that lefts and sorted are defined in terms of each other. Also the above sortV function is synthesizable.

    • Static/Structure-dependent recursion

      Static, or, structure-dependent recursion is a rather vague concept. What we mean by this concept are recursive definitions where a user can sensibly imagine that the recursive definition can be completely unfolded (all recursion is eliminated) at compile-time in a finite amount of time.

      Such definitions would e.g. be:

      mapV :: (a -> b) -> Vec n a -> Vec n b
      mapV _ Nil         = Nil
      mapV f (Cons x xs) = Cons (f x) (mapV f xs)
      
      topEntity :: Vec 4 Int -> Vec 4 Int
      topEntity = mapV (+1)
      

      Where one can imagine that a compiler can unroll the definition of mapV four times, knowing that the topEntity function applies mapV to a Vec of length 4. Sadly, the compile-time evaluation mechanisms in the Clash compiler are very poor, and a user-defined function such as the mapV function defined above, is currently not synthesizable. We do plan to add support for this in the future. In the mean time, this poor support for user-defined recursive functions is amortized by the fact that the Clash compiler has built-in support for the higher-order functions defined in Clash.Sized.Vector. Most regular design patterns often encountered in circuit design are captured by the higher-order functions in Clash.Sized.Vector.

  • Recursive datatypes

    The Clash compiler needs to be able to determine a bit-size for any value that will be represented in the eventual circuit. More specifically, we need to know the maximum number of bits needed to represent a value. While this is trivial for values of the elementary types, sum types, and product types, putting a fixed upper bound on recursive types is not (always) feasible. This means that the ubiquitous list type is unsupported! The only recursive types that are currently supported by the Clash compiler is the Vector and RTree types, for which the compiler has hard-coded knowledge.

    For "easy" Vector literals you should use Template Haskell splices and the listToVecTH meta-function.

  • GADTs

    Clash has experimental support for GADTs. Similar to recursive types, Clash cannot determine bit-sizes of GADTs. Notable exceptions to this rule are Vec and RTree. You can still use your own GADTs, as long as they can be removed through static analysis. For example, the following case will be optimized away and is therefore fine to use:

    x =
      case resetKind @System of
        SAsynchronous -> 'a'
        SSynchronous -> 'b'
    
  • Floating point types

    There is no support for the Float and Double types, if you need numbers with a fractional part you can use the Fixed point type.

    As to why there is no support for these floating point types:

    1. In order to achieve reasonable operating frequencies, arithmetic circuits for floating point data types must be pipelined.

    2. Haskell's primitive arithmetic operators on floating point data types, such as plusFloat#

      plusFloat# :: Float# -> Float# -> Float#
      

      which underlie Float's Num instance, must be implemented as purely combinational circuits according to their type. Remember, sequential circuits operate on values of type Signal dom a.

    Although it is possible to implement purely combinational (not pipelined) arithmetic circuits for floating point data types, the circuit would be unreasonable slow. So, without synthesis possibilities for the basic arithmetic operations, there is no point in supporting the floating point data types.

  • Haskell primitive types

    Only the following primitive Haskell types are supported:

    • Integer
    • Int
    • Int8
    • Int16
    • Int32
    • Int64 (not available when compiling with -fclash-intwidth=32 on a 64-bit machine)
    • Word
    • Word8
    • Word16
    • Word32
    • Word64 (not available when compiling with -fclash-intwidth=32 on a 64-bit machine)
    • Char

    There are several aspects of which you should take note:

    • Int and Word are represented by the same number of bits as is native for the architecture of the computer on which the Clash compiler is executed. This means that if you are working on a 64-bit machine, Int and Word will be 64-bit. This might be problematic when you are working in a team, and one designer has a 32-bit machine, and the other has a 64-bit machine. In general, you should be avoiding Int in such cases, but as a band-aid solution, you can force the Clash compiler to use a specific bit-width for Int and Word using the -fclash-intwidth=N flag, where N must either be 32 or 64.

    • When you use the -fclash-intwidth=32 flag on a 64-bit machine, the Word64 and Int64 types cannot be translated. This restriction does not apply to the other three combinations of -fclash-intwidth flag and machine type.

    • The translation of Integer is not meaning-preserving. Integer in Haskell is an arbitrary precision integer, something that cannot be represented in a statically known number of bits. In the Clash compiler, we chose to represent Integer by the same number of bits as we do for Int and Word. As you have read in a previous bullet point, this number of bits is either 32 or 64, depending on the architecture of the machine the Clash compiler is running on, or the setting of the -fclash-intwidth flag.

      Consequently, you should use Integer with due diligence; be especially careful when using fromIntegral as it does a conversion via Integer. For example:

      signedToUnsigned :: Signed 128 -> Unsigned 128
      signedToUnsigned = fromIntegral
      

      can either lose the top 64 or 96 bits depending on whether Integer is represented by 64 or 32 bits. Instead, when doing such conversions, you should use bitCoerce:

      signedToUnsigned :: Signed 128 -> Unsigned 128
      signedToUnsigned = bitCoerce
      
  • Side-effects: IO, ST, etc.

    There is no support for side-effecting computations such as those in the IO or ST monad. There is also no support for Haskell's FFI.

Hacking on the Clash Compiler

Prerequisites

Hacking on Clash requires more dependencies than simply running Clash. The test suite requires having a tool available to synthesize any backend being tested. This means you need:

  • ghdl installed to test VHDL
  • iverilog installed to test Verilog
  • ModelSim installed to test SystemVerilog
  • SymbiYosys and Z3 installed to test Verilog and SystemVerilog

Get Clash from source

Get the source code using Git and enter the cloned directory:

git clone git@github.com:clash-lang/clash-compiler.git

# Alternatively, if you haven't setup SSH keys with GitHub:
# git clone https://github.com/clash-lang/clash-compiler.git

cd clash-compiler

To check out a released version, use:

git checkout v1.2.3

To checkout a release branch use:

git checkout 1.2

Note that release branches might contain non-released patches.

Cabal

To use Cabal you need both Cabal and GHC installed on your system. For Linux and MacOS users we recommend using ghcup. Windows users are recommended to use the Haskell Platform.

To run clash use:

cabal v2-run --write-ghc-environment-files=always -- clash

If this fails, make sure you've got an up-to-date package index:

cabal update

Stack

Install Stack and run:

stack run -- clash

Nix

Or use Nix to get a shell with the clash and clashi binaries on your PATH:

nix-shell

Subprojects

The Clash compiler consists of different cabal libraries, which together provide a complete compiler. Primarily, this consists of

clash-ghc

The frontend of the compiler, using parts of the GHC frontend. This provides the ability to load modules, translate GHC Core to Clash Core, and implements the clash and clashi executables.

A lot of the code in this library is separated by the version of GHC it works with. For example, src-bin-9.0 is specific to GHC 9.0.x.

clash-lib

The backend of the compiler, exposed as a library. This is the largest library in the project, and includes the various ASTs (e.g. Core, Netlist), normalization, code generation, and primitives / black boxes.

clash-prelude

The standard library for Clash as a language. This includes anything that is used to develop hardware in Clash, such as Signals, Clocks and combinators for common forms of state machine.

The clash-prelude library also re-exports parts of the Haskell base library, allowing circuit designs to reuse common functions and definitions.

The repository also contains other libraries. These either provide additional functionality which is not required, or are not yet production-ready. These are

clash-cosim

Co-simulation for Clash, allowing Verilog to be run inline as though it were a normal Haskell function. This provides a QuasiQuoter for use in Haskell.

Warning

This library is very experimental, and is not guaranteed to work with the most recent development version of Clash.

clash-term

A development tool for analyzing how the normalizer in clash-lib affects the core of a particular design. It allows the result of each different optimizer pass to be seen for debugging purposes.

clash-lib-hedgehog

Hedgehog Generators for clash-lib.

clash-prelude-hedgehog

Hedgehog Generators for clash-prelude.