In the simple counter example we printed the value of the output ports during the simulation. Printing outputs or even modelling the expected behaviour of a circuit dynamically is sometimes the best way to test it. However, the majority of the time we just want to visualize the result. For that, we can use waveforms.
The hardcaml_waveterm
library can capture and print waveforms from
Hardcaml simulations.
We can capture a waveform with
Waveform.create
.
It takes as an argument a simulator and returns a waveform and
modified simulator that captures the input and output port values.
The waveform can be displayed with Waveform.print
.
# let testbench () =
let sim = Simulator.create create in
let waves, sim = Waveform.create sim in
testbench sim;
waves
val testbench : unit -> Waveform.t = <fun>
# let waves = testbench ()
val waves : Waveform.t = <abstr>
# Waveform.print ~display_height:12 waves
┌Signals────────┐┌Waves──────────────────────────────────────────────┐
│clock ││┌───┐ ┌───┐ ┌───┐ ┌───┐ ┌───┐ ┌───┐ ┌──│
│ ││ └───┘ └───┘ └───┘ └───┘ └───┘ └───┘ │
│clear ││ ┌───────┐ │
│ ││────────────────────────┘ └─────────────── │
│incr ││ ┌───────────────┐ │
│ ││────────┘ └─────────────────────── │
│ ││────────────────┬───────┬───────┬─────────────── │
│dout ││ 00 │01 │02 │00 │
│ ││────────────────┴───────┴───────┴─────────────── │
│ ││ │
└───────────────┘└───────────────────────────────────────────────────┘
- : unit = ()
Waveforms can be captured as expect test output.
let%expect_test "counter" =
let waves = testbench ()
Waveform.print ~display_height:12 waves
[%expect {|
┌Signals────────┐┌Waves──────────────────────────────────────────────┐
│clock ││┌───┐ ┌───┐ ┌───┐ ┌───┐ ┌───┐ ┌───┐ ┌──│
│ ││ └───┘ └───┘ └───┘ └───┘ └───┘ └───┘ │
│clear ││ ┌───────┐ │
│ ││────────────────────────┘ └─────────────── │
│incr ││ ┌───────────────┐ │
│ ││────────┘ └─────────────────────── │
│ ││────────────────┬───────┬───────┬─────────────── │
│dout ││ 00 │01 │02 │00 │
│ ││────────────────┴───────┴───────┴─────────────── │
│ ││ │
└───────────────┘└───────────────────────────────────────────────────┘
|}]
The Waveform.print
function takes optional arguments which control the rendering of the waveform.
start_cycle
first cycle to displaydisplay_width
,display_height
width and height of the waveformwave_width
scale at which the waveform is shown (negative values allowed)display_rules
configuration of the signals to show
# Waveform.print
~display_height:10
~display_rules:
Display_rule.[ port_name_is "dout" ~wave_format:Unsigned_int
; port_name_matches Re.Posix.(compile (re "cl.*")) ~wave_format:Bit ]
waves
┌Signals────────┐┌Waves──────────────────────────────────────────────┐
│ ││────────────────┬───────┬───────┬─────────────── │
│dout ││ 0 │1 │2 │0 │
│ ││────────────────┴───────┴───────┴─────────────── │
│clear ││ ┌───────┐ │
│ ││────────────────────────┘ └─────────────── │
│clock ││┌───┐ ┌───┐ ┌───┐ ┌───┐ ┌───┐ ┌───┐ ┌──│
│ ││ └───┘ └───┘ └───┘ └───┘ └───┘ └───┘ │
│ ││ │
└───────────────┘└───────────────────────────────────────────────────┘
- : unit = ()
The signals are shown in the order of the first matching
display_rule
.
The way the value is shown is configured with the
wave_format
argument. Groups of signals can be specified using regular expressions.