Example #1
0
File: idle.py Project: zyp/luna
    def elaborate(self, platform):
        m = Module()

        data_word = self.sink.data
        ctrl_word = self.sink.ctrl

        # Capture the previous data word; so we have a record of eight consecutive signals.
        last_word = Past(self.sink.data)
        last_ctrl = Past(self.sink.ctrl)

        # Logical idle descrambles to the raw data value zero; so we only need to validate that
        # the last and current words are both zeroes.
        last_word_was_idle = (last_word == 0) & (last_ctrl == 0)
        current_word_is_idle = (data_word == 0) & (ctrl_word == 0)
        m.d.comb += [
            self.idle_detected.eq(last_word_was_idle & current_word_is_idle)
        ]

        #
        # Handshake condition detector.
        #
        seen_idle = Signal()
        enable_counter = Signal(range(self.RX_CYCLES_REQUIRED + 1))

        with m.If(self.enable):

            # Keep track of how many consecutive cycles we're enabled for; as we must
            # send logical idle for at least 16B in order to complete the Idle handshake.
            with m.If(enable_counter < self.RX_CYCLES_REQUIRED):
                m.d.ss += enable_counter.eq(enable_counter + 1)

            # Keep track of whether we've ever seen eight consecutive cycles of idle.
            with m.If(self.idle_detected):
                m.d.ss += seen_idle.eq(1)

            # Our handshake is complete once we've sent logical idle for at least 16 bytes,
            # and we've seen at least eight byte
            send_condition_met = enable_counter == self.RX_CYCLES_REQUIRED
            m.d.comb += self.idle_handshake_complete.eq(seen_idle
                                                        & send_condition_met)

        # When we're not idle, clear all of our state.
        with m.Else():
            m.d.ss += [enable_counter.eq(0), seen_idle.eq(0)]

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

        data_word = self.sink.data
        ctrl_word = self.sink.ctrl

        # Capture the previous data word; so we have a record of eight consecutive signals.
        last_word = Past(self.sink.data)
        last_ctrl = Past(self.sink.ctrl)

        # Logical idle descrambles to the raw data value zero; so we only need to validate that
        # the last and current words are both zeroes.
        last_word_was_idle = (last_word == 0) & (last_ctrl == 0)
        current_word_is_idle = (data_word == 0) & (ctrl_word == 0)
        m.d.comb += [
            self.idle_detected.eq(last_word_was_idle & current_word_is_idle)
        ]

        return m
