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
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
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
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
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
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
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
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
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
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