/ test / cache / test_icache.py
test_icache.py
  1  from collections import deque
  2  from parameterized import parameterized_class
  3  import random
  4  
  5  from amaranth import Elaboratable, Module
  6  from amaranth.utils import exact_log2
  7  
  8  from transactron.lib import AdapterTrans, Adapter
  9  from coreblocks.cache.icache import ICache, ICacheBypass, CacheRefillerInterface
 10  from coreblocks.params import GenParams
 11  from coreblocks.interface.layouts import ICacheLayouts
 12  from coreblocks.params.configurations import test_core_config
 13  from coreblocks.cache.refiller import SimpleCommonBusCacheRefiller
 14  
 15  from transactron.testing import TestCaseWithSimulator, TestbenchIO, def_method_mock, TestbenchContext
 16  from transactron.testing.functions import MethodData
 17  from transactron.testing.method_mock import MethodMock
 18  from transactron.testing.testbenchio import CallTrigger
 19  from ..peripherals.bus_mock import BusMockParameters, MockMasterAdapter
 20  
 21  
 22  class SimpleCommonBusCacheRefillerTestCircuit(Elaboratable):
 23      def __init__(self, gen_params: GenParams):
 24          self.gen_params = gen_params
 25          self.cp = self.gen_params.icache_params
 26  
 27      def elaborate(self, platform):
 28          m = Module()
 29  
 30          bus_mock_params = BusMockParameters(
 31              data_width=self.gen_params.isa.xlen,
 32              addr_width=self.gen_params.isa.xlen,
 33          )
 34          self.bus_master_adapter = MockMasterAdapter(bus_mock_params)
 35  
 36          self.refiller = SimpleCommonBusCacheRefiller(
 37              self.gen_params.get(ICacheLayouts), self.cp, self.bus_master_adapter
 38          )
 39  
 40          self.start_refill = TestbenchIO(AdapterTrans.create(self.refiller.start_refill))
 41          self.accept_refill = TestbenchIO(AdapterTrans.create(self.refiller.accept_refill))
 42  
 43          m.submodules.bus_master_adapter = self.bus_master_adapter
 44          m.submodules.refiller = self.refiller
 45          m.submodules.start_refill = self.start_refill
 46          m.submodules.accept_refill = self.accept_refill
 47  
 48          return m
 49  
 50  
 51  @parameterized_class(
 52      ("name", "isa_xlen", "line_size", "fetch_block"),
 53      [
 54          ("line16B_block4B_rv32i", 32, 4, 2),
 55          ("line32B_block8B_rv32i", 32, 5, 3),
 56          ("line32B_block8B_rv64i", 64, 5, 3),
 57          ("line64B_block16B_rv32i", 32, 6, 4),
 58          ("line16B_block16B_rv32i", 32, 4, 4),
 59      ],
 60  )
 61  class TestSimpleCommonBusCacheRefiller(TestCaseWithSimulator):
 62      isa_xlen: int
 63      line_size: int
 64      fetch_block: int
 65  
 66      def setup_method(self) -> None:
 67          self.gen_params = GenParams(
 68              test_core_config.replace(
 69                  xlen=self.isa_xlen, icache_line_bytes_log=self.line_size, fetch_block_bytes_log=self.fetch_block
 70              )
 71          )
 72          self.cp = self.gen_params.icache_params
 73          self.test_module = SimpleCommonBusCacheRefillerTestCircuit(self.gen_params)
 74  
 75          random.seed(42)
 76  
 77          self.bad_addresses = set()
 78          self.bad_fetch_blocks = set()
 79          self.mem = dict()
 80  
 81          self.requests = deque()
 82          for _ in range(100):
 83              # Make the address aligned to the beginning of a cache line
 84              addr = random.randrange(2**self.gen_params.phys_addr_bits) & ~(self.cp.line_size_bytes - 1)
 85              self.requests.append(addr)
 86  
 87              if random.random() < 0.21:
 88                  # Choose an address in this cache line to be erroneous
 89                  bad_addr = addr + random.randrange(self.cp.line_size_bytes)
 90  
 91                  # Make the address aligned to the machine word size
 92                  bad_addr = bad_addr & ~(self.cp.word_width_bytes - 1)
 93  
 94                  self.bad_addresses.add(bad_addr)
 95                  self.bad_fetch_blocks.add(bad_addr & ~(self.cp.fetch_block_bytes - 1))
 96  
 97      async def bus_mock(self, sim: TestbenchContext):
 98          while True:
 99              req = await self.test_module.bus_master_adapter.request_read_mock.call(sim)
