Example #1
0
    def elaborate(self, platform):
        m = Module()
        m.submodules.bridge = self._bridge

        # Shortcuts to our components.
        interface      = self.interface
        token          = self.interface.tokenizer
        rx             = self.interface.rx
        handshakes_out = self.interface.handshakes_out

        # Logic condition for getting a new setup packet.
        new_setup = token.new_token & token.is_setup

        #
        # Core FIFO.
        #
        m.submodules.fifo = fifo = ResetInserter(new_setup)(SyncFIFOBuffered(width=8, depth=10))

        m.d.comb += [

            # We'll write to the active FIFO whenever the last received token is a SETUP
            # token, and we have incoming data; and we'll always write the data received
            fifo.w_en         .eq(token.is_setup & rx.valid & rx.next),
            fifo.w_data       .eq(rx.payload),

            # We'll advance the FIFO whenever our CPU reads from the data CSR;
            # and we'll always read our data from the FIFO.
            fifo.r_en         .eq(self.data.r_stb),
            self.data.r_data  .eq(fifo.r_data),

            # Pass the FIFO status on to our CPU.
            self.have.r_data  .eq(fifo.r_rdy),

            # Always acknowledge SETUP packets as they arrive.
            handshakes_out.ack  .eq(token.is_setup & interface.rx_ready_for_response)
        ]

        #
        # Control registers
        #

        # Our address register always reads the current address of the device;
        # but will generate a
        m.d.comb += self._address.r_data.eq(interface.active_address)
        with m.If(self._address.w_stb):
            m.d.comb += [
                interface.address_changed  .eq(1),
                interface.new_address      .eq(self._address.w_data),
            ]


        #
        # Status and interrupts.
        #

        with m.If(token.new_token):
            m.d.usb += self.epno.r_data.eq(token.endpoint)

        # TODO: generate interrupts

        return DomainRenamer({"sync": "usb"})(m)
Example #2
0
    def elaborate(self, platform):
        m = Module()
        m.submodules.bridge = self._bridge

        # Shortcuts to our components.
        interface      = self.interface
        token          = self.interface.tokenizer
        rx             = self.interface.rx
        handshakes_out = self.interface.handshakes_out

        #
        # Control registers
        #

        # Active endpoint number.
        with m.If(self.epno.w_stb):
            m.d.usb += self.epno.r_data.eq(self.epno.w_data)

        # Keep track of which endpoints are stalled.
        endpoint_stalled = Array(Signal() for _ in range(16))

        # Allow the CPU to set our enable bit.
        with m.If(self.enable.w_stb):
            m.d.usb += self.enable.r_data.eq(self.enable.w_data)

        # If we've just ACK'd a receive, clear our enable.
        with m.If(interface.handshakes_out.ack):
            m.d.usb += self.enable.r_data.eq(0)


        # Set the value of our endpoint `stall` based on our `stall` register...
        with m.If(self.stall.w_stb):
            m.d.usb += endpoint_stalled[self.epno.r_data].eq(self.stall.w_data)

        # ... but clear our endpoint `stall` when we get a SETUP packet.
        with m.If(token.is_setup & token.new_token):
            m.d.usb += endpoint_stalled[token.endpoint].eq(0)

        #
        # Core FIFO.
        #
        m.submodules.fifo = fifo = ResetInserter(self.reset.w_stb)(SyncFIFOBuffered(width=8, depth=10))

        # Shortcut for when we should allow a receive. We'll read when:
        #  - Our `epno` register matches the target register; and
        #  - We've primed the relevant endpoint.
        #  - Our most recent token is an OUT.
        #  - We're not stalled.
        stalled          = token.is_out & endpoint_stalled[token.endpoint]
        endpoint_matches = (token.endpoint == self.epno.r_data)
        allow_receive    = endpoint_matches & self.enable.r_data & token.is_out & ~stalled
        nak_receives     = token.is_out & ~allow_receive & ~stalled

        m.d.comb += [

            # We'll write to the endpoint iff we have a primed
            fifo.w_en         .eq(allow_receive & rx.valid & rx.next),
            fifo.w_data       .eq(rx.payload),

            # We'll advance the FIFO whenever our CPU reads from the data CSR;
            # and we'll always read our data from the FIFO.
            fifo.r_en         .eq(self.data.r_stb),
            self.data.r_data  .eq(fifo.r_data),

            # Pass the FIFO status on to our CPU.
            self.have.r_data  .eq(fifo.r_rdy),

            # If we've just finished an allowed receive, ACK.
            handshakes_out.ack    .eq(allow_receive & interface.rx_ready_for_response),

            # If we were stalled, stall.
            handshakes_out.stall  .eq(stalled & interface.rx_ready_for_response),

            # If we're not ACK'ing or STALL'ing, NAK all packets.
            handshakes_out.nak    .eq(nak_receives & interface.rx_ready_for_response)
        ]


        #
        # Interrupt/status
        #

        return DomainRenamer({"sync": "usb"})(m)
