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.
Basic Syntax
Section titled “Basic Syntax”Comments
Section titled “Comments”OCaml uses (* ... *) for comments:
(* This is a comment *)let x = 5 (* Inline comment *)Let Bindings
Section titled “Let Bindings”Use let to bind values to names:
let x = 5let y = 10let sum = x + yFunctions
Section titled “Functions”Functions are defined with let and can have parameters:
let add a b = a + blet result = add 3 4 (* result = 7 *)Functions can also use type annotations:
let add (a : int) (b : int) : int = a + bType Annotations
Section titled “Type Annotations”You can explicitly annotate types using : type:
let x : int = 5let name : string = "Hardcaml"Type Parameters
Section titled “Type Parameters”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
Section titled “Records”Records are like structs in other languages:
type point = { x : int; y : int }
let p = { x = 3; y = 4 }let x_coord = p.xIn 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 }endModules
Section titled “Modules”Modules are OCaml’s way of organizing code. Hardcaml uses them extensively.
Defining Modules
Section titled “Defining Modules”module MyModule = struct let value = 42 let add x y = x + yendModule Signatures
Section titled “Module Signatures”Signatures (.mli files) define what a module exposes:
module MyModule : sig val value : int val add : int -> int -> intendOpening Modules
Section titled “Opening Modules”Use open to bring module contents into scope:
open Coreopen Hardcamlopen SignalThe open! syntax (with !) forces the open even if there are name conflicts.
Local Modules
Section titled “Local Modules”You can define modules locally:
let create scope = let module Scoped = Hierarchy.In_scope (I) (O) in Scoped.hierarchical ~scope ~name:"my_circuit" createPattern Matching
Section titled “Pattern Matching”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
Section titled “Attributes”Attributes are metadata attached to code. Hardcaml uses them extensively.
[@@deriving hardcaml]
Section titled “[@@deriving hardcaml]”This attribute generates boilerplate code for Hardcaml:
module I = struct type 'a t = { a : 'a; b : 'a } [@@deriving hardcaml]endThis automatically generates functions to convert between Signal.t and Bits.t versions of the type.
[@bits n]
Section titled “[@bits n]”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 Operators
Section titled “Hardcaml Operators”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 *)Optional Labeled Arguments
Section titled “Optional Labeled Arguments”OCaml supports labeled arguments with ~:
let create ~clock ~clear ~enable = ...You can call it with labels:
create ~clock:clk ~clear:rst ~enable:enOr use the shorthand when the variable name matches:
let clock = ... inlet clear = ... increate ~clock ~clear ~enable:enHardcaml uses this pattern:
let spec = Reg_spec.create ~clock ~clear ()Local Bindings with let ... in
Section titled “Local Bindings with let ... in”Use let ... in for local bindings:
let result = let x = 5 in let y = 10 in x + yThis 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 }Putting It All Together
Section titled “Putting It All Together”Here’s a complete Hardcaml example with annotations:
(* Open required modules *)open! Coreopen! Hardcamlopen! 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 *)Next Steps
Section titled “Next Steps”- Try the Hardcaml Basics Examples in the IDE
- Read Your First Circuit tutorial
- Explore the Hardcaml Examples in the IDE