DRAM FIFO
- ENTITY DRAM_FIFO IS
FIFO logic built around an external DDR memory using the Avalon Memory-Mapped (AVMM) interface. Data is written to memory one beat at a time. The first beat of each N packets is the packet length metadata beat, followed by the packet payload beats. Write addresses increment manually after each accepted beat.
Generics
PortsGeneric
Type
Default
Description
DDR_ADDR_WIDTH
natural
27
Width of AVMM_ADDRESS. Set by the card’s memory controller.
DDR_BURST_WIDTH
natural
7
Width of AVMM_BURSTCOUNT. Transactions are single-beat, so the output is tied to 1.
DDR_DATA_WIDTH
natural
512
Width of the AVMM data path, and therefore of one ring item.
AXI_DATA_WIDTH
natural
512
Width of both AXI-Stream ports.
FIFO_ITEMS
natural
2 ** 13
Depth in DDR words. Must not exceed the memory behind the AVMM port.
PKT_MTU
natural
2 ** 12
Largest packet in bytes (4 KB). Sets LEN_WIDTH = log2(PKT_MTU+1) and the width of TX_PKT_LEN. A frame larger than this has its stored length wrapped, which corrupts every later packet in the metaword.
DEVICE
string
“AGILEX”
Target device string, passed to the FIFO primitives.
FIFO_BUFF_IN_ITEMS
natural
2**16
Depth of the on-chip write buffer. An assert checks that at least one MTU packet fits.
FIFO_BUFF_OUT_ITEMS
natural
2**12
Depth of the read prefetch buffer. An assert checks that at least one MTU packet fits.
Port
Type
Mode
Description
=====
FIFO interface
=====
=====
CLK
std_logic
in
RESET
std_logic
in
=====
Write interface
=====
=====
RX_AXI_DATA
std_logic_vector(AXI_DATA_WIDTH - 1 downto 0)
in
RX_AXI_KEEP
std_logic_vector(AXI_DATA_WIDTH/8 - 1 downto 0)
in
RX_AXI_LAST
std_logic
in
RX_AXI_VALID
std_logic
in
RX_AXI_READY
std_logic
out
=====
Read interface
=====
=====
TX_AXI_DATA
std_logic_vector(AXI_DATA_WIDTH - 1 downto 0)
out
TX_AXI_KEEP
std_logic_vector(AXI_DATA_WIDTH/8 - 1 downto 0)
out
TX_AXI_LAST
std_logic
out
TX_AXI_VALID
std_logic
out
TX_AXI_READY
std_logic
in
TX_PKT_LEN
std_logic_vector(log2(PKT_MTU+1)-1 downto 0)
out
Packet length output, valid with first payload beat (TX_AXI_VALID=1 and SOF)
=====
DRAM interface
=====
=====
AVMM_ADDRESS
std_logic_vector(DDR_ADDR_WIDTH - 1 downto 0)
out
Request interface
AVMM_BURSTCOUNT
std_logic_vector(DDR_BURST_WIDTH - 1 downto 0)
out
AVMM_WRITE
std_logic
out
AVMM_WRITEDATA
std_logic_vector(DDR_DATA_WIDTH - 1 downto 0)
out
AVMM_READ
std_logic
out
AVMM_READY
std_logic
in
AVMM_READDATA
std_logic_vector(DDR_DATA_WIDTH - 1 downto 0)
in
Response interface
AVMM_READDATAVALID
std_logic
in
DDR_FULL
std_logic
out
Low when another word of FIFO_ITEMS b can be written.
DDR_READ_EN
std_logic
in
Set by user - when high reading via the AVMM_* interface is enabled
See the DRAM Packet Capture application for how this component is used and sized.
Structure
The write side is three stages:
AXIS_PACKET_LENmeasures each incoming packet and emits its length.fifo_buffer_in(AXIS_FIFO,FIFO_BUFF_IN_ITEMSdeep) holds the payload beats while the length is being packed.a shift register packs up to
META_LEN_COUNTlengths into one metaword, which is pushed intometa_fifo(FIFOX, 1024 entries).
The read side is two stages: fifo_buffer_out (FIFOX,
FIFO_BUFF_OUT_ITEMS deep) prefetches DDR words, and a two-state formatting
FSM turns them back into AXI-Stream packets.
Metaword format
The structure of a metaword depends entirely on PKT_MTU.
The following example uses PKT_MTU = 4096 B
+---------+--------+---------+-----+--------+
| PADDING | COUNT | P[37] | ... | P[0] |
| 12b | 6b | 13b | | 13b |
+---------+--------+---------+-----+--------+
MSB LSB
Note
The field widths are derived, not fixed.
Constant |
Derivation |
Default |
|---|---|---|
|
|
|
|
|
|
|
|
|
|
remainder |
|
COUNT says how many of the length slots are valid, so a partial metaword is
legal — which is what makes the flush below safe.
Write path
A packet is written to DDR only when both halves are ready: fifo_buffer_in
holds at least one complete packet and meta_fifo holds a metaword
describing it.
Each burst to memory is the metaword beat first, then that group’s payload
beats. metaword_sent selects between meta_fifo_do and fifo_tdata on
AVMM_WRITEDATA, and the payload FIFO is popped only during payload beats.
Metaword flush
A metaword is normally emitted once all 38 slots are filled. Waiting for a 38th
packet that may never arrive would strand the previous 37 in on-chip memory, so
flush_timer counts cycles since the last new length and forces out a partial
word after FLUSH_TIMEOUT (1024) idle cycles.
Read path
Prefetching is credit-based. out_fifo_credits starts at
FIFO_BUFF_OUT_ITEMS, decrements when a read is issued and increments when a
beat is popped.
A read is issued only when all three hold:
diff > outstanding_read_cnt— there is unread data in DDR not already requested.out_fifo_credits > 1— there is somewhere to put the result.DDR_READ_EN = '1'.
The formatting FSM has two states:
FMT_METAPops a metaword, extracts
COUNTand the length array, loads the first length, and moves toFMT_PAYLOAD.FMT_PAYLOADEmits payload beats. While more than one beat remains,
TX_AXI_KEEPis all ones. On the final beatTX_AXI_LASTasserts andones_mask()builds a partialTX_AXI_KEEPfrom the bytes left. State advances only on an AXI handshake. When the last packet of the group completes it returns toFMT_META.
Ring accounting and flow control
write_ptr and read_ptr are one bit wider than the address space. The
extra bit is a wrap flag, so diff = write_ptr - read_ptr distinguishes full
from empty:
can_write <= '1' when diff < (FIFO_ITEMS - 1) else '0';
can_read <= '1' when diff > 0 else '0';
DDR_FULL <= not can_write;
Pointers advance on accepted AVMM transactions (AVMM_WRITE and AVMM_READY),
not on request assertion. Both pointers are cleared only by RESET;
DDR_READ_EN and DDR_FULL never move them, so data left in the ring is
read out before anything written later.
AVMM arbitration
One AVMM port serves both directions, so writes and reads are arbitrated:
grant_write <= lock_is_write when req_lock = '1' else write_wants;
grant_read <= not lock_is_write when req_lock = '1' else read_wants and not write_wants;
Writes win. A read is granted only when no write is pending, which maximizes RX throughput.
req_lock holds a grant until the memory accepts it. Without it, a request
could switch direction mid-transaction while AVMM_READY was still low (AVMM protocol violation), and
the address would change under an in-flight request.
Sizing constraints
Two elaboration-time assertions enforce that both buffers hold at least one full MTU packet:
assert FIFO_BUFF_IN_ITEMS >= PKT_MTU/(AXI_DATA_WIDTH/8)
assert FIFO_BUFF_OUT_ITEMS >= PKT_MTU/(AXI_DATA_WIDTH/8)
Note
FIFO_ITEMS must not exceed the memory actually behind the AVMM port. The
address is truncated, so an oversized ring aliases onto itself and
overwrites unread data. In this application the depth is derived from
MEM_TARGET; see Memory interface.
Simulation
The component has its own cocotb testbench in cocotb/, which drives the AXI
ports directly and answers the AVMM port with a memory model, so no card or
application core is involved.
cd apps/dram_pkt_capture/comp/dram_fifo/cocotb
make
run_test sends 10000 random frames of 100–4096 B with DDR_READ_EN tied
high, and checks that what comes out of the read port matches what went in.
Generics are left at their entity defaults, so PKT_MTU is 4096 — exactly
the largest frame the test generates.
For the whole capture path end to end, see Top-level simulation.