A general-purpose means of representing memories. The Config.t
type allows the user to configure the underlying memory implementations. Eg: Using URAM for bits 0-72, and BRAMs for bits 73-80. This module allows construction of memories in 1D or 2D Modes. See further documentation below.
Configuration for the memory builder. See documentation below for elaboration about the purpose of configuration fields.
val read_latency : _ t -> Base.int
Returns the read latency of the memory, including possibly any combinational latency due to muxing.
val complete : _ t -> Base.unit
Must be called strictly after all set_{read/write}_
functions have been called. This assigns to the underlying instantiated memories, and asserts that the memory satisfies the config's requirements.
Straightforward 1D Memories
1-dimensional memories are laid out the way one would expect. A memory of depth d
, with columns shared between URAM and BRAM will have the following layout:
| URAM | BRAM | | x0
| | x1
| | .... | | xd-1
| ------------------------
Read_port_1d
does not implement Hardcaml.Interface.S, since the address
and data
are not necessarily known without a config. To get something one can use in Hardcaml interfaces, you'll have to specialize it with Specialize
.
General purpose 1D read ports.
General purpose 1D Write ports. Note that it is possible to write 'a Write_port_1d.M(Foo).t
in type declarations in mlis.
Assigns the read port for 1D memories. Raises a runtime exception when called on a non single dimensional memory.
Similar to set_read_port_1d
, but for write ports.
General-Purpose "2D" Memories
The more general-purporse memory builders can be used to construct memories with the layout H * V
where every entry is B
bits wide. One can read from any of the entires through the read ports. When writing, however, an entire horizontal index must be written to at once, namely x*, v
. This is because the memory is represented using parallel columns of memories along the H axis.
Under the hood, this constructs a memory of depth V
and width H * B
using the underlying_memories
constructs provided by the Config.t
. When reading (h, v), the memory reads the underlying memory at index v
, which returns H
copies of the the data, and multiplexes them with h
. For that reason, V
must be a power of two, but H
does not have to be.
Here's a possible underlying layout by using this memory builder, for H=3, V=8, implemented with 2 URAMs and 1 BRAMs.
| URAM | BRAM | BRAM | | x0
0
| x1
0
| x2
0
| | x0
1
| x1
1
| x2
1
| | x0
2
| x1
2
| x2
2
| | x0
3
| x1
3
| x2
3
| | x0
4
| x1
4
| x2
4
| | x0
5
| x1
5
| x2
5
| | x0
6
| x1
6
| x2
6
| | x0
7
| x1
7
| x2
7
| --------------------------------------
Since writes to a row are atomic, for a given v
and d
, all entries at x{0, 1, 2}
d
must be written to simultaneously.
Note that 1D memories are simply a special case of 2D memories where H = 1.
General-purpose Read Ports.
General purpose write ports. Note that it is possible to write 'a Write_port_2d.M(Foo).t in type declarations in mlis
Set the read port. This should only ever be called once.
module M (X : Base.T1) : sig ... end
Shorthand useful for writing Memory_builder.M(Foo).t