def __init__(self, data, baud=9600, bits=8, stop_bits=1, *args, **kwargs): self.log = logging.getLogger(f"cocotb.{data._path}") self._data = data self._baud = baud self._bits = bits self._stop_bits = stop_bits self.log.info("UART source") self.log.info("cocotbext-uart version %s", __version__) self.log.info("Copyright (c) 2020 Alex Forencich") self.log.info("https://github.com/alexforencich/cocotbext-uart") super().__init__(*args, **kwargs) self.active = False self.queue = Queue() self._idle = Event() self._idle.set() self._data.setimmediatevalue(1) self.log.info("UART source configuration:") self.log.info(" Baud rate: %d bps", self._baud) self.log.info(" Byte size: %d bits", self._bits) self.log.info(" Stop bits: %f bits", self._stop_bits) self._run_cr = None self._restart()
async def test_analog_model(digital) -> None: """Exercise an Analog Front-end and its digital controller.""" clock = Clock(digital.clk, 1, units="us") # create a 1us period clock on port clk cocotb.start_soon(clock.start()) # start the clock afe_in_queue = Queue() afe_out_queue = Queue() afe = AFE(in_queue=afe_in_queue, out_queue=afe_out_queue) # instantiate the analog front-end cocotb.start_soon(gain_select(digital, afe)) for in_V in [0.1, 0.1, 0.0, 0.25, 0.25]: # set the input voltage await afe_in_queue.put(in_V) # get the converted digital value afe_out = await afe_out_queue.get() digital._log.info( f"AFE converted input value {in_V}V to {int(afe_out)}") # hand digital value over as "meas_val" to digital part (HDL) # "meas_val_valid" pulses for one clock cycle await RisingEdge(digital.clk) digital.meas_val.value = afe_out digital.meas_val_valid.value = 1 await RisingEdge(digital.clk) digital.meas_val_valid.value = 0 await Timer(3.3, "us")
def __init__(self, manager): self.current_tag = 0 self._cmd_queue = Queue() self._current_cmd = None self._resp_queue = Queue() self._cr = None self._manager = manager
async def test_fair_scheduling(dut): NUM_PUTTERS = 10 NUM_PUTS = 10 q = Queue(maxsize=1) async def putter(i): for _ in range(NUM_PUTS): await q.put(i) # fill queue to force contention q.put_nowait(None) # create NUM_PUTTER contending putters putters = [await cocotb.start(putter(i)) for i in range(NUM_PUTTERS)] # remove value that forced contention assert q.get_nowait() is None, "Popped unexpected value" # test fair scheduling by ensuring that each putter is serviced for its first # write before the second write on any putter is serviced. for _ in range(NUM_PUTS): remaining = set(range(NUM_PUTTERS)) for _ in range(NUM_PUTTERS): v = await q.get() assert v in remaining, "Unfair scheduling occurred" remaining.remove(v) assert all(not p for p in putters), "Not all putters finished?"
def __init__(self, bus, clock, reset=None, *args, **kwargs): self.bus = bus self.clock = clock self.reset = reset self.log = logging.getLogger(f"cocotb.{bus._entity._name}.{bus._name}") super().__init__(*args, **kwargs) self.active = False self.queue = Queue() self.dequeue_event = Event() self.idle_event = Event() self.idle_event.set() self.active_event = Event() self.pause = False self._pause_generator = None self._pause_cr = None self.queue_occupancy_bytes = 0 self.queue_occupancy_frames = 0 self.width = len(self.bus.tdata) self.byte_lanes = len(self.bus.tkeep) self.byte_size = self.width // self.byte_lanes self.byte_mask = 2**self.byte_size - 1 assert self.width in [64, 128, 256, 512] assert self.byte_size == 32
async def run_stress_test(dut, idle_inserter=None, backpressure_inserter=None): tb = TB(dut) byte_lanes = tb.axil_master.read_if.byte_lanes counter_size = max( int(os.getenv("PARAM_STAT_COUNT_WIDTH")) // 8, byte_lanes) stat_inc_width = len(dut.s_axis_stat_tdata) stat_id_width = len(dut.s_axis_stat_tid) await tb.cycle_reset() tb.set_idle_generator(idle_inserter) tb.set_backpressure_generator(backpressure_inserter) await Timer(4000, 'ns') async def worker(source, queue, count=128): for k in range(count): count = random.randrange(1, 2**stat_inc_width) num = random.randrange(0, 2**stat_id_width) await source.send(AxiStreamTransaction(tdata=count, tid=num)) await queue.put((num, count)) await Timer(random.randint(1, 1000), 'ns') workers = [] queue = Queue() for k in range(16): workers.append( cocotb.start_soon(worker(tb.stat_source, queue, count=128))) while workers: await workers.pop(0).join() await Timer(1000, 'ns') data_ref = [0] * 2**stat_id_width while not queue.empty(): num, count = await queue.get() data_ref[num] += count print(data_ref) data = await tb.axil_master.read_words(0, 2**stat_id_width, ws=counter_size) print(data) assert data == data_ref await RisingEdge(dut.clk) await RisingEdge(dut.clk)
def __init__(self, bus, clock, reset=None, reset_active_level=True, max_burst_len=256): self.log = logging.getLogger(f"cocotb.{bus.aw._entity._name}.{bus.aw._name}") self.log.info("AXI master (write)") self.log.info("cocotbext-axi version %s", __version__) self.log.info("Copyright (c) 2020 Alex Forencich") self.log.info("https://github.com/alexforencich/cocotbext-axi") self.aw_channel = AxiAWSource(bus.aw, clock, reset, reset_active_level) self.aw_channel.queue_occupancy_limit = 2 self.w_channel = AxiWSource(bus.w, clock, reset, reset_active_level) self.w_channel.queue_occupancy_limit = 2 self.b_channel = AxiBSink(bus.b, clock, reset, reset_active_level) self.b_channel.queue_occupancy_limit = 2 self.write_command_queue = Queue() self.current_write_command = None self.id_count = 2**len(self.aw_channel.bus.awid) self.cur_id = 0 self.active_id = Counter() self.int_write_resp_command_queue = [Queue() for k in range(self.id_count)] self.current_write_resp_command = [None for k in range(self.id_count)] self.int_write_resp_queue_list = [Queue() for k in range(self.id_count)] self.in_flight_operations = 0 self._idle = Event() self._idle.set() self.width = len(self.w_channel.bus.wdata) self.byte_size = 8 self.byte_width = self.width // self.byte_size self.strb_mask = 2**self.byte_width-1 self.max_burst_len = max(min(max_burst_len, 256), 1) self.max_burst_size = (self.byte_width-1).bit_length() self.log.info("AXI master configuration:") self.log.info(" Address width: %d bits", len(self.aw_channel.bus.awaddr)) self.log.info(" ID width: %d bits", len(self.aw_channel.bus.awid)) self.log.info(" Byte size: %d bits", self.byte_size) self.log.info(" Data width: %d bits (%d bytes)", self.width, self.byte_width) self.log.info(" Max burst size: %d (%d bytes)", self.max_burst_size, 2**self.max_burst_size) self.log.info(" Max burst length: %d cycles (%d bytes)", self.max_burst_len, self.max_burst_len*self.byte_width) assert self.byte_width == len(self.w_channel.bus.wstrb) assert self.byte_width * self.byte_size == self.width assert len(self.b_channel.bus.bid) == len(self.aw_channel.bus.awid) self._process_write_cr = None self._process_write_resp_cr = None self._process_write_resp_id_cr = None self._init_reset(reset, reset_active_level)
def __init__(self, bus, clock, reset=None, reset_active_level=True): self.bus = bus self.clock = clock self.reset = reset self.log = logging.getLogger( f"cocotb.{bus.ar._entity._name}.{bus.ar._name}") self.log.info("AXI lite master (read)") self.log.info("cocotbext-axi version %s", __version__) self.log.info("Copyright (c) 2020 Alex Forencich") self.log.info("https://github.com/alexforencich/cocotbext-axi") self.ar_channel = AxiLiteARSource(bus.ar, clock, reset, reset_active_level) self.ar_channel.queue_occupancy_limit = 2 self.r_channel = AxiLiteRSink(bus.r, clock, reset, reset_active_level) self.r_channel.queue_occupancy_limit = 2 self.read_command_queue = Queue() self.current_read_command = None self.int_read_resp_command_queue = Queue() self.current_read_resp_command = None self.in_flight_operations = 0 self._idle = Event() self._idle.set() self.width = len(self.r_channel.bus.rdata) self.byte_size = 8 self.byte_lanes = self.width // self.byte_size self.arprot_present = hasattr(self.bus.ar, "arprot") self.log.info("AXI lite master configuration:") self.log.info(" Address width: %d bits", len(self.ar_channel.bus.araddr)) self.log.info(" Byte size: %d bits", self.byte_size) self.log.info(" Data width: %d bits (%d bytes)", self.width, self.byte_lanes) self.log.info("AXI lite master signals:") for bus in (self.bus.ar, self.bus.r): for sig in sorted( list(set().union(bus._signals, bus._optional_signals))): if hasattr(bus, sig): self.log.info(" %s width: %d bits", sig, len(getattr(bus, sig))) else: self.log.info(" %s: not present", sig) assert self.byte_lanes * self.byte_size == self.width self._process_read_cr = None self._process_read_resp_cr = None self._init_reset(reset, reset_active_level)
def __init__(self, data, er, dv, clock, reset=None, enable=None, reset_active_level=True, *args, **kwargs): self.log = logging.getLogger(f"cocotb.{data._path}") self.data = data self.er = er self.dv = dv self.clock = clock self.reset = reset self.enable = enable self.log.info("MII source") self.log.info("cocotbext-eth version %s", __version__) self.log.info("Copyright (c) 2020 Alex Forencich") self.log.info("https://github.com/alexforencich/cocotbext-eth") super().__init__(*args, **kwargs) self.active = False self.queue = Queue() self.dequeue_event = Event() self.current_frame = None self.idle_event = Event() self.idle_event.set() self.ifg = 12 self.queue_occupancy_bytes = 0 self.queue_occupancy_frames = 0 self.queue_occupancy_limit_bytes = -1 self.queue_occupancy_limit_frames = -1 self.width = 4 self.byte_width = 1 assert len(self.data) == 4 self.data.setimmediatevalue(0) if self.er is not None: assert len(self.er) == 1 self.er.setimmediatevalue(0) assert len(self.dv) == 1 self.dv.setimmediatevalue(0) self._run_cr = None self._init_reset(reset, reset_active_level)
def add_switch_port(self, port): self.switch_ports.append(port) cocotb.start_soon(self._run_routing(port)) cocotb.start_soon(self._run_arbitration(port)) for k in range(len(self.switch_ports)-1): tx_queue = Queue() rx_queue = Queue() port.tx_queues.append((self.switch_ports[k], tx_queue)) port.rx_queues.append(rx_queue) self.switch_ports[k].rx_queues.append(tx_queue) self.switch_ports[k].tx_queues.append((port, rx_queue))
def __init__(self, bus, clock, reset=None, reset_active_level=True, *args, **kwargs): self.bus = bus self.clock = clock self.reset = reset self.log = logging.getLogger(f"cocotb.{bus._entity._name}.{bus._name}") super().__init__(*args, **kwargs) self.active = False self.queue = Queue() self.dequeue_event = Event() self.idle_event = Event() self.idle_event.set() self.active_event = Event() self.ready = None self.valid = None if self._ready_signal is not None and hasattr(self.bus, self._ready_signal): self.ready = getattr(self.bus, self._ready_signal) if self._ready_init is not None: self.ready.setimmediatevalue(self._ready_init) if self._valid_signal is not None and hasattr(self.bus, self._valid_signal): self.valid = getattr(self.bus, self._valid_signal) if self._valid_init is not None: self.valid.setimmediatevalue(self._valid_init) for sig in self._signals + self._optional_signals: if hasattr(self.bus, sig): if sig in self._signal_widths: assert len(getattr(self.bus, sig)) == self._signal_widths[sig] if self._init_x and sig not in (self._valid_signal, self._ready_signal): v = getattr(self.bus, sig).value v.binstr = 'x' * len(v) getattr(self.bus, sig).setimmediatevalue(v) self._run_cr = None self._init_reset(reset, reset_active_level)
def __init__(self, data, ctrl, clock, reset=None, enable=None, reset_active_level=True, *args, **kwargs): self.log = logging.getLogger(f"cocotb.{data._path}") self.data = data self.ctrl = ctrl self.clock = clock self.reset = reset self.enable = enable self.log.info("XGMII sink") self.log.info("cocotbext-eth version %s", __version__) self.log.info("Copyright (c) 2020 Alex Forencich") self.log.info("https://github.com/alexforencich/cocotbext-eth") super().__init__(*args, **kwargs) self.active = False self.queue = Queue() self.active_event = Event() self.queue_occupancy_bytes = 0 self.queue_occupancy_frames = 0 self.width = len(self.data) self.byte_size = 8 self.byte_lanes = len(self.ctrl) assert self.width == self.byte_lanes * self.byte_size self.log.info("XGMII sink model configuration") self.log.info(" Byte size: %d bits", self.byte_size) self.log.info(" Data width: %d bits (%d bytes)", self.width, self.byte_lanes) self._run_cr = None self._init_reset(reset, reset_active_level)
def __init__(self, data, header, clock, enable=None, scramble=True, reverse=False, *args, **kwargs): self.log = logging.getLogger(f"cocotb.{data._path}") self.data = data self.header = header self.clock = clock self.enable = enable self.scramble = scramble self.reverse = reverse self.log.info("BASE-R serdes sink") self.log.info("Copyright (c) 2021 Alex Forencich") self.log.info("https://github.com/alexforencich/verilog-ethernet") super().__init__(*args, **kwargs) self.active = False self.queue = Queue() self.active_event = Event() self.queue_occupancy_bytes = 0 self.queue_occupancy_frames = 0 self.width = len(self.data) self.byte_size = 8 self.byte_lanes = 8 assert self.width == self.byte_lanes * self.byte_size self.log.info("BASE-R serdes sink model configuration") self.log.info(" Byte size: %d bits", self.byte_size) self.log.info(" Data width: %d bits (%d bytes)", self.width, self.byte_lanes) self.log.info(" Enable scrambler: %s", self.scramble) self.log.info(" Bit reverse: %s", self.reverse) self._run_cr = cocotb.fork(self._run())
def __init__(self, in_queue: Optional[Queue] = None, out_queue: Optional[Queue] = None) -> None: self.in_queue = in_queue self.out_queue = out_queue self.pga_to_adc_queue = Queue() self.pga = PGA(in_queue=self.in_queue, out_queue=self.pga_to_adc_queue) self.adc = ADC(in_queue=self.pga_to_adc_queue, out_queue=self.out_queue)
def __init__(self, bus, clock, reset=None, reset_active_level=True): self.log = logging.getLogger(f"cocotb.{bus.aw._entity._name}.{bus.aw._name}") self.log.info("AXI lite master (write)") self.log.info("cocotbext-axi version %s", __version__) self.log.info("Copyright (c) 2020 Alex Forencich") self.log.info("https://github.com/alexforencich/cocotbext-axi") self.aw_channel = AxiLiteAWSource(bus.aw, clock, reset, reset_active_level) self.aw_channel.queue_occupancy_limit = 2 self.w_channel = AxiLiteWSource(bus.w, clock, reset, reset_active_level) self.w_channel.queue_occupancy_limit = 2 self.b_channel = AxiLiteBSink(bus.b, clock, reset, reset_active_level) self.b_channel.queue_occupancy_limit = 2 self.write_command_queue = Queue() self.current_write_command = None self.int_write_resp_command_queue = Queue() self.current_write_resp_command = None self.in_flight_operations = 0 self._idle = Event() self._idle.set() self.width = len(self.w_channel.bus.wdata) self.byte_size = 8 self.byte_width = self.width // self.byte_size self.strb_mask = 2**self.byte_width-1 self.log.info("AXI lite master configuration:") self.log.info(" Address width: %d bits", len(self.aw_channel.bus.awaddr)) self.log.info(" Byte size: %d bits", self.byte_size) self.log.info(" Data width: %d bits (%d bytes)", self.width, self.byte_width) assert self.byte_width == len(self.w_channel.bus.wstrb) assert self.byte_width * self.byte_size == self.width self._process_write_cr = None self._process_write_resp_cr = None self._init_reset(reset, reset_active_level)
def __init__(self, bridge): self.bridge = bridge self.upstream = False self.ingress_queue = Queue(1) self.tx_queues = [] self.rx_queues = [] self.rx_event = Event() self.tx_handler = None
def __init__(self, fc_init=[[0]*6]*8, *args, **kwargs): self.log = logging.getLogger(f"cocotb.pcie.{type(self).__name__}.{id(self)}") self.log.name = f"cocotb.pcie.{type(self).__name__}" self.parent = None self.rx_handler = None self.max_link_speed = None self.max_link_width = None self.tx_queue = Queue(1) self.tx_queue_sync = Event() self.rx_queue = Queue() self.cur_link_speed = None self.cur_link_width = None self.time_scale = get_sim_steps(1, 'sec') # ACK/NAK protocol # TX self.next_transmit_seq = 0x000 self.ackd_seq = 0xfff self.retry_buffer = Queue() # RX self.next_recv_seq = 0x000 self.nak_scheduled = False self.ack_nak_latency_timer = 0 self.max_payload_size = 128 self.max_latency_timer = 0 self.send_ack = Event() self._ack_latency_timer_cr = None # Flow control self.send_fc = Event() self.fc_state = [FcChannelState(fc_init[k], self.start_fc_update_timer) for k in range(8)] self.fc_initialized = False self.fc_init_vc = 0 self.fc_init_type = FcType.P self.fc_idle_timer_steps = get_sim_steps(10, 'us') self.fc_update_steps = get_sim_steps(30, 'us') self._fc_update_timer_cr = None super().__init__(*args, **kwargs) # VC0 is always active self.fc_state[0].active = True cocotb.fork(self._run_transmit()) cocotb.fork(self._run_receive()) cocotb.fork(self._run_fc_update_idle_timer())
def __init__(self, data, er, dv, clock, reset=None, enable=None, mii_select=None, reset_active_level=True, *args, **kwargs): self.log = logging.getLogger(f"cocotb.{data._path}") self.data = data self.er = er self.dv = dv self.clock = clock self.reset = reset self.enable = enable self.mii_select = mii_select self.log.info("GMII sink") self.log.info("cocotbext-eth version %s", __version__) self.log.info("Copyright (c) 2020 Alex Forencich") self.log.info("https://github.com/alexforencich/cocotbext-eth") super().__init__(*args, **kwargs) self.active = False self.queue = Queue() self.active_event = Event() self.mii_mode = False self.queue_occupancy_bytes = 0 self.queue_occupancy_frames = 0 self.width = 8 self.byte_width = 1 assert len(self.data) == 8 if self.er is not None: assert len(self.er) == 1 if self.dv is not None: assert len(self.dv) == 1 self._run_cr = None self._init_reset(reset, reset_active_level)
class TagContext: def __init__(self, manager): self.current_tag = 0 self._cmd_queue = Queue() self._current_cmd = None self._resp_queue = Queue() self._cr = None self._manager = manager async def get_resp(self): return await self._resp_queue.get() def get_resp_nowait(self): return self._resp_queue.get_nowait() def _start(self): if self._cr is None: self._cr = cocotb.start_soon(self._process_queue()) def _flush(self): flushed_cmds = [] if self._cr is not None: self._cr.kill() self._cr = None self._manager._set_idle(self) if self._current_cmd is not None: flushed_cmds.append(self._current_cmd) self._current_cmd = None while not self._cmd_queue.empty(): flushed_cmds.append(self._cmd_queue.get_nowait()) while not self._resp_queue.empty(): self._resp_queue.get_nowait() return flushed_cmds async def _process_queue(self): while True: cmd = await self._cmd_queue.get() self._current_cmd = cmd await self._manager._process(self, cmd) self._current_cmd = None if self._cmd_queue.empty() and self._resp_queue.empty(): self._manager._set_idle(self)
class UsPcieBase: _signal_widths = {"tvalid": 1, "tready": 1} _valid_signal = "tvalid" _ready_signal = "tready" _transaction_obj = UsPcieTransaction _frame_obj = UsPcieFrame def __init__(self, bus, clock, reset=None, *args, **kwargs): self.bus = bus self.clock = clock self.reset = reset self.log = logging.getLogger(f"cocotb.{bus._entity._name}.{bus._name}") super().__init__(*args, **kwargs) self.active = False self.queue = Queue() self.dequeue_event = Event() self.idle_event = Event() self.idle_event.set() self.active_event = Event() self.pause = False self._pause_generator = None self._pause_cr = None self.queue_occupancy_bytes = 0 self.queue_occupancy_frames = 0 self.width = len(self.bus.tdata) self.byte_lanes = len(self.bus.tkeep) self.byte_size = self.width // self.byte_lanes self.byte_mask = 2**self.byte_size - 1 assert self.width in [64, 128, 256, 512] assert self.byte_size == 32 def _init(self): pass def count(self): return self.queue.qsize() def empty(self): return self.queue.empty() def clear(self): while not self.queue.empty(): self.queue.get_nowait() self.idle_event.set() self.active_event.clear() def idle(self): raise NotImplementedError() async def wait(self): raise NotImplementedError() def set_pause_generator(self, generator=None): if self._pause_cr is not None: self._pause_cr.kill() self._pause_cr = None self._pause_generator = generator if self._pause_generator is not None: self._pause_cr = cocotb.fork(self._run_pause()) def clear_pause_generator(self): self.set_pause_generator(None) async def _run_pause(self): for val in self._pause_generator: self.pause = val await RisingEdge(self.clock)
class StreamBase(Reset): _signals = ["data", "valid", "ready"] _optional_signals = [] _signal_widths = {"valid": 1, "ready": 1} _init_x = False _valid_signal = "valid" _valid_init = None _ready_signal = "ready" _ready_init = None _transaction_obj = StreamTransaction _bus_obj = StreamBus def __init__(self, bus, clock, reset=None, reset_active_level=True, *args, **kwargs): self.bus = bus self.clock = clock self.reset = reset self.log = logging.getLogger(f"cocotb.{bus._entity._name}.{bus._name}") super().__init__(*args, **kwargs) self.active = False self.queue = Queue() self.dequeue_event = Event() self.idle_event = Event() self.idle_event.set() self.active_event = Event() self.ready = None self.valid = None if self._ready_signal is not None and hasattr(self.bus, self._ready_signal): self.ready = getattr(self.bus, self._ready_signal) if self._ready_init is not None: self.ready.setimmediatevalue(self._ready_init) if self._valid_signal is not None and hasattr(self.bus, self._valid_signal): self.valid = getattr(self.bus, self._valid_signal) if self._valid_init is not None: self.valid.setimmediatevalue(self._valid_init) for sig in self._signals + self._optional_signals: if hasattr(self.bus, sig): if sig in self._signal_widths: assert len(getattr(self.bus, sig)) == self._signal_widths[sig] if self._init_x and sig not in (self._valid_signal, self._ready_signal): v = getattr(self.bus, sig).value v.binstr = 'x' * len(v) getattr(self.bus, sig).setimmediatevalue(v) self._run_cr = None self._init_reset(reset, reset_active_level) def count(self): return self.queue.qsize() def empty(self): return self.queue.empty() def clear(self): while not self.queue.empty(): self.queue.get_nowait() self.dequeue_event.set() self.idle_event.set() self.active_event.clear() def _handle_reset(self, state): if state: self.log.info("Reset asserted") if self._run_cr is not None: self._run_cr.kill() self._run_cr = None self.active = False if self.queue.empty(): self.idle_event.set() else: self.log.info("Reset de-asserted") if self._run_cr is None: self._run_cr = cocotb.start_soon(self._run()) async def _run(self): raise NotImplementedError()
def __init__(self, bus, clock, reset=None, reset_active_level=True, byte_size=None, byte_lanes=None, *args, **kwargs): self.bus = bus self.clock = clock self.reset = reset self.log = logging.getLogger(f"cocotb.{bus._entity._name}.{bus._name}") self.log.info("AXI stream %s", self._type) self.log.info("cocotbext-axi version %s", __version__) self.log.info("Copyright (c) 2020 Alex Forencich") self.log.info("https://github.com/alexforencich/cocotbext-axi") super().__init__(*args, **kwargs) self.active = False self.queue = Queue() self.dequeue_event = Event() self.current_frame = None self.idle_event = Event() self.idle_event.set() self.active_event = Event() self.queue_occupancy_bytes = 0 self.queue_occupancy_frames = 0 self.width = len(self.bus.tdata) self.byte_lanes = 1 if self._valid_init is not None and hasattr(self.bus, "tvalid"): self.bus.tvalid.setimmediatevalue(self._valid_init) if self._ready_init is not None and hasattr(self.bus, "tready"): self.bus.tready.setimmediatevalue(self._ready_init) for sig in self._signals + self._optional_signals: if hasattr(self.bus, sig): if self._init_x and sig not in ("tvalid", "tready"): v = getattr(self.bus, sig).value v.binstr = 'x' * len(v) getattr(self.bus, sig).setimmediatevalue(v) if hasattr(self.bus, "tkeep"): self.byte_lanes = len(self.bus.tkeep) if byte_size is not None or byte_lanes is not None: raise ValueError( "Cannot specify byte_size or byte_lanes if tkeep is connected" ) else: if byte_lanes is not None: self.byte_lanes = byte_lanes if byte_size is not None: raise ValueError( "Cannot specify both byte_size and byte_lanes") elif byte_size is not None: self.byte_lanes = self.width // byte_size self.byte_size = self.width // self.byte_lanes self.byte_mask = 2**self.byte_size - 1 self.log.info("AXI stream %s configuration:", self._type) self.log.info(" Byte size: %d bits", self.byte_size) self.log.info(" Data width: %d bits (%d bytes)", self.width, self.byte_lanes) self.log.info( " tvalid: %s", "present" if hasattr(self.bus, "tvalid") else "not present") self.log.info( " tready: %s", "present" if hasattr(self.bus, "tready") else "not present") self.log.info( " tlast: %s", "present" if hasattr(self.bus, "tlast") else "not present") if hasattr(self.bus, "tkeep"): self.log.info(" tkeep width: %d bits", len(self.bus.tkeep)) else: self.log.info(" tkeep: not present") if hasattr(self.bus, "tid"): self.log.info(" tid width: %d bits", len(self.bus.tid)) else: self.log.info(" tid: not present") if hasattr(self.bus, "tdest"): self.log.info(" tdest width: %d bits", len(self.bus.tdest)) else: self.log.info(" tdest: not present") if hasattr(self.bus, "tuser"): self.log.info(" tuser width: %d bits", len(self.bus.tuser)) else: self.log.info(" tuser: not present") if self.byte_lanes * self.byte_size != self.width: raise ValueError( f"Bus does not evenly divide into byte lanes " f"({self.byte_lanes} * {self.byte_size} != {self.width})") self._run_cr = None self._init_reset(reset, reset_active_level)
def __init__(self, bus, clock, reset=None, ready_latency=0, *args, **kwargs): self.bus = bus self.clock = clock self.reset = reset self.ready_latency = ready_latency self.log = logging.getLogger(f"cocotb.{bus._entity._name}.{bus._name}") super().__init__(*args, **kwargs) self.active = False self.queue = Queue() self.dequeue_event = Event() self.idle_event = Event() self.idle_event.set() self.active_event = Event() self.pause = False self._pause_generator = None self._pause_cr = None self.queue_occupancy_bytes = 0 self.queue_occupancy_frames = 0 self.width = len(self.bus.data) self.byte_size = 32 self.byte_lanes = self.width // self.byte_size self.byte_mask = 2**self.byte_size - 1 self.seg_count = len(self.bus.valid) self.seg_width = self.width // self.seg_count self.seg_mask = 2**self.seg_width - 1 self.seg_par_width = self.seg_width // 8 self.seg_par_mask = 2**self.seg_par_width - 1 self.seg_byte_lanes = self.byte_lanes // self.seg_count self.seg_empty_width = (self.seg_byte_lanes - 1).bit_length() self.seg_empty_mask = 2**self.seg_empty_width - 1 assert self.width in {256, 512} assert len(self.bus.data) == self.seg_count * self.seg_width assert len(self.bus.sop) == self.seg_count assert len(self.bus.eop) == self.seg_count assert len(self.bus.valid) == self.seg_count if hasattr(self.bus, "empty"): assert len(self.bus.empty) == self.seg_count * self.seg_empty_width if hasattr(self.bus, "err"): assert len(self.bus.err) == self.seg_count if hasattr(self.bus, "bar_range"): assert len(self.bus.bar_range) == self.seg_count * 3 if hasattr(self.bus, "vf_active"): assert len(self.bus.vf_active) == self.seg_count if hasattr(self.bus, "func_num"): assert len(self.bus.func_num) == self.seg_count * 2 if hasattr(self.bus, "vf_num"): assert len(self.bus.vf_num) == self.seg_count * 11 if hasattr(self.bus, "parity"): assert len(self.bus.parity) == self.seg_count * self.seg_width // 8
class S10PcieBase: _signal_widths = {"ready": 1} _valid_signal = "valid" _ready_signal = "ready" _transaction_obj = S10PcieTransaction _frame_obj = S10PcieFrame def __init__(self, bus, clock, reset=None, ready_latency=0, *args, **kwargs): self.bus = bus self.clock = clock self.reset = reset self.ready_latency = ready_latency self.log = logging.getLogger(f"cocotb.{bus._entity._name}.{bus._name}") super().__init__(*args, **kwargs) self.active = False self.queue = Queue() self.dequeue_event = Event() self.idle_event = Event() self.idle_event.set() self.active_event = Event() self.pause = False self._pause_generator = None self._pause_cr = None self.queue_occupancy_bytes = 0 self.queue_occupancy_frames = 0 self.width = len(self.bus.data) self.byte_size = 32 self.byte_lanes = self.width // self.byte_size self.byte_mask = 2**self.byte_size - 1 self.seg_count = len(self.bus.valid) self.seg_width = self.width // self.seg_count self.seg_mask = 2**self.seg_width - 1 self.seg_par_width = self.seg_width // 8 self.seg_par_mask = 2**self.seg_par_width - 1 self.seg_byte_lanes = self.byte_lanes // self.seg_count self.seg_empty_width = (self.seg_byte_lanes - 1).bit_length() self.seg_empty_mask = 2**self.seg_empty_width - 1 assert self.width in {256, 512} assert len(self.bus.data) == self.seg_count * self.seg_width assert len(self.bus.sop) == self.seg_count assert len(self.bus.eop) == self.seg_count assert len(self.bus.valid) == self.seg_count if hasattr(self.bus, "empty"): assert len(self.bus.empty) == self.seg_count * self.seg_empty_width if hasattr(self.bus, "err"): assert len(self.bus.err) == self.seg_count if hasattr(self.bus, "bar_range"): assert len(self.bus.bar_range) == self.seg_count * 3 if hasattr(self.bus, "vf_active"): assert len(self.bus.vf_active) == self.seg_count if hasattr(self.bus, "func_num"): assert len(self.bus.func_num) == self.seg_count * 2 if hasattr(self.bus, "vf_num"): assert len(self.bus.vf_num) == self.seg_count * 11 if hasattr(self.bus, "parity"): assert len(self.bus.parity) == self.seg_count * self.seg_width // 8 def count(self): return self.queue.qsize() def empty(self): return self.queue.empty() def clear(self): while not self.queue.empty(): self.queue.get_nowait() self.idle_event.set() self.active_event.clear() def idle(self): raise NotImplementedError() async def wait(self): raise NotImplementedError() def set_pause_generator(self, generator=None): if self._pause_cr is not None: self._pause_cr.kill() self._pause_cr = None self._pause_generator = generator if self._pause_generator is not None: self._pause_cr = cocotb.start_soon(self._run_pause()) def clear_pause_generator(self): self.set_pause_generator(None) async def _run_pause(self): clock_edge_event = RisingEdge(self.clock) for val in self._pause_generator: self.pause = val await clock_edge_event
class UartSource: def __init__(self, data, baud=9600, bits=8, stop_bits=1, *args, **kwargs): self.log = logging.getLogger(f"cocotb.{data._path}") self._data = data self._baud = baud self._bits = bits self._stop_bits = stop_bits self.log.info("UART source") self.log.info("cocotbext-uart version %s", __version__) self.log.info("Copyright (c) 2020 Alex Forencich") self.log.info("https://github.com/alexforencich/cocotbext-uart") super().__init__(*args, **kwargs) self.active = False self.queue = Queue() self._idle = Event() self._idle.set() self._data.setimmediatevalue(1) self.log.info("UART source configuration:") self.log.info(" Baud rate: %d bps", self._baud) self.log.info(" Byte size: %d bits", self._bits) self.log.info(" Stop bits: %f bits", self._stop_bits) self._run_cr = None self._restart() def _restart(self): if self._run_cr is not None: self._run_cr.kill() self._run_cr = cocotb.fork( self._run(self._data, self._baud, self._bits, self._stop_bits)) @property def baud(self): return self._baud @baud.setter def baud(self, value): self.baud = value self._restart() @property def bits(self): return self._bits @bits.setter def bits(self, value): self.bits = value self._restart() @property def stop_bits(self): return self._stop_bits @stop_bits.setter def stop_bits(self, value): self.stop_bits = value self._restart() async def write(self, data): for b in data: await self.queue.put(int(b)) self._idle.clear() def write_nowait(self, data): for b in data: self.queue.put_nowait(int(b)) self._idle.clear() def count(self): return self.queue.qsize() def empty(self): return self.queue.empty() def idle(self): return self.empty() and not self.active def clear(self): while not self.queue.empty(): frame = self.queue.get_nowait() async def wait(self): await self._idle.wait() async def _run(self, data, baud, bits, stop_bits): self.active = False bit_t = Timer(int(1e9 / self.baud), 'ns') stop_bit_t = Timer(int(1e9 / self.baud * stop_bits), 'ns') while True: if self.empty(): self.active = False self._idle.set() b = await self.queue.get() self.active = True self.log.info("Write byte 0x%02x", b) # start bit data.value = 0 await bit_t # data bits for k in range(self.bits): data.value = b & 1 b >>= 1 await bit_t # stop bit data.value = 1 await stop_bit_t
class TinyAluBfm(metaclass=utility_classes.Singleton): def __init__(self): self.dut = cocotb.top self.driver_queue = Queue(maxsize=1) self.cmd_mon_queue = Queue(maxsize=0) self.result_mon_queue = Queue(maxsize=0) async def send_op(self, aa, bb, op): command_tuple = (aa, bb, op) await self.driver_queue.put(command_tuple) async def get_cmd(self): cmd = await self.cmd_mon_queue.get() return cmd async def get_result(self): result = await self.result_mon_queue.get() return result async def reset(self): await FallingEdge(self.dut.clk) self.dut.reset_n.value = 0 self.dut.A.value = 0 self.dut.B.value = 0 self.dut.op.value = 0 await FallingEdge(self.dut.clk) self.dut.reset_n.value = 1 await FallingEdge(self.dut.clk) async def driver_bfm(self): self.dut.start.value = 0 self.dut.A.value = 0 self.dut.B.value = 0 self.dut.op.value = 0 while True: await FallingEdge(self.dut.clk) start = get_int(self.dut.start) done = get_int(self.dut.done) if start == 0 and done == 0: try: (aa, bb, op) = self.driver_queue.get_nowait() self.dut.A.value = aa self.dut.B.value = bb self.dut.op.value = op self.dut.start.value = 1 except QueueEmpty: pass elif start == 1: if done == 1: self.dut.start.value = 0 async def cmd_mon_bfm(self): prev_start = 0 while True: await FallingEdge(self.dut.clk) start = get_int(self.dut.start) if start == 1 and prev_start == 0: cmd_tuple = (get_int(self.dut.A), get_int(self.dut.B), get_int(self.dut.op)) self.cmd_mon_queue.put_nowait(cmd_tuple) prev_start = start async def result_mon_bfm(self): prev_done = 0 while True: await FallingEdge(self.dut.clk) done = get_int(self.dut.done) if prev_done == 0 and done == 1: result = get_int(self.dut.result) self.result_mon_queue.put_nowait(result) prev_done = done def start_bfm(self): cocotb.start_soon(self.driver_bfm()) cocotb.start_soon(self.cmd_mon_bfm()) cocotb.start_soon(self.result_mon_bfm())
def __init__(self): self.dut = cocotb.top self.driver_queue = Queue(maxsize=1) self.cmd_mon_queue = Queue(maxsize=0) self.result_mon_queue = Queue(maxsize=0)
class MiiSource(Reset): def __init__(self, data, er, dv, clock, reset=None, enable=None, reset_active_level=True, *args, **kwargs): self.log = logging.getLogger(f"cocotb.{data._path}") self.data = data self.er = er self.dv = dv self.clock = clock self.reset = reset self.enable = enable self.log.info("MII source") self.log.info("cocotbext-eth version %s", __version__) self.log.info("Copyright (c) 2020 Alex Forencich") self.log.info("https://github.com/alexforencich/cocotbext-eth") super().__init__(*args, **kwargs) self.active = False self.queue = Queue() self.dequeue_event = Event() self.current_frame = None self.idle_event = Event() self.idle_event.set() self.ifg = 12 self.queue_occupancy_bytes = 0 self.queue_occupancy_frames = 0 self.queue_occupancy_limit_bytes = -1 self.queue_occupancy_limit_frames = -1 self.width = 4 self.byte_width = 1 assert len(self.data) == 4 self.data.setimmediatevalue(0) if self.er is not None: assert len(self.er) == 1 self.er.setimmediatevalue(0) assert len(self.dv) == 1 self.dv.setimmediatevalue(0) self._run_cr = None self._init_reset(reset, reset_active_level) async def send(self, frame): while self.full(): self.dequeue_event.clear() await self.dequeue_event.wait() frame = GmiiFrame(frame) await self.queue.put(frame) self.idle_event.clear() self.queue_occupancy_bytes += len(frame) self.queue_occupancy_frames += 1 def send_nowait(self, frame): if self.full(): raise QueueFull() frame = GmiiFrame(frame) self.queue.put_nowait(frame) self.idle_event.clear() self.queue_occupancy_bytes += len(frame) self.queue_occupancy_frames += 1 def count(self): return self.queue.qsize() def empty(self): return self.queue.empty() def full(self): if self.queue_occupancy_limit_bytes > 0 and self.queue_occupancy_bytes > self.queue_occupancy_limit_bytes: return True elif self.queue_occupancy_limit_frames > 0 and self.queue_occupancy_frames > self.queue_occupancy_limit_frames: return True else: return False def idle(self): return self.empty() and not self.active def clear(self): while not self.queue.empty(): frame = self.queue.get_nowait() frame.sim_time_end = None frame.handle_tx_complete() self.dequeue_event.set() self.idle_event.set() self.queue_occupancy_bytes = 0 self.queue_occupancy_frames = 0 async def wait(self): await self.idle_event.wait() def _handle_reset(self, state): if state: self.log.info("Reset asserted") if self._run_cr is not None: self._run_cr.kill() self._run_cr = None self.active = False self.data.value = 0 if self.er is not None: self.er.value = 0 self.dv.value = 0 if self.current_frame: self.log.warning("Flushed transmit frame during reset: %s", self.current_frame) self.current_frame.handle_tx_complete() self.current_frame = None if self.queue.empty(): self.idle_event.set() else: self.log.info("Reset de-asserted") if self._run_cr is None: self._run_cr = cocotb.start_soon(self._run()) async def _run(self): frame = None frame_offset = 0 frame_data = None frame_error = None ifg_cnt = 0 self.active = False clock_edge_event = RisingEdge(self.clock) while True: await clock_edge_event if self.enable is None or self.enable.value: if ifg_cnt > 0: # in IFG ifg_cnt -= 1 elif frame is None and not self.queue.empty(): # send frame frame = self.queue.get_nowait() self.dequeue_event.set() self.queue_occupancy_bytes -= len(frame) self.queue_occupancy_frames -= 1 self.current_frame = frame frame.sim_time_start = get_sim_time() frame.sim_time_sfd = None frame.sim_time_end = None self.log.info("TX frame: %s", frame) frame.normalize() # convert to MII frame_data = [] frame_error = [] for b, e in zip(frame.data, frame.error): frame_data.append(b & 0x0F) frame_data.append(b >> 4) frame_error.append(e) frame_error.append(e) self.active = True frame_offset = 0 if frame is not None: d = frame_data[frame_offset] if frame.sim_time_sfd is None and d == 0xD: frame.sim_time_sfd = get_sim_time() self.data.value = d if self.er is not None: self.er.value = frame_error[frame_offset] self.dv.value = 1 frame_offset += 1 if frame_offset >= len(frame_data): ifg_cnt = max(self.ifg, 1) frame.sim_time_end = get_sim_time() frame.handle_tx_complete() frame = None self.current_frame = None else: self.data.value = 0 if self.er is not None: self.er.value = 0 self.dv.value = 0 self.active = False self.idle_event.set()
class MiiSink(Reset): def __init__(self, data, er, dv, clock, reset=None, enable=None, reset_active_level=True, *args, **kwargs): self.log = logging.getLogger(f"cocotb.{data._path}") self.data = data self.er = er self.dv = dv self.clock = clock self.reset = reset self.enable = enable self.log.info("MII sink") self.log.info("cocotbext-eth version %s", __version__) self.log.info("Copyright (c) 2020 Alex Forencich") self.log.info("https://github.com/alexforencich/cocotbext-eth") super().__init__(*args, **kwargs) self.active = False self.queue = Queue() self.active_event = Event() self.queue_occupancy_bytes = 0 self.queue_occupancy_frames = 0 self.width = 4 self.byte_width = 1 assert len(self.data) == 4 if self.er is not None: assert len(self.er) == 1 if self.dv is not None: assert len(self.dv) == 1 self._run_cr = None self._init_reset(reset, reset_active_level) def _recv(self, frame, compact=True): if self.queue.empty(): self.active_event.clear() self.queue_occupancy_bytes -= len(frame) self.queue_occupancy_frames -= 1 if compact: frame.compact() return frame async def recv(self, compact=True): frame = await self.queue.get() return self._recv(frame, compact) def recv_nowait(self, compact=True): frame = self.queue.get_nowait() return self._recv(frame, compact) def count(self): return self.queue.qsize() def empty(self): return self.queue.empty() def idle(self): return not self.active def clear(self): while not self.queue.empty(): self.queue.get_nowait() self.active_event.clear() self.queue_occupancy_bytes = 0 self.queue_occupancy_frames = 0 async def wait(self, timeout=0, timeout_unit=None): if not self.empty(): return if timeout: await First(self.active_event.wait(), Timer(timeout, timeout_unit)) else: await self.active_event.wait() def _handle_reset(self, state): if state: self.log.info("Reset asserted") if self._run_cr is not None: self._run_cr.kill() self._run_cr = None self.active = False else: self.log.info("Reset de-asserted") if self._run_cr is None: self._run_cr = cocotb.start_soon(self._run()) async def _run(self): frame = None self.active = False clock_edge_event = RisingEdge(self.clock) while True: await clock_edge_event if self.enable is None or self.enable.value: d_val = self.data.value.integer dv_val = self.dv.value.integer er_val = 0 if self.er is None else self.er.value.integer if frame is None: if dv_val: # start of frame frame = GmiiFrame(bytearray(), []) frame.sim_time_start = get_sim_time() else: if not dv_val: # end of frame odd = True sync = False b = 0 be = 0 data = bytearray() error = [] for n, e in zip(frame.data, frame.error): odd = not odd b = (n & 0x0F) << 4 | b >> 4 be |= e if not sync and b == EthPre.SFD: odd = True sync = True if odd: data.append(b) error.append(be) be = 0 frame.data = data frame.error = error frame.compact() frame.sim_time_end = get_sim_time() self.log.info("RX frame: %s", frame) self.queue_occupancy_bytes += len(frame) self.queue_occupancy_frames += 1 self.queue.put_nowait(frame) self.active_event.set() frame = None if frame is not None: if frame.sim_time_sfd is None and d_val == 0xD: frame.sim_time_sfd = get_sim_time() frame.data.append(d_val) frame.error.append(er_val)
class Port: """Base port""" def __init__(self, fc_init=[[0]*6]*8, *args, **kwargs): self.log = logging.getLogger(f"cocotb.pcie.{type(self).__name__}.{id(self)}") self.log.name = f"cocotb.pcie.{type(self).__name__}" self.parent = None self.rx_handler = None self.max_link_speed = None self.max_link_width = None self.tx_queue = Queue(1) self.tx_queue_sync = Event() self.rx_queue = Queue() self.cur_link_speed = None self.cur_link_width = None self.time_scale = get_sim_steps(1, 'sec') # ACK/NAK protocol # TX self.next_transmit_seq = 0x000 self.ackd_seq = 0xfff self.retry_buffer = Queue() # RX self.next_recv_seq = 0x000 self.nak_scheduled = False self.ack_nak_latency_timer = 0 self.max_payload_size = 128 self.max_latency_timer = 0 self.send_ack = Event() self._ack_latency_timer_cr = None # Flow control self.send_fc = Event() self.fc_state = [FcChannelState(fc_init[k], self.start_fc_update_timer) for k in range(8)] self.fc_initialized = False self.fc_init_vc = 0 self.fc_init_type = FcType.P self.fc_idle_timer_steps = get_sim_steps(10, 'us') self.fc_update_steps = get_sim_steps(30, 'us') self._fc_update_timer_cr = None super().__init__(*args, **kwargs) # VC0 is always active self.fc_state[0].active = True cocotb.fork(self._run_transmit()) cocotb.fork(self._run_receive()) cocotb.fork(self._run_fc_update_idle_timer()) def classify_tlp_vc(self, tlp): return 0 async def send(self, pkt): pkt.release_fc() await self.fc_state[self.classify_tlp_vc(pkt)].tx_tlp_fc_gate(pkt) await self.tx_queue.put(pkt) self.tx_queue_sync.set() async def _run_transmit(self): await NullTrigger() while True: while self.tx_queue.empty() and not self.send_ack.is_set() and not self.send_fc.is_set() and self.fc_initialized: self.tx_queue_sync.clear() await First(self.tx_queue_sync.wait(), self.send_ack.wait(), self.send_fc.wait()) pkt = None if self.send_ack.is_set(): # Send ACK or NAK DLLP # Runs when # - ACK timer expires # - ACK/NAK transmit requested self.send_ack.clear() if self.nak_scheduled: pkt = Dllp.create_nak((self.next_recv_seq-1) & 0xfff) else: pkt = Dllp.create_ack((self.next_recv_seq-1) & 0xfff) elif self.send_fc.is_set() or (not self.fc_initialized and self.tx_queue.empty()): # Send FC DLLP # Runs when # - FC timer expires # - FC update DLLP transmit requested # - FC init is not done AND no TLPs are queued for transmit if self.send_fc.is_set(): # Send FC update DLLP for fc_ch in self.fc_state: if not fc_ch.active or not fc_ch.fi2: continue sim_time = get_sim_time() if fc_ch.next_fc_p_tx <= sim_time: pkt = Dllp() pkt.vc = self.fc_init_vc pkt.type = DllpType.UPDATE_FC_P pkt.hdr_fc = fc_ch.ph.rx_credits_allocated pkt.data_fc = fc_ch.pd.rx_credits_allocated fc_ch.next_fc_p_tx = sim_time + self.fc_update_steps break if fc_ch.next_fc_np_tx <= sim_time: pkt = Dllp() pkt.vc = self.fc_init_vc pkt.type = DllpType.UPDATE_FC_NP pkt.hdr_fc = fc_ch.nph.rx_credits_allocated pkt.data_fc = fc_ch.npd.rx_credits_allocated fc_ch.next_fc_np_tx = sim_time + self.fc_update_steps break if fc_ch.next_fc_cpl_tx <= sim_time: pkt = Dllp() pkt.vc = self.fc_init_vc pkt.type = DllpType.UPDATE_FC_CPL pkt.hdr_fc = fc_ch.cplh.rx_credits_allocated pkt.data_fc = fc_ch.cpld.rx_credits_allocated fc_ch.next_fc_cpl_tx = sim_time + self.fc_update_steps break if not self.fc_initialized and not pkt: # Send FC init DLLP fc_ch = self.fc_state[self.fc_init_vc] pkt = Dllp() pkt.vc = self.fc_init_vc if self.fc_init_type == FcType.P: pkt.type = DllpType.INIT_FC1_P if not fc_ch.fi1 else DllpType.INIT_FC2_P pkt.hdr_fc = fc_ch.ph.rx_credits_allocated pkt.data_fc = fc_ch.pd.rx_credits_allocated self.fc_init_type = FcType.NP elif self.fc_init_type == FcType.NP: pkt.type = DllpType.INIT_FC1_NP if not fc_ch.fi1 else DllpType.INIT_FC2_NP pkt.hdr_fc = fc_ch.nph.rx_credits_allocated pkt.data_fc = fc_ch.npd.rx_credits_allocated self.fc_init_type = FcType.CPL elif self.fc_init_type == FcType.CPL: pkt.type = DllpType.INIT_FC1_CPL if not fc_ch.fi1 else DllpType.INIT_FC2_CPL pkt.hdr_fc = fc_ch.cplh.rx_credits_allocated pkt.data_fc = fc_ch.cpld.rx_credits_allocated self.fc_init_type = FcType.P # find next active VC that hasn't finished FC init for k in range(8): vc = (self.fc_init_vc+1+k) % 8 if self.fc_state[vc].active and not self.fc_state[vc].fi2: self.fc_init_vc = vc break # check all active VC and report FC not initialized if any are not complete self.fc_initialized = True for vc in range(8): if self.fc_state[vc].active and not self.fc_state[vc].fi2: self.fc_initialized = False if not pkt: # no more DLLPs to send, clear event self.send_fc.clear() if pkt is not None: self.log.debug("Send DLLP %s", pkt) elif not self.tx_queue.empty(): pkt = self.tx_queue.get_nowait() pkt.seq = self.next_transmit_seq self.log.debug("Send TLP %s", pkt) self.next_transmit_seq = (self.next_transmit_seq + 1) & 0xfff self.retry_buffer.put_nowait(pkt) if pkt: await self.handle_tx(pkt) async def handle_tx(self, pkt): raise NotImplementedError() async def ext_recv(self, pkt): if isinstance(pkt, Dllp): # DLLP self.log.debug("Receive DLLP %s", pkt) self.handle_dllp(pkt) else: # TLP self.log.debug("Receive TLP %s", pkt) if pkt.seq == self.next_recv_seq: # expected seq self.next_recv_seq = (self.next_recv_seq + 1) & 0xfff self.nak_scheduled = False self.start_ack_latency_timer() pkt = Tlp(pkt) self.fc_state[self.classify_tlp_vc(pkt)].rx_process_tlp_fc(pkt) self.rx_queue.put_nowait(pkt) elif (self.next_recv_seq - pkt.seq) & 0xfff < 2048: self.log.warning("Received duplicate TLP, discarding (seq %d, expecting %d)", pkt.seq, self.next_recv_seq) self.stop_ack_latency_timer() self.send_ack.set() else: self.log.warning("Received out-of-sequence TLP, sending NAK (seq %d, expecting %d)", pkt.seq, self.next_recv_seq) if not self.nak_scheduled: self.nak_scheduled = True self.stop_ack_latency_timer() self.send_ack.set() async def _run_receive(self): while True: tlp = await self.rx_queue.get() if self.rx_handler is None: raise Exception("Receive handler not set") await self.rx_handler(tlp) def handle_dllp(self, dllp): if dllp.type == DllpType.NOP: # discard NOP pass elif dllp.type in {DllpType.ACK, DllpType.NAK}: # ACK or NAK if (((self.next_transmit_seq-1) & 0xfff) - dllp.seq) & 0xfff > 2048: self.log.warning("Received ACK/NAK DLLP for future TLP, discarding (seq %d, next TX %d, ACK %d)", dllp.seq, self.next_transmit_seq, self.ackd_seq) elif (dllp.seq - self.ackd_seq) & 0xfff > 2048: self.log.warning("Received ACK/NAK DLLP for previously-ACKed TLP, discarding (seq %d, next TX %d, ACK %d)", dllp.seq, self.next_transmit_seq, self.ackd_seq) else: while dllp.seq != self.ackd_seq: # purge TLPs from retry buffer self.retry_buffer.get_nowait() self.ackd_seq = (self.ackd_seq + 1) & 0xfff self.log.debug("ACK TLP seq %d", self.ackd_seq) if dllp.type == DllpType.NAK: # retransmit self.log.warning("Got NAK DLLP, start TLP replay") raise Exception("TODO") elif dllp.type in {DllpType.INIT_FC1_P, DllpType.INIT_FC1_NP, DllpType.INIT_FC1_CPL, DllpType.INIT_FC2_P, DllpType.INIT_FC2_NP, DllpType.INIT_FC2_CPL, DllpType.UPDATE_FC_P, DllpType.UPDATE_FC_NP, DllpType.UPDATE_FC_CPL}: # Flow control self.fc_state[dllp.vc].handle_fc_dllp(dllp) else: raise Exception("TODO") def start_ack_latency_timer(self): if self._ack_latency_timer_cr is not None: if not self._ack_latency_timer_cr._finished: # already running return self._ack_latency_timer_cr = cocotb.fork(self._run_ack_latency_timer()) def stop_ack_latency_timer(self): if self._ack_latency_timer_cr is not None: self._ack_latency_timer_cr.kill() self._ack_latency_timer_cr = None async def _run_ack_latency_timer(self): d = int(self.time_scale * self.max_latency_timer) await Timer(max(d, 1), 'step') if not self.nak_scheduled: self.send_ack.set() def start_fc_update_timer(self): if self._fc_update_timer_cr is not None: if not self._fc_update_timer_cr._finished: # already running return self._fc_update_timer_cr = cocotb.fork(self._run_fc_update_timer()) def stop_fc_update_timer(self): if self._fc_update_timer_cr is not None: self._fc_update_timer_cr.kill() self._fc_update_timer_cr = None async def _run_fc_update_timer(self): d = int(self.time_scale * self.max_latency_timer) await Timer(max(d, 1), 'step') self.send_fc.set() async def _run_fc_update_idle_timer(self): while True: await Timer(self.fc_idle_timer_steps, 'step') self.send_fc.set()