Пример #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):
        m  = Module()
        m.submodules.ila = self.ila

        transaction_start = Rose(self.spi.cs)

        # Connect up our SPI transciever to our public interface.
        interface = SPIDeviceInterface(
            word_size=self.bits_per_word,
            clock_polarity=self.clock_polarity,
            clock_phase=self.clock_phase
        )
        m.submodules.spi = interface
        m.d.comb += [
            interface.spi      .connect(self.spi),

            # Always output the captured sample.
            interface.word_out .eq(self.ila.captured_sample)
        ]

        # Count where we are in the current transmission.
        current_sample_number = Signal(range(0, self.ila.sample_depth))

        # Our first piece of data is latched in when the transaction
        # starts, so we'll move on to sample #1.
        with m.If(self.spi.cs):
            with m.If(transaction_start):
                m.d.sync += current_sample_number.eq(1)

            # From then on, we'll move to the next sample whenever we're finished
            # scanning out a word (and thus our current samples are latched in).
            with m.Elif(interface.word_complete):
                m.d.sync += current_sample_number.eq(current_sample_number + 1)

        # Whenever CS is low, we should be providing the very first sample,
        # so reset our sample counter to 0.
        with m.Else():
            m.d.sync += current_sample_number.eq(0)


        # Ensure our ILA module outputs the right sample.
        m.d.sync += [
            self.ila.captured_sample_number .eq(current_sample_number)
        ]

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

        serdes_output = Signal(4)
        real_bitslip = Signal()

        bitslip = Rose(self.bitslip, domain=self.word_x2_domain)

        with m.If(bitslip):
            m.d[self.word_x2_domain] += real_bitslip.eq(~real_bitslip)

        lower_upper = Signal()
        with m.If(bitslip & ~real_bitslip):
            m.d[self.word_x2_domain] += lower_upper.eq(lower_upper)
        with m.Else():
            m.d[self.word_x2_domain] += lower_upper.eq(~lower_upper)

        with m.If(lower_upper):
            m.d[self.word_x2_domain] += self.output[0:4].eq(serdes_output)
        with m.Else():
            m.d[self.word_x2_domain] += self.output[4:8].eq(serdes_output)

        m.submodules.iddr = Instance(
            "IDDRX2E",
            i_D=self.input,
            i_ECLK=ClockSignal(self.ddr_domain),
            i_SCLK=ClockSignal(),
            i_RST=ResetSignal(),
            i_ALIGNWD=(bitslip & real_bitslip),
            o_Q0=serdes_output[0],
            o_Q1=serdes_output[1],
            o_Q2=serdes_output[2],
            o_Q3=serdes_output[3],
        )

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

        # Create an in-domain version of our square-wave-detector signal.
        present = synchronize(m, self.signaling_detected)

        # Figure out how large of a counter we're going to need...
        burst_cycles_min = _ns_to_cycles(self._clock_frequency,
                                         self._pattern.burst.t_min)
        repeat_cycles_min = _ns_to_cycles(self._clock_frequency,
                                          self._pattern.repeat.t_min)
        burst_cycles_max = _ns_to_cycles(self._clock_frequency,
                                         self._pattern.burst.t_max)
        repeat_cycles_max = _ns_to_cycles(self._clock_frequency,
                                          self._pattern.repeat.t_max)
        counter_max = max(burst_cycles_max, repeat_cycles_max)

        # ... and create our counter.
        count = Signal(range(0, counter_max + 1))
        m.d.ss += count.eq(count + 1)

        # Keep track of whether our previous iteration matched; as we're interested in detecting
        # sequences of two correct LFPS cycles in a row.
        last_iteration_matched = Signal()

        #
        # Detector state machine.
        #
        with m.FSM(domain="ss"):

            # WAIT_FOR_NEXT_BURST -- we're not currently in a measurement; but are waiting for a
            # burst to begin, so we can perform a full measurement.
            with m.State("WAIT_FOR_NEXT_BURST"):
                m.d.ss += last_iteration_matched.eq(0)

                # If we've just seen the start of a burst, start measuring it.
                with m.If(Rose(present)):
                    m.d.ss += count.eq(1),
                    m.next = "MEASURE_BURST"

            # MEASURE_BURST -- we're seeing something we believe to be a burst; and measuring its length.
            with m.State("MEASURE_BURST"):

                # Failing case: if our counter has gone longer than our maximum burst time, this isn't
                # a relevant burst. We'll wait for the next one.
                with m.If(count == burst_cycles_max):
                    m.next = 'WAIT_FOR_NEXT_BURST'

                # Once our burst is over, we'll need to decide if the burst matches our pattern.
                with m.If(~present):

                    # Failing case: if our burst is over, but we've not yet reached our minimum burst time,
                    # then this isn't a relevant burst. We'll wait for the next one.
                    with m.If(count < burst_cycles_min):
                        m.next = 'WAIT_FOR_NEXT_BURST'

                    # If our burst ended within a reasonable span, move on to measuring the spacing between
                    # bursts ("repeat interval").
                    with m.Else():
                        m.next = "MEASURE_REPEAT"

            # MEASURE_REPEAT -- we've just finished seeing a burst; and now we're measuring the gap between
            # successive bursts, which the USB specification calls the "repeat interval". [USB3.2r1: Fig 6-32]
            with m.State("MEASURE_REPEAT"):

                # Failing case: if our counter has gone longer than our maximum burst time, this isn't
                # a relevant burst. We'll wait for the next one.
                with m.If(count == repeat_cycles_max):
                    m.next = 'WAIT_FOR_NEXT_BURST'

                # Once we see another potential burst, we'll start our detection back from the top.
                with m.If(present):
                    m.d.ss += count.eq(1)
                    m.next = 'MEASURE_BURST'

                    # If this lasted for a reasonable repeat interval, we've seen a correct burst!
                    with m.If(count >= repeat_cycles_min):

                        # Mark this as a correct iteration, and if the previous iteration was also
                        # a correct one, indicate that we've detected our output.
                        m.d.ss += last_iteration_matched.eq(1)
                        m.d.comb += self.detect.eq(last_iteration_matched)

                    with m.Else():
                        m.d.ss += last_iteration_matched.eq(0)

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

        # Create the component parts of our ULPI interfacing hardware.
        m.submodules.register_window = register_window = ULPIRegisterWindow()
        m.submodules.control_translator = control_translator = ULPIControlTranslator(
            register_window=register_window)
        m.submodules.rxevent_decoder = rxevent_decoder = ULPIRxEventDecoder(
            ulpi_bus=self.ulpi)
        m.submodules.transmit_translator = transmit_translator = ULPITransmitTranslator(
        )

        # If we're choosing to honor any registers defined in the platform file, apply those
        # before continuing with elaboration.
        if self.use_platform_registers and hasattr(platform,
                                                   'ulpi_extra_registers'):
            for address, value in platform.ulpi_extra_registers.items():
                self.add_extra_register(address, value)

        # Add any extra registers provided by the user to our control translator.
        for address, values in self._extra_registers.items():
            control_translator.add_composite_register(
                m, address, values['value'], reset_value=values['default'])

        # Keep track of when any of our components are busy
        any_busy = \
            register_window.busy     | \
            transmit_translator.busy | \
            control_translator.busy  | \
            self.ulpi.dir

        # If we're handling ULPI clocking, do so.
        if self.handle_clocking:

            # We can't currently handle bidirectional clock lines, as we don't know if they
            # should be used in input or output modes.
            if hasattr(self.ulpi.clk, 'oe'):
                raise TypeError(
                    "ULPI records with bidirectional clock lines require manual handling."
                )

            # Just Input (TM) and Just Output (TM) clocks are simpler: we know how to drive them.
            elif hasattr(self.ulpi.clk, 'o'):
                m.d.comb += self.ulpi.clk.eq(ClockSignal('usb'))
            elif hasattr(self.ulpi.clk, 'i'):
                m.d.comb += ClockSignal('usb').eq(self.ulpi.clk)

            # Clocks that don't seem to be I/O pins aren't what we're expecting; fail out.
            else:
                raise TypeError(f"ULPI `clk` was an unexpected type {type(self.ulpi.clk)}." \
                    " You may need to handle clocking manually.")

        # Connect our ULPI control signals to each of our subcomponents.
        m.d.comb += [

            # Drive the bus whenever the target PHY isn't.
            self.ulpi.data.oe.eq(~self.ulpi.dir),

            # Generate our busy signal.
            self.busy.eq(any_busy),

            # Connect up our clock and reset signals.
            self.ulpi.rst.eq(ResetSignal("usb")),

            # Connect our data inputs to the event decoder.
            # Note that the event decoder is purely passive.
            rxevent_decoder.register_operation_in_progress.eq(
                register_window.busy),
            self.last_rx_command.eq(rxevent_decoder.last_rx_command),

            # Connect our inputs to our transmit translator.
            transmit_translator.ulpi_nxt.eq(self.ulpi.nxt),
            transmit_translator.op_mode.eq(self.op_mode),
            transmit_translator.bus_idle.eq(~control_translator.busy
                                            & ~self.ulpi.dir),
            transmit_translator.tx_data.eq(self.tx_data),
            transmit_translator.tx_valid.eq(self.tx_valid),
            self.tx_ready.eq(transmit_translator.tx_ready),

            # Connect our inputs to our control translator / register window.
            control_translator.bus_idle.eq(~transmit_translator.busy),
            register_window.ulpi_data_in.eq(self.ulpi.data.i),
            register_window.ulpi_dir.eq(self.ulpi.dir),
            register_window.ulpi_next.eq(self.ulpi.nxt),
        ]

        # Control our the source of our ULPI data output.
        # If a transmit request is active, prioritize that over
        # any register reads/writes.
        with m.If(transmit_translator.ulpi_out_req):
            m.d.comb += [
                self.ulpi.data.o.eq(transmit_translator.ulpi_data_out),
                self.ulpi.stp.eq(transmit_translator.ulpi_stp)
            ]
        # Otherwise, yield control to the register handler.
        # This is a slight optimization: since it properly generates NOPs
        # while not in use, we can let it handle idle, as well, saving a mux.
        with m.Else():
            m.d.comb += [
                self.ulpi.data.o.eq(register_window.ulpi_data_out),
                self.ulpi.stp.eq(register_window.ulpi_stop)
            ]

        # Connect our RxEvent status signals from our RxEvent decoder.
        for signal_name, _ in self.RXEVENT_STATUS_SIGNALS:
            signal = getattr(rxevent_decoder, signal_name)
            m.d.comb += self.__dict__[signal_name].eq(signal)

        # Connect our control signals through the control translator.
        for signal_name, _ in self.CONTROL_SIGNALS:
            signal = getattr(control_translator, signal_name)
            m.d.comb += signal.eq(self.__dict__[signal_name])

        # RxActive handler:
        # A transmission starts when DIR goes high with NXT, or when an RxEvent indicates
        # a switch from RxActive = 0 to RxActive = 1. A transmission stops when DIR drops low,
        # or when the RxEvent RxActive bit drops from 1 to 0, or an error occurs.A
        dir_rising_edge = Rose(self.ulpi.dir.i, domain="usb")
        dir_based_start = dir_rising_edge & self.ulpi.nxt

        with m.If(~self.ulpi.dir | rxevent_decoder.rx_stop):
            # TODO: this should probably also trigger if RxError
            m.d.usb += self.rx_active.eq(0)
        with m.Elif(dir_based_start | rxevent_decoder.rx_start):
            m.d.usb += self.rx_active.eq(1)

        # Data-out: we'll connect this almost direct through from our ULPI
        # interface, as it's essentially the same as in the UTMI spec. We'll
        # add a one cycle processing delay so it matches the rest of our signals.

        # RxValid: equivalent to NXT whenever a Rx is active.
        m.d.usb += [
            self.rx_data.eq(self.ulpi.data.i),
            self.rx_valid.eq(self.ulpi.nxt & self.rx_active)
        ]

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

        # Create the component parts of our ULPI interfacing hardware.
        m.submodules.register_window    = register_window    = ULPIRegisterWindow()
        m.submodules.control_translator = control_translator = ULPIControlTranslator(register_window=register_window)
        m.submodules.rxevent_decoder    = rxevent_decoder    = ULPIRxEventDecoder(ulpi_bus=self.ulpi)

        # If we're choosing to honor any registers defined in the platform file, apply those
        # before continuing with elaboration.
        if self.use_platform_registers and hasattr(platform, 'ulpi_extra_registers'):
            for address, value in platform.ulpi_extra_registers.items():
                self.add_extra_register(address, value)

        # Add any extra registers provided by the user to our control translator.
        for address, values in self._extra_registers.items():
            control_translator.add_composite_register(m, address, values['value'], reset_value=values['default'])

        # Connect our ULPI control signals to each of our subcomponents.
        m.d.comb += [

            # Drive the bus whenever the target PHY isn't.
            self.ulpi.data.oe            .eq(~self.ulpi.dir),

            # Generate our busy signal.
            self.busy                    .eq(register_window.busy),

            # Connect up our clock and reset signals.
            self.ulpi.clk                .eq(ClockSignal("ulpi")),
            self.ulpi.rst                .eq(ResetSignal("ulpi")),

            # Connect our data inputs to the event decoder.
            # Note that the event decoder is purely passive.
            rxevent_decoder.register_operation_in_progress.eq(register_window.busy),
            self.last_rx_command          .eq(rxevent_decoder.last_rx_command),

            # Connect our signals to our register window.
            register_window.ulpi_data_in  .eq(self.ulpi.data.i),
            register_window.ulpi_dir      .eq(self.ulpi.dir),
            register_window.ulpi_next     .eq(self.ulpi.nxt),
            self.ulpi.data.o              .eq(register_window.ulpi_data_out),
            self.ulpi.stp                 .eq(register_window.ulpi_stop),

        ]

        # Connect our RxEvent status signals from our RxEvent decoder.
        for signal_name in self.RXEVENT_STATUS_SIGNALS:
            signal = getattr(rxevent_decoder, signal_name)
            m.d.comb += self.__dict__[signal_name].eq(signal)

        # Connect our control signals through the control translator.
        for signal_name, _ in self.CONTROL_SIGNALS:
            signal = getattr(control_translator, signal_name)
            m.d.comb += signal.eq(self.__dict__[signal_name])


        # RxActive handler:
        # A transmission starts when DIR goes high with NXT, or when an RxEvent indicates
        # a switch from RxActive = 0 to RxActive = 1. A transmission stops when DIR drops low,
        # or when the RxEvent RxActive bit drops from 1 to 0, or an error occurs.
        dir_rising_edge = Rose(self.ulpi.dir, domain='ulpi')
        dir_based_start = dir_rising_edge & self.ulpi.nxt

        with m.If(~self.ulpi.dir | rxevent_decoder.rx_stop):
            # TODO: this should probably also trigger if RxError
            m.d.ulpi += self.rx_active.eq(0)
        with m.Elif(dir_based_start | rxevent_decoder.rx_start):
            m.d.ulpi += self.rx_active.eq(1)


        # Data-out: we'll connect this almost direct through from our ULPI
        # interface, as it's essentially the same as in the UTMI spec. We'll
        # add a one cycle processing delay so it matches the rest of our signals.

        # RxValid: equivalent to NXT whenever a Rx is active.
        m.d.ulpi += [
            self.rx_data   .eq(self.ulpi.data.i),
            self.rx_valid  .eq(self.ulpi.nxt & self.rx_active)
        ]


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

        #
        # General state signals.
        #

        # Our line state is always taken directly from D- and D+.
        m.d.comb += self.line_state.eq(Cat(self._io.d_n.i, self._io.d_p.i))

        # If we have a ``vbus_valid`` indication, use it to drive our ``vbus_valid``
        # signal. Otherwise, we'll pretend ``vbus_valid`` is always true, for compatibility.
        if hasattr(self._io, 'vbus_valid'):
            m.d.comb += [
                self.vbus_valid.eq(self._io.vbus_valid),
                self.session_end.eq(~self._io.vbus_valid)
            ]
        else:
            m.d.comb += [self.vbus_valid.eq(1), self.session_end.eq(0)]

        #
        # General control signals.
        #

        # If we have a pullup signal, drive it based on ``term_select``.
        if hasattr(self._io, 'pullup'):
            m.d.comb += self._io.pullup.eq(self.term_select)

        # If we have a pulldown signal, drive it based on our pulldown controls.
        if hasattr(self._io, 'pulldown'):
            m.d.comb += self._io.pullup.eq(self.dm_pulldown | self.dp_pulldown)

        #
        # Transmitter
        #
        in_normal_mode = (self.op_mode == self.OP_MODE_NORMAL)
        in_non_encoding_mode = (self.op_mode == self.OP_MODE_NO_ENCODING)

        m.submodules.transmitter = transmitter = TxPipeline()

        # When we're in normal mode, we'll drive the USB bus with our standard USB transmitter data.
        with m.If(in_normal_mode):
            m.d.comb += [

                # UTMI Transmit data.
                transmitter.i_data_payload.eq(self.tx_data),
                transmitter.i_oe.eq(self.tx_valid),
                self.tx_ready.eq(transmitter.o_data_strobe),

                # USB output.
                self._io.d_p.o.eq(transmitter.o_usbp),
                self._io.d_n.o.eq(transmitter.o_usbn),

                # USB tri-state control.
                self._io.d_p.oe.eq(transmitter.o_oe),
                self._io.d_n.oe.eq(transmitter.o_oe),
            ]

        # When we're in non-encoding mode ("disable bitstuff and NRZI"),
        # we'll output to D+ and D- directly when tx_valid is true.
        with m.Elif(in_non_encoding_mode):
            m.d.comb += [
                # USB output.
                self._io.d_p.o.eq(self.tx_data),
                self._io.d_n.o.eq(~self.tx_data),

                # USB tri-state control.
                self._io.d_p.oe.eq(self.tx_valid),
                self._io.d_n.oe.eq(self.tx_valid)
            ]

        # When we're in other modes (non-driving or invalid), we'll not output at all.
        # This block does nothing, as signals default to zero, but it makes the intention obvious.
        with m.Else():
            m.d.comb += [
                self._io.d_p.oe.eq(0),
                self._io.d_n.oe.eq(0),
            ]

        # Generate our USB clock strobe, which should pulse at 12MHz.
        m.d.usb_io += transmitter.i_bit_strobe.eq(
            Rose(ClockSignal("usb"), domain="usb_io"))

        #
        # Receiver
        #
        m.submodules.receiver = receiver = RxPipeline()
        m.d.comb += [

            # We'll listen for packets on D+ and D- _whenever we're not transmitting._.
            # (If we listen while we're transmitting, we'll hear our own packets.)
            receiver.i_usbp.eq(self._io.d_p & ~transmitter.o_oe),
            receiver.i_usbn.eq(self._io.d_n & ~transmitter.o_oe),
            self.rx_data.eq(receiver.o_data_payload),
            self.rx_valid.eq(receiver.o_data_strobe
                             & receiver.o_pkt_in_progress),
            self.rx_active.eq(receiver.o_pkt_in_progress),
            self.rx_error.eq(receiver.o_receive_error)
        ]
        m.d.usb += self.rx_complete.eq(receiver.o_pkt_end)

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

        # Partner detection is indicated by the value `011` being present on RX_STATUS
        # after a detection completes.
        PARTNER_PRESENT_STATUS = 0b011

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

            # IDLE_P2 -- our post-startup state; represents when we're IDLE but in P2.
            # This is typically only seen at board startup.
            with m.State("IDLE_P2"):
                m.d.comb += self.power_state.eq(2)

                with m.If(self.request_detection):
                    m.next = "PERFORM_DETECT"

            # PERFORM_DETECT -- we're asking our PHY to perform the core of our detection,
            # and waiting for that detection to complete.
            with m.State("PERFORM_DETECT"):

                # Per [TUSB1310A, 5.3.5.2], we should hold our detection control high until
                # PHY_STATUS pulses high; when we'll get the results of our detection.
                m.d.comb += [
                    self.power_state.eq(2),
                    self.detection_control.eq(1)
                ]

                # When we see PHY_STATUS strobe high, we know our result is in RX_STATUS. Since
                # both PHY_STATUS and RX_STATUS are geared down, we'll have to check both halves.
                for i in range(2):

                    # When our detection is complete...
                    with m.If(self.phy_status[i]):

                        # ... capture the results, but don't mark ourselves as complete, yet, as we're
                        # still in P2. We'll need to move to operational state.
                        m.d.ss += self.partner_present.eq(
                            self._rx_status[i] == PARTNER_PRESENT_STATUS)
                        m.next = "MOVE_TO_P0"

            # MOVE_TO_P0 -- we've completed a detection, and now are ready to move (back) into our
            # operational state.
            with m.State("MOVE_TO_P0"):

                # Ask the PHY to put us back down in P0.
                m.d.comb += self.power_state.eq(0)

                # Once the PHY indicates it's put us into the relevant power state, we're done.
                # We can now broadcast our result.
                with m.If(self.phy_status != 0):
                    m.d.comb += self.new_result.eq(1)
                    m.next = "IDLE_P0"

            # IDLE_P0 -- our normal operational state; usually reached after at least one detection
            # has completed successfully. We'll wait until another detection is requested.
            with m.State("IDLE_P0"):
                m.d.comb += self.power_state.eq(0)

                # We can only perform detections from P2; so, when the user requests a detection, we'll
                # need to move back to P2.
                with m.If(Rose(self.request_detection)):
                    m.next = "MOVE_TO_P2"

            # MOVE_TO_P2 -- our user has requested a detection, which we can only perform from P2.
            # Accordingly, we'll move to P2, and -then- perform our detection.
            with m.State("MOVE_TO_P2"):

                # Ask the PHY to put us into P2.
                m.d.comb += self.power_state.eq(2)

                # Once the PHY indicates it's put us into the relevant power state, we can begin
                # our link partner detection.
                with m.If(self.phy_status != 0):
                    m.next = "PERFORM_DETECT"

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

        m.submodules.bridge = self._bridge

        tck = 1 / (2 * self._sys_clk_freq)
        nphases = 2
        databits = len(self.pads.dq.io)

        burstdet_reg = Signal(databits // 8, reset_less=True)
        m.d.comb += self.burstdet.r_data.eq(burstdet_reg)

        # Burstdet clear
        with m.If(self.burstdet.w_stb):
            m.d.sync += burstdet_reg.eq(0)

        # Init -------------------------------------------------------------------------------------
        m.submodules.init = init = ECP5DDRPHYInit()

        # Parameters -------------------------------------------------------------------------------
        cl, cwl = get_cl_cw("DDR3", tck)
        cl_sys_latency = get_sys_latency(nphases, cl)
        cwl_sys_latency = get_sys_latency(nphases, cwl)

        # DFI Interface ----------------------------------------------------------------------------
        dfi = self.dfi

        bl8_chunk = Signal()

        # Clock --------------------------------------------------------------------------------
        m.d.comb += [
            self.pads.clk.o_clk.eq(ClockSignal("dramsync")),
            self.pads.clk.o_fclk.eq(ClockSignal("sync2x")),
        ]
        for i in range(len(self.pads.clk.o0)):
            m.d.comb += [
                self.pads.clk.o0[i].eq(0),
                self.pads.clk.o1[i].eq(1),
                self.pads.clk.o2[i].eq(0),
                self.pads.clk.o3[i].eq(1),
            ]

        # Addresses and Commands ---------------------------------------------------------------
        m.d.comb += [
            self.pads.a.o_clk.eq(ClockSignal("dramsync")),
            self.pads.a.o_fclk.eq(ClockSignal("sync2x")),
            self.pads.ba.o_clk.eq(ClockSignal("dramsync")),
            self.pads.ba.o_fclk.eq(ClockSignal("sync2x")),
        ]
        for i in range(len(self.pads.a.o0)):
            m.d.comb += [
                self.pads.a.o0[i].eq(dfi.phases[0].address[i]),
                self.pads.a.o1[i].eq(dfi.phases[0].address[i]),
                self.pads.a.o2[i].eq(dfi.phases[1].address[i]),
                self.pads.a.o3[i].eq(dfi.phases[1].address[i]),
            ]
        for i in range(len(self.pads.ba.o0)):
            m.d.comb += [
                self.pads.ba.o0[i].eq(dfi.phases[0].bank[i]),
                self.pads.ba.o1[i].eq(dfi.phases[0].bank[i]),
                self.pads.ba.o2[i].eq(dfi.phases[1].bank[i]),
                self.pads.ba.o3[i].eq(dfi.phases[1].bank[i]),
            ]

        # Control pins
        controls = ["ras", "cas", "we", "clk_en", "odt"]
        if hasattr(self.pads, "reset"):
            controls.append("reset")
        if hasattr(self.pads, "cs"):
            controls.append("cs")
        for name in controls:
            m.d.comb += [
                getattr(self.pads, name).o_clk.eq(ClockSignal("dramsync")),
                getattr(self.pads, name).o_fclk.eq(ClockSignal("sync2x")),
            ]
            for i in range(len(getattr(self.pads, name).o0)):
                m.d.comb += [
                    getattr(self.pads,
                            name).o0[i].eq(getattr(dfi.phases[0], name)[i]),
                    getattr(self.pads,
                            name).o1[i].eq(getattr(dfi.phases[0], name)[i]),
                    getattr(self.pads,
                            name).o2[i].eq(getattr(dfi.phases[1], name)[i]),
                    getattr(self.pads,
                            name).o3[i].eq(getattr(dfi.phases[1], name)[i]),
                ]

        # DQ ---------------------------------------------------------------------------------------
        dq_oe = Signal()
        dqs_re = Signal()
        dqs_oe = Signal()
        dqs_postamble = Signal()
        dqs_preamble = Signal()
        for i in range(databits // 8):
            # DQSBUFM
            dqs_i = Signal()
            dqsr90 = Signal()
            dqsw270 = Signal()
            dqsw = Signal()
            rdpntr = Signal(3)
            wrpntr = Signal(3)
            burstdet = Signal()
            datavalid = Signal()
            datavalid_prev = Signal()
            m.d.sync += datavalid_prev.eq(datavalid)

            dqsbufm_manager = _DQSBUFMSettingManager(self.rdly[i])
            setattr(m.submodules, f"dqsbufm_manager{i}", dqsbufm_manager)

            m.submodules += Instance(
                "DQSBUFM",
                p_DQS_LI_DEL_ADJ="MINUS",
                p_DQS_LI_DEL_VAL=1,
                p_DQS_LO_DEL_ADJ="MINUS",
                p_DQS_LO_DEL_VAL=4,

                # Delay
                i_DYNDELAY0=0,
                i_DYNDELAY1=0,
                i_DYNDELAY2=0,
                i_DYNDELAY3=0,
                i_DYNDELAY4=0,
                i_DYNDELAY5=0,
                i_DYNDELAY6=0,
                i_DYNDELAY7=0,

                # Clocks / Reset
                i_SCLK=ClockSignal("sync"),
                i_ECLK=ClockSignal("sync2x"),
                i_RST=ResetSignal("dramsync"),
                i_DDRDEL=init.delay,
                i_PAUSE=init.pause | dqsbufm_manager.pause,

                # Control
                # Assert LOADNs to use DDRDEL control
                i_RDLOADN=0,
                i_RDMOVE=0,
                i_RDDIRECTION=1,
                i_WRLOADN=0,
                i_WRMOVE=0,
                i_WRDIRECTION=1,

                # Reads (generate shifted DQS clock for reads)
                i_READ0=dqs_re,
                i_READ1=dqs_re,
                i_READCLKSEL0=dqsbufm_manager.readclksel[0],
                i_READCLKSEL1=dqsbufm_manager.readclksel[1],
                i_READCLKSEL2=dqsbufm_manager.readclksel[2],
                i_DQSI=dqs_i,
                o_DQSR90=dqsr90,
                o_RDPNTR0=rdpntr[0],
                o_RDPNTR1=rdpntr[1],
                o_RDPNTR2=rdpntr[2],
                o_WRPNTR0=wrpntr[0],
                o_WRPNTR1=wrpntr[1],
                o_WRPNTR2=wrpntr[2],
                o_BURSTDET=burstdet,
                o_DATAVALID=datavalid,

                # Writes (generate shifted ECLK clock for writes)
                o_DQSW270=dqsw270,
                o_DQSW=dqsw)

            with m.If(Rose(burstdet)):
                m.d.sync += burstdet_reg[i].eq(1)

            # DQS and DM ---------------------------------------------------------------------------
            dm_o_data = Signal(8)
            dm_o_data_d = Signal(8, reset_less=True)
            dm_o_data_muxed = Signal(4, reset_less=True)
            m.d.comb += dm_o_data.eq(
                Cat(dfi.phases[0].wrdata_mask[0 * databits // 8 + i],
                    dfi.phases[0].wrdata_mask[1 * databits // 8 + i],
                    dfi.phases[0].wrdata_mask[2 * databits // 8 + i],
                    dfi.phases[0].wrdata_mask[3 * databits // 8 + i],
                    dfi.phases[1].wrdata_mask[0 * databits // 8 + i],
                    dfi.phases[1].wrdata_mask[1 * databits // 8 + i],
                    dfi.phases[1].wrdata_mask[2 * databits // 8 + i],
                    dfi.phases[1].wrdata_mask[3 * databits // 8 + i]), )
            m.d.sync += dm_o_data_d.eq(dm_o_data)

            with m.If(bl8_chunk):
                m.d.sync += dm_o_data_muxed.eq(dm_o_data_d[4:])
            with m.Else():
                m.d.sync += dm_o_data_muxed.eq(dm_o_data[:4])

            m.submodules += Instance("ODDRX2DQA",
                                     i_RST=ResetSignal("dramsync"),
                                     i_ECLK=ClockSignal("sync2x"),
                                     i_SCLK=ClockSignal("dramsync"),
                                     i_DQSW270=dqsw270,
                                     i_D0=dm_o_data_muxed[0],
                                     i_D1=dm_o_data_muxed[1],
                                     i_D2=dm_o_data_muxed[2],
                                     i_D3=dm_o_data_muxed[3],
                                     o_Q=self.pads.dm.o[i])

            dqs = Signal()
            dqs_oe_n = Signal()
            m.submodules += [
                Instance("ODDRX2DQSB",
                         i_RST=ResetSignal("dramsync"),
                         i_ECLK=ClockSignal("sync2x"),
                         i_SCLK=ClockSignal(),
                         i_DQSW=dqsw,
                         i_D0=0,
                         i_D1=1,
                         i_D2=0,
                         i_D3=1,
                         o_Q=dqs),
                Instance("TSHX2DQSA",
                         i_RST=ResetSignal("dramsync"),
                         i_ECLK=ClockSignal("sync2x"),
                         i_SCLK=ClockSignal(),
                         i_DQSW=dqsw,
                         i_T0=~(dqs_oe | dqs_postamble),
                         i_T1=~(dqs_oe | dqs_preamble),
                         o_Q=dqs_oe_n),
                Instance("BB",
                         i_I=dqs,
                         i_T=dqs_oe_n,
                         o_O=dqs_i,
                         io_B=self.pads.dqs.p[i]),
            ]

            for j in range(8 * i, 8 * (i + 1)):
                dq_o = Signal()
                dq_i = Signal()
                dq_oe_n = Signal()
                dq_i_delayed = Signal()
                dq_i_data = Signal(4)
                dq_o_data = Signal(8)
                dq_o_data_d = Signal(8, reset_less=True)
                dq_o_data_muxed = Signal(4, reset_less=True)
                m.d.comb += dq_o_data.eq(
                    Cat(dfi.phases[0].wrdata[0 * databits + j],
                        dfi.phases[0].wrdata[1 * databits + j],
                        dfi.phases[0].wrdata[2 * databits + j],
                        dfi.phases[0].wrdata[3 * databits + j],
                        dfi.phases[1].wrdata[0 * databits + j],
                        dfi.phases[1].wrdata[1 * databits + j],
                        dfi.phases[1].wrdata[2 * databits + j],
                        dfi.phases[1].wrdata[3 * databits + j]))

                m.d.sync += dq_o_data_d.eq(dq_o_data)
                with m.If(bl8_chunk):
                    m.d.sync += dq_o_data_muxed.eq(dq_o_data_d[4:])
                with m.Else():
                    m.d.sync += dq_o_data_muxed.eq(dq_o_data[:4])

                m.submodules += [
                    Instance("ODDRX2DQA",
                             i_RST=ResetSignal("dramsync"),
                             i_ECLK=ClockSignal("sync2x"),
                             i_SCLK=ClockSignal(),
                             i_DQSW270=dqsw270,
                             i_D0=dq_o_data_muxed[0],
                             i_D1=dq_o_data_muxed[1],
                             i_D2=dq_o_data_muxed[2],
                             i_D3=dq_o_data_muxed[3],
                             o_Q=dq_o),
                    Instance("DELAYF",
                             p_DEL_MODE="DQS_ALIGNED_X2",
                             i_LOADN=1,
                             i_MOVE=0,
                             i_DIRECTION=0,
                             i_A=dq_i,
                             o_Z=dq_i_delayed),
                    Instance("IDDRX2DQA",
                             i_RST=ResetSignal("dramsync"),
                             i_ECLK=ClockSignal("sync2x"),
                             i_SCLK=ClockSignal(),
                             i_DQSR90=dqsr90,
                             i_RDPNTR0=rdpntr[0],
                             i_RDPNTR1=rdpntr[1],
                             i_RDPNTR2=rdpntr[2],
                             i_WRPNTR0=wrpntr[0],
                             i_WRPNTR1=wrpntr[1],
                             i_WRPNTR2=wrpntr[2],
                             i_D=dq_i_delayed,
                             o_Q0=dq_i_data[0],
                             o_Q1=dq_i_data[1],
                             o_Q2=dq_i_data[2],
                             o_Q3=dq_i_data[3]),
                    Instance("TSHX2DQA",
                             i_RST=ResetSignal("dramsync"),
                             i_ECLK=ClockSignal("sync2x"),
                             i_SCLK=ClockSignal(),
                             i_DQSW270=dqsw270,
                             i_T0=~dq_oe,
                             i_T1=~dq_oe,
                             o_Q=dq_oe_n),
                    Instance("BB",
                             i_I=dq_o,
                             i_T=dq_oe_n,
                             o_O=dq_i,
                             io_B=self.pads.dq.io[j])
                ]
                with m.If(~datavalid_prev & datavalid):
                    m.d.sync += [
                        dfi.phases[0].rddata[0 * databits + j].eq(
                            dq_i_data[0]),
                        dfi.phases[0].rddata[1 * databits + j].eq(
                            dq_i_data[1]),
                        dfi.phases[0].rddata[2 * databits + j].eq(
                            dq_i_data[2]),
                        dfi.phases[0].rddata[3 * databits + j].eq(
                            dq_i_data[3]),
                    ]
                with m.Elif(datavalid):
                    m.d.sync += [
                        dfi.phases[1].rddata[0 * databits + j].eq(
                            dq_i_data[0]),
                        dfi.phases[1].rddata[1 * databits + j].eq(
                            dq_i_data[1]),
                        dfi.phases[1].rddata[2 * databits + j].eq(
                            dq_i_data[2]),
                        dfi.phases[1].rddata[3 * databits + j].eq(
                            dq_i_data[3]),
                    ]

        # Read Control Path ------------------------------------------------------------------------
        # Creates a shift register of read commands coming from the DFI interface. This shift register
        # is used to control DQS read (internal read pulse of the DQSBUF) and to indicate to the
        # DFI interface that the read data is valid.
        #
        # The DQS read must be asserted for 2 sys_clk cycles before the read data is coming back from
        # the DRAM (see 6.2.4 READ Pulse Positioning Optimization of FPGA-TN-02035-1.2)
        #
        # The read data valid is asserted for 1 sys_clk cycle when the data is available on the DFI
        # interface, the latency is the sum of the ODDRX2DQA, CAS, IDDRX2DQA latencies.
        rddata_en = Signal(self.settings.read_latency)
        rddata_en_last = Signal.like(rddata_en)
        m.d.comb += rddata_en.eq(
            Cat(dfi.phases[self.settings.rdphase].rddata_en, rddata_en_last))
        m.d.sync += rddata_en_last.eq(rddata_en)
        m.d.comb += dqs_re.eq(rddata_en[cl_sys_latency + 1]
                              | rddata_en[cl_sys_latency + 2])

        rddata_valid = Signal()
        m.d.sync += rddata_valid.eq(datavalid_prev & ~datavalid)
        for phase in dfi.phases:
            m.d.comb += phase.rddata_valid.eq(rddata_valid)

        # Write Control Path -----------------------------------------------------------------------
        # Creates a shift register of write commands coming from the DFI interface. This shift register
        # is used to control DQ/DQS tristates and to select write data of the DRAM burst from the DFI
        # interface: The PHY is operating in halfrate mode (so provide 4 datas every sys_clk cycles:
        # 2x for DDR, 2x for halfrate) but DDR3 requires a burst of 8 datas (BL8) for best efficiency.
        # Writes are then performed in 2 sys_clk cycles and data needs to be selected for each cycle.
        # FIXME: understand +2
        wrdata_en = Signal(cwl_sys_latency + 4)
        wrdata_en_last = Signal.like(wrdata_en)
        m.d.comb += wrdata_en.eq(
            Cat(dfi.phases[self.settings.wrphase].wrdata_en, wrdata_en_last))
        m.d.sync += wrdata_en_last.eq(wrdata_en)
        m.d.comb += dq_oe.eq(wrdata_en[cwl_sys_latency + 1]
                             | wrdata_en[cwl_sys_latency + 2])
        m.d.comb += bl8_chunk.eq(wrdata_en[cwl_sys_latency + 1])
        m.d.comb += dqs_oe.eq(dq_oe)

        # Write DQS Postamble/Preamble Control Path ------------------------------------------------
        # Generates DQS Preamble 1 cycle before the first write and Postamble 1 cycle after the last
        # write. During writes, DQS tristate is configured as output for at least 4 sys_clk cycles:
        # 1 for Preamble, 2 for the Write and 1 for the Postamble.
        m.d.comb += dqs_preamble.eq(wrdata_en[cwl_sys_latency + 0]
                                    & ~wrdata_en[cwl_sys_latency + 1])
        m.d.comb += dqs_postamble.eq(wrdata_en[cwl_sys_latency + 3]
                                     & ~wrdata_en[cwl_sys_latency + 2])

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


        #
        # General state signals.
        #

        # Our line state is always taken directly from D- and D+.
        m.d.comb += self.line_state.eq(Cat(self._io.d_n.i, self._io.d_p.i))

        # If we have a ``vbus_valid`` indication, use it to drive our ``vbus_valid``
        # signal. Otherwise, we'll pretend ``vbus_valid`` is always true, for compatibility.
        if hasattr(self._io, 'vbus_valid'):
            m.d.comb += [
                self.vbus_valid   .eq(self._io.vbus_valid),
                self.session_end  .eq(~self._io.vbus_valid)
            ]
        else:
            m.d.comb += [
                self.vbus_valid   .eq(1),
                self.session_end  .eq(0)
            ]


        #
        # General control signals.
        #

        # If we have a pullup signal, drive it based on ``term_select``.
        if hasattr(self._io, 'pullup'):
            m.d.comb += self._io.pullup.eq(self.term_select)

        # If we have a pulldown signal, drive it based on our pulldown controls.
        if hasattr(self._io, 'pulldown'):
            m.d.comb += self._io.pullup.eq(self.dm_pulldown | self.dp_pulldown)


        #
        # Transmitter
        #
        in_non_driving_mode = (self.op_mode == self.OP_MODE_NONDRIVING)

        m.submodules.transmitter = transmitter = TxPipeline()
        m.d.comb += [

            # UTMI Transmit data.
            transmitter.i_data_payload  .eq(self.tx_data),
            transmitter.i_oe            .eq(self.tx_valid),
            self.tx_ready               .eq(transmitter.o_data_strobe),

            # USB output.
            self._io.d_p.o   .eq(transmitter.o_usbp),
            self._io.d_n.o   .eq(transmitter.o_usbn),

            # USB tri-state control.
            self._io.d_p.oe  .eq(~in_non_driving_mode & transmitter.o_oe),
            self._io.d_n.oe  .eq(~in_non_driving_mode & transmitter.o_oe),
        ]


        # Generate our USB clock strobe, which should pulse at 12MHz.
        m.d.usb_io += transmitter.i_bit_strobe.eq(Rose(ClockSignal("usb")))

        #
        # Receiver
        #
        m.submodules.receiver = receiver = RxPipeline()
        m.d.comb += [

            # We'll listen for packets on D+ and D- _whenever we're not transmitting._.
            # (If we listen while we're transmitting, we'll hear our own packets.)
            receiver.i_usbp  .eq(self._io.d_p & ~transmitter.o_oe),
            receiver.i_usbn  .eq(self._io.d_n & ~transmitter.o_oe),

            self.rx_data     .eq(receiver.o_data_payload),
            self.rx_valid    .eq(receiver.o_data_strobe & receiver.o_pkt_in_progress),
            self.rx_active   .eq(receiver.o_pkt_in_progress),
            self.rx_error    .eq(receiver.o_receive_error)
        ]
        m.d.usb += self.rx_complete .eq(receiver.o_pkt_end)


        return m