async def test_event_is_set(dut): e = Event() assert not e.is_set() e.set() assert e.is_set() e.clear() assert not e.is_set()
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()