Example #3
0
    def elaborate(self, platform):
        m = Module()
        shared = self.shared

        #
        # Pass through signals being routed -to- our pre-mux interfaces.
        #
        for interface in self._interfaces:
            m.d.comb += [

                # CRC and timer shared signals interface.
                interface.data_crc.crc           .eq(shared.data_crc.crc),
                interface.timer.tx_allowed       .eq(shared.timer.tx_allowed),
                interface.timer.tx_timeout       .eq(shared.timer.tx_timeout),
                interface.timer.rx_timeout       .eq(shared.timer.rx_timeout),

                # Detectors.
                shared.handshakes_in             .connect(interface.handshakes_in),
                shared.tokenizer                 .connect(interface.tokenizer),

                # Rx interface.
                shared.rx                        .connect(interface.rx),
                interface.rx_complete            .eq(shared.rx_complete),
                interface.rx_ready_for_response  .eq(shared.rx_ready_for_response),
                interface.rx_invalid             .eq(shared.rx_invalid),
                interface.rx_pid_toggle          .eq(shared.rx_pid_toggle),

                # State signals.
                interface.speed                  .eq(shared.speed),
                interface.active_config          .eq(shared.active_config),
                interface.active_address         .eq(shared.active_address)
            ]

        #
        # Multiplex the signals being routed -from- our pre-mux interface.
        #
        self._multiplex_signals(m,
            when='address_changed',
            multiplex=['address_changed', 'new_address']
        )
        self._multiplex_signals(m,
            when='config_changed',
            multiplex=['config_changed', 'new_config']
        )

        # Connect up our transmit interface.
        m.submodules.tx_mux = tx_mux = OneHotMultiplexer(
            interface_type=USBInStreamInterface,
            mux_signals=('payload',),
            or_signals=('valid', 'first', 'last'),
            pass_signals=('ready',)
        )
        tx_mux.add_interfaces(i.tx for i in self._interfaces)
        m.d.comb += self.shared.tx.connect(tx_mux.output)

        # OR together all of our handshake-generation requests...
        self.or_join_interface_signals(m, lambda interface : interface.handshakes_out.ack)
        self.or_join_interface_signals(m, lambda interface : interface.handshakes_out.nak)
        self.or_join_interface_signals(m, lambda interface : interface.handshakes_out.stall)

        # ... our CRC start signals...
        self.or_join_interface_signals(m, lambda interface : interface.data_crc.start)

        # ... and our timer start signals.
        self.or_join_interface_signals(m, lambda interface : interface.timer.start)

        # Finally, connect up our transmit PID select.
        conditional = m.If

        # We'll connect our PID toggle to whichever interface has a valid transmission going.
        for interface in self._interfaces:
            with conditional(interface.tx.valid | Past(interface.tx.valid)):
                m.d.comb += shared.tx_pid_toggle.eq(interface.tx_pid_toggle)

            conditional = m.Elif

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

        # Buffer our stream inputs here to improve timing.
        stream_in_data = Signal.like(self.sink.data)
        stream_in_ctrl = Signal.like(self.sink.ctrl)
        stream_in_valid = Signal.like(self.sink.valid)
        m.d.sync += [
            stream_in_data.eq(self.sink.data),
            stream_in_ctrl.eq(self.sink.ctrl),
            stream_in_valid.eq(self.sink.valid),
        ]

        # Aliases.
        stream_in = self.sink
        if self._flip_bytes:
            stream_in_data = stream_in.data.rotate_right(
                (self._words_in // 2) * 8)
            stream_in_ctrl = stream_in.ctrl.rotate_right(self._words_in // 2)
        else:
            stream_in_data = stream_in.data
            stream_in_ctrl = stream_in.ctrl

        # If our output domain is the same as our input domain, we'll directly drive our output stream.
        # Otherwise, we'll drive an internal signal; and then cross that into our output domain.
        if self._output_domain == self._input_domain:
            stream_out = self.source
        else:
            stream_out = USBRawSuperSpeedStream()

        # Create proxies that allow us access to the upper and lower halves of our output data stream.
        data_out_halves = Array(
            stream_out.data.word_select(i, self._data_bits_in)
            for i in range(2))
        ctrl_out_halves = Array(
            stream_out.ctrl.word_select(i, self._ctrl_bits_in)
            for i in range(2))

        # Word select -- selects whether we're targeting the upper or lower half of the output word.
        # Toggles every input-domain cycle.
        targeting_upper_half = Signal(reset=1 if self._flip_bytes else 0)
        m.d.sync += targeting_upper_half.eq(~targeting_upper_half)

        # Pass through our data and control every cycle.
        m.d.sync += [
            data_out_halves[targeting_upper_half].eq(stream_in_data),
            ctrl_out_halves[targeting_upper_half].eq(stream_in_ctrl),
        ]

        # Set our valid signal high only if both the current and previously captured word are valid.
        m.d.comb += [
            stream_out.valid.eq(
                stream_in.valid
                & Past(stream_in.valid, domain=self._input_domain))
        ]

        if self._input_domain != self._output_domain:
            in_domain_signals = Cat(stream_out.data, stream_out.ctrl,
                                    stream_out.valid)
            out_domain_signals = Cat(self.source.data, self.source.ctrl,
                                     self.source.valid)

            # Create our async FIFO...
            m.submodules.cdc = fifo = AsyncFIFOBuffered(
                width=len(in_domain_signals),
                depth=8,
                w_domain="sync",
                r_domain=self._output_domain)

            m.d.comb += [
                # ... fill it from our in-domain stream...
                fifo.w_data.eq(in_domain_signals),
                fifo.w_en.eq(targeting_upper_half),

                # ... and output it into our output stream.
                out_domain_signals.eq(fifo.r_data),
                self.source.valid.eq(fifo.r_level > 2),
                fifo.r_en.eq(1),
            ]

        # If our source domain isn't `sync`, translate `sync` to the proper domain name.
        if self._input_domain != "sync":
            m = DomainRenamer({'sync': self._input_domain})(m)

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

        # If we're receiving data from an domain other than our output domain,
        # cross it over nicely.
        if self._input_domain != self._output_domain:
            stream_in = USBRawSuperSpeedStream(payload_words=self._words_in)
            in_domain_signals = Cat(
                self.sink.data,
                self.sink.ctrl,
            )
            out_domain_signals = Cat(
                stream_in.data,
                stream_in.ctrl,
            )

            # Advance our FIFO only ever other cycle.
            advance_fifo = Signal()
            m.d.tx += advance_fifo.eq(~advance_fifo)

            # Create our async FIFO...
            m.submodules.cdc = fifo = AsyncFIFOBuffered(
                width=len(in_domain_signals),
                depth=8,
                w_domain=self._input_domain,
                r_domain="tx")

            m.d.comb += [
                # ... fill it from our in-domain stream...
                fifo.w_data.eq(in_domain_signals),
                fifo.w_en.eq(1),
                self.sink.ready.eq(1),

                # ... and output it into our output stream.
                out_domain_signals.eq(fifo.r_data),
                stream_in.valid.eq(fifo.r_rdy),
                fifo.r_en.eq(advance_fifo),
            ]

        # Otherwise, use our data-stream directly.
        else:
            stream_in = self.sink
            m.d.comb += self.sink.ready.eq(1)

        # Word select -- selects whether we're targeting the upper or lower half of the input word.
        # Toggles every input-domain cycle.
        next_half_targeted = Signal()
        targeting_upper_half = Signal()

        # If our data has just changed, we should always be targeting the upper word.
        # This "locks" us to the data's changes.
        data_changed = stream_in.data != Past(stream_in.data, domain="tx")
        ctrl_changed = stream_in.ctrl != Past(stream_in.ctrl, domain="tx")
        with m.If(data_changed | ctrl_changed):
            m.d.comb += targeting_upper_half.eq(1 if self._flip_bytes else 0)
            m.d.tx += next_half_targeted.eq(0 if self._flip_bytes else 1)
        with m.Else():
            m.d.comb += targeting_upper_half.eq(next_half_targeted)
            m.d.tx += next_half_targeted.eq(~next_half_targeted)

        # If we're flipping the bytes in our output stream (e.g. to maintain endianness),
        # create a flipped version; otherwise, use our output stream directly.
        stream_out = self.source

        # Create proxies that allow us access to the upper and lower halves of our input data stream.
        data_in_halves = Array(
            stream_in.data.word_select(i,
                                       len(stream_in.data) // 2)
            for i in range(2))
        ctrl_in_halves = Array(
            stream_in.ctrl.word_select(i,
                                       len(stream_in.ctrl) // 2)
            for i in range(2))

        # Pass through our data and control every cycle.
        # This is registered to loosen timing.
        if self._flip_bytes:
            stream_out_data = data_in_halves[targeting_upper_half]
            stream_out_ctrl = ctrl_in_halves[targeting_upper_half]
            m.d.tx += [
                stream_out.data.eq(
                    stream_out_data.rotate_right(len(stream_out_data) // 2)),
                stream_out.ctrl.eq(
                    stream_out_ctrl.rotate_right(len(stream_out_ctrl) // 2)),
                stream_out.valid.eq(1),
            ]
        else:
            m.d.tx += [
                stream_out.data.eq(data_in_halves[targeting_upper_half]),
                stream_out.ctrl.eq(ctrl_in_halves[targeting_upper_half]),
                stream_out.valid.eq(1),
            ]

        # If our output domain isn't `sync`, translate `sync` to the proper domain name.
        if self._output_domain != "tx":
            m = DomainRenamer({'tx': self._output_domain})(m)

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

        # Aliases
        stream_in = self.sink
        stream_out = self.source

        # Values from previous cycles.
        previous_data = Past(stream_in.data, domain="ss")
        previous_ctrl = Past(stream_in.ctrl, domain="ss")

        # Alignment register: stores how many words the data must be shifted by in order to
        # have correctly aligned data.
        shift_to_apply = Signal(range(4))

        #
        # Alignment shift register.
        #
        # To align our data, we'll create a conglomeration of two consecutive words;
        # and then select the chunk between those words that has the alignment correct.
        # (These words should always be in chronological order; so we'll need different
        # orders for big endian and little endian output.)
        if self._is_big_endian:
            data = Cat(stream_in.data, previous_data)
            ctrl = Cat(stream_in.ctrl, previous_ctrl)
        else:
            data = Cat(previous_data, stream_in.data)
            ctrl = Cat(previous_ctrl, stream_in.ctrl)

        # Create two multiplexers that allow us to select from each of our four possible alignments.
        shifted_data_slices = Array(data[8 * i:] for i in range(4))
        shifted_ctrl_slices = Array(ctrl[i:] for i in range(4))

        #
        # Alignment detection.
        #

        # Our aligner is simple: we'll search for the start of a TS1/TS2 training set; which we know
        # starts with a burst of four consecutive commas.
        four_comma_data = Repl(COM.value_const(), 4)
        four_comma_ctrl = Repl(COM.ctrl_const(), 4)

        # We'll check each possible alignment to see if it would produce a valid start-of-TS1/TS2.
        possible_alignments = len(shifted_data_slices)
        for i in range(possible_alignments):
            data_matches = (shifted_data_slices[i][0:32] == four_comma_data)
            ctrl_matches = (shifted_ctrl_slices[i][0:4] == four_comma_ctrl)

            # If it would, we'll accept that as our alignment going forward.
            with m.If(data_matches & ctrl_matches):
                m.d.ss += shift_to_apply.eq(i)

        #
        # Alignment application.
        #

        # Grab the shifted data/ctrl associated with our alignment.
        m.d.ss += [
            stream_out.data.eq(shifted_data_slices[shift_to_apply]),
            stream_out.ctrl.eq(shifted_ctrl_slices[shift_to_apply]),

            # We're part of a chain that always produces data (currently);
            # so for now, we'll always indicate our data is valid. This may
            # need to be changed if we add in nicer CTC.
            stream_out.valid.eq(1),

            # Debug output.
            self.alignment_offset.eq(shift_to_apply)
        ]

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

        # Aliases
        stream_in = self.sink
        stream_out = self.source

        # Values from previous cycles.
        previous_data = Past(stream_in.data, domain="ss")
        previous_ctrl = Past(stream_in.ctrl, domain="ss")

        # Alignment register: stores how many words the data must be shifted by in order to
        # have correctly aligned data.
        shift_to_apply = Signal(range(4))

        #
        # Alignment detector.
        #
        if self._is_big_endian:
            alignment_byte_precedence = reversed(range(4))
        else:
            alignment_byte_precedence = range(4)

        # Apply new alignments only if we're the first seen COM (since USB3 packets can contain multiple
        # consecutive COMs), and if alignment is enabled.
        following_data_byte = (previous_ctrl == 0)
        with m.If(self.align & following_data_byte):

            # Detect any alignment markers by looking for a COM signal in any of the four positions.
            for i in alignment_byte_precedence:
                data_matches = (stream_in.data.word_select(i, 8) == COM.value)
                ctrl_matches = stream_in.ctrl[i]

                # If the current position has a comma in it, mark this as our alignment position;
                # and compute how many times we'll need to rotate our data _right_ in order to achieve
                # proper alignment.
                with m.If(data_matches & ctrl_matches):
                    if self._is_big_endian:
                        m.d.ss += [
                            shift_to_apply.eq(3 - i),
                            stream_out.valid.eq(shift_to_apply == (3 - i))
                        ]
                    else:
                        m.d.ss += [
                            shift_to_apply.eq(i),
                            stream_out.valid.eq(shift_to_apply == i)
                        ]

        #
        # Aligner.
        #

        # To align our data, we'll create a conglomeration of two consecutive words;
        # and then select the chunk between those words that has the alignment correct.
        # (These words should always be in chronological order; so we'll need different
        # orders for big endian and little endian output.)
        if self._is_big_endian:
            data = Cat(stream_in.data, previous_data)
            ctrl = Cat(stream_in.ctrl, previous_ctrl)
        else:
            data = Cat(previous_data, stream_in.data)
            ctrl = Cat(previous_ctrl, stream_in.ctrl)

        # Create two multiplexers that allow us to select from each of our four possible
        # alignments...
        shifted_data_slices = Array(data[8 * i:] for i in range(4))
        shifted_ctrl_slices = Array(ctrl[i:] for i in range(4))

        # ... and output our data accordingly.
        m.d.ss += [
            stream_out.data.eq(shifted_data_slices[shift_to_apply]),
            stream_out.ctrl.eq(shifted_ctrl_slices[shift_to_apply]),

            # Debug output.
            self.alignment_offset.eq(shift_to_apply)
        ]

        return m
Example #8
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 #9
0
    def elaborate(self, platform):
        m = Module()

        # Mark ourselves as always ready for new data.
        m.d.comb += self.sink.ready.eq(1)

        # Values from previous cycles.
        previous_data = Signal.like(self.sink.data)
        previous_ctrl = Signal.like(self.sink.ctrl)
        with m.If(self.sink.valid):
            m.d.ss += [
                previous_data.eq(self.sink.data),
                previous_ctrl.eq(self.sink.ctrl),
            ]

        previous_valid = Past(self.sink.valid, domain="ss")

        # Alignment register: stores how many words the data must be shifted by in order to
        # have correctly aligned data.
        shift_to_apply = Signal(range(4))

        #
        # Alignment shift register.
        #
        # To align our data, we'll create a conglomeration of two consecutive words;
        # and then select the chunk between those words that has the alignment correct.
        data = Cat(previous_data, self.sink.data)
        ctrl = Cat(previous_ctrl, self.sink.ctrl)

        # Create two multiplexers that allow us to select from each of our four possible alignments.
        shifted_data_slices = Array(data[8 * i:] for i in range(4))
        shifted_ctrl_slices = Array(ctrl[i:] for i in range(4))

        #
        # Alignment detection.
        #

        # Our aligner is simple: we'll search for the start of a TS1/TS2 training set; which we know
        # starts with a burst of four consecutive commas.
        four_comma_data = Repl(COM.value_const(), 4)
        four_comma_ctrl = Repl(COM.ctrl_const(), 4)

        # We'll check each possible alignment to see if it would produce a valid start-of-TS1/TS2;
        # ignoring any words not marked as valid.
        with m.If(self.sink.valid):
            possible_alignments = len(shifted_data_slices)
            for i in range(possible_alignments):
                data_matches = (
                    shifted_data_slices[i][0:32] == four_comma_data)
                ctrl_matches = (shifted_ctrl_slices[i][0:4] == four_comma_ctrl)

                # If it would, we'll accept that as our alignment going forward.
                with m.If(data_matches & ctrl_matches):
                    m.d.ss += shift_to_apply.eq(i)

        #
        # Alignment application.
        #

        # Grab the shifted data/ctrl associated with our alignment.
        m.d.ss += [
            self.source.data.eq(shifted_data_slices[shift_to_apply]),
            self.source.ctrl.eq(shifted_ctrl_slices[shift_to_apply]),
            self.source.valid.eq(self.sink.valid),
            self.alignment_offset.eq(shift_to_apply),
        ]

        return m