Hardcaml provides support for enumerations with a finite number of instances through the Enum module An Enum type declares a finite ie non recursive set of variants and requires a deriving specification as follows The variant cases defining an enum may in turn have arguments which are also Enums The functor creates two modules called and Each of these modules conforms to the signature so they can be treated as regular Hardcaml interfaces The parameter specifies the underlying type of the enum ie in designing circuits it would be and in simulations it will be Both variants have a similar API differing only in their encoding as a bit vector From here on we will demonstrate the API The generated modules contain functions to transform statically known enums into arbitrary desired so long the provided satisfied the interface Both and shipped with Hardcaml support them You get several enum specific features for defining circuits in the regular Signal like API notably the function for multiplexing on the enum value Note that this differs from which creates a multiplexer that returns a value of the type enum The function is also defined for the Always API which makes it really clean to write pattern match like applications on enums Similar to there is an optional argument for non exhaustive matches This generated enum module implements the so you get access to the various convenient functions defined in and For example The generated enum can even be used as part of regular Hardcaml interfaces This makes Hardcaml Enums powerful since the circuit interfaces can simply use it without nasty type conversions For example For more advanced usage you can convert to the underlying or value The function only performs checks on widths It is the caller s responsibility to ensure that when it s the provided raw value is always one hot when it s the provided raw value should be strictly less than the number of possible cases Enums are well supported in simulations too Let s make a little state machine that increments or decrements a counter based on an enum s input value The module simulation will also output the previous clock cycle s enum input Since these enum files are not simply of type we need to opt for special APIs for getting setting them in simulations Using those functions we can drive a Hardcaml Cyclesim t similar to regular Hardcaml sims module Simple_enum_example struct module Enum struct type t | Foo | Bar @@deriving sexp_of compare localize enumerate end include Hardcaml Enum Make_enums Enum end open Base open Hardcaml module Foo struct type t | Foo_a | Foo_b @@deriving sexp_of compare localize enumerate end module Bar struct type t | Bar_a | Bar_b | Bar_c @@deriving sexp_of compare localize enumerate end module Hello struct module Enum struct type t | Foo of Foo t | Bar of Bar t @@deriving sexp_of compare localize enumerate end include Hardcaml Enum Make_enums Enum end Make_enums One_hot Binary Hardcaml Interface S a Signal t Bits t ref Binary of_enum a t a Comb S module Bits module Signal # let x Signal t Hello Binary t Hello Binary of_enum module Signal Foo Foo_a val x Signal t Hello Binary t <abstr> # let y Hello Binary Of_signal of_enum Bar Bar_a val y Signal t Hello Binary t <abstr> # let z Hello Binary Of_signal x y val z Signal t const width 1 value 0b0 # let a Hello Binary Of_signal is x Foo Foo_a val a Signal t const width 1 value 0b1 match_ mux let non_exhaustive_matching let open Signal in Hello Binary match_ module Signal * default needs to be specified when the match cases are not exhaustive * default zero 10 x Foo Foo_a ones 10 Bar Bar_a zero 10 * Of_signal match_ is similar to match_ except it does not require an explicit a first class module argument * let exhaustive_matching let open Signal in Hello Binary Of_signal match_ y Foo Foo_a of_unsigned_int width 10 55 Foo Foo_b of_unsigned_int width 10 44 Bar Bar_a of_unsigned_int width 10 66 Bar Bar_b of_unsigned_int width 10 88 Bar Bar_c of_unsigned_int width 10 77 match_ Of_signal match_ default # Hello Binary Of_always match_ default Always t list > Signal t Hello Binary t > Hello Enum t * Always t list list > Always t <fun> # let exhaustive_matching case Hello Binary Of_always match_ default * The default case here * case Foo Foo_a * Some logic here * Bar Bar_a * Even more logic here * val exhaustive_matching Signal t Hello Binary t > Always t <fun> Hardcaml Interface S Of_signal Of_always Of_bits * A multiplexer that returns value of the enum as opposed to multiplexing on the enum itself as match_ does * let multiplexers let selector Signal input selector 2 in Hello Binary Of_signal mux selector of_enum Foo Foo_a of_enum Bar Bar_a of_enum Bar Bar_b of_enum Foo Foo_b let clock Signal input clock 1 let spec Signal Reg_spec create clock * Registers that stores the enum value * let registers Signal t Hello Binary t let wires Hello Binary Of_signal wires in Hello Binary Of_signal reg enable Signal vdd spec wires * Usage in always blocks You can almost seamlessly assign values to them * let _ Always t list let cond Signal input cond 1 in let var Hello Binary Of_always reg enable Signal vdd spec in let assign_hello Hello Binary Of_always assign in Always if_ cond assign_hello var Hello Binary Of_signal of_enum Foo Foo_a @@ * This doesn t make much sense in practice This is just to the value function * assign_hello var Hello Binary Of_always value var module I struct type a t clock a clear a hello a Hello Binary t @@deriving hardcaml end Bits t Signal t of_raw One_hot Binary # let this_will_raise_due_to_a_width_mismatch Hello Binary Of_signal of_raw Signal of_unsigned_int width 30 10 Exception Failure Width mismatch Enum expects 3 but obtained 30 # let this_is_valid_and_fine Hello Binary Of_signal of_raw Signal of_unsigned_int width 3 0 val this_is_valid_and_fine Signal t Hello Binary t <abstr> # let this_is_undefined_and_will_not_raise Hello Binary Of_signal of_raw Signal of_unsigned_int width 3 6 val this_is_undefined_and_will_not_raise Signal t Hello Binary t <abstr> module O struct type a t counter a @bits 32 prev_hello a Hello Binary t @rtlmangle true @@deriving hardcaml end # let create i _ I t let open Signal in let spec Signal Reg_spec create clock i clock clear i clear in let ctr Always Variable reg spec width 32 enable vdd in let prev_hello Hello Binary Of_always reg enable vdd spec in Always compile Hello Binary Of_always match_ i hello * Foo increments * Foo Foo_a ctr < ctr value 1 Foo Foo_b ctr < ctr value 2 * Bar decrements * Bar Bar_a ctr < ctr value 1 Bar Bar_b ctr < ctr value 2 Bar Bar_c ctr < ctr value 3 Hello Binary Of_always assign prev_hello i hello O counter ctr value prev_hello Hello Binary Of_always value prev_hello val create Signal t I t > Signal t O t <fun> Bits t ref # Hello Binary sim_set Bits t ref Hello Binary t > Hello Enum t > unit <fun> # Hello Binary sim_set_raw Bits t ref Hello Binary t > Bits t > unit <fun> # Hello Binary sim_get Bits t ref Hello Binary t > Hello Enum t Or_error t <fun> # Hello Binary sim_get_raw Bits t ref Hello Binary t > Bits t <fun> let sim let module Sim Cyclesim With_interface I O in Sim create create let inputs Cyclesim inputs sim let outputs Cyclesim outputs sim let print let prev_hello Or_error ok_exn Hello Binary sim_get outputs prev_hello in let counter Bits to_unsigned_int outputs counter in Stdio print_s %message prev_hello Hello Enum t counter int # Hello Binary sim_set inputs hello Foo Foo_a unit # Cyclesim cycle sim unit # print prev_hello Foo Foo_a counter 1 unit # Hello Binary sim_set inputs hello Foo Foo_b unit # Cyclesim cycle sim unit # print prev_hello Foo Foo_b counter 3 unit # Hello Binary sim_set inputs hello Bar Bar_a unit # Cyclesim cycle sim unit # Cyclesim cycle sim unit # print prev_hello Bar Bar_a counter 1 unit