Common FAQs
in-depth insights into Hardcaml
Hardcaml FAQ & Guide
What is a Signal in Hardcaml?
In Hardcaml, a Signal represents a wire or register in hardware. It is the core datatype used to describe combinational and sequential logic in your design. Think of it as a symbolic representation of a bitvector that can be wired to other logic β like inputs, outputs, gates, or flip-flops.
Key points about Signals:
A
Signal.tis not like a regular value (int or bool) - it's a symbolic expression representing a circuit connectionIt will later be turned into actual hardware (e.g., in Verilog or VHDL)
During circuit construction, a signal is not the actual number, it's a symbolic expression
What's the difference between a Wire and a Register?
Wire:
A wire is a combinational connection β it carries a signal instantly from one logic gate to another
No memory: A wire does not store anything
Instant: Its value is updated immediately based on the logic driving it
Used in: Data paths, logic gates, connections between modules
Register:
A register is a sequential element β it stores data across clock cycles
Has memory: It holds its value until it is explicitly updated
Synchronous: Updates its value only on a clock edge (typically rising edge)
Used in: State machines, counters, pipelines, buffering
Summary Table:
Memory
β No
β Yes
Updates
Instantly (combinational)
On clock edge (sequential)
Typical Use
Logic routing, gates
State holding, counters
Control
No control (passive)
Controlled by clock/reset
Signal Operations and Creation
What can a Signal hold?
A Signal.t can hold a symbolic bit-vector expression β it represents a sequence of bits (0s and 1s) of a given width.
Signal can hold:
Bit Vectors - Basic type representing N-bit wide wire or register
Combinational Expressions - Composed logic like adders, muxes, gates
Constants - Created using
Signal.of_int,Signal.of_string,Signal.gnd(zero),Signal.vdd(one)Registered Values (State) - With a
Reg_spec, a signal can hold state
Signal cannot hold:
Actual runtime values during circuit construction
Pointers, objects, data structures
Mutable values (signals are immutable)
How do I create signals from values?
Why use of_string "01" instead of just 1?
of_string "01" instead of just 1?In Hardcaml, you're building hardware descriptions, not just doing computations. You're working with bitvectors that have:
A width (how many bits)
A bit-level value (e.g., "01", "10")
OCaml integers like 1 and 2 are not Signal.t values β they don't carry bit width, binary shape, or signal expression info.
Understanding Signal vs Bits
What's the difference between Signal and Bits?
This is crucial to understand:
What it is
Abstract representation of a circuit
Concrete bit-level values
When used
At circuit definition time
At simulation time
Mutable?
No (pure & immutable)
Yes (in simulation)
Example use
Describing combinational logic, registers, muxes
Running testbenches, printing outputs
OCaml type
Signal.t
Bits.t
Analogy
Verilog wire, reg
Runtime logic values
Signal describes the structure:
Bits represents actual values:
Binary Arithmetic
How many numbers can I represent with N bits?
With N bits, you can represent 2^N distinct numbers.
For 8 bits:
Unsigned: 0 to 255 (256 values total)
Signed (two's complement): -128 to 127 (256 values total)
Binary conversion table (0-10)
0
0000
1
0001
2
0010
3
0011
4
0100
5
0101
6
0110
7
0111
8
1000
9
1001
10
1010
Working with Registers
What is a Reg_spec?
A Reg_spec (register specification) is a configuration describing how a register should behave with respect to clock, reset, and optionally enable.
What it contains:
clock: When the register triggers (clock edge)clear: Optional reset signalenable: Optional enable signal (only updates when enabled)
How does reg_fb work?
reg_fb creates a "register with feedback" β a register whose next value depends on its current value.
Components:
spec: Contains clock/reset configuration~enable:Signal.vdd: Always enabled (updates every cycle)~width:8: 8-bit register~f:(fun d -> d +:. 1): Next value = current value + 1
Why do we need wires for feedback?
In OCaml, you can't refer to a variable before it's defined (no forward references). But in hardware, feedback loops are common:
Solution: Use a wire as a placeholder:
What does "enable" do in a register?
The enable signal controls whether the register updates on the clock edge:
If
enable = 1: Register loads new value on clock edgeIf
enable = 0: Register holds its previous value
Example behavior:
1
1
0
1
2
1
1
2
3
0
2
2 (holds)
4
1
2
3
Circuit Building Functions
How does select work?
select extracts a slice (range of bits) from a wider signal.
Example:
Important notes:
Uses inclusive indexing:
select s 5 2returns 4 bits: s[5], s[4], s[3], s[2]Indexing is from LSB = 0
If high < low, you'll get a runtime error
What is mux?
mux builds a hardware multiplexer - it selects one signal from a list based on a selector:
Example:
What is mux2?
mux2 is a convenience function for 2-way multiplexing:
It's equivalent to mux sel [a; b] but cleaner for binary choices.
How does concatenation (@:) work?
The @: operator concatenates bit vectors:
Example with JK flip-flop:
0
0
00
0
0
1
01
1
1
0
10
2
1
1
11
3
Memory Operations
How does multiport_memory work?
multiport_memory creates a RAM block with multiple read and write ports.
Write Port structure:
Read behavior:
Returns an array of read data signals
q.(0)is the data from the first read addressCan be synchronous (1 cycle delay) or asynchronous
What do the |...| symbols mean?
The [| ... |] syntax creates an OCaml array (not a list):
Arrays are:
Fixed size
Mutable
Accessed via
arr.(i)
PPX and Interfaces
What does [@@deriving hardcaml] do?
When you use [@@deriving hardcaml] on an interface type:
It generates many utility functions:
Generated functions:
make- Create signal bundlesmap- Transform each fieldmap2- Combine two interfaces field by fieldto_bits/of_bits- Convert between structured records and flat bitvectorsport_names_and_widths- Get metadata
Always DSL
What is Always.Variable?
Always.Variable is used within the Always DSL to describe sequential logic in an imperative style (like Verilog):
Key operations:
.create ~width- Makes a new variable/register.set- Assigns to it (like<=in Verilog).value- Reads the current signal value
Simulation
Why do we dereference with (!) in simulation?
In Hardcaml simulation, signals are stored as mutable references (ref):
Why refs?
Simulation signals change every clock cycle
Need mutable containers to hold/update values
Standard OCaml pattern:
:=to write,!to read
What does Cyclesim.cycle do?
Cyclesim.cycle sim advances the simulation by one clock cycle:
Evaluates all combinational logic
Updates all registers on clock edge
Propagates new values through the circuit
How do I view signal output?
To see human-readable signal values:
Common Patterns
Building a Counter
This creates an 8-bit counter that:
Increments every cycle (always enabled)
Wraps from 255 to 0
Can be cleared via the
spec
Creating a State Machine
JK Flip-Flop Pattern
This implements JK flip-flop logic:
J=0, K=0: Hold (q)
J=0, K=1: Reset (0)
J=1, K=0: Set (1)
J=1, K=1: Toggle (~q)
Debugging Tips
Understanding Signal Tree Output
When you see verbose output like:
This is the internal representation of your circuit graph. It shows:
Node type (Op2 = binary operation)
Operation (Signal_add = addition)
Arguments (the inputs to the operation)
Common Errors
"Unbound module Bits"
Use
Hardcaml.Bitsor addopen Hardcaml
Type errors with signals
Remember: Signal.t for circuit building, Bits.t for simulation
Can't mix regular OCaml ints with signals
Forward reference errors
Use
Signal.wirefor feedback loopsConnect wires after creating dependent signals
Looking through the document again, I believe I've covered all the major topics, but let me add a few more sections that were in the original document to ensure completeness:
Additional Topics
What is MSBs?
MSB stands for Most Significant Bit β the bit that represents the highest value in a binary number. msbs typically refers to a function or slice that returns the top (leftmost) bits of a signal.
Example:
Use cases:
Determining the sign bit in signed arithmetic
Address decoding (using MSBs to select memory banks)
Control logic where upper bits determine branches
What is dout?
dout is just a naming convention β short for data out β typically used to label the output signal of a hardware module. It's not a keyword.
Example:
Common uses:
In a counter: the current count value
In an ALU: the result of an operation
In a memory: the value read from RAM
Array Indexing Syntax .(i)
The .(i) syntax is OCaml's array indexing:
In multiport_memory context:
Understanding Internal Signal Representations
When you see output like:
This is the internal binary encoding of a Bits.t value. It's not meant for human readability. To view it properly:
Step-by-Step Counter Operation
Here's exactly how a counter increments with clock cycles and clear:
Counter definition:
Simulation trace:
0
0
00000000
0
Start value
1
0
00000001
1
+1
2
0
00000010
2
+1
3
0
00000011
3
+1
4
1
00000000
0
Cleared to 0
5
0
00000001
1
Counting resumes
ASCII Waveform:
Simulation Step Function Pattern
The step function in testbenches encapsulates a single simulation cycle:
What it does:
Sets input signals based on parameters
Prints current output value (before the clock)
Advances simulation by one clock cycle
Why use it:
Cleaner than repeating code
Easy to parameterize tests
Better for loops and automated testing
Signal Constants
Common signal constants you'll use:
Signal.gnd- Logic 0Signal.vdd- Logic 1 (VDD = supply voltage)Signal.zero width- Zero of specific widthSignal.ones width- All ones of specific width
Read and Write Port Details
Write Port Fields:
write_enable: When high, enables writing on clock edgeaddress: Target memory locationdata: Value to write
Read Port Behavior:
Asynchronous: Data updates immediately when address changes
Synchronous: Data updates on next clock cycle (typical for FPGA RAM)
Example usage:
Last updated