class DspBlockTB(object): def __init__(self, dut, debug=False): self.dut = dut self.stream_in = STDriver(dut, "ValNamein_0", dut.clock, name_map=axi4stream_chisel_name_map) self.backpressure = BitDriver(self.dut.out_0_ready, self.dut.clock) self.stream_out = STMonitor(dut, "out_0", dut.clock, name_map=axi4stream_chisel_name_map) self.csr = MemMaster(dut, "ValNameioMem_0", dut.clock, name_map=axi4_chisel_name_map) self.set_rotation(0) # Reconstruct the input transactions from the pins # and send them to our 'model' self.stream_in_recovered = STMonitor( dut, "ValNamein_0", dut.clock, callback=self.model, name_map=axi4stream_chisel_name_map) # Create a scoreboard on the stream_out bus self.pkts_sent = 0 self.expected_output = [] self.scoreboard = Scoreboard(dut) self.scoreboard.add_interface(self.stream_out, self.expected_output) # Set verbosity on our various interfaces level = logging.DEBUG if debug else logging.WARNING self.stream_in.log.setLevel(level) self.stream_in_recovered.log.setLevel(level) def set_rotation(self, rotation): self.rotation = rotation return self.csr.write(0, self.rotation) def model(self, transaction): """Model the DUT based on the input transaction""" ## TODO apply rotation self.expected_output.append(transaction) self.pkts_sent += 1 @cocotb.coroutine def reset(self, duration=10): self.dut._log.debug("Resetting DUT") self.dut.reset <= 1 self.stream_in.bus.TVALID <= 0 yield Timer(duration, units='ns') yield RisingEdge(self.dut.clock) self.dut.reset <= 0 self.dut._log.debug("Out of reset")
class BasebandTB(object): def __init__(self, dut, debug=False): self.dut = dut self.csrBase = 0x79040000 self.stream_in = STDriver(dut, "adc_0", dut.clock, big_endian=False, **stream_names) self.csr = MemMaster(dut, "s_axi", dut.s_axi_aclk, **lower_axil) self.memory = np.arange(1024 * 1024 * 1024, dtype=np.dtype('b')) self.mem = MemSlave(dut, "m_axi", dut.s_axi_aclk, memory=self.memory, **lower_axi) # self.stream_in_recovered = STMonitor(dut, "adc_0", dut.clock, **stream_names) self.stream_out = STMonitor( dut, "dac_0", dut.clock, **stream_names) #, callback = self.append_channel) self.expected_output = [] self.txdata = [] self.write_monitor = WriteMonitor(dut, "m_axi", dut.s_axi_aclk, **lower_axi) eq_block = dut.sAxiIsland.freqRx.freqRx.eq # self.eq_monitor_in = DecoupledMonitor(eq_block, "in", eq_block.clock, reset=eq_block.reset) fft_block = dut.sAxiIsland.freqRx.freqRx.fft # self.fft_mon = FFTMonitor(fft_block, fft_block.clock) self.scoreboard = Scoreboard(dut) # self.scoreboard.add_interface(self.stream_out, self.expected_output) # self.scoreboard.add_interface(self.write_monitor, self.txdata) level = logging.DEBUG if debug else logging.WARNING self.stream_in.log.setLevel(level) self.csr.log.setLevel(level) self.mem.log.setLevel(level) # self.stream_in_recovered.log.setLevel(level) self.channel_model = SISOChannel() @cocotb.coroutine def append_channel(self): yield ReadOnly() while True: if self.dut.dac_0_valid.value: data = self.dut.dac_0_data.value.get_value() else: data = 0 # print("append_channel") self.channel_model.push_packed_sample(data) yield RisingEdge(self.dut.clock) yield ReadOnly() @cocotb.coroutine def get_channel(self): dataword = BinaryValue(n_bits=32) # self.stream_in.bus.TVALID <= 1 while True: # print("get_channel") # dataword.assign(str(self.channel_model.pop_packed_sample())) # self.stream_in.bus.TDATA <= dataword data = self.channel_model.pop_packed_sample() yield self.stream_in._driver_send(data=data, sync=False) # yield RisingEdge(self.dut.clock) @cocotb.coroutine def dma_to_mm(self, *, base=0, size=None): if size is None: size = len(self.memory) // 4 # base yield self.csr.write(self.csrBase + 4 * 4, base) # length yield self.csr.write(self.csrBase + 5 * 4, size - 1) # cycles yield self.csr.write(self.csrBase + 6 * 4, 0) # fixed yield self.csr.write(self.csrBase + 7 * 4, 0) # go yield self.csr.write(self.csrBase + 8 * 4, 1) while True: bytesLeft = yield self.csr.read(self.csrBase + 8 * 4) if not bytesLeft: break @cocotb.coroutine def dma_mm_to_dac(self, *, base=0, size=None): if size is None: size = len(self.memory) // 4 # enable Tx / disable DMA passthrough yield self.csr.write(0x79040A00 + 0 * 4, 1) # base yield self.csr.write(self.csrBase + 0x9 * 4, base) # length yield self.csr.write(self.csrBase + 0xA * 4, size - 1) # cycles yield self.csr.write(self.csrBase + 0xB * 4, 100) # fixed yield self.csr.write(self.csrBase + 0xC * 4, 0) # skid disable yield self.csr.write(self.csrBase + 0x200, 0) # skid drain upstream queue yield self.csr.write(self.csrBase + 0x200 + 0x5 * 4, 1) # skid clear overflow register yield self.csr.write(self.csrBase + 0x200 + 0x3 * 4, 0) # go yield self.csr.write(self.csrBase + 0xD * 4, 0) # enable yield self.csr.write(self.csrBase + 0x0, 1) # skid enable yield self.csr.write(self.csrBase + 0x200, 1) # wait for end while True: yield ClockCycles(self.dut.s_axi_aclk, 50) out = yield self.csr.read(self.csrBase + 0xD * 4) if out == 0: break print("Done with TX") @cocotb.coroutine def set_aligner(self, base=0x100, *, en=True, cnt=1, cntPassthrough=False): if cntPassthrough: cntPassthrough = 1 else: cntPassthrough = 0 if en: en = 1 else: en = 0 if base < 0x70000000: base = base + self.csrBase yield self.csr.write(base + 0xC, cnt) yield self.csr.write(base + 0x10, cntPassthrough) yield self.csr.write(base, 1) @cocotb.coroutine def set_input_splitter_mux(self, base=0x900): if base < 0x70000000: base = base + self.csrBase yield self.csr.write(base, 0) @cocotb.coroutine def set_input_stream_mux(self, base=0x300): if base < 0x70000000: base = base + self.csrBase yield self.csr.write(base, 0) # yield self.csr.write(base + 4, 0) @cocotb.coroutine def set_schedule(self, base=0x800, *, length, time): if base < 0x70000000: base = base + self.csrBase yield self.csr.write(base, length) yield self.csr.write(base + 0x4, time) yield self.csr.write(base + 0xC, 1) # go! @cocotb.coroutine def set_timerx(self, base=0x400, **kwargs): if base < 0x70000000: base = base + self.csrBase settings = { 'autocorrFF': 0.9, 'peakThreshold': 0.05, 'peakOffset': 0.0, 'freqMultiplier': 0.0, 'autocorrDepthApart': 65, 'autocorrDepthOverlap': 63, 'peakDetectNumPeaks': 16, 'peakDetectPeakDistance': 32, 'packetLength': 222 + 7, 'samplesToDrop': 0, 'inputDelay': 74, } representations = { 'autocorrFF': FixedPointRepresentation(bp=17), 'peakThreshold': FixedPointRepresentation(bp=17), 'peakOffset': FixedPointRepresentation(bp=17), 'freqMultiplier': FixedPointRepresentation(bp=17), 'autocorrDepthApart': IntRepresentation(), 'autocorrDepthOverlap': IntRepresentation(), 'peakDetectNumPeaks': IntRepresentation(), 'peakDetectPeakDistance': IntRepresentation(), 'packetLength': IntRepresentation(), 'samplesToDrop': IntRepresentation(), 'inputDelay': IntRepresentation() } settings = {**settings, **kwargs} for key in settings.keys(): kwargs.pop(key, None) if len(kwargs) != 0: raise TypeError(f"Unexpected kwargs {kwargs}") for idx, (key, val) in enumerate(settings.items()): # print(f"Writing {representations[key](val)} ({val}) to {base + idx * 4}") yield self.csr.write(base + idx * 4, representations[key](val)) # write globalCycleEn yield self.csr.write(base + len(settings) * 4, 1) @cocotb.coroutine def transmit(self, data): txdata = encode_tx(data, addPreamble=True) # txdata = encode_linear_seq(222) self.txdata.extend(data) for i in range(len(txdata)): self.memory[i] = txdata[i] yield self.dma_mm_to_dac(base=0, size=len(txdata) // 4 - 1) # self.dma_to_mm(base = 0 * 1024 * 4, size = len(txdata)) @cocotb.coroutine def transmit_forever(self, data, wait_cycles=100): txdata = encode_tx(data, addPreamble=True) # txdata = encode_linear_seq(222) self.txdata.extend(data) for i in range(len(txdata)): self.memory[i] = txdata[i] while True: yield ClockCycles(self.dut.clock, wait_cycles) yield self.dma_mm_to_dac(base=0, size=len(txdata) // 4 - 1) @cocotb.coroutine def handle_packet_detect(self, *, base=0x400): if base < 0x70000000: base = base + self.csrBase while True: # check if an interrupt has fired if not self.dut.skid_ints_0.value: # if not, wait for one yield RisingEdge(self.dut.skid_ints_0) time = yield self.csr.read(base + 13 * 4) print(f"Saw packet at time {time.signed_integer}") @cocotb.coroutine def reset(self, duration=5): self.dut._log.debug("Resetting DUT") self.dut.reset <= 1 self.dut.s_axi_aresetn <= 0 self.stream_in.bus.TVALID <= 0 yield ClockCycles(self.dut.clock, duration) self.dut.reset <= 0 yield RisingEdge(self.dut.s_axi_aclk) self.dut.s_axi_aresetn <= 1 self.dut.s_axi_awprot <= 0 self.dut.dac_0_ready <= 1 self.dut.dac_1_ready <= 1 self.dut._log.debug("Setting registers to drain input streams") yield self.csr.write(self.csrBase + 0x200 + 0x5 * 4, 1) yield self.csr.write(self.csrBase + 0x200, 0) yield self.csr.write(self.csrBase + 0x100, 0) self.dut._log.debug("Out of reset")