Hardcaml: writing hardware in OCaml and simulating it before you synthesise
Hardcaml is an OCaml library for designing hardware.
At a glance
- What is it?
- Hardcaml is an OCaml library for designing hardware, with a simulation backend and a path down to Verilog or VHDL, so a design can be written, simulated and synthesised in one language. The judgement is that the type system is the real argument for it, and that the ecosystem of satellite libraries rather than the core library is where a project's actual timeline lives.
- Who is it for?
- Hardcaml fits a team with an OCaml background that wants to write hardware with the same abstractions it uses for software, needs the same design to be simulatable and synthesizable without a rewrite, and values type-level safety over a conventional HDL.
- Can I use it commercially?
- Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 105 days ago.
- What is it written in?
- Mainly OCaml, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 22, 2026, and from our analysis. They are not legal advice.
Editorial analysis
The argument is about types, not about syntax
Hardcaml is an OCaml library for designing and testing hardware designs. The description sets out the scope clearly: it includes a simulation backend and support for compiling down to RTL, so an entire hardware project, from designing the hardware to validating the design to synthesising it for an FPGA, can be done in OCaml.
The capabilities listed are five. Express hardware designs in OCaml. Make generic designs using higher-order functions, lists, maps and functors. Simulate designs in OCaml. Convert to hierarchical Verilog or VHDL. And write new modules to transform or analyse circuits, or provide new backends.
The motivation explains why. To get the advantages of implementing software algorithms in hardware, you usually have to unroll iterative or sequential operations into a hardware pipeline that can compute many things in parallel. Traditional hardware description languages offer only very primitive generation features, so this unrolling becomes unwieldy and tends to result in code that is hard to read and to reuse across projects.
The payoff claimed for OCaml is a strong type system plus expressive metaprogramming, which together allow highly flexible circuits to be implemented concisely. The type-confusion argument is the sharpest part of it. In hardware everything is just a wire, but you can assign semantic meanings to those wires using OCaml's type system, which eliminates that class of bug. And parametrising components with functors and higher-order functions lets you reuse them across designs and projects.
That is a real argument rather than a slogan. In a conventional HDL, a signal that is meant to be a 12-bit counter value and a signal that happens to carry 12 bits are indistinguishable. In OCaml they are different types.
One install pulls three packages
The installation is a single opam command that installs three packages together:
$ opam install hardcaml ppx_hardcaml hardcaml_wavetermThe three names map onto the three things a project needs. `hardcaml` is the core library. `ppx_hardcaml` is the preprocessor package. And `hardcaml_waveterm` is the waveform printing library, which is needed to see what your simulation did.
The repository layout matches the split. There is a `ppx/` directory for the preprocessor, a `src/` directory for the library, an `interface_types/` directory, a `kernel/` directory, a `js/` directory for the JavaScript parts, a `vcd/` directory for value change dump output, a `docs/` directory, a `test/` directory, and `hardcaml.opam` at the root for the package definition. The build is Dune, with `dune-project` and a `dune` file, and the `Makefile` is a thin wrapper around it:
default:
dune build
install:
dune install $(INSTALL_ARGS)The `Makefile` also has `uninstall`, `reinstall` and `clean` targets, and the `INSTALL_ARGS` variable lets you pass a prefix. So `make install PREFIX=/some/path` works, which is what you need if you are not using the opam switch.
There is a `.ocamlformat` file, so the project uses ocamlformat for formatting, and a `CHANGES.md` and `CONTRIBUTING.md`.
Simulating with a cycle-accurate simulator and printing waves
The testing story is the part that makes Hardcaml usable, and the example in the README is a complete testbench.
A testbench creates a simulator, attaches a waveform recorder to it, pulls out the inputs, and then drives it cycle by cycle:
let testbench () =
let sim = Simulator.create create in
let waves, sim = Waveform.create sim in
let inputs = Cyclesim.inputs sim in
let step ~clear ~incr =
inputs.clear := if clear=1 then Bits.vdd else Bits.gnd;
inputs.incr := if incr=1 then Bits.vdd else Bits.gnd;
Cyclesim.cycle sim
in
step ~clear:0 ~incr:0;
step ~clear:0 ~incr:1;
step ~clear:0 ~incr:1;
step ~clear:1 ~incr:0;
step ~clear:0 ~incr:0;
step ~clear:0 ~incr:0;
waves
;;The mechanics are worth reading. `Simulator.create create` instantiates the simulator from the design function. `Waveform.create sim` wraps it and returns the waveform plus a new simulator handle, which is the pattern you will see throughout: the wrapper threads the modified simulator through the rest of the pipeline. `Cyclesim.inputs` gives access to the input signals, and `Cyclesim.cycle` advances the simulation by one clock.
The signal values are `Bits.vdd` and `Bits.gnd`, so a one-bit signal is written as a conditional on an integer. That is slightly awkward, and it is the price of the type system: the width of a signal is part of its type, so a one-bit control and a 32-bit value do not get confused.
The output is printed as ASCII waveforms:
โSignalsโโโโโโโโโโWavesโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โclock โโโโโโโ โโโโโ โโโโโ โโโโโ โโโโโ โโโโโ โโโโ
โ โโ โโโโโ โโโโโ โโโโโ โโโโโ โโโโโ โโโโโ โ
โclear โโ โโโโโโโโโ โThe library that does this, `hardcaml_waveterm`, is described as usable in expect tests or from an interactive terminal application, which is a useful detail. Being able to print a waveform inside an expect-style test means a hardware test can assert on signal timing rather than only on final values.
The ecosystem around the core library is the real surface area
The list of tools and libraries is long, and it is where a project's actual dependency surface lives. The core library is the smallest part of what you end up using.
On the simulation side, `hardcaml_c` converts Hardcaml designs to C-based simulation models, provides an API compatible with the standard Cyclesim module, and trades compilation time for runtime performance. `hardcaml_verilator` converts designs to a very high speed simulation model using the open source Verilator compiler. `hardcaml_step_testbench` provides a monadic testbench API, letting you control multiple tasks synchronised to a clock without converting to a statemachine coding style. That last one is the most interesting design choice in the list, because it addresses the fact that hardware testbenches are naturally concurrent while OCaml code is not.
On the vendor side, `hardcaml_xilinx` provides various Xilinx primitives wrapped with Hardcaml interfaces and simulation models, and `hardcaml_xilinx_components` is a tool to read Xilinx unisim and xpm component definitions and generate Hardcaml interfaces. `hardcaml_xilinx_reports` handles automated generation of synthesis reports from Vivado. So the Xilinx story is a set of wrappers and generators around the vendor toolchain, not a replacement for it.
On the input and output sides, `hardcaml_of_verilog` converts a Verilog design to Hardcaml using Yosys, which is the migration path in. And `hardcaml_verify` provides SAT based formal verification tools for Hardcaml designs.
Then there is a library of designs rather than tooling: `hardcaml_circuits` is a library of useful or interesting Hardcaml designs, `hardcaml_fixed_point` provides fixed point arithmetic with rounding and overflow control, and `hardcaml_waveterm` is the waveform tool described above.
The projects using Hardcaml are also worth a look, because they show the range. Advent of Hardcaml uses FPGAs to solve Advent of Code problems, the Hardcaml ZPrize entry is about multi-scalar multiplication and number theoretic transform accelerators, Hardcaml Mips is a simple 5-stage MIPS CPU, Hardcaml Reed-Solomon is a configurable encoder and decoder, Hardcaml JPEG is a JPEG decoder design, and `hardcaml_arty` is infrastructure targeting the Arty A7 board.
A 0.x library with old release tags and a recent push
The version picture needs reading carefully, because the release tags and the repository activity tell different stories.
The listed releases are v0.14.1 on 2021-01-27 and v0.14.2 on 2021-03-25. Both are from 2021. The last push to the repository was on 2026-07-10, and the repository is not archived. So the code is being worked on while the published version numbers have not moved since March 2021.
That is a common pattern for a library that is developed alongside its own ecosystem. The satellite packages, the `hardcaml_waveterm` and `hardcaml_c` and `hardcaml_verilator` libraries, each have their own release cadence, and the core library changes in ways that are consumed by them. A project pinning `hardcaml 0.14.2` from opam is getting a 2021 snapshot.
The version number itself is a signal about API stability. A 0.x version in the OCaml ecosystem carries no compatibility guarantee, so a project that depends on Hardcaml should expect the API to move, and the `CHANGES.md` at the repository root is the place to look before an upgrade.
What this means in practice is that adoption has a specific shape. The realistic route for a new project is to depend on the opam packages and track the ecosystem releases, rather than to vendor a specific commit and expect it to age well. The design of the library is what you are buying, and the design has been stable enough to keep a satellite ecosystem growing for years, which is the strongest evidence available here.
Editorial conclusion
Hardcaml fits a team with an OCaml background that wants to write hardware with the same abstractions it uses for software, needs the same design to be simulatable and synthesizable without a rewrite, and values type-level safety over a conventional HDL. It does not fit a team that needs vendor tooling as the primary path, since the Xilinx support is a set of wrapper libraries around Vivado rather than a replacement for it, and it does not fit a project that must deliver quickly with an existing Verilog codebase. Verify first that the OCaml compiler and Dune versions you have match what the `hardcaml.opam` package expects, that the `ppx_hardcaml` preprocessor is the one you want, since the install command pulls three packages together, and that the specific device family you target has the primitives you need in `hardcaml_xilinx` rather than assuming the core library covers them.
Frequently asked questions
How do I install Hardcaml?
Run `opam install hardcaml ppx_hardcaml hardcaml_waveterm`, which installs the core library, the PPX extension that gives signals their types, and the waveform printing library. From a source checkout, the Makefile wraps Dune, so `make` runs `dune build` and `make install` runs `dune install`.
Why write hardware in OCaml instead of Verilog or VHDL?
The project argues that traditional hardware description languages offer only very primitive generation features, so the unrolling needed for parallel pipelines becomes unwieldy and hard to reuse. OCaml's type system lets you assign semantic meaning to wires, which removes type-confusion bugs, and functors and higher-order functions let you parametrise and reuse components.
How do I see the waveforms from a Hardcaml simulation?
Use the `hardcaml_waveterm` library. In a testbench you wrap the simulator with `Waveform.create sim`, drive it with `Cyclesim.cycle sim`, and print the result with `Waveform.print`, which renders ASCII waveforms usable in expect tests or an interactive terminal.
What backends can Hardcaml simulate and synthesise with?
The core library simulates in OCaml and converts to hierarchical Verilog or VHDL. Separate libraries cover the other backends: `hardcaml_c` for C-based simulation models, `hardcaml_verilator` for high speed simulation through Verilator, `hardcaml_of_verilog` to bring a Verilog design in through Yosys, and `hardcaml_verify` for SAT based formal verification.
How mature is Hardcaml for an FPGA project?
The core library's listed release tags are v0.14.1 from 2021-01-27 and v0.14.2 from 2021-03-25, and the repository was last pushed on 2026-07-10. The library is MIT licensed, and the README lists real projects built with it including a 5-stage MIPS CPU, a JPEG decoder, a Reed-Solomon codec and an Arty A7 board infrastructure.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/janestreet-hardcaml)Was this useful?