package hardcaml

  1. Overview
  2. Docs
Legend:
Library
Module
Module type
Parameter
Class
Class type
type signal_op =
  1. | Signal_add
  2. | Signal_sub
  3. | Signal_mulu
  4. | Signal_muls
  5. | Signal_and
  6. | Signal_or
  7. | Signal_xor
  8. | Signal_eq
  9. | Signal_not
  10. | Signal_lt
  11. | Signal_cat
  12. | Signal_mux

simple operators

module Uid : sig ... end
module Uid_map : sig ... end
module Uid_set : sig ... end
type signal_id = {
  1. s_id : Uid.t;
  2. mutable s_names : Base.String.t Base.List.t;
  3. s_width : Base.Int.t;
  4. mutable s_attributes : Rtl_attribute.t Base.List.t;
    (*

    Making this mutable turns hardcaml from pretty functional to pretty imperative. however, if used carefully and only with the library, we can provide a potentially easier way of changing the graph structure in some cases

    *)
  5. mutable s_deps : t Base.List.t;
  6. caller_id : Caller_id.t Base.Option.t;
}

internal structure for tracking signals

and t =
  1. | Empty
  2. | Const of signal_id * Bits.t
  3. | Op of signal_id * signal_op
  4. | Wire of signal_id * t Base.Ref.t
  5. | Select of signal_id * Base.Int.t * Base.Int.t
  6. | Reg of signal_id * register
  7. | Mem of signal_id * Uid.t * register * memory
  8. | Multiport_mem of signal_id * Base.Int.t * write_port Base.Array.t
  9. | Mem_read_port of signal_id * t * t
  10. | Inst of signal_id * Uid.t * instantiation

main signal data type

and write_port = {
  1. write_clock : t;
  2. write_address : t;
  3. write_enable : t;
  4. write_data : t;
}
and read_port = {
  1. read_clock : t;
  2. read_address : t;
  3. read_enable : t;
}
and register = {
  1. reg_clock : t;
    (*

    clock

    *)
  2. reg_clock_edge : Edge.t;
    (*

    active clock edge

    *)
  3. reg_reset : t;
    (*

    asynchronous reset

    *)
  4. reg_reset_edge : Edge.t;
    (*

    asynchronous reset edge

    *)
  5. reg_reset_value : t;
    (*

    asychhronous reset value

    *)
  6. reg_clear : t;
    (*

    synchronous clear

    *)
  7. reg_clear_level : Level.t;
    (*

    synchronous clear level

    *)
  8. reg_clear_value : t;
    (*

    sychhronous clear value

    *)
  9. reg_enable : t;
    (*

    global system enable

    *)
}

These types are used to define a particular type of register as per the following template, where each part is optional:

       always @(?edge clock, ?edge reset)
         if (reset == reset_level) d <= reset_value;
         else if (clear == clear_level) d <= clear_value;
         else if (enable) d <= ...;
and memory = {
  1. mem_size : Base.Int.t;
  2. mem_read_address : t;
  3. mem_write_address : t;
}
and instantiation = {
  1. inst_name : Base.String.t;
    (*

    name of circuit

    *)
  2. inst_instance : Base.String.t;
    (*

    instantiation label

    *)
  3. inst_generics : Parameter.t Base.List.t;
    (*

    Parameter.int ...

    *)
  4. inst_inputs : (Base.String.t * t) Base.List.t;
    (*

    name and input signal

    *)
  5. inst_outputs : (Base.String.t * (Base.Int.t * Base.Int.t)) Base.List.t;
    (*

    name, width and low index of output

    *)
  6. inst_lib : Base.String.t;
  7. inst_arch : Base.String.t;
}
type signal = t
val signal_id : t -> signal_id

returns the (private) signal_id. For internal use only.

val uid : t -> Uid.t

returns the unique id of the signal

val deps : t -> t Base.List.t

returns the signal's dependencies

val names : t -> Base.String.t Base.List.t

