Write Coalescing

Merge the data in fifo order

Introduction

In this PPA challenge, you are given a write coalescing buffer. It holds writes, each with an address, byte enables and data, and hands them out oldest first. When a new write arrives for an address that's already waiting in the buffer, it's coalesced into that entry instead of taking a new one: its enabled bytes overwrite the stored bytes and the entry's byte enables become the union of both. Each address is therefore held at most once, and writes leave the buffer in the order their addresses were first written.

For example, with the buffer holding writes to addresses A and B, a write to A with byte enables 4'b0011 followed by another write to A with 4'b0100 leaves a single entry for A with byte enables 4'b0111, still ahead of B.

The write address arrives late in the cycle. The boilerplate code works but is slow: it keeps the writes in a fifo, works out which slots are occupied from the pointers and count, compares the push address with every slot, picks the first match with a priority encoder and writes the merged bytes into that slot. Your task is to keep the same behaviour while improving the timing and area of the design. You can store the writes in any structure you like, for example a table of entries with head and tail pointers on top of it. The synthesis and timing report after each run shows how your design compares with the reference solution.

All the flops should be positive edge triggered with active high resets (if any).

Interface Definition

DEPTH        => Number of writes the buffer can hold
ADDR_W       => Width of the write address
DATA_W       => Width of the write data, a multiple of 8; there are DATA_W/8 byte enables

push_i       => Push a write (or coalesce it into the held write to the same address)
push_addr_i  => Address of the write
push_be_i    => Byte enables of the write: push_be_i[b] covers push_data_i[8*b +: 8]
push_data_i  => Data of the write

pop_i        => Pop the oldest write
pop_addr_o   => Address of the oldest write
pop_be_o     => Byte enables of the oldest write: every byte written to that address so far
pop_data_o   => Data of the oldest write (only the enabled bytes are checked)

count_o      => Number of writes currently held

Interface Requirements

  • A push to an address that's already held coalesces into that entry: its enabled bytes overwrite the stored ones and the byte enables are OR-ed together. It doesn't take a new entry, and the entry keeps its place in the order
  • A push to an address that isn't held takes a new entry, behind every held write
  • The entry being popped in the same cycle can't be coalesced into: a push to its address takes a new entry instead
  • Push and pop can happen in the same cycle
  • pop_addr_o, pop_be_o and pop_data_o must show the oldest write whenever the buffer isn't empty
  • count_o must reflect the writes held at the start of the cycle (it updates the cycle after a push or pop)
  • push_i will only be asserted when the push coalesces into a held entry or there's room for a new one (counting the entry popped in the same cycle)
  • pop_i will only be asserted when the buffer isn't empty (count_o > 0)
  • push_be_i always has at least one bit set

Loading the interactive workspace...