Example #3
0
    def elaborate(self, platform):
        m = Module()
        m.submodules.bridge = self._bridge

        # Shortcuts to our components.
        token          = self.interface.tokenizer
        tx             = self.interface.tx
        handshakes_out = self.interface.handshakes_out

        #
        # Core FIFO.
        #


        # Create our FIFO; and set it to be cleared whenever the user requests.
        m.submodules.fifo = fifo = \
             ResetInserter(self.reset.w_stb)(SyncFIFOBuffered(width=8, depth=self.max_packet_size))

        m.d.comb += [
            # Whenever the user DATA register is written to, add the relevant data to our FIFO.
            fifo.w_en         .eq(self.data.w_stb),
            fifo.w_data       .eq(self.data.w_data),
        ]

        # Keep track of the amount of data in our FIFO.
        bytes_in_fifo = Signal(range(0, self.max_packet_size + 1))

        # If we're clearing the whole FIFO, reset our data count.
        with m.If(self.reset.w_stb):
            m.d.usb += bytes_in_fifo.eq(0)

        # Keep track of our FIFO's data count as data is added or removed.
        increment = fifo.w_en & fifo.w_rdy
        decrement = fifo.r_en & fifo.r_rdy

        with m.Elif(increment & ~decrement):
            m.d.usb += bytes_in_fifo.eq(bytes_in_fifo + 1)
        with m.Elif(decrement & ~increment):
            m.d.usb += bytes_in_fifo.eq(bytes_in_fifo - 1)


        #
        # Register updates.
        #

        # Active endpoint number.
        with m.If(self.epno.w_stb):
            m.d.usb += self.epno.r_data.eq(self.epno.w_data)

        # Keep track of which endpoints are stalled.
        endpoint_stalled = Array(Signal() for _ in range(16))

        # Set the value of our endpoint `stall` based on our `stall` register...
        with m.If(self.stall.w_stb):
            m.d.usb += endpoint_stalled[self.epno.r_data].eq(self.stall.w_data)

        # ... but clear our endpoint `stall` when we get a SETUP packet.
        with m.If(token.is_setup & token.new_token):
            m.d.usb += endpoint_stalled[token.endpoint].eq(0)

        # Manual data toggle control.
        # TODO: Remove this in favor of automated tracking?
        m.d.comb += self.interface.tx_pid_toggle.eq(self.pid.r_data)
        with m.If(self.pid.w_stb):
            m.d.usb += self.pid.r_data.eq(self.pid.w_data)


        #
        # Status registers.
        #
        m.d.comb += [
            self.have.r_data  .eq(fifo.r_rdy)
        ]

        #
        # Control logic.
        #

        # Logic shorthand.
        new_in_token     = (token.is_in & token.ready_for_response)
        endpoint_matches = (token.endpoint == self.epno.r_data)
        stalled          = endpoint_stalled[token.endpoint]

        with m.FSM(domain='usb') as f:

            # Drive our IDLE line based on our FSM state.
            m.d.comb += self.idle.r_data.eq(f.ongoing('IDLE'))

            # IDLE -- our CPU hasn't yet requested that we send data.
            # We'll wait for it to do so, and NAK any packets that arrive.
            with m.State("IDLE"):

                # If we get an IN token...
                with m.If(new_in_token):

                    # STALL it, if the endpoint is STALL'd...
                    with m.If(stalled):
                        m.d.comb += handshakes_out.stall.eq(1)

                    # Otherwise, NAK.
                    with m.Else():
                        m.d.comb += handshakes_out.nak.eq(1)


                # If the user request that we send data, "prime" the endpoint.
                # This means we have data to send, but are just waiting for an IN token.
                with m.If(self.epno.w_stb & ~stalled):
                    m.next = "PRIMED"

            # PRIMED -- our CPU has provided data, but we haven't been sent an IN token, yet.
            # Await that IN token.
            with m.State("PRIMED"):

                with m.If(new_in_token):

                    # If the target endpoint is STALL'd, reply with STALL no matter what.
                    with m.If(stalled):
                        m.d.comb += handshakes_out.stall.eq(1)

                    # If we have a new IN token to our endpoint, move to responding to it.
                    with m.Elif(endpoint_matches):

                        # If there's no data in our endpoint, send a ZLP.
                        with m.If(~fifo.r_rdy):
                            m.next = "SEND_ZLP"

                        # Otherwise, send our data, starting with our first byte.
                        with m.Else():
                            m.d.usb += tx.first.eq(1)
                            m.next = "SEND_DATA"

                    # Otherwise, we don't have a response; NAK the packet.
                    with m.Else():
                        m.d.comb += handshakes_out.nak.eq(1)

            # SEND_ZLP -- we're now now ready to respond to an IN token with a ZLP.
            # Send our response.
            with m.State("SEND_ZLP"):
                m.d.comb += [
                    tx.valid  .eq(1),
                    tx.last   .eq(1)
                ]
                m.next = 'IDLE'

            # SEND_DATA -- we're now ready to respond to an IN token to our endpoint.
            # Send our response.
            with m.State("SEND_DATA"):
                last_packet = (bytes_in_fifo == 1)

                m.d.comb += [
                    tx.valid    .eq(1),
                    tx.last     .eq(last_packet),

                    # Drive our transmit data directly from our FIFO...
                    tx.payload  .eq(fifo.r_data),

                    # ... and advance our FIFO each time a data byte is transmitted.
                    fifo.r_en   .eq(tx.ready)
                ]

                # After we've sent a byte, drop our first flag.
                with m.If(tx.ready):
                    m.d.usb += tx.first.eq(0)

                # Once we transmit our last packet, we're done transmitting. Move back to IDLE.
                with m.If(last_packet & tx.ready):
                    m.next = 'IDLE'

        return DomainRenamer({"sync": "usb"})(m)
