Circular Buffer with Overwrite
Telemetry logging systems and audio streaming pipelines often require a history buffer that retains only the most recent N samples. Unlike a strict FIFO that stalls the upstream producer when full, an overwriting circular buffer continuously accepts new data by silently discarding the oldest unread samples. This ensures the system always has access to the latest data without requiring complex software management.
The module manages a 4-deep, 8-bit wide ring buffer. It maintains internal write and read pointers to track data. When a write occurs, data is stored and the write pointer advances. If the buffer is full during a write, the oldest data is overwritten, and the read pointer is pushed forward to point to the new oldest entry. When a valid read occurs, the data at the read pointer is fetched, and the read pointer advances.
Timing and reset rules: • Clock edge: Positive-edge triggered clk. • Reset: Asynchronous active-low rst_n. • On reset, empty is 1, full is 0, r_valid is 0, r_data is 0, and all internal pointers are 0. • empty is 1 when the buffer contains 0 valid elements. • full is 1 when the buffer contains exactly 4 valid elements. • empty and full are combinational outputs reflecting the immediate state of the internal pointers. • r_data and r_valid are registered outputs updated on the clock edge. • When r_en is 1 and empty is 0, r_data updates with the read value and r_valid becomes 1 on the next clock cycle. • If r_en is 1 and empty is 1, the read is ignored; r_valid becomes 0 and r_data holds its previous value. • If w_en and r_en are asserted simultaneously when the buffer is full, the read operation fetches the current oldest data, and the write operation overwrites that same slot with new data. Both pointers increment by 1.
Worked trace: • Cycle 1: rst_n=0 → empty=1, full=0, r_valid=0, r_data=0 • Cycle 2: rst_n=1, w_en=1, w_data=10 → empty=0, full=0 • Cycle 3: w_en=1, w_data=20 → empty=0, full=0 • Cycle 4: w_en=1, w_data=30 → empty=0, full=0 • Cycle 5: w_en=1, w_data=40 → empty=0, full=1 (Buffer is now full) • Cycle 6: w_en=1, w_data=50, r_en=0 → empty=0, full=1 (Overwrites 10; read pointer is pushed forward) • Cycle 7: w_en=0, r_en=1 → empty=0, full=0, r_valid=1, r_data=20 (Reads 20, the new oldest data) • Cycle 8: w_en=0, r_en=1 → empty=0, full=0, r_valid=1, r_data=30
| Signal | Direction | Width | Description | |--------|-----------|-------|-------------| | clk | input | 1 | Positive-edge triggered clock | | rst_n | input | 1 | Asynchronous active-low reset | | w_en | input | 1 | Write enable | | w_data | input | 8 | Data to be written to the buffer | | r_en | input | 1 | Read enable | | r_data | output | 8 | Registered read data output | | r_valid | output | 1 | Registered valid flag for read data | | empty | output | 1 | Combinational flag; 1 when buffer is empty | | full | output | 1 | Combinational flag; 1 when buffer is full |
Constraints
- The buffer depth is exactly 4 elements.
emptyandfullmust be combinational logic driven by the current pointer states.r_dataandr_validmust be registered logic updated on the positive clock edge.- When full, a write without a simultaneous read forces the read pointer to increment by 1.
- When a read is ignored due to an empty buffer,
r_validmust be set to 0 on the next clock cycle, andr_datamust hold its previous value.
Topics
Solve this problem
Write the module in Verilog, SystemVerilog or VHDL. Your submission is compiled and simulated against a real testbench — you get the waveform back, not a stored answer.
This problem is part of Codiode Pro. The statement above is free to read.
The circuit builder and code editor need a desktop screen. On a phone, read the problem here and open it on a laptop to solve.