diff options
| -rw-r--r-- | tb/gfx_shader_bind/dut.sv | 139 | ||||
| -rw-r--r-- | tb/gfx_shader_bind/mod.mk | 10 | ||||
| -rw-r--r-- | tb/gfx_shader_bind/testbench/__init__.py | 0 | ||||
| -rw-r--r-- | tb/gfx_shader_bind/testbench/axi.py | 190 | ||||
| -rw-r--r-- | tb/gfx_shader_bind/testbench/checkers.py | 68 | ||||
| -rw-r--r-- | tb/gfx_shader_bind/testbench/common.py | 6 | ||||
| -rw-r--r-- | tb/gfx_shader_bind/testbench/data.py | 23 | ||||
| -rw-r--r-- | tb/gfx_shader_bind/testbench/drivers.py | 85 | ||||
| -rw-r--r-- | tb/gfx_shader_bind/testbench/main.py | 91 | ||||
| -rw-r--r-- | tb/gfx_shader_bind/testbench/models.py | 140 | ||||
| -rw-r--r-- | tb/gfx_shader_bind/testbench/monitors.py | 30 | ||||
| -rw-r--r-- | tb/mod.mk | 2 |
12 files changed, 783 insertions, 1 deletions
diff --git a/tb/gfx_shader_bind/dut.sv b/tb/gfx_shader_bind/dut.sv new file mode 100644 index 0000000..e1d1228 --- /dev/null +++ b/tb/gfx_shader_bind/dut.sv @@ -0,0 +1,139 @@ +module dut +import gfx::*; +( + input logic clk, + rst_n, + + output logic mem_AWVALID, + input logic mem_AWREADY, + output logic[7:0] mem_AWID, + output logic[7:0] mem_AWLEN, + output logic[2:0] mem_AWSIZE, + output logic[2:0] mem_AWPROT, + output logic[1:0] mem_AWBURST, + output word mem_AWADDR, + + output logic mem_WVALID, + input logic mem_WREADY, + output word mem_WDATA, + output logic mem_WLAST, + output logic[3:0] mem_WSTRB, + + input logic mem_BVALID, + output logic mem_BREADY, + input logic[7:0] mem_BID, + input logic[1:0] mem_BRESP, + + output logic mem_ARVALID, + input logic mem_ARREADY, + output logic[7:0] mem_ARID, + output logic[7:0] mem_ARLEN, + output logic[2:0] mem_ARSIZE, + output logic[2:0] mem_ARPROT, + output logic[1:0] mem_ARBURST, + output word mem_ARADDR, + + input logic mem_RVALID, + output logic mem_RREADY, + input logic[7:0] mem_RID, + input word mem_RDATA, + input logic[1:0] mem_RRESP, + input logic mem_RLAST, + + input logic icache_flush, + + input logic loop_valid, + input group_id loop_group, + + input word_ptr pc_front_pc, + output group_id pc_front_group, + + output logic wave_valid, + wave_retry, + output group_id wave_group, + output word wave_insn, + + output logic runnable_in_ready, + runnable_out_valid, + output group_id runnable_out_data +); + + if_axib mem(); + gfx_front_back front_back(); + gfx_regfile_io regfile(); + + assign mem_AWID = mem.s.awid; + assign mem_AWLEN = mem.s.awlen; + assign mem_AWADDR = mem.s.awaddr; + assign mem_AWSIZE = mem.s.awsize; + assign mem_AWBURST = mem.s.awburst; + assign mem_AWVALID = mem.s.awvalid; + assign mem.s.awready = mem_AWREADY; + + assign mem_WDATA = mem.s.wdata; + assign mem_WLAST = mem.s.wlast; + assign mem_WSTRB = mem.s.wstrb; + assign mem_WVALID = mem.s.wvalid; + assign mem.s.wready = mem_WREADY; + + assign mem_BREADY = mem.s.bready; + assign mem.s.bid = mem_BID; + assign mem.s.bresp = mem_BRESP; + assign mem.s.bvalid = mem_BVALID; + + assign mem_ARID = mem.s.arid; + assign mem_ARLEN = mem.s.arlen; + assign mem_ARADDR = mem.s.araddr; + assign mem_ARSIZE = mem.s.arsize; + assign mem_ARBURST = mem.s.arburst; + assign mem_ARVALID = mem.s.arvalid; + assign mem.s.arready = mem_ARREADY; + + assign mem_RREADY = mem.s.rready; + assign mem.s.rid = mem_RID; + assign mem.s.rdata = mem_RDATA; + assign mem.s.rresp = mem_RRESP; + assign mem.s.rlast = mem_RLAST; + assign mem.s.rvalid = mem_RVALID; + + assign mem_AWID = mem.s.awid; + assign mem_AWLEN = mem.s.awlen; + assign mem_AWADDR = mem.s.awaddr; + assign mem_AWSIZE = mem.s.awsize; + assign mem_AWBURST = mem.s.awburst; + assign mem_AWVALID = mem.s.awvalid; + assign mem.s.awready = mem_AWREADY; + + assign mem_WDATA = mem.s.wdata; + assign mem_WLAST = mem.s.wlast; + assign mem_WSTRB = mem.s.wstrb; + assign mem_WVALID = mem.s.wvalid; + assign mem.s.wready = mem_WREADY; + + assign pc_front_group = regfile.regs.pc_front_group; + assign regfile.regs.pc_front = pc_front_pc; + + assign front_back.back.loop.group = loop_group; + assign front_back.back.loop.valid = loop_valid; + + assign wave_insn = front.bind_.wave.insn; + assign wave_group = front.bind_.wave.group; + assign wave_retry = front.bind_.wave.retry; + assign wave_valid = front.bind_.wave.valid; + + assign runnable_in_ready = front.bind_.runnable_in.tx.ready; + assign runnable_out_data = front.bind_.runnable_out.rx.data; + assign runnable_out_valid = front.bind_.runnable_out.rx.valid; + + gfx_shader_front front + ( + .clk, + .rst_n, + .front(front_back.front), + .reg_read(regfile.read), // Únicamente para que verilator esté feliz + .reg_bind(regfile.bind_), + .fetch_mem(mem.m), + .icache_flush + ); + +endmodule diff --git a/tb/gfx_shader_bind/mod.mk b/tb/gfx_shader_bind/mod.mk new file mode 100644 index 0000000..e031f52 --- /dev/null +++ b/tb/gfx_shader_bind/mod.mk @@ -0,0 +1,10 @@ +define core + $(this)/deps := gfx + $(this)/targets := test + + $(this)/rtl_top := dut + $(this)/rtl_files := dut.sv + + $(this)/cocotb_paths := . + $(this)/cocotb_modules := testbench.main +endef diff --git a/tb/gfx_shader_bind/testbench/__init__.py b/tb/gfx_shader_bind/testbench/__init__.py new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/tb/gfx_shader_bind/testbench/__init__.py diff --git a/tb/gfx_shader_bind/testbench/axi.py b/tb/gfx_shader_bind/testbench/axi.py new file mode 100644 index 0000000..2f58531 --- /dev/null +++ b/tb/gfx_shader_bind/testbench/axi.py @@ -0,0 +1,190 @@ +import enum + +import cocotb +from cocotb.binary import BinaryValue +from cocotb.triggers import Lock, RisingEdge, ReadOnly + +from cocotb_bus.drivers import BusDriver + +class AXIBurst(enum.IntEnum): + FIXED = 0b00 + INCR = 0b01 + WRAP = 0b10 + + +class AXIxRESP(enum.IntEnum): + OKAY = 0b00 + EXOKAY = 0b01 + SLVERR = 0b10 + DECERR = 0b11 + + +class AXIProtocolError(Exception): + def __init__(self, message: str, xresp: AXIxRESP): + super().__init__(message) + self.xresp = xresp + + +class AXIReadBurstLengthMismatch(Exception): + pass + + +class AXI4Agent(BusDriver): + ''' + AXI4 Agent + + Monitors an internal memory and handles read and write requests. + ''' + _signals = [ + "ARREADY", "ARVALID", "ARADDR", # Read address channel + "ARLEN", "ARSIZE", "ARBURST", "ARPROT", + + "RREADY", "RVALID", "RDATA", "RLAST", # Read response channel + + "AWREADY", "AWADDR", "AWVALID", # Write address channel + "AWPROT", "AWSIZE", "AWBURST", "AWLEN", + + "WREADY", "WVALID", "WDATA", + + ] + + # Not currently supported by this driver + _optional_signals = [ + "WLAST", "WSTRB", + "BVALID", "BREADY", "BRESP", "RRESP", + "RCOUNT", "WCOUNT", "RACOUNT", "WACOUNT", + "ARLOCK", "AWLOCK", "ARCACHE", "AWCACHE", + "ARQOS", "AWQOS", "ARID", "AWID", + "BID", "RID", "WID" + ] + + def __init__(self, entity, name, clock, memory, callback=None, event=None, + big_endian=False, **kwargs): + + BusDriver.__init__(self, entity, name, clock, **kwargs) + self.clock = clock + + self.big_endian = big_endian + self.bus.ARREADY.setimmediatevalue(0) + self.bus.RVALID.setimmediatevalue(0) + self.bus.RLAST.setimmediatevalue(0) + self.bus.AWREADY.setimmediatevalue(0) + self._memory = memory + + self.write_address_busy = Lock("%s_wabusy" % name) + self.read_address_busy = Lock("%s_rabusy" % name) + self.write_data_busy = Lock("%s_wbusy" % name) + + cocotb.start_soon(self._read_data()) + cocotb.start_soon(self._write_data()) + + def _size_to_bytes_in_beat(self, AxSIZE): + if AxSIZE < 7: + return 2 ** AxSIZE + return None + + async def _write_data(self): + clock_re = RisingEdge(self.clock) + + while True: + while True: + self.bus.WREADY.value = 0 + await ReadOnly() + if self.bus.AWVALID.value: + self.bus.WREADY.value = 1 + break + await clock_re + + await ReadOnly() + _awaddr = int(self.bus.AWADDR) + _awlen = int(self.bus.AWLEN) + _awsize = int(self.bus.AWSIZE) + _awburst = int(self.bus.AWBURST) + _awprot = int(self.bus.AWPROT) + + burst_length = _awlen + 1 + bytes_in_beat = self._size_to_bytes_in_beat(_awsize) + + if __debug__: + self.log.debug( + "AWADDR %d\n" % _awaddr + + "AWLEN %d\n" % _awlen + + "AWSIZE %d\n" % _awsize + + "AWBURST %d\n" % _awburst + + "AWPROT %d\n" % _awprot + + "BURST_LENGTH %d\n" % burst_length + + "Bytes in beat %d\n" % bytes_in_beat) + + burst_count = burst_length + + await clock_re + + while True: + if self.bus.WVALID.value: + word = self.bus.WDATA.value + word.big_endian = self.big_endian + _burst_diff = burst_length - burst_count + _st = _awaddr + (_burst_diff * bytes_in_beat) # start + _end = _awaddr + ((_burst_diff + 1) * bytes_in_beat) # end + self._memory[_st:_end] = array.array('B', word.buff) + burst_count -= 1 + if burst_count == 0: + break + await clock_re + + async def _read_data(self): + clock_re = RisingEdge(self.clock) + + while True: + self.bus.ARREADY.value = 1 + while True: + await ReadOnly() + if self.bus.ARVALID.value: + break + await clock_re + + await ReadOnly() + _araddr = int(self.bus.ARADDR) + _arlen = int(self.bus.ARLEN) + _arsize = int(self.bus.ARSIZE) + _arburst = int(self.bus.ARBURST) + _arprot = int(self.bus.ARPROT) + + burst_length = _arlen + 1 + bytes_in_beat = self._size_to_bytes_in_beat(_arsize) + + word = BinaryValue(n_bits=bytes_in_beat*8, bigEndian=self.big_endian) + + if __debug__: + self.log.debug( + "ARADDR %d\n" % _araddr + + "ARLEN %d\n" % _arlen + + "ARSIZE %d\n" % _arsize + + "ARBURST %d\n" % _arburst + + "ARPROT %d\n" % _arprot + + "BURST_LENGTH %d\n" % burst_length + + "Bytes in beat %d\n" % bytes_in_beat) + + burst_count = burst_length + + await clock_re + self.bus.ARREADY.value = 0 + + self.bus.RVALID.value = 1 + while burst_count > 0: + _burst_diff = burst_length - burst_count + _st = _araddr + (_burst_diff * bytes_in_beat) + _end = _araddr + ((_burst_diff + 1) * bytes_in_beat) + word.buff = self._memory[_st:_end].tobytes() + self.bus.RDATA.value = word + self.bus.RLAST.value = int(burst_count == 1) + + await ReadOnly() + + if self.bus.RREADY.value: + burst_count -= 1 + + await clock_re + self.bus.RLAST.value = 0 + + self.bus.RVALID.value = 0 diff --git a/tb/gfx_shader_bind/testbench/checkers.py b/tb/gfx_shader_bind/testbench/checkers.py new file mode 100644 index 0000000..0109249 --- /dev/null +++ b/tb/gfx_shader_bind/testbench/checkers.py @@ -0,0 +1,68 @@ +import cocotb +from cocotb.triggers import RisingEdge, ReadOnly + +from cocotb_coverage.coverage import CoverCheck + +class PipelineIntegrityChecker: + def __init__(self, dut, name, clk): + self._clk, self._dut = clk, dut + self._queue = [None] * dut.front.bind_.BIND_STAGES.value + self._ready_sticky = False + + @CoverCheck( + f'{name}.runnable_tx_ready', + f_pass = lambda ready: not self._ready_sticky and ready, + f_fail = lambda ready: self._ready_sticky and not ready, + ) + def sample_ready(ready): + self._ready_sticky = self._ready_sticky or ready + + @CoverCheck( + f'{name}.in_to_out_integrity', + f_pass = lambda group: group == self._queue[0] and group is not None, + f_fail = lambda group: group != self._queue[0], + ) + def sample_wave_group(group): + pass + + self._sample_ready = sample_ready + self._sample_wave_group = sample_wave_group + + cocotb.start_soon(self._run()) + + async def _run(self): + while True: + await RisingEdge(self._clk) + await ReadOnly() + + self._sample_ready(self._dut.runnable_in_ready.value) + + if self._dut.wave_valid.value: + self._sample_wave_group(self._dut.wave_group.value) + else: + self._sample_wave_group(None) + + new_group = None + if self._dut.runnable_out_valid.value: + new_group = self._dut.runnable_out_data.value + + self._queue[:-1] = self._queue[1:] + self._queue[-1] = new_group + +class PcChecker: + def __init__(self, dut, name, clk, *, mem, pc_table): + self._clk, self._dut = clk, dut + self._mem, self._pc_table = mem, pc_table + + @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), + ) + def sample_wave(wave): + pass + + self.sample_wave = sample_wave + + def _pc_ok(self, wave): + return wave.insn == self._mem.read(self._pc_table[wave.group] * 4) diff --git a/tb/gfx_shader_bind/testbench/common.py b/tb/gfx_shader_bind/testbench/common.py new file mode 100644 index 0000000..0e9a97b --- /dev/null +++ b/tb/gfx_shader_bind/testbench/common.py @@ -0,0 +1,6 @@ +import cocotb + +MAX_GROUPS = 64 +MEM_WORDS = 64 << (20 - 2) + +log = cocotb.logging.getLogger("cocotb.test") diff --git a/tb/gfx_shader_bind/testbench/data.py b/tb/gfx_shader_bind/testbench/data.py new file mode 100644 index 0000000..4aa3630 --- /dev/null +++ b/tb/gfx_shader_bind/testbench/data.py @@ -0,0 +1,23 @@ +BAD_PC = 0xffff_ffff >> 2 + +class FrontWave: + def __init__(self, *, group, insn, soft=False): + self.group, self.insn = group, insn + self.retry = insn is None + self._soft = soft + + def __eq__(self, other): + if self._soft and not other._soft: + 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))) + + 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 '' + 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/drivers.py b/tb/gfx_shader_bind/testbench/drivers.py new file mode 100644 index 0000000..c069d97 --- /dev/null +++ b/tb/gfx_shader_bind/testbench/drivers.py @@ -0,0 +1,85 @@ +import cocotb +from cocotb.clock import Clock +from cocotb.queue import Queue +from cocotb.triggers import Event, ReadOnly, RisingEdge + +from cocotb_bus.drivers import BusDriver + +from .data import BAD_PC + +class ClockResetDriver: + def __init__(self, dut): + self._reset_event = Event('reset_done') + + dut.clk.setimmediatevalue(0) + dut.rst_n.setimmediatevalue(0) + + self._dut = dut + self._clock_gen = Clock(dut.clk, 2, 'step') + + def start(self): + cocotb.start_soon(self._clock_gen.start()) + cocotb.start_soon(self.reset()) + + async def reset(self): + self._reset_event.clear() + + self._dut.rst_n.value = 0 + await RisingEdge(self._dut.clk) + self._dut.rst_n.value = 1 + await ReadOnly() + + self._reset_event.set() + + async def wait_for_reset(self): + await self._reset_event.wait() + +class PcDriver(BusDriver): + _signals = ['pc', 'group'] + + def __init__(self, *args, table, **kwargs): + super().__init__(*args, **kwargs) + + self._table = table + self._delay1 = BAD_PC + self._delay2 = BAD_PC + self._delay3 = BAD_PC + + cocotb.start_soon(self._run()) + + async def _run(self): + while True: + self.bus.pc.value = self._delay3 + + await RisingEdge(self.clock) + + self._delay3 = self._delay2 + self._delay2 = self._delay1 + self._delay1 = self._table[self.bus.group.value] + +class LoopDriver(BusDriver): + _signals = ['group', 'valid'] + + def __init__(self, *args, maxsize=8, **kwargs): + super().__init__(*args, **kwargs) + + self._queue = Queue(maxsize=maxsize) + self.bus.valid.setimmediatevalue(0) + + cocotb.start_soon(self._run()) + + async def put(self, group): + await self._queue.put(group) + + async def _run(self): + while True: + await RisingEdge(self.clock) + + valid = not self._queue.empty() + if valid: + group = self._queue.get_nowait() + valid = group is not None + + self.bus.valid.value = int(valid) + if valid: + self.bus.group.value = group diff --git a/tb/gfx_shader_bind/testbench/main.py b/tb/gfx_shader_bind/testbench/main.py new file mode 100644 index 0000000..f31e198 --- /dev/null +++ b/tb/gfx_shader_bind/testbench/main.py @@ -0,0 +1,91 @@ +import random + +import cocotb +from cocotb.triggers import ClockCycles, FallingEdge, ReadOnly, RisingEdge + +from cocotb_bus.scoreboard import Scoreboard + +from cocotb_coverage.coverage import coverage_db + +from .axi import AXI4Agent +from .data import FrontWave +from .common import MAX_GROUPS, MEM_WORDS, log +from .models import Memory, PcTable +from .drivers import ClockResetDriver, LoopDriver, PcDriver +from .checkers import PcChecker, PipelineIntegrityChecker +from .monitors import FrontWaveMonitor + +@cocotb.test() +async def test(dut): + mem = Memory('insn_mem', word_size=4, words=MEM_WORDS) + mem_agent = AXI4Agent(dut, 'mem', dut.clk, mem, case_insensitive=False) + + out_groups = set() + out_groups_retry = set() + out_monitor = FrontWaveMonitor(dut, 'wave', dut.clk, case_insensitive=False) + + def out_callback(wave): + nonlocal out_groups, out_groups_retry + + out_groups.add(wave.group) + if wave.insn is None: + out_groups_retry.add(wave.group) + + out_monitor.add_callback(out_callback) + + out_expected = [] + out_scoreboard = Scoreboard(dut, fail_immediately=False) + out_scoreboard.add_interface(out_monitor, out_expected) + + clock_reset = ClockResetDriver(dut) + clock_reset.start() + + pc_table = PcTable() + pc_driver = PcDriver(dut, 'pc_front', dut.clk, table=pc_table, case_insensitive=False) + loop_driver = LoopDriver(dut, 'loop', dut.clk, case_insensitive=False) + + pc_checker = PcChecker(dut, 'bind', dut.clk, mem=mem, pc_table=pc_table) + pipeline_checker = PipelineIntegrityChecker(dut, 'bind', dut.clk) + + out_monitor.add_callback(pc_checker.sample_wave) + + await clock_reset.wait_for_reset() + + await FallingEdge(dut.front.bind_.icache.in_flush) #FIXME + await RisingEdge(dut.clk) + + for iteration in range(1000): + all_groups = list(range(MAX_GROUPS)) + random.shuffle(all_groups) + + for group in all_groups: + pc = random.randint(0, MEM_WORDS) * 4 + pc_table[group] = pc + mem.randomize_line(pc) + + out_expected.append(FrontWave(group=group, insn=mem.read(pc), 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) + + out_expected.append(FrontWave(group=group, insn=mem.read(pc_table[group] * 4), soft=True)) + + await loop_driver.put(group) + + await RisingEdge(dut.clk) + + for _ in range(100): + await RisingEdge(dut.clk) + + out_groups.clear() + out_groups_retry.clear() + + coverage_db.report_coverage(log.info, bins=True) + coverage_db.export_to_xml(filename="coverage.xml") + coverage_db.export_to_yaml(filename='coverage.yml') + + raise out_scoreboard.result 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 diff --git a/tb/gfx_shader_bind/testbench/monitors.py b/tb/gfx_shader_bind/testbench/monitors.py new file mode 100644 index 0000000..6ecea0f --- /dev/null +++ b/tb/gfx_shader_bind/testbench/monitors.py @@ -0,0 +1,30 @@ +import cocotb +from cocotb.triggers import ReadOnly, RisingEdge + +from cocotb_bus.monitors import BusMonitor + +from .data import FrontWave + +class FrontWaveMonitor(BusMonitor): + _signals = ['insn', 'group', 'retry', 'valid'] + + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + + async def _monitor_recv(self): + pkt_receiving = False + received_data = [] + + while True: + await RisingEdge(self.clock) + await ReadOnly() + + if not self.bus.valid.value: + continue + + wave = FrontWave( + group=self.bus.group.value.integer, + insn=(self.bus.insn.value.integer if not self.bus.retry.value else None), + ) + + self._recv(wave) @@ -1,5 +1,5 @@ cores := ip_mul interconnect -subdirs := top/conspiracion +subdirs := gfx_shader_bind top/conspiracion define core/ip_mul $(this)/rtl_files := dsp_mul.sv |