Example #4
0
    def elaborate(self, platform):
        m = Module()

        sync_pulse = Signal(8)

        da_reset_shifter = Signal()
        da_reset_bitstuff = Signal(
        )  # Need to reset the bit stuffer 1 cycle after the shifter.
        stall = Signal()

        # These signals are set during the sync pulse
        sp_reset_bitstuff = Signal()
        sp_reset_shifter = Signal()
        sp_bit = Signal()
        sp_o_data_strobe = Signal()

        # 12MHz domain
        bitstuff_valid_data = Signal()

        # Keep a Gray counter around to smoothly transition between states
        state_gray = Signal(2)
        state_data = Signal()
        state_sync = Signal()

        #
        # Transmit gearing.
        #
        m.submodules.shifter = shifter = TxShifter(width=8)
        m.d.comb += [
            shifter.i_data.eq(self.i_data_payload),
            shifter.i_enable.eq(~stall),
            shifter.i_clear.eq(da_reset_shifter | sp_reset_shifter)
        ]

        #
        # Bit-stuffing and NRZI.
        #
        bitstuff = ResetInserter(da_reset_bitstuff)(TxBitstuffer())
        m.submodules.bitstuff = bitstuff

        m.submodules.nrzi = nrzi = TxNRZIEncoder()

        #
        # Transmit controller.
        #

        m.d.comb += [
            # Send a data strobe when we're two bits from the end of the sync pulse.
            # This is because the pipeline takes two bit times, and we want to ensure the pipeline
            # has spooled up enough by the time we're there.
            bitstuff.i_data.eq(shifter.o_data),
            stall.eq(bitstuff.o_stall),
            sp_bit.eq(sync_pulse[0]),
            sp_reset_bitstuff.eq(sync_pulse[0]),

            # The shifter has one clock cycle of latency, so reset it
            # one cycle before the end of the sync byte.
            sp_reset_shifter.eq(sync_pulse[1]),
            sp_o_data_strobe.eq(sync_pulse[5]),
            state_data.eq(state_gray[0] & state_gray[1]),
            state_sync.eq(state_gray[0] & ~state_gray[1]),
            self.fit_oe.eq(state_data | state_sync),
            self.fit_dat.eq((state_data & shifter.o_data & ~bitstuff.o_stall)
                            | sp_bit),
            self.o_data_strobe.eq(state_data & shifter.o_get & ~stall
                                  & self.i_oe),
        ]

        # If we reset the shifter, then o_empty will go high on the next cycle.
        #

        m.d.usb += [
            # If the shifter runs out of data, percolate the "reset" signal to the
            # shifter, and then down to the bitstuffer.
            # da_reset_shifter.eq(~stall & shifter.o_empty & ~da_stalled_reset),
            # da_stalled_reset.eq(da_reset_shifter),
            # da_reset_bitstuff.eq(~stall & da_reset_shifter),
            bitstuff_valid_data.eq(~stall & shifter.o_get & self.i_oe),
        ]

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

            with m.State('IDLE'):
                with m.If(self.i_oe):
                    m.d.usb += [sync_pulse.eq(1 << 7), state_gray.eq(0b01)]
                    m.next = "SEND_SYNC"
                with m.Else():
                    m.d.usb += state_gray.eq(0b00)

            with m.State('SEND_SYNC'):
                m.d.usb += sync_pulse.eq(sync_pulse >> 1)

                with m.If(sync_pulse[0]):
                    m.d.usb += state_gray.eq(0b11)
                    m.next = "SEND_DATA"
                with m.Else():
                    m.d.usb += state_gray.eq(0b01)

            with m.State('SEND_DATA'):
                with m.If(~self.i_oe & shifter.o_empty & ~bitstuff.o_stall):
                    with m.If(bitstuff.o_will_stall):
                        m.next = 'STUFF_LAST_BIT'
                    with m.Else():
                        m.d.usb += state_gray.eq(0b10)
                        m.next = 'IDLE'

                with m.Else():
                    m.d.usb += state_gray.eq(0b11)

            with m.State('STUFF_LAST_BIT'):
                m.d.usb += state_gray.eq(0b10)
                m.next = 'IDLE'

        # 48MHz domain
        # NRZI encoding
        nrzi_dat = Signal()
        nrzi_oe = Signal()

        # Cross the data from the 12MHz domain to the 48MHz domain
        cdc_dat = FFSynchronizer(self.fit_dat,
                                 nrzi_dat,
                                 o_domain="usb_io",
                                 stages=3)
        cdc_oe = FFSynchronizer(self.fit_oe,
                                nrzi_oe,
                                o_domain="usb_io",
                                stages=3)
        m.submodules += [cdc_dat, cdc_oe]

        m.d.comb += [
            nrzi.i_valid.eq(self.i_bit_strobe),
            nrzi.i_data.eq(nrzi_dat),
            nrzi.i_oe.eq(nrzi_oe),
            self.o_usbp.eq(nrzi.o_usbp),
            self.o_usbn.eq(nrzi.o_usbn),
            self.o_oe.eq(nrzi.o_oe),
        ]

        return m