100  
101              # Bus model is addressing words, so we need to shift it a bit to get the real address.
102              addr = req.addr << exact_log2(self.cp.word_width_bytes)
103  
104              await self.random_wait_geom(sim, 0.5)
105  
106              err = 1 if addr in self.bad_addresses else 0
107  
108              data = random.randrange(2**self.gen_params.isa.xlen)
109              self.mem[addr] = data
110  
111              await self.test_module.bus_master_adapter.get_read_response_mock.call(sim, data=data, err=err)
112  
113      async def refiller_process(self, sim: TestbenchContext):
114          while self.requests:
115              req_addr = self.requests.pop()
116              await self.test_module.start_refill.call(sim, paddr=req_addr)
117  
118              for i in range(self.cp.fetch_blocks_in_line):
119                  ret = await self.test_module.accept_refill.call(sim)
120  
121                  cur_addr = req_addr + i * self.cp.fetch_block_bytes
122  
123                  assert ret["paddr"] == cur_addr
124  
125                  if cur_addr in self.bad_fetch_blocks:
126                      assert ret["error"] == 1
127                      assert ret["last"] == 1
128                      break
129  
130                  fetch_block = ret["fetch_block"]
131                  for j in range(self.cp.words_in_fetch_block):
132                      word = (fetch_block >> (j * self.cp.word_width)) & (2**self.cp.word_width - 1)
133                      assert word == self.mem[cur_addr + j * self.cp.word_width_bytes]
134  
135                  assert ret["error"] == 0
136  
137                  last = 1 if i == self.cp.fetch_blocks_in_line - 1 else 0
138                  assert ret["last"] == last
139  
140      def test(self):
141          with self.run_simulation(self.test_module) as sim:
142              sim.add_testbench(self.bus_mock, background=True)
143              sim.add_testbench(self.refiller_process)
144  
145  
146  class ICacheBypassTestCircuit(Elaboratable):
147      def __init__(self, gen_params: GenParams):
148          self.gen_params = gen_params
149          self.cp = self.gen_params.icache_params
150  
151      def elaborate(self, platform):
152          m = Module()
153  
154          bus_mock_params = BusMockParameters(
155              data_width=self.gen_params.isa.xlen,
156              addr_width=self.gen_params.isa.xlen,
157          )
158  
159          m.submodules.bus_master_adapter = self.bus_master_adapter = MockMasterAdapter(bus_mock_params)
160          m.submodules.bypass = self.bypass = ICacheBypass(
161              self.gen_params.get(ICacheLayouts), self.cp, self.bus_master_adapter
162          )
163          m.submodules.issue_req = self.issue_req = TestbenchIO(AdapterTrans.create(self.bypass.issue_req))
164          m.submodules.accept_res = self.accept_res = TestbenchIO(AdapterTrans.create(self.bypass.accept_res))
165  
166          return m
167  
168  
169  @parameterized_class(
170      ("name", "isa_xlen", "fetch_block"),
171      [
172          ("rv32i", 32, 2),
173          ("rv64i", 64, 3),
174      ],
175  )
176  class TestICacheBypass(TestCaseWithSimulator):
177      isa_xlen: int
178      fetch_block: int
179  
180      def setup_method(self) -> None:
181          self.gen_params = GenParams(
182              test_core_config.replace(xlen=self.isa_xlen, fetch_block_bytes_log=self.fetch_block, icache_enable=False)
183          )
184          self.cp = self.gen_params.icache_params
185          self.m = ICacheBypassTestCircuit(self.gen_params)
186  
187          random.seed(42)
188  
189          self.mem = dict()
190          self.bad_addrs = dict()
191  
192          self.requests = deque()
193  
194          # Add two consecutive addresses
195          self.requests.append(0)
196          self.requests.append(4)
197  
198          for _ in range(100):
199              addr = random.randrange(0, 2**self.gen_params.phys_addr_bits, 4)
200              self.requests.append(addr)
201  
202              if random.random() < 0.10:
203                  self.bad_addrs[addr] = True
204  
205      def load_or_gen_mem(self, addr: int):
206          if addr not in self.mem:
207              self.mem[addr] = random.randrange(2**self.gen_params.isa.ilen)
208          return self.mem[addr]
209  
210      async def bus_mock(self, sim: TestbenchContext):
211          while True:
212              req = await self.m.bus_master_adapter.request_read_mock.call(sim)
213  
214              # Bus model is addressing words, so we need to shift it a bit to get the real address.
215              addr = req.addr << exact_log2(self.cp.word_width_bytes)
216  
217              await self.random_wait_geom(sim, 0.5)
218  
219              err = 1 if addr in self.bad_addrs else 0
220  
221              data = self.load_or_gen_mem(addr)
222              if self.gen_params.isa.xlen == 64:
223                  data = self.load_or_gen_mem(addr + 4) << 32 | data
224  
225              await self.m.bus_master_adapter.get_read_response_mock.call(sim, data=data, err=err)
226  
227      async def user_process(self, sim: TestbenchContext):
228          while self.requests:
229              req_addr = self.requests.popleft() & ~(self.cp.fetch_block_bytes - 1)
230              await self.m.issue_req.call(sim, paddr=req_addr)
231  
232              await self.random_wait_geom(sim, 0.5)
233  
234              ret = await self.m.accept_res.call(sim)
235  
236              if (req_addr & ~(self.cp.word_width_bytes - 1)) in self.bad_addrs:
237                  assert ret["error"]
238              else:
239                  assert not ret["error"]
240  
241                  data = self.mem[req_addr]
242                  if self.gen_params.isa.xlen == 64:
243                      data |= self.mem[req_addr + 4] << 32
244                  assert ret["fetch_block"] == data
245  
246              await self.random_wait_geom(sim, 0.5)
247  
248      def test(self):
249          with self.run_simulation(self.m) as sim:
250              sim.add_testbench(self.bus_mock, background=True)
251              sim.add_testbench(self.user_process)
252  
253  
254  class MockedCacheRefiller(Elaboratable, CacheRefillerInterface):
255      def __init__(self, gen_params: GenParams):
256          layouts = gen_params.get(ICacheLayouts)
257  
258          self.start_refill_mock = TestbenchIO(Adapter(i=layouts.start_refill))
259          self.accept_refill_mock = TestbenchIO(Adapter(o=layouts.accept_refill))
260  
261          self.start_refill = self.start_refill_mock.adapter.iface
262          self.accept_refill = self.accept_refill_mock.adapter.iface
263  
264      def elaborate(self, platform):
265          m = Module()
266  
267          m.submodules.start_refill = self.start_refill_mock
268          m.submodules.accept_refill = self.accept_refill_mock
269  
270          return m
271  
272  
273  class ICacheTestCircuit(Elaboratable):
274      def __init__(self, gen_params: GenParams):
275          self.gen_params = gen_params
276          self.cp = self.gen_params.icache_params
277  
278      def elaborate(self, platform):
279          m = Module()
280  
281          m.submodules.refiller = self.refiller = MockedCacheRefiller(self.gen_params)
282          m.submodules.cache = self.cache = ICache(self.gen_params.get(ICacheLayouts), self.cp, self.refiller)
283          m.submodules.issue_req = self.issue_req = TestbenchIO(AdapterTrans.create(self.cache.issue_req))
284          m.submodules.accept_res = self.accept_res = TestbenchIO(AdapterTrans.create(self.cache.accept_res))
285          m.submodules.flush_cache = self.flush_cache = TestbenchIO(AdapterTrans.create(self.cache.flush))
286  
287          return m
288  
289  
290  @parameterized_class(
291      ("name", "isa_xlen", "line_size", "fetch_block"),
292      [
293          ("line16B_block8B_rv32i", 32, 4, 2),
294          ("line64B_block16B_rv32i", 32, 6, 4),
295          ("line32B_block16B_rv64i", 64, 5, 4),
296          ("line32B_block32B_rv64i", 64, 5, 5),
297      ],
298  )
299  class TestICache(TestCaseWithSimulator):
300      isa_xlen: int
301      line_size: int
302      fetch_block: int
303  
304      def setup_method(self) -> None:
305          random.seed(42)
306  
307          self.mem = dict()
308          self.bad_addrs = set()
309          self.bad_cache_lines = set()
310          self.refill_requests = deque()
311          self.refill_block_cnt = 0
312          self.issued_requests = deque()
313  
314          self.accept_refill_request = True
315  
316          self.refill_in_fly = False
317          self.refill_word_cnt = 0
318          self.refill_addr = 0
319  
320      def init_module(self, ways, sets) -> None:
321          self.gen_params = GenParams(
322              test_core_config.replace(
323                  xlen=self.isa_xlen,
324                  icache_ways=ways,
325                  icache_sets_bits=exact_log2(sets),
326                  icache_line_bytes_log=self.line_size,
327                  fetch_block_bytes_log=self.fetch_block,
328              )
329          )
330          self.cp = self.gen_params.icache_params
331          self.m = ICacheTestCircuit(self.gen_params)
332  
333      @def_method_mock(lambda self: self.m.refiller.start_refill_mock, enable=lambda self: self.accept_refill_request)
334      def start_refill_mock(self, paddr):
335          @MethodMock.effect
336          def eff():
337              self.refill_requests.append(paddr)
338              self.refill_block_cnt = 0
339              self.refill_in_fly = True
340              self.refill_addr = paddr
341  
342      def enen(self):
343          return self.refill_in_fly
344  
345      @def_method_mock(lambda self: self.m.refiller.accept_refill_mock, enable=enen)
346      def accept_refill_mock(self):
347          addr = self.refill_addr + self.refill_block_cnt * self.cp.fetch_block_bytes
348  
349          fetch_block = 0
350          bad_addr = False
351          for i in range(0, self.cp.fetch_block_bytes, 4):
352              fetch_block |= self.load_or_gen_mem(addr + i) << (8 * i)
353              if addr + i in self.bad_addrs:
354                  bad_addr = True
355  
356          last = self.refill_block_cnt + 1 == self.cp.fetch_blocks_in_line or bad_addr
357  
358          @MethodMock.effect
359          def eff():
360              self.refill_block_cnt += 1
361  
362              if last:
363                  self.refill_in_fly = False
364  
365          return {
366              "paddr": addr,
367              "fetch_block": fetch_block,
368              "error": bad_addr,
369              "last": last,
370          }
371  
372      def load_or_gen_mem(self, addr: int):
373          if addr not in self.mem:
374              self.mem[addr] = random.randrange(2**self.gen_params.isa.ilen)
375          return self.mem[addr]
376  
377      def add_bad_addr(self, addr: int):
378          self.bad_addrs.add(addr)
379          self.bad_cache_lines.add(addr & ~((1 << self.cp.offset_bits) - 1))
380  
381      async def send_req(self, sim: TestbenchContext, addr: int):
382          self.issued_requests.append(addr)
383          await self.m.issue_req.call(sim, paddr=addr)
384  
385      async def expect_resp(self, sim: TestbenchContext, wait=False):
386          if wait:
387              *_, resp = await self.m.accept_res.sample_outputs_until_done(sim)
388          else:
389              *_, resp = await self.m.accept_res.sample_outputs(sim)
390  
391          self.assert_resp(resp)
392  
393      def assert_resp(self, resp: MethodData):
394          addr = self.issued_requests.popleft() & ~(self.cp.fetch_block_bytes - 1)
395  
396          if (addr & ~((1 << self.cp.offset_bits) - 1)) in self.bad_cache_lines:
397              assert resp["error"]
398          else:
399              assert not resp["error"]
400              fetch_block = 0
401              for i in range(0, self.cp.fetch_block_bytes, 4):
402                  fetch_block |= self.mem[addr + i] << (8 * i)
403  
404              assert resp["fetch_block"] == fetch_block
405  
406      def expect_refill(self, addr: int):
407          assert self.refill_requests.popleft() == addr
408  
409      async def call_cache(self, sim: TestbenchContext, addr: int):
410          await self.send_req(sim, addr)
411          self.m.accept_res.enable(sim)
412          await self.expect_resp(sim, wait=True)
413          self.m.accept_res.disable(sim)
414  
415      def test_1_way(self):
416          self.init_module(1, 4)
417  
418          async def cache_user_process(sim: TestbenchContext):
419              # The first request should cause a cache miss
420              await self.call_cache(sim, 0x00010004)
421              self.expect_refill(0x00010000)
422  
423              # Accesses to the same cache line shouldn't cause a cache miss
424              for i in range(self.cp.fetch_blocks_in_line):
425                  await self.call_cache(sim, 0x00010000 + i * self.cp.fetch_block_bytes)
426                  assert len(self.refill_requests) == 0
427  
428              # Now go beyond the first cache line
429              await self.call_cache(sim, 0x00010000 + self.cp.line_size_bytes)
430              self.expect_refill(0x00010000 + self.cp.line_size_bytes)
431  
432              # Trigger cache aliasing
433              await self.call_cache(sim, 0x00020000)
434              await self.call_cache(sim, 0x00010000)
435              self.expect_refill(0x00020000)
436              self.expect_refill(0x00010000)
437  
438              # Fill the whole cache
439              for i in range(0, self.cp.line_size_bytes * self.cp.num_of_sets, 4):
440                  await self.call_cache(sim, i)
441              for i in range(self.cp.num_of_sets):
442                  self.expect_refill(i * self.cp.line_size_bytes)
443  
444              # Now do some accesses within the cached memory
445              for i in range(50):
446                  await self.call_cache(sim, random.randrange(0, self.cp.line_size_bytes * self.cp.num_of_sets, 4))
447              assert len(self.refill_requests) == 0
448  
449          with self.run_simulation(self.m) as sim:
450              sim.add_testbench(cache_user_process)
451  
452      def test_2_way(self):
453          self.init_module(2, 4)
454  
455          async def cache_process(sim: TestbenchContext):
456              # Fill the first set of both ways
457              await self.call_cache(sim, 0x00010000)
458              await self.call_cache(sim, 0x00020000)
459              self.expect_refill(0x00010000)
460              self.expect_refill(0x00020000)
461  
462              # And now both lines should be in the cache
463              await self.call_cache(sim, 0x00010004)
464              await self.call_cache(sim, 0x00020004)
465              assert len(self.refill_requests) == 0
466  
467          with self.run_simulation(self.m) as sim:
468              sim.add_testbench(cache_process)
469  
470      # Tests whether the cache is fully pipelined and the latency between requests and response is exactly one cycle.
471      def test_pipeline(self):
472          self.init_module(2, 4)
473  
474          async def cache_process(sim: TestbenchContext):
475              # Fill the cache
476              for i in range(self.cp.num_of_sets):
477                  addr = 0x00010000 + i * self.cp.line_size_bytes
478                  await self.call_cache(sim, addr)
479                  self.expect_refill(addr)
480  
481              await self.tick(sim, 4)
482  
483              # Create a stream of requests to ensure the pipeline is working
484              self.m.accept_res.enable(sim)
485              for i in range(0, self.cp.num_of_sets * self.cp.line_size_bytes, 4):
486                  addr = 0x00010000 + i
487                  self.issued_requests.append(addr)
488  
489                  # Send the request
490                  ret = await self.m.issue_req.call_try(sim, paddr=addr)
491                  assert ret is not None
492  
493                  # After a cycle the response should be ready
494                  await self.expect_resp(sim)
495  
496              self.m.accept_res.disable(sim)
497  
498              await self.tick(sim, 4)
499  
500              # Check how the cache handles queuing the requests
501              await self.send_req(sim, 0x00010000 + 3 * self.cp.line_size_bytes)
502              await self.send_req(sim, 0x00010004)
503  
504              # Wait a few cycles. There are two requests queued
505              await self.tick(sim, 4)
506  
507              self.m.accept_res.enable(sim)
508              await self.expect_resp(
509                  sim,
510              )
511              await self.expect_resp(
512                  sim,
513              )
514              await self.send_req(sim, 0x0001000C)
515              await self.expect_resp(
516                  sim,
517              )
518  
519              self.m.accept_res.disable(sim)
520  
521              await self.tick(sim, 4)
522  
523              # Schedule two requests, the first one causing a cache miss
524              await self.send_req(sim, 0x00020000)
525              await self.send_req(sim, 0x00010000 + self.cp.line_size_bytes)
526  
527              self.m.accept_res.enable(sim)
528  
529              await self.expect_resp(sim, wait=True)
530              await self.expect_resp(
531                  sim,
532              )
533              self.m.accept_res.disable(sim)
534  
535              await self.tick(sim, 2)
536  
537              # Schedule two requests, the second one causing a cache miss
538              await self.send_req(sim, 0x00020004)
539              await self.send_req(sim, 0x00030000 + self.cp.line_size_bytes)
540  
541              self.m.accept_res.enable(sim)
542  
543              await self.expect_resp(
544                  sim,
545              )
546              await self.expect_resp(sim, wait=True)
547              self.m.accept_res.disable(sim)
548  
549              await self.tick(sim, 2)
550  
551              # Schedule two requests, both causing a cache miss
552              await self.send_req(sim, 0x00040000)
553              await self.send_req(sim, 0x00050000 + self.cp.line_size_bytes)
554  
555              self.m.accept_res.enable(sim)
556  
557              await self.expect_resp(sim, wait=True)
558              await self.expect_resp(sim, wait=True)
559              self.m.accept_res.disable(sim)
560  
561          with self.run_simulation(self.m) as sim:
562              sim.add_testbench(cache_process)
563  
564      def test_flush(self):
565          self.init_module(2, 4)
566  
567          async def cache_process(sim: TestbenchContext):
568              # Fill the whole cache
569              for s in range(self.cp.num_of_sets):
570                  for w in range(self.cp.num_of_ways):
571                      addr = w * 0x00010000 + s * self.cp.line_size_bytes
572                      await self.call_cache(sim, addr)
573                      self.expect_refill(addr)
574  
575              # Everything should be in the cache
576              for s in range(self.cp.num_of_sets):
577                  for w in range(self.cp.num_of_ways):
578                      addr = w * 0x00010000 + s * self.cp.line_size_bytes
579                      await self.call_cache(sim, addr)
580  
581              assert len(self.refill_requests) == 0
582  
583              await self.m.flush_cache.call(sim)
584  
585              # The cache should be empty
586              for s in range(self.cp.num_of_sets):
587                  for w in range(self.cp.num_of_ways):
588                      addr = w * 0x00010000 + s * self.cp.line_size_bytes
589                      await self.call_cache(sim, addr)
590                      self.expect_refill(addr)
591  
592              # Try to flush during refilling the line
593              await self.send_req(sim, 0x00030000)
594              await self.m.flush_cache.call(sim)
595              # We still should be able to accept the response for the last request
596              self.assert_resp(await self.m.accept_res.call(sim))
597              self.expect_refill(0x00030000)
598  
599              await self.call_cache(sim, 0x00010000)
600              self.expect_refill(0x00010000)
601  
602              # Try to execute issue_req and flush_cache methods at the same time
603              self.issued_requests.append(0x00010000)
604              issue_req_res, flush_cache_res = (
605                  await CallTrigger(sim).call(self.m.issue_req, paddr=0x00010000).call(self.m.flush_cache)
606              )
607              assert issue_req_res is None
608              assert flush_cache_res is not None
609              await self.m.issue_req.call(sim, paddr=0x00010000)
610              self.assert_resp(await self.m.accept_res.call(sim))
611              self.expect_refill(0x00010000)
612  
613              # Schedule two requests and then flush
614              await self.send_req(sim, 0x00000000 + self.cp.line_size_bytes)
615              await self.send_req(sim, 0x00010000)
616  
617              res = await self.m.flush_cache.call_try(sim)
618              # We cannot flush until there are two pending requests
619              assert res is None
620              res = await self.m.flush_cache.call_try(sim)
621              assert res is None
622  
623              # Accept the first response
624              self.assert_resp(await self.m.accept_res.call(sim))
625  
626              await self.m.flush_cache.call(sim)
627  
628              # And accept the second response ensuring that we got old data
629              self.assert_resp(await self.m.accept_res.call(sim))
630              self.expect_refill(0x00000000 + self.cp.line_size_bytes)
631  
632              # Just make sure that the line is truly flushed
633              await self.call_cache(sim, 0x00010000)
634              self.expect_refill(0x00010000)
635  
636          with self.run_simulation(self.m) as sim:
637              sim.add_testbench(cache_process)
638  
639      def test_errors(self):
640          self.init_module(1, 4)
641  
642          async def cache_process(sim: TestbenchContext):
643              self.add_bad_addr(0x00010000)  # Bad addr at the beginning of the line
644              self.add_bad_addr(0x00020008)  # Bad addr in the middle of the line
645              self.add_bad_addr(
646                  0x00030000 + self.cp.line_size_bytes - self.cp.word_width_bytes
647              )  # Bad addr at the end of the line
648  
649              await self.call_cache(sim, 0x00010008)
650              self.expect_refill(0x00010000)
651  
652              # Requesting a bad addr again should retrigger refill
653              await self.call_cache(sim, 0x00010008)
654              self.expect_refill(0x00010000)
655  
656              await self.call_cache(sim, 0x00020000)
657              self.expect_refill(0x00020000)
658  
659              await self.call_cache(sim, 0x00030008)
660              self.expect_refill(0x00030000)
661  
662              # Test how pipelining works with errors
663  
664              self.m.accept_res.disable(sim)
665  
666              # Schedule two requests, the first one causing an error
667              await self.send_req(sim, 0x00020000)
668              await self.send_req(sim, 0x00011000)
669  
670              self.m.accept_res.enable(sim)
671  
672              await self.expect_resp(sim, wait=True)
673              await self.expect_resp(sim, wait=True)
674              self.m.accept_res.disable(sim)
675  
676              await self.tick(sim, 3)
677  
678              # Schedule two requests, the second one causing an error
679              await self.send_req(sim, 0x00021004)
680              await self.send_req(sim, 0x00030000)
681  
682              await self.tick(sim, 10)
683  
684              self.m.accept_res.enable(sim)
685  
686              await self.expect_resp(sim, wait=True)
687              await self.expect_resp(sim, wait=True)
688              self.m.accept_res.disable(sim)
689  
690              await self.tick(sim, 3)
691  
692              # Schedule two requests, both causing an error
693              await self.send_req(sim, 0x00020000)
694              await self.send_req(sim, 0x00010000)
695  
696              self.m.accept_res.enable(sim)
697  
698              await self.expect_resp(sim, wait=True)
699              await self.expect_resp(sim, wait=True)
700              self.m.accept_res.disable(sim)
701  
702              # The second request will cause an error
703              await self.send_req(sim, 0x00021004)
704              await self.send_req(sim, 0x00030000)
705  
706              await self.tick(sim, 10)
707  
708              # Accept the first response
709              self.m.accept_res.enable(sim)
710              await self.expect_resp(sim, wait=True)
711  
712              # Wait before accepting the second response
713              self.m.accept_res.disable(sim)
714              await self.tick(sim, 10)
715              self.m.accept_res.enable(sim)
716              await self.expect_resp(sim, wait=True)
717  
718              # This request should not cause an error
719              await self.send_req(sim, 0x00011000)
720              await self.expect_resp(sim, wait=True)
721  
722          with self.run_simulation(self.m) as sim:
723              sim.add_testbench(cache_process)
724  
725      def test_random(self):
726          self.init_module(4, 8)
727  
728          max_addr = 16 * self.cp.line_size_bytes * self.cp.num_of_sets
729          iterations = 1000
730  
731          for i in range(0, max_addr, 4):
732              if random.random() < 0.05:
733                  self.add_bad_addr(i)
734  
735          async def refiller_ctrl(sim: TestbenchContext):
736              while True:
737                  await self.random_wait_geom(sim, 0.4)
738                  self.accept_refill_request = False
739  
740                  await self.random_wait_geom(sim, 0.7)
741                  self.accept_refill_request = True
742  
743          async def sender(sim: TestbenchContext):
744              for _ in range(iterations):
745                  await self.send_req(sim, random.randrange(0, max_addr, 4))
746                  await self.random_wait_geom(sim, 0.5)
747  
748          async def receiver(sim: TestbenchContext):
749              for _ in range(iterations):
750                  while len(self.issued_requests) == 0:
751                      await sim.tick()
752  
753                  self.assert_resp(await self.m.accept_res.call(sim))
754                  await self.random_wait_geom(sim, 0.2)
755  
756          with self.run_simulation(self.m) as sim:
757              sim.add_testbench(sender)
758              sim.add_testbench(receiver)
759              sim.add_testbench(refiller_ctrl, background=True)