Пример #1
0
Файл: spi.py Проект: ymz000/luna
    def spi_edge_detectors(self, m):
        """ Generates edge detectors for the sample and output clocks, based on the current SPI mode.

        Returns:
            sample_edge, output_edge -- signals that pulse high for a single cycle when we should
                                        sample and change our outputs, respectively
        """

        # Select whether we're working with an inverted or un-inverted serial clock.
        serial_clock = Signal()
        if self.clock_polarity:
            m.d.comb += serial_clock.eq(~self.spi.sck)
        else:
            m.d.comb += serial_clock.eq(self.spi.sck)

        # Generate the leading and trailing edge detectors.
        # Note that we use rising and falling edge detectors, but call these leading and
        # trailing edges, as our clock here may have been inverted.
        leading_edge = Rose(serial_clock)
        trailing_edge = Fell(serial_clock)

        # Determine the sample and output edges based on the SPI clock phase.
        sample_edge = trailing_edge if self.clock_phase else leading_edge
        output_edge = leading_edge if self.clock_phase else trailing_edge

        return sample_edge, output_edge
Пример #2
0
    def elaborate(self, platform):
        bus = self.bus
        stream = self.stream

        m = Module()

        # Output SCLK asynchronously, to be passed on to dependent TX blocks.
        m.d.comb += [
            self.recovered_clock.eq(bus.sclk),
        ]

        # Configure outputs and output enables.
        # SYNC and SERCLK are driven by framer in this mode.
        m.d.comb += [
            bus.sync.o.eq(0),
            bus.sync.oe.eq(0),
            bus.serclk.o.eq(0),
            bus.serclk.oe.eq(0),
        ]

        # Register inputs.
        sync = Signal()
        crcsync = Signal()
        casync = Signal()
        serclk = Signal()
        ser = Signal()
        sclk = Signal()

        m.d.sync += [
            sync.eq(bus.sync.i),
            crcsync.eq(bus.crcsync),
            casync.eq(bus.casync),
            serclk.eq(bus.serclk.i),
            ser.eq(bus.ser),
            sclk.eq(bus.sclk),
        ]

        # Falling edge of SERCLK captures other inputs on this interface.
        capture_strobe = Fell(serclk)

        # (E1 mode only) CASYNC output is high during first bit of an E1 CAS multiframe.
        e1_cas_multiframe_strobe = Signal()

        m.d.sync += [
            stream.data_strobe.eq(0),
            stream.frame_strobe.eq(0),
            stream.multiframe_strobe.eq(0),
            e1_cas_multiframe_strobe.eq(0),
        ]

        with m.If(capture_strobe):
            m.d.sync += [
                stream.data.eq(ser),
                stream.data_strobe.eq(1),
                stream.frame_strobe.eq(sync),
                stream.multiframe_strobe.eq(crcsync),
                e1_cas_multiframe_strobe.eq(casync),
            ]

        return m
Пример #3
0
    def elaborate(self, platform):
        bus = self.bus
        stream = self.stream

        m = Module()

        # Configure outputs and output enables.
        m.d.comb += [
            # Output base rate clock (wherever it comes from) to framer's SERCLK pin.
            bus.serclk.o.eq(self.base_rate_clock),
            bus.serclk.oe.eq(self.enable),
            bus.sync.o.eq(0),
            bus.sync.oe.eq(0),
            bus.msync.o.eq(0),
            bus.msync.oe.eq(0),
        ]

        # Register inputs.
        serclk = Signal()
        sync = Signal()
        msync = Signal()

        # Synchronize input signals.
        m.d.sync += [
            # Framer SERCLK input is driven by FPGA SERCLK output.
            serclk.eq(bus.serclk.i),
            sync.eq(bus.sync.i),
            msync.eq(bus.msync.i),
        ]

        # Capture signals on the rising edge of TxSERCLKn.
        capture_strobe = Fell(serclk)

        m.d.sync += [
            stream.data_strobe.eq(0),
            stream.frame_strobe.eq(0),
            stream.multiframe_strobe.eq(0),
        ]

        with m.If(capture_strobe):
            m.d.sync += [
                stream.data_strobe.eq(1),
                stream.frame_strobe.eq(sync),
                stream.multiframe_strobe.eq(msync),
            ]

        m.d.comb += [
            bus.ser.eq(stream.data),
        ]

        return m