Example #5
0
    def elaborate(self, platform):
        m = Module()

        #
        # Clock/Data recovery.
        #
        clock_data_recovery = RxClockDataRecovery(self.i_usbp, self.i_usbn)
        m.submodules.clock_data_recovery = clock_data_recovery
        m.d.comb += self.o_bit_strobe.eq(clock_data_recovery.line_state_valid)

        #
        # NRZI decoding
        #
        m.submodules.nrzi = nrzi = RxNRZIDecoder()
        m.d.comb += [
            nrzi.i_valid.eq(self.o_bit_strobe),
            nrzi.i_dj.eq(clock_data_recovery.line_state_dj),
            nrzi.i_dk.eq(clock_data_recovery.line_state_dk),
            nrzi.i_se0.eq(clock_data_recovery.line_state_se0),
        ]

        #
        # Packet boundary detection.
        #
        m.submodules.detect = detect = RxPacketDetect()
        m.d.comb += [
            detect.i_valid.eq(nrzi.o_valid),
            detect.i_se0.eq(nrzi.o_se0),
            detect.i_data.eq(nrzi.o_data),
        ]

        #
        # Bitstuff remover.
        #
        m.submodules.bitstuff = bitstuff = \
            ResetInserter(~detect.o_pkt_active)(RxBitstuffRemover())
        m.d.comb += [
            bitstuff.i_valid.eq(nrzi.o_valid),
            bitstuff.i_data.eq(nrzi.o_data),
            self.o_receive_error.eq(bitstuff.o_error)
        ]

        #
        # 1bit->8bit (1byte) gearing
        #
        m.submodules.shifter = shifter = RxShifter(width=8)
        m.d.comb += [
            shifter.reset.eq(detect.o_pkt_end),
            shifter.i_data.eq(bitstuff.o_data),
            shifter.i_valid.eq(~bitstuff.o_stall
                               & Past(detect.o_pkt_active, domain="usb_io")),
        ]

        #
        # Clock domain crossing.
        #
        flag_start = Signal()
        flag_end = Signal()
        flag_valid = Signal()
        m.submodules.payload_fifo = payload_fifo = AsyncFIFOBuffered(
            width=8, depth=4, r_domain="usb", w_domain="usb_io")
        m.d.comb += [
            payload_fifo.w_data.eq(shifter.o_data[::-1]),
            payload_fifo.w_en.eq(shifter.o_put),
            self.o_data_payload.eq(payload_fifo.r_data),
            self.o_data_strobe.eq(payload_fifo.r_rdy),
            payload_fifo.r_en.eq(1)
        ]

        m.submodules.flags_fifo = flags_fifo = AsyncFIFOBuffered(
            width=2, depth=4, r_domain="usb", w_domain="usb_io")
        m.d.comb += [
            flags_fifo.w_data[1].eq(detect.o_pkt_start),
            flags_fifo.w_data[0].eq(detect.o_pkt_end),
            flags_fifo.w_en.eq(detect.o_pkt_start | detect.o_pkt_end),
            flag_start.eq(flags_fifo.r_data[1]),
            flag_end.eq(flags_fifo.r_data[0]),
            flag_valid.eq(flags_fifo.r_rdy),
            flags_fifo.r_en.eq(1),
        ]

        # Packet flag signals (in 12MHz domain)
        m.d.comb += [
            self.o_pkt_start.eq(flag_start & flag_valid),
            self.o_pkt_end.eq(flag_end & flag_valid),
        ]

        with m.If(self.o_pkt_start):
            m.d.usb += self.o_pkt_in_progress.eq(1)
        with m.Elif(self.o_pkt_end):
            m.d.usb += self.o_pkt_in_progress.eq(0)

        return m