returns the list of names assigned to the signal

val add_attribute : t -> Rtl_attribute.t -> t

Add an attribute to node. This is currently supported only in Verilog.

val attributes : t -> Rtl_attribute.t Base.List.t

Returns attributes associated to the signal

val has_name : t -> Base.Bool.t
val is_reg : t -> Base.Bool.t

is the signal a register?

val is_mem : t -> Base.Bool.t

is the signal a memory, or multiport memory?

val is_multiport_mem : t -> Base.Bool.t

is the signal a multiport memory?

val is_inst : t -> Base.Bool.t

is the signal an instantiation?

val is_const : t -> Base.Bool.t

is the signal a constant?

val is_select : t -> Base.Bool.t

is the signal a part selection?

val is_wire : t -> Base.Bool.t

is the signal a wire?

val is_op : signal_op -> t -> Base.Bool.t

is the signal the given operator?

val const_value : t -> Bits.t

return the (binary) string representing a constants value

val new_id : Base.Unit.t -> Uid.t

creates a new signal uid

val reset_id : Base.Unit.t -> Base.Unit.t

resets the signal identifiers

val make_id : Base.Int.t -> t Base.List.t -> signal_id

constructs a signal_id type

val structural_compare : ?check_names:Base.Bool.t -> ?check_deps:Base.Bool.t -> t -> t -> Base.Bool.t

perform a recursive structural comparison of two signals

val sexp_of_signal_recursive : ?show_uids:Base.Bool.t -> depth:Base.Int.t -> t -> Base.Sexp.t

sexp_of_signal_recursive ~depth signal converts a signal recursively to a sexp for up to depth levels. If show_uids is false then signal identifiers will not be printed. max_list_length controls how many mux and concat arguments (dependancies) are printed.

module Const_prop : sig ... end

Combinatorial signal API

include Comb.S with type t := t
val sexp_of_t : t -> Ppx_sexp_conv_lib.Sexp.t
include Base.Equal.S with type t := t
val equal : t Base.Equal.equal
val empty : t

the empty signal

val is_empty : t -> Base.Bool.t
val (--) : t -> Base.String.t -> t

names a signal

let a = a -- "a" in ...

signals may have multiple names.

val width : t -> Base.Int.t

returns the width (number of bits) of a signal.

let w = width s in ...

val address_bits_for : Base.Int.t -> Base.Int.t

addess_bits_for num_elements returns the address width required to index num_elements.

It is the same as Int.ceil_log2, except it wll return a minimum value of 1 (since you cannot have 0 width vectors). Raises if num_elements is < 0.

val num_bits_to_represent : Base.Int.t -> Base.Int.t

num_bits_to_represent x returns the number of bits required to represent the number x, which should be >= 0.

val of_constant : Constant.t -> t
val to_constant : t -> Constant.t
val constb : Base.String.t -> t

convert binary string to constant

val consti : width:Base.Int.t -> Base.Int.t -> t

convert integer to constant

val consti32 : width:Base.Int.t -> Base.Int32.t -> t
val consti64 : width:Base.Int.t -> Base.Int64.t -> t
val consthu : width:Base.Int.t -> Base.String.t -> t

convert unsigned hex string to constant

val consths : width:Base.Int.t -> Base.String.t -> t

convert signed hex string to constant

val constd : width:Base.Int.t -> Base.String.t -> t

convert decimal string to constant

val constv : Base.String.t -> t

convert verilog style string to constant

val constibl : Base.Int.t Base.List.t -> t

convert IntbitsList to constant

val const : Base.String.t -> t

convert verilog style or binary string to constant

val concat : t Base.List.t -> t

concat ts concatenates a list of signals - the msb of the head of the list will become the msb of the result.

let c = concat [ a; b; c ] in ...

concat raises if ts is empty or if any t in ts is empty.

val concat_e : t Base.List.t -> t

same as concat except empty signals are first filtered out

