Hardware design datatype suitable for simulation and netlist generation
type t = Hardcaml__.Signal__type.t
module Type : sig ... end
Naming
One or more string names applied to a signal. These will be used during RTL code generation and simulation to aid design inspection.
val set_names : t -> Base.string Base.list -> Base.unit
Set the given names on the signal. Wipes any names currently set.
Attributes
Attributes are attached to signals and written during RTL generation. They can be used to control downstream synthesis and place and route tools. They do not otherwise affect the Hardcaml circuit.
Add an attribute to node. This is currently supported only in Verilog.
Returns attributes associated to the signal.
Set the format used to display the signal
A comment can be associated with a signal, and this is sometimes useful to pass information to downstream tooling (specifically Verilator).
Set the comment associated with the signal. This is currently only supported in Verilog.
Remove the comment associated with the signal. This is currently only supported in Verilog.
Returns comment associated with the signal.
Specification of the module level input and output ports. Ports are represented in Hardcaml as wires with a single name. Note that Hardcaml will generally rewrite and legalize names depending on context but will not do so for ports.
val output : Base.string -> t -> t
Wires
Wires are used in Hardcaml to conenct two signals together. They are first created then assigned to (internally they have a mutable reference to their driver). Wires are how we can create cycles which we require in order to build logic like, for example, a counter.
It is entirely possible to create combinational loops using wires. Often this is not the intent and Hardcaml has functions to detect this - for example, Cyclesim will fail if there is such a loop.
Creates an unassigned wire.
Creates an assigned wire.
val (<==) : t -> t -> Base.unit
val assign : t -> t -> Base.unit
Combinational Logic
The main combinational logic API for creating things like adders, multiplexers etc. This API is the same as provided by Bits.t
.
By default Hardcaml performs constant propogation over signals - operations with purely constant values (and some other simple cases such as a &: vdd
) will be simplified. You can avoid this by using the operations from the Unoptimized
module.
Combinational logic API with constant propogation optimizations.
include Comb.S with type t := t
val sexp_of_t : t -> Sexplib0.Sexp.t
include Base.Equal.S with type t := t
val equal : t Base__Equal.equal
val is_empty : t -> Base.bool
val (--) : t -> Base.string -> t
names a signal
let a = a -- "a" in ...
signals may have multiple names.
val width : t -> Base.int
returns the width (number of bits) of a signal.
let w = width s in ...
val address_bits_for : Base.int -> Base.int
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 -> Base.int
num_bits_to_represent x
returns the number of bits required to represent the number x
, which should be >= 0
.
val of_bit_string : Base.string -> t
convert binary string to constant
val of_int : width:Base.int -> Base.int -> t
convert integer to constant
val of_int32 : width:Base.int -> Base.int32 -> t
val of_int64 : width:Base.int -> Base.int64 -> t
val of_hex : ?signedness:Signedness.t -> width:Base.int -> Base.string -> t
convert hex string to a constant. If the target width is greater than the hex length and signedness
is Signed
then the result is sign extended. Otherwise the result is zero padded.
val of_octal : ?signedness:Signedness.t -> width:Base.int -> Base.string -> t
convert octal string to a constant. If the target width is greater than the octal length and signedness
is Signed
then the result is sign extended. Otherwise the result is zero padded.
val of_z : width:Base.int -> Z.t -> t
Convert an arbitrarily wide integer value to a constant.
val of_string : Base.string -> t
convert verilog style or binary string to constant
val of_bit_list : Base.int Base.list -> t
convert IntbitsList to constant
val of_decimal_string : width:Base.int -> Base.string -> t
val of_char : Base.char -> t
convert a char
to an 8 bit constant
val of_bool : Base.bool -> t
convert a bool
to vdd
or gnd
Convert bits to a Zarith.t
val concat_msb : t Base.list -> 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
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
same as concat_msb
except empty signals are first filtered out
val concat_lsb_e : t Base.list -> t
same as concat_lsb
except empty signals are first filtered out
concatenate two signals.
let c = a @: b in ...
equivalent to concat [ a; b ]
val is_vdd : t -> Base.bool
val is_gnd : t -> Base.bool
zero w
makes a the zero valued constant of width w
ones w
makes a constant of all ones of width w
one w
makes a one valued constant of width w
val select : t -> Base.int -> Base.int -> 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 -> Base.int -> t
same as select
except invalid indices return empty
val bit : t -> Base.int -> t
get least significant bits
get least significant bit
get most significant bits
val drop_bottom : t -> Base.int -> t
drop_bottom s n
drop bottom n
bits of s
val drop_top : t -> Base.int -> t
drop_top s n
drop top n
bits of s
val sel_bottom : t -> Base.int -> t
sel_bottom s n
select bottom n
bits of s
val sel_top : t -> Base.int -> t
sel_top s n
select top n
bits of s
val (.:[]) : t -> (Base.int * Base.int) -> t
x.:[hi, lo]
== select x hi lo
val (.:+[]) : t -> (Base.int * Base.int Base.option) -> 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 Base.option * Base.int) -> 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
val insert : into:t -> t -> at_offset:Base.int -> t
insert ~into:t x ~at_offset
insert x
into t
at given offet
val mux : t -> t Base.list -> 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 -> f:(Base.int -> t) -> t
val (&:.) : t -> Base.int -> t
val (|:.) : t -> Base.int -> t
val (^:.) : t -> Base.int -> t
val (+:.) : t -> Base.int -> t
val (-:.) : t -> Base.int -> t
val (==:.) : t -> Base.int -> t
val (<>:.) : t -> Base.int -> t
val (<:.) : t -> Base.int -> t
val (>:.) : t -> Base.int -> t
val (<=:.) : t -> Base.int -> t
val (>=:.) : t -> Base.int -> t
val (<+.) : t -> Base.int -> t
val (>+.) : t -> Base.int -> t
signed less than or equal to
val (<=+.) : t -> Base.int -> t
signed greated than or equal to
val (>=+.) : t -> Base.int -> t
Propositional logic implication operator
val to_string : t -> Base.string
create string from signal
val to_int : t -> Base.int
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
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
val to_sint32 : t -> Base.int32
val to_int64 : t -> Base.int64
val to_sint64 : t -> Base.int64
val to_bool : t -> Base.bool
val to_char : t -> Base.char
Convert signal to a char
. The signal must be 8 bits wide.
val to_bstr : t -> Base.string
create binary string from signal
val bits_msb : t -> t Base.list
convert signal to a list of bits with msb at head of list
val bits_lsb : t -> t Base.list
convert signal to a list of bits with lsb at head of list
val to_array : t -> t Base.array
to_array s
convert signal s
to array of bits with lsb at index 0
val of_array : t Base.array -> t
of_array a
convert array a
of bits to signal with lsb at index 0
val repeat : t -> Base.int -> t
val split_in_half_msb : ?msbs:Base.int -> t -> t * t
Split signal in half. The most significant bits will be in the left half of the returned tuple.
- If
msbs
is not provided, the signal will be split in half with the MSB part possibly containing one more bit. - If
msbs
is provided, msbs
most significant bits will be split off.
val split_in_half_lsb : ?lsbs:Base.int -> t -> t * t
Same as split_in_half_msb
, but
- If
lsbs
is not provided, the LSB part might have one more bit. - If
lsbs
is provided, lsbs
least significant bits will be split off.
The most significant bits will still be in the left half of the tuple.
val split_lsb : ?exact:Base.bool -> part_width:Base.int -> t -> t Base.list
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 -> part_width:Base.int -> t -> t Base.list
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
val srl : t -> Base.int -> t
val sra : t -> Base.int -> t
val rotl : t -> Base.int -> t
val rotr : t -> Base.int -> t
val log_shift : (t -> Base.int -> t) -> t -> t -> t
val uresize : t -> Base.int -> 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 select
s the w
low bits of t
. If w >
width t
, this extends t
with zero (w - width t)
.
val sresize : t -> Base.int -> 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 select
s the w
low bits of t
. If w >
width t
, this extends t
with w - width t
copies of msb t
.
unsigned resize by +1 bit
val resize_list : resize:(t -> Base.int -> t) -> t Base.list -> t Base.list
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
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 -> 'a
val mod_counter : max:Base.int -> 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
then from zero again. If max == (1<<n - 1)
, then a comparator is not generated and overflow arithmetic is used instead.
val compute_arity : steps:Base.int -> Base.int -> Base.int
compute_arity ~steps num_values
computes the tree arity required to reduce num_values
in steps
. steps<=0
raises.
val compute_tree_branches : steps:Base.int -> Base.int -> Base.int Base.list
compute_tree_branches ~steps num_values
returns a list of length steps
of branching factors required to reduce num_values
. This tends to produce a slightly more balanced sequence than just applying compute_arity
at every step.
val tree : arity:Base.int -> f:('a Base.list -> 'a) -> 'a Base.list -> '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) Hardcaml__.Comb_intf.with_valid2 Base.list ->
(t, t) Hardcaml__.Comb_intf.with_valid2
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
if no case is selected.
val priority_select_with_default :
?branching_factor:Base.int ->
(t, t) Hardcaml__.Comb_intf.with_valid2 Base.list ->
default:t ->
t
Same as priority_select
except returns default
if no case matches.
val onehot_select :
?branching_factor:Base.int ->
(t, t) Hardcaml__.Comb_intf.with_valid2 Base.list ->
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
popcount t
returns the number of bits set in t
.
val is_pow2 : ?branching_factor:Base.int -> 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
leading_ones t
returns the number of consecutive 1
s from the most significant bit of t
down.
val trailing_ones : ?branching_factor:Base.int -> t -> t
trailing_ones t
returns the number of consecutive 1
s from the least significant bit of t
up.
val leading_zeros : ?branching_factor:Base.int -> t -> t
leading_zeros t
returns the number of consecutive 0
s from the most significant bit of t
down.
val trailing_zeros : ?branching_factor:Base.int -> t -> t
trailing_zeros t
returns the number of consecutive 0
s from the least significant bit of t
up.
val floor_log2 :
?branching_factor:Base.int ->
t ->
(t, t) Hardcaml__.Comb_intf.with_valid2
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) Hardcaml__.Comb_intf.with_valid2
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
val onehot_to_binary : t -> t
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
create random constant vector of given width
Unsigned vector operations (ie may operate on Bits.t
or Signal.t
directly).
Signed vector operations (ie may operate on Bits.t
or Signal.t
directly).
Combinational logic API without constant propogation optimizations.
Registers
Registers are configured using a Reg_spec.t
. This encodes the clock, synchronous clear, and asychronous reset used.
val reg_fb : ?enable:t -> Reg_spec.t -> width:Base.int -> f:(t -> t) -> t
Pipeline a signal n
times with the given register specification. If set, a list of RTL attributes will also be applied to each register created.
Memories
multiport_memory
provides the low-level primitive from which Hardcaml memories are created. It provides a memory with an arbitrary number of read and write ports that is asychronously read.
By default synthesizers will infer either LUT ram or register banks from this primitive.
Limiting the number of read/write ports and placing a register on the read address or read output port will allow synthesizers to infer single or simple dual port RAM from RTL. ram_rbw
and ram_wbr
are examples of this.
val multiport_memory :
?name:Base.string ->
?attributes:Rtl_attribute.t Base.list ->
Base.int ->
write_ports:t Write_port.t Base.array ->
read_addresses:t Base.array ->
t Base.array
val pp : Base.Formatter.t -> t -> Base.unit