Memories, FIFOs and arbitration
- Compare memory technologies and port arrangements
- Define read latency and read-during-write behavior
- Calculate FIFO occupancy and flags
- Prevent overflow and underflow
- Compare fixed-priority with round-robin arbitration
Explanation
Storage choices
| Structure | Typical property | Common use |
|---|---|---|
| Flip-flop register | Fast, individually controlled, area intensive | Small state and pipelines |
| Register file | Multiple addressed registers and ports | Processor or datapath operands |
| SRAM | Dense volatile on-chip memory | Buffers, caches, tables |
| DRAM | Very dense, refreshed, normally off core logic | Large system memory |
| ROM | Fixed or initialization-defined content | Lookup tables, boot data |
| Cache | Stores copies to reduce average access time | Processor and system hierarchy |
RTL arrays may infer a memory macro on one target and flip-flops on another. Coding style, dimensions, ports, reset behavior, and target library determine inference.
Ports and read timing
- Single-port: one access through one port per cycle.
- Simple dual-port: commonly one write port and one read port.
- True dual-port: either port may read or write, subject to collision rules.
An asynchronous read output changes combinationally with the address. A synchronous read returns data after a clock edge, often one cycle later. The interface must state request-to-data latency.
Read-during-write behavior
When a read and write target the same address on the same cycle, a memory may return:
- old data, often called read-first;
- new write data, often called write-first;
- unchanged output, sometimes called no-change;
- unspecified data.
This behavior varies by macro and FPGA primitive. If correctness depends on it, specify it and confirm that the inferred or instantiated memory supports it. Cross-port collisions may have different rules.
FIFO behavior
A FIFO preserves order while decoupling producer and consumer timing. A synchronous FIFO uses one clock. An asynchronous FIFO connects different write and read clocks with specialized pointer synchronization.
For a depth D FIFO, occupancy changes as follows:
| Accepted write | Accepted read | Occupancy change |
|---|---|---|
| 0 | 0 | 0 |
| 1 | 0 | +1 |
| 0 | 1 | -1 |
| 1 | 1 | 0 |
“Accepted” matters. A write request while full must be rejected or explicitly overwrite according to policy. A read request while empty must be rejected or flagged. Simultaneous operations at boundaries need precise definitions.
Pointers and flags
A circular FIFO uses write and read pointers. Equal address portions can mean either empty or full, so implementations use an extra wrap bit, an occupancy counter, or another disambiguation method.
Useful flags include empty, full, almost_empty, and almost_full. Almost-full gives a producer time to stop before overflow when backpressure has latency.
For an asynchronous FIFO, binary pointers address memory, while Gray-coded pointer values cross domains through synchronizers. Full and empty are generated in their relevant local clock domains. This structure requires careful CDC constraints and established design patterns.
Arbitration
An arbiter selects among requesters sharing a resource.
Fixed priority is small and predictable: requester 0 may always beat requester 1. It can starve a lower-priority requester if higher-priority traffic is continuous.
Round robin rotates the next preference after a grant, improving fairness among persistent requesters. It requires state and a clear policy for idle cycles and withdrawn requests.
Other policies include weighted priority, age-based selection, and time division. The specification should define:
- whether a grant is a pulse or held until completion;
- whether requests may be withdrawn;
- how simultaneous requests are handled;
- maximum service latency or fairness expectation;
- what happens on reset and error.
Bandwidth and buffering
Required buffer depth depends on production rate, consumption rate, burst size, and backpressure delay. Average rates alone are insufficient. A producer and consumer can have equal average bandwidth while temporary bursts overflow a shallow buffer.
Worked example
A depth-8 FIFO starts with occupancy 6. During four cycles, request pairs (write, read) are:
cycle 1: (1,0)
cycle 2: (1,1)
cycle 3: (1,0)
cycle 4: (1,1)
Assume a read and write are both accepted when occupancy is between 1 and 7. At full, a simultaneous read frees one entry and the write is accepted.
| Cycle | Start | Accepted W/R | End |
|---|---|---|---|
| 1 | 6 | 1/0 | 7 |
| 2 | 7 | 1/1 | 7 |
| 3 | 7 | 1/0 | 8, full |
| 4 | 8 | 1/1 | 8, full |
If the interface instead blocks every write whenever full is high at the start of a cycle, cycle 4 ends at 7. Both policies are implementable. The interface must state which one applies, and verification must target the boundary.
Check yourself
Why must read-during-write behavior be part of a memory specification? How can a FIFO's read and write pointers be equal in both the empty and full conditions?