def __init__(self, dut, num_samples, input_width): clk = Clock(dut.clk_i, 40) clk_3x = Clock(dut.clk_3x_i, 120) self.multiclock = MultiClock([clk, clk_3x]) self.dut = dut self.re_inputs = random_samples(input_width, num_samples) self.im_inputs = random_samples(input_width, num_samples) self.outputs = np.fft.fft(self.re_inputs + 1j * self.im_inputs)
def __init__(self, dut): clk = Clock(dut.clk_i, 40) clk_7_5 = Clock(dut.clk_7_5mhz, 7.5) clk_120 = Clock(dut.clk_120mhz, 120) clk_80 = Clock(dut.clk_80mhz, 80) clk_20 = Clock(dut.clk_20mhz, 20) ftclk = Clock(dut.ft_clkout_i, 60) self.multiclock = MultiClock( [clk, clk_7_5, clk_120, clk_80, clk_20, ftclk]) self.dut = dut
class FT245_TB: """ FT2232H FT245 asynchronous mode FIFO testbench class. """ def __init__(self, dut): clk = Clock(dut.clk, 40) ft_clk = Clock(dut.ft_clk, 60) slow_ft_clk = Clock(dut.slow_ft_clk, 7.5) self.multiclock = MultiClock([clk, ft_clk, slow_ft_clk]) self.dut = dut @cocotb.coroutine async def setup(self): self.multiclock.start_all_clocks() await self.reset() @cocotb.coroutine async def reset(self): await self.await_all_clocks() self.dut.rst_n <= 0 await self.await_all_clocks() self.dut.rst_n <= 1 @cocotb.coroutine async def await_all_clocks(self): trigs = [] for clk in self.multiclock.clocks: trigs.append(RisingEdge(clk.clk)) await Combine(*trigs) @cocotb.coroutine async def fpga_write_continuous(self, inputs): """ Continuously write from FPGA. When inputs have been exhausted, write zeros. """ sample_ctr = 0 num_samples = len(inputs) self.dut.wren <= 1 while True: if sample_ctr < num_samples: self.dut.wrdata <= BinaryValue( int(inputs[sample_ctr].item()), 64, binaryRepresentation=2) else: self.dut.wrdata <= 0 sample_ctr += 1 await RisingEdge(self.dut.clk)
class PllSyncCtrTB: def __init__(self, dut): fst_clk = Clock(dut.fst_clk, 80) slw_clk = Clock(dut.slw_clk, 10) self.multiclock = MultiClock([fst_clk, slw_clk]) self.dut = dut @cocotb.coroutine async def setup(self): self.multiclock.start_all_clocks() await self.await_all_clocks() self.dut.rst_n <= 0 await self.await_all_clocks() self.dut.rst_n <= 1 @cocotb.coroutine async def await_all_clocks(self): trigs = [] for clk in self.multiclock.clocks: trigs.append(RisingEdge(clk.clk)) await Combine(*trigs)
def __init__(self, dut): rdclk = Clock(dut.rdclk, 60) wrclk = Clock(dut.wrclk, 40) self.multiclock = MultiClock([rdclk, wrclk]) self.dut = dut
class AsyncFifoTB: """ Async FIFO testbench class. Used to setup, drive and monitor the async FIFO under test. """ def __init__(self, dut): rdclk = Clock(dut.rdclk, 60) wrclk = Clock(dut.wrclk, 40) self.multiclock = MultiClock([rdclk, wrclk]) self.dut = dut @cocotb.coroutine async def setup(self): """Startup the async FIFO.""" self.multiclock.start_all_clocks() await self.reset() @cocotb.coroutine async def reset(self): """ Perform a sufficiently long reset to be registered by all clock domains. """ await self.await_all_clocks() self.dut.rst_n <= 0 self.dut.rden <= 0 self.dut.wren <= 0 await self.await_all_clocks() self.dut.rst_n <= 1 @cocotb.coroutine async def await_all_clocks(self): """ Wait for positive edge on both clocks before proceeding. """ trigs = [] for clk in self.multiclock.clocks: trigs.append(RisingEdge(clk.clk)) await Combine(*trigs) @cocotb.coroutine async def write(self, val): """Write a value to the FIFO.""" await RisingEdge(self.dut.wrclk) self.dut.wren <= 1 self.dut.wrdata <= val await RisingEdge(self.dut.wrclk) @cocotb.coroutine async def read(self): """Read a value from the FIFO.""" await RisingEdge(self.dut.rdclk) self.dut.rden <= 1 await RisingEdge(self.dut.rdclk) await ReadOnly() rdval = self.dut.rddata.value return rdval @cocotb.coroutine async def wait_n_read_cycles(self, ncycles): """Wait ncycles cycles for rdclk.""" await RisingEdge(self.dut.rdclk) self.dut.rden <= 0 while ncycles > 0: ncycles -= 1 await RisingEdge(self.dut.rdclk) @cocotb.coroutine async def wait_n_write_cycles(self, ncycles): """Wait ncycles cycles for wrclk.""" await RisingEdge(self.dut.wrclk) self.dut.wren <= 0 while ncycles > 0: ncycles -= 1 await RisingEdge(self.dut.wrclk) @cocotb.coroutine async def get_value(self, obj): """ Return the current value of some part of the FIFO. Note that this does not wait for a clock edge, so care should be taken that the read is performed at the desired time. obj: The value to read (e.g. dut.wraddr) """ await ReadOnly() return obj.value
def __init__(self, dut): fst_clk = Clock(dut.fst_clk, 80) slw_clk = Clock(dut.slw_clk, 10) self.multiclock = MultiClock([fst_clk, slw_clk]) self.dut = dut
def __init__(self, dut): clk = Clock(dut.clk, 40) ft_clk = Clock(dut.ft_clk, 60) slow_ft_clk = Clock(dut.slow_ft_clk, 7.5) self.multiclock = MultiClock([clk, ft_clk, slow_ft_clk]) self.dut = dut
class FFTTB: """ R22 SDF FFT testbench class. """ def __init__(self, dut, num_samples, input_width): clk = Clock(dut.clk_i, 40) clk_3x = Clock(dut.clk_3x_i, 120) self.multiclock = MultiClock([clk, clk_3x]) self.dut = dut self.re_inputs = random_samples(input_width, num_samples) self.im_inputs = random_samples(input_width, num_samples) self.outputs = np.fft.fft(self.re_inputs + 1j * self.im_inputs) @cocotb.coroutine async def setup(self): self.multiclock.start_all_clocks() await self.reset() @cocotb.coroutine async def reset(self): await self.await_all_clocks() self.dut.rst_n <= 0 await self.await_all_clocks() self.dut.rst_n <= 1 @cocotb.coroutine async def await_all_clocks(self): """ Wait for positive edge on both clocks before proceeding. """ trigs = [] for clk in self.multiclock.clocks: trigs.append(RisingEdge(clk.clk)) await Combine(*trigs) def check_outputs(self, tolerance): """ Check that the measured outputs are within the specified tolerance of the actual outputs. Raise a test failure if not. If the tolerance is satisfied, return a tuple of the difference values. """ bit_rev_ctr = self.dut.data_ctr_o.value.integer rval = self.dut.data_re_o.value.signed_integer rexp = np.real(self.outputs)[bit_rev_ctr].item() rdiff = rval - rexp ival = self.dut.data_im_o.value.signed_integer iexp = np.imag(self.outputs)[bit_rev_ctr].item() idiff = ival - iexp if abs(rval - rexp) > tolerance: raise TestFailure(("Actual real output differs from expected." " Actual: %d, expected: %d, difference: %d." " Tolerance set at %d.") % (rval, rexp, rval - rexp, tolerance)) if abs(ival - iexp) > tolerance: raise TestFailure(("Actual imaginary output differs from expected." " Actual: %d, expected: %d, difference: %d." " Tolerance set at %d.") % (ival, iexp, ival - iexp, tolerance)) return (rdiff, idiff) @cocotb.coroutine async def write_inputs(self): """ Send all calculated inputs to dut. """ ctr = 0 num_samples = len(self.re_inputs) while True: if ctr < num_samples: self.dut.data_re_i <= self.re_inputs[ctr].item() self.dut.data_im_i <= self.im_inputs[ctr].item() else: self.dut.data_re_i <= 0 self.dut.data_im_i <= 0 await RisingEdge(self.dut.clk_i) ctr += 1 @cocotb.coroutine async def send_intermittent_resets(self): """ Randomly send reset signals to FFT. """ timestep = min(self.multiclock.clock_periods()) while True: self.dut.rst_n <= 1 time_on = timestep * np.random.randint(1e2, 1e4, dtype=int) await Timer(time_on) self.dut.rst_n <= 0 time_off = timestep * np.random.randint(1e2, 1e3, dtype=int) await Timer(time_off)
class TopTB: def __init__(self, dut): clk = Clock(dut.clk_i, 40) clk_7_5 = Clock(dut.clk_7_5mhz, 7.5) clk_120 = Clock(dut.clk_120mhz, 120) clk_80 = Clock(dut.clk_80mhz, 80) clk_20 = Clock(dut.clk_20mhz, 20) ftclk = Clock(dut.ft_clkout_i, 60) self.multiclock = MultiClock( [clk, clk_7_5, clk_120, clk_80, clk_20, ftclk]) self.dut = dut @cocotb.coroutine async def setup(self): self.multiclock.start_all_clocks() self.dut.ft_rxf_n_i <= 1 self.dut.ft_txe_n_i <= 1 self.dut.ft_suspend_n_i <= 1 await self.reset() @cocotb.coroutine async def reset(self): await self.await_all_clocks() self.dut.pll_lock <= 0 await self.await_all_clocks() self.dut.pll_lock <= 1 @cocotb.coroutine async def await_all_clocks(self): trigs = [] for clk in self.multiclock.clocks: trigs.append(RisingEdge(clk.clk)) await Combine(*trigs) @cocotb.coroutine async def rand_lose_lock(self): """ Simulate random loss of PLL lock. This attempts to be somewhat physically accurate by mainting the lock for longer periods than it is lost. """ time_unit = min(self.multiclock.clock_periods()) while True: time_on = np.random.randint(1e3 * time_unit, 1e4 * time_unit, dtype=int) await Timer(time_on) self.dut.pll_lock <= 0 time_off = np.random.randint(1e1 * time_unit, 1e2 * time_unit, dtype=int) await Timer(time_off) self.dut.pll_lock <= 1 @cocotb.coroutine async def pc_request_mode(self, mode): """ Simulate sending a request from the host PC for a certain kind of data. This is equivalent to using the fmcw -i flag. The valid values are the same. I.e. @mode can be 'raw', 'fir', 'window', or 'fft'. """ if mode == "fft": mode_bits = 1 elif mode == "window": mode_bits = 2 elif mode == "fir": mode_bits = 3 elif mode == "raw": mode_bits = 4 else: raise ValueError("request_mode: invalid request") await RisingEdge(self.dut.ft_clkout_i) self.dut.ft_rxf_n_i <= 0 self.dut.ft_data_io <= mode_bits await RisingEdge(self.dut.ft_clkout_i) self.dut.ft_data_io <= mode_bits await RisingEdge(self.dut.ft_clkout_i) self.dut.ft_data_io <= mode_bits await RisingEdge(self.dut.ft_clkout_i) await RisingEdge(self.dut.ft_clkout_i) await ReadOnly() while self.dut.ft_rd_n_o.value.integer: await RisingEdge(self.dut.ft_clkout_i) await ReadOnly() await RisingEdge(self.dut.ft_clkout_i) self.dut.ft_rxf_n_i <= 1 @cocotb.coroutine async def pc_request_data(self): """ Signal the PC is asking to read data from the FPGA. """ await RisingEdge(self.dut.ft_clkout_i) self.dut.ft_txe_n_i <= 0 await RisingEdge(self.dut.ft_clkout_i) @cocotb.coroutine # TODO add samples on falling edge for chan b async def gen_samples(self, inputs): sample_ctr = 0 num_samples = len(inputs) while True: if sample_ctr == num_samples: sample_ctr = 0 self.dut.adc_d_i <= BinaryValue( int(inputs[sample_ctr].item()), 12, binaryRepresentation=2) sample_ctr += 1 await RisingEdge(self.dut.clk_i)