Hardcaml.Signal
Hardware design datatype suitable for simulation and netlist generation
type signal_op =
| Signal_add |
| Signal_sub |
| Signal_mulu |
| Signal_muls |
| Signal_and |
| Signal_or |
| Signal_xor |
| Signal_eq |
| Signal_lt |
simple operators
val sexp_of_signal_op : signal_op -> Sexplib0.Sexp.t
val hash_fold_signal_op :
Ppx_hash_lib.Std.Hash.state ->
signal_op ->
Ppx_hash_lib.Std.Hash.state
val hash_signal_op : signal_op -> Ppx_hash_lib.Std.Hash.hash_value
module Uid : sig ... end
module Uid_set : sig ... end
module Uid_map : sig ... end
type signal_id = {
s_id : Uid.t; | |
mutable s_names : Base.string Base.list; | |
s_width : Base.int; | |
mutable s_attributes : Rtl_attribute.t Base.list; | |
mutable s_comment : Base.string Base.option; | (* 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 *) |
mutable s_deps : t Base.list; | |
caller_id : Caller_id.t Base.option; |
}
internal structure for tracking signals
and t =
| Empty | ||||
| Const of {
} | ||||
| Op2 of {
} | ||||
| Mux of {
} | ||||
| Cat of {
} | ||||
| Not of {
} | ||||
| Wire of {
} | ||||
| Select of {
} | ||||
| Reg of {
} | ||||
| Mem of {
} | ||||
| Multiport_mem of {
} | ||||
| Mem_read_port of {
} | ||||
| Inst of {
} |
main signal data type
and register = {
reg_clock : t; | (* clock *) |
reg_clock_edge : Edge.t; | (* active clock edge *) |
reg_reset : t; | (* asynchronous reset *) |
reg_reset_edge : Edge.t; | (* asynchronous reset edge *) |
reg_reset_value : t; | (* asychhronous reset value *) |
reg_clear : t; | (* synchronous clear *) |
reg_clear_level : Level.t; | (* synchronous clear level *) |
reg_clear_value : t; | (* sychhronous clear value *) |
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 instantiation = {
inst_name : Base.string; | (* name of circuit *) |
inst_instance : Base.string; | (* instantiation label *) |
inst_generics : Parameter.t Base.list; | (*
|
inst_inputs : (Base.string * t) Base.list; | (* name and input signal *) |
inst_outputs : (Base.string * (Base.int * Base.int)) Base.list; | (* name, width and low index of output *) |
inst_lib : Base.string; | |
inst_arch : Base.string; |
}
type signal = t
val names : t -> Base.string Base.list
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
Returns attributes associated to the signal
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
val comment : t -> Base.string Base.option
Returns comment associated with the signal
val has_name : t -> Base.bool
val is_reg : t -> Base.bool
is the signal a register?
val is_mem : t -> Base.bool
is the signal a memory, or multiport memory?
val is_multiport_mem : t -> Base.bool
is the signal a multiport memory?
val is_mem_read_port : t -> Base.bool
is the signal a memory read port?
val is_inst : t -> Base.bool
is the signal an instantiation?
val is_const : t -> Base.bool
is the signal a constant?
val is_select : t -> Base.bool
is the signal a part selection?
val is_wire : t -> Base.bool
is the signal a wire?
val is_cat : t -> Base.bool
is the signal concatenation?
val is_mux : t -> Base.bool
is the signal a multiplexer?
val is_not : t -> Base.bool
is the signal a not>
val new_id : Base.unit -> Uid.t
creates a new signal uid
val structural_compare :
?check_names:Base.bool ->
?check_deps:Base.bool ->
?initial_deps:Uid_set.t ->
t ->
t ->
Uid_set.t * Base.bool
perform a recursive structural comparison of two signals
val sexp_of_signal_recursive :
?show_uids:Base.bool ->
depth:Base.int ->
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.
Combinatorial signal API. This API automatically performs constant propogations (eg: replacing (a + 1 + 5) with (a + 6)). This reduces the amount of work that needs to be done during simulation by simply reducing the number of simulation nodes.
To use raw signals, ie: keeping the simulation nodes as described, use Raw
below.
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 empty : t
the empty signal
val is_empty : t -> Base.bool
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 ...
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
.
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
convert binary string to constant
val of_bit_string : Base.string -> t
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:Constant.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:Constant.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 -> Hardcaml__.Zarith.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
val to_z : t -> signedness:Constant.Signedness.t -> Hardcaml__.Zarith.Z.t
Convert bits to a Zarith.t
val constv : Base.string -> t
val consti : width:Base.int -> Base.int -> t
val consti32 : width:Base.int -> Base.int32 -> t
val consti64 : width:Base.int -> Base.int64 -> t
val constibl : Base.int Base.list -> t
val consthu : width:Base.int -> Base.string -> t
val consths : width:Base.int -> Base.string -> t
val const : Base.string -> t
val constd : width:Base.int -> Base.string -> 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.
Similar to concat_msb
except the lsb of the head of the list will become the lsb of the result.
val vdd : t
logic 1
val is_vdd : t -> Base.bool
val gnd : t
logic 0
val is_gnd : t -> Base.bool
val zero : Base.int -> t
zero w
makes a the zero valued constant of width w
val ones : Base.int -> t
ones w
makes a constant of all ones of width w
val one : Base.int -> t
one w
makes a one valued constant of width w
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
.
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
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
insert ~into:t x ~at_offset
insert x
into t
at given offet
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
.
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 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
.
width t > Int.num_bits
then the upper bits are truncated.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.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
.
width t > Int.num_bits
then the upper bits are truncated.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
split signal in half. The most significant bits will be in the left half of the returned tuple.
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 ]
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
.
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)
.
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
.
resize_list ?resize l
finds the maximum width in l
and applies resize el max
to each element.
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
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.
compute_arity ~steps num_values
computes the tree arity required to reduce num_values
in steps
. steps<=0
raises.
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.
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.
is_pow2 t
returns a bit to indicate if t
is a power of 2.
leading_ones t
returns the number of consecutive 1
s from the most significant bit of t
down.
trailing_ones t
returns the number of consecutive 1
s from the least significant bit of t
up.
leading_zeros t
returns the number of consecutive 0
s from the most significant bit of t
down.
trailing_zeros t
returns the number of consecutive 0
s from the least significant bit of t
up.
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
.
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 random : width:Base.int -> t
create random constant vector of given width
module type TypedMath = sig ... end
val wire : Base.int -> t
creates an unassigned wire
val input : Base.string -> Base.int -> t
creates an input
module Unoptimized : Comb.S with type t = t
Comb logic API without constant propogation optimizations.
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 : ?enable:t -> Reg_spec_.t -> width:Base.int -> f:( t -> t ) -> t
val pipeline :
?attributes:Rtl_attribute.t Base.list ->
Reg_spec_.t ->
n:Base.int ->
?enable: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.
val memory : Base.int -> write_port:write_port -> read_address:t -> t
val ram_wbr :
?name:Base.string ->
?attributes:Rtl_attribute.t Base.list ->
write_port:write_port ->
read_port:read_port ->
Base.int ->
t
val ram_rbw :
?name:Base.string ->
?attributes:Rtl_attribute.t Base.list ->
write_port:write_port ->
read_port:read_port ->
Base.int ->
t
val multiport_memory :
?name:Base.string ->
?attributes:Rtl_attribute.t Base.list ->
Base.int ->
write_ports:write_port Base.array ->
read_addresses:t Base.array ->
t Base.array
val pp : Base.Formatter.t -> t -> Base.unit
Pretty printer.