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)