Пример #4
0
    def elaborate(self, platform):
        m = Module()

        #
        # Sequence tracking.
        #

        # Keep track of which sequence number we expect to see.
        expected_sequence_number = Signal(self.SEQUENCE_NUMBER_WIDTH)

        # Keep track of which credit we'll need to issue next...
        next_credit_to_issue = Signal(range(self._buffer_count))

        # ... and which header we'll need to ACK next.
        # We'll start with the maximum number, so our first advertisement wraps us back around to zero.
        next_header_to_ack = Signal.like(expected_sequence_number, reset=-1)

        #
        # Task "queues".
        #

        # Keep track of how many header received acknowledgements (LGOODs) we need to send.
        acks_to_send = Signal(range(self._buffer_count + 1), reset=1)
        enqueue_ack = Signal()
        dequeue_ack = Signal()

        with m.If(enqueue_ack & ~dequeue_ack):
            m.d.ss += acks_to_send.eq(acks_to_send + 1)
        with m.If(dequeue_ack & ~enqueue_ack):
            m.d.ss += acks_to_send.eq(acks_to_send - 1)

        # Keep track of how many link credits we've yet to free.
        # We'll start with every one of our buffers marked as "pending free"; this ensures
        # we perform our credit restoration properly.
        credits_to_issue = Signal.like(acks_to_send, reset=self._buffer_count)
        enqueue_credit_issue = Signal()
        dequeue_credit_issue = Signal()

        with m.If(enqueue_credit_issue & ~dequeue_credit_issue):
            m.d.ss += credits_to_issue.eq(credits_to_issue + 1)
        with m.If(dequeue_credit_issue & ~enqueue_credit_issue):
            m.d.ss += credits_to_issue.eq(credits_to_issue - 1)

        # Keep track of whether we should be sending an LBAD.
        lbad_pending = Signal()

        # Keep track of whether a retry has been requested.
        retry_pending = Signal()
        with m.If(self.retry_required):
            m.d.ss += retry_pending.eq(1)

        # Keep track of whether a keepalive has been requested.
        keepalive_pending = Signal()
        with m.If(self.keepalive_required):
            m.d.ss += keepalive_pending.eq(1)

        # Keep track of whether we're expected to send an power state response.
        lau_pending = Signal()
        lxu_pending = Signal()
        lpma_pending = Signal()

        with m.If(self.accept_power_state):
            m.d.ss += lau_pending.eq(1)
        with m.If(self.reject_power_state):
            m.d.ss += lxu_pending.eq(1)
        with m.If(self.acknowledge_power_state):
            m.d.ss += lpma_pending.eq(1)

        #
        # Header Packet Buffers
        #

        # Track which buffer will be filled next.
        read_pointer = Signal(range(self._buffer_count))
        write_pointer = Signal.like(read_pointer)

        # Track how many buffers we currently have in use.
        buffers_filled = Signal.like(credits_to_issue, reset=0)
        reserve_buffer = Signal()
        release_buffer = Signal()

        with m.If(reserve_buffer & ~release_buffer):
            m.d.ss += buffers_filled.eq(buffers_filled + 1)
        with m.If(release_buffer & ~reserve_buffer):
            m.d.ss += buffers_filled.eq(buffers_filled - 1)

        # Create buffers to receive any incoming header packets.
        buffers = Array(HeaderPacket() for _ in range(self._buffer_count))

        #
        # Packet reception (physical layer -> link layer).
        #

        # Flag that determines when we should ignore packets.
        #
        # After a receive error, we'll want to ignore all packets until we see a "retry"
        # link command; so we don't receive packets out of order.
        ignore_packets = Signal()

        # Create our raw packet parser / receiver.
        m.submodules.receiver = rx = RawHeaderPacketReceiver()
        m.d.comb += [
            # Our receiver passively monitors the data received for header packets.
            rx.sink.tap(self.sink),

            # Ensure it's always up to date about what sequence numbers we expect.
            rx.expected_sequence.eq(expected_sequence_number),

            # If we ever get a bad header packet sequence, we're required to retrain
            # the link [USB3.2r1: 7.2.4.1.5]. Pass the event through directly.
            self.recovery_required.eq(rx.bad_sequence & ~ignore_packets),

            # Notify the link layer when packets are received, for keeping track of our timers.
            self.packet_received.eq(rx.new_packet),

            # Notify the link layer if any bad packets are received; for diagnostics.
            self.bad_packet_received.eq(rx.bad_packet)
        ]

        # If we receive a valid packet, it's time for us to buffer it!
        with m.If(rx.new_packet & ~ignore_packets):
            m.d.ss += [
                # Load our header packet into the next write buffer...
                buffers[write_pointer].eq(rx.packet),

                # ... advance to the next buffer and sequence number...
                write_pointer.eq(write_pointer + 1),
                expected_sequence_number.eq(expected_sequence_number + 1),
            ]
            m.d.comb += [
                # ... mark the buffer space as occupied by valid data ...
                reserve_buffer.eq(1),

                # ... and queue an ACK for this packet.
                enqueue_ack.eq(1)
            ]

        # If we receive a bad packet, we'll need to request that the other side re-send.
        # The rules for this are summarized in [USB3.2r1: 7.2.4.1.5], and in comments below.
        with m.If(rx.bad_packet & ~ignore_packets):

            m.d.ss += [
                # First, we'll need to schedule transmission of an LBAD, which notifies the other
                # side that we received a bad packet; and that it'll need to transmit all unack'd
                # header packets to us again.
                lbad_pending.eq(1),

                # Next, we'll need to make sure we don't receive packets out of sequence. This means
                # we'll have to start ignoring packets until the other side responds to the LBAD.
                # The other side respond with an Retry link command (LRTY) once it's safe for us to
                # pay attention to packets again.
                ignore_packets.eq(1)
            ]

        # Finally, if we receive a Retry link command, this means we no longer need to ignore packets.
        # This typically happens in response to us sending an LBAD and marking future packets as ignored.
        with m.If(self.retry_received):
            m.d.ss += ignore_packets.eq(0)

        #
        # Packet delivery (link layer -> physical layer).
        #
        m.d.comb += [
            # As long as we have at least one buffer filled, we have header packets pending.
            self.queue.valid.eq(buffers_filled > 0),

            # Always provide the value of our oldest packet out to our consumer.
            self.queue.header.eq(buffers[read_pointer])
        ]

        # If the protocol layer is marking one of our packets as consumed, we no longer
        # need to buffer it -- it's the protocol layer's problem, now!
        with m.If(self.queue.valid & self.queue.ready):

            # Move on to reading from the next buffer in sequence.
            m.d.ss += read_pointer.eq(read_pointer + 1)

            m.d.comb += [
                # First, we'll free the buffer associated with the relevant packet...
                release_buffer.eq(1),

                # ... and request that our link partner be notified of the new space.
                enqueue_credit_issue.eq(1)
            ]

        #
        # Automatic credit expiration.
        #

        # FIXME: implement this!

        #
        # Link command generation.
        #
        m.submodules.lc_generator = lc_generator = LinkCommandGenerator()
        m.d.comb += [
            self.source.stream_eq(lc_generator.source),
            self.link_command_sent.eq(lc_generator.done),
        ]

        with m.FSM(domain="ss"):

            # DISPATCH_COMMAND -- the state in which we identify any pending link commands necessary,
            # and then move to the state in which we'll send them.
            with m.State("DISPATCH_COMMAND"):

                with m.If(self.enable):
                    # NOTE: the order below is important; changing it can easily break things:
                    # - ACKS must come before credits, as we must send an LGOOD before we send our initial credits.
                    # - LBAD must come after ACKs and credit management, as all scheduled ACKs need to be
                    #   sent to the other side for the LBAD to have the correct semantic meaning.

                    with m.If(retry_pending):
                        m.next = "SEND_LRTY"

                    # If we have acknowledgements to send, send them.
                    with m.Elif(acks_to_send):
                        m.next = "SEND_ACKS"

                    # If we have link credits to issue, move to issuing them to the other side.
                    with m.Elif(credits_to_issue):
                        m.next = "ISSUE_CREDITS"

                    # If we need to send an LBAD, do so.
                    with m.Elif(lbad_pending):
                        m.next = "SEND_LBAD"

                    # If we need to send a link power-state command, do so.
                    with m.Elif(lxu_pending):
                        m.next = "SEND_LXU"

                    # If we need to send a keepalive, do so.
                    with m.Elif(keepalive_pending):
                        m.next = "SEND_KEEPALIVE"

                # Once we've become disabled, we'll want to prepare for our next enable.
                # This means preparing for our advertisement, by:
                with m.If(Fell(self.enable) | self.usb_reset):
                    m.d.ss += [
                        # -Resetting our pending ACKs to 1, so we perform an sequence number advertisement
                        #  when we're next enabled.
                        acks_to_send.eq(1),

                        # -Decreasing our next sequence number; so we maintain a continuity of sequence numbers
                        #  without counting the advertising one. This doesn't seem to be be strictly necessary
                        #  per the spec; but seem to make analyzers happier, so we'll go with it.
                        next_header_to_ack.eq(next_header_to_ack - 1),

                        # - Clearing all of our buffers.
                        read_pointer.eq(0),
                        write_pointer.eq(0),
                        buffers_filled.eq(0),

                        # - Preparing to re-issue all of our buffer credits.
                        next_credit_to_issue.eq(0),
                        credits_to_issue.eq(self._buffer_count),

                        # - Clear our pending events.
                        retry_pending.eq(0),
                        lbad_pending.eq(0),
                        keepalive_pending.eq(0),
                        ignore_packets.eq(0)
                    ]

                    # If this is a USB Reset, also reset our sequences.
                    with m.If(self.usb_reset):
                        m.d.ss += [
                            expected_sequence_number.eq(0),
                            next_header_to_ack.eq(-1)
                        ]

            # SEND_ACKS -- a valid header packet has been received, or we're advertising
            # our initial sequence number; send an LGOOD packet.
            with m.State("SEND_ACKS"):

                # Send an LGOOD command, acknowledging the last received packet header.
                m.d.comb += [
                    lc_generator.generate.eq(1),
                    lc_generator.command.eq(LinkCommand.LGOOD),
                    lc_generator.subtype.eq(next_header_to_ack)
                ]

                # Wait until our link command is done, and then move on.
                with m.If(lc_generator.done):
                    # Move to the next header packet in the sequence, and decrease
                    # the number of outstanding ACKs.
                    m.d.comb += dequeue_ack.eq(1)
                    m.d.ss += next_header_to_ack.eq(next_header_to_ack + 1)

                    # If this was the last ACK we had to send, move back to our dispatch state.
                    with m.If(acks_to_send == 1):
                        m.next = "DISPATCH_COMMAND"

            # ISSUE_CREDITS -- header packet buffers have been freed; and we now need to notify the
            # other side, so it knows we have buffers available.
            with m.State("ISSUE_CREDITS"):

                # Send an LCRD command, indicating that we have a free buffer.
                m.d.comb += [
                    lc_generator.generate.eq(1),
                    lc_generator.command.eq(LinkCommand.LCRD),
                    lc_generator.subtype.eq(next_credit_to_issue)
                ]

                # Wait until our link command is done, and then move on.
                with m.If(lc_generator.done):
                    # Move to the next credit...
                    m.d.comb += dequeue_credit_issue.eq(1)
                    m.d.ss += next_credit_to_issue.eq(next_credit_to_issue + 1)

                    # If this was the last credit we had to issue, move back to our dispatch state.
                    with m.If(credits_to_issue == 1):
                        m.next = "DISPATCH_COMMAND"

            # SEND_LBAD -- we've received a bad header packet; we'll need to let the other side know.
            with m.State("SEND_LBAD"):
                m.d.comb += [
                    lc_generator.generate.eq(1),
                    lc_generator.command.eq(LinkCommand.LBAD),
                ]

                # Once we've sent the LBAD, we can mark is as no longer pending and return to our dispatch.
                # (We can't ever have multiple LBADs queued up; as we ignore future packets after sending one.)
                with m.If(lc_generator.done):
                    m.d.ss += lbad_pending.eq(0)
                    m.next = "DISPATCH_COMMAND"

            # SEND_LRTY -- our transmitter has requested that we send an retry indication to the other side.
            # We'll do our transmitter a favor and do so.
            with m.State("SEND_LRTY"):
                m.d.comb += [
                    lc_generator.generate.eq(1),
                    lc_generator.command.eq(LinkCommand.LRTY)
                ]

                with m.If(lc_generator.done):
                    m.d.ss += retry_pending.eq(0)
                    m.next = "DISPATCH_COMMAND"

            # SEND_KEEPALIVE -- our link layer timer has requested that we send a keep-alive,
            # indicating that we're still in U0 and the link is still good. Do so.
            with m.State("SEND_KEEPALIVE"):

                # Send the correct packet type for the direction our port is facing.
                command = LinkCommand.LDN if self._is_downstream_facing else LinkCommand.LUP

                m.d.comb += [
                    lc_generator.generate.eq(1),
                    lc_generator.command.eq(command)
                ]

                # Once we've send the keepalive, we can mark is as no longer pending and return to our dispatch.
                # (There's no sense in sending repeated keepalives; one gets the message across.)
                with m.If(lc_generator.done):
                    m.d.ss += keepalive_pending.eq(0)
                    m.next = "DISPATCH_COMMAND"

            # SEND_LXU -- we're being instructed to reject a requested power-state transfer.
            # We'll send an LXU packet to inform the other side of the rejection.
            with m.State("SEND_LXU"):
                m.d.comb += [
                    lc_generator.generate.eq(1),
                    lc_generator.command.eq(LinkCommand.LXU)
                ]

                with m.If(lc_generator.done):
                    m.d.ss += lxu_pending.eq(0)
                    m.next = "DISPATCH_COMMAND"

        return m
