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

val sexp_of_signal_op : signal_op -> Ppx_sexp_conv_lib.Sexp.t
val compare_signal_op : signal_op -> signal_op -> Base.Int.t
val hash_fold_signal_op : Base.Hash.state -> signal_op -> Base.Hash.state
val hash_signal_op : signal_op -> Base.Hash.hash_value
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_mem_read_port : t -> Base.Bool.t

is the signal a memory read port?

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 -> ?initial_deps:Uid_set.t -> t -> t -> Uid_set.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_msb : 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_lsb : t Base.List.t -> t

Similar to concat_msb except the lsb of the head of the list will become the lsb of the result.

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

same as concat_msb except empty signals are first filtered out

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

same as concat_lsb 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 (.:[]) : t -> (Base.Int.t * Base.Int.t) -> t

x.:[hi, lo] == select x hi lo

val (.:+[]) : t -> (Base.Int.t * Base.Int.t Base.Option.t) -> t

x.:+[lo, width] == select x (lo + width - 1) lo. If width is None it selects all remaining msbs of the vector ie x.:+[lo,None] == drop_bottom x lo

val (.:-[]) : t -> (Base.Int.t Base.Option.t * Base.Int.t) -> t

x.:-[hi, width] == select x hi (hi - width + 1). If hi is None it defaults to the msb of the vector ie x.:-[None, width] == sel_top x width

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

x.(i) == bit x i

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_msb : t -> t Base.List.t

convert signal to a list of bits with msb at head of list

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

convert signal to a list of bits with lsb at head of list

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_msb : t -> t * t

split signal in half. The most significant bits will be in the left half of the returned tuple.

val split_lsb : ?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. When exact is false and the input signal width is not exactly divisible by part_width, the last element will contains residual bits.

eg:

        split_lsb ~part_width:4 16b0001_0010_0011_0100 =
          [ 4b0100; 4b0011; 4b0010; 4b0001 ]

        split_lsb ~exact:false ~part_width:4 17b11_0001_0010_0011_0100 =
          [ 4b0100; 4b0011; 4b0010; 4b0001; 2b11 ]
val split_msb : ?exact:Base.Bool.t -> part_width:Base.Int.t -> t -> t Base.List.t

Like split_lsb except the most significant bits are at the head of the returned list. Residual bits when exact is false goes to the last element of the list, so in the general case split_lsb is not necessarily equivalent to split_msb |> List.rev.

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 rotl : t -> Base.Int.t -> t

rotate left

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

rotate right

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 : ?attributes:Rtl_attribute.t Base.List.t -> write_port:write_port -> read_port:read_port -> Base.Int.t -> t
val ram_rbw : ?attributes:Rtl_attribute.t Base.List.t -> write_port:write_port -> read_port:read_port -> Base.Int.t -> t
val multiport_memory : ?name:Base.String.t -> ?attributes:Rtl_attribute.t Base.List.t -> Base.Int.t -> write_ports:write_port Base.Array.t -> read_addresses:t Base.Array.t -> t Base.Array.t

Pretty printer.

OCaml

Innovation. Community. Security.