Skip to content

OCaml Introduction for Hardcaml

This guide covers the essential OCaml concepts you need to know to write Hardcaml circuits. We focus only on what’s necessary for Hardcaml, keeping it minimal and practical.

OCaml uses (* ... *) for comments:

(* This is a comment *)
let x = 5 (* Inline comment *)

Use let to bind values to names:

let x = 5
let y = 10
let sum = x + y

Functions are defined with let and can have parameters:

let add a b = a + b
let result = add 3 4 (* result = 7 *)

Functions can also use type annotations:

let add (a : int) (b : int) : int = a + b

You can explicitly annotate types using : type:

let x : int = 5
let name : string = "Hardcaml"

OCaml uses type parameters (like generics) with 'a, 'b, etc.:

type 'a t = { value : 'a }

This means t is a type that can hold any type 'a. In Hardcaml, we use this pattern extensively:

type 'a t = { clock : 'a; data : 'a }

Here, 'a could be Signal.t (for the circuit) or Bits.t (for simulation).

Records are like structs in other languages:

type point = { x : int; y : int }
let p = { x = 3; y = 4 }
let x_coord = p.x

In Hardcaml, we use records for input/output interfaces:

module I = struct
type 'a t = { a : 'a; b : 'a }
end
module O = struct
type 'a t = { out : 'a }
end

Modules are OCaml’s way of organizing code. Hardcaml uses them extensively.

module MyModule = struct
let value = 42
let add x y = x + y
end

Signatures (.mli files) define what a module exposes:

module MyModule : sig
val value : int
val add : int -> int -> int
end

Use open to bring module contents into scope:

open Core
open Hardcaml
open Signal

The open! syntax (with !) forces the open even if there are name conflicts.

You can define modules locally:

let create scope =
let module Scoped = Hierarchy.In_scope (I) (O) in
Scoped.hierarchical ~scope ~name:"my_circuit" create

Pattern matching lets you destructure values. With records:

let create scope ({ clock; clear; enable } : _ I.t) =
(* clock, clear, and enable are now available *)
...

This is equivalent to:

let create scope (i : _ I.t) =
let clock = i.clock in
let clear = i.clear in
let enable = i.enable in
...

Attributes are metadata attached to code. Hardcaml uses them extensively.

This attribute generates boilerplate code for Hardcaml:

module I = struct
type 'a t = { a : 'a; b : 'a }
[@@deriving hardcaml]
end

This automatically generates functions to convert between Signal.t and Bits.t versions of the type.

This attribute specifies the bit width of a signal:

type 'a t = { data : 'a [@bits 16] }

This tells Hardcaml that data is a 16-bit signal.

Hardcaml provides infix operators for hardware operations:

  • &: - Bitwise AND
  • |: - Bitwise OR
  • ^: - Bitwise XOR
  • ~: - Bitwise NOT
  • +: - Addition
  • -: - Subtraction
  • ==: - Equality comparison
  • <>: - Inequality comparison

Example:

let out = i.a &: i.b (* AND gate *)
let sum = a +: b (* Adder *)
let eq = a ==: b (* Equality check *)

OCaml supports labeled arguments with ~:

let create ~clock ~clear ~enable = ...

You can call it with labels:

create ~clock:clk ~clear:rst ~enable:en

Or use the shorthand when the variable name matches:

let clock = ... in
let clear = ... in
create ~clock ~clear ~enable:en

Hardcaml uses this pattern:

let spec = Reg_spec.create ~clock ~clear ()

Use let ... in for local bindings:

let result =
let x = 5 in
let y = 10 in
x + y

This is common in Hardcaml:

let create scope i =
let spec = Reg_spec.create ~clock:i.clock ~clear:i.clear () in
let counter = reg spec ~width:8 i.data in
{ out = counter }

Here’s a complete Hardcaml example with annotations:

(* Open required modules *)
open! Core
open! Hardcaml
open! Signal
(* Define input interface *)
module I = struct
type 'a t = { a : 'a; b : 'a } (* Record type with type parameter *)
[@@deriving hardcaml] (* Generate Hardcaml boilerplate *)
end
(* Define output interface *)
module O = struct
type 'a t = { out : 'a }
[@@deriving hardcaml]
end
(* Circuit creation function *)
let create _scope (i : _ I.t) : _ O.t = (* Pattern match on input *)
{ out = i.a &: i.b } (* Use Hardcaml AND operator *)