diff options
| author | Alejandro Soto <alejandro@34project.org> | 2024-05-18 09:13:11 -0600 |
|---|---|---|
| committer | Alejandro Soto <alejandro@34project.org> | 2024-05-24 05:58:41 -0600 |
| commit | 4cf5ed94a8767efd13265abcc20a5082acc02824 (patch) | |
| tree | 0a98aaac24245a8daf76d25e8161cd3c03acd2fb /tb/gfx_shader_bind/testbench/models.py | |
| parent | a3559d9e53100cd2d9c1e481ec81eeb90ca0ba5b (diff) | |
tb/gfx_shader_bind: initial commit
Diffstat (limited to 'tb/gfx_shader_bind/testbench/models.py')
| -rw-r--r-- | tb/gfx_shader_bind/testbench/models.py | 140 |
1 files changed, 140 insertions, 0 deletions
diff --git a/tb/gfx_shader_bind/testbench/models.py b/tb/gfx_shader_bind/testbench/models.py new file mode 100644 index 0000000..928f07b --- /dev/null +++ b/tb/gfx_shader_bind/testbench/models.py @@ -0,0 +1,140 @@ +import random + +from cocotb.binary import BinaryValue + +from cocotb_coverage.coverage import CoverCross, CoverPoint + +from .data import BAD_PC +from .common import log + +class PcTable: + def __init__(self): + self._pcs = {} + + def __getitem__(self, group): + if isinstance(group, BinaryValue): + group = group.integer + + return self._pcs.get(group, BAD_PC) + + def __setitem__(self, group, pc): + assert (pc & 3) == 0 + if isinstance(group, BinaryValue): + group = group.integer + + self._pcs[group] = pc >> 2 + +class Memory: + def __init__(self, name, *, word_size, words, start=0): + word_mask = word_size - 1 + assert word_size > 0 and (word_mask & word_size) == 0, \ + f'{word_size} is not a power of two' + + self._data = {} + self._dirty = set() + self._observed = set() + + self._start = start + self._words = words + self._word_size = word_size + self._subword_mask = word_mask + + self._all_ones = (1 << (8 * word_size)) - 1 + + @CoverPoint( + f'{name}.read_dirty', + bins = [True, False], + bins_labels = ['dirty', 'clean'], + rel = lambda word_num, dirty: self._test_bin(word_num, self._dirty, dirty), + ) + @CoverPoint( + f'{name}.read_observed', + bins = [True, False], + bins_labels = ['multiple_reads', 'first_read'], + rel = lambda word_num, observed: self._test_bin(word_num, self._observed, observed), + ) + @CoverCross( + f'{name}.read_dirty_observed', + items = [ + f'{name}.read_dirty', + f'{name}.read_observed', + ], + ign_bins = [ + ('dirty', 'first_read'), + ], + ) + def _read_word(word_num): + if self._expect_in_range(word_num): + self._observed.add(word_num) + + word = self._data.get(word_num) + if word is None: + log.warning(f'Uninitialized memory read: {self._addr_repr(word_num)}') + word = self._all_ones + + return word + + @CoverPoint( + f'{name}.write_dirty', + bins = [True, False], + bins_labels = ['dirty_write', 'clean_write'], + xf = lambda word_num, data: word_num, + rel = lambda word_num, dirty: self._test_bin(word_num, self._observed, dirty), + ) + 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) + + self._read_word = _read_word + self._write_word = _write_word + + def read(self, addr): + return self._data[addr >> 2] + + def randomize_line(self, addr): + first_word = (addr >> 2) & ~15 + for word_num in range(first_word, first_word + 16): + self._write_word(word_num, random.randint(0, self._all_ones)) + + def __getitem__(self, index): + if not isinstance(index, slice): + return super()[index] + + assert index.stop >= index.start + assert (index.stop & self._subword_mask) == 0 + assert (index.start & self._subword_mask) == 0 + + return MemoryRead(self, index.start // self._word_size, index.stop // self._word_size) + + def _expect_in_range(self, word_num): + delta = word_num - self._start + + in_range = delta >= 0 and delta < self._words + if not in_range: + log.error(f'Bad memory address: {self._addr_repr(word_num)}') + + return in_range + + def _test_bin(self, word_num, flag_set, flag_bin): + if not self._expect_in_range(word_num): + return False + + return (word_num in flag_set) == flag_bin + + def _addr_repr(self, word_num): + addr = word_num * self._word_size + return f'0x{addr:08x}' + +class MemoryRead: + def __init__(self, mem, start, stop): + self._mem, self._start, self._stop = mem, start, stop + + def tobytes(self): + array = bytearray() + for word_num in range(self._start, self._stop): + word = self._mem._read_word(word_num) + array.extend(word.to_bytes(self._mem._word_size, 'little')) + + return array |
