summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlejandro Soto <alejandro@34project.org>2024-06-01 14:54:11 -0600
committerAlejandro Soto <alejandro@34project.org>2024-06-01 15:10:05 -0600
commit41a944b7577833e7dad6f2bee74ccc0082cc5717 (patch)
treee9b913dff83f174d3d7f67237eb8b1385b3b6ef4
parent766cdd53d55796c66a2a968769655b0c68e7c57f (diff)
tb/gfx_shader_bind: update testbench
-rw-r--r--tb/gfx_shader_bind/testbench/checkers.py18
-rw-r--r--tb/gfx_shader_bind/testbench/data.py22
-rw-r--r--tb/gfx_shader_bind/testbench/main.py47
-rw-r--r--tb/gfx_shader_bind/testbench/models.py18
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):