val (@:) : t -> t -> t

concatenate two signals.

let c = a @: b in ...

equivalent to concat [ a; b ]

val vdd : t

logic 1

val is_vdd : t -> Base.Bool.t
val gnd : t

logic 0

val is_gnd : t -> Base.Bool.t
val zero : Base.Int.t -> t

zero w makes a the zero valued constant of width w

val ones : Base.Int.t -> t

ones w makes a constant of all ones of width w

val one : Base.Int.t -> t

one w makes a one valued constant of width w

val select : t -> Base.Int.t -> Base.Int.t -> t

select t hi lo selects from t bits in the range hi...lo, inclusive. select raises unless hi and lo fall within 0 .. width t - 1 and hi >= lo.

val select_e : t -> Base.Int.t -> Base.Int.t -> t

same as select except invalid indices return empty

val bit : t -> Base.Int.t -> t

select a single bit

val msb : t -> t

get most significant bit

val lsbs : t -> t

get least significant bits

val lsb : t -> t

get least significant bit

val msbs : t -> t

get most significant bits

val drop_bottom : t -> Base.Int.t -> t

drop_bottom s n drop bottom n bits of s

val drop_top : t -> Base.Int.t -> t

drop_top s n drop top n bits of s

val sel_bottom : t -> Base.Int.t -> t

sel_bottom s n select bottom n bits of s

val sel_top : t -> Base.Int.t -> t

sel_top s n select top n bits of s

val insert : into:t -> t -> at_offset:Base.Int.t -> t

insert ~into:t x ~at_offset insert x into t at given offet

val sel : t -> (Base.Int.t * Base.Int.t) -> t
val mux : t -> t Base.List.t -> t

multiplexer.

let m = mux sel inputs in ...

Given l = List.length inputs and w = width sel the following conditions must hold.

l <= 2**w, l >= 2

If l < 2**w, the last input is repeated.

All inputs provided must have the same width, which will in turn be equal to the width of m.

val mux2 : t -> t -> t -> t

mux2 c t f 2 input multiplexer. Selects t if c is high otherwise f.

t and f must have same width and c must be 1 bit.

Equivalent to mux c [f; t]

val mux_init : t -> Base.Int.t -> f:(Base.Int.t -> t) -> t
val cases : t -> t -> (Base.Int.t * t) Base.List.t -> t

case mux

val matches : ?resize:(t -> Base.Int.t -> t) -> ?default:t -> t -> (Base.Int.t * t) Base.List.t -> t

match mux

val (&:) : t -> t -> t

logical and

val (&:.) : t -> Base.Int.t -> t
val (&&:) : t -> t -> t

a <>:. 0 &: b <>:. 0

val (|:) : t -> t -> t

logical or

val (|:.) : t -> Base.Int.t -> t
val (||:) : t -> t -> t

a <>:. 0 |: b <>:. 0

val (^:) : t -> t -> t

logic xor

val (^:.) : t -> Base.Int.t -> t
val (~:) : t -> t

logical not

val (+:) : t -> t -> t

addition

val (+:.) : t -> Base.Int.t -> t
val (-:) : t -> t -> t

subtraction

val (-:.) : t -> Base.Int.t -> t
val negate : t -> t

negation

val (*:) : t -> t -> t

unsigned multiplication

val (*+) : t -> t -> t

signed multiplication

val (==:) : t -> t -> t

equality

val (==:.) : t -> Base.Int.t -> t
val (<>:) : t -> t -> t

inequality

val (<>:.) : t -> Base.Int.t -> t
val (<:) : t -> t -> t

less than

val (<:.) : t -> Base.Int.t -> t
val lt : t -> t -> t
val (>:) : t -> t -> t

greater than

val (>:.) : t -> Base.Int.t -> t
val (<=:) : t -> t -> t

less than or equal to

val (<=:.) : t -> Base.Int.t -> t
val (>=:) : t -> t -> t

greater than or equal to

