def elaborate(self, platform): m = Module() if self.has_tx: m.d.comb += self.tx_t.oe.eq(1) if self.invert_tx: m.d.comb += self.tx_t.o.eq(~self.tx_o) else: m.d.comb += self.tx_t.o.eq(self.tx_o) if self.has_rx: if self.invert_rx: m.submodules += FFSynchronizer(~self.rx_t.i, self.rx_i, reset=1) else: m.submodules += FFSynchronizer(self.rx_t.i, self.rx_i, reset=1) return m
def elaborate(self, platform): m = Module() # Keep track of how many cycles we'll keep our PHY in reset. # This is larger than any requirement, in order to work with a broad swathe of PHYs, # in case a PHY other than the TUSB1310A ever makes it to the market. cycles_in_reset = int(5e-6 * 50e6) cycles_spent_in_reset = Signal(range(cycles_in_reset + 1)) # Create versions of our phy_status signals that are observable: # 1) as an asynchronous inputs for startup pulses # 2) as a single-cycle pulse, for power-state-change notifications phy_status = Signal() # Convert our PHY status signal into a simple, sync-domain signal. m.submodules += FFSynchronizer(self.phy_status[0] | self.phy_status[1], phy_status), with m.FSM(): # STARTUP_RESET -- post configuration, we'll reset the PIPE PHY. # This is distinct from the PHY's built-in power-on-reset, as we run this # on every FPGA configuration. with m.State("STARTUP_RESET"): m.d.comb += [ self.reset.eq(1), ] # Once we've extended past a reset time, we can move on. m.d.sync += cycles_spent_in_reset.eq(cycles_spent_in_reset + 1) with m.If(cycles_spent_in_reset == cycles_in_reset): m.next = "DETECT_PHY_STARTUP" # DETECT_PHY_STARTUP -- post-reset, the PHY should drive its status line high. # We'll wait for this to happen, so we can track the PHY's progress. with m.State("DETECT_PHY_STARTUP"): with m.If(phy_status): m.next = "WAIT_FOR_STARTUP" # WAIT_FOR_STARTUP -- we've now detected that the PHY is starting up. # We'll wait for that startup signal to be de-asserted, indicating that the PHY is ready. with m.State("WAIT_FOR_STARTUP"): # For now, we'll start up in P0. This will change once we implement proper RxDetect. with m.If(~phy_status): m.next = "READY" # READY -- our PHY is all started up and ready for use. # For now, we'll remain here until we're reset. with m.State("READY"): m.d.comb += self.ready.eq(1) return m
def _synchronize_(self, m, output, o_domain="sync", stages=2): """ Creates a synchronized copy of this interface's I/O. """ # Synchronize our inputs... m.submodules += [ FFSynchronizer(self.sck, output.sck, o_domain=o_domain, stages=stages), FFSynchronizer(self.sdi, output.sdi, o_domain=o_domain, stages=stages), FFSynchronizer(self.cs, output.cs, o_domain=o_domain, stages=stages), ] # ... and connect our output directly through. m.d.comb += self.sdo.eq(output.sdo)
def elaborate(self, platform): m = Module() pads = self._pads m.d.comb += [ pads.sbwtck_t.oe.eq(1), pads.sbwtck_t.o.eq(self.sbwtck), pads.sbwtdio_t.oe.eq(~self.sbwtd_z), pads.sbwtdio_t.o.eq(self.sbwtd_o), ] m.submodules += [ FFSynchronizer(pads.sbwtdio_t.i, self.sbwtd_i), ] return m
def elaborate(self, platform): m = Module() m.d.comb += [ self.pads.clk_t.oe.eq(1), self.pads.clk_t.o.eq(self.clk), ] m.submodules += [ FFSynchronizer(self.pads.din_t.i, self.din), ] if hasattr(self.pads, "osc_t"): m.d.comb += [ self.pads.osc_t.oe.eq(1), self.pads.osc_t.o.eq(self.osc), ] return m
def elaborate(self, platform): m = Module() m.submodules += self.analyzer pins_i = Signal.like(self.pads.i_t.i) pins_r = Signal.like(self.pads.i_t.i) m.submodules += FFSynchronizer(self.pads.i_t.i, pins_i) m.d.sync += pins_r.eq(pins_i) m.d.comb += [ self.event_source.data.eq(pins_i), self.event_source.trigger.eq(pins_i != pins_r) ] return m
def elaborate(self, platform): m = Module() m.d.comb += [ self.pads.clock_t.o.eq(0), self.pads.clock_t.oe.eq(~self.clock_o), self.pads.data_t.o.eq(0), self.pads.data_t.oe.eq(~self.data_o), ] m.submodules += [ FFSynchronizer(self.pads.clock_t.i, self.clock_i, reset=1), FFSynchronizer(self.pads.data_t.i, self.data_i, reset=1), ] clock_s = Signal(reset=1) clock_r = Signal(reset=1) m.d.sync += [ clock_s.eq(self.clock_i), clock_r.eq(clock_s), self.falling.eq(clock_r & ~clock_s), self.rising.eq(~clock_r & clock_s), ] return m
def elaborate(self, platform) -> Module: """assemble the module""" m = Module() comb = m.d.comb sync = m.d.sync nrzi = Signal() nrzi_prev = Signal() got_edge = Signal() m.submodules.cdc = FFSynchronizer(self.nrzi_in, nrzi) sync += nrzi_prev.eq(nrzi) comb += got_edge.eq(nrzi_prev ^ nrzi) # we are looking for 10 non changing bits # and those will be ~900ns long @48kHz # and if we clock at not more than 100MHz # the counter will run up to 900ns/10ns = 90 # so 7 bits will suffice for the counter sync_counter = DividingCounter(divisor=12, width=7) m.submodules.sync_counter = sync_counter bit_time = sync_counter.divided_counter_out with m.FSM(): with m.State("SYNC"): comb += self.running.eq(0) sync += [ self.data_out.eq(0), self.data_out_en.eq(0), sync_counter.reset_in.eq(0) ] self.find_bit_timings(m, sync_counter, got_edge) with m.State("DECODE"): comb += self.running.eq(1) self.decode_nrzi(m, bit_time, got_edge, sync_counter) return m
def elaborate(self, platform): m = Module() # The ECP5 SerDes uses a simple feedback mechanism to keep its FIFO clocks in sync # with the FPGA's fabric. Accordingly, we'll need to capture the output clocks and then # pass them back to the SerDes; this allows the placer to handle clocking correctly, allows us # to attach clock constraints for analysis, and allows us to use these clocks for -very- simple tasks. txoutclk = Signal() rxoutclk = Signal() # Internal state. rx_los = Signal() rx_lol = Signal() rx_lsm = Signal() rx_align = Signal() rx_bus = Signal(24) tx_lol = Signal() tx_bus = Signal(24) # # Clock domain crossing. # tx_produce_square_wave = Signal() tx_produce_pattern = Signal() tx_pattern = Signal(20) m.submodules += [ # Transmit control synchronization. FFSynchronizer(self.tx_produce_square_wave, tx_produce_square_wave, o_domain="tx"), FFSynchronizer(self.tx_produce_pattern, tx_produce_pattern, o_domain="tx"), FFSynchronizer(self.tx_pattern, tx_pattern, o_domain="tx"), # Receive control synchronization. FFSynchronizer(self.rx_align, rx_align, o_domain="rx"), FFSynchronizer(rx_los, self.rx_idle, o_domain="sync"), ] # # Clocking / reset control. # # The SerDes needs to be brought up gradually; we'll do that here. m.submodules.reset_sequencer = reset = ECP5ResetSequencer() m.d.comb += [ reset.tx_pll_locked .eq(~tx_lol), reset.rx_pll_locked .eq(~rx_lol) ] # Create a local transmit domain, for our transmit-side hardware. m.domains.tx = ClockDomain() m.d.comb += ClockSignal("tx").eq(txoutclk) m.submodules += [ ResetSynchronizer(ResetSignal("sync"), domain="tx"), FFSynchronizer(~ResetSignal("tx"), self.tx_ready) ] # Create the same setup, buf for the receive side. m.domains.rx = ClockDomain() m.d.comb += ClockSignal("rx").eq(rxoutclk) m.submodules += [ ResetSynchronizer(ResetSignal("sync"), domain="rx"), FFSynchronizer(~ResetSignal("rx"), self.rx_ready) ] # # Core SerDes instantiation. # serdes_params = dict( # DCU — power management p_D_MACROPDB = "0b1", p_D_IB_PWDNB = "0b1", # undocumented (required for RX) p_D_TXPLL_PWDNB = "0b1", i_D_FFC_MACROPDB = 1, # DCU — reset i_D_FFC_MACRO_RST = ResetSignal("sync"), i_D_FFC_DUAL_RST = ResetSignal("sync"), # DCU — clocking i_D_REFCLKI = self._pll.refclk, o_D_FFS_PLOL = tx_lol, p_D_REFCK_MODE = { 25: "0b100", 20: "0b000", 16: "0b010", 10: "0b001", 8: "0b011"}[self._pll.config["mult"]], p_D_TX_MAX_RATE = "5.0", # 5.0 Gbps p_D_TX_VCO_CK_DIV = { 32: "0b111", 16: "0b110", 8: "0b101", 4: "0b100", 2: "0b010", 1: "0b000"}[1], # DIV/1 p_D_BITCLK_LOCAL_EN = "0b1", # Use clock from local PLL # Clock multiplier unit configuration p_D_CMUSETBIASI = "0b00", # begin undocumented (10BSER sample code used) p_D_CMUSETI4CPP = "0d3", p_D_CMUSETI4CPZ = "0d3", p_D_CMUSETI4VCO = "0b00", p_D_CMUSETICP4P = "0b01", p_D_CMUSETICP4Z = "0b101", p_D_CMUSETINITVCT = "0b00", p_D_CMUSETISCL4VCO = "0b000", p_D_CMUSETP1GM = "0b000", p_D_CMUSETP2AGM = "0b000", p_D_CMUSETZGM = "0b000", p_D_SETIRPOLY_AUX = "0b01", p_D_SETICONST_AUX = "0b01", p_D_SETIRPOLY_CH = "0b01", p_D_SETICONST_CH = "0b10", p_D_SETPLLRC = "0d1", p_D_RG_EN = "0b0", p_D_RG_SET = "0b00", p_D_REQ_ISET = "0b011", p_D_PD_ISET = "0b11", # end undocumented # DCU — FIFOs p_D_LOW_MARK = "0d4", # Clock compensation FIFO low water mark (mean=8) p_D_HIGH_MARK = "0d12", # Clock compensation FIFO high water mark (mean=8) # CHX common --------------------------------------------------------------------------- # CHX — protocol p_CHX_PROTOCOL = "10BSER", p_CHX_UC_MODE = "0b1", p_CHX_ENC_BYPASS = "******", # Use the 8b10b encoder p_CHX_DEC_BYPASS = "******", # Use the 8b10b decoder # CHX receive -------------------------------------------------------------------------- # CHX RX — power management p_CHX_RPWDNB = "0b1", i_CHX_FFC_RXPWDNB = 1, # CHX RX — reset i_CHX_FFC_RRST = ~self.rx_enable | reset.serdes_rx_reset, i_CHX_FFC_LANE_RX_RST = ~self.rx_enable | reset.pcs_reset, # CHX RX — input i_CHX_HDINP = self._rx_pads.p, i_CHX_HDINN = self._rx_pads.n, p_CHX_REQ_EN = "0b1", # Enable equalizer p_CHX_REQ_LVL_SET = "0b01", p_CHX_RX_RATE_SEL = "0d09", # Equalizer pole position p_CHX_RTERM_RX = { "5k-ohms": "0b00000", "80-ohms": "0b00001", "75-ohms": "0b00100", "70-ohms": "0b00110", "60-ohms": "0b01011", "50-ohms": "0b10011", "46-ohms": "0b11001", "wizard-50-ohms": "0d22"}["wizard-50-ohms"], p_CHX_RXIN_CM = "0b11", # CMFB (wizard value used) p_CHX_RXTERM_CM = "0b10", # RX Input (wizard value used) # CHX RX — clocking i_CHX_RX_REFCLK = self._pll.refclk, o_CHX_FF_RX_PCLK = rxoutclk, i_CHX_FF_RXI_CLK = ClockSignal("rx"), p_CHX_CDR_MAX_RATE = "5.0", # 5.0 Gbps p_CHX_RX_DCO_CK_DIV = { 32: "0b111", 16: "0b110", 8: "0b101", 4: "0b100", 2: "0b010", 1: "0b000"}[1], # DIV/1 p_CHX_RX_GEAR_MODE = "0b1", # 1:2 gearbox p_CHX_FF_RX_H_CLK_EN = "0b1", # enable DIV/2 output clock p_CHX_FF_RX_F_CLK_DIS = "0b1", # disable DIV/1 output clock p_CHX_SEL_SD_RX_CLK = "0b1", # FIFO driven by recovered clock p_CHX_AUTO_FACQ_EN = "0b1", # undocumented (wizard value used) p_CHX_AUTO_CALIB_EN = "0b1", # undocumented (wizard value used) p_CHX_PDEN_SEL = "0b0", # phase detector disabled on LOS p_CHX_DCOATDCFG = "0b00", # begin undocumented (sample code used) p_CHX_DCOATDDLY = "0b00", p_CHX_DCOBYPSATD = "0b1", p_CHX_DCOCALDIV = "0b000", p_CHX_DCOCTLGI = "0b011", p_CHX_DCODISBDAVOID = "0b0", p_CHX_DCOFLTDAC = "0b00", p_CHX_DCOFTNRG = "0b001", p_CHX_DCOIOSTUNE = "0b010", p_CHX_DCOITUNE = "0b00", p_CHX_DCOITUNE4LSB = "0b010", p_CHX_DCOIUPDNX2 = "0b1", p_CHX_DCONUOFLSB = "0b100", p_CHX_DCOSCALEI = "0b01", p_CHX_DCOSTARTVAL = "0b010", p_CHX_DCOSTEP = "0b11", # end undocumented # CHX RX — loss of signal o_CHX_FFS_RLOS = rx_los, p_CHX_RLOS_SEL = "0b1", p_CHX_RX_LOS_EN = "0b0", p_CHX_RX_LOS_LVL = "0b101", # Lattice "TBD" (wizard value used) p_CHX_RX_LOS_CEQ = "0b11", # Lattice "TBD" (wizard value used) p_CHX_RX_LOS_HYST_EN = "0b1", # CHX RX — loss of lock o_CHX_FFS_RLOL = rx_lol, # CHX RX — link state machine # Note that Lattice Diamond needs these in their provided bases (and string lengths!). # Changing their bases will work with the open toolchain, but will make Diamond mad. i_CHX_FFC_SIGNAL_DETECT = rx_align, o_CHX_FFS_LS_SYNC_STATUS= rx_lsm, p_CHX_ENABLE_CG_ALIGN = "0b1", p_CHX_UDF_COMMA_MASK = "0x0ff", # compare the 8 lsbs p_CHX_UDF_COMMA_A = "0x003", # "0b0000000011", # K28.1, K28.5 and K28.7 p_CHX_UDF_COMMA_B = "0x07c", # "0b0001111100", # K28.1, K28.5 and K28.7 p_CHX_CTC_BYPASS = "******", # bypass CTC FIFO p_CHX_MIN_IPG_CNT = "0b11", # minimum interpacket gap of 4 p_CHX_MATCH_2_ENABLE = "0b0", # 2 character skip matching p_CHX_MATCH_4_ENABLE = "0b0", # 4 character skip matching p_CHX_CC_MATCH_1 = "0x000", p_CHX_CC_MATCH_2 = "0x000", p_CHX_CC_MATCH_3 = "0x000", p_CHX_CC_MATCH_4 = "0x000", # CHX RX — data **{"o_CHX_FF_RX_D_%d" % n: rx_bus[n] for n in range(len(rx_bus))}, # CHX transmit ------------------------------------------------------------------------- # CHX TX — power management p_CHX_TPWDNB = "0b1", i_CHX_FFC_TXPWDNB = 1, # CHX TX — reset i_D_FFC_TRST = ~self.tx_enable | reset.serdes_tx_reset, i_CHX_FFC_LANE_TX_RST = ~self.tx_enable | reset.pcs_reset, # CHX TX — output o_CHX_HDOUTP = self._tx_pads.p, o_CHX_HDOUTN = self._tx_pads.n, p_CHX_TXAMPLITUDE = "0d1000", # 1000 mV p_CHX_RTERM_TX = { "5k-ohms": "0b00000", "80-ohms": "0b00001", "75-ohms": "0b00100", "70-ohms": "0b00110", "60-ohms": "0b01011", "50-ohms": "0b10011", "46-ohms": "0b11001", "wizard-50-ohms": "0d19"}["50-ohms"], p_CHX_TDRV_SLICE0_CUR = "0b011", # 400 uA p_CHX_TDRV_SLICE0_SEL = "0b01", # main data p_CHX_TDRV_SLICE1_CUR = "0b000", # 100 uA p_CHX_TDRV_SLICE1_SEL = "0b00", # power down p_CHX_TDRV_SLICE2_CUR = "0b11", # 3200 uA p_CHX_TDRV_SLICE2_SEL = "0b01", # main data p_CHX_TDRV_SLICE3_CUR = "0b10", # 2400 uA p_CHX_TDRV_SLICE3_SEL = "0b01", # main data p_CHX_TDRV_SLICE4_CUR = "0b00", # 800 uA p_CHX_TDRV_SLICE4_SEL = "0b00", # power down p_CHX_TDRV_SLICE5_CUR = "0b00", # 800 uA p_CHX_TDRV_SLICE5_SEL = "0b00", # power down # CHX TX — clocking o_CHX_FF_TX_PCLK = txoutclk, i_CHX_FF_TXI_CLK = ClockSignal("tx"), p_CHX_TX_GEAR_MODE = "0b1", # 1:2 gearbox p_CHX_FF_TX_H_CLK_EN = "0b1", # enable DIV/2 output clock p_CHX_FF_TX_F_CLK_DIS = "0b1", # disable DIV/1 output clock # CHX TX — data **{"i_CHX_FF_TX_D_%d" % n: tx_bus[n] for n in range(len(tx_bus))}, # SCI interface. #**{"i_D_SCIWDATA%d" % n: sci.sci_wdata[n] for n in range(8)}, #**{"i_D_SCIADDR%d" % n: sci.sci_addr[n] for n in range(6)}, #**{"o_D_SCIRDATA%d" % n: sci.sci_rdata[n] for n in range(8)}, #i_D_SCIENAUX = sci.dual_sel, #i_D_SCISELAUX = sci.dual_sel, #i_CHX_SCIEN = sci.chan_sel, #i_CHX_SCISEL = sci.chan_sel, #i_D_SCIRD = sci.sci_rd, #i_D_SCIWSTN = sci.sci_wrn, # Out-of-band signaling Rx support. p_CHX_LDR_RX2CORE_SEL = "0b1", # Enables low-speed out-of-band input. o_CHX_LDR_RX2CORE = self.rx_gpio, # Out-of-band signaling Tx support. p_CHX_LDR_CORE2TX_SEL = "0b0", # Uses CORE2TX_EN to enable out-of-band output. i_CHX_LDR_CORE2TX = self.tx_gpio, i_CHX_FFC_LDR_CORE2TX_EN = self.tx_gpio_en ) # Translate the 'CHX' string to the correct channel name in each of our SerDes parameters, # and create our SerDes instance. serdes_params = {k.replace("CHX", f"CH{self._channel}"):v for (k,v) in serdes_params.items()} m.submodules.serdes = serdes = Instance("DCUA", **serdes_params) # Bind our SerDes to the correct location inside the FPGA. serdes.attrs["LOC"] = "DCU{}".format(self._dual) serdes.attrs["CHAN"] = "CH{}".format(self._channel) serdes.attrs["BEL"] = "X42/Y71/DCU" # # TX and RX datapaths (SerDes <-> stream conversion) # sink = self.sink source = self.source m.d.comb += [ # Grab our received data directly from our SerDes; modifying things to match the # SerDes Rx bus layout, which squishes status signals between our two geared words. source.data[0: 8] .eq(rx_bus[ 0: 8]), source.data[8:16] .eq(rx_bus[12:20]), source.ctrl[0] .eq(rx_bus[8]), source.ctrl[1] .eq(rx_bus[20]), source.valid .eq(1), # Stick the data we'd like to transmit into the SerDes; again modifying things to match # the transmit bus layout. tx_bus[ 0: 8] .eq(sink.data[0: 8]), tx_bus[12:20] .eq(sink.data[8:16]), tx_bus[8] .eq(sink.ctrl[0]), tx_bus[20] .eq(sink.ctrl[1]), sink.ready .eq(1) ] return m
def elaborate(self, platform: Platform) -> Module: m = Module() # Do TX CDC # FFSynchronizer if False: m.submodules += FFSynchronizer(Cat(self.tx_symbol, self.tx_set_disp, self.tx_disp, self.tx_e_idle), Cat(self.__lane.tx_symbol, self.__lane.tx_set_disp, self.__lane.tx_disp, self.__lane.tx_e_idle), o_domain="tx", stages=4) # No CDC # TODO: Check if this actually works if False: m.d.comb += Cat(self.__lane.tx_symbol, self.__lane.tx_set_disp, self.__lane.tx_disp, self.__lane.tx_e_idle).eq( Cat(self.tx_symbol, self.tx_set_disp, self.tx_disp, self.tx_e_idle)) # AsyncFIFOBuffered if True: tx_fifo = m.submodules.tx_fifo = AsyncFIFOBuffered( width=self.ratio * 12, depth=8, r_domain="tx", w_domain="rx") m.d.comb += tx_fifo.w_data.eq( Cat(self.tx_symbol, self.tx_set_disp, self.tx_disp, self.tx_e_idle)) m.d.comb += Cat(self.__lane.tx_symbol, self.__lane.tx_set_disp, self.__lane.tx_disp, self.__lane.tx_e_idle).eq(tx_fifo.r_data) m.d.comb += tx_fifo.r_en.eq(1) m.d.comb += tx_fifo.w_en.eq(1) # Testing symbols if False: m.d.comb += self.__lane.tx_symbol.eq(Cat(Ctrl.COM, D(10, 2))) self.slip = SymbolSlip(symbol_size=10, word_size=self.__lane.ratio, comma=Cat(Ctrl.COM, 1)) m.submodules += self.slip m.d.comb += [ self.slip.en.eq(self.rx_align), self.slip.i.eq( Cat((self.__lane.rx_symbol.word_select(n, 9), self.__lane.rx_valid[n]) for n in range(self.__lane.ratio))), self.rx_symbol.eq( Cat( Part(self.slip.o, 10 * n, 9) for n in range(self.__lane.ratio))), self.rx_valid.eq( Cat(self.slip.o[10 * n + 9] for n in range(self.__lane.ratio))), ] return m
def elaborate(self, platform): m = Module() sync = m.d.sync comb = m.d.comb self.BAR = Signal() sync += self.BAR.eq(~self.BAR) self.tms = tms = Signal.like(self.port.tms) self.tck = tck = Signal.like(self.port.tck) self.tdi = tdi = Signal.like(self.port.tdi) self.tdo = tdo = Signal.like(self.port.tdo) m.submodules += [ FFSynchronizer(self.port.tms, tms), FFSynchronizer(self.port.tck, tck), FFSynchronizer(self.port.tdi, tdi), ] prev_tck = Signal() self.rising_tck = rising_tck = Signal() self.falling_tck = falling_tck = Signal() sync += prev_tck.eq(tck) comb += [ rising_tck.eq((~prev_tck) & tck), falling_tck.eq(prev_tck & (~tck)), ] self.ir = Signal(JtagIR) assert self.ir.width == 5 # Spike self.dr = Signal(max([len(v) for _, v in self.regs.items()])) self.DATA_WRITE = Signal(debug_module_register_len(JtagIR.DMI)) self.DATA_READ = Signal(debug_module_register_len(JtagIR.DMI)) self.DMI_WRITE = Signal(32) # TODO for ir, record in self.regs.items(): sync += record.update.eq(0) sync += record.capture.eq(0) with m.FSM() as jtag_fsm: with m.State("TEST-LOGIC-RESET"): with m.If(rising_tck & ~tms): sync += self.ir.eq(self.ir_reset) m.next = "RUN-TEST-IDLE" with m.State("RUN-TEST-IDLE"): with m.If(rising_tck & tms): m.next = "SELECT-DR-SCAN" with m.State("SELECT-DR-SCAN"): with m.If(rising_tck): with m.If(tms): m.next = "SELECT-IR-SCAN" # IR path with m.Else(): m.next = "CAPTURE-DR" # DR path # DR path with m.State("CAPTURE-DR"): with m.Switch(self.ir): for ir, record in self.regs.items(): with m.Case(ir): sync += self.DATA_READ.eq(record.r) sync += self.dr.eq(record.r) sync += record.capture.eq(rising_tck) with m.If(rising_tck): with m.If(tms): m.next = "EXIT1-DR" with m.Else(): m.next = "SHIFT-DR" with m.State("SHIFT-DR"): with m.If(falling_tck): sync += self.port.tdo.eq(self.dr[0]) with m.Switch(self.ir): for ir, record in self.regs.items(): with m.Case(ir): with m.If(rising_tck): sync += self.dr.eq( Cat(self.dr[1:len(record.r)], tdi)) # below is not enough, as it may effect in garbage # sync += self.dr.eq(Cat(self.dr[1:], tdi)) with m.If(rising_tck & tms): m.next = "EXIT1-DR" with m.State("EXIT1-DR"): sync += self.port.tdo.eq(0) # TODO with m.If(rising_tck): with m.If(tms): m.next = "UPDATE-DR" with m.Else(): m.next = "PAUSE-DR" with m.State("PAUSE-DR"): with m.If(rising_tck & tms): m.next = "EXIT2-DR" with m.State("EXIT2-DR"): with m.If(rising_tck): with m.If(tms): m.next = "UPDATE-DR" with m.Else(): m.next = "SHIFT-DR" with m.State("UPDATE-DR"): with m.Switch(self.ir): for ir, record in self.regs.items(): with m.Case(ir): sync += self.DATA_WRITE.eq(self.dr) with m.If(ir == JtagIR.DMI): sync += self.DMI_WRITE.eq(self.dr[2:34]) sync += record.w.eq(self.dr) sync += record.update.eq(falling_tck) with m.If(rising_tck): with m.If(tms): m.next = "SELECT-DR-SCAN" with m.Else(): m.next = "RUN-TEST-IDLE" # IR path with m.State("SELECT-IR-SCAN"): with m.If(rising_tck): with m.If(tms): m.next = "TEST-LOGIC-RESET" with m.Else(): m.next = "CAPTURE-IR" with m.State("CAPTURE-IR"): sync += self.ir.eq(JtagIR.IDCODE) with m.If(rising_tck): with m.If(tms): m.next = "EXIT1-IR" with m.Else(): m.next = "SHIFT-IR" with m.State("SHIFT-IR"): with m.If(falling_tck): sync += self.port.tdo.eq(self.ir[0]) with m.If(rising_tck): sync += self.ir.eq(Cat(self.ir[1:], tdi)) comb += tdo.eq(self.ir[0]) with m.If(rising_tck & tms): m.next = "EXIT1-IR" with m.State("EXIT1-IR"): sync += self.port.tdo.eq(0) # TODO with m.If(rising_tck): with m.If(tms): m.next = "UPDATE-IR" with m.Else(): m.next = "PAUSE-IR" with m.State("PAUSE-IR"): with m.If(rising_tck & tms): m.next = "EXIT2-IR" with m.State("EXIT2-IR"): with m.If(rising_tck & tms): m.next = "UPDATE-IR" with m.State("UPDATE-IR"): with m.If(rising_tck): with m.If(tms): m.next = "SELECT-IR-SCAN" with m.Else(): m.next = "RUN-TEST-IDLE" return m
def elaborate(self, platform): m = Module() pads = self._pads if hasattr(pads, "ce_t"): m.d.comb += [ pads.ce_t.oe.eq(1), pads.ce_t.o.eq(~self.ce), ] if hasattr(pads, "oe_t"): m.d.comb += [ pads.oe_t.oe.eq(1), pads.oe_t.o.eq(~self.oe), ] if hasattr(pads, "we_t"): m.d.comb += [ pads.we_t.oe.eq(1), pads.we_t.o.eq(~self.we), ] m.d.comb += [ pads.dq_t.oe.eq(~self.oe), pads.dq_t.o.eq(self.d), ] m.submodules += FFSynchronizer(pads.dq_t.i, self.q) m.d.comb += [ pads.a_t.oe.eq(1), pads.a_t.o.eq(self.a), # directly drive low bits ] if hasattr(pads, "a_clk_t") and hasattr(pads, "a_si_t"): a_clk = Signal(reset=1) a_si = Signal() a_lat = Signal(reset=0) if hasattr(pads, "a_lat_t") else None m.d.comb += [ pads.a_clk_t.oe.eq(1), pads.a_clk_t.o.eq(a_clk), pads.a_si_t.oe.eq(1), pads.a_si_t.o.eq(a_si), ] if a_lat is not None: m.d.comb += [pads.a_lat_t.oe.eq(1), pads.a_lat_t.o.eq(a_lat)] # "sa" is the sliced|shifted address, referring to the top-most bits sa_input = self.a[len(pads.a_t.o):] # This represents a buffer of those high address bits, # not to be confused with the latch pin. sa_latch = Signal(self.a_bits - len(pads.a_t.o)) sh_cyc = math.ceil(platform.default_clk_frequency / self._sh_freq) timer = Signal(range(sh_cyc), reset=sh_cyc - 1) count = Signal(range(len(sa_latch) + 1)) first = Signal(reset=1) with m.FSM(): with m.State("READY"): m.d.sync += first.eq(0) with m.If((sa_latch == sa_input) & ~first): m.d.comb += self.rdy.eq(1) with m.Else(): m.d.sync += count.eq(len(sa_latch)) m.d.sync += sa_latch.eq(sa_input) m.next = "SHIFT" with m.State("SHIFT"): with m.If(timer == 0): m.d.sync += timer.eq(timer.reset) m.d.sync += a_clk.eq(~a_clk) with m.If(a_clk): m.d.sync += a_si.eq(sa_latch[-1]) m.d.sync += count.eq(count - 1) with m.Else(): m.d.sync += sa_latch.eq(sa_latch.rotate_left(1)) with m.If(count == 0): if a_lat is None: m.next = "READY" else: m.next = "LATCH-1" with m.Else(): m.d.sync += timer.eq(timer - 1) if a_lat is not None: with m.State("LATCH-1"): m.d.sync += a_lat.eq(1) with m.If(timer == 0): m.d.sync += timer.eq(timer.reset) m.next = "LATCH-2" with m.Else(): m.d.sync += timer.eq(timer - 1) with m.State("LATCH-2"): with m.If(timer == 0): m.d.sync += timer.eq(timer.reset) m.d.sync += a_lat.eq(0) m.next = "READY" with m.Else(): m.d.sync += timer.eq(timer - 1) else: m.d.comb += self.rdy.eq(1) return m
def elaborate(self, platform): m = Module() sync_pulse = Signal(8) da_reset_shifter = Signal() da_reset_bitstuff = Signal( ) # Need to reset the bit stuffer 1 cycle after the shifter. stall = Signal() # These signals are set during the sync pulse sp_reset_bitstuff = Signal() sp_reset_shifter = Signal() sp_bit = Signal() sp_o_data_strobe = Signal() # 12MHz domain bitstuff_valid_data = Signal() # Keep a Gray counter around to smoothly transition between states state_gray = Signal(2) state_data = Signal() state_sync = Signal() # # Transmit gearing. # m.submodules.shifter = shifter = TxShifter(width=8) m.d.comb += [ shifter.i_data.eq(self.i_data_payload), shifter.i_enable.eq(~stall), shifter.i_clear.eq(da_reset_shifter | sp_reset_shifter) ] # # Bit-stuffing and NRZI. # bitstuff = ResetInserter(da_reset_bitstuff)(TxBitstuffer()) m.submodules.bitstuff = bitstuff m.submodules.nrzi = nrzi = TxNRZIEncoder() # # Transmit controller. # m.d.comb += [ # Send a data strobe when we're two bits from the end of the sync pulse. # This is because the pipeline takes two bit times, and we want to ensure the pipeline # has spooled up enough by the time we're there. bitstuff.i_data.eq(shifter.o_data), stall.eq(bitstuff.o_stall), sp_bit.eq(sync_pulse[0]), sp_reset_bitstuff.eq(sync_pulse[0]), # The shifter has one clock cycle of latency, so reset it # one cycle before the end of the sync byte. sp_reset_shifter.eq(sync_pulse[1]), sp_o_data_strobe.eq(sync_pulse[5]), state_data.eq(state_gray[0] & state_gray[1]), state_sync.eq(state_gray[0] & ~state_gray[1]), self.fit_oe.eq(state_data | state_sync), self.fit_dat.eq((state_data & shifter.o_data & ~bitstuff.o_stall) | sp_bit), self.o_data_strobe.eq(state_data & shifter.o_get & ~stall & self.i_oe), ] # If we reset the shifter, then o_empty will go high on the next cycle. # m.d.usb += [ # If the shifter runs out of data, percolate the "reset" signal to the # shifter, and then down to the bitstuffer. # da_reset_shifter.eq(~stall & shifter.o_empty & ~da_stalled_reset), # da_stalled_reset.eq(da_reset_shifter), # da_reset_bitstuff.eq(~stall & da_reset_shifter), bitstuff_valid_data.eq(~stall & shifter.o_get & self.i_oe), ] with m.FSM(domain="usb"): with m.State('IDLE'): with m.If(self.i_oe): m.d.usb += [sync_pulse.eq(1 << 7), state_gray.eq(0b01)] m.next = "SEND_SYNC" with m.Else(): m.d.usb += state_gray.eq(0b00) with m.State('SEND_SYNC'): m.d.usb += sync_pulse.eq(sync_pulse >> 1) with m.If(sync_pulse[0]): m.d.usb += state_gray.eq(0b11) m.next = "SEND_DATA" with m.Else(): m.d.usb += state_gray.eq(0b01) with m.State('SEND_DATA'): with m.If(~self.i_oe & shifter.o_empty & ~bitstuff.o_stall): with m.If(bitstuff.o_will_stall): m.next = 'STUFF_LAST_BIT' with m.Else(): m.d.usb += state_gray.eq(0b10) m.next = 'IDLE' with m.Else(): m.d.usb += state_gray.eq(0b11) with m.State('STUFF_LAST_BIT'): m.d.usb += state_gray.eq(0b10) m.next = 'IDLE' # 48MHz domain # NRZI encoding nrzi_dat = Signal() nrzi_oe = Signal() # Cross the data from the 12MHz domain to the 48MHz domain cdc_dat = FFSynchronizer(self.fit_dat, nrzi_dat, o_domain="usb_io", stages=3) cdc_oe = FFSynchronizer(self.fit_oe, nrzi_oe, o_domain="usb_io", stages=3) m.submodules += [cdc_dat, cdc_oe] m.d.comb += [ nrzi.i_valid.eq(self.i_bit_strobe), nrzi.i_data.eq(nrzi_dat), nrzi.i_oe.eq(nrzi_oe), self.o_usbp.eq(nrzi.o_usbp), self.o_usbn.eq(nrzi.o_usbn), self.o_oe.eq(nrzi.o_oe), ] return m
from amaranth import * from amaranth.lib.cdc import FFSynchronizer from amaranth.cli import main i, o = Signal(name="i"), Signal(name="o") m = Module() m.submodules += FFSynchronizer(i, o) if __name__ == "__main__": main(m, ports=[i, o])
def elaborate(self, platform): m = Module() # # Reference clock selection. # # If we seem to have a raw pin record, we'll assume we're being passed the external REFCLK. # We'll instantiate an instance that captures the reference clock signal. if hasattr(self._refclk, 'p'): refclk = Signal() m.submodules.refclk_input = refclk_in = Instance("EXTREFB", i_REFCLKP = self._refclk.p, i_REFCLKN = self._refclk.n, o_REFCLKO = refclk, p_REFCK_PWDNB = "0b1", p_REFCK_RTERM = "0b1", # 100 Ohm ) refclk_in.attrs["LOC"] = f"EXTREF{self._refclk_num}" # Otherwise, we'll accept the reference clock directly. else: refclk = self._refclk # # Raw serdes. # pll_config = ECP5SerDesPLLConfiguration(refclk, refclk_freq=self._refclk_frequency, linerate=5e9) serdes = ECP5SerDes( pll_config = pll_config, tx_pads = self._tx_pads, rx_pads = self._rx_pads, channel = self._channel, ) m.submodules.serdes = serdes m.d.comb += [ serdes.train_equalizer .eq(self.train_equalizer), self.ready .eq(serdes.tx_ready & serdes.rx_ready) ] # # Transmit datapath. # m.submodules.tx_datapath = tx_datapath = TransmitPreprocessing() m.d.comb += [ serdes.tx_idle .eq(self.tx_idle), serdes.tx_enable .eq(self.enable), tx_datapath.sink .stream_eq(self.sink), serdes.sink .stream_eq(tx_datapath.source), serdes.tx_gpio_en .eq(self.use_tx_as_gpio), serdes.tx_gpio .eq(self.tx_gpio) ] # # Receive datapath. # m.submodules.rx_datapath = rx_datapath = ReceivePostprocessing() m.d.comb += [ self.rx_idle .eq(serdes.rx_idle), serdes.rx_enable .eq(self.enable), serdes.rx_align .eq(self.rx_align), rx_datapath.align .eq(self.rx_align), rx_datapath.sink .stream_eq(serdes.source), self.source .stream_eq(rx_datapath.source) ] # Pass through a synchronized version of our SerDes' rx-gpio. m.submodules += FFSynchronizer(serdes.rx_gpio, self.rx_gpio, o_domain="fast") # # LFPS Detection # m.submodules.lfps_detector = lfps_detector = LFPSSquareWaveDetector(self._fast_clock_frequency) m.d.comb += [ lfps_detector.rx_gpio .eq(self.rx_gpio), self.lfps_signaling_detected .eq(lfps_detector.present) ] # debug signals m.d.comb += [ self.raw_rx_data.eq(serdes.source.data), self.raw_rx_ctrl.eq(serdes.source.ctrl), ] return m
def elaborate(self, platform: Platform) -> Module: m = Module() lane = self.lane m.submodules += lane # Add the PCIe Lane as a submodule # TODO: Change this for 5 Gbit/s platform.add_clock_constraint( self.rx_clk, (500e6 if self.speed_5GTps else 250e6) / self.gearing ) # For NextPNR, set the maximum clock frequency such that errors are given platform.add_clock_constraint( self.tx_clk, (500e6 if self.speed_5GTps else 250e6) / self.gearing) # RX and TX clock input signals, these go to the SERDES. rx_clk_i = Signal() tx_clk_i = Signal() # RX and TX clock output signals, these come from the SERDES. rx_clk_o = Signal() tx_clk_o = Signal() # Connect RX and TX SERDES clock inputs to the RX clock output m.d.comb += rx_clk_i.eq(rx_clk_o) m.d.comb += tx_clk_i.eq(tx_clk_o) # Clocks exposed by this module are the clock for rx symbol input and tx symbol output, usually they should be frequency-locked but have variable phase offset. m.d.comb += self.rx_clk.eq(rx_clk_o) m.d.comb += self.tx_clk.eq(tx_clk_o) if self.fabric_clk: m.d.comb += self.ref_clk.eq(ClockSignal()) else: # The clock input, on the Versa board this comes from the ispCLOCK IC m.submodules.extref0 = Instance( "EXTREFB", o_REFCLKO=self. ref_clk, # The reference clock is output to ref_clk, it is not really accessible as a signal, since it only exists within the SERDES p_REFCK_PWDNB="0b1", p_REFCK_RTERM="0b1", # 100 Ohm p_REFCK_DCBIAS_EN="0b0", ) m.submodules.extref0.attrs["LOC"] = "EXTREF0" # Locate it if self.gearing == 1: # Different gearing compatibility! # If it is 1:1, only the first symbol has data m.d.comb += [ lane.rx_symbol.eq(self.rx_bus[0:9]), lane.rx_valid.eq( self.rx_bus[0:9] != K(14, 7) ), # SERDES outputs K14.7 when there are coding errors self.tx_bus.eq( Cat(lane.tx_symbol[0:9], lane.tx_set_disp[0], lane.tx_disp[0], lane.tx_e_idle[0])) ] else: # For 1:2, the output symbols get composed from both symbols, structure of rx_data and tx_data is shown on page 8/9 of TN1261 m.d.comb += [ lane.rx_symbol.eq(Cat(self.rx_bus[0:9], self.rx_bus[12:21])), lane.rx_valid.eq( Cat(self.rx_bus[0:9] != K(14, 7), self.rx_bus[12:21] != K(14, 7))), self.tx_bus.eq( Cat(lane.tx_symbol[0:9], lane.tx_set_disp[0], lane.tx_disp[0], lane.tx_e_idle[0], lane.tx_symbol[9:18], lane.tx_set_disp[1], lane.tx_disp[1], lane.tx_e_idle[1])), ] # vals_ch_write = [ # LSB first #[0x16, "----0---"], #[0x18, "------10"], #[0x04, "----1--1"], # CTC bypass and lsm_disable #[0x05, "----00--"], # Disanble skip match #[0x1A, "---1----"], # pden_sel #[0x1B, "------00"], # los_en #[0x18, "----1---"], ] vals_du_write = [ # LSB first #[0x0B, "11-----0"], #[0x18, "----1---"], ] vals_ch_read = [ #[0x37, self.debug.dco_status], # Read address 16 to test1 [0x0B, self.debug.dco_status], # Read address 16 to test1 ] vals_du_read = [] # SCI interface. (from LUNA https://github.com/greatscottgadgets/luna/blob/main/luna/gateware/interface/serdes_phy/backends/ecp5.py) m.submodules.sci = sci = ECP5SerDesConfigInterface() m.submodules.sci_ctrl = sci_ctrl = ECP5SerDesConfigController( sci, vals_ch_write, vals_du_write, vals_ch_read, vals_du_read) # RX signals and their domain-crossed parts rx_los = Signal() # Loss of Signal rx_los_s = Signal() rx_lol = Signal() # RX Loss of Lock rx_lol_s = Signal() rx_lsm = Signal() # Sync state machine status rx_lsm_s = Signal() rx_inv = Signal() # Invert RX rx_det = Signal() # RX detected # TX signals tx_lol = Signal() # TX PLL Loss of Lock tx_lol_s = Signal() # Reset Signals serdes_tx_reset = Signal() serdes_rx_reset = Signal() pcs_reset = Signal() cnt = Signal(8) with m.FSM(domain="rx"): # Inspirations taken from LUNA with m.State("init"): m.d.comb += [ serdes_tx_reset.eq(1), serdes_rx_reset.eq(1), pcs_reset.eq(1), lane.reset_done.eq(0), ] m.d.rx += cnt.eq(0) with m.If(~self.lane.reset): m.next = "start-tx" with m.State("start-tx"): m.d.comb += [ serdes_tx_reset.eq(0), serdes_rx_reset.eq(1), pcs_reset.eq(1), lane.reset_done.eq(0), ] m.d.rx += cnt.eq(cnt + 1) with m.If(~tx_lol_s | (cnt > 200)): m.d.rx += cnt.eq(0) m.next = "start-rx" with m.State("start-rx"): m.d.comb += [ serdes_tx_reset.eq(0), serdes_rx_reset.eq(0), pcs_reset.eq(1), lane.reset_done.eq(0), ] m.d.rx += cnt.eq(cnt + 1) with m.If(~rx_lol_s | (cnt > 200)): m.next = "start-pcs-done" with m.State("start-pcs-done"): m.d.comb += [ serdes_tx_reset.eq(0), serdes_rx_reset.eq(0), pcs_reset.eq(0), lane.reset_done.eq(1), ] with m.If(self.lane.reset): m.next = "init" # Clock domain crossing for status signals and tx data m.submodules += [ FFSynchronizer(rx_los, rx_los_s, o_domain="rx"), FFSynchronizer(rx_lol, rx_lol_s, o_domain="rx"), FFSynchronizer(rx_lsm, rx_lsm_s, o_domain="rx"), FFSynchronizer(tx_lol, tx_lol_s, o_domain="rx"), # FFSynchronizer(self.tx_bus, tx_bus_s, o_domain="rx"), ] # Connect the signals to the lanes signals m.d.comb += [ rx_inv.eq(lane.rx_invert), rx_det.eq(lane.rx_align), lane.rx_present.eq(~rx_los_s), lane.rx_locked.eq(~rx_lol_s), lane.rx_aligned.eq(rx_lsm_s), lane.tx_locked.eq(~tx_lol_s) ] pcie_det_en = Signal() # Enable lane detection pcie_ct = Signal() # Scan enable flag pcie_done = Signal() # Scan finished flag pcie_done_s = Signal() pcie_con = Signal() # PCIe lane connected pcie_con_s = Signal() det_timer = Signal(range(16)) # Detection Timer # Clock domain crossing for PCIe detection signals m.submodules += [ FFSynchronizer(pcie_done, pcie_done_s, o_domain="tx"), FFSynchronizer(pcie_con, pcie_con_s, o_domain="tx") ] with m.FSM(domain="tx", reset="START"): with m.State("START"): # Before starting a Receiver Detection test, the transmitter must be put into # electrical idle by setting the tx_idle_ch#_c input high. The Receiver Detection # test can begin 120 ns after tx_elec_idle is set high by driving the appropriate # pci_det_en_ch#_c high. m.d.tx += det_timer.eq(15) #m.d.tx += lane.det_valid.eq(0) with m.If(lane.det_enable): m.next = "SET-DETECT-H" with m.State("SET-DETECT-H"): # 1. The user drives pcie_det_en high, putting the corresponding TX driver into # receiver detect mode. [...] The TX driver takes some time to enter this state # so the pcie_det_en must be driven high for at least 120ns before pcie_ct # is asserted. with m.If(det_timer == 0): m.d.tx += pcie_det_en.eq(1) m.d.tx += det_timer.eq(15) m.next = "SET-STROBE-H" with m.Else(): m.d.tx += det_timer.eq(det_timer - 1) with m.State("SET-STROBE-H"): # 2. The user drives pcie_ct high for four byte clocks. with m.If(det_timer == 0): m.d.tx += pcie_ct.eq(1) m.d.tx += det_timer.eq(3) m.next = "SET-STROBE-L" with m.Else(): m.d.tx += det_timer.eq(det_timer - 1) with m.State("SET-STROBE-L"): # 3. SERDES drives the corresponding pcie_done low. # (this happens asynchronously, so we're going to observe a few samples of pcie_done # as high) with m.If(det_timer == 0): m.d.tx += pcie_ct.eq(0) m.next = "WAIT-DONE-L" with m.Else(): m.d.tx += det_timer.eq(det_timer - 1) with m.State("WAIT-DONE-L"): with m.If(~pcie_done_s): m.next = "WAIT-DONE-H" with m.State("WAIT-DONE-H"): with m.If(pcie_done_s): #m.d.tx += lane.det_status.eq(pcie_con_s) TODO: Figure this out #m.d.tx += lane.det_status.eq(pcie_con_s) m.d.tx += lane.det_status.eq(1) m.next = "DONE" with m.State("DONE"): m.d.tx += lane.det_valid.eq(1) with m.If(~lane.det_enable): m.next = "START" with m.Else(): m.next = "DONE" gearing_str = "0b0" if self.gearing == 1 else "0b1" # Automatically select value based on gearing dcu_config = { "p_D_MACROPDB": "0b1", "p_D_IB_PWDNB": "0b1", # undocumented, seems to be "input buffer power down" "p_D_TXPLL_PWDNB": "0b1", "i_D_FFC_MACROPDB": 1, # DCU — reset "i_D_FFC_MACRO_RST": 0, "i_D_FFC_DUAL_RST": 0, "i_D_FFC_TRST": serdes_tx_reset, # DCU — clocking "i_D_REFCLKI": self.ref_clk, "o_D_FFS_PLOL": tx_lol, "p_D_REFCK_MODE": "0b100", # 25x ref_clk "p_D_TX_MAX_RATE": "5.0" if self.speed_5GTps else "2.5", # How many Gbps "p_D_TX_VCO_CK_DIV": "0b000", # DIV/1 "p_D_BITCLK_LOCAL_EN": "0b1", # undocumented (PCIe sample code used) "p_D_SYNC_LOCAL_EN": "0b1", "p_D_BITCLK_FROM_ND_EN": "0b0", # DCU — unknown "p_D_CMUSETBIASI": "0b00", # begin undocumented (PCIe sample code used) "p_D_CMUSETI4CPP": "0d3", # 0d4 in Yumewatari "p_D_CMUSETI4CPZ": "0b101", # 0d3 in Yumewatari "p_D_CMUSETI4VCO": "0b00", "p_D_CMUSETICP4P": "0b01", "p_D_CMUSETICP4Z": "0b101", "p_D_CMUSETINITVCT": "0b00", "p_D_CMUSETISCL4VCO": "0b000", "p_D_CMUSETP1GM": "0b000", "p_D_CMUSETP2AGM": "0b000", #"p_D_CMUSETZGM" :"0b100", #"p_D_SETIRPOLY_AUX" :"0b10", "p_D_CMUSETZGM": "0b000", "p_D_SETIRPOLY_AUX": "0b00", "p_D_SETICONST_AUX": "0b01", #"p_D_SETIRPOLY_CH" :"0b10", #"p_D_SETICONST_CH" :"0b10", "p_D_SETIRPOLY_CH": "0b00", "p_D_SETICONST_CH": "0b00", "p_D_SETPLLRC": "0d1", "p_D_RG_EN": "0b1", "p_D_RG_SET": "0b00", # end undocumented # DCU — FIFOs "p_D_LOW_MARK": "0d4", "p_D_HIGH_MARK": "0d12", } ch_config = { # CH0 — protocol "p_CHx_PROTOCOL": "PCIe", "p_CHx_PCIE_MODE": "0b1", # RX CH — power management "p_CHx_RPWDNB": "0b1", "i_CHx_FFC_RXPWDNB": 1, # RX CH — reset "i_CHx_FFC_RRST": serdes_rx_reset, "i_CHx_FFC_LANE_RX_RST": pcs_reset, # RX CH — input "i_CHx_FFC_SB_INV_RX": rx_inv, #"p_CHx_REQ_EN" :"0b1", #"p_CHx_RX_RATE_SEL" :"0d8", # LUNA uses 0d09 here #"p_CHx_REQ_LVL_SET" :"0b00", # LUNA uses 0b01 (9 dB) here "p_CHx_RTERM_RX": "0d22", # 50 Ohm (wizard value used, does not match datasheet) "p_CHx_RXIN_CM": "0b11", # CMFB (wizard value used) "p_CHx_RXTERM_CM": "0b10", # RX Input terminate to GND # RX CH — clocking "i_CHx_RX_REFCLK": self.ref_clk, "o_CHx_FF_RX_PCLK": rx_clk_o, "i_CHx_FF_RXI_CLK": rx_clk_i, "p_CHx_RX_GEAR_MODE": gearing_str, # 1:2 gearbox "p_CHx_FF_RX_H_CLK_EN": gearing_str, # enable DIV/2 output clock "p_CHx_FF_RX_F_CLK_DIS": gearing_str, # disable DIV/1 output clock "i_CHx_FFC_RATE_MODE_RX": self.divide_clk, # Divide by 2 when set to 1 "p_CHx_AUTO_FACQ_EN": "0b1", # undocumented (wizard value used) "p_CHx_AUTO_CALIB_EN": "0b1", # undocumented (wizard value used) #"p_CHx_BAND_THRESHOLD" :"0b00", "p_CHx_CDR_MAX_RATE": "5.0" if self.speed_5GTps else "2.5", # How many Gbps "p_CHx_RX_DCO_CK_DIV": "0b000", # DIV/1 "p_CHx_PDEN_SEL": "0b1", # phase detector disabled on ~LOS "p_CHx_SEL_SD_RX_CLK": "0b1", # FIFO driven by recovered clock #"p_CHx_SEL_SD_RX_CLK" :"0b0", # FIFO driven by FF_EBRD_CLK "p_CHx_CTC_BYPASS": "******", # bypass CTC FIFO # FIFO bridge clocking "i_CHx_FF_EBRD_CLK": tx_clk_i, "p_CHx_TXDEPRE": "DISABLED", "p_CHx_TXDEPOST": "DISABLED", "p_CHx_DCOATDCFG": "0b00", # begin undocumented (PCIe sample code used) "p_CHx_DCOATDDLY": "0b00", "p_CHx_DCOBYPSATD": "0b1", #"p_CHx_DCOCALDIV :"0b010", #"p_CHx_DCOCTLGI :"0b011", #"p_CHx_DCODISBDAVOID :"0b1", #"p_CHx_DCOFLTDAC :"0b00", #"p_CHx_DCOFTNRG :"0b010", #"p_CHx_DCOIOSTUNE :"0b010", "p_CHx_DCOCALDIV": "0b001", "p_CHx_DCOCTLGI": "0b010", "p_CHx_DCODISBDAVOID": "0b0", "p_CHx_DCOFLTDAC": "0b01", "p_CHx_DCOFTNRG": "0b111", "p_CHx_DCOIOSTUNE": "0b000", "p_CHx_DCOITUNE": "0b00", #"p_CHx_DCOITUNE4LSB :"0b010", "p_CHx_DCOITUNE4LSB": "0b111", "p_CHx_DCOIUPDNX2": "0b1", "p_CHx_DCONUOFLSB": "0b101", #"p_CHx_DCOSCALEI :"0b01", #"p_CHx_DCOSTARTVAL :"0b010", #"p_CHx_DCOSTEP :"0b11", # end undocumented "p_CHx_DCOSCALEI": "0b00", "p_CHx_DCOSTARTVAL": "0b000", "p_CHx_DCOSTEP": "0b00", # end undocumented #"p_CHx_DCOATDCFG" : "0b00", # begin undocumented (sample code used) (from LUNA https://github.com/greatscottgadgets/luna/blob/0a0393528519d68bfd8dcb2b771502134f331923/luna/gateware/interface/serdes_phy/backends/ecp5.py) #"p_CHx_DCOATDDLY" : "0b00", #"p_CHx_DCOBYPSATD" : "0b1", #"p_CHx_DCOCALDIV" : "0b000", #"p_CHx_DCOCTLGI" : "0b011", #"p_CHx_DCODISBDAVOID" : "0b0", #"p_CHx_DCOFLTDAC" : "0b00", #"p_CHx_DCOFTNRG" : "0b001", #"p_CHx_DCOIOSTUNE" : "0b010", #"p_CHx_DCOITUNE" : "0b00", #"p_CHx_DCOITUNE4LSB" : "0b010", #"p_CHx_DCOIUPDNX2" : "0b1", #"p_CHx_DCONUOFLSB" : "0b100", #"p_CHx_DCOSCALEI" : "0b01", #"p_CHx_DCOSTARTVAL" : "0b010", #"p_CHx_DCOSTEP" : "0b11", # end undocumented # RX CH — link state machine "i_CHx_FFC_SIGNAL_DETECT": rx_det, # WARNING: If 0, then no symbol lock happens "o_CHx_FFS_LS_SYNC_STATUS": rx_lsm, "p_CHx_ENABLE_CG_ALIGN": "0b1", "p_CHx_UDF_COMMA_MASK": "0x3ff", # compare all 10 bits "p_CHx_UDF_COMMA_A": "0x283", # K28.5 inverted, encoded in reversed order "p_CHx_UDF_COMMA_B": "0x17C", # K28.5, encoded in reversed order "p_CHx_MIN_IPG_CNT": "0b11", # minimum interpacket gap of 4 "p_CHx_MATCH_4_ENABLE": "0b1", # 4 character skip matching "p_CHx_CC_MATCH_1": "0x1BC", # K28.5 Comma "p_CHx_CC_MATCH_2": "0x11C", # K28.0 Skip "p_CHx_CC_MATCH_3": "0x11C", # K28.0 Skip "p_CHx_CC_MATCH_4": "0x11C", # K28.0 Skip # RX CH — loss of signal "o_CHx_FFS_RLOS": rx_los, "p_CHx_RLOS_SEL": "0b1", # Maybe try if values from LUNA are better here? "p_CHx_RX_LOS_EN": "0b1", "p_CHx_RX_LOS_LVL": "0b100", # Lattice "TBD" (wizard value used) "p_CHx_RX_LOS_CEQ": "0b11", # Lattice "TBD" (wizard value used) "p_CHx_RX_LOS_HYST_EN": "0b0", # RX CH — loss of lock "o_CHx_FFS_RLOL": rx_lol, # RX CH — data **{ "o_CHx_FF_RX_D_%d" % n: self.rx_bus[n] for n in range(self.rx_bus.width) }, # Connect outputs to RX data signals "p_CHx_DEC_BYPASS": "******", # Bypass 8b10b? # TX CH — power management #"p_CHx_TPWDNB" :"0b1", "p_CHx_TPWDNB": "0b0", "i_CHx_FFC_TXPWDNB": 1, # TX CH — reset "i_CHx_FFC_LANE_TX_RST": pcs_reset, # TX CH — output "p_CHx_TXAMPLITUDE": "0d1000", # 1000 mV "p_CHx_RTERM_TX": "0d19", # 50 Ohm "p_CHx_TDRV_SLICE0_CUR": "0b011", # 400 uA "p_CHx_TDRV_SLICE0_SEL": "0b01", # main data "p_CHx_TDRV_SLICE1_CUR": "0b000", # 100 uA "p_CHx_TDRV_SLICE1_SEL": "0b00", # power down "p_CHx_TDRV_SLICE2_CUR": "0b11", # 3200 uA "p_CHx_TDRV_SLICE2_SEL": "0b01", # main data "p_CHx_TDRV_SLICE3_CUR": "0b11", # 3200 uA "p_CHx_TDRV_SLICE3_SEL": "0b01", # main data "p_CHx_TDRV_SLICE4_CUR": "0b11", # 3200 uA "p_CHx_TDRV_SLICE4_SEL": "0b01", # main data "p_CHx_TDRV_SLICE5_CUR": "0b00", # 800 uA "p_CHx_TDRV_SLICE5_SEL": "0b00", # power down # TX CH — clocking "o_CHx_FF_TX_PCLK": tx_clk_o, # Output from SERDES "i_CHx_FF_TXI_CLK": tx_clk_i, # Input to SERDES "p_CHx_TX_GEAR_MODE": gearing_str, # 1:2 gearbox "p_CHx_FF_TX_H_CLK_EN": gearing_str, # disable DIV/1 output clock "p_CHx_FF_TX_F_CLK_DIS": gearing_str, # enable DIV/2 output clock "i_CHx_FFC_RATE_MODE_TX": self.divide_clk, # Divide by 2 when set to 1 # TX CH — data **{ "o_CHx_FF_TX_D_%d" % n: self.tx_bus[n] for n in range(self.tx_bus.width) }, # Connect TX SERDES inputs to the signals "p_CHx_ENC_BYPASS": "******", # Bypass 8b10b # CHx DET "i_CHx_FFC_PCIE_DET_EN": pcie_det_en, "i_CHx_FFC_PCIE_CT": pcie_ct, "o_CHx_FFS_PCIE_DONE": pcie_done, "o_CHx_FFS_PCIE_CON": pcie_con, # Bit Slip "i_CHx_FFC_CDR_EN_BITSLIP": self.slip, #"i_CHx_FFC_FB_LOOPBACK" : 3, # SCI interface, see pages 52-55 in TN1261 **{"i_D_SCIWDATA%d" % n: sci.sci_wdata[n] for n in range(8)}, # Data in **{"i_D_SCIADDR%d" % n: sci.sci_addr[n] for n in range(6)}, # Address **{"o_D_SCIRDATA%d" % n: sci.sci_rdata[n] for n in range(8)}, # Data out "i_D_SCIENAUX": sci.dual_sel, # Select dual registers "i_D_SCISELAUX": sci.dual_sel, "i_CH1_SCIEN": sci.chan_sel, # Select channel registers "i_CH1_SCISEL": sci.chan_sel, "i_D_SCIRD": sci.sci_rd, # Read "i_D_SCIWSTN": sci.sci_wrn, # Write } modified_ch_config = {} for key in ch_config: modified_ch_config[key.replace( "CHx", "CH0" if self.CH == 0 else "CH1")] = ch_config[key] m.submodules.dcu0 = Instance("DCUA", **dcu_config, **modified_ch_config) m.submodules.dcu0.attrs["LOC"] = "DCU0" if self.DCU == 0 else "DCU1" # Needed for Lattice Diamond, Trellis does not need this. m.submodules.dcu0.attrs["CHAN"] = "CH0" if self.CH == 0 else "CH1" m.submodules.dcu0.attrs["BEL"] = "X42/Y71/DCU" return m
def elaborate(self, platform): m = Module() # Memory ports. write_port = self.mem.write_port() read_port = self.mem.read_port(domain="sync") m.submodules += [write_port, read_port] # If necessary, create synchronized versions of the relevant signals. if self.samples_pretrigger >= 2: delayed_inputs = Signal.like(self.inputs) m.submodules += FFSynchronizer(self.inputs, delayed_inputs, stages=self.samples_pretrigger) elif self.samples_pretrigger == 1: delayed_inputs = Signal.like(self.inputs) m.d.sync += delayed_inputs.eq(self.inputs) else: delayed_inputs = self.inputs # Counter that keeps track of our write position. write_position = Signal(range(0, self.sample_depth)) # Set up our write port to capture the input signals, # and our read port to provide the output. m.d.comb += [ write_port.data.eq(delayed_inputs), write_port.addr.eq(write_position), self.captured_sample.eq(read_port.data), read_port.addr.eq(self.captured_sample_number) ] # Don't sample unless our FSM asserts our sample signal explicitly. m.d.sync += write_port.en.eq(0) with m.FSM(name="ila_state") as fsm: m.d.comb += self.sampling.eq(~fsm.ongoing("IDLE")) # IDLE: wait for the trigger strobe with m.State('IDLE'): with m.If(self.trigger): m.next = 'SAMPLE' # Grab a sample as our trigger is asserted. m.d.sync += [ write_port.en.eq(1), write_position.eq(0), self.complete.eq(0), ] # SAMPLE: do our sampling with m.State('SAMPLE'): # Sample until we run out of samples. m.d.sync += [ write_port.en.eq(1), write_position.eq(write_position + 1), ] # If this is the last sample, we're done. Finish up. with m.If(write_position + 1 == self.sample_depth): m.next = "IDLE" m.d.sync += [self.complete.eq(1), write_port.en.eq(0)] # Convert our sync domain to the domain requested by the user, if necessary. if self.domain != "sync": m = DomainRenamer(self.domain)(m) return m
def elaborate(self, platform): m = Module() pins = self._pins in_fifo = self._in_fifo out_fifo = self._out_fifo jtag_oe = Signal(len(pins)) jtag_o = Signal(len(pins)) jtag_i = Signal(len(pins)) m.d.comb += [ Cat(pin.oe for pin in pins).eq(jtag_oe), Cat(pin.o for pin in pins).eq(jtag_o), ] m.submodules += FFSynchronizer(Cat(pin.i for pin in pins), jtag_i) timer = Signal(range(self._period_cyc)) cmd = Signal(8) data = Signal(16) with m.FSM(): with m.State("RECV-COMMAND"): with m.If(out_fifo.readable): m.d.comb += out_fifo.re.eq(1) m.d.sync += cmd.eq(out_fifo.dout) with m.If(out_fifo.dout == CMD_W): m.d.sync += timer.eq(self._period_cyc - 1) m.next = "WAIT" with m.Elif(out_fifo.dout == CMD_I): m.next = "SAMPLE" with m.Else(): m.next = "RECV-DATA-1" with m.State("RECV-DATA-1"): with m.If(out_fifo.readable): m.d.comb += out_fifo.re.eq(1) m.d.sync += data[0:8].eq(out_fifo.dout) m.next = "RECV-DATA-2" with m.State("RECV-DATA-2"): with m.If(out_fifo.readable): m.d.comb += out_fifo.re.eq(1) m.d.sync += data[8:16].eq(out_fifo.dout) m.next = "DRIVE" with m.State("DRIVE"): with m.If(cmd == CMD_OE): m.d.sync += jtag_oe.eq(data) with m.Elif(cmd == CMD_O): m.d.sync += jtag_o.eq( data) with m.Elif(cmd == CMD_L): m.d.sync += jtag_o.eq(~data & jtag_o) with m.Elif(cmd == CMD_H): m.d.sync += jtag_o.eq( data | jtag_o) m.next = "RECV-COMMAND" with m.State("WAIT"): with m.If(timer == 0): m.next = "RECV-COMMAND" with m.Else(): m.d.sync += timer.eq(timer - 1) with m.State("SAMPLE"): m.d.sync += data.eq(jtag_i) m.next = "SEND-DATA-1" with m.State("SEND-DATA-1"): with m.If(in_fifo.writable): m.d.comb += in_fifo.we.eq(1) m.d.comb += in_fifo.din.eq(data[0:8]) m.next = "SEND-DATA-2" with m.State("SEND-DATA-2"): with m.If(in_fifo.writable): m.d.comb += in_fifo.we.eq(1) m.d.comb += in_fifo.din.eq(data[8:16]) m.next = "RECV-COMMAND" return m
def create_synchronizer(signal, output): return FFSynchronizer(signal, output, o_domain=o_domain, stages=stages)