Search backwards for grant seeds in icache memory model

The code before this patch maintained a mailbox, where it would add an
item for each request it saw, and then pop items off until finding the
right address whenever it saw a grant.

Most of the time, you might expect to see a sequence like this:

    request 100
    grant   100
    request 104
    grant   104
    request 108
    grant   108

This scheme is also resilient when glitches (to do with the
delta-cycle scheduling in the simulator) mean you actually see
something like:

    request 999
    request 100
    grant   100
    request 104
    grant   104
    ...

However, there's another source of "mismatch" possible too: the cache
can change the request address if the request hasn't been granted (as
opposed to a ready/valid interface, where this sort of tomfoolery is
not allowed!).

When the cache is branching all over the place, as in the sanity
sequence, this doesn't really matter. But if the branch destinations
are constrained, as in the passthru sequence, you can see things like
this:

    request 100     (1)
    request 120     (2)
    request 100     (3)
    grant   100     (4)
    request 104
    grant   104
    ...

Note that the mailbox has two entries for address 100 when searching
at point (4). This might be ok, but will cause failures if we get a
new seed at (2) or (3).

This patch replaces the mailbox with a queue. New requests get
inserted at the end, as before, but grants search from the end, rather
than the start. This means that when we get to (4) in the example
above, we'll pick the latest seed (and duplicate entries disappear
quickly).
This commit is contained in:
Rupert Swarbrick 2020-05-19 09:37:35 +01:00 committed by Rupert Swarbrick
parent c385354b3c
commit e3fe0c5032

View file

@ -19,10 +19,11 @@ class ibex_icache_mem_resp_seq extends ibex_icache_mem_base_seq;
// before the grant, we might accidentally handle the grant with the new seed rather than the
// old one, which would cause confusion in the scoreboard.
//
// To avoid this problem, we keep a FIFO of pending addresses along with their corresponding
// seeds. When a grant message comes in, we know that we can look up the correct seed there.
protected mailbox #(bit[63:0]) pending_grants;
protected bit [31:0] cur_seed = 0;
// To avoid this problem, we keep a queue of pending addresses along with their corresponding
// seeds. When a grant message comes in, we know that we can look up the correct seed there,
// discarding results until we find it.
protected bit [63:0] pending_grants[$];
protected bit [31:0] cur_seed = 0;
`uvm_object_utils(ibex_icache_mem_resp_seq)
`uvm_object_new
@ -36,8 +37,6 @@ class ibex_icache_mem_resp_seq extends ibex_icache_mem_base_seq;
ibex_icache_mem_req_item req_item = new("req_item");
ibex_icache_mem_resp_item resp_item = new("resp_item");
pending_grants = new("pending_grants");
forever begin
// Wait for a transaction request.
p_sequencer.request_fifo.get(req_item);
@ -76,7 +75,7 @@ class ibex_icache_mem_resp_seq extends ibex_icache_mem_base_seq;
resp_item.err = mem_model.is_pmp_error(cur_seed, req_item.address);
// Warn the "grant side" to expect this fetch
pending_grants.put({req_item.address, cur_seed});
pending_grants.push_back({req_item.address, cur_seed});
// And finish the item
finish_item(resp_item);
@ -91,18 +90,27 @@ class ibex_icache_mem_resp_seq extends ibex_icache_mem_base_seq;
bit [63:0] gnt_data;
bit [31:0] gnt_addr, gnt_seed;
// Search through the pending_grants mailbox to find the seed from the request that matched.
gnt_addr = req_item.address + 1;
while (pending_grants.num() > 0) begin
pending_grants.get(gnt_data);
{gnt_addr, gnt_seed} = gnt_data;
if (gnt_addr == req_item.address) break;
// Search through the pending_grants queue to find the seed from the request that matched. We
// search from the back, rather than the front. This avoids problems if there were requests for
// an address that never got granted: we always want the most recent request for this address.
int i, N;
N = pending_grants.size();
for (i = 0; i < N; i++) begin
{gnt_addr, gnt_seed} = pending_grants[N - 1 - i];
if (gnt_addr == req_item.address)
break;
end
// If nothing went wrong, we should have found the right item and gnt_addr should now
// equal req_item.address
`DV_CHECK_FATAL(gnt_addr == req_item.address,
$sformatf("No pending grant for address 0x%08h.", req_item.address))
// If i == N, we didn't find a hit. That's not supposed to happen.
`DV_CHECK_FATAL(i < N,
$sformatf("No pending grant for address 0x%08h (%0d items in queue).",
req_item.address, N))
// Otherwise, we have a hit at index N - 1 - i. Throw away all previous items in the queue. We
// don't throw away this item, because it's possible to end up with repeated addresses in the
// queue (if the cache asked for the same address twice in a row) and if we throw away the hit
// the first time, we can't find it for the second grant.
pending_grants = pending_grants[N - 1 - i:$];
// Using the seed that we saw for the request, check the memory model for a (non-PMP) error
// at this address. On success, look up the memory data too.