Пример #1
0
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()
Пример #2
0
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()