diff options
| author | Alejandro Soto <alejandro@34project.org> | 2024-06-01 14:54:11 -0600 |
|---|---|---|
| committer | Alejandro Soto <alejandro@34project.org> | 2024-06-01 15:10:05 -0600 |
| commit | 41a944b7577833e7dad6f2bee74ccc0082cc5717 (patch) | |
| tree | e9b913dff83f174d3d7f67237eb8b1385b3b6ef4 /tb | |
| parent | 766cdd53d55796c66a2a968769655b0c68e7c57f (diff) | |
tb/gfx_shader_bind: update testbench
Diffstat (limited to 'tb')
| -rw-r--r-- | tb/gfx_shader_bind/testbench/checkers.py | 18 | ||||
| -rw-r--r-- | tb/gfx_shader_bind/testbench/data.py | 22 | ||||
| -rw-r--r-- | tb/gfx_shader_bind/testbench/main.py | 47 | ||||
| -rw-r--r-- | tb/gfx_shader_bind/testbench/models.py | 18 |
4 files changed, 79 insertions, 26 deletions
diff --git a/tb/gfx_shader_bind/testbench/checkers.py b/tb/gfx_shader_bind/testbench/checkers.py index 0109249..256b83f 100644 --- a/tb/gfx_shader_bind/testbench/checkers.py +++ b/tb/gfx_shader_bind/testbench/checkers.py @@ -3,6 +3,8 @@ from cocotb.triggers import RisingEdge, ReadOnly from cocotb_coverage.coverage import CoverCheck +from .common import log + class PipelineIntegrityChecker: def __init__(self, dut, name, clk): self._clk, self._dut = clk, dut @@ -56,8 +58,8 @@ class PcChecker: @CoverCheck( f'{name}.pc_ok', - f_pass = lambda wave: wave.insn and self._pc_ok(wave), - f_fail = lambda wave: wave.insn and not self._pc_ok(wave), + f_pass = lambda wave: wave.insn is not None and self._pc_ok(wave), + f_fail = lambda wave: wave.insn is not None and not self._pc_ok(wave), ) def sample_wave(wave): pass @@ -65,4 +67,14 @@ class PcChecker: self.sample_wave = sample_wave def _pc_ok(self, wave): - return wave.insn == self._mem.read(self._pc_table[wave.group] * 4) + pc = self._pc_table[wave.group] * 4 + + expected = self._mem.read_cached(pc) + if type(expected) is int: + expected = (expected,) + + if wave.insn not in expected: + log.error(f'group {wave.group} outputs insn {wave.insn}, but any of {expected} was expected') + return False + + return True diff --git a/tb/gfx_shader_bind/testbench/data.py b/tb/gfx_shader_bind/testbench/data.py index 4aa3630..d119210 100644 --- a/tb/gfx_shader_bind/testbench/data.py +++ b/tb/gfx_shader_bind/testbench/data.py @@ -11,13 +11,27 @@ class FrontWave: return other.__eq__(self) if other._soft: - return self.group == other.group and \ - ((other.insn is None and self.insn is None) or \ - (other.insn is not None and (not self.insn or self.insn == other.insn))) + if self.group != other.group: + return False + elif other.insn is None and self.insn is None: + return True + elif other.insn is None: + return False + elif self.insn is None: + return True + elif type(other.insn) is tuple: + return self.insn in other.insn + else: + return self.insn == other.insn return self.group == other.group and self.insn == other.insn def __repr__(self): - insn = f', insn=0x{self.insn:08x}' if not self.retry else '' + if type(self.insn) is tuple: + insn = '(' + ','.join(f'0x{insn:08x}' for insn in self.insn) + ')' + elif self.insn is not None: + insn = f'0x{self.insn:08x}' + + insn = f', insn={insn}' if not self.retry else '' soft = f', soft' if self._soft else '' return f'FrontWave(group={self.group}, retry={int(self.retry)}{insn}{soft})' diff --git a/tb/gfx_shader_bind/testbench/main.py b/tb/gfx_shader_bind/testbench/main.py index f31e198..5f607bd 100644 --- a/tb/gfx_shader_bind/testbench/main.py +++ b/tb/gfx_shader_bind/testbench/main.py @@ -8,7 +8,7 @@ from cocotb_bus.scoreboard import Scoreboard from cocotb_coverage.coverage import coverage_db from .axi import AXI4Agent -from .data import FrontWave +from .data import BAD_PC, FrontWave from .common import MAX_GROUPS, MEM_WORDS, log from .models import Memory, PcTable from .drivers import ClockResetDriver, LoopDriver, PcDriver @@ -21,15 +21,24 @@ async def test(dut): mem_agent = AXI4Agent(dut, 'mem', dut.clk, mem, case_insensitive=False) out_groups = set() + out_groups_done = set() out_groups_retry = set() out_monitor = FrontWaveMonitor(dut, 'wave', dut.clk, case_insensitive=False) + lst = set() def out_callback(wave): - nonlocal out_groups, out_groups_retry + nonlocal out_groups, out_groups_done, out_groups_retry + + if wave.insn is not None: + lst.add(wave.group) out_groups.add(wave.group) if wave.insn is None: out_groups_retry.add(wave.group) + else: + out_groups_done.add(wave.group) + if wave.group in out_groups_retry: + out_groups_retry.remove(wave.group) out_monitor.add_callback(out_callback) @@ -54,35 +63,41 @@ async def test(dut): await FallingEdge(dut.front.bind_.icache.in_flush) #FIXME await RisingEdge(dut.clk) - for iteration in range(1000): + for iteration in range(50): all_groups = list(range(MAX_GROUPS)) random.shuffle(all_groups) + for group in range(MAX_GROUPS): + pc_table[group] = BAD_PC * 4 + for group in all_groups: - pc = random.randint(0, MEM_WORDS) * 4 + pc = random.randint(0, 512) * 4 pc_table[group] = pc mem.randomize_line(pc) - out_expected.append(FrontWave(group=group, insn=mem.read(pc), soft=True)) + for group in all_groups: + out_expected.append(FrontWave(group=group, insn=mem.read_cached(4 * pc_table[group]), soft=True)) await loop_driver.put(group) - #while out_groups_retry or len(out_groups) < MAX_GROUPS: - for _ in range(100): - if out_groups_retry: - group = out_groups_retry.pop() - out_groups.remove(group) + for _ in range(30): + retry = out_groups_retry.copy() + retry_len = len(retry) + out_groups_retry.clear() - out_expected.append(FrontWave(group=group, insn=mem.read(pc_table[group] * 4), soft=True)) + if not retry and len(out_groups_done) == MAX_GROUPS: + break + for group in retry: + out_expected.append(FrontWave(group=group, insn=mem.read_cached(pc_table[group] * 4), soft=True)) await loop_driver.put(group) + await RisingEdge(dut.clk) - await RisingEdge(dut.clk) - - for _ in range(100): - await RisingEdge(dut.clk) + await ClockCycles(dut.clk, max(0, 16 - retry_len)) + else: + raise TimeoutError(f'Timed out during iteration {iteration} waiting for all groups to finish: {list(out_groups_retry)}') out_groups.clear() - out_groups_retry.clear() + out_groups_done.clear() coverage_db.report_coverage(log.info, bins=True) coverage_db.export_to_xml(filename="coverage.xml") diff --git a/tb/gfx_shader_bind/testbench/models.py b/tb/gfx_shader_bind/testbench/models.py index 928f07b..e65b053 100644 --- a/tb/gfx_shader_bind/testbench/models.py +++ b/tb/gfx_shader_bind/testbench/models.py @@ -31,7 +31,7 @@ class Memory: f'{word_size} is not a power of two' self._data = {} - self._dirty = set() + self._dirty = {} self._observed = set() self._start = start @@ -83,9 +83,12 @@ class Memory: ) def _write_word(word_num, data): if self._expect_in_range(word_num): - self._data[word_num] = data if word_num in self._observed: - self._dirty.add(word_num) + dirty = self._dirty.setdefault(word_num, set()) + dirty.add(self._data.get(word_num, self._all_ones)) + dirty.add(data) + + self._data[word_num] = data self._read_word = _read_word self._write_word = _write_word @@ -93,6 +96,15 @@ class Memory: def read(self, addr): return self._data[addr >> 2] + def read_cached(self, addr): + addr = addr >> 2 + + data = self._data[addr] + if dirty := self._dirty.get(addr): + data = tuple(dirty.union({data})) + + return data + def randomize_line(self, addr): first_word = (addr >> 2) & ~15 for word_num in range(first_word, first_word + 16): |