val (>=:.) : t -> Base.Int.t -> t
val (<+) : t -> t -> t

signed less than

val (<+.) : t -> Base.Int.t -> t
val (>+) : t -> t -> t

signed greater than

val (>+.) : t -> Base.Int.t -> t
val (<=+) : t -> t -> t

signed less than or equal to

val (<=+.) : t -> Base.Int.t -> t
val (>=+) : t -> t -> t

signed greated than or equal to

val (>=+.) : t -> Base.Int.t -> t
val to_string : t -> Base.String.t

create string from signal

val to_int : t -> Base.Int.t

to_int t treats t as unsigned and resizes it to fit exactly within an OCaml Int.t.

  • If width t > Int.num_bits then the upper bits are truncated.
  • If width t >= Int.num_bits and bit t (Int.num_bits-1) = vdd (i.e. the msb of the resulting Int.t is set), then the result is negative.
  • If t is Signal.t and not a constant value, an exception is raised.
val to_sint : t -> Base.Int.t

to_sint t treats t as signed and resizes it to fit exactly within an OCaml Int.t.

  • If width t > Int.num_bits then the upper bits are truncated.
  • If t is Signal.t and not a constant value, an exception is raised.
val to_int32 : t -> Base.Int32.t
val to_sint32 : t -> Base.Int32.t
val to_int64 : t -> Base.Int64.t
val to_sint64 : t -> Base.Int64.t
val to_bstr : t -> Base.String.t

create binary string from signal

val bits : t -> t Base.List.t

convert signal to a list of bits, msb first

val to_array : t -> t Base.Array.t

to_array s convert signal s to array of bits with lsb at index 0

val of_array : t Base.Array.t -> t

of_array a convert array a of bits to signal with lsb at index 0

val repeat : t -> Base.Int.t -> t

repeat signal n times

val split_in_half : t -> t * t

split signal in half

val split : ?exact:Base.Bool.t -> part_width:Base.Int.t -> t -> t Base.List.t

Split signal into a list of signals with width equal to part_width. The least significant bits are at the head of the returned list. If exact is true the input signal width must be exactly divisable by part_width.

val sll : t -> Base.Int.t -> t

shift left logical

val srl : t -> Base.Int.t -> t

shift right logical

val sra : t -> Base.Int.t -> t

shift right arithmetic

val log_shift : (t -> Base.Int.t -> t) -> t -> t -> t

shift by variable amount

val uresize : t -> Base.Int.t -> t

uresize t w returns the unsigned resize of t to width w. If w = width t, this is a no-op. If w < width t, this selects the w low bits of t. If w > width t, this extends t with zero (width t - w).

val sresize : t -> Base.Int.t -> t

sresize t w returns the signed resize of t to width w. If w = width t, this is a no-op. If w < width t, this selects the w low bits of t. If w > width t, this extends t with width t - w copies of msb t.

val ue : t -> t

unsigned resize by +1 bit

val se : t -> t

signed resize by +1 bit

val resize_list : resize:(t -> Base.Int.t -> t) -> t Base.List.t -> t Base.List.t

resize_list ?resize l finds the maximum width in l and applies resize el max to each element.

val resize_op2 : resize:(t -> Base.Int.t -> t) -> (t -> t -> t) -> t -> t -> t

resize_op2 ~resize f a b applies resize x w to a and b where w is the maximum of their widths. It then returns f a b

