# ICache Memory UVM Agent

The ICache memory UVM agent models the instruction memory bus downstream of the ICache.

The basic idea is that instruction memory is modelled with a single 32-bit *seed*.
A simple hash function takes this seed and calculates 32 bits of memory for each (aligned) base address.
The seed also determines a memory region which should result in memory errors.

The agent exposes a slave interface to the core using the usual UVM architecture for a reactive slave.
The entire dance is as follows:

  1. The monitor spots a new request (either because of a posedge on the `req` line or a change in the `addr` line).
  1. It generates an `ibex_icache_mem_req_item` with `is_grant` field false and writes it to the sequencer's request port.
  1. This request gets picked up by the sequence (in `ibex_icache_mem_resp_seq.sv`).
  1. The request will get granted at some point when the `grant` line goes high. At this point, the monitor will spot the request being granted and create another `ibex_icache_mem_req_item`, writing it to the sequencer's request port. This time, the `is_grant` field is true.
  1. This request in turn gets picked up by the sequence (in `ibex_icache_mem_resp_seq.sv`).
  1. The sequence uses its memory model to decide the data to be fetched and whether the response should have an error. The results get written into a sequence item with `is_grant` field true.
  1. When the driver picks up this sequence item, it adds it to a response queue.
  1. Responses get popped from the response queue in order. Each causes a delay of zero or more cycles and then the response is finally driven onto the bus with `rvalid`, `rdata` and `err`.

The underlying memory seed can be updated by pushing a new value to the sequencer's `seed_fifo`.
New seeds are generated by the core model, which ensures we only get a new seed when either the cache is disabled or on an invalidation.
