Why an external SRAM needs a controller at all
An on-chip block RAM is just a synthesized memory with clean synchronous ports. An external SRAM chip on a board is a different animal: it has its own access-time specification (it needs a certain number of nanoseconds after the address and control lines settle before its output data is valid), a single bidirectional data bus shared between reads and writes (so something has to make sure only one side drives it at a time), and separate active-low control pins (CE#, OE#, WE#) that must be sequenced correctly. A controller FSM’s job is to translate a simple internal request/response interface into that external timing and bus-direction discipline.
Interface
| Signal |
Direction |
Description |
clk/rst |
input |
Clock and synchronous, active-high reset. |
req_valid |
input |
Request a new access. |
req_we |
input |
1 = write, 0 = read. |
req_addr |
input |
Address for this access. |
req_wdata |
input |
Data to write (write accesses only). |
req_ready |
output |
High when the controller is idle and can accept a new request. |
req_rdata |
output |
Captured read data (read accesses only), valid the cycle req_rvalid pulses. |
req_rvalid |
output |
Pulses for one cycle when req_rdata is valid. |
sram_addr |
output |
Address driven to the external SRAM. |
sram_data |
inout |
Bidirectional data bus to/from the external SRAM. |
sram_ce_n |
output |
Active-low chip-enable. |
sram_oe_n |
output |
Active-low output-enable (asserted for reads). |
sram_we_n |
output |
Active-low write-enable (asserted for writes). |
Access sequencing
| State |
ce_n |
oe_n (read) |
we_n (write) |
Bus driven? |
Purpose |
S_IDLE |
1 |
1 |
1 |
no |
Idle; accepts a new request. |
S_ACCESS0 |
0 |
0 or 1 |
1 or 0 |
write only |
Wait state 1 — memory access time. |
S_ACCESS1 |
0 |
0 or 1 |
1 or 0 |
write only |
Wait state 2 — memory access time. |
S_CAPTURE |
0→1 |
0→1 |
0→1 |
write only, then released |
Sample read data / finish write; return to idle. |
Correctness constraints
- Bus direction discipline: the controller must drive
sram_data only during a write (and release it to high-Z otherwise), so the external device (or, in the testbench, the behavioral SRAM model) is free to drive the bus during a read without contention. Driving the bus during a read is a bus-contention bug.
- The controller must insert enough wait states (
S_ACCESS0, S_ACCESS1) between asserting the address/control lines and sampling read data for the external memory’s access time to be respected — sampling sram_data too early (in the same cycle control lines are first asserted) risks capturing stale or undefined data.
req_ready must deassert for the entire duration of an in-flight access and only reassert once the controller returns to S_IDLE.
req_rvalid must pulse for exactly one cycle, only for read accesses, and only once the captured data is stable.
- Write data (
wdata_latched) must remain stable on sram_data for the entire write-access window, not just the first cycle.