val reduce : f:('a -> 'a -> 'a) -> 'a Base.List.t -> 'a

fold 'op' though list

val reverse : t -> t

reverse bits

val mod_counter : max:Base.Int.t -> t -> t

mod_counter max t is if t = max then 0 else (t + 1), and can be used to count from 0 to (max-1) then from zero again. If max == 1<<n, then a comparator is not generated and overflow arithmetic used instead. If

val tree : arity:Base.Int.t -> f:('a Base.List.t -> 'a) -> 'a Base.List.t -> 'a

tree ~arity ~f input creates a tree of operations. The arity of the operator is configurable. tree raises if input = [].

val priority_select : ?branching_factor:Base.Int.t -> t With_valid.t Base.List.t -> t With_valid.t

priority_select cases returns the value associated with the first case whose valid signal is high. valid will be set low in the returned With_valid.t if no case is selected.

val priority_select_with_default : ?branching_factor:Base.Int.t -> t With_valid.t Base.List.t -> default:t -> t

Same as priority_select except returns default if no case matches.

val onehot_select : ?branching_factor:Base.Int.t -> t With_valid.t Base.List.t -> t

Select a case where one and only one valid signal is enabled. If more than one case is valid then the return value is undefined. If no cases are valid, 0 is returned by the current implementation, though this should not be relied upon.

val popcount : ?branching_factor:Base.Int.t -> t -> t

popcount t returns the number of bits set in t.

val is_pow2 : ?branching_factor:Base.Int.t -> t -> t

is_pow2 t returns a bit to indicate if t is a power of 2.

val leading_ones : ?branching_factor:Base.Int.t -> t -> t

leading_ones t returns the number of consecutive 1s from the most significant bit of t down.

val trailing_ones : ?branching_factor:Base.Int.t -> t -> t

trailing_ones t returns the number of consecutive 1s from the least significant bit of t up.

val leading_zeros : ?branching_factor:Base.Int.t -> t -> t

leading_zeros t returns the number of consecutive 0s from the most significant bit of t down.

val trailing_zeros : ?branching_factor:Base.Int.t -> t -> t

trailing_zeros t returns the number of consecutive 0s from the least significant bit of t up.

val floor_log2 : ?branching_factor:Base.Int.t -> t -> t With_valid.t

floor_log2 x returns the floor of log-base-2 of x. x is treated as unsigned and an error is indicated by valid = gnd in the return value if x = 0.

val ceil_log2 : ?branching_factor:Base.Int.t -> t -> t With_valid.t

ceil_log2 x returns the ceiling of log-base-2 of x. x is treated as unsigned and an error is indicated by valid = gnd in the return value if x = 0.

val binary_to_onehot : t -> t

convert binary to onehot

val onehot_to_binary : t -> t

convert onehot to binary

val binary_to_gray : t -> t

convert binary to gray code

val gray_to_binary : t -> t

convert gray code to binary

val random : width:Base.Int.t -> t

create random constant vector of given width

module type TypedMath = sig ... end
module Signed : TypedMath
module Uop : TypedMath with type v := t

Unsigned operations compatible with type t

module Sop : TypedMath with type v := t

Signed operations compatible with type t

val wire : Base.Int.t -> t

creates an unassigned wire

val wireof : t -> t

creates an assigned wire

val (<==) : t -> t -> Base.Unit.t

assigns to wire

val assign : t -> t -> Base.Unit.t
val input : Base.String.t -> Base.Int.t -> t

creates an input

val output : Base.String.t -> t -> t

creates an output

module Reg_spec_ : sig ... end

Reg_spec_ is a register specification. It is named Reg_spec_ rather than Reg_spec so that people consistently use the name Hardcaml.Reg_spec rather than Hardcaml.Signal.Reg_spec_.

val reg : Reg_spec_.t -> enable:t -> t -> t
val reg_fb : Reg_spec_.t -> enable:t -> w:Base.Int.t -> (t -> t) -> t
val pipeline : Reg_spec_.t -> n:Base.Int.t -> enable:t -> t -> t
val memory : Base.Int.t -> write_port:write_port -> read_address:t -> t
val ram_wbr : Base.Int.t -> write_port:write_port -> read_port:read_port -> t
val ram_rbw : Base.Int.t -> write_port:write_port -> read_port:read_port -> t
val multiport_memory : Base.Int.t -> write_ports:write_port Base.Array.t -> read_addresses:t Base.Array.t -> t Base.Array.t

Pretty printer.