123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145(** A vector like structure with insert and delete operations at arbitrary positions.
This table stores indexes and optional tags and should be connected to a RAM. The
structure is built from registers. For [N] slots, it requires [N * log2 N * width tag]
register bits.
*)openBaseopenHardcamlmoduletypeArg=sig(** Tag associated with each index. Set to [Hardcaml.Interface.None] to disable. *)moduleTag:Interface.S(** Register spec associated with tag values. *)valspec:index:int->Reg_spec.t->Reg_spec.tTag.t(** Size of the vec. *)valvec_size:intendmoduletypeS=sigtypettypetag(** {2 Hardware design and queries}
The vec is a small, growable array of elements. Elements may be inserted and removed
from any position. Note that insertion and deletion shuffle elements up/down.
Unlike a normal data structure, it isn't supposed to hold the actual data. Instead
it holds the index at which the data should reside. It should be connected to an
external ram with the address connected to:
- [insertion_index] when inserting
- [deletion_index] when deleting
- [access_index] (with the address on [op.slot] and [op.op=noop]) when you want to
access the data at a slot.
To insert an element, set [op.op = insert] and *during the same cycle* write the data
to the (external) memory at [insertion_index]. Similarly to delete an element.
In some cases it is necessary to associate some extra bits that move with the
indices - this can be done using tags. Tags may be changed (though the [tag_next]
function) so long as the circuit is not currently inserting or deleting. To modify
the tag value during insertion or deletion, see [insertion_tag] and [deletion_tag]
in the type [Make_tagged.op] below. *)(** Return an array of current indexes *)valindexes:t->Signal.tarray(** Get the index for the given position in the vec *)valindex:at:Signal.t->t->Signal.t(** Return an array of current tags *)valtags:t->tagarray(** Get the tag for the given position in the vec *)valtag:at:Signal.t->t->tag(** Index at which insertion will take place. It is valid _while_ the insert op is
performed. *)valinsertion_index:t->Signal.t(** Index at which deletion will take place. It is valid _while_ the delete op is
performed. *)valdeletion_index:t->Signal.t(** Index corresponding to [op.slot]. *)valaccess_index:t->Signal.t(** {2 Length status}
The length is tracked during inserts and removes. To compute the length it tracks the
insertion/deletion slot. Removing from an 'empty' slot does not decrease the size.
Using length is optional and probably not required unless treating the vec as a queue
or stack (which can also be implemented more efficiently with a fifo like structure).
[full] and [empty] are derived combinationally from [length]. *)(** Length of the vec *)vallength:t->Signal.t(** Is the vec full? *)valfull:t->Signal.t(** Is the vec empty? *)valempty:t->Signal.tendmoduletypeIndex_vec=sigmoduletypeArg=ArgmoduletypeS=S(** Index vector circuit which tracks indexes and arbitrary user tags *)moduleMake_tagged(Arg:Arg):sigincludeSwithtypetag:=Signal.tArg.Tag.t(** Operation performed on the [vec] circuit. *)typeop={slot:Signal.t(** Slot to perform operation at *);op:Signal.t(** Operation type (insert, remove or nothing) *);insertion_tag:Signal.tArg.Tag.toption(** Value to set tag to on insertion. If [None] the value associated with the free
slot moving in is kept *);deletion_tag:Signal.tArg.Tag.toption(** Value to set the tag to on deletion. If [None] the value associated with the
slot being kicked out is kept *)}(** Create the vec with the given size.
The tag values may be arbitrarily updated with [tag_next] on cycles in which no
insertion or deletion is taking place ([tag_next] is similar the the callback
argument provided to [reg_fb] - it takes the current value and optionally modifies
it to produce the next value). *)valcreate:?index_next:(index:int->Signal.t->Signal.t)->tag_next:(index:int->Signal.tArg.Tag.t->Signal.tArg.Tag.t)->Reg_spec.t->op->tend(** Index vector circuit with tracks indexes. *)moduleMake(X:sigvalvec_size:intend):sigincludeSwithtypetag:=Signal.tInterface.Empty.t(** Operation performed on the [vec] circuit. *)typeop={slot:Signal.t(** Slot to perform operation at *);op:Signal.t(** Operation type (insert, remove or nothing) *)}(** Create the vec with the given size.
[index_next] is an optional function that can be used to update the index values
themselves in cycles which no insertion or deletion is taking place. This is
useful for multiplexing vector operations over data stored in RAM. *)valcreate:?index_next:(index:int->Signal.t->Signal.t)->Reg_spec.t->op->tendend