Пример #5
0
    def elaborate(self, platform):
        m = Module()

        #
        # Credit tracking.
        #

        # Keep track of how many transmit credits we currently have.
        credits_available = Signal(range(self._buffer_count + 1))
        credit_received = Signal()
        credit_consumed = Signal()

        with m.If(credit_received & ~credit_consumed):
            m.d.ss += credits_available.eq(credits_available + 1)
        with m.Elif(credit_consumed & ~credit_received):
            m.d.ss += credits_available.eq(credits_available - 1)

        # Provide a flag that indicates when we're done with our full bringup.
        with m.If(self.enable == 0):
            m.d.ss += self.bringup_complete.eq(0)
        with m.Elif(credits_available == self._buffer_count):
            m.d.ss += self.bringup_complete.eq(1)

        #
        # Task "queues".
        #

        # Control signals.
        retire_packet = Signal()

        # Pending packet count.
        packets_to_send = Signal(range(self._buffer_count + 1))
        enqueue_send = Signal()
        dequeue_send = Signal()

        # Un-retired packet count.
        packets_awaiting_ack = Signal()

        # If we need to retry sending our packets, we'll need to reset our pending packet count.
        # Otherwise, we increment and decrement our "to send" counts normally.
        with m.If(self.retry_required):
            m.d.ss += packets_to_send.eq(packets_awaiting_ack)
        with m.Elif(enqueue_send & ~dequeue_send):
            m.d.ss += packets_to_send.eq(packets_to_send + 1)
        with m.Elif(dequeue_send & ~enqueue_send):
            m.d.ss += packets_to_send.eq(packets_to_send - 1)

        # Track how many packets are yet to be retired.
        with m.If(enqueue_send & ~retire_packet):
            m.d.ss += packets_awaiting_ack.eq(packets_awaiting_ack + 1)
        with m.Elif(retire_packet & ~enqueue_send & packets_awaiting_ack > 0):
            m.d.ss += packets_awaiting_ack.eq(packets_awaiting_ack - 1)

        #
        # Header Packet Buffers
        #

        # Track:
        # - which buffer should be filled next
        # - which buffer we should send from next
        # - which buffer we've last acknowledged
        read_pointer = Signal(range(self._buffer_count))
        write_pointer = Signal.like(read_pointer)
        ack_pointer = Signal.like(read_pointer)

        # Create buffers to receive any incoming header packets.
        buffers = Array(HeaderPacket() for _ in range(self._buffer_count))

        # If we need to retry sending our packets, we'll need to start reading
        # again from the last acknowledged packet; so we'll reset our read pointer.
        with m.If(self.retry_required):
            m.d.ss += read_pointer.eq(ack_pointer)
        with m.Elif(dequeue_send):
            m.d.ss += read_pointer.eq(read_pointer + 1)

        # Last ACK'd buffer / packet retirement tracker.
        with m.If(retire_packet):
            m.d.ss += ack_pointer.eq(ack_pointer + 1)

        #
        # Packet acceptance (protocol layer -> link layer).
        #

        # If we have link credits available, we're able to accept data from the protocol layer.
        m.d.comb += self.queue.ready.eq(credits_available != 0)

        # If the protocol layer is handing us a packet...
        with m.If(self.queue.valid & self.queue.ready):
            # ... consume a credit, as we're going to be using up a receiver buffer, and
            # schedule sending of that packet.
            m.d.comb += [credit_consumed.eq(1), enqueue_send.eq(1)]

            # Finally, capture the packet into the buffer for transmission.
            m.d.ss += [
                buffers[write_pointer].eq(self.queue.header),
                write_pointer.eq(write_pointer + 1)
            ]

        #
        # Packet delivery (link layer -> physical layer)
        #
        m.submodules.packet_tx = packet_tx = RawPacketTransmitter()
        m.d.comb += [
            self.source.stream_eq(packet_tx.source),
            packet_tx.data_sink.stream_eq(self.data_sink)
        ]

        # Keep track of the next sequence number we'll need to send...
        transmit_sequence_number = Signal(self.SEQUENCE_NUMBER_WIDTH)

        with m.FSM(domain="ss"):

            # DISPATCH_PACKET -- wait packet transmissions to be scheduled, and prepare
            # our local transmitter with the proper data to send them.
            with m.State("DISPATCH_PACKET"):

                # If we have packets to send, pass them to our transmitter.
                with m.If(packets_to_send & self.enable):
                    m.d.ss += [
                        # Grab the packet from our read queue, and pass it to the transmitter;
                        # but override its sequence number field with our current sequence number.
                        packet_tx.header.eq(buffers[read_pointer]),
                        packet_tx.header.sequence_number.eq(
                            transmit_sequence_number),
                    ]

                    # Move on to sending our packet.
                    m.next = "WAIT_FOR_SEND"

            # WAIT_FOR_SEND -- we've now dispatched our packet; and we're ready to wait for it to be sent.
            with m.State("WAIT_FOR_SEND"):
                m.d.comb += packet_tx.generate.eq(1)

                # Once the packet is done...
                with m.If(packet_tx.done):
                    m.d.comb += dequeue_send.eq(1)
                    m.d.ss += transmit_sequence_number.eq(
                        transmit_sequence_number + 1)

                    # If this was the last packet we needed to send, resume waiting for one.
                    with m.If(packets_to_send == 1):
                        m.next = "DISPATCH_PACKET"

        #
        # Header Packet Timer
        #
        # FIXME: implement this

        #
        # Link Command Handling
        #

        # Core link command receiver.
        m.submodules.lc_detector = lc_detector = LinkCommandDetector()
        m.d.comb += [
            lc_detector.sink.tap(self.sink),
            self.link_command_received.eq(lc_detector.new_command)
        ]

        # Keep track of which credit we expect to get back next.
        next_expected_credit = Signal(range(self._buffer_count))

        # Keep track of what sequence number we expect to have ACK'd next.
        next_expected_ack_number = Signal(self.SEQUENCE_NUMBER_WIDTH, reset=-1)

        # Handle link commands as we receive them.
        with m.If(lc_detector.new_command):
            with m.Switch(lc_detector.command):

                #
                # Link Credit Reception
                #
                with m.Case(LinkCommand.LCRD):

                    # If the credit matches the sequence we're expecting, we can accept it!
                    with m.If(next_expected_credit == lc_detector.subtype):
                        m.d.comb += credit_received.eq(1)

                        # Next time, we'll expect the next credit in the sequence.
                        m.d.ss += next_expected_credit.eq(
                            next_expected_credit + 1)

                    # Otherwise, we've lost synchronization. We'll need to trigger link recovery.
                    with m.Else():
                        m.d.comb += self.recovery_required.eq(1)

                #
                # Packet Acknowledgement
                #
                with m.Case(LinkCommand.LGOOD):

                    # If the credit matches the sequence we're expecting, we can accept it!
                    with m.If(next_expected_ack_number == lc_detector.subtype):
                        m.d.comb += retire_packet.eq(1)

                        # Next time, we'll expect the next credit in the sequence.
                        m.d.ss += next_expected_ack_number.eq(
                            next_expected_ack_number + 1)

                    # Otherwise, we've lost synchronization. We'll need to trigger link recovery.
                    with m.Else():
                        m.d.comb += self.recovery_required.eq(1)

                #
                # Packet Negative Acknowledgement
                #
                with m.Case(LinkCommand.LBAD):

                    # LBADs aren't sequenced; instead, they require a retry of all unacknowledged
                    # (unretired) packets. Mark ourselves as requiring a retry; which should trigger
                    # our internal logic to execute a retry.
                    m.d.comb += self.retry_required.eq(1)

                #
                # Link Partner Retrying Send
                #
                with m.Case(LinkCommand.LRTY):

                    # If we see an LRTY, it's an indication that our link partner is performing a
                    # retried send. This information is handled by the Receiver, so we'll forward it along.
                    m.d.comb += self.retry_received.eq(1)

                #
                # Link Power State transition request.
                #
                with m.Case(LinkCommand.LGO_U):

                    # We don't handle LGO requests locally; so instead, we'll pass this back to the link layer.
                    m.d.comb += [
                        self.lgo_received.eq(1),
                        self.lgo_target.eq(lc_detector.subtype)
                    ]

        #
        # Reset Handling
        #
        with m.If(self.usb_reset | Fell(self.enable)):
            m.d.ss += [
                packets_to_send.eq(0),
                packets_awaiting_ack.eq(0),
                next_expected_credit.eq(0),
                read_pointer.eq(0),
                write_pointer.eq(0),
                ack_pointer.eq(0),
                credits_available.eq(0),
            ]

            with m.If(self.usb_reset):
                m.d.ss += [
                    next_expected_ack_number.eq(0),
                    transmit_sequence_number.eq(0)
                ]

        #
        # Debug outputs.
        #
        m.d.comb += [
            self.credits_available.eq(credits_available),
            self.packets_to_send.eq(packets_to_send)
        ]

        return m
