A 64 byte RAM implemented using 512 latches.
Resetting the project does not reset the RAM contents.
To read a byte from memory:
addr
pins to the desired address and set wr_en
lowclk
data_out
(the output pins) reads the value at the memory location.To write a byte to memory:
addr
pins to the desired address, set data_in
(the bidirectional pins) to the desired value, and set wr_en
highclk
data_out
pins will now read the old value at this address.On the cycle immediately after a write the value of wr_en
and data_in
will be ignored - the cycle is always a read. If addr
is left the same then the value read will be the value just written to that location.
Setting values into latches reliably is a little tricky. There are two important considerations:
To ensure the restrictions are met, writes take 2 cycles, and only 1 write can be in flight at once, so the cycle after any write is always treated as a read.
The scheme used is described in detail below.
The write address, addr_write
, is always set to the same value for 2 clocks when doing a write.
When the write is requested addr_write
and data_to_write
are captured. wr_en_next
is set high.
If wr_en_next
was already high the write is ignored, so the inputs to the latches aren't
modified when a write is about to happen.
On the next clock, wr_en_valid
is set to wr_en_next
. addr_write
is stable at this time so the
sel_byte
wires, that contain the result of the comparison of the write address with the byte address for each latch, will already be stable at the point wr_en_valid
goes high.
wr_en_ok
is a negative edge triggered flop that is set to !wr_en_valid
. This will therefore
go low half a clock after wr_en_valid
is set high. And because two consecutive writes are not
allowed it will always be high when wr_en_valid
goes high.
The latch gate is set by and
ing together wr_en_valid
, wr_en_ok
and the sel_byte
for that byte.
This means the latch gate for just the selected byte's latches goes high for the first half of
the write clock cycle. data_to_write is stable across this time (it can not change until the
next clock rising edge), so will be cleanly captured by the latch when the latch gate goes low.
Reading the latches is straightforward. However, a 64:1 mux for each bit is relatively area intensive, so instead for each bit we have 4 16:1 muxes feeding 4 tri-state buffers.
Only the tri-state buffer corresponding to the selected read address is enabled, and the output is taken from the wire driven by those 4 buffers.
To minimize contention, the tri-state enable pin of the buffers is driven directly from a flop which
captures the selected read address directly from the inputs, at the same cycle as the addr_read
flops are set.
The combined output wire then goes to a final buffer before leaving the module, ensuring the outputs are driven cleanly.
# | Input | Output | Bidirectional |
---|---|---|---|
0 | addr[0] | data_out[0] | data_in[0] |
1 | addr[1] | data_out[1] | data_in[1] |
2 | addr[2] | data_out[2] | data_in[2] |
3 | addr[3] | data_out[3] | data_in[3] |
4 | addr[4] | data_out[4] | data_in[4] |
5 | addr[5] | data_out[5] | data_in[5] |
6 | data_out[6] | data_in[6] | |
7 | wr_en | data_out[7] | data_in[7] |