Example #6
0
    def elaborate(self, platform):
        m = Module()

        sync_pulse = Signal(8)

        da_reset_shifter = Signal()
        da_reset_bitstuff = Signal() # Need to reset the bit stuffer 1 cycle after the shifter.
        stall = Signal()

        # These signals are set during the sync pulse
        sp_reset_bitstuff = Signal()
        sp_reset_shifter = Signal()
        sp_bit = Signal()
        sp_o_data_strobe = Signal()

        # 12MHz domain
        bitstuff_valid_data = Signal()

        # Keep a Gray counter around to smoothly transition between states
        state_gray = Signal(2)
        state_data = Signal()
        state_sync = Signal()


        tx_pipeline_fsm = FSM()
        m.submodules.tx_pipeline_fsm = DomainRenamer({"sync": "usb"})(tx_pipeline_fsm)

        shifter = EnableInserter(~stall)(ResetInserter(da_reset_shifter | sp_reset_shifter)(TxShifter(width=8)))
        m.submodules.shifter = DomainRenamer({"sync": "usb"})(shifter)

        bitstuff = ResetInserter(da_reset_bitstuff)(TxBitstuffer())
        m.submodules.bitstuff = DomainRenamer({"sync": "usb"})(bitstuff)

        nrzi = TxNRZIEncoder()
        m.submodules.nrzi = DomainRenamer({"sync": "usb_io"})(nrzi)


        m.d.comb += [
            shifter.i_data.eq(self.i_data_payload),
            # Send a data strobe when we're two bits from the end of the sync pulse.
            # This is because the pipeline takes two bit times, and we want to ensure the pipeline
            # has spooled up enough by the time we're there.


            bitstuff.i_data.eq(shifter.o_data),
            stall.eq(bitstuff.o_stall),

            sp_bit.eq(sync_pulse[0]),
            sp_reset_bitstuff.eq(sync_pulse[0]),

            # The shifter has one clock cycle of latency, so reset it
            # one cycle before the end of the sync byte.
            sp_reset_shifter.eq(sync_pulse[1]),

            sp_o_data_strobe.eq(sync_pulse[5]),

            state_data.eq(state_gray[0] & state_gray[1]),
            state_sync.eq(state_gray[0] & ~state_gray[1]),

            self.fit_oe.eq(state_data | state_sync),
            self.fit_dat.eq((state_data & shifter.o_data & ~bitstuff.o_stall) | sp_bit),
            self.o_data_strobe.eq(state_data & shifter.o_get & ~stall & self.i_oe),
        ]

        # If we reset the shifter, then o_empty will go high on the next cycle.
        #

        m.d.usb += [
            # If the shifter runs out of data, percolate the "reset" signal to the
            # shifter, and then down to the bitstuffer.
            # da_reset_shifter.eq(~stall & shifter.o_empty & ~da_stalled_reset),
            # da_stalled_reset.eq(da_reset_shifter),
            # da_reset_bitstuff.eq(~stall & da_reset_shifter),
            bitstuff_valid_data.eq(~stall & shifter.o_get & self.i_oe),
        ]

        tx_pipeline_fsm.act('IDLE',
            If(self.i_oe,
                NextState('SEND_SYNC'),
                NextValue(sync_pulse, 1 << 7),
                NextValue(state_gray, 0b01),
            ).Else(
                NextValue(state_gray, 0b00),
            )
        )

        tx_pipeline_fsm.act('SEND_SYNC',
            NextValue(sync_pulse, sync_pulse >> 1),

            If(sync_pulse[0],
                NextState('SEND_DATA'),
                NextValue(state_gray, 0b11),
            ).Else(
                NextValue(state_gray, 0b01),
            ),
        )

        tx_pipeline_fsm.act('SEND_DATA',
            If(~self.i_oe & shifter.o_empty & ~bitstuff.o_stall,
                If(bitstuff.o_will_stall,
                    NextState('STUFF_LAST_BIT')
                ).Else(
                    NextValue(state_gray, 0b10),
                    NextState('IDLE'),
                )
            ).Else(
                NextValue(state_gray, 0b11),
            ),
        )

        tx_pipeline_fsm.act('STUFF_LAST_BIT',
            NextValue(state_gray, 0b10),
            NextState('IDLE'),
        )

        # 48MHz domain
        # NRZI encoding
        nrzi_dat = Signal()
        nrzi_oe = Signal()

        # Cross the data from the 12MHz domain to the 48MHz domain
        cdc_dat = FFSynchronizer(self.fit_dat, nrzi_dat, o_domain="usb_io", stages=3)
        cdc_oe  = FFSynchronizer(self.fit_oe, nrzi_oe, o_domain="usb_io", stages=3)
        m.submodules += [cdc_dat, cdc_oe]

        m.d.comb += [
            nrzi.i_valid.eq(self.i_bit_strobe),
            nrzi.i_data.eq(nrzi_dat),
            nrzi.i_oe.eq(nrzi_oe),

            self.o_usbp.eq(nrzi.o_usbp),
            self.o_usbn.eq(nrzi.o_usbn),
            self.o_oe.eq(nrzi.o_oe),

        ]

        return m