Пример #6
0
Файл: spi.py Проект: ymz000/luna
    def elaborate(self, platform):

        m = Module()
        spi = self.spi

        sample_edge = Fell(spi.sck)

        # Bit counter: counts the number of bits received.
        max_bit_count = max(self.word_size, self.command_size)
        bit_count = Signal(range(0, max_bit_count + 1))

        # Shift registers for our command and data.
        current_command = Signal.like(self.command)
        current_word = Signal.like(self.word_received)

        # De-assert our control signals unless explicitly asserted.
        m.d.sync += [self.command_ready.eq(0), self.word_complete.eq(0)]

        with m.FSM() as fsm:
            m.d.comb += [
                self.idle.eq(fsm.ongoing('IDLE')),
                self.stalled.eq(fsm.ongoing('STALL')),
            ]

            # STALL: entered when we can't accept new bits -- either when
            # CS starts asserted, or when we've received more data than expected.
            with m.State("STALL"):

                # Wait for CS to clear.
                with m.If(~spi.cs):
                    m.next = 'IDLE'

            # We ignore all data until chip select is asserted, as that data Isn't For Us (TM).
            # We'll spin and do nothing until the bus-master addresses us.
            with m.State('IDLE'):
                m.d.sync += bit_count.eq(0)

                with m.If(spi.cs):
                    m.next = 'RECEIVE_COMMAND'

            # Once CS is low, we'll shift in our command.
            with m.State('RECEIVE_COMMAND'):

                # If CS is de-asserted early; our transaction is being aborted.
                with m.If(~spi.cs):
                    m.next = 'IDLE'

                # Continue shifting in data until we have a full command.
                with m.If(bit_count < self.command_size):
                    with m.If(sample_edge):
                        m.d.sync += [
                            bit_count.eq(bit_count + 1),
                            current_command.eq(
                                Cat(spi.sdi, current_command[:-1]))
                        ]

                # ... and then pass that command out to our controller.
                with m.Else():
                    m.d.sync += [
                        bit_count.eq(0),
                        self.command_ready.eq(1),
                        self.command.eq(current_command)
                    ]
                    m.next = 'PROCESSING'

            # Give our controller a wait state to prepare any response they might want to...
            with m.State('PROCESSING'):
                m.next = 'LATCH_OUTPUT'

            # ... and then latch in the response to transmit.
            with m.State('LATCH_OUTPUT'):
                m.d.sync += current_word.eq(self.word_to_send)
                m.next = 'SHIFT_DATA'

            # Finally, exchange data.
            with m.State('SHIFT_DATA'):

                # If CS is de-asserted early; our transaction is being aborted.
                with m.If(~spi.cs):
                    m.next = 'IDLE'

                m.d.sync += spi.sdo.eq(current_word[-1])

                # Continue shifting data until we have a full word.
                with m.If(bit_count < self.word_size):
                    with m.If(sample_edge):
                        m.d.sync += [
                            bit_count.eq(bit_count + 1),
                            current_word.eq(Cat(spi.sdi, current_word[:-1]))
                        ]

                # ... and then output that word on our bus.
                with m.Else():
                    m.d.sync += [
                        bit_count.eq(0),
                        self.word_complete.eq(1),
                        self.word_received.eq(current_word)
                    ]

                    # Stay in the stall state until CS is de-asserted.
                    m.next = 'STALL'

        return m
Пример #7
0
    def elaborate(self, platform: Platform) -> Module:
        m = Module()

        # UART pins for the AsyncSerial, is needed for doing output enable
        uart_pins = Record([("rx", [("i", 1)]), ("tx", [("o", 1)])])

        # Add the UART resources of the ROCKPro64
        platform.add_resources(
            [Resource("rx_rp64", 0, Pins(self.rx, dir="i"))])
        platform.add_resources(
            [Resource("tx_rp64", 0, Pins(self.tx, dir="oe"))])

        rx_pin = platform.request("rx_rp64", 0)
        tx_pin = platform.request("tx_rp64", 0)

        m.d.comb += uart_pins.rx.i.eq(rx_pin.i)
        m.d.comb += tx_pin.o.eq(uart_pins.tx.o)

        # ROCKPro64 refuses to boot if this is high
        enable_tx = Signal()
        m.d.comb += tx_pin.oe.eq(enable_tx)

        # 1.5 Megabaud UART to the ROCKPro64
        uart = AsyncSerial(divisor=int(self.clk / 1.5E6), pins=uart_pins)
        m.submodules += uart

        # Send 0x03 (Ctrl C) until the u-boot prompt appears (might take quite a while because apparently it seems to try to connect to the network interface for a long time)
        # Send 'pci enum' once '=>' is received and init is asserted
        # Set init_sent to high for 1 cycle after '\n' has been sent

        # Turn a string into a list of bytes
        def generate_memory(data_string):
            result = []
            for char in data_string:
                result.append(ord(char))
            return result

        # Command to send. Don't forget to change depth when changing this command.
        depth = 10
        pci_mem = Memory(width=8,
                         depth=depth,
                         init=generate_memory(' pci enum\n'))
        pci_rport = m.submodules.pci_rport = pci_mem.read_port()

        # Hardwired to 1, since boot is not yet controlled
        m.d.comb += enable_tx.eq(1)

        # We can always accept data
        m.d.comb += uart.rx.ack.eq(1)

        with m.FSM():
            with m.State("Wait"):
                m.d.sync += [
                    uart.tx.data.eq(0x03),  # Spam Ctrl C
                    uart.tx.ack.eq(1),
                ]

                # Wait for '=>'
                with m.If(uart.rx.data == ord('=')):
                    m.next = "uboot-1"

            with m.State("uboot-1"):
                m.d.sync += self.init_sent.eq(0)
                with m.If(uart.rx.data == ord('>')):
                    m.next = "uboot-2"

            # Arrived at u-boot prompt, ready to sent, waiting for init signal
            with m.State("uboot-2"):
                m.d.sync += self.rdy.eq(1)
                with m.If(self.init):
                    m.next = "send-pci-start"

            # Go! Set the UART data to what the memory outputs.
            with m.State("send-pci-start"):
                m.d.sync += [
                    self.rdy.eq(0),
                    uart.tx.data.eq(pci_rport.data),
                    pci_rport.addr.eq(0),
                ]

                # Once the TX is ready, send data
                with m.If(uart.tx.rdy):
                    m.next = "send-pci-data"

            with m.State("send-pci-data"):
                m.d.sync += uart.tx.data.eq(pci_rport.data)
                m.d.sync += uart.tx.ack.eq(uart.tx.rdy)

                # When the TX stops being ready, set the next byte. Doesn't work with 'Rose'.
                with m.If(Fell(uart.tx.rdy)):
                    m.d.sync += pci_rport.addr.eq(pci_rport.addr + 1)

                # Once all data has been sent, go back to waiting for '>' and strobe init_sent.
                with m.If((pci_rport.addr == depth - 1)):
                    m.next = "uboot-1"
                    m.d.sync += self.init_sent.eq(1)

        uart_pins = platform.request("uart", 0)

        #m.d.comb += uart_pins.tx.o.eq(tx_pin.o)
        m.d.comb += uart_pins.tx.o.eq(rx_pin.i)

        return m