def __init__(self, si5324_clkin, si5324_clkout_fabric, ref_clk=None, ref_div2=False): self.switch_clocks = CSRStorage() self.phase_shift = CSR() self.phase_shift_done = CSRStatus(reset=1) self.sample_result = CSRStatus() # 125MHz/62.5MHz reference clock to 150MHz. VCO @ 625MHz. # Used to provide a startup clock to the transceiver through the Si, # we do not use the crystal reference so that the PFD (f3) frequency # can be high. mmcm_freerun_fb = Signal() mmcm_freerun_output = Signal() self.specials += \ Instance("MMCME2_BASE", p_CLKIN1_PERIOD=16.0 if ref_div2 else 8.0, i_CLKIN1=ClockSignal("sys") if ref_clk is None else ref_clk, i_RST=ResetSignal("sys") if ref_clk is None else 0, p_CLKFBOUT_MULT_F=12.0 if ref_div2 else 6.0, p_DIVCLK_DIVIDE=1, o_CLKFBOUT=mmcm_freerun_fb, i_CLKFBIN=mmcm_freerun_fb, p_CLKOUT0_DIVIDE_F=5.0, o_CLKOUT0=mmcm_freerun_output, ) # 150MHz to 150MHz with controllable phase shift, VCO @ 1200MHz. # Inserted between CDR and output to Si, used to correct # non-determinstic skew of Si5324. mmcm_ps_fb = Signal() mmcm_ps_output = Signal() mmcm_ps_psdone = Signal() self.specials += \ Instance("MMCME2_ADV", p_CLKIN1_PERIOD=1e9/150e6, i_CLKIN1=ClockSignal("rtio_rx0"), i_RST=ResetSignal("rtio_rx0"), i_CLKINSEL=1, # yes, 1=CLKIN1 0=CLKIN2 p_CLKFBOUT_MULT_F=8.0, p_CLKOUT0_DIVIDE_F=8.0, p_DIVCLK_DIVIDE=1, o_CLKFBOUT=mmcm_ps_fb, i_CLKFBIN=mmcm_ps_fb, p_CLKOUT0_USE_FINE_PS="TRUE", o_CLKOUT0=mmcm_ps_output, i_PSCLK=ClockSignal(), i_PSEN=self.phase_shift.re, i_PSINCDEC=self.phase_shift.r, o_PSDONE=mmcm_ps_psdone, ) self.sync += [ If(self.phase_shift.re, self.phase_shift_done.status.eq(0)), If(mmcm_ps_psdone, self.phase_shift_done.status.eq(1)) ] si5324_clkin_se = Signal() self.specials += [ Instance("BUFGMUX", i_I0=mmcm_freerun_output, i_I1=mmcm_ps_output, i_S=self.switch_clocks.storage, o_O=si5324_clkin_se ), Instance("OBUFDS", i_I=si5324_clkin_se, o_O=si5324_clkin.p, o_OB=si5324_clkin.n ) ] si5324_clkout_se = Signal() self.specials += \ Instance("IBUFDS", p_DIFF_TERM="TRUE", p_IBUF_LOW_PWR="TRUE", i_I=si5324_clkout_fabric.p, i_IB=si5324_clkout_fabric.n, o_O=si5324_clkout_se), clkout_sample1 = Signal() # IOB register self.sync.rtio_rx0 += clkout_sample1.eq(si5324_clkout_se) self.specials += MultiReg(clkout_sample1, self.sample_result.status) # expose MMCM outputs - used for clock constraints self.mmcm_freerun_output = mmcm_freerun_output self.mmcm_ps_output = mmcm_ps_output
def __init__(self, sys_clk_freq, rx): self.done = Signal() self.restart = Signal() # GTX signals self.plllock = Signal() self.pllreset = Signal() self.gtXxreset = Signal() self.Xxresetdone = Signal() self.Xxdlysreset = Signal() self.Xxdlysresetdone = Signal() self.Xxphaligndone = Signal() self.Xxuserrdy = Signal() # # # # Double-latch transceiver asynch outputs plllock = Signal() Xxresetdone = Signal() Xxdlysresetdone = Signal() Xxphaligndone = Signal() self.specials += [ MultiReg(self.plllock, plllock), MultiReg(self.Xxresetdone, Xxresetdone), MultiReg(self.Xxdlysresetdone, Xxdlysresetdone), MultiReg(self.Xxphaligndone, Xxphaligndone) ] # Deglitch FSM outputs driving transceiver asynch inputs gtXxreset = Signal() Xxdlysreset = Signal() Xxuserrdy = Signal() self.sync += [ self.gtXxreset.eq(gtXxreset), self.Xxdlysreset.eq(Xxdlysreset), self.Xxuserrdy.eq(Xxuserrdy) ] # After configuration, transceiver resets have to stay low for # at least 500ns (see AR43482) startup_cycles = ceil(500 * sys_clk_freq / 1000000000) startup_timer = WaitTimer(startup_cycles) self.submodules += startup_timer startup_fsm = ResetInserter()(FSM(reset_state="RESET_ALL")) self.submodules += startup_fsm ready_timer = WaitTimer(1 * sys_clk_freq // 1000) self.submodules += ready_timer self.comb += [ ready_timer.wait.eq(~self.done & ~startup_fsm.reset), startup_fsm.reset.eq(self.restart | ready_timer.done) ] if rx: cdr_stable_timer = WaitTimer(1024) self.submodules += cdr_stable_timer Xxphaligndone_r = Signal(reset=1) Xxphaligndone_rising = Signal() self.sync += Xxphaligndone_r.eq(Xxphaligndone) self.comb += Xxphaligndone_rising.eq(Xxphaligndone & ~Xxphaligndone_r) startup_fsm.act("RESET_ALL", gtXxreset.eq(1), self.pllreset.eq(1), startup_timer.wait.eq(1), NextState("RELEASE_PLL_RESET")) startup_fsm.act( "RELEASE_PLL_RESET", gtXxreset.eq(1), startup_timer.wait.eq(1), If(plllock & startup_timer.done, NextState("RELEASE_GTX_RESET"))) # Release GTX reset and wait for GTX resetdone # (from UG476, GTX is reset on falling edge # of gtXxreset) if rx: startup_fsm.act( "RELEASE_GTX_RESET", Xxuserrdy.eq(1), cdr_stable_timer.wait.eq(1), If(Xxresetdone & cdr_stable_timer.done, NextState("ALIGN"))) else: startup_fsm.act("RELEASE_GTX_RESET", Xxuserrdy.eq(1), If(Xxresetdone, NextState("ALIGN"))) # Start delay alignment (pulse) startup_fsm.act("ALIGN", Xxuserrdy.eq(1), Xxdlysreset.eq(1), NextState("WAIT_ALIGN")) # Wait for delay alignment startup_fsm.act( "WAIT_ALIGN", Xxuserrdy.eq(1), If(Xxdlysresetdone, NextState("WAIT_FIRST_ALIGN_DONE"))) # Wait 2 rising edges of Xxphaligndone # (from UG476 in buffer bypass config) startup_fsm.act( "WAIT_FIRST_ALIGN_DONE", Xxuserrdy.eq(1), If(Xxphaligndone_rising, NextState("WAIT_SECOND_ALIGN_DONE"))) startup_fsm.act("WAIT_SECOND_ALIGN_DONE", Xxuserrdy.eq(1), If(Xxphaligndone_rising, NextState("READY"))) startup_fsm.act("READY", Xxuserrdy.eq(1), self.done.eq(1))
def __init__(self, pads, data_width): if pads is None: pads = Record(self.pads_layout) if not hasattr(pads, "cs_n"): pads.cs_n = Signal() self.pads = pads self.data_width = data_width self.start = Signal() self.length = Signal(8) self.done = Signal() self.irq = Signal() self.mosi = Signal(data_width) self.miso = Signal(data_width) self.cs = Signal() self.loopback = Signal() # # # clk = Signal() cs = Signal() mosi = Signal() miso = Signal() # IOs <--> Internal (input resynchronization) ---------------------------------------------- self.specials += [ MultiReg(pads.clk, clk), MultiReg(~pads.cs_n, cs), MultiReg(pads.mosi, mosi), ] self.comb += pads.miso.eq(miso) # Clock detection -------------------------------------------------------------------------- clk_d = Signal() clk_rise = Signal() clk_fall = Signal() self.sync += clk_d.eq(clk) self.comb += clk_rise.eq(clk & ~clk_d) self.comb += clk_fall.eq(~clk & clk_d) # Control FSM ------------------------------------------------------------------------------ self.submodules.fsm = fsm = FSM(reset_state="IDLE") fsm.act( "IDLE", If(cs, self.start.eq(1), NextValue(self.length, 0), NextState("XFER")).Else(self.done.eq(1))) fsm.act("XFER", If(~cs, self.irq.eq(1), NextState("IDLE")), NextValue(self.length, self.length + clk_rise)) # Master In Slave Out (MISO) generation (generated on spi_clk falling edge) ---------------- miso_data = Signal(data_width) self.sync += \ If(self.start, miso_data.eq(self.miso) ).Elif(cs & clk_fall, miso_data.eq(Cat(Signal(), miso_data[:-1])) ) self.comb += \ If(self.loopback, miso.eq(mosi) ).Else( miso.eq(miso_data[-1]), ) # Master Out Slave In (MOSI) capture (captured on spi_clk rising edge) --------------------- self.sync += \ If(cs & clk_rise, self.mosi.eq(Cat(mosi, self.mosi[:-1])) )
def __init__(self, pll, tx_pads, rx_pads, sys_clk_freq, data_width=20, tx_buffer_enable=False, rx_buffer_enable=False, clock_aligner=True, tx_polarity=0, rx_polarity=0): assert (data_width == 20) or (data_width == 40) # TX controls self.tx_restart = Signal() self.tx_disable = Signal() self.tx_produce_square_wave = Signal() self.tx_prbs_config = Signal(2) # RX controls self.rx_ready = Signal() self.rx_restart = Signal() self.rx_prbs_config = Signal(2) self.rx_prbs_errors = Signal(32) # DRP self.drp = DRPInterface() # Loopback self.loopback = Signal(3) # # # use_cpll = isinstance(pll, GTHChannelPLL) use_qpll0 = isinstance(pll, GTHQuadPLL) and pll.config["qpll"] == "qpll0" use_qpll1 = isinstance(pll, GTHQuadPLL) and pll.config["qpll"] == "qpll1" self.nwords = nwords = data_width // 10 self.submodules.encoder = ClockDomainsRenamer("tx")(Encoder( nwords, True)) self.decoders = [ ClockDomainsRenamer("rx")(Decoder(True)) for _ in range(nwords) ] self.submodules += self.decoders self.tx_ready = Signal() self.rx_ready = Signal() # transceiver direct clock outputs # useful to specify clock constraints in a way palatable to Vivado self.txoutclk = Signal() self.rxoutclk = Signal() self.tx_clk_freq = pll.config["linerate"] / data_width self.rx_clk_freq = pll.config["linerate"] / data_width # control/status cdc tx_produce_square_wave = Signal() tx_prbs_config = Signal(2) rx_prbs_config = Signal(2) rx_prbs_errors = Signal(32) self.specials += [ MultiReg(self.tx_produce_square_wave, tx_produce_square_wave, "tx"), MultiReg(self.tx_prbs_config, tx_prbs_config, "tx"), ] self.specials += [ MultiReg(self.rx_prbs_config, rx_prbs_config, "rx"), MultiReg(rx_prbs_errors, self.rx_prbs_errors, "sys"), # FIXME ] # # # # TX generates TX clock, init must be in system domain self.submodules.tx_init = tx_init = GTHTXInit(sys_clk_freq) self.comb += tx_init.restart.eq(self.tx_restart) # RX receives restart commands from TX domain self.submodules.rx_init = rx_init = ClockDomainsRenamer("tx")( GTHRXInit(self.tx_clk_freq)) self.comb += [ tx_init.plllock.eq(pll.lock), rx_init.plllock.eq(pll.lock), pll.reset.eq(tx_init.pllreset) ] # DRP mux self.submodules.drp_mux = drp_mux = DRPMux() drp_mux.add_interface(self.drp) txdata = Signal(data_width) rxdata = Signal(data_width) rxphaligndone = Signal() self.gth_params = dict( p_ACJTAG_DEBUG_MODE=0b0, p_ACJTAG_MODE=0b0, p_ACJTAG_RESET=0b0, p_ADAPT_CFG0=0b1111100000000000, p_ADAPT_CFG1=0b0000000000000000, p_ALIGN_COMMA_DOUBLE="FALSE", p_ALIGN_COMMA_ENABLE=0b0000000000, p_ALIGN_COMMA_WORD=1, p_ALIGN_MCOMMA_DET="FALSE", p_ALIGN_MCOMMA_VALUE=0b1010000011, p_ALIGN_PCOMMA_DET="FALSE", p_ALIGN_PCOMMA_VALUE=0b0101111100, p_A_RXOSCALRESET=0b0, p_A_RXPROGDIVRESET=0b0, p_A_TXPROGDIVRESET=0b0, p_CBCC_DATA_SOURCE_SEL="ENCODED", p_CDR_SWAP_MODE_EN=0b0, p_CHAN_BOND_KEEP_ALIGN="FALSE", p_CHAN_BOND_MAX_SKEW=1, p_CHAN_BOND_SEQ_1_1=0b0000000000, p_CHAN_BOND_SEQ_1_2=0b0000000000, p_CHAN_BOND_SEQ_1_3=0b0000000000, p_CHAN_BOND_SEQ_1_4=0b0000000000, p_CHAN_BOND_SEQ_1_ENABLE=0b1111, p_CHAN_BOND_SEQ_2_1=0b0000000000, p_CHAN_BOND_SEQ_2_2=0b0000000000, p_CHAN_BOND_SEQ_2_3=0b0000000000, p_CHAN_BOND_SEQ_2_4=0b0000000000, p_CHAN_BOND_SEQ_2_ENABLE=0b1111, p_CHAN_BOND_SEQ_2_USE="FALSE", p_CHAN_BOND_SEQ_LEN=1, p_CLK_CORRECT_USE="FALSE", p_CLK_COR_KEEP_IDLE="FALSE", p_CLK_COR_MAX_LAT=12 if rx_buffer_enable else 20, p_CLK_COR_MIN_LAT=8 if rx_buffer_enable else 18, p_CLK_COR_PRECEDENCE="TRUE", p_CLK_COR_REPEAT_WAIT=0, p_CLK_COR_SEQ_1_1=0b0000000000, p_CLK_COR_SEQ_1_2=0b0000000000, p_CLK_COR_SEQ_1_3=0b0000000000, p_CLK_COR_SEQ_1_4=0b0000000000, p_CLK_COR_SEQ_1_ENABLE=0b1111, p_CLK_COR_SEQ_2_1=0b0000000000, p_CLK_COR_SEQ_2_2=0b0000000000, p_CLK_COR_SEQ_2_3=0b0000000000, p_CLK_COR_SEQ_2_4=0b0000000000, p_CLK_COR_SEQ_2_ENABLE=0b1111, p_CLK_COR_SEQ_2_USE="FALSE", p_CLK_COR_SEQ_LEN=1, p_CPLL_CFG0=0b0110011111111000, p_CPLL_CFG1=0b1010010010101100, p_CPLL_CFG2=0b0000000000000111, p_CPLL_CFG3=0b000000, p_CPLL_FBDIV=1 if (use_qpll0 | use_qpll1) else pll.config["n2"], p_CPLL_FBDIV_45=4 if (use_qpll0 | use_qpll1) else pll.config["n1"], p_CPLL_INIT_CFG0=0b0000001010110010, p_CPLL_INIT_CFG1=0b00000000, p_CPLL_LOCK_CFG=0b0000000111101000, p_CPLL_REFCLK_DIV=1 if (use_qpll0 | use_qpll1) else pll.config["m"], p_DDI_CTRL=0b00, p_DDI_REALIGN_WAIT=15, p_DEC_MCOMMA_DETECT="FALSE", p_DEC_PCOMMA_DETECT="FALSE", p_DEC_VALID_COMMA_ONLY="FALSE", p_DFE_D_X_REL_POS=0b0, p_DFE_VCM_COMP_EN=0b0, p_DMONITOR_CFG0=0b0000000000, p_DMONITOR_CFG1=0b00000000, p_ES_CLK_PHASE_SEL=0b0, p_ES_CONTROL=0b000000, p_ES_ERRDET_EN="FALSE", p_ES_EYE_SCAN_EN="FALSE", p_ES_HORZ_OFFSET=0b000000000000, p_ES_PMA_CFG=0b0000000000, p_ES_PRESCALE=0b00000, p_ES_QUALIFIER0=0b0000000000000000, p_ES_QUALIFIER1=0b0000000000000000, p_ES_QUALIFIER2=0b0000000000000000, p_ES_QUALIFIER3=0b0000000000000000, p_ES_QUALIFIER4=0b0000000000000000, p_ES_QUAL_MASK0=0b0000000000000000, p_ES_QUAL_MASK1=0b0000000000000000, p_ES_QUAL_MASK2=0b0000000000000000, p_ES_QUAL_MASK3=0b0000000000000000, p_ES_QUAL_MASK4=0b0000000000000000, p_ES_SDATA_MASK0=0b0000000000000000, p_ES_SDATA_MASK1=0b0000000000000000, p_ES_SDATA_MASK2=0b0000000000000000, p_ES_SDATA_MASK3=0b0000000000000000, p_ES_SDATA_MASK4=0b0000000000000000, p_EVODD_PHI_CFG=0b00000000000, p_EYE_SCAN_SWAP_EN=0b0, p_FTS_DESKEW_SEQ_ENABLE=0b1111, p_FTS_LANE_DESKEW_CFG=0b1111, p_FTS_LANE_DESKEW_EN="FALSE", p_GEARBOX_MODE=0b00000, p_GM_BIAS_SELECT=0b0, p_LOCAL_MASTER=0b1, p_OOBDIVCTL=0b00, p_OOB_PWRUP=0b0, p_PCI3_AUTO_REALIGN="OVR_1K_BLK", p_PCI3_PIPE_RX_ELECIDLE=0b0, p_PCI3_RX_ASYNC_EBUF_BYPASS=0b00, p_PCI3_RX_ELECIDLE_EI2_ENABLE=0b0, p_PCI3_RX_ELECIDLE_H2L_COUNT=0b000000, p_PCI3_RX_ELECIDLE_H2L_DISABLE=0b000, p_PCI3_RX_ELECIDLE_HI_COUNT=0b000000, p_PCI3_RX_ELECIDLE_LP4_DISABLE=0b0, p_PCI3_RX_FIFO_DISABLE=0b0, p_PCIE_BUFG_DIV_CTRL=0b0001000000000000, p_PCIE_RXPCS_CFG_GEN3=0b0000001010100100, p_PCIE_RXPMA_CFG=0b0000000000001010, p_PCIE_TXPCS_CFG_GEN3=0b0010010010100100, p_PCIE_TXPMA_CFG=0b0000000000001010, p_PCS_PCIE_EN="FALSE", p_PCS_RSVD0=0b0000000000000000, p_PCS_RSVD1=0b000, p_PD_TRANS_TIME_FROM_P2=0b000000111100, p_PD_TRANS_TIME_NONE_P2=0b00011001, p_PD_TRANS_TIME_TO_P2=0b01100100, p_PLL_SEL_MODE_GEN12=0b00, p_PLL_SEL_MODE_GEN3=0b11, p_PMA_RSV1=0b1111000000000000, p_PROCESS_PAR=0b010, p_RATE_SW_USE_DRP=0b1, p_RESET_POWERSAVE_DISABLE=0b0, ) self.gth_params.update( p_RXBUFRESET_TIME=0b00011, p_RXBUF_ADDR_MODE="FAST", p_RXBUF_EIDLE_HI_CNT=0b1000, p_RXBUF_EIDLE_LO_CNT=0b0000, p_RXBUF_EN="TRUE" if rx_buffer_enable else "FALSE", p_RXBUF_RESET_ON_CB_CHANGE="TRUE", p_RXBUF_RESET_ON_COMMAALIGN="FALSE", p_RXBUF_RESET_ON_EIDLE="FALSE", p_RXBUF_RESET_ON_RATE_CHANGE="TRUE", p_RXBUF_THRESH_OVFLW=57 if rx_buffer_enable else 0, p_RXBUF_THRESH_OVRD="TRUE" if rx_buffer_enable else "FALSE", p_RXBUF_THRESH_UNDFLW=3 if rx_buffer_enable else 0, p_RXCDRFREQRESET_TIME=0b00001, p_RXCDRPHRESET_TIME=0b00001, p_RXCDR_CFG0=0b0000000000000000, p_RXCDR_CFG0_GEN3=0b0000000000000000, p_RXCDR_CFG1=0b0000000000000000, p_RXCDR_CFG1_GEN3=0b0000000000000000, p_RXCDR_CFG2=0b0000011111010110, p_RXCDR_CFG2_GEN3=0b0000011111100110, p_RXCDR_CFG3=0b0000000000000000, p_RXCDR_CFG3_GEN3=0b0000000000000000, p_RXCDR_CFG4=0b0000000000000000, p_RXCDR_CFG4_GEN3=0b0000000000000000, p_RXCDR_CFG5=0b0000000000000000, p_RXCDR_CFG5_GEN3=0b0000000000000000, p_RXCDR_FR_RESET_ON_EIDLE=0b0, p_RXCDR_HOLD_DURING_EIDLE=0b0, p_RXCDR_LOCK_CFG0=0b0100010010000000, p_RXCDR_LOCK_CFG1=0b0101111111111111, p_RXCDR_LOCK_CFG2=0b0111011111000011, p_RXCDR_PH_RESET_ON_EIDLE=0b0, p_RXCFOK_CFG0=0b0100000000000000, p_RXCFOK_CFG1=0b0000000001100101, p_RXCFOK_CFG2=0b0000000000101110, p_RXDFELPMRESET_TIME=0b0001111, p_RXDFELPM_KL_CFG0=0b0000000000000000, p_RXDFELPM_KL_CFG1=0b0000000000000010, p_RXDFELPM_KL_CFG2=0b0000000000000000, p_RXDFE_CFG0=0b0000101000000000, p_RXDFE_CFG1=0b0000000000000000, p_RXDFE_GC_CFG0=0b0000000000000000, p_RXDFE_GC_CFG1=0b0111100001110000, p_RXDFE_GC_CFG2=0b0000000000000000, p_RXDFE_H2_CFG0=0b0000000000000000, p_RXDFE_H2_CFG1=0b0000000000000000, p_RXDFE_H3_CFG0=0b0100000000000000, p_RXDFE_H3_CFG1=0b0000000000000000, p_RXDFE_H4_CFG0=0b0010000000000000, p_RXDFE_H4_CFG1=0b0000000000000011, p_RXDFE_H5_CFG0=0b0010000000000000, p_RXDFE_H5_CFG1=0b0000000000000011, p_RXDFE_H6_CFG0=0b0010000000000000, p_RXDFE_H6_CFG1=0b0000000000000000, p_RXDFE_H7_CFG0=0b0010000000000000, p_RXDFE_H7_CFG1=0b0000000000000000, p_RXDFE_H8_CFG0=0b0010000000000000, p_RXDFE_H8_CFG1=0b0000000000000000, p_RXDFE_H9_CFG0=0b0010000000000000, p_RXDFE_H9_CFG1=0b0000000000000000, p_RXDFE_HA_CFG0=0b0010000000000000, p_RXDFE_HA_CFG1=0b0000000000000000, p_RXDFE_HB_CFG0=0b0010000000000000, p_RXDFE_HB_CFG1=0b0000000000000000, p_RXDFE_HC_CFG0=0b0000000000000000, p_RXDFE_HC_CFG1=0b0000000000000000, p_RXDFE_HD_CFG0=0b0000000000000000, p_RXDFE_HD_CFG1=0b0000000000000000, p_RXDFE_HE_CFG0=0b0000000000000000, p_RXDFE_HE_CFG1=0b0000000000000000, p_RXDFE_HF_CFG0=0b0000000000000000, p_RXDFE_HF_CFG1=0b0000000000000000, p_RXDFE_OS_CFG0=0b1000000000000000, p_RXDFE_OS_CFG1=0b0000000000000000, p_RXDFE_UT_CFG0=0b1000000000000000, p_RXDFE_UT_CFG1=0b0000000000000011, p_RXDFE_VP_CFG0=0b1010101000000000, p_RXDFE_VP_CFG1=0b0000000000110011, p_RXDLY_CFG=0b0000000000011111, p_RXDLY_LCFG=0b0000000000110000, p_RXELECIDLE_CFG="SIGCFG_4", p_RXGBOX_FIFO_INIT_RD_ADDR=4, p_RXGEARBOX_EN="FALSE", p_RXISCANRESET_TIME=0b00001, p_RXLPM_CFG=0b0000000000000000, p_RXLPM_GC_CFG=0b0001000000000000, p_RXLPM_KH_CFG0=0b0000000000000000, p_RXLPM_KH_CFG1=0b0000000000000010, p_RXLPM_OS_CFG0=0b1000000000000000, p_RXLPM_OS_CFG1=0b0000000000000010, p_RXOOB_CFG=0b000000110, p_RXOOB_CLK_CFG="PMA", p_RXOSCALRESET_TIME=0b00011, p_RXOUT_DIV=pll.config["d"], p_RXPCSRESET_TIME=0b00011, p_RXPHBEACON_CFG=0b0000000000000000, p_RXPHDLY_CFG=0b0010000000100000, p_RXPHSAMP_CFG=0b0010000100000000, p_RXPHSLIP_CFG=0b0110011000100010, p_RXPH_MONITOR_SEL=0b00000, p_RXPI_CFG0=0b00, p_RXPI_CFG1=0b00, p_RXPI_CFG2=0b00, p_RXPI_CFG3=0b00, p_RXPI_CFG4=0b1, p_RXPI_CFG5=0b1, p_RXPI_CFG6=0b000, p_RXPI_LPM=0b0, p_RXPI_VREFSEL=0b0, p_RXPMACLK_SEL="DATA", p_RXPMARESET_TIME=0b00011, p_RXPRBS_ERR_LOOPBACK=0b0, p_RXPRBS_LINKACQ_CNT=15, p_RXSLIDE_AUTO_WAIT=7, p_RXSLIDE_MODE="OFF", p_RXSYNC_MULTILANE=0b0, p_RXSYNC_OVRD=0b0, p_RXSYNC_SKIP_DA=0b0, p_RX_AFE_CM_EN=0b0, p_RX_BIAS_CFG0=0b0000101010110100, p_RX_BUFFER_CFG=0b000000, p_RX_CAPFF_SARC_ENB=0b0, p_RX_CLK25_DIV=5, p_RX_CLKMUX_EN=0b1, p_RX_CLK_SLIP_OVRD=0b00000, p_RX_CM_BUF_CFG=0b1010, p_RX_CM_BUF_PD=0b0, p_RX_CM_SEL=0b11, p_RX_CM_TRIM=0b1010, p_RX_CTLE3_LPF=0b00000001, p_RX_DATA_WIDTH=data_width, p_RX_DDI_SEL=0b000000, p_RX_DEFER_RESET_BUF_EN="TRUE", p_RX_DFELPM_CFG0=0b0110, p_RX_DFELPM_CFG1=0b1, p_RX_DFELPM_KLKH_AGC_STUP_EN=0b1, p_RX_DFE_AGC_CFG0=0b10, p_RX_DFE_AGC_CFG1=0b100, p_RX_DFE_KL_LPM_KH_CFG0=0b01, p_RX_DFE_KL_LPM_KH_CFG1=0b100, p_RX_DFE_KL_LPM_KL_CFG0=0b01, p_RX_DFE_KL_LPM_KL_CFG1=0b100, p_RX_DFE_LPM_HOLD_DURING_EIDLE=0b0, p_RX_DISPERR_SEQ_MATCH="TRUE", p_RX_DIVRESET_TIME=0b00001, p_RX_EN_HI_LR=0b0, p_RX_EYESCAN_VS_CODE=0b0000000, p_RX_EYESCAN_VS_NEG_DIR=0b0, p_RX_EYESCAN_VS_RANGE=0b00, p_RX_EYESCAN_VS_UT_SIGN=0b0, p_RX_FABINT_USRCLK_FLOP=0b0, p_RX_INT_DATAWIDTH=data_width == 40, p_RX_PMA_POWER_SAVE=0b0, p_RX_PROGDIV_CFG=0.0, p_RX_SAMPLE_PERIOD=0b111, p_RX_SIG_VALID_DLY=11, p_RX_SUM_DFETAPREP_EN=0b0, p_RX_SUM_IREF_TUNE=0b0000, p_RX_SUM_RES_CTRL=0b00, p_RX_SUM_VCMTUNE=0b0000, p_RX_SUM_VCM_OVWR=0b0, p_RX_SUM_VREF_TUNE=0b000, p_RX_TUNE_AFE_OS=0b10, p_RX_WIDEMODE_CDR=0b0, p_RX_XCLK_SEL="RXDES" if rx_buffer_enable else "RXUSR", p_SAS_MAX_COM=64, p_SAS_MIN_COM=36, p_SATA_BURST_SEQ_LEN=0b1110, p_SATA_CPLL_CFG="VCO_3000MHZ", p_SATA_MAX_BURST=8, p_SATA_MAX_INIT=21, p_SATA_MAX_WAKE=7, p_SATA_MIN_BURST=4, p_SATA_MIN_INIT=12, p_SATA_MIN_WAKE=4, p_SHOW_REALIGN_COMMA="TRUE", p_SIM_RECEIVER_DETECT_PASS="******", p_SIM_RESET_SPEEDUP="TRUE", p_SIM_TX_EIDLE_DRIVE_LEVEL=0b0, p_SIM_VERSION=2, p_TAPDLY_SET_TX=0b00, p_TEMPERATUR_PAR=0b0010, p_TERM_RCAL_CFG=0b100001000010000, p_TERM_RCAL_OVRD=0b000, p_TRANS_TIME_RATE=0b00001110, p_TST_RSV0=0b00000000, p_TST_RSV1=0b00000000, ) self.gth_params.update( p_TXBUF_EN="TRUE" if tx_buffer_enable else "FALSE", p_TXBUF_RESET_ON_RATE_CHANGE="TRUE", p_TXDLY_CFG=0b0000000000001001, p_TXDLY_LCFG=0b0000000001010000, p_TXDRVBIAS_N=0b1010, p_TXDRVBIAS_P=0b1010, p_TXFIFO_ADDR_CFG="LOW", p_TXGBOX_FIFO_INIT_RD_ADDR=4, p_TXGEARBOX_EN="FALSE", p_TXOUT_DIV=pll.config["d"], p_TXPCSRESET_TIME=0b00011, p_TXPHDLY_CFG0=0b0010000000100000, p_TXPHDLY_CFG1=0b0000000001110101, p_TXPH_CFG=0b0000100110000000, p_TXPH_MONITOR_SEL=0b00000, p_TXPI_CFG0=0b00, p_TXPI_CFG1=0b00, p_TXPI_CFG2=0b00, p_TXPI_CFG3=0b1, p_TXPI_CFG4=0b1, p_TXPI_CFG5=0b000, p_TXPI_GRAY_SEL=0b0, p_TXPI_INVSTROBE_SEL=0b0, p_TXPI_LPM=0b0, p_TXPI_PPMCLK_SEL="TXUSRCLK2", p_TXPI_PPM_CFG=0b00000000, p_TXPI_SYNFREQ_PPM=0b001, p_TXPI_VREFSEL=0b0, p_TXPMARESET_TIME=0b00011, p_TXSYNC_MULTILANE=0, p_TXSYNC_OVRD=0b0, p_TXSYNC_SKIP_DA=0b0, p_TX_CLK25_DIV=5, p_TX_CLKMUX_EN=0b1, p_TX_DATA_WIDTH=data_width, p_TX_DCD_CFG=0b000010, p_TX_DCD_EN=0b0, p_TX_DEEMPH0=0b000000, p_TX_DEEMPH1=0b000000, p_TX_DIVRESET_TIME=0b00001, p_TX_DRIVE_MODE="DIRECT", p_TX_EIDLE_ASSERT_DELAY=0b100, p_TX_EIDLE_DEASSERT_DELAY=0b011, p_TX_EML_PHI_TUNE=0b0, p_TX_FABINT_USRCLK_FLOP=0b0, p_TX_IDLE_DATA_ZERO=0b0, p_TX_INT_DATAWIDTH=data_width == 40, p_TX_LOOPBACK_DRIVE_HIZ="FALSE", p_TX_MAINCURSOR_SEL=0b0, p_TX_MARGIN_FULL_0=0b1001111, p_TX_MARGIN_FULL_1=0b1001110, p_TX_MARGIN_FULL_2=0b1001100, p_TX_MARGIN_FULL_3=0b1001010, p_TX_MARGIN_FULL_4=0b1001000, p_TX_MARGIN_LOW_0=0b1000110, p_TX_MARGIN_LOW_1=0b1000101, p_TX_MARGIN_LOW_2=0b1000011, p_TX_MARGIN_LOW_3=0b1000010, p_TX_MARGIN_LOW_4=0b1000000, p_TX_MODE_SEL=0b000, p_TX_PMADATA_OPT=0b0, p_TX_PMA_POWER_SAVE=0b0, p_TX_PROGCLK_SEL="PREPI", p_TX_PROGDIV_CFG=0.0, p_TX_QPI_STATUS_EN=0b0, p_TX_RXDETECT_CFG=0b00000000110010, p_TX_RXDETECT_REF=0b100, p_TX_SAMPLE_PERIOD=0b111, p_TX_SARC_LPBK_ENB=0b0, p_TX_XCLK_SEL="TXOUT" if tx_buffer_enable else "TXUSR", p_USE_PCS_CLK_PHASE_SEL=0b0, p_WB_MODE=0b00, ) self.gth_params.update( # Reset modes i_GTRESETSEL=0, i_RESETOVRD=0, # DRP i_DRPADDR=drp_mux.addr, i_DRPCLK=drp_mux.clk, i_DRPDI=drp_mux.di, o_DRPDO=drp_mux.do, i_DRPEN=drp_mux.en, o_DRPRDY=drp_mux.rdy, i_DRPWE=drp_mux.we, # CPLL i_CPLLRESET=0, i_CPLLPD=0 if (use_qpll0 | use_qpll1) else pll.reset, o_CPLLLOCK=Signal() if (use_qpll0 | use_qpll1) else pll.lock, i_CPLLLOCKEN=1, i_CPLLREFCLKSEL=0b001, i_TSTIN=2**20 - 1, i_GTREFCLK0=0 if (use_qpll0 | use_qpll1) else pll.refclk, # QPLL i_QPLL0CLK=0 if (use_cpll | use_qpll1) else pll.clk, i_QPLL0REFCLK=0 if (use_cpll | use_qpll1) else pll.refclk, i_QPLL1CLK=0 if (use_cpll | use_qpll0) else pll.clk, i_QPLL1REFCLK=0 if (use_cpll | use_qpll0) else pll.refclk, # TX clock o_TXOUTCLK=self.txoutclk, i_TXSYSCLKSEL=0b00 if use_cpll else 0b10 if use_qpll0 else 0b11, i_TXPLLCLKSEL=0b00 if use_cpll else 0b11 if use_qpll0 else 0b10, i_TXOUTCLKSEL=0b010 if tx_buffer_enable else 0b011, # TX Startup/Reset i_GTTXRESET=tx_init.gtXxreset, o_TXRESETDONE=tx_init.Xxresetdone, i_TXDLYSRESET=tx_init.Xxdlysreset, o_TXDLYSRESETDONE=tx_init.Xxdlysresetdone, o_TXPHALIGNDONE=tx_init.Xxphaligndone, i_TXUSERRDY=tx_init.Xxuserrdy, i_TXSYNCMODE=1, i_TXDLYBYPASS=1 if tx_buffer_enable else 0, i_TXPHDLYPD=1 if tx_buffer_enable else 0, # TX data i_TXCTRL0=Cat(*[txdata[10 * i + 8] for i in range(nwords)]), i_TXCTRL1=Cat(*[txdata[10 * i + 9] for i in range(nwords)]), i_TXDATA=Cat(*[txdata[10 * i:10 * i + 8] for i in range(nwords)]), i_TXUSRCLK=ClockSignal("tx"), i_TXUSRCLK2=ClockSignal("tx"), # TX electrical i_TXPD=0b00, i_TXBUFDIFFCTRL=0b000, i_TXDIFFCTRL=0b1100, i_TXINHIBIT=self.tx_disable, # Internal Loopback i_LOOPBACK=self.loopback, # RX Startup/Reset i_GTRXRESET=rx_init.gtXxreset, o_RXRESETDONE=rx_init.Xxresetdone, i_RXDLYSRESET=rx_init.Xxdlysreset, o_RXPHALIGNDONE=rxphaligndone, i_RXSYNCALLIN=rxphaligndone, i_RXUSERRDY=rx_init.Xxuserrdy, i_RXSYNCIN=0, i_RXSYNCMODE=1, o_RXSYNCDONE=rx_init.Xxsyncdone, i_RXDLYBYPASS=1 if rx_buffer_enable else 0, i_RXPHDLYPD=1 if rx_buffer_enable else 0, # RX AFE i_RXDFEAGCCTRL=1, i_RXDFEXYDEN=1, i_RXLPMEN=1, i_RXOSINTCFG=0xd, i_RXOSINTEN=1, # RX clock i_RXRATE=0, i_RXSYSCLKSEL=0b00, i_RXOUTCLKSEL=0b010, i_RXPLLCLKSEL=0b00, o_RXOUTCLK=self.rxoutclk, i_RXUSRCLK=ClockSignal("rx"), i_RXUSRCLK2=ClockSignal("rx"), # RX data o_RXCTRL0=Cat(*[rxdata[10 * i + 8] for i in range(nwords)]), o_RXCTRL1=Cat(*[rxdata[10 * i + 9] for i in range(nwords)]), o_RXDATA=Cat(*[rxdata[10 * i:10 * i + 8] for i in range(nwords)]), # RX electrical i_RXPD=0b00, i_RXELECIDLEMODE=0b11, # Polarity i_TXPOLARITY=tx_polarity, i_RXPOLARITY=rx_polarity, # Pads i_GTHRXP=rx_pads.p, i_GTHRXN=rx_pads.n, o_GTHTXP=tx_pads.p, o_GTHTXN=tx_pads.n) # tx clocking tx_reset_deglitched = Signal() tx_reset_deglitched.attr.add("no_retiming") self.sync += tx_reset_deglitched.eq(~tx_init.done) self.clock_domains.cd_tx = ClockDomain() if not tx_buffer_enable: tx_bufg_div = pll.config["clkin"] / self.tx_clk_freq else: txoutclk_div = 1 assert tx_bufg_div == int(tx_bufg_div) self.specials += [ Instance("BUFG_GT", i_I=self.txoutclk, o_O=self.cd_tx.clk, i_DIV=int(tx_bufg_div) - 1), AsyncResetSynchronizer(self.cd_tx, tx_reset_deglitched) ] # rx clocking rx_reset_deglitched = Signal() rx_reset_deglitched.attr.add("no_retiming") self.sync.tx += rx_reset_deglitched.eq(~rx_init.done) self.clock_domains.cd_rx = ClockDomain() self.specials += [ Instance("BUFG_GT", i_I=self.rxoutclk, o_O=self.cd_rx.clk), AsyncResetSynchronizer(self.cd_rx, rx_reset_deglitched) ] # tx data and prbs self.submodules.tx_prbs = ClockDomainsRenamer("tx")(PRBSTX( data_width, True)) self.comb += self.tx_prbs.config.eq(tx_prbs_config) self.comb += [ self.tx_prbs.i.eq( Cat(*[self.encoder.output[i] for i in range(nwords)])), If( tx_produce_square_wave, # square wave @ linerate/data_width for scope observation txdata.eq(Signal(data_width, reset=1 << (data_width // 2) - 1) )).Else(txdata.eq(self.tx_prbs.o)) ] # rx data and prbs self.submodules.rx_prbs = ClockDomainsRenamer("rx")(PRBSRX( data_width, True)) self.comb += [ self.rx_prbs.config.eq(rx_prbs_config), rx_prbs_errors.eq(self.rx_prbs.errors) ] for i in range(nwords): self.comb += self.decoders[i].input.eq(rxdata[10 * i:10 * (i + 1)]) self.comb += self.rx_prbs.i.eq(rxdata) # clock alignment if clock_aligner: clock_aligner = BruteforceClockAligner(0b0101111100, self.tx_clk_freq) self.submodules += clock_aligner self.comb += [ clock_aligner.rxdata.eq(rxdata), rx_init.restart.eq(clock_aligner.restart | self.rx_restart), self.rx_ready.eq(clock_aligner.ready) ] else: self.comb += self.rx_ready.eq(rx_init.done)
def __init__(self, default_video_timings="800x600@60Hz"): vt = video_timings[default_video_timings] # MMAP Control/Status Registers. self._enable = CSRStorage(reset=1) self._hres = CSRStorage(hbits, vt["h_active"]) self._hsync_start = CSRStorage(hbits, vt["h_active"] + vt["h_sync_offset"]) self._hsync_end = CSRStorage( hbits, vt["h_active"] + vt["h_sync_offset"] + vt["h_sync_width"]) self._hscan = CSRStorage(hbits, vt["h_active"] + vt["h_blanking"]) self._vres = CSRStorage(vbits, vt["v_active"]) self._vsync_start = CSRStorage(vbits, vt["v_active"] + vt["v_sync_offset"]) self._vsync_end = CSRStorage( vbits, vt["v_active"] + vt["v_sync_offset"] + vt["v_sync_width"]) self._vscan = CSRStorage(vbits, vt["v_active"] + vt["v_blanking"]) # Video Timing Source self.source = source = stream.Endpoint(video_timing_layout) # # # # Resynchronize Enable to Video clock domain. self.enable = enable = Signal() self.specials += MultiReg(self._enable.storage, enable) # Resynchronize Horizontal Timings to Video clock domain. self.hres = hres = Signal(hbits) self.hsync_start = hsync_start = Signal(hbits) self.hsync_end = hsync_end = Signal(hbits) self.hscan = hscan = Signal(hbits) self.specials += MultiReg(self._hres.storage, hres) self.specials += MultiReg(self._hsync_start.storage, hsync_start) self.specials += MultiReg(self._hsync_end.storage, hsync_end) self.specials += MultiReg(self._hscan.storage, hscan) # Resynchronize Vertical Timings to Video clock domain. self.vres = vres = Signal(vbits) self.vsync_start = vsync_start = Signal(vbits) self.vsync_end = vsync_end = Signal(vbits) self.vscan = vscan = Signal(vbits) self.specials += MultiReg(self._vres.storage, vres) self.specials += MultiReg(self._vsync_start.storage, vsync_start) self.specials += MultiReg(self._vsync_end.storage, vsync_end) self.specials += MultiReg(self._vscan.storage, vscan) # Generate timings. hactive = Signal() vactive = Signal() fsm = FSM(reset_state="IDLE") fsm = ResetInserter()(fsm) self.submodules.fsm = fsm self.comb += fsm.reset.eq(~enable) fsm.act("IDLE", NextValue(hactive, 0), NextValue(vactive, 0), NextValue(source.hres, hres), NextValue(source.vres, vres), NextValue(source.hcount, 0), NextValue(source.vcount, 0), NextState("RUN")) self.comb += source.de.eq( hactive & vactive) # DE when both HActive and VActive. self.sync += source.first.eq((source.hcount == 0) & (source.vcount == 0)), self.sync += source.last.eq((source.hcount == hscan) & (source.vcount == vscan)), fsm.act( "RUN", source.valid.eq(1), If( source.ready, # Increment HCount. NextValue(source.hcount, source.hcount + 1), # Generate HActive / HSync. If(source.hcount == 0, NextValue(hactive, 1)), # Start of HActive. If(source.hcount == hres, NextValue(hactive, 0)), # End of HActive. If(source.hcount == hsync_start, NextValue(source.hsync, 1)), # Start of HSync. If(source.hcount == hsync_end, NextValue(source.hsync, 0)), # End of HSync. # End of HScan. If( source.hcount == hscan, # Reset HCount. NextValue(source.hcount, 0), # Increment VCount. NextValue(source.vcount, source.vcount + 1), # Generate VActive / VSync. If(source.vcount == 0, NextValue(vactive, 1)), # Start of VActive. If(source.vcount == vres, NextValue(vactive, 0)), # End of HActive. If(source.vcount == vsync_start, NextValue(source.vsync, 1)), # Start of VSync. If(source.vcount == vsync_end, NextValue(source.vsync, 0)), # End of VSync. # End of VScan. If( source.vcount == vscan, # Reset VCount. NextValue(source.vcount, 0), ))))
def __init__(self, pads, out_fifo, in_fifo): di = Signal(4) self.comb += [ pads.rs_t.oe.eq(1), pads.rw_t.oe.eq(1), pads.e_t.oe.eq(1), pads.d_t.oe.eq(~pads.rw_t.o), ] self.specials += [MultiReg(pads.d_t.i, di)] rx_setup_cyc = math.ceil(60e-9 * 30e6) e_pulse_cyc = math.ceil(500e-9 * 30e6) e_wait_cyc = math.ceil(700e-9 * 30e6) cmd_wait_cyc = math.ceil(1.52e-3 * 30e6) timer = Signal( max=max([rx_setup_cyc, e_pulse_cyc, e_wait_cyc, cmd_wait_cyc])) cmd = Signal(8) data = Signal(8) msb = Signal() self.submodules.fsm = FSM(reset_state="IDLE") self.fsm.act( "IDLE", NextValue(pads.e_t.o, 0), If(out_fifo.readable, out_fifo.re.eq(1), NextValue(cmd, out_fifo.dout), NextState("COMMAND"))) self.fsm.act( "COMMAND", NextValue(msb, (cmd & XFER_BIT_HALF) == 0), NextValue(pads.rs_t.o, (cmd & XFER_BIT_DATA) != 0), NextValue(pads.rw_t.o, (cmd & XFER_BIT_READ) != 0), If(cmd & XFER_BIT_WAIT, NextValue(timer, cmd_wait_cyc), NextState("WAIT")).Elif(cmd & XFER_BIT_READ, NextValue(timer, rx_setup_cyc), NextState("READ-SETUP")).Elif( out_fifo.readable, out_fifo.re.eq(1), NextValue(data, out_fifo.dout), NextState("WRITE"), )) self.fsm.act( "WRITE", If( timer == 0, NextValue(pads.e_t.o, 1), If(msb, NextValue(pads.d_t.o, data[4:])).Else(NextValue(pads.d_t.o, data[:4])), NextValue(timer, e_pulse_cyc), NextState("WRITE-HOLD")).Else(NextValue(timer, timer - 1))) self.fsm.act( "WRITE-HOLD", If(timer == 0, NextValue(pads.e_t.o, 0), NextValue(msb, ~msb), NextValue(timer, e_wait_cyc), If(msb, NextState("WRITE")).Else(NextState("WAIT"))).Else( NextValue(timer, timer - 1))) self.fsm.act( "READ-SETUP", If(timer == 0, NextValue(pads.e_t.o, 1), NextValue(timer, e_pulse_cyc), NextState("READ")).Else(NextValue(timer, timer - 1))) self.fsm.act( "READ", If( timer == 0, If(~(cmd & XFER_BIT_DATA) & msb & di[3], # BF=1, wait until it goes low ).Else( NextValue(pads.e_t.o, 0), NextValue(msb, ~msb), NextValue(timer, e_wait_cyc), If(msb, NextValue(data[4:], di), NextState("READ-SETUP")).Else( NextValue(data[:4], di), NextState("READ-PROCESS")))).Else( NextValue(timer, timer - 1))) self.fsm.act( "READ-PROCESS", If( cmd & XFER_BIT_DATA, If(in_fifo.writable, in_fifo.din.eq(data), in_fifo.we.eq(1), NextState("WAIT"))).Else( # done reading status register, ignore it and continue NextState("WAIT"))) self.fsm.act( "WAIT", If( timer == 0, NextState("IDLE"), ).Else(NextValue(timer, timer - 1)))
def __init__(self, input_pads, rtio_clk_freq=150e6): N = 64 self.reset = CSRStorage(reset=1) self.locked = CSRStatus() self.dt = CSRStatus(N.bit_length()) # # # self.clock_domains.cd_helper = ClockDomain(reset_less=True) helper_locked = Signal() helper_fb = Signal() helper_output = Signal() input_se = Signal() beat1 = Signal() beat2 = Signal() self.specials += [ Instance("MMCME2_BASE", p_CLKIN1_PERIOD=1e9/rtio_clk_freq, i_CLKIN1=ClockSignal("rtio"), i_RST=self.reset.storage, o_LOCKED=helper_locked, # VCO at 1200MHz with 150MHz RTIO frequency p_CLKFBOUT_MULT_F=8.0, p_DIVCLK_DIVIDE=1, o_CLKFBOUT=helper_fb, i_CLKFBIN=helper_fb, # helper PLL ratio: 64/65 (N=64) p_CLKOUT0_DIVIDE_F=8.125, o_CLKOUT0=helper_output, ), MultiReg(helper_locked, self.locked.status), Instance("BUFG", i_I=helper_output, o_O=self.cd_helper.clk), Instance("IBUFDS", i_I=input_pads.p, i_IB=input_pads.n, o_O=input_se), Instance("FD", i_C=self.cd_helper.clk, i_D=input_se, o_Q=beat1, attr={("IOB", "TRUE")}), Instance("FD", i_C=self.cd_helper.clk, i_D=ClockSignal("rtio"), o_Q=beat2), ] ed1 = DDMTDEdgeDetector(beat1) ed2 = DDMTDEdgeDetector(beat2) self.submodules += ed1, ed2 counting = Signal() counter = Signal(N.bit_length()) result = Signal.like(counter) self.sync.helper += [ If(counting, counter.eq(counter + 1) ).Else( result.eq(counter) ), If(ed1.rising, counting.eq(1), counter.eq(0)), If(ed2.rising, counting.eq(0)) ] bsync = BusSynchronizer(len(result), "helper", "sys") self.submodules += bsync self.comb += [ bsync.i.eq(result), self.dt.status.eq(bsync.o) ]
def __init__(self, pads, default=_default_edid): self._hpd_notif = CSRStatus() self._hpd_en = CSRStorage() mem_size = len(default) assert mem_size % 128 == 0 self.specials.mem = Memory(8, mem_size, init=default) # # # # HPD if hasattr(pads, "hpd_notif"): if hasattr(getattr(pads, "hpd_notif"), "inverted"): hpd_notif_n = Signal() self.comb += hpd_notif_n.eq(~pads.hpd_notif) self.specials += MultiReg(hpd_notif_n, self._hpd_notif.status) else: self.specials += MultiReg(pads.hpd_notif, self._hpd_notif.status) else: self.comb += self._hpd_notif.status.eq(1) if hasattr(pads, "hpd_en"): self.comb += pads.hpd_en.eq(self._hpd_en.storage) # EDID scl_raw = Signal() sda_i = Signal() sda_raw = Signal() sda_drv = Signal() _sda_drv_reg = Signal() _sda_i_async = Signal() self.sync += _sda_drv_reg.eq(sda_drv) pad_scl = getattr(pads, "scl") if hasattr(pad_scl, "inverted"): self.specials += MultiReg(~pads.scl, scl_raw) else: self.specials += MultiReg(pads.scl, scl_raw) if hasattr(pads, "sda_pu") and hasattr(pads, "sda_pd"): pad_sda = getattr(pads, "sda") if hasattr(pad_sda, "inverted"): self.specials += MultiReg(~pads.sda, sda_raw) else: self.specials += MultiReg(pads.sda, sda_raw) self.comb += [ pads.sda_pu.eq(0), pads.sda_pd.eq(_sda_drv_reg), ] else: self.specials += [ Tristate(pads.sda, 0, _sda_drv_reg, _sda_i_async), MultiReg(_sda_i_async, sda_raw), ] # for debug self.scl = scl_raw self.sda_i = sda_i self.sda_o = Signal() self.comb += self.sda_o.eq(~_sda_drv_reg) self.sda_oe = _sda_drv_reg scl_i = Signal() samp_count = Signal(6) samp_carry = Signal() self.sync += [ Cat(samp_count, samp_carry).eq(samp_count + 1), If(samp_carry, scl_i.eq(scl_raw), sda_i.eq(sda_raw)) ] scl_r = Signal() sda_r = Signal() scl_rising = Signal() sda_rising = Signal() sda_falling = Signal() self.sync += [scl_r.eq(scl_i), sda_r.eq(sda_i)] self.comb += [ scl_rising.eq(scl_i & ~scl_r), sda_rising.eq(sda_i & ~sda_r), sda_falling.eq(~sda_i & sda_r) ] start = Signal() self.comb += start.eq(scl_i & sda_falling) din = Signal(8) counter = Signal(max=9) self.sync += [ If(start, counter.eq(0)), If( scl_rising, If(counter == 8, counter.eq(0)).Else(counter.eq(counter + 1), din.eq(Cat(sda_i, din[:7])))) ] self.din = din self.counter = counter is_read = Signal() update_is_read = Signal() self.sync += If(update_is_read, is_read.eq(din[0])) offset_counter = Signal(max=mem_size) oc_load = Signal() oc_inc = Signal() self.sync += \ If(oc_load, offset_counter.eq(din) ).Elif(oc_inc, offset_counter.eq(offset_counter + 1) ) rdport = self.mem.get_port() self.specials += rdport self.comb += rdport.adr.eq(offset_counter) data_bit = Signal() zero_drv = Signal() data_drv = Signal() self.comb += \ If(zero_drv, sda_drv.eq(1) ).Elif(data_drv, sda_drv.eq(~data_bit) ) data_drv_en = Signal() data_drv_stop = Signal() self.sync += \ If(data_drv_en, data_drv.eq(1) ).Elif(data_drv_stop, data_drv.eq(0) ) self.sync += \ If(data_drv_en, chooser(rdport.dat_r, counter, data_bit, 8, reverse=True) ) self.submodules.fsm = fsm = FSM() fsm.act("WAIT_START") fsm.act( "RCV_ADDRESS", If( counter == 8, If(din[1:] == 0x50, update_is_read.eq(1), NextState("ACK_ADDRESS0")).Else(NextState("WAIT_START")))) fsm.act("ACK_ADDRESS0", If(~scl_i, NextState("ACK_ADDRESS1"))) fsm.act("ACK_ADDRESS1", zero_drv.eq(1), If(scl_i, NextState("ACK_ADDRESS2"))) fsm.act( "ACK_ADDRESS2", zero_drv.eq(1), If(~scl_i, If(is_read, NextState("READ")).Else(NextState("RCV_OFFSET")))) fsm.act("RCV_OFFSET", If(counter == 8, oc_load.eq(1), NextState("ACK_OFFSET0"))) fsm.act("ACK_OFFSET0", If(~scl_i, NextState("ACK_OFFSET1"))) fsm.act("ACK_OFFSET1", zero_drv.eq(1), If(scl_i, NextState("ACK_OFFSET2"))) fsm.act("ACK_OFFSET2", zero_drv.eq(1), If(~scl_i, NextState("RCV_ADDRESS"))) fsm.act( "READ", If( ~scl_i, If(counter == 8, data_drv_stop.eq(1), NextState("ACK_READ")).Else(data_drv_en.eq(1)))) fsm.act( "ACK_READ", If(scl_rising, oc_inc.eq(1), If(sda_i, NextState("WAIT_START")).Else(NextState("READ")))) for state in fsm.actions.keys(): fsm.act(state, If(start, NextState("RCV_ADDRESS"))) if hasattr(pads, "hpd_en"): fsm.act(state, If(~self._hpd_en.storage, NextState("WAIT_START")))
def __init__(self, pads): def io_bus(n): return Record([("oe", 1), ("i", n), ("o", n)]) # # # self.clk_enable = Signal() self.cs = Signal() self.dq = io_bus(32) self.rwds = io_bus(4) ## IO Delay shifting self.dly_io = delayf_pins() self.dly_clk = delayf_pins() dq = self.add_tristate( pads.dq) if not hasattr(pads.dq, "oe") else pads.dq rwds = self.add_tristate( pads.rwds) if not hasattr(pads.rwds, "oe") else pads.rwds # Shift non DDR signals to match the FF's inside DDR modules. self.specials += MultiReg(self.cs, pads.cs_n, n=3) self.specials += MultiReg(self.rwds.oe, rwds.oe, n=3) self.specials += MultiReg(self.dq.oe, dq.oe, n=3) # mask off clock when no CS clk_en = Signal() self.comb += clk_en.eq(self.clk_enable & ~self.cs) #clk_out clkp = Signal() clkn = Signal() self.specials += [ Instance("ODDRX2F", i_D3=clk_en, i_D2=0, i_D1=clk_en, i_D0=0, i_SCLK=ClockSignal("hr_90"), i_ECLK=ClockSignal("hr2x_90"), i_RST=ResetSignal("hr"), o_Q=clkp), Instance( "DELAYF", p_DEL_MODE="USER_DEFINED", p_DEL_VALUE=0, # (25ps per tap) i_A=clkp, i_LOADN=self.dly_clk.loadn, i_MOVE=self.dly_clk.move, i_DIRECTION=self.dly_clk.direction, o_Z=pads.clk_p) ] self.specials += [ Instance("ODDRX2F", i_D3=~clk_en, i_D2=1, i_D1=~clk_en, i_D0=1, i_SCLK=ClockSignal("hr_90"), i_ECLK=ClockSignal("hr2x_90"), i_RST=ResetSignal("hr"), o_Q=clkn), Instance( "DELAYF", p_DEL_MODE="USER_DEFINED", p_DEL_VALUE=0, # (25ps per tap) i_A=clkn, i_LOADN=self.dly_clk.loadn, i_MOVE=self.dly_clk.move, i_DIRECTION=self.dly_clk.direction, o_Z=pads.clk_n) ] # DQ_out for i in range(8): self.specials += [ Instance("ODDRX2F", i_D3=self.dq.o[i], i_D2=self.dq.o[8 + i], i_D1=self.dq.o[16 + i], i_D0=self.dq.o[24 + i], i_SCLK=ClockSignal("hr"), i_ECLK=ClockSignal("hr2x"), i_RST=ResetSignal("hr"), o_Q=dq.o[i]) ] # DQ_in for i in range(8): dq_in = Signal() self.specials += [ Instance("IDDRX2F", i_D=dq_in, i_SCLK=ClockSignal("hr"), i_ECLK=ClockSignal("hr2x"), i_RST=ResetSignal("hr"), o_Q3=self.dq.i[i], o_Q2=self.dq.i[i + 8], o_Q1=self.dq.i[i + 16], o_Q0=self.dq.i[i + 24]), Instance( "DELAYF", p_DEL_MODE="USER_DEFINED", p_DEL_VALUE=0, # (25ps per tap) i_A=dq.i[i], i_LOADN=self.dly_io.loadn, i_MOVE=self.dly_io.move, i_DIRECTION=self.dly_io.direction, o_Z=dq_in) ] # RWDS_out self.specials += [ Instance("ODDRX2F", i_D3=self.rwds.o[0], i_D2=self.rwds.o[1], i_D1=self.rwds.o[2], i_D0=self.rwds.o[3], i_SCLK=ClockSignal("hr"), i_ECLK=ClockSignal("hr2x"), i_RST=ResetSignal("hr"), o_Q=rwds.o) ] # RWDS_in rwds_in = Signal() self.specials += [ Instance("IDDRX2F", i_D=rwds_in, i_SCLK=ClockSignal("hr"), i_ECLK=ClockSignal("hr2x"), i_RST=ResetSignal("hr"), o_Q3=self.rwds.i[0], o_Q2=self.rwds.i[1], o_Q1=self.rwds.i[2], o_Q0=self.rwds.i[3]), Instance( "DELAYF", p_DEL_MODE="USER_DEFINED", p_DEL_VALUE=0, # (25ps per tap) i_A=rwds.i, i_LOADN=self.dly_io.loadn, i_MOVE=self.dly_io.move, i_DIRECTION=self.dly_io.direction, o_Z=rwds_in) ]
def __init__(self, pll, pads, mode="master"): self.tx_pattern = CSRStorage(20) self.tx_produce_square_wave = CSRStorage() self.tx_prbs_config = CSRStorage(2) self.rx_pattern = CSRStatus(20) self.rx_prbs_config = CSRStorage(2) self.rx_prbs_errors = CSRStatus(32) self.rx_bitslip_value = CSRStorage(5) self.rx_delay_rst = CSR() self.rx_delay_inc = CSRStorage() self.rx_delay_ce = CSR() # # # self.submodules.encoder = ClockDomainsRenamer("serdes")(Encoder( 2, True)) self.decoders = [ ClockDomainsRenamer("serdes")(Decoder(True)) for _ in range(2) ] self.submodules += self.decoders # clocking # master mode: # - linerate/10 pll refclk provided externally # - linerate/10 clock generated on clk_pads # slave mode: # - linerate/10 pll refclk provided by clk_pads self.clock_domains.cd_serdes = ClockDomain() self.clock_domains.cd_serdes_10x = ClockDomain() self.clock_domains.cd_serdes_2p5x = ClockDomain() self.comb += [ self.cd_serdes.clk.eq(pll.serdes_clk), self.cd_serdes_10x.clk.eq(pll.serdes_10x_clk), self.cd_serdes_2p5x.clk.eq(pll.serdes_2p5x_clk) ] self.specials += [ AsyncResetSynchronizer(self.cd_serdes, ~pll.lock), AsyncResetSynchronizer(self.cd_serdes_10x, ~pll.lock), AsyncResetSynchronizer(self.cd_serdes_2p5x, ~pll.lock) ] # control/status cdc tx_pattern = Signal(20) tx_produce_square_wave = Signal() tx_prbs_config = Signal(2) rx_pattern = Signal(20) rx_prbs_config = Signal(2) rx_prbs_errors = Signal(32) rx_bitslip_value = Signal(5) self.specials += [ MultiReg(self.tx_pattern.storage, tx_pattern, "serdes"), MultiReg(self.tx_produce_square_wave.storage, tx_produce_square_wave, "serdes"), MultiReg(self.tx_prbs_config.storage, tx_prbs_config, "serdes"), ] self.specials += [ MultiReg(rx_pattern, self.rx_pattern.status, "sys"), MultiReg(self.rx_prbs_config.storage, rx_prbs_config, "serdes"), MultiReg(rx_prbs_errors, self.rx_prbs_errors.status, "sys"), # FIXME ] self.specials += MultiReg(self.rx_bitslip_value.storage, rx_bitslip_value, "serdes"), # tx clock (linerate/10) if mode == "master": self.submodules.tx_clk_gearbox = Gearbox(20, "serdes", 8, "serdes_2p5x") self.comb += self.tx_clk_gearbox.i.eq(0b11111000001111100000) clk_o = Signal() self.specials += [ Instance("OSERDESE2", p_DATA_WIDTH=8, p_TRISTATE_WIDTH=1, p_DATA_RATE_OQ="DDR", p_DATA_RATE_TQ="BUF", p_SERDES_MODE="MASTER", o_OQ=clk_o, i_OCE=1, i_RST=ResetSignal("serdes_2p5x"), i_CLK=ClockSignal("serdes_10x"), i_CLKDIV=ClockSignal("serdes_2p5x"), i_D1=self.tx_clk_gearbox.o[0], i_D2=self.tx_clk_gearbox.o[1], i_D3=self.tx_clk_gearbox.o[2], i_D4=self.tx_clk_gearbox.o[3], i_D5=self.tx_clk_gearbox.o[4], i_D6=self.tx_clk_gearbox.o[5], i_D7=self.tx_clk_gearbox.o[6], i_D8=self.tx_clk_gearbox.o[7]), Instance("OBUFDS", i_I=clk_o, o_O=pads.clk_p, o_OB=pads.clk_n) ] # tx data and prbs self.submodules.tx_prbs = ClockDomainsRenamer("serdes")(PRBSTX( 20, True)) self.comb += self.tx_prbs.config.eq(tx_prbs_config) self.submodules.tx_gearbox = Gearbox(20, "serdes", 8, "serdes_2p5x") self.sync.serdes += [ self.tx_prbs.i.eq(Cat(*[self.encoder.output[i] for i in range(2)])), If(tx_pattern != 0, self.tx_gearbox.i.eq(tx_pattern)).Elif( tx_produce_square_wave, # square wave @ linerate/20 for scope observation self.tx_gearbox.i.eq(0b11111111110000000000)).Else( self.tx_gearbox.i.eq(self.tx_prbs.o)) ] serdes_o = Signal() self.specials += [ Instance("OSERDESE2", p_DATA_WIDTH=8, p_TRISTATE_WIDTH=1, p_DATA_RATE_OQ="DDR", p_DATA_RATE_TQ="BUF", p_SERDES_MODE="MASTER", o_OQ=serdes_o, i_OCE=1, i_RST=ResetSignal("serdes_2p5x"), i_CLK=ClockSignal("serdes_10x"), i_CLKDIV=ClockSignal("serdes_2p5x"), i_D1=self.tx_gearbox.o[0], i_D2=self.tx_gearbox.o[1], i_D3=self.tx_gearbox.o[2], i_D4=self.tx_gearbox.o[3], i_D5=self.tx_gearbox.o[4], i_D6=self.tx_gearbox.o[5], i_D7=self.tx_gearbox.o[6], i_D8=self.tx_gearbox.o[7]), Instance("OBUFDS", i_I=serdes_o, o_O=pads.tx_p, o_OB=pads.tx_n) ] # rx clock use_bufr = False if mode == "slave": clk_i = Signal() clk_i_bufg = Signal() self.specials += [ Instance("IBUFDS", i_I=pads.clk_p, i_IB=pads.clk_n, o_O=clk_i) ] if use_bufr: clk_i_bufr = Signal() self.specials += [ Instance("BUFR", i_I=clk_i, o_O=clk_i_bufr), Instance("BUFG", i_I=clk_i_bufr, o_O=clk_i_bufg), ] else: self.specials += Instance("BUFG", i_I=clk_i, o_O=clk_i_bufg), self.comb += pll.refclk.eq(clk_i_bufg) # rx self.submodules.rx_gearbox = Gearbox(8, "serdes_2p5x", 20, "serdes") self.submodules.rx_bitslip = ClockDomainsRenamer("serdes")(BitSlip(20)) self.submodules.phase_detector = ClockDomainsRenamer("serdes_2p5x")( PhaseDetector()) # use 2 serdes for phase detection: 1 master/ 1 slave serdes_m_i_nodelay = Signal() serdes_s_i_nodelay = Signal() self.specials += [ Instance( "IBUFDS_DIFF_OUT", i_I=pads.rx_p, i_IB=pads.rx_n, o_O=serdes_m_i_nodelay, o_OB=serdes_s_i_nodelay, ) ] serdes_m_i_delayed = Signal() serdes_m_q = Signal(8) serdes_m_idelay_value = int(1 / (4 * pll.linerate) / 78e-12) # 1/4 bit period assert serdes_m_idelay_value < 32 self.specials += [ Instance("IDELAYE2", p_DELAY_SRC="IDATAIN", p_SIGNAL_PATTERN="DATA", p_CINVCTRL_SEL="FALSE", p_HIGH_PERFORMANCE_MODE="TRUE", p_REFCLK_FREQUENCY=200.0, p_PIPE_SEL="FALSE", p_IDELAY_TYPE="VARIABLE", p_IDELAY_VALUE=serdes_m_idelay_value, i_C=ClockSignal(), i_LD=self.rx_delay_rst.re, i_CE=self.rx_delay_ce.re, i_LDPIPEEN=0, i_INC=self.rx_delay_inc.storage, i_IDATAIN=serdes_m_i_nodelay, o_DATAOUT=serdes_m_i_delayed), Instance("ISERDESE2", p_DATA_WIDTH=8, p_DATA_RATE="DDR", p_SERDES_MODE="MASTER", p_INTERFACE_TYPE="NETWORKING", p_NUM_CE=1, p_IOBDELAY="IFD", i_DDLY=serdes_m_i_delayed, i_CE1=1, i_RST=ResetSignal("serdes_2p5x"), i_CLK=ClockSignal("serdes_10x"), i_CLKB=~ClockSignal("serdes_10x"), i_CLKDIV=ClockSignal("serdes_2p5x"), i_BITSLIP=0, o_Q8=serdes_m_q[0], o_Q7=serdes_m_q[1], o_Q6=serdes_m_q[2], o_Q5=serdes_m_q[3], o_Q4=serdes_m_q[4], o_Q3=serdes_m_q[5], o_Q2=serdes_m_q[6], o_Q1=serdes_m_q[7]), ] self.comb += self.phase_detector.mdata.eq(serdes_m_q) serdes_s_i_delayed = Signal() serdes_s_q = Signal(8) serdes_s_idelay_value = int(1 / (2 * pll.linerate) / 78e-12) # 1/2 bit period assert serdes_s_idelay_value < 32 self.specials += [ Instance("IDELAYE2", p_DELAY_SRC="IDATAIN", p_SIGNAL_PATTERN="DATA", p_CINVCTRL_SEL="FALSE", p_HIGH_PERFORMANCE_MODE="TRUE", p_REFCLK_FREQUENCY=200.0, p_PIPE_SEL="FALSE", p_IDELAY_TYPE="VARIABLE", p_IDELAY_VALUE=serdes_s_idelay_value, i_C=ClockSignal(), i_LD=self.rx_delay_rst.re, i_CE=self.rx_delay_ce.re, i_LDPIPEEN=0, i_INC=self.rx_delay_inc.storage, i_IDATAIN=serdes_s_i_nodelay, o_DATAOUT=serdes_s_i_delayed), Instance("ISERDESE2", p_DATA_WIDTH=8, p_DATA_RATE="DDR", p_SERDES_MODE="MASTER", p_INTERFACE_TYPE="NETWORKING", p_NUM_CE=1, p_IOBDELAY="IFD", i_DDLY=serdes_s_i_delayed, i_CE1=1, i_RST=ResetSignal("serdes_2p5x"), i_CLK=ClockSignal("serdes_10x"), i_CLKB=~ClockSignal("serdes_10x"), i_CLKDIV=ClockSignal("serdes_2p5x"), i_BITSLIP=0, o_Q8=serdes_s_q[0], o_Q7=serdes_s_q[1], o_Q6=serdes_s_q[2], o_Q5=serdes_s_q[3], o_Q4=serdes_s_q[4], o_Q3=serdes_s_q[5], o_Q2=serdes_s_q[6], o_Q1=serdes_s_q[7]), ] self.comb += self.phase_detector.sdata.eq(~serdes_s_q) # rx data and prbs self.submodules.rx_prbs = ClockDomainsRenamer("serdes")(PRBSRX( 20, True)) self.comb += [ self.rx_prbs.config.eq(rx_prbs_config), rx_prbs_errors.eq(self.rx_prbs.errors) ] self.comb += [ self.rx_gearbox.i.eq(serdes_m_q), self.rx_bitslip.value.eq(rx_bitslip_value), self.rx_bitslip.i.eq(self.rx_gearbox.o), rx_pattern.eq(self.rx_gearbox.o), self.decoders[0].input.eq(self.rx_bitslip.o[:10]), self.decoders[1].input.eq(self.rx_bitslip.o[10:]), self.rx_prbs.i.eq(self.rx_bitslip.o) ]
def __init__(self, platform, pads, data_width=64, bar0_size=1*MB, cd="sys", pll1=None): self.sink = stream.Endpoint(phy_layout(data_width)) self.source = stream.Endpoint(phy_layout(data_width)) self.msi = stream.Endpoint(msi_layout()) self._lnk_up = CSRStatus() self._msi_enable = CSRStatus() self._bus_master_enable = CSRStatus() self._max_request_size = CSRStatus(16) self._max_payload_size = CSRStatus(16) self.data_width = data_width self.id = Signal(16) self.bar0_size = bar0_size self.bar0_mask = get_bar_mask(bar0_size) self.max_request_size = Signal(16) self.max_payload_size = Signal(16) # # # # clocking pcie_clk = Signal() pcie_rst = Signal() pcie_refclk = Signal() self.specials += Instance("IBUFDS_GTE2", i_CEB=0, i_I=pads.clk_p, i_IB=pads.clk_n, o_O=pcie_refclk ) pcie_refclk.attr.add("keep") platform.add_period_constraint(pcie_refclk, 10.0) self.clock_domains.cd_pcie = ClockDomain() self.clock_domains.cd_pcie_reset_less = ClockDomain(reset_less=True) self.cd_pcie.clk.attr.add("keep") platform.add_period_constraint(self.cd_pcie.clk, 8.0) pcie_refclk_present = Signal() pcie_refclk_timer = ClockDomainsRenamer("pcie_reset_less")(WaitTimer(1024)) self.submodules += pcie_refclk_timer self.comb += [ pcie_refclk_timer.wait.eq(1), pcie_refclk_present.eq(pcie_refclk_timer.done) ] self.comb += [ self.cd_pcie.clk.eq(pcie_clk), self.cd_pcie.rst.eq(pcie_rst & pcie_refclk_present), self.cd_pcie_reset_less.clk.eq(pcie_clk), ] # tx cdc (fpga --> host) if cd == "pcie": s_axis_tx = self.sink else: tx_buffer = stream.Buffer(phy_layout(data_width)) tx_buffer = ClockDomainsRenamer(cd)(tx_buffer) tx_cdc = stream.AsyncFIFO(phy_layout(data_width), 4) tx_cdc = ClockDomainsRenamer({"write": cd, "read": "pcie"})(tx_cdc) self.submodules += tx_buffer, tx_cdc self.comb += [ self.sink.connect(tx_buffer.sink), tx_buffer.source.connect(tx_cdc.sink) ] s_axis_tx = tx_cdc.source # rx cdc (host --> fpga) if cd == "pcie": m_axis_rx = self.source else: rx_cdc = stream.AsyncFIFO(phy_layout(data_width), 4) rx_cdc = ClockDomainsRenamer({"write": "pcie", "read": cd})(rx_cdc) rx_buffer = stream.Buffer(phy_layout(data_width)) rx_buffer = ClockDomainsRenamer(cd)(rx_buffer) self.submodules += rx_buffer, rx_cdc self.comb += [ rx_cdc.source.connect(rx_buffer.sink), rx_buffer.source.connect(self.source) ] m_axis_rx = rx_cdc.sink # msi cdc (fpga --> host) if cd == "pcie": cfg_msi = self.msi else: msi_cdc = stream.AsyncFIFO(msi_layout(), 4) msi_cdc = ClockDomainsRenamer({"write": cd, "read": "pcie"})(msi_cdc) self.submodules += msi_cdc self.comb += self.msi.connect(msi_cdc.sink) cfg_msi = msi_cdc.source # config def convert_size(command, size): cases = {} value = 128 for i in range(6): cases[i] = size.eq(value) value = value*2 return Case(command, cases) lnk_up = Signal() msienable = Signal() bus_number = Signal(8) device_number = Signal(5) function_number = Signal(3) command = Signal(16) dcommand = Signal(16) self.sync.pcie += [ convert_size(dcommand[12:15], self.max_request_size), convert_size(dcommand[5:8], self.max_payload_size), self.id.eq(Cat(function_number, device_number, bus_number)) ] self.specials += [ MultiReg(lnk_up, self._lnk_up.status), MultiReg(command[2], self._bus_master_enable.status), MultiReg(msienable, self._msi_enable.status), MultiReg(self.max_request_size, self._max_request_size.status), MultiReg(self.max_payload_size, self._max_payload_size.status) ] # hard ip self.specials += Instance("pcie_phy", p_C_DATA_WIDTH=data_width, p_C_PCIE_GT_DEVICE={ "xc7k": "GTX", "xc7a": "GTP"}[platform.device[:4]], p_C_BAR0=get_bar_mask(bar0_size), i_sys_clk=pcie_refclk, i_sys_rst_n=1 if not hasattr(pads, "rst_n") else pads.rst_n, o_pci_exp_txp=pads.tx_p, o_pci_exp_txn=pads.tx_n, i_pci_exp_rxp=pads.rx_p, i_pci_exp_rxn=pads.rx_n, o_user_clk=pcie_clk, o_user_reset=pcie_rst, o_user_lnk_up=lnk_up, #o_tx_buf_av=, #o_tx_terr_drop=, #o_tx_cfg_req=, i_tx_cfg_gnt=1, i_s_axis_tx_tvalid=s_axis_tx.valid, i_s_axis_tx_tlast=s_axis_tx.last, o_s_axis_tx_tready=s_axis_tx.ready, i_s_axis_tx_tdata=s_axis_tx.dat, i_s_axis_tx_tkeep=s_axis_tx.be, i_s_axis_tx_tuser=0, i_rx_np_ok=1, i_rx_np_req=1, o_m_axis_rx_tvalid=m_axis_rx.valid, o_m_axis_rx_tlast=m_axis_rx.last, i_m_axis_rx_tready=m_axis_rx.ready, o_m_axis_rx_tdata=m_axis_rx.dat, o_m_axis_rx_tkeep=m_axis_rx.be, #o_m_axis_rx_tuser=, #o_cfg_to_turnoff=, o_cfg_bus_number=bus_number, o_cfg_device_number=device_number, o_cfg_function_number=function_number, o_cfg_command=command, o_cfg_dcommand=dcommand, o_cfg_interrupt_msienable=msienable, i_cfg_interrupt=cfg_msi.valid, o_cfg_interrupt_rdy=cfg_msi.ready, i_cfg_interrupt_di=cfg_msi.dat, p_QPLL_PLL1_FBDIV=4 if pll1 is None else pll1.config["n2"], p_QPLL_PLL1_FBDIV_45=4 if pll1 is None else pll1.config["n1"], p_QPLL_PLL1_REFCLK_DIV=1 if pll1 is None else pll1.config["m"], i_QPLL_GTGREFCLK1=0 if pll1 is None else pll1.gtgrefclk, i_QPLL_GTREFCLK1=0 if pll1 is None else pll1.gtrefclk, i_QPLL_PLL1LOCKEN=1, i_QPLL_PLL1PD=1 if pll1 is None else 0, i_QPLL_PLL1REFCLKSEL=0b001 if pll1 is None else pll1.refclksel, i_QPLL_PLL1RESET=1 if pll1 is None else pll1.reset, o_QPLL_PLL1LOCK=Signal() if pll1 is None else pll1.lock, o_QPLL_PLL1OUTCLK=Signal() if pll1 is None else pll1.clk, o_QPLL_PLL1OUTREFCLK=Signal() if pll1 is None else pll1.refclk ) litepcie_phy_path = os.path.abspath(os.path.dirname(__file__)) platform.add_source_dir(os.path.join(litepcie_phy_path, "xilinx", "7-series", "common")) platform.add_source(os.path.join(litepcie_phy_path, "xilinx", "7-series", "common", "xpm_cdc.sv")) if platform.device[:4] == "xc7k": platform.add_source_dir(os.path.join(litepcie_phy_path, "xilinx", "7-series", "kintex7")) elif platform.device[:4] == "xc7a": platform.add_source_dir(os.path.join(litepcie_phy_path, "xilinx", "7-series", "artix7"))
def __init__(self, pll, tx_pads, rx_pads, sys_clk_freq, data_width=20, tx_buffer_enable=False, rx_buffer_enable=False, clock_aligner=True, clock_aligner_comma=0b0101111100, tx_polarity=0, rx_polarity=0, pll_master=True): assert (data_width == 20) or (data_width == 40) # TX controls self.tx_enable = Signal() self.tx_ready = Signal() self.tx_inhibit = Signal() self.tx_produce_square_wave = Signal() self.tx_produce_pattern = Signal() self.tx_pattern = Signal(data_width) self.tx_prbs_config = Signal(2) # RX controls self.rx_enable = Signal() self.rx_ready = Signal() self.rx_align = Signal(reset=1) self.rx_prbs_config = Signal(2) self.rx_prbs_errors = Signal(32) # DRP self.drp = DRPInterface() # Loopback self.loopback = Signal(3) # # # self.nwords = nwords = data_width//10 self.submodules.encoder = ClockDomainsRenamer("tx")(Encoder(nwords, True)) self.decoders = [ClockDomainsRenamer("rx")(Decoder(True)) for _ in range(nwords)] self.submodules += self.decoders # Transceiver direct clock outputs (useful to specify clock constraints) self.txoutclk = Signal() self.rxoutclk = Signal() self.tx_clk_freq = pll.config["linerate"]/data_width self.rx_clk_freq = pll.config["linerate"]/data_width # Control/Status CDC tx_produce_square_wave = Signal() tx_produce_pattern = Signal() tx_prbs_config = Signal(2) rx_prbs_config = Signal(2) rx_prbs_errors = Signal(32) self.specials += [ MultiReg(self.tx_produce_square_wave, tx_produce_square_wave, "tx"), MultiReg(self.tx_produce_pattern, tx_produce_pattern, "tx"), MultiReg(self.tx_prbs_config, tx_prbs_config, "tx"), ] self.specials += [ MultiReg(self.rx_prbs_config, rx_prbs_config, "rx"), MultiReg(rx_prbs_errors, self.rx_prbs_errors, "sys"), # FIXME ] # # # use_cpll = isinstance(pll, GTXChannelPLL) use_qpll = isinstance(pll, GTXQuadPLL) rxcdr_cfgs = { 1 : 0x03000023ff10400020, 2 : 0x03000023ff10200020, 4 : 0x03000023ff10100020, 8 : 0x03000023ff10080020, 16 : 0x03000023ff10080020, } # TX init ---------------------------------------------------------------------------------- self.submodules.tx_init = tx_init = GTXTXInit(sys_clk_freq, buffer_enable=tx_buffer_enable) self.comb += [ self.tx_ready.eq(tx_init.done), tx_init.restart.eq(~self.tx_enable) ] # RX init ---------------------------------------------------------------------------------- self.submodules.rx_init = rx_init = GTXRXInit(sys_clk_freq, buffer_enable=rx_buffer_enable) self.comb += [ self.rx_ready.eq(rx_init.done), rx_init.restart.eq(~self.rx_enable) ] # PLL ---------------------------------------------------------------------------------- self.comb += [ tx_init.plllock.eq(pll.lock), rx_init.plllock.eq(pll.lock) ] if pll_master: self.comb += pll.reset.eq(tx_init.pllreset) # DRP mux ---------------------------------------------------------------------------------- self.submodules.drp_mux = drp_mux = DRPMux() drp_mux.add_interface(self.drp) # GTXE2_CHANNEL instance ------------------------------------------------------------------- txdata = Signal(data_width) rxdata = Signal(data_width) self.gtx_params = dict( # Simulation-Only Attributes p_SIM_RECEIVER_DETECT_PASS = "******", p_SIM_TX_EIDLE_DRIVE_LEVEL = "X", p_SIM_RESET_SPEEDUP = "FALSE", p_SIM_CPLLREFCLK_SEL = "FALSE", p_SIM_VERSION = "4.0", # RX Byte and Word Alignment Attributes p_ALIGN_COMMA_DOUBLE = "FALSE", p_ALIGN_COMMA_ENABLE = 0b1111111111, p_ALIGN_COMMA_WORD = 2 if data_width == 20 else 4, p_ALIGN_MCOMMA_DET = "TRUE", p_ALIGN_MCOMMA_VALUE = 0b1010000011, p_ALIGN_PCOMMA_DET = "TRUE", p_ALIGN_PCOMMA_VALUE = 0b0101111100, p_SHOW_REALIGN_COMMA = "TRUE", p_RXSLIDE_AUTO_WAIT = 7, p_RXSLIDE_MODE = "OFF" if rx_buffer_enable else "PCS", p_RX_SIG_VALID_DLY = 10, # RX 8B/10B Decoder Attributes p_RX_DISPERR_SEQ_MATCH = "TRUE", p_DEC_MCOMMA_DETECT = "TRUE", p_DEC_PCOMMA_DETECT = "TRUE", p_DEC_VALID_COMMA_ONLY = "TRUE", # RX Clock Correction Attributes p_CBCC_DATA_SOURCE_SEL = "DECODED", p_CLK_COR_SEQ_2_USE = "FALSE", p_CLK_COR_KEEP_IDLE = "FALSE", p_CLK_COR_MAX_LAT = 9 if data_width == 20 else 20, p_CLK_COR_MIN_LAT = 7 if data_width == 20 else 16, p_CLK_COR_PRECEDENCE = "TRUE", p_CLK_COR_REPEAT_WAIT = 0, p_CLK_COR_SEQ_LEN = 1, p_CLK_COR_SEQ_1_ENABLE = 0b1111, p_CLK_COR_SEQ_1_1 = 0b0100000000, p_CLK_COR_SEQ_1_2 = 0b0000000000, p_CLK_COR_SEQ_1_3 = 0b0000000000, p_CLK_COR_SEQ_1_4 = 0b0000000000, p_CLK_CORRECT_USE = "FALSE", p_CLK_COR_SEQ_2_ENABLE = 0b1111, p_CLK_COR_SEQ_2_1 = 0b0100000000, p_CLK_COR_SEQ_2_2 = 0b0000000000, p_CLK_COR_SEQ_2_3 = 0b0000000000, p_CLK_COR_SEQ_2_4 = 0b0000000000, # RX Channel Bonding Attributes p_CHAN_BOND_KEEP_ALIGN = "FALSE", p_CHAN_BOND_MAX_SKEW = 1, p_CHAN_BOND_SEQ_LEN = 1, p_CHAN_BOND_SEQ_1_1 = 0b0000000000, p_CHAN_BOND_SEQ_1_2 = 0b0000000000, p_CHAN_BOND_SEQ_1_3 = 0b0000000000, p_CHAN_BOND_SEQ_1_4 = 0b0000000000, p_CHAN_BOND_SEQ_1_ENABLE = 0b1111, p_CHAN_BOND_SEQ_2_1 = 0b0000000000, p_CHAN_BOND_SEQ_2_2 = 0b0000000000, p_CHAN_BOND_SEQ_2_3 = 0b0000000000, p_CHAN_BOND_SEQ_2_4 = 0b0000000000, p_CHAN_BOND_SEQ_2_ENABLE = 0b1111, p_CHAN_BOND_SEQ_2_USE = "FALSE", p_FTS_DESKEW_SEQ_ENABLE = 0b1111, p_FTS_LANE_DESKEW_CFG = 0b1111, p_FTS_LANE_DESKEW_EN = "FALSE", # RX Margin Analysis Attributes p_ES_CONTROL = 0b000000, p_ES_ERRDET_EN = "FALSE", p_ES_EYE_SCAN_EN = "TRUE", p_ES_HORZ_OFFSET = 0x000, p_ES_PMA_CFG = 0b0000000000, p_ES_PRESCALE = 0b00000, p_ES_QUALIFIER = 0x00000000000000000000, p_ES_QUAL_MASK = 0x00000000000000000000, p_ES_SDATA_MASK = 0x00000000000000000000, p_ES_VERT_OFFSET = 0b000000000, # FPGA RX Interface Attributes p_RX_DATA_WIDTH = data_width, # PMA Attributes p_OUTREFCLK_SEL_INV = 0b11, p_PMA_RSV = 0x001e7080, p_PMA_RSV2 = 0x2050, p_PMA_RSV3 = 0b00, p_PMA_RSV4 = 0x00000000, p_RX_BIAS_CFG = 0b000000000100, p_DMONITOR_CFG = 0x000A00, p_RX_CM_SEL = 0b11, p_RX_CM_TRIM = 0b010, p_RX_DEBUG_CFG = 0b000000000000, p_RX_OS_CFG = 0b0000010000000, p_TERM_RCAL_CFG = 0b10000, p_TERM_RCAL_OVRD = 0b0, p_TST_RSV = 0x00000000, p_RX_CLK25_DIV = 5, p_TX_CLK25_DIV = 5, p_UCODEER_CLR = 0b0, # PCI Express Attributes p_PCS_PCIE_EN = "FALSE", # PCS Attributes p_PCS_RSVD_ATTR = 0x000000000000, # RX Buffer Attributes p_RXBUF_ADDR_MODE = "FAST", p_RXBUF_EIDLE_HI_CNT = 0b1000, p_RXBUF_EIDLE_LO_CNT = 0b0000, p_RXBUF_EN = "TRUE" if rx_buffer_enable else "FALSE", p_RX_BUFFER_CFG = 0b000000, p_RXBUF_RESET_ON_CB_CHANGE = "TRUE", p_RXBUF_RESET_ON_COMMAALIGN = "FALSE", p_RXBUF_RESET_ON_EIDLE = "FALSE", p_RXBUF_RESET_ON_RATE_CHANGE = "TRUE", p_RXBUFRESET_TIME = 0b00001, p_RXBUF_THRESH_OVFLW = 61, p_RXBUF_THRESH_OVRD = "FALSE", p_RXBUF_THRESH_UNDFLW = 4, p_RXDLY_CFG = 0x001F, p_RXDLY_LCFG = 0x030, p_RXDLY_TAP_CFG = 0x0000, p_RXPH_CFG = 0x000000, p_RXPHDLY_CFG = 0x084020, p_RXPH_MONITOR_SEL = 0b00000, p_RX_XCLK_SEL = "RXREC" if rx_buffer_enable else "RXUSR", p_RX_DDI_SEL = 0b000000, p_RX_DEFER_RESET_BUF_EN = "TRUE", # CDR Attributes p_RXCDR_CFG = rxcdr_cfgs[pll.config["d"]], p_RXCDR_FR_RESET_ON_EIDLE = 0b0, p_RXCDR_HOLD_DURING_EIDLE = 0b0, p_RXCDR_PH_RESET_ON_EIDLE = 0b0, p_RXCDR_LOCK_CFG = 0b010101, # RX Initialization and Reset Attributes p_RXCDRFREQRESET_TIME = 0b00001, p_RXCDRPHRESET_TIME = 0b00001, p_RXISCANRESET_TIME = 0b00001, p_RXPCSRESET_TIME = 0b00001, p_RXPMARESET_TIME = 0b00011, # RX OOB Signaling Attributes p_RXOOB_CFG = 0b0000110, # RX Gearbox Attributes p_RXGEARBOX_EN = "FALSE", p_GEARBOX_MODE = 0b000, # PRBS Detection Attribute p_RXPRBS_ERR_LOOPBACK = 0b0, # Power-Down Attributes p_PD_TRANS_TIME_FROM_P2 = 0x03c, p_PD_TRANS_TIME_NONE_P2 = 0x3c, p_PD_TRANS_TIME_TO_P2 = 0x64, # RX OOB Signaling Attributes p_SAS_MAX_COM = 64, p_SAS_MIN_COM = 36, p_SATA_BURST_SEQ_LEN = 0b0101, p_SATA_BURST_VAL = 0b100, p_SATA_EIDLE_VAL = 0b100, p_SATA_MAX_BURST = 8, p_SATA_MAX_INIT = 21, p_SATA_MAX_WAKE = 7, p_SATA_MIN_BURST = 4, p_SATA_MIN_INIT = 12, p_SATA_MIN_WAKE = 4, # RX Fabric Clock Output Control Attributes p_TRANS_TIME_RATE = 0x0E, # TX Buffer Attributes p_TXBUF_EN = "TRUE" if tx_buffer_enable else "FALSE", p_TXBUF_RESET_ON_RATE_CHANGE = "TRUE", p_TXDLY_CFG = 0x001F, p_TXDLY_LCFG = 0x030, p_TXDLY_TAP_CFG = 0x0000, p_TXPH_CFG = 0x0780, p_TXPHDLY_CFG = 0x084020, p_TXPH_MONITOR_SEL = 0b00000, p_TX_XCLK_SEL = "TXOUT" if tx_buffer_enable else "TXUSR", # FPGA TX Interface Attributes p_TX_DATA_WIDTH = data_width, # TX Configurable Driver Attributes p_TX_DEEMPH0 = 0b00000, p_TX_DEEMPH1 = 0b00000, p_TX_EIDLE_ASSERT_DELAY = 0b110, p_TX_EIDLE_DEASSERT_DELAY = 0b100, p_TX_LOOPBACK_DRIVE_HIZ = "FALSE", p_TX_MAINCURSOR_SEL = 0b0, p_TX_DRIVE_MODE = "DIRECT", p_TX_MARGIN_FULL_0 = 0b1001110, p_TX_MARGIN_FULL_1 = 0b1001001, p_TX_MARGIN_FULL_2 = 0b1000101, p_TX_MARGIN_FULL_3 = 0b1000010, p_TX_MARGIN_FULL_4 = 0b1000000, p_TX_MARGIN_LOW_0 = 0b1000110, p_TX_MARGIN_LOW_1 = 0b1000100, p_TX_MARGIN_LOW_2 = 0b1000010, p_TX_MARGIN_LOW_3 = 0b1000000, p_TX_MARGIN_LOW_4 = 0b1000000, # TX Gearbox Attributes p_TXGEARBOX_EN = "FALSE", # TX Initialization and Reset Attributes p_TXPCSRESET_TIME = 0b00001, p_TXPMARESET_TIME = 0b00001, # TX Receiver Detection Attributes p_TX_RXDETECT_CFG = 0x1832, p_TX_RXDETECT_REF = 0b100, # CPLL Attributes p_CPLL_CFG = 0xBC07DC, p_CPLL_FBDIV = 1 if use_qpll else pll.config["n2"], p_CPLL_FBDIV_45 = 4 if use_qpll else pll.config["n1"], p_CPLL_INIT_CFG = 0x00001E, p_CPLL_LOCK_CFG = 0x01E8, p_CPLL_REFCLK_DIV = 1 if use_qpll else pll.config["m"], p_RXOUT_DIV = pll.config["d"], p_TXOUT_DIV = pll.config["d"], p_SATA_CPLL_CFG = "VCO_3000MHZ", # RX Initialization and Reset Attributes p_RXDFELPMRESET_TIME = 0b0001111, # RX Equalizer Attributes p_RXLPM_HF_CFG = 0b00000011110000, p_RXLPM_LF_CFG = 0b00000011110000, p_RX_DFE_GAIN_CFG = 0x020FEA, p_RX_DFE_H2_CFG = 0b000000000000, p_RX_DFE_H3_CFG = 0b000001000000, p_RX_DFE_H4_CFG = 0b00011110000, p_RX_DFE_H5_CFG = 0b00011100000, p_RX_DFE_KL_CFG = 0b0000011111110, p_RX_DFE_LPM_CFG = 0x0954, p_RX_DFE_LPM_HOLD_DURING_EIDLE = 0b0, p_RX_DFE_UT_CFG = 0b10001111000000000, p_RX_DFE_VP_CFG = 0b00011111100000011, # Power-Down Attributes p_RX_CLKMUX_PD = 0b1, p_TX_CLKMUX_PD = 0b1, # FPGA RX Interface Attribute p_RX_INT_DATAWIDTH = data_width == 40, # FPGA TX Interface Attribute p_TX_INT_DATAWIDTH = data_width == 40, # TX Configurable Driver Attributes p_TX_QPI_STATUS_EN = 0b0, # RX Equalizer Attributes p_RX_DFE_KL_CFG2 = 0x301148AC, p_RX_DFE_XYD_CFG = 0b0000000000000, # TX Configurable Driver Attributes p_TX_PREDRIVER_MODE = 0b0 ) self.gtx_params.update( # CPLL Ports #o_CPLLFBCLKLOST =, o_CPLLLOCK = Signal() if use_qpll else pll.lock, i_CPLLLOCKDETCLK = ClockSignal(), i_CPLLLOCKEN = 1, i_CPLLPD = 0, #o_CPLLREFCLKLOST = , i_CPLLREFCLKSEL = 0b001, i_CPLLRESET = 0 if use_qpll else pll.reset, i_GTRSVD = 0b0000000000000000, i_PCSRSVDIN = 0b0000000000000000, i_PCSRSVDIN2 = 0b00000, i_PMARSVDIN = 0b00000, i_PMARSVDIN2 = 0b00000, i_TSTIN = 0b11111111111111111111, #o_TSTOUT =, # Channel i_CLKRSVD = 0b0000, # Channel - Clocking Ports i_GTGREFCLK = 0, i_GTNORTHREFCLK0 = 0, i_GTNORTHREFCLK1 = 0, i_GTREFCLK0 = 0 if use_qpll else pll.refclk, i_GTREFCLK1 = 0, i_GTSOUTHREFCLK0 = 0, i_GTSOUTHREFCLK1 = 0, # Channel - DRP Ports i_DRPADDR = drp_mux.addr, i_DRPCLK = drp_mux.clk, i_DRPDI = drp_mux.di, o_DRPDO = drp_mux.do, i_DRPEN = drp_mux.en, o_DRPRDY = drp_mux.rdy, i_DRPWE = drp_mux.we, # Clocking Ports #o_GTREFCLKMONITOR =, i_QPLLCLK = 0 if use_cpll else pll.clk, i_QPLLREFCLK = 0 if use_cpll else pll.refclk, i_RXSYSCLKSEL = 0b11 if use_qpll else 0b00, i_TXSYSCLKSEL = 0b11 if use_qpll else 0b00, # Digital Monitor Ports #o_DMONITOROUT =, # FPGA TX Interface Datapath Configuration i_TX8B10BEN = 0, # Loopback Ports i_LOOPBACK = self.loopback, # PCI Express Ports #o_PHYSTATUS =, i_RXRATE = 0b000, #o_RXVALID =, # Power-Down Ports i_RXPD = Cat(rx_init.gtXxpd, rx_init.gtXxpd), i_TXPD = 0b00, # RX 8B/10B Decoder Ports i_SETERRSTATUS = 0, # RX Initialization and Reset Ports i_EYESCANRESET = 0, i_RXUSERRDY = rx_init.Xxuserrdy, # RX Margin Analysis Ports #o_EYESCANDATAERROR =, i_EYESCANMODE = 0, i_EYESCANTRIGGER = 0, # Receive Ports - CDR Ports i_RXCDRFREQRESET = 0, i_RXCDRHOLD = 0, #o_RXCDRLOCK =, i_RXCDROVRDEN = 0, i_RXCDRRESET = 0, i_RXCDRRESETRSV = 0, # Receive Ports - Clock Correction Ports #o_RXCLKCORCNT =, # Receive Ports - FPGA RX Interface Datapath Configuration i_RX8B10BEN = 0, # Receive Ports - FPGA RX Interface Ports i_RXUSRCLK = ClockSignal("rx"), i_RXUSRCLK2 = ClockSignal("rx"), # Receive Ports - FPGA RX interface Ports o_RXDATA = Cat(*[rxdata[10*i:10*i+8] for i in range(nwords)]), # Receive Ports - Pattern Checker Ports #o_RXPRBSERR =, i_RXPRBSSEL = 0b000, # Receive Ports - Pattern Checker ports i_RXPRBSCNTRESET = 0, # Receive Ports - RX Equalizer Ports i_RXDFEXYDEN = 1, i_RXDFEXYDHOLD = 0, i_RXDFEXYDOVRDEN = 0, # Receive Ports - RX 8B/10B Decoder Ports i_RXDISPERR = Cat(*[rxdata[10*i+9] for i in range(nwords)]), #o_RXNOTINTABLE =, # Receive Ports - RX AFE i_GTXRXP = rx_pads.p, i_GTXRXN = rx_pads.n, # Receive Ports - RX Buffer Bypass Ports i_RXBUFRESET = 0, #o_RXBUFSTATUS =, i_RXDDIEN = 0 if rx_buffer_enable else 1, i_RXDLYBYPASS = 1 if rx_buffer_enable else 0, i_RXDLYEN = 0, i_RXDLYOVRDEN = 0, i_RXDLYSRESET = rx_init.Xxdlysreset, o_RXDLYSRESETDONE = rx_init.Xxdlysresetdone, i_RXPHALIGN = 0, o_RXPHALIGNDONE = rx_init.Xxphaligndone, i_RXPHALIGNEN = 0, i_RXPHDLYPD = 0, i_RXPHDLYRESET = 0, #o_RXPHMONITOR =, i_RXPHOVRDEN = 0, #o_RXPHSLIPMONITOR =, #o_RXSTATUS =, # Receive Ports - RX Byte and Word Alignment Ports #o_RXBYTEISALIGNED =, #o_RXBYTEREALIGN =, #o_RXCOMMADET =, i_RXCOMMADETEN = 1, i_RXMCOMMAALIGNEN = (~clock_aligner & self.rx_align & (rx_prbs_config == 0b00)) if rx_buffer_enable else 0, i_RXPCOMMAALIGNEN = (~clock_aligner & self.rx_align & (rx_prbs_config == 0b00)) if rx_buffer_enable else 0, # Receive Ports - RX Channel Bonding Ports #o_RXCHANBONDSEQ =, i_RXCHBONDEN = 0, i_RXCHBONDLEVEL = 0b000, i_RXCHBONDMASTER = 0, #o_RXCHBONDO =, i_RXCHBONDSLAVE = 0, # Receive Ports - RX Channel Bonding Ports #o_RXCHANISALIGNED =, #o_RXCHANREALIGN =, # Receive Ports - RX Equailizer Ports i_RXLPMHFHOLD = 0, i_RXLPMHFOVRDEN = 0, i_RXLPMLFHOLD = 0, # Receive Ports - RX Equalizer Ports i_RXDFEAGCHOLD = 0, i_RXDFEAGCOVRDEN = 0, i_RXDFECM1EN = 0, i_RXDFELFHOLD = 0, i_RXDFELFOVRDEN = 1, i_RXDFELPMRESET = 0, i_RXDFETAP2HOLD = 0, i_RXDFETAP2OVRDEN = 0, i_RXDFETAP3HOLD = 0, i_RXDFETAP3OVRDEN = 0, i_RXDFETAP4HOLD = 0, i_RXDFETAP4OVRDEN = 0, i_RXDFETAP5HOLD = 0, i_RXDFETAP5OVRDEN = 0, i_RXDFEUTHOLD = 0, i_RXDFEUTOVRDEN = 0, i_RXDFEVPHOLD = 0, i_RXDFEVPOVRDEN = 0, i_RXDFEVSEN = 0, i_RXLPMLFKLOVRDEN = 0, #o_RXMONITOROUT = i_RXMONITORSEL = 0, i_RXOSHOLD = 0, i_RXOSOVRDEN = 0, # Receive Ports - RX Fabric ClocK Output Control Ports #o_RXRATEDONE =, # Receive Ports - RX Fabric Output Control Ports o_RXOUTCLK = self.rxoutclk, #o_RXOUTCLKFABRIC =, #o_RXOUTCLKPCS =, i_RXOUTCLKSEL = 0b010, # Receive Ports - RX Gearbox Ports #o_RXDATAVALID =, #o_RXHEADER =, #o_RXHEADERVALID =, #o_RXSTARTOFSEQ =, # Receive Ports - RX Gearbox Ports i_RXGEARBOXSLIP = 0, # Receive Ports - RX Initialization and Reset Ports i_GTRXRESET = rx_init.gtXxreset, i_RXOOBRESET = 0, i_RXPCSRESET = 0, i_RXPMARESET = 0, # Receive Ports - RX Margin Analysis ports i_RXLPMEN = 0, # Receive Ports - RX OOB Signaling ports #o_RXCOMSASDET =, #o_RXCOMWAKEDET =, # Receive Ports - RX OOB Signaling ports #o_RXCOMINITDET =, # Receive Ports - RX OOB signalling Ports #o_RXELECIDLE =, i_RXELECIDLEMODE = 0b11, # Receive Ports - RX Polarity Control Ports i_RXPOLARITY = rx_polarity, # Receive Ports - RX gearbox ports i_RXSLIDE = 0, # Receive Ports - RX8B/10B Decoder Ports #o_RXCHARISCOMMA =, o_RXCHARISK = Cat(*[rxdata[10*i+8] for i in range(nwords)]), # Receive Ports - Rx Channel Bonding Ports i_RXCHBONDI = 0b00000, # Receive Ports -RX Initialization and Reset Ports o_RXRESETDONE = rx_init.Xxresetdone, # Rx AFE Ports i_RXQPIEN = 0, #o_RXQPISENN =, #o_RXQPISENP =, # TX Buffer Bypass Ports i_TXPHDLYTSTCLK = 0, # TX Configurable Driver Ports i_TXPOSTCURSOR = 0b00000, i_TXPOSTCURSORINV = 0, i_TXPRECURSOR = 0b00000, i_TXPRECURSORINV = 0, i_TXQPIBIASEN = 0, i_TXQPISTRONGPDOWN = 0, i_TXQPIWEAKPUP = 0, # TX Initialization and Reset Ports i_CFGRESET = 0, i_GTTXRESET = tx_init.gtXxreset, #o_PCSRSVDOUT =, i_TXUSERRDY = tx_init.Xxuserrdy, # Transceiver Reset Mode Operation i_GTRESETSEL = 0, i_RESETOVRD = 0, # Transmit Ports - 8b10b Encoder Control Ports i_TXCHARDISPMODE = Cat(*[txdata[10*i+9] for i in range(nwords)]), i_TXCHARDISPVAL = Cat(*[txdata[10*i+8] for i in range(nwords)]), # Transmit Ports - FPGA TX Interface Ports i_TXUSRCLK = ClockSignal("tx"), i_TXUSRCLK2 = ClockSignal("tx"), # Transmit Ports - PCI Express Ports i_TXELECIDLE = 0, i_TXMARGIN = 0b000, i_TXRATE = 0b000, i_TXSWING = 0, # Transmit Ports - Pattern Generator Ports i_TXPRBSFORCEERR = 0, # Transmit Ports - TX Buffer Bypass Ports i_TXDLYBYPASS = 1 if tx_buffer_enable else 0, i_TXDLYEN = 0, i_TXDLYHOLD = 0, i_TXDLYOVRDEN = 0, i_TXDLYSRESET = tx_init.Xxdlysreset, o_TXDLYSRESETDONE = tx_init.Xxdlysresetdone, i_TXDLYUPDOWN = 0, i_TXPHALIGN = 0, o_TXPHALIGNDONE = tx_init.Xxphaligndone, i_TXPHALIGNEN = 0, i_TXPHDLYPD = 0, i_TXPHDLYRESET = 0, i_TXPHINIT = 0, #o_TXPHINITDONE =, i_TXPHOVRDEN = 0, # Transmit Ports - TX Buffer Ports #o_TXBUFSTATUS =, # Transmit Ports - TX Configurable Driver Ports i_TXBUFDIFFCTRL = 0b100, i_TXDEEMPH = 0, i_TXDIFFCTRL = 0b1000, i_TXDIFFPD = 0, i_TXINHIBIT = self.tx_inhibit, i_TXMAINCURSOR = 0b0000000, i_TXPISOPD = 0, # Transmit Ports - TX Data Path interface i_TXDATA = Cat(*[txdata[10*i:10*i+8] for i in range(nwords)]), # Transmit Ports - TX Driver and OOB signaling o_GTXTXN = tx_pads.n, o_GTXTXP = tx_pads.p, # Transmit Ports - TX Fabric Clock Output Control Ports o_TXOUTCLK = self.txoutclk, #o_TXOUTCLKFABRIC =, #o_TXOUTCLKPCS =, i_TXOUTCLKSEL = 0b010 if tx_buffer_enable else 0b011, #o_TXRATEDONE =, # Transmit Ports - TX Gearbox Ports i_TXCHARISK = 0b00000000, #o_TXGEARBOXREADY =, i_TXHEADER = 0b000, i_TXSEQUENCE = 0b0000000, i_TXSTARTSEQ = 0, # Transmit Ports - TX Initialization and Reset Ports i_TXPCSRESET = 0, i_TXPMARESET = 0, o_TXRESETDONE = tx_init.Xxresetdone, # Transmit Ports - TX OOB signaling Ports #o_TXCOMFINISH =, i_TXCOMINIT = 0, i_TXCOMSAS = 0, i_TXCOMWAKE = 0, i_TXPDELECIDLEMODE = 0, # Transmit Ports - TX Polarity Control Ports i_TXPOLARITY = tx_polarity, # Transmit Ports - TX Receiver Detection Ports i_TXDETECTRX = 0, # Transmit Ports - TX8b/10b Encoder Ports i_TX8B10BBYPASS = 0b00000000, # Transmit Ports - pattern Generator Ports i_TXPRBSSEL = 0b000, # Tx Configurable Driver Ports #o_TXQPISENN =, #o_TXQPISENP =, ) # TX clocking ------------------------------------------------------------------------------ tx_reset_deglitched = Signal() tx_reset_deglitched.attr.add("no_retiming") self.sync += tx_reset_deglitched.eq(~tx_init.done) self.clock_domains.cd_tx = ClockDomain() txoutclk_bufg = Signal() self.specials += Instance("BUFG", i_I=self.txoutclk, o_O=txoutclk_bufg) if not tx_buffer_enable: txoutclk_div = pll.config["clkin"]/self.tx_clk_freq else: txoutclk_div = 1 # Use txoutclk_bufg when divider is 1 if txoutclk_div == 1: self.comb += self.cd_tx.clk.eq(txoutclk_bufg) self.specials += AsyncResetSynchronizer(self.cd_tx, tx_reset_deglitched) # Use a BUFR when integer divider (with BUFR_DIVIDE) elif txoutclk_div == int(txoutclk_div): txoutclk_bufr = Signal() self.specials += [ Instance("BUFR", i_I=txoutclk_bufg, o_O=txoutclk_bufr, i_CE=1, p_BUFR_DIVIDE=str(int(txoutclk_div))), Instance("BUFG", i_I=txoutclk_bufr, o_O=self.cd_tx.clk), AsyncResetSynchronizer(self.cd_tx, tx_reset_deglitched) ] # Use a PLL when non-integer divider else: txoutclk_pll = S7PLL() self.comb += txoutclk_pll.reset.eq(tx_reset_deglitched) self.submodules += txoutclk_pll txoutclk_pll.register_clkin(txoutclk_bufg, pll.config["clkin"]) txoutclk_pll.create_clkout(self.cd_tx, self.tx_clk_freq) # RX clocking ------------------------------------------------------------------------------ rx_reset_deglitched = Signal() rx_reset_deglitched.attr.add("no_retiming") self.sync.tx += rx_reset_deglitched.eq(~rx_init.done) self.clock_domains.cd_rx = ClockDomain() self.specials += [ Instance("BUFG", i_I=self.rxoutclk, o_O=self.cd_rx.clk), AsyncResetSynchronizer(self.cd_rx, rx_reset_deglitched) ] # TX Datapath and PRBS --------------------------------------------------------------------- self.submodules.tx_prbs = ClockDomainsRenamer("tx")(PRBSTX(data_width, True)) self.comb += self.tx_prbs.config.eq(tx_prbs_config) self.comb += [ self.tx_prbs.i.eq(Cat(*[self.encoder.output[i] for i in range(nwords)])), If(tx_produce_square_wave, # square wave @ linerate/data_width for scope observation txdata.eq(Signal(data_width, reset=(1<<(data_width//2))-1)) ).Elif(tx_produce_pattern, txdata.eq(self.tx_pattern) ).Else( txdata.eq(self.tx_prbs.o) ) ] # RX Datapath and PRBS --------------------------------------------------------------------- self.submodules.rx_prbs = ClockDomainsRenamer("rx")(PRBSRX(data_width, True)) self.comb += [ self.rx_prbs.config.eq(rx_prbs_config), rx_prbs_errors.eq(self.rx_prbs.errors) ] for i in range(nwords): self.comb += self.decoders[i].input.eq(rxdata[10*i:10*(i+1)]) self.comb += self.rx_prbs.i.eq(rxdata) # Clock Aligner ---------------------------------------------------------------------------- if clock_aligner: clock_aligner = BruteforceClockAligner(clock_aligner_comma, self.tx_clk_freq) self.submodules.clock_aligner = clock_aligner ps_restart = PulseSynchronizer("tx", "sys") self.submodules += ps_restart self.comb += [ clock_aligner.rxdata.eq(rxdata), ps_restart.i.eq(clock_aligner.restart), rx_init.restart.eq((ps_restart.o & self.rx_align) | ~self.rx_enable), self.rx_ready.eq(clock_aligner.ready) ]
def __init__(self, platform, pads, data_width=64, bar0_size=1*MB, cd="sys"): # Streams ---------------------------------------------------------------------------------- self.sink = stream.Endpoint(phy_layout(data_width)) self.source = stream.Endpoint(phy_layout(data_width)) self.msi = stream.Endpoint(msi_layout()) # Registers -------------------------------------------------------------------------------- self._lnk_up = CSRStatus() self._msi_enable = CSRStatus() self._bus_master_enable = CSRStatus() self._max_request_size = CSRStatus(16) self._max_payload_size = CSRStatus(16) # Parameters/Locals ------------------------------------------------------------------------ self.platform = platform self.data_width = data_width self.id = Signal(16) self.bar0_size = bar0_size self.bar0_mask = get_bar_mask(bar0_size) self.max_request_size = Signal(16) self.max_payload_size = Signal(16) self.external_hard_ip = False # # # # Clocking --------------------------------------------------------------------------------- pcie_refclk = Signal() self.specials += Instance("IBUFDS_GTE2", i_CEB=0, i_I=pads.clk_p, i_IB=pads.clk_n, o_O=pcie_refclk ) self.clock_domains.cd_pcie = ClockDomain() # TX CDC (FPGA --> HOST) ------------------------------------------------------------------- if cd == "pcie": s_axis_tx = self.sink else: tx_buffer = stream.Buffer(phy_layout(data_width)) tx_buffer = ClockDomainsRenamer(cd)(tx_buffer) tx_cdc = stream.AsyncFIFO(phy_layout(data_width), 4) tx_cdc = ClockDomainsRenamer({"write": cd, "read": "pcie"})(tx_cdc) self.submodules += tx_buffer, tx_cdc self.comb += [ self.sink.connect(tx_buffer.sink), tx_buffer.source.connect(tx_cdc.sink) ] s_axis_tx = tx_cdc.source # RX CDC (HOST --> FPGA) ------------------------------------------------------------------- if cd == "pcie": m_axis_rx = self.source else: rx_cdc = stream.AsyncFIFO(phy_layout(data_width), 4) rx_cdc = ClockDomainsRenamer({"write": "pcie", "read": cd})(rx_cdc) rx_buffer = stream.Buffer(phy_layout(data_width)) rx_buffer = ClockDomainsRenamer(cd)(rx_buffer) self.submodules += rx_buffer, rx_cdc self.comb += [ rx_cdc.source.connect(rx_buffer.sink), rx_buffer.source.connect(self.source) ] m_axis_rx = rx_cdc.sink # MSI CDC (FPGA --> HOST) ------------------------------------------------------------------ if cd == "pcie": cfg_msi = self.msi else: msi_cdc = stream.AsyncFIFO(msi_layout(), 4) msi_cdc = ClockDomainsRenamer({"write": cd, "read": "pcie"})(msi_cdc) self.submodules += msi_cdc self.comb += self.msi.connect(msi_cdc.sink) cfg_msi = msi_cdc.source # Hard IP Configuration -------------------------------------------------------------------- def convert_size(command, size): cases = {} value = 128 for i in range(6): cases[i] = size.eq(value) value = value*2 return Case(command, cases) lnk_up = Signal() msienable = Signal() bus_number = Signal(8) device_number = Signal(5) function_number = Signal(3) command = Signal(16) dcommand = Signal(16) self.sync.pcie += [ convert_size(dcommand[12:15], self.max_request_size), convert_size(dcommand[5:8], self.max_payload_size), self.id.eq(Cat(function_number, device_number, bus_number)) ] self.specials += [ MultiReg(lnk_up, self._lnk_up.status), MultiReg(command[2], self._bus_master_enable.status), MultiReg(msienable, self._msi_enable.status), MultiReg(self.max_request_size, self._max_request_size.status), MultiReg(self.max_payload_size, self._max_payload_size.status) ] # Hard IP ---------------------------------------------------------------------------------- m_axis_rx_tlast = Signal() m_axis_rx_tuser = Signal(32) self.pcie_phy_params = dict( p_C_DATA_WIDTH=data_width, p_C_PCIE_GT_DEVICE={ "xc7k": "GTX", "xc7a": "GTP"}[platform.device[:4]], p_C_BAR0=get_bar_mask(bar0_size), i_sys_clk=pcie_refclk, i_sys_rst_n=1 if not hasattr(pads, "rst_n") else pads.rst_n, o_pci_exp_txp=pads.tx_p, o_pci_exp_txn=pads.tx_n, i_pci_exp_rxp=pads.rx_p, i_pci_exp_rxn=pads.rx_n, o_user_clk=ClockSignal("pcie"), o_user_reset=ResetSignal("pcie"), o_user_lnk_up=lnk_up, #o_tx_buf_av=, #o_tx_terr_drop=, #o_tx_cfg_req=, i_tx_cfg_gnt=1, i_s_axis_tx_tvalid=s_axis_tx.valid, i_s_axis_tx_tlast=s_axis_tx.last, o_s_axis_tx_tready=s_axis_tx.ready, i_s_axis_tx_tdata=s_axis_tx.dat, i_s_axis_tx_tkeep=s_axis_tx.be, i_s_axis_tx_tuser=0, i_rx_np_ok=1, i_rx_np_req=1, o_m_axis_rx_tvalid=m_axis_rx.valid, o_m_axis_rx_tlast=m_axis_rx_tlast, i_m_axis_rx_tready=m_axis_rx.ready, o_m_axis_rx_tdata=m_axis_rx.dat, o_m_axis_rx_tkeep=m_axis_rx.be, o_m_axis_rx_tuser=m_axis_rx_tuser, #o_cfg_to_turnoff=, o_cfg_bus_number=bus_number, o_cfg_device_number=device_number, o_cfg_function_number=function_number, o_cfg_command=command, o_cfg_dcommand=dcommand, o_cfg_interrupt_msienable=msienable, i_cfg_interrupt=cfg_msi.valid, o_cfg_interrupt_rdy=cfg_msi.ready, i_cfg_interrupt_di=cfg_msi.dat, i_QPLL_PLL1PD=1, ) if data_width == 128: self.comb += m_axis_rx.last.eq(m_axis_rx_tuser[21]) else: self.comb += m_axis_rx.last.eq(m_axis_rx_tlast)
def __init__(self, capture_depth, **kwargs): self.platform = Platform(**kwargs) self.platform.add_extension([ ("tp0", 0, Pins("X3:5"), IOStandard("LVCMOS33")), ]) self.clock_domains.cd_ref = ClockDomain() self.clock_domains.cd_rx = ClockDomain() self.clock_domains.cd_tx = ClockDomain() self.submodules.serdes = serdes = \ LatticeECP5PCIeSERDES(self.platform.request("pcie_x1")) self.submodules.aligner = aligner = \ ClockDomainsRenamer("rx")(PCIeSERDESAligner(serdes.lane)) self.comb += [ self.cd_ref.clk.eq(serdes.ref_clk), serdes.rx_clk_i.eq(serdes.rx_clk_o), self.cd_rx.clk.eq(serdes.rx_clk_i), serdes.tx_clk_i.eq(serdes.tx_clk_o), self.cd_tx.clk.eq(serdes.tx_clk_i), ] self.submodules.tx_phy = ClockDomainsRenamer("tx")(PCIePHYTX(aligner)) self.comb += [ self.aligner.rx_align.eq(1), self.tx_phy.ts.n_fts.eq(0xff), self.tx_phy.ts.rate.gen1.eq(1), ] with open("top.sdc", "w") as f: f.write("define_clock -name {n:serdes_ref_clk} -freq 100.000\n") f.write("define_clock -name {n:serdes_tx_clk_o} -freq 150.000\n") f.write("define_clock -name {n:serdes_rx_clk_o} -freq 150.000\n") self.platform.add_source("top.sdc") self.platform.add_platform_command( """FREQUENCY NET "serdes_ref_clk" 100 MHz;""") self.platform.add_platform_command( """FREQUENCY NET "serdes_rx_clk_o" 125 MHz;""") self.platform.add_platform_command( """FREQUENCY NET "serdes_tx_clk_o" 125 MHz;""") refclkcounter = Signal(32) self.sync.ref += refclkcounter.eq(refclkcounter + 1) rxclkcounter = Signal(32) self.sync.rx += rxclkcounter.eq(rxclkcounter + 1) txclkcounter = Signal(32) self.sync.tx += txclkcounter.eq(txclkcounter + 1) led_att1 = self.platform.request("user_led") led_att2 = self.platform.request("user_led") led_sta1 = self.platform.request("user_led") led_sta2 = self.platform.request("user_led") led_err1 = self.platform.request("user_led") led_err2 = self.platform.request("user_led") led_err3 = self.platform.request("user_led") led_err4 = self.platform.request("user_led") self.comb += [ led_att1.eq(~(refclkcounter[25])), led_att2.eq(~(0)), led_sta1.eq(~(rxclkcounter[25])), led_sta2.eq(~(txclkcounter[25])), led_err1.eq(~(~serdes.lane.rx_present)), led_err2.eq(~(~serdes.lane.rx_locked)), led_err3.eq(~(~serdes.lane.rx_aligned)), led_err4.eq(~(0)), ] trigger_rx = Signal() trigger_ref = Signal() self.specials += MultiReg(trigger_ref, trigger_rx, odomain="rx") capture = Signal() self.submodules.symbols = symbols = ClockDomainsRenamer({ "write": "rx", "read": "ref" })(AsyncFIFO(width=18, depth=capture_depth)) self.comb += [ symbols.din.eq(Cat(aligner.rx_symbol)), symbols.we.eq(capture) ] self.sync.rx += [ If(trigger_rx, capture.eq(1)).Elif(~symbols.writable, capture.eq(0)) ] uart_pads = Pads(self.platform.request("serial")) self.submodules += uart_pads self.submodules.uart = uart = ClockDomainsRenamer("ref")(UART( uart_pads, bit_cyc=uart_bit_cyc(100e6, 115200)[0])) self.comb += [uart.rx_ack.eq(uart.rx_rdy), trigger_ref.eq(uart.rx_rdy)] self.submodules.fsm = ClockDomainsRenamer("ref")( FSM(reset_state="WAIT")) self.fsm.act("WAIT", If(uart.rx_rdy, NextState("SYNC-1"))) self.fsm.act( "SYNC-1", If(uart.tx_rdy, uart.tx_ack.eq(1), uart.tx_data.eq(0xff), NextState("SYNC-2"))) self.fsm.act( "SYNC-2", If(uart.tx_rdy, uart.tx_ack.eq(1), uart.tx_data.eq(0xff), NextState("BYTE-0"))) self.fsm.act( "BYTE-0", If(symbols.readable & uart.tx_rdy, uart.tx_ack.eq(1), uart.tx_data.eq(symbols.dout[16:]), NextState("BYTE-1")).Elif(~symbols.readable, NextState("WAIT"))) self.fsm.act( "BYTE-1", If(symbols.readable & uart.tx_rdy, uart.tx_ack.eq(1), uart.tx_data.eq(symbols.dout[8:]), NextState("BYTE-2"))) self.fsm.act( "BYTE-2", If(symbols.readable & uart.tx_rdy, uart.tx_ack.eq(1), uart.tx_data.eq(symbols.dout[0:]), symbols.re.eq(1), NextState("BYTE-0"))) tp0 = self.platform.request("tp0")
def __init__(self, clkspertick, clkfreq, bits=64): self.clkspertick = int(clkfreq / clkspertick) self.intro = ModuleDoc("""TickTimer: A practical systick timer. TIMER0 in the system gives a high-resolution, sysclk-speed timer which overflows very quickly and requires OS overhead to convert it into a practically usable time source which counts off in systicks, instead of sysclks. The hardware parameter to the block is the divisor of sysclk, and sysclk. So if the divisor is 1000, then the increment for a tick is 1ms. If the divisor is 2000, the increment for a tick is 0.5ms. Note to self: substantial area savings could be hand by being smarter about the synchronization between the always-on and the TickTimer domains. Right now about 1.8% of the chip is eaten up by ~1100 synchronization registers to cross the 64-bit values between the clock domains. Since the values move rarely, a slightly smarter method would be to create a lock-out around a read pulse and then create some false_path rules around the datapaths to keep the place/route from getting distracted by the cross-domain clocks. """) resolution_in_ms = 1000 * (self.clkspertick / clkfreq) self.note = ModuleDoc( title="Configuration", body= "This timer was configured with {} bits, which rolls over in {:.2f} years, with each bit giving {}ms resolution" .format(bits, (2**bits / (60 * 60 * 24 * 365)) * (self.clkspertick / clkfreq), resolution_in_ms)) prescaler = Signal(max=self.clkspertick, reset=self.clkspertick) timer = Signal(bits) # cross-process domain signals. Broken out to a different CSR so it can be on a different virtual memory page. self.pause = Signal() pause = Signal() self.specials += MultiReg(self.pause, pause, "always_on") self.load = Signal() self.submodules.load_xfer = BlindTransfer("sys", "always_on") self.comb += self.load_xfer.i.eq(self.load) self.paused = Signal() paused = Signal() self.specials += MultiReg(paused, self.paused) self.timer = Signal(bits) self.submodules.timer_sync = BusSynchronizer(bits, "always_on", "sys") self.comb += [ self.timer_sync.i.eq(timer), self.timer.eq(self.timer_sync.o) ] self.resume_time = Signal(bits) self.submodules.resume_sync = BusSynchronizer(bits, "sys", "always_on") self.comb += [self.resume_sync.i.eq(self.resume_time)] self.control = CSRStorage(fields=[ CSRField( "reset", description= "Write a `1` to this bit to reset the count to 0. This bit has priority over all other requests.", pulse=True), ]) self.time = CSRStatus(bits, name="time", description="""Elapsed time in systicks""") self.comb += self.time.status.eq(self.timer_sync.o) self.submodules.reset_xfer = BlindTransfer("sys", "always_on") self.comb += [ self.reset_xfer.i.eq(self.control.fields.reset), ] self.sync.always_on += [ If( self.reset_xfer.o, timer.eq(0), prescaler.eq(self.clkspertick), ).Elif( self.load_xfer.o, prescaler.eq(self.clkspertick), timer.eq(self.resume_sync.o), ).Else( If( prescaler == 0, prescaler.eq(self.clkspertick), If(pause == 0, timer.eq(timer + 1), paused.eq(0)).Else(timer.eq(timer), paused.eq(1))).Else( prescaler.eq(prescaler - 1), )) ] self.msleep = ModuleDoc("""msleep extension The msleep extension is a Xous-specific add-on to aid the implementation of the msleep server. msleep fires an interrupt when the requested time is less than or equal to the current elapsed time in systicks. The interrupt remains active until a new target is set, or masked. There is a slight slip in time (~200ns) from when the msleep timer is set before it can take effect. This is because it takes many CPU clock cycles to transfer this data into the always-on clock domain, which runs at a much slower rate than the CPU clock. """) self.msleep_target = CSRStorage( size=bits, description="Target time in {}ms ticks".format(resolution_in_ms)) self.submodules.ev = EventManager() self.ev.alarm = EventSourceLevel() # sys-domain alarm is computed using sys-domain time view, so that the trigger condition # corresponds tightly to the setting of the target time alarm_trigger = Signal() self.comb += self.ev.alarm.trigger.eq(alarm_trigger) # always_on domain gets a delayed copy of msleep_target # thus its output may not match that of the sys-domain alarm # in particular, it takes time for msleep_target update to propagate through # the bus synchronizers; however, the "trigger" enable for the system is handled # in the sys-domain, and can be set *before* the bus synchronizers have passed the # data through. This causes the alarm to glitch prematurely. # if we seem to be errantly aborting WFI's that are entered shortly after # setting an msleep target, this race condition is likely the culprit. # the circuit below locks out alarms for the duration of time that it takes for # msleep_target to propagate to its target, and back again self.submodules.ping = BlindTransfer("sys", "always_on") self.comb += self.ping.i.eq(self.msleep_target.re) self.submodules.pong = BlindTransfer("always_on", "sys") self.comb += self.pong.i.eq(self.ping.o) lockout_alarm = Signal() self.comb += [ If(lockout_alarm, alarm_trigger.eq(0)).Else( alarm_trigger.eq( self.msleep_target.storage <= self.timer_sync.o)) ] self.sync += [ If(self.msleep_target.re, lockout_alarm.eq(1)).Elif( self.pong.o, lockout_alarm.eq(0)).Else(lockout_alarm.eq(lockout_alarm)) ] # re-compute the alarm signal in the "always on" domain -- so that this can trigger even when the CPU clock is stopped alarm = Signal() self.submodules.target_xfer = BusSynchronizer(bits, "sys", "always_on") self.comb += self.target_xfer.i.eq(self.msleep_target.storage) self.sync.always_on += alarm.eq(self.target_xfer.o <= timer) self.alarm_always_on = Signal() self.comb += self.alarm_always_on.eq(alarm)
def __init__(self, pads, fifo_depth=256, controller=False, master=False, concatenate_channels=True, sample_width=16, frame_format=I2S_FORMAT.I2S_LEFT_JUSTIFIED, lrck_ref_freq=100e6, lrck_freq=44100, bits_per_channel=28): if master == True: print( "Master/slave terminology deprecated, please use controller/peripheral. Please see http://oshwa.org/a-resolution-to-redefine-spi-signal-names." ) controller = True self.intro = ModuleDoc("""Intro I2S controller/peripheral creates a controller/peripheral audio interface instance depending on a configured controller variable. Tx and Rx interfaces are inferred based upon the presence or absence of the respective pins in the "pads" argument. When device is configured as controller you can manipulate LRCK and SCLK signals using below variables. - lrck_ref_freq - is a reference signal that is required to achive desired LRCK and SCLK frequencies. Have be the same as your sys_clk. - lrck_freq - this variable defines requested LRCK frequency. Mind you, that based on sys_clk frequency, configured value will be more or less acurate. - bits_per_channel - defines SCLK frequency. Mind you, that based on sys_clk frequency, the requested amount of bits per channel may vary from configured. When device is configured as peripheral I2S interface, sampling rate and framing is set by the programming of the audio CODEC chip. A peripheral configuration defers the generation of audio clocks to the CODEC, which has PLLs specialized to generate the correct frequencies for audio sampling rates. I2S core supports two formats: standard and left-justified. - Standard format requires a device to receive and send data with one bit offset for both channels. Left channel begins with low signal on LRCK. - Left justified format requires from device to receive and send data without any bit offset for both channels. Left channel begins with high signal on LRCK. Sample width can be any of 1 to 32 bits. When sample_width is less than or equal to 16-bit and concatenate_channels is enabled, sending and reciving channels is performed atomically. eg. both samples are transfered from/to fifo in single operation. System Interface ---------------- `fifo_depth` is the depth at which either a read interrupt is fired (guaranteeing at least `fifo_depth` stereo samples in the receive FIFO) or a write interrupt is fired (guaranteeing at least `fifo_depth` free space in the transmit FIFO). The maximum depth is 512. To receive audio data: - reset the Rx FIFO, to guarantee all pointers at zero - hook the Rx full interrupt with an interrupt handler (optional) - if the CODEC is not yet transmitting data, initiate data transmission - enable Rx FIFO to run - poll or wait for interrupt; upon interrupt, read `fifo_depth` words. Repeat. - to close the stream, simply clear the Rx FIFO enable bit. The next initiation should call a reset of the FIFO to ensure leftover previous data is cleared from the FIFO. To transmit audio data: - reset the Tx FIFO, to guarantee all pointers at zero - hook the Tx available interrupt with an interrupt handler (optional) - write 512 words of data into the Tx FIFO, filling it to the max - if the CODEC is not yet requesting data and unmuted, unmute and initiate reception - enable the Tx FIFO to run - poll or wait for interrupt; upon interrupt, write `fifo_depth` words. Repeat. - to close stream, mute the DAC and stop the request clock. Ideally, this can be completed before the FIFO is emptied, so there is no jarring pop or truncation of data - stop FIFO running. Next initiation should reset the FIFO to ensure leftover previous data in FIFO is cleared. CODEC Interface --------------- The interface assumes we have a sysclk domain running around 100MHz, and that our typical max audio rate is 44.1kHz * 24bits * 2channels = 2.1168MHz audio clock. Thus, the architecture treats the audio clock and data as asynchronous inputs that are MultiReg-syncd into the clock domain. Probably the slowest sysclk rate this might work with is around 20-25MHz (10x over sampling), but at 100MHz things will be quite comfortable. The upside of the fully asynchronous implementation is that we can leave the I/O unconstrained, giving the place/route more latitude to do its job. Here's the timing format targeted by this I2S interface: .. wavedrom:: :caption: Timing format of the I2S interface { "signal" : [ { "name": "clk", "wave": "n....|.......|......" }, { "name": "sync", "wave": "1.0..|....1..|....0." }, { "name": "tx/rx", "wave": ".====|==x.===|==x.=x", "data": ["L15", "L14", "...", "L1", "L0", "R15", "R14", "...", "R1", "R0", "L15"] }, ]} - Data is updated on the falling edge - Data is sampled on the rising edge - Words are MSB-to-LSB, - Sync is an input or output based on configure mode, - Sync can be longer than the wordlen, extra bits are just ignored - Tx is data to the codec (SDI pin on LM49352) - Rx is data from the codec (SDO pin on LM49352) """) # One cache line is 8 32-bit words, need to always have enough space for one line or else # nothing works if fifo_depth > 504: fifo_depth = 504 print( "I2S warning: fifo depth greater than 504 selected; truncating to 504" ) if fifo_depth < 8: fifo_depth = 8 print( "I2S warning: fifo depth less than 8 selected; truncating to 8" ) if sample_width > 32: sample_width = 32 print( "I2S warning: sample width greater than 32 bits. truncating to 32" ) # Connect pins, synchronizers, and edge detectors if hasattr(pads, 'tx'): tx_pin = Signal() self.comb += pads.tx.eq(tx_pin) if hasattr(pads, 'rx'): rx_pin = Signal() self.specials += MultiReg(pads.rx, rx_pin) fifo_data_width = sample_width if concatenate_channels: if sample_width <= 16: fifo_data_width = sample_width * 2 else: concatenate_channels = False print( "I2S warning: sample width greater than 16 bits. your channels can't be glued" ) sync_pin = Signal() self.specials += MultiReg(pads.sync, sync_pin) clk_pin = Signal() self.specials += MultiReg(pads.clk, clk_pin) clk_d = Signal() self.sync += clk_d.eq(clk_pin) rising_edge = Signal() falling_edge = Signal() self.comb += [ rising_edge.eq(clk_pin & ~clk_d), falling_edge.eq(~clk_pin & clk_d) ] # Wishbone bus self.bus = bus = wishbone.Interface() rd_ack = Signal() wr_ack = Signal() self.comb += [ If( bus.we, bus.ack.eq(wr_ack), ).Else(bus.ack.eq(rd_ack), ) ] if controller == True: if bits_per_channel < sample_width and frame_format == I2S_FORMAT.I2S_STANDARD: bits_per_channel = sample_width + 1 print( "I2S warning: bits per channel can't be smaller than sample_width. Setting bits per channel to {}" .format(sample_width + 1)) # implementing LRCK signal lrck_period = int(lrck_ref_freq / (lrck_freq * 2)) lrck_counter = Signal(16) self.sync += [ If( (lrck_counter == lrck_period), lrck_counter.eq(0), pads.sync.eq(~pads.sync), ).Else(lrck_counter.eq(lrck_counter + 1)) ] # implementing SCLK signal sclk_period = int(lrck_period / (bits_per_channel * 2)) sclk_counter = Signal(16) self.sync += [ If( (sclk_counter == sclk_period), sclk_counter.eq(0), pads.clk.eq(~pads.clk), ).Else(sclk_counter.eq(sclk_counter + 1)) ] # Interrupts self.submodules.ev = EventManager() if hasattr(pads, 'rx'): self.ev.rx_ready = EventSourcePulse( description="Indicates FIFO is ready to read" ) # Rising edge triggered self.ev.rx_error = EventSourcePulse( description= "Indicates an Rx error has happened (over/underflow)") if hasattr(pads, 'tx'): self.ev.tx_ready = EventSourcePulse( description= "Indicates enough space available for next Tx quanta of {} words" .format(fifo_depth)) self.ev.tx_error = EventSourcePulse( description="Indicates a Tx error has happened (over/underflow" ) self.ev.finalize() # build the RX subsystem if hasattr(pads, 'rx'): rx_rd_d = Signal(fifo_data_width) rx_almostfull = Signal() rx_almostempty = Signal() rx_full = Signal() rx_empty = Signal() rx_rdcount = Signal(9) rx_rderr = Signal() rx_wrerr = Signal() rx_wrcount = Signal(9) rx_rden = Signal() rx_wr_d = Signal(fifo_data_width) rx_wren = Signal() self.rx_ctl = CSRStorage( description="Rx data path control", fields=[ CSRField("enable", size=1, description="Enable the receiving data"), CSRField( "reset", size=1, description= "Writing `1` resets the FIFO. Reset happens regardless of enable state.", pulse=1) ]) self.rx_stat = CSRStatus( description="Rx data path status", fields=[ CSRField("overflow", size=1, description="Rx overflow"), CSRField("underflow", size=1, description="Rx underflow"), CSRField( "dataready", size=1, description="{} words of data loaded and ready to read" .format(fifo_depth)), CSRField("empty", size=1, description="No data available in FIFO to read" ), # next flags probably never used CSRField("wrcount", size=9, description="Write count"), CSRField("rdcount", size=9, description="Read count"), CSRField("fifo_depth", size=9, description="FIFO depth as synthesized"), CSRField( "concatenate_channels", size=1, reset=concatenate_channels, description="Receive and send both channels atomically" ) ]) self.rx_conf = CSRStatus( description="Rx configuration", fields=[ CSRField( "format", size=2, reset=frame_format.value, description= "I2S sample format. {} is left-justified, {} is I2S standard" .format(I2S_FORMAT.I2S_LEFT_JUSTIFIED, I2S_FORMAT.I2S_STANDARD)), CSRField("sample_width", size=6, reset=sample_width, description="Single sample width"), CSRField("lrck_freq", size=24, reset=lrck_freq, description="Audio sampling rate frequency"), ]) self.comb += self.rx_stat.fields.fifo_depth.eq(fifo_depth) rx_rst_cnt = Signal(3) rx_reset = Signal() self.sync += [ If( self.rx_ctl.fields.reset, rx_rst_cnt.eq(5), # 5 cycles reset required by design rx_reset.eq(1)).Else( If(rx_rst_cnt == 0, rx_reset.eq(0)).Else(rx_rst_cnt.eq(rx_rst_cnt - 1), rx_reset.eq(1))) ] # At a width of 32 bits, an 18kiB fifo is 512 entries deep self.specials += Instance( "FIFO_SYNC_MACRO", p_DEVICE="7SERIES", p_FIFO_SIZE="18Kb", p_DATA_WIDTH=fifo_data_width, p_ALMOST_EMPTY_OFFSET=8, p_ALMOST_FULL_OFFSET=(512 - fifo_depth), p_DO_REG=0, i_CLK=ClockSignal(), i_RST=rx_reset, o_ALMOSTFULL=rx_almostfull, o_ALMOSTEMPTY=rx_almostempty, o_FULL=rx_full, o_EMPTY=rx_empty, i_WREN=rx_wren & ~rx_reset, i_DI=rx_wr_d, i_RDEN=rx_rden & ~rx_reset, o_DO=rx_rd_d, o_RDCOUNT=rx_rdcount, o_RDERR=rx_rderr, o_WRCOUNT=rx_wrcount, o_WRERR=rx_wrerr, ) self.comb += [ # Wire up the status signals and interrupts self.rx_stat.fields.underflow.eq(rx_rderr), self.rx_stat.fields.dataready.eq(rx_almostfull), self.rx_stat.fields.wrcount.eq(rx_wrcount), self.rx_stat.fields.rdcount.eq(rx_rdcount), self.ev.rx_ready.trigger.eq(rx_almostfull), self.ev.rx_error.trigger.eq(rx_wrerr | rx_rderr), ] bus_read = Signal() bus_read_d = Signal() rd_ack_pipe = Signal() self.comb += bus_read.eq(bus.cyc & bus.stb & ~bus.we & (bus.cti == 0)) self.sync += [ # This is the bus responder -- only works for uncached memory regions bus_read_d.eq(bus_read), If( bus_read & ~bus_read_d, # One response, one cycle rd_ack_pipe.eq(1), If( ~rx_empty, bus.dat_r.eq(rx_rd_d), rx_rden.eq(1), ).Else( # Don't stall the bus indefinitely if we try to read from an empty fifo...just # return garbage bus.dat_r.eq(0xdeadbeef), rx_rden.eq(0), )).Else( rx_rden.eq(0), rd_ack_pipe.eq(0), ), rd_ack.eq(rd_ack_pipe), ] rx_cnt_width = math.ceil(math.log(fifo_data_width, 2)) rx_cnt = Signal(rx_cnt_width) rx_delay_cnt = Signal() rx_delay_val = 1 if frame_format == I2S_FORMAT.I2S_STANDARD else 0 self.submodules.rxi2s = rxi2s = FSM(reset_state="IDLE") rxi2s.act( "IDLE", NextValue(rx_wr_d, 0), If( self.rx_ctl.fields.enable, # Wait_sync guarantees we start at the beginning of a left frame, and not in # the middle If( rising_edge & (~sync_pin if frame_format == I2S_FORMAT.I2S_STANDARD else sync_pin), NextState("WAIT_SYNC"), NextValue(rx_delay_cnt, rx_delay_val)))), rxi2s.act( "WAIT_SYNC", If( rising_edge & (~sync_pin if frame_format == I2S_FORMAT.I2S_STANDARD else sync_pin), If(rx_delay_cnt > 0, NextValue(rx_delay_cnt, rx_delay_cnt - 1), NextState("WAIT_SYNC")).Else( NextState("LEFT"), NextValue(rx_delay_cnt, rx_delay_val), NextValue(rx_cnt, sample_width))), ) rxi2s.act( "LEFT", If(~self.rx_ctl.fields.enable, NextState("IDLE")).Else( NextValue(rx_wr_d, Cat(rx_pin, rx_wr_d[:-1])), NextValue(rx_cnt, rx_cnt - 1), NextState("LEFT_WAIT"))) if concatenate_channels: rxi2s.act( "LEFT_WAIT", If(~self.rx_ctl.fields.enable, NextState("IDLE")).Else( If( rising_edge, If((rx_cnt == 0), If((sync_pin if frame_format == I2S_FORMAT.I2S_STANDARD else ~sync_pin), If( rx_delay_cnt == 0, NextValue(rx_cnt, sample_width), NextValue(rx_delay_cnt, rx_delay_val), NextState("RIGHT"), ).Else( NextValue(rx_delay_cnt, rx_delay_cnt - 1), NextState("LEFT_WAIT"))).Else( NextState("LEFT_WAIT"))).Elif( rx_cnt > 0, NextState("LEFT"))))) else: rxi2s.act( "LEFT_WAIT", If(~self.rx_ctl.fields.enable, NextState("IDLE")).Else( If( rising_edge, If( (rx_cnt == 0), If( (sync_pin if frame_format == I2S_FORMAT.I2S_STANDARD else ~sync_pin), If( rx_delay_cnt == 0, NextValue(rx_cnt, sample_width), NextValue(rx_delay_cnt, rx_delay_val), NextState("RIGHT"), rx_wren.eq( 1 ) # Pulse rx_wren to write the current data word ).Else( NextValue(rx_delay_cnt, rx_delay_cnt - 1), NextState("LEFT_WAIT"))).Else( NextState("LEFT_WAIT"))).Elif( rx_cnt > 0, NextState("LEFT"))))) rxi2s.act( "RIGHT", If(~self.rx_ctl.fields.enable, NextState("IDLE")).Else( NextValue(rx_wr_d, Cat(rx_pin, rx_wr_d[:-1])), NextValue(rx_cnt, rx_cnt - 1), NextState("RIGHT_WAIT"))) rxi2s.act( "RIGHT_WAIT", If(~self.rx_ctl.fields.enable, NextState("IDLE")).Else( If( rising_edge, If( (rx_cnt == 0) & (~sync_pin if frame_format == I2S_FORMAT.I2S_STANDARD else sync_pin), If( rx_delay_cnt == 0, NextValue(rx_cnt, sample_width), NextValue(rx_delay_cnt, rx_delay_val), NextState("LEFT"), rx_wren.eq( 1 ) # Pulse rx_wren to write the current data word ).Else(NextValue(rx_delay_cnt, rx_delay_cnt - 1), NextState("RIGHT_WAIT"))).Elif( rx_cnt > 0, NextState("RIGHT"))))) # Build the TX subsystem if hasattr(pads, 'tx'): tx_rd_d = Signal(fifo_data_width) tx_almostfull = Signal() tx_almostempty = Signal() tx_full = Signal() tx_empty = Signal() tx_rdcount = Signal(9) tx_rderr = Signal() tx_wrerr = Signal() tx_wrcount = Signal(9) tx_rden = Signal() tx_wr_d = Signal(fifo_data_width) tx_wren = Signal() self.tx_ctl = CSRStorage( description="Tx data path control", fields=[ CSRField("enable", size=1, description="Enable the transmission data"), CSRField( "reset", size=1, description= "Writing `1` resets the FIFO. Reset happens regardless of enable state.", pulse=1) ]) self.tx_stat = CSRStatus( description="Tx data path status", fields=[ CSRField("overflow", size=1, description="Tx overflow"), CSRField("underflow", size=1, description="Tx underflow"), CSRField( "free", size=1, description="At least {} words of space free".format( fifo_depth)), CSRField("almostfull", size=1, description="Less than 8 words space available" ), # the next few flags should be rarely used CSRField("full", size=1, description="FIFO is full or overfull"), CSRField("empty", size=1, description="FIFO is empty"), CSRField("wrcount", size=9, description="Tx write count"), CSRField("rdcount", size=9, description="Tx read count"), CSRField( "concatenate_channels", size=1, reset=concatenate_channels, description="Receive and send both channels atomically" ) ]) self.tx_conf = CSRStatus( description="TX configuration", fields=[ CSRField( "format", size=2, reset=frame_format.value, description= "I2S sample format. {} is left-justified, {} is I2S standard" .format(I2S_FORMAT.I2S_LEFT_JUSTIFIED, I2S_FORMAT.I2S_STANDARD)), CSRField("sample_width", size=6, reset=sample_width, description="Single sample width"), CSRField("lrck_freq", size=24, reset=lrck_freq, description="Audio sampling rate frequency"), ]) tx_rst_cnt = Signal(3) tx_reset = Signal() self.sync += [ If( self.tx_ctl.fields.reset, tx_rst_cnt.eq(5), # 5 cycles reset required by design tx_reset.eq(1)).Else( If(tx_rst_cnt == 0, tx_reset.eq(0)).Else(tx_rst_cnt.eq(tx_rst_cnt - 1), tx_reset.eq(1))) ] # At a width of 32 bits, an 18kiB fifo is 512 entries deep self.specials += Instance( "FIFO_SYNC_MACRO", p_DEVICE="7SERIES", p_FIFO_SIZE="18Kb", p_DATA_WIDTH=fifo_data_width, p_ALMOST_EMPTY_OFFSET=fifo_depth, p_ALMOST_FULL_OFFSET=8, p_DO_REG=0, i_CLK=ClockSignal(), i_RST=tx_reset, o_ALMOSTFULL=tx_almostfull, o_ALMOSTEMPTY=tx_almostempty, o_FULL=tx_full, o_EMPTY=tx_empty, i_WREN=tx_wren & ~tx_reset, i_DI=tx_wr_d, i_RDEN=tx_rden & ~tx_reset, o_DO=tx_rd_d, o_RDCOUNT=tx_rdcount, o_RDERR=tx_rderr, o_WRCOUNT=tx_wrcount, o_WRERR=tx_wrerr, ) self.comb += [ # Wire up the status signals and interrupts self.tx_stat.fields.underflow.eq(tx_rderr), self.tx_stat.fields.free.eq(tx_almostempty), self.tx_stat.fields.almostfull.eq(tx_almostfull), self.tx_stat.fields.full.eq(tx_full), self.tx_stat.fields.empty.eq(tx_empty), self.tx_stat.fields.rdcount.eq(tx_rdcount), self.tx_stat.fields.wrcount.eq(tx_wrcount), self.ev.tx_ready.trigger.eq(tx_almostempty), self.ev.tx_error.trigger.eq(tx_wrerr | tx_rderr), ] self.sync += [ # This is the bus responder -- need to check how this interacts with uncached memory # region If( bus.cyc & bus.stb & bus.we & ~bus.ack, If( ~tx_full, tx_wr_d.eq(bus.dat_w), tx_wren.eq(1), wr_ack.eq(1), ).Else( tx_wren.eq(0), wr_ack.eq(0), )).Else( tx_wren.eq(0), wr_ack.eq(0), ) ] tx_buf_width = fifo_data_width + 1 if frame_format == I2S_FORMAT.I2S_STANDARD else fifo_data_width sample_width = sample_width + 1 if frame_format == I2S_FORMAT.I2S_STANDARD else sample_width offset = [0] if frame_format == I2S_FORMAT.I2S_STANDARD else [] tx_cnt_width = math.ceil(math.log(fifo_data_width, 2)) tx_cnt = Signal(tx_cnt_width) tx_buf = Signal(tx_buf_width) sample_msb = fifo_data_width - 1 self.submodules.txi2s = txi2s = FSM(reset_state="IDLE") txi2s.act( "IDLE", If( self.tx_ctl.fields.enable, If( falling_edge & (~sync_pin if frame_format == I2S_FORMAT.I2S_STANDARD else sync_pin), NextState("WAIT_SYNC"), ))), txi2s.act( "WAIT_SYNC", If( falling_edge & (~sync_pin if frame_format == I2S_FORMAT.I2S_STANDARD else sync_pin), NextState("LEFT"), NextValue(tx_cnt, sample_width), NextValue(tx_buf, Cat(tx_rd_d, offset)), tx_rden.eq(1), )) txi2s.act( "LEFT", If(~self.tx_ctl.fields.enable, NextState("IDLE")).Else( NextValue(tx_pin, tx_buf[sample_msb]), NextValue(tx_buf, Cat(0, tx_buf[:-1])), NextValue(tx_cnt, tx_cnt - 1), NextState("LEFT_WAIT"))) if concatenate_channels: txi2s.act( "LEFT_WAIT", If(~self.tx_ctl.fields.enable, NextState("IDLE")).Else( If( falling_edge, If((tx_cnt == 0), If( (sync_pin if frame_format == I2S_FORMAT.I2S_STANDARD else ~sync_pin), NextValue(tx_cnt, sample_width), NextState("RIGHT"), ).Else(NextState("LEFT_WAIT"), )).Elif( tx_cnt > 0, NextState("LEFT"), )))) else: txi2s.act( "LEFT_WAIT", If(~self.tx_ctl.fields.enable, NextState("IDLE")).Else( If( falling_edge, If((tx_cnt == 0), If( (sync_pin if frame_format == I2S_FORMAT.I2S_STANDARD else ~sync_pin), NextValue(tx_cnt, sample_width), NextState("RIGHT"), NextValue(tx_buf, Cat(tx_rd_d, offset)), tx_rden.eq(1), ).Else(NextState("LEFT_WAIT"), )).Elif( tx_cnt > 0, NextState("LEFT"), )))) txi2s.act( "RIGHT", If(~self.tx_ctl.fields.enable, NextState("IDLE")).Else( NextValue(tx_pin, tx_buf[sample_msb]), NextValue(tx_buf, Cat(0, tx_buf[:-1])), NextValue(tx_cnt, tx_cnt - 1), NextState("RIGHT_WAIT"))) txi2s.act( "RIGHT_WAIT", If(~self.tx_ctl.fields.enable, NextState("IDLE")).Else( If( falling_edge, If((tx_cnt == 0) & (~sync_pin if frame_format == I2S_FORMAT.I2S_STANDARD else sync_pin), NextValue(tx_cnt, sample_width), NextState("LEFT"), NextValue(tx_buf, Cat(tx_rd_d, offset)), tx_rden.eq(1)).Elif(tx_cnt > 0, NextState("RIGHT")))))
def __init__(self, signal): self._in = CSRStatus(len(signal)) self.specials += MultiReg(signal, self._in.status)
def __init__(self, sys_clk_freq): self.done = Signal() self.restart = Signal() # GTP signals self.plllock = Signal() self.pllreset = Signal() self.gttxreset = Signal() self.txresetdone = Signal() self.txdlysreset = Signal() self.txdlysresetdone = Signal() self.txphinit = Signal() self.txphinitdone = Signal() self.txphalign = Signal() self.txphaligndone = Signal() self.txdlyen = Signal() self.txuserrdy = Signal() # # # # Double-latch transceiver asynch outputs plllock = Signal() txresetdone = Signal() txdlysresetdone = Signal() txphinitdone = Signal() txphaligndone = Signal() self.specials += [ MultiReg(self.plllock, plllock), MultiReg(self.txresetdone, txresetdone), MultiReg(self.txdlysresetdone, txdlysresetdone), MultiReg(self.txphinitdone, txphinitdone), MultiReg(self.txphaligndone, txphaligndone) ] # Deglitch FSM outputs driving transceiver asynch inputs gttxreset = Signal() txdlysreset = Signal() txphinit = Signal() txphalign = Signal() txdlyen = Signal() txuserrdy = Signal() self.sync += [ self.gttxreset.eq(gttxreset), self.txdlysreset.eq(txdlysreset), self.txphinit.eq(txphinit), self.txphalign.eq(txphalign), self.txdlyen.eq(txdlyen), self.txuserrdy.eq(txuserrdy) ] # PLL reset must be at least 500us pll_reset_cycles = ceil(500e-9 * sys_clk_freq) pll_reset_timer = WaitTimer(pll_reset_cycles) self.submodules += pll_reset_timer startup_fsm = ResetInserter()(FSM(reset_state="PLL_RESET")) self.submodules += startup_fsm ready_timer = WaitTimer(int(1e-3 * sys_clk_freq)) self.submodules += ready_timer self.comb += [ ready_timer.wait.eq(~self.done & ~startup_fsm.reset), startup_fsm.reset.eq(self.restart | ready_timer.done) ] txphaligndone_r = Signal(reset=1) txphaligndone_rising = Signal() self.sync += txphaligndone_r.eq(txphaligndone) self.comb += txphaligndone_rising.eq(txphaligndone & ~txphaligndone_r) startup_fsm.act("PLL_RESET", self.pllreset.eq(1), pll_reset_timer.wait.eq(1), If(pll_reset_timer.done, NextState("GTP_RESET"))) startup_fsm.act("GTP_RESET", gttxreset.eq(1), If(plllock, NextState("WAIT_GTP_RESET_DONE"))) # Release GTP reset and wait for GTP resetdone # (from UG482, GTP is reset on falling edge # of gttxreset) startup_fsm.act("WAIT_GTP_RESET_DONE", txuserrdy.eq(1), If(txresetdone, NextState("ALIGN"))) # Start delay alignment startup_fsm.act("ALIGN", txuserrdy.eq(1), txdlysreset.eq(1), If(txdlysresetdone, NextState("PHALIGN"))) # Start phase alignment startup_fsm.act("PHALIGN", txuserrdy.eq(1), txphinit.eq(1), If(txphinitdone, NextState("WAIT_FIRST_ALIGN_DONE"))) # Wait 2 rising edges of Xxphaligndone # (from UG482 in TX Buffer Bypass in Single-Lane Auto Mode) startup_fsm.act( "WAIT_FIRST_ALIGN_DONE", txuserrdy.eq(1), txphalign.eq(1), If(txphaligndone_rising, NextState("WAIT_SECOND_ALIGN_DONE"))) startup_fsm.act("WAIT_SECOND_ALIGN_DONE", txuserrdy.eq(1), txdlyen.eq(1), If(txphaligndone_rising, NextState("READY"))) startup_fsm.act("READY", txuserrdy.eq(1), self.done.eq(1), If(self.restart, NextState("PLL_RESET")))
def __init__(self, platform, pads, data_width=64, bar0_size=1 * MB, cd="sys", pcie_data_width=64): # Streams ---------------------------------------------------------------------------------- self.sink = stream.Endpoint(phy_layout(data_width)) self.source = stream.Endpoint(phy_layout(data_width)) self.msi = stream.Endpoint(msi_layout()) # Registers -------------------------------------------------------------------------------- self._link_up = CSRStatus( description="Link Up Status. ``1``: Link is Up.") self._msi_enable = CSRStatus( description="MSI Enable Status. ``1``: MSI is enabled.") self._bus_master_enable = CSRStatus( description="Bus Mastering Status. ``1``: Bus Mastering enabled.") self._max_request_size = CSRStatus( 16, description="Negiotiated Max Request Size (in bytes).") self._max_payload_size = CSRStatus( 16, description="Negiotiated Max Payload Size (in bytes).") # Parameters/Locals ------------------------------------------------------------------------ self.platform = platform self.data_width = data_width self.pcie_data_width = pcie_data_width self.id = Signal(16) self.bar0_size = bar0_size self.bar0_mask = get_bar_mask(bar0_size) self.max_request_size = Signal(16) self.max_payload_size = Signal(16) self.external_hard_ip = False # # # self.nlanes = nlanes = len(pads.tx_p) assert nlanes in [1, 2, 4] assert data_width in [64, 128] assert pcie_data_width in [64, 128] # Clocking --------------------------------------------------------------------------------- pcie_refclk = Signal() self.specials += Instance("IBUFDS_GTE2", i_CEB=0, i_I=pads.clk_p, i_IB=pads.clk_n, o_O=pcie_refclk) platform.add_period_constraint(pads.clk_p, 1e9 / 100e6) self.clock_domains.cd_pcie = ClockDomain() pcie_clk_freq = max(125e6, nlanes * 62.5e6 * 64 / pcie_data_width) platform.add_period_constraint(self.cd_pcie.clk, 1e9 / pcie_clk_freq) # TX (FPGA --> HOST) CDC / Data Width Convertion ------------------------------------------- if (cd == "pcie") and (data_width == pcie_data_width): s_axis_tx = self.sink else: tx_pipe_valid = stream.PipeValid(phy_layout(data_width)) tx_pipe_valid = ClockDomainsRenamer(cd)(tx_pipe_valid) tx_cdc = stream.AsyncFIFO(phy_layout(data_width), 4) tx_cdc = ClockDomainsRenamer({"write": cd, "read": "pcie"})(tx_cdc) tx_converter = stream.StrideConverter(phy_layout(data_width), phy_layout(pcie_data_width)) tx_converter = ClockDomainsRenamer("pcie")(tx_converter) tx_pipe_ready = stream.PipeValid(phy_layout(pcie_data_width)) tx_pipe_ready = ClockDomainsRenamer("pcie")(tx_pipe_ready) self.submodules += tx_pipe_valid, tx_cdc, tx_converter, tx_pipe_ready self.comb += [ self.sink.connect(tx_pipe_valid.sink), tx_pipe_valid.source.connect(tx_cdc.sink), tx_cdc.source.connect(tx_converter.sink), tx_converter.source.connect(tx_pipe_ready.sink) ] s_axis_tx = tx_pipe_ready.source # RX (HOST --> FPGA) CDC / Data Width Convertion ------------------------------------------- if (cd == "pcie") and (data_width == pcie_data_width): m_axis_rx = self.source else: rx_pipe_ready = stream.PipeReady(phy_layout(pcie_data_width)) rx_pipe_ready = ClockDomainsRenamer("pcie")(rx_pipe_ready) rx_converter = stream.StrideConverter(phy_layout(pcie_data_width), phy_layout(data_width)) rx_converter = ClockDomainsRenamer("pcie")(rx_converter) rx_cdc = stream.AsyncFIFO(phy_layout(data_width), 4) rx_cdc = ClockDomainsRenamer({"write": "pcie", "read": cd})(rx_cdc) rx_pipe_valid = stream.PipeValid(phy_layout(data_width)) rx_pipe_valid = ClockDomainsRenamer(cd)(rx_pipe_valid) self.submodules += rx_pipe_ready, rx_converter, rx_pipe_valid, rx_cdc self.comb += [ rx_pipe_ready.source.connect(rx_converter.sink), rx_converter.source.connect(rx_cdc.sink), rx_cdc.source.connect(rx_pipe_valid.sink), rx_pipe_valid.source.connect(self.source), ] m_axis_rx = rx_pipe_ready.sink if pcie_data_width == 128: rx_aligner = AXISRX128BAligner() rx_aligner = ClockDomainsRenamer("pcie")(rx_aligner) self.submodules += rx_aligner self.comb += rx_aligner.source.connect(m_axis_rx) m_axis_rx = rx_aligner.sink # MSI CDC (FPGA --> HOST) ------------------------------------------------------------------ if cd == "pcie": cfg_msi = self.msi else: msi_cdc = stream.AsyncFIFO(msi_layout(), 4) msi_cdc = ClockDomainsRenamer({ "write": cd, "read": "pcie" })(msi_cdc) self.submodules += msi_cdc self.comb += self.msi.connect(msi_cdc.sink) cfg_msi = msi_cdc.source # Hard IP Configuration -------------------------------------------------------------------- def convert_size(command, size, max_size): cases = {} value = 128 for i in range(6): cases[i] = size.eq(value) value = min(value * 2, max_size) return Case(command, cases) link_up = Signal() msi_enable = Signal() bus_number = Signal(8) device_number = Signal(5) function_number = Signal(3) command = Signal(16) dcommand = Signal(16) self.sync.pcie += [ convert_size(dcommand[12:15], self.max_request_size, max_size=512), convert_size(dcommand[5:8], self.max_payload_size, max_size=512), self.id.eq(Cat(function_number, device_number, bus_number)) ] self.specials += [ MultiReg(link_up, self._link_up.status), MultiReg(command[2], self._bus_master_enable.status), MultiReg(msi_enable, self._msi_enable.status), MultiReg(self.max_request_size, self._max_request_size.status), MultiReg(self.max_payload_size, self._max_payload_size.status) ] # Hard IP ---------------------------------------------------------------------------------- class Open(Signal): pass m_axis_rx_tlast = Signal() m_axis_rx_tuser = Signal(32) self.pcie_phy_params = dict( # Parameters --------------------------------------------------------------------------- p_LINK_CAP_MAX_LINK_WIDTH=nlanes, p_C_DATA_WIDTH=pcie_data_width, p_KEEP_WIDTH=pcie_data_width // 8, p_PCIE_REFCLK_FREQ=0, # 100MHz refclk p_PCIE_USERCLK1_FREQ=3 if nlanes <= 2 else 4, p_PCIE_USERCLK2_FREQ=3 if (pcie_clk_freq == 125e6) else 4, p_PCIE_GT_DEVICE={ "xc7a": "GTP", "xc7k": "GTX", "xc7v": "GTX" }[platform.device[:4]], p_PCIE_USE_MODE="1.0", # PCI Express Interface ---------------------------------------------------------------- i_sys_clk=pcie_refclk, i_sys_rst_n=1 if not hasattr(pads, "rst_n") else pads.rst_n, # TX o_pci_exp_txp=pads.tx_p, o_pci_exp_txn=pads.tx_n, # RX i_pci_exp_rxp=pads.rx_p, i_pci_exp_rxn=pads.rx_n, # Clocking Sharing Interface ----------------------------------------------------------- o_pipe_pclk_out_slave=Open(), o_pipe_rxusrclk_out=Open(), o_pipe_rxoutclk_out=Open(), o_pipe_dclk_out=Open(), o_pipe_userclk1_out=Open(), o_pipe_userclk2_out=Open(), o_pipe_oobclk_out=Open(), o_pipe_mmcm_lock_out=Open(), i_pipe_pclk_sel_slave=0b00, i_pipe_mmcm_rst_n=1, # AXI-S Interface ---------------------------------------------------------------------- # Common o_user_clk_out=ClockSignal("pcie"), o_user_reset_out=ResetSignal("pcie"), o_user_lnk_up=link_up, o_user_app_rdy=Open(), # TX o_tx_buf_av=Open(), o_tx_err_drop=Open(), o_tx_cfg_req=Open(), i_tx_cfg_gnt=1, i_s_axis_tx_tvalid=s_axis_tx.valid, i_s_axis_tx_tlast=s_axis_tx.last, o_s_axis_tx_tready=s_axis_tx.ready, i_s_axis_tx_tdata=s_axis_tx.dat, i_s_axis_tx_tkeep=s_axis_tx.be, i_s_axis_tx_tuser=0, # RX i_rx_np_ok=1, i_rx_np_req=1, o_m_axis_rx_tvalid=m_axis_rx.valid, o_m_axis_rx_tlast=m_axis_rx_tlast, i_m_axis_rx_tready=m_axis_rx.ready, o_m_axis_rx_tdata=m_axis_rx.dat, o_m_axis_rx_tkeep=m_axis_rx.be, o_m_axis_rx_tuser=m_axis_rx_tuser, # Flow Control o_fc_cpld=Open(), o_fc_cplh=Open(), o_fc_npd=Open(), o_fc_nph=Open(), o_fc_pd=Open(), o_fc_ph=Open(), i_fc_sel=0, # Management Interface ----------------------------------------------------------------- o_cfg_mgmt_do=Open(), o_cfg_mgmt_rd_wr_done=Open(), i_cfg_mgmt_di=0, i_cfg_mgmt_byte_en=0, i_cfg_mgmt_dwaddr=0, i_cfg_mgmt_wr_en=0, i_cfg_mgmt_rd_en=0, i_cfg_mgmt_wr_readonly=0, i_cfg_mgmt_wr_rw1c_as_rw=0, # Error Reporting Interface ------------------------------------------------------------ i_cfg_err_ecrc=0, i_cfg_err_ur=0, i_cfg_err_cpl_timeout=0, i_cfg_err_cpl_unexpect=0, i_cfg_err_cpl_abort=0, i_cfg_err_posted=0, i_cfg_err_cor=0, i_cfg_err_atomic_egress_blocked=0, i_cfg_err_internal_cor=0, i_cfg_err_malformed=0, i_cfg_err_mc_blocked=0, i_cfg_err_poisoned=0, i_cfg_err_norecovery=0, i_cfg_err_tlp_cpl_header=0, o_cfg_err_cpl_rdy=Open(), i_cfg_err_locked=0, i_cfg_err_acs=0, i_cfg_err_internal_uncor=0, # AER interface ------------------------------------------------------------------------ i_cfg_err_aer_headerlog=0, i_cfg_aer_interrupt_msgnum=0, o_cfg_err_aer_headerlog_set=Open(), o_cfg_aer_ecrc_check_en=Open(), o_cfg_aer_ecrc_gen_en=Open(), i_cfg_turnoff_ok=0, i_cfg_trn_pending=0, i_cfg_pm_halt_aspm_l0s=0, i_cfg_pm_halt_aspm_l1=0, i_cfg_pm_force_state_en=0, i_cfg_pm_force_state=0, i_cfg_dsn=0, i_cfg_pm_send_pme_to=0, i_cfg_ds_bus_number=0, i_cfg_ds_device_number=0, i_cfg_ds_function_number=0, i_cfg_pm_wake=0, # Interrupt Interface ------------------------------------------------------------------ i_cfg_interrupt=cfg_msi.valid, o_cfg_interrupt_rdy=cfg_msi.ready, i_cfg_interrupt_assert=0, i_cfg_interrupt_di=cfg_msi.dat, o_cfg_interrupt_do=Open(), o_cfg_interrupt_mmenable=Open(), o_cfg_interrupt_msienable=msi_enable, o_cfg_interrupt_msixenable=Open(), o_cfg_interrupt_msixfm=Open(), i_cfg_interrupt_stat=0, i_cfg_pciecap_interrupt_msgnum=0, # Configuration Interface -------------------------------------------------------------- o_cfg_status=Open(), o_cfg_command=command, o_cfg_dstatus=Open(), o_cfg_dcommand=dcommand, o_cfg_lstatus=Open(), o_cfg_lcommand=Open(), o_cfg_dcommand2=Open(), o_cfg_pcie_link_state=Open(), o_cfg_to_turnoff=Open(), o_cfg_bus_number=bus_number, o_cfg_device_number=device_number, o_cfg_function_number=function_number, o_cfg_pmcsr_pme_en=Open(), o_cfg_pmcsr_powerstate=Open(), o_cfg_pmcsr_pme_status=Open(), o_cfg_received_func_lvl_rst=Open(), o_cfg_bridge_serr_en=Open(), o_cfg_slot_control_electromech_il_ctl_pulse=Open(), o_cfg_root_control_syserr_corr_err_en=Open(), o_cfg_root_control_syserr_non_fatal_err_en=Open(), o_cfg_root_control_syserr_fatal_err_en=Open(), o_cfg_root_control_pme_int_en=Open(), o_cfg_aer_rooterr_corr_err_reporting_en=Open(), o_cfg_aer_rooterr_non_fatal_err_reporting_en=Open(), o_cfg_aer_rooterr_fatal_err_reporting_en=Open(), o_cfg_aer_rooterr_corr_err_received=Open(), o_cfg_aer_rooterr_non_fatal_err_received=Open(), o_cfg_aer_rooterr_fatal_err_received=Open(), # VC Interface ------------------------------------------------------------------------- o_cfg_vc_tcvc_map=Open(), o_cfg_msg_received=Open(), o_cfg_msg_data=Open(), o_cfg_msg_received_pm_as_nak=Open(), o_cfg_msg_received_setslotpowerlimit=Open(), o_cfg_msg_received_err_cor=Open(), o_cfg_msg_received_err_non_fatal=Open(), o_cfg_msg_received_err_fatal=Open(), o_cfg_msg_received_pm_pme=Open(), o_cfg_msg_received_pme_to_ack=Open(), o_cfg_msg_received_assert_int_a=Open(), o_cfg_msg_received_assert_int_b=Open(), o_cfg_msg_received_assert_int_c=Open(), o_cfg_msg_received_assert_int_d=Open(), o_cfg_msg_received_deassert_int_a=Open(), o_cfg_msg_received_deassert_int_b=Open(), o_cfg_msg_received_deassert_int_c=Open(), o_cfg_msg_received_deassert_int_d=Open(), # Physical Layer Interface ------------------------------------------------------------- i_pl_directed_link_change=0, i_pl_directed_link_width=0, i_pl_directed_link_speed=0, i_pl_directed_link_auton=0, i_pl_upstream_prefer_deemph=1, o_pl_sel_lnk_rate=Open(), o_pl_sel_lnk_width=Open(), o_pl_ltssm_state=Open(), o_pl_lane_reversal_mode=Open(), o_pl_phy_lnk_up=Open(), o_pl_tx_pm_state=Open(), o_pl_rx_pm_state=Open(), o_pl_link_upcfg_cap=Open(), o_pl_link_gen2_cap=Open(), o_pl_link_partner_gen2_supported=Open(), o_pl_initial_link_width=Open(), o_pl_directed_change_done=Open(), o_pl_received_hot_rst=Open(), i_pl_transmit_hot_rst=0, i_pl_downstream_deemph_source=0, # PCIe DRP Interface ------------------------------------------------------------------- i_pcie_drp_clk=1, i_pcie_drp_en=0, i_pcie_drp_we=0, i_pcie_drp_addr=0, i_pcie_drp_di=0, o_pcie_drp_rdy=Open(), o_pcie_drp_do=Open(), ) if pcie_data_width == 128: rx_is_sof = m_axis_rx_tuser[ 10:15] # Start of a new packet header in m_axis_rx_tdata. rx_is_eof = m_axis_rx_tuser[ 17:22] # End of a packet in m_axis_rx_tdata. self.comb += [ m_axis_rx.first.eq(rx_is_sof[-1]), m_axis_rx.last.eq(rx_is_eof[-1]), If(rx_is_sof == 0b11000, rx_aligner.first_dword.eq(2)), ] else: self.comb += [ m_axis_rx.first.eq(0), m_axis_rx.last.eq(m_axis_rx_tlast), ]
def __init__(self, sys_clk_freq): self.done = Signal() self.restart = Signal() # GTP signals self.plllock = Signal() self.gtrxreset = Signal() self.rxresetdone = Signal() self.rxdlysreset = Signal() self.rxdlysresetdone = Signal() self.rxphalign = Signal() self.rxdlyen = Signal() self.rxuserrdy = Signal() self.rxsyncdone = Signal() self.rxpmaresetdone = Signal() self.drpaddr = Signal(9) self.drpen = Signal() self.drpdi = Signal(16) self.drprdy = Signal() self.drpdo = Signal(16) self.drpwe = Signal() # # # drpvalue = Signal(16) drpmask = Signal() self.comb += [ self.drpaddr.eq(0x011), If(drpmask, self.drpdi.eq(drpvalue & 0xf7ff)).Else(self.drpdi.eq(drpvalue)) ] rxpmaresetdone = Signal() self.specials += MultiReg(self.rxpmaresetdone, rxpmaresetdone) rxpmaresetdone_r = Signal() self.sync += rxpmaresetdone_r.eq(rxpmaresetdone) # Double-latch transceiver asynch outputs plllock = Signal() rxresetdone = Signal() rxdlysresetdone = Signal() rxsyncdone = Signal() self.specials += [ MultiReg(self.plllock, plllock), MultiReg(self.rxresetdone, rxresetdone), MultiReg(self.rxdlysresetdone, rxdlysresetdone), MultiReg(self.rxsyncdone, rxsyncdone) ] # Deglitch FSM outputs driving transceiver asynch inputs gtrxreset = Signal() rxdlysreset = Signal() rxphalign = Signal() rxdlyen = Signal() rxuserrdy = Signal() self.sync += [ self.gtrxreset.eq(gtrxreset), self.rxdlysreset.eq(rxdlysreset), self.rxphalign.eq(rxphalign), self.rxdlyen.eq(rxdlyen), self.rxuserrdy.eq(rxuserrdy) ] # After configuration, transceiver resets have to stay low for # at least 500ns (see AR43482) pll_reset_cycles = ceil(500e-9 * sys_clk_freq) pll_reset_timer = WaitTimer(pll_reset_cycles) self.submodules += pll_reset_timer startup_fsm = ResetInserter()(FSM(reset_state="GTP_RESET")) self.submodules += startup_fsm ready_timer = WaitTimer(int(4e-3 * sys_clk_freq)) self.submodules += ready_timer self.comb += [ ready_timer.wait.eq(~self.done & ~startup_fsm.reset), startup_fsm.reset.eq(self.restart | ready_timer.done) ] cdr_stable_timer = WaitTimer(1024) self.submodules += cdr_stable_timer startup_fsm.act("GTP_RESET", gtrxreset.eq(1), NextState("DRP_READ_ISSUE")) startup_fsm.act("DRP_READ_ISSUE", gtrxreset.eq(1), self.drpen.eq(1), NextState("DRP_READ_WAIT")) startup_fsm.act( "DRP_READ_WAIT", gtrxreset.eq(1), If(self.drprdy, NextValue(drpvalue, self.drpdo), NextState("DRP_MOD_ISSUE"))) startup_fsm.act("DRP_MOD_ISSUE", gtrxreset.eq(1), drpmask.eq(1), self.drpen.eq(1), self.drpwe.eq(1), NextState("DRP_MOD_WAIT")) startup_fsm.act("DRP_MOD_WAIT", gtrxreset.eq(1), If(self.drprdy, NextState("WAIT_PMARST_FALL"))) startup_fsm.act( "WAIT_PMARST_FALL", rxuserrdy.eq(1), If(rxpmaresetdone_r & ~rxpmaresetdone, NextState("DRP_RESTORE_ISSUE"))) startup_fsm.act("DRP_RESTORE_ISSUE", rxuserrdy.eq(1), self.drpen.eq(1), self.drpwe.eq(1), NextState("DRP_RESTORE_WAIT")) startup_fsm.act("DRP_RESTORE_WAIT", rxuserrdy.eq(1), If(self.drprdy, NextState("WAIT_GTP_RESET_DONE"))) # Release GTP reset and wait for GTP resetdone # (from UG482, GTP is reset on falling edge # of gtrxreset) startup_fsm.act( "WAIT_GTP_RESET_DONE", rxuserrdy.eq(1), cdr_stable_timer.wait.eq(1), If(rxresetdone & cdr_stable_timer.done, NextState("ALIGN"))) # Start delay alignment startup_fsm.act("ALIGN", rxuserrdy.eq(1), rxdlysreset.eq(1), If(rxdlysresetdone, NextState("WAIT_ALIGN_DONE"))) # Wait for delay alignment startup_fsm.act("WAIT_ALIGN_DONE", rxuserrdy.eq(1), If(rxsyncdone, NextState("READY"))) startup_fsm.act("READY", rxuserrdy.eq(1), self.done.eq(1), If(self.restart, NextState("GTP_RESET")))
def __init__(self, default_video_timings="800x600@60Hz"): # Check / Get Video Timings (can be str or dict) if isinstance(default_video_timings, str): try: self.video_timings = vt = video_timings[default_video_timings] except KeyError: msg = [f"Video Timings {default_video_timings} not supported, availables:"] for video_timing in video_timings.keys(): msg.append(f" - {video_timing} / {video_timings[video_timing]['pix_clk']/1e6:3.2f}MHz.") raise ValueError("\n".join(msg)) else: self.video_timings = vt = default_video_timings # MMAP Control/Status Registers. self._enable = CSRStorage(reset=1) self._hres = CSRStorage(hbits, vt["h_active"]) self._hsync_start = CSRStorage(hbits, vt["h_active"] + vt["h_sync_offset"]) self._hsync_end = CSRStorage(hbits, vt["h_active"] + vt["h_sync_offset"] + vt["h_sync_width"]) self._hscan = CSRStorage(hbits, vt["h_active"] + vt["h_blanking"]) self._vres = CSRStorage(vbits, vt["v_active"]) self._vsync_start = CSRStorage(vbits, vt["v_active"] + vt["v_sync_offset"]) self._vsync_end = CSRStorage(vbits, vt["v_active"] + vt["v_sync_offset"] + vt["v_sync_width"]) self._vscan = CSRStorage(vbits, vt["v_active"] + vt["v_blanking"]) # Video Timing Source self.source = source = stream.Endpoint(video_timing_layout) # # # # Resynchronize Enable to Video clock domain. self.enable = enable = Signal() self.specials += MultiReg(self._enable.storage, enable) # Resynchronize Horizontal Timings to Video clock domain. self.hres = hres = Signal(hbits) self.hsync_start = hsync_start = Signal(hbits) self.hsync_end = hsync_end = Signal(hbits) self.hscan = hscan = Signal(hbits) self.specials += MultiReg(self._hres.storage, hres) self.specials += MultiReg(self._hsync_start.storage, hsync_start) self.specials += MultiReg(self._hsync_end.storage, hsync_end) self.specials += MultiReg(self._hscan.storage, hscan) # Resynchronize Vertical Timings to Video clock domain. self.vres = vres = Signal(vbits) self.vsync_start = vsync_start = Signal(vbits) self.vsync_end = vsync_end = Signal(vbits) self.vscan = vscan = Signal(vbits) self.specials += MultiReg(self._vres.storage, vres) self.specials += MultiReg(self._vsync_start.storage, vsync_start) self.specials += MultiReg(self._vsync_end.storage, vsync_end) self.specials += MultiReg(self._vscan.storage, vscan) # Generate timings. hactive = Signal() vactive = Signal() fsm = FSM(reset_state="IDLE") fsm = ResetInserter()(fsm) self.submodules.fsm = fsm self.comb += fsm.reset.eq(~enable) fsm.act("IDLE", NextValue(hactive, 0), NextValue(vactive, 0), NextValue(source.hres, hres), NextValue(source.vres, vres), NextValue(source.hcount, 0), NextValue(source.vcount, 0), NextState("RUN") ) self.comb += source.de.eq(hactive & vactive) # DE when both HActive and VActive. self.sync += source.first.eq((source.hcount == 0) & (source.vcount == 0)), self.sync += source.last.eq( (source.hcount == hscan) & (source.vcount == vscan)), fsm.act("RUN", source.valid.eq(1), If(source.ready, # Increment HCount. NextValue(source.hcount, source.hcount + 1), # Generate HActive / HSync. If(source.hcount == 0, NextValue(hactive, 1)), # Start of HActive. If(source.hcount == hres, NextValue(hactive, 0)), # End of HActive. If(source.hcount == hsync_start, NextValue(source.hsync, 1)), # Start of HSync. If(source.hcount == hsync_end, NextValue(source.hsync, 0)), # End of HSync. # End of HScan. If(source.hcount == hscan, # Reset HCount. NextValue(source.hcount, 0), # Increment VCount. NextValue(source.vcount, source.vcount + 1), # Generate VActive / VSync. If(source.vcount == 0, NextValue(vactive, 1)), # Start of VActive. If(source.vcount == vres, NextValue(vactive, 0)), # End of HActive. If(source.vcount == vsync_start, NextValue(source.vsync, 1)), # Start of VSync. If(source.vcount == vsync_end, NextValue(source.vsync, 0)), # End of VSync. # End of VScan. If(source.vcount == vscan, # Reset VCount. NextValue(source.vcount, 0), ) ) ) )
def __init__(self, pads, clk_freq, fifo_depth=8, read_time=128, write_time=128): dw = len(pads.data) self.clk_freq = clk_freq # timings tRD = self.ns(30) # RD# active pulse width (t4) tRDDataSetup = self.ns(14) # RD# to DATA (t3) tWRDataSetup = self.ns(5) # DATA to WR# active setup time (t8) tWR = self.ns(30) # WR# active pulse width (t10) tMultiReg = 2 # read fifo (FTDI --> SoC) read_fifo = stream.SyncFIFO(phy_description(dw), fifo_depth) # write fifo (SoC --> FTDI) write_fifo = stream.SyncFIFO(phy_description(dw), fifo_depth) self.submodules += read_fifo, write_fifo # sink / source interfaces self.sink = write_fifo.sink self.source = read_fifo.source # read / write arbitration wants_write = Signal() wants_read = Signal() txe_n = Signal() rxf_n = Signal() self.specials += [ MultiReg(pads.txe_n, txe_n), MultiReg(pads.rxf_n, rxf_n) ] self.comb += [ wants_write.eq(~txe_n & write_fifo.source.valid), wants_read.eq(~rxf_n & read_fifo.sink.ready), ] read_time_en, max_read_time = anti_starvation(self, read_time) write_time_en, max_write_time = anti_starvation(self, write_time) fsm = FSM(reset_state="READ") self.submodules += fsm read_done = Signal() write_done = Signal() commuting = Signal() fsm.act( "READ", read_time_en.eq(1), If( wants_write & read_done, If(~wants_read | max_read_time, commuting.eq(1), NextState("RTW")))) fsm.act("RTW", NextState("WRITE")) fsm.act( "WRITE", write_time_en.eq(1), If( wants_read & write_done, If(~wants_write | max_write_time, commuting.eq(1), NextState("WTR")))) fsm.act("WTR", NextState("READ")) # databus tristate data_w = Signal(dw) data_r_async = Signal(dw) data_r = Signal(dw) data_oe = Signal() self.specials += [ Tristate(pads.data, data_w, data_oe, data_r_async), MultiReg(data_r_async, data_r) ] # read actions pads.rd_n.reset = 1 read_fsm = FSM(reset_state="IDLE") self.submodules += read_fsm read_counter = Signal(8) read_fsm.act( "IDLE", read_done.eq(1), NextValue(read_counter, 0), If( fsm.ongoing("READ") & wants_read, If(~commuting, NextState("PULSE_RD_N")))) read_fsm.act( "PULSE_RD_N", pads.rd_n.eq(0), NextValue(read_counter, read_counter + 1), If(read_counter == max(tRD - 1, tRDDataSetup + tMultiReg - 1), NextState("ACQUIRE_DATA"))) read_fsm.act("ACQUIRE_DATA", read_fifo.sink.valid.eq(1), read_fifo.sink.data.eq(data_r), NextState("WAIT_RXF_N")) read_fsm.act("WAIT_RXF_N", If(rxf_n, NextState("IDLE"))) # write actions pads.wr_n.reset = 1 write_fsm = FSM(reset_state="IDLE") self.submodules += write_fsm write_counter = Signal(8) write_fsm.act( "IDLE", write_done.eq(1), NextValue(write_counter, 0), If( fsm.ongoing("WRITE") & wants_write, If(~commuting, NextState("SET_DATA")))) write_fsm.act( "SET_DATA", data_oe.eq(1), data_w.eq(write_fifo.source.data), NextValue(write_counter, write_counter + 1), If(write_counter == (tWRDataSetup - 1), NextValue(write_counter, 0), NextState("PULSE_WR_N"))) write_fsm.act("PULSE_WR_N", data_oe.eq(1), data_w.eq(write_fifo.source.data), pads.wr_n.eq(0), NextValue(write_counter, write_counter + 1), If(write_counter == (tWR - 1), NextState("WAIT_TXE_N"))) write_fsm.act( "WAIT_TXE_N", If(txe_n, write_fifo.source.ready.eq(1), NextState("IDLE")))
def __init__(self, data_width, depth): self.sink = sink = stream.Endpoint(core_layout(data_width)) self.enable = CSRStorage() self.done = CSRStatus() self.length = CSRStorage(bits_for(depth)) self.offset = CSRStorage(bits_for(depth)) self.mem_valid = CSRStatus() self.mem_ready = CSR() self.mem_data = CSRStatus(data_width) # # # # control re-synchronization enable = Signal() enable_d = Signal() enable = Signal() enable_d = Signal() self.specials += MultiReg(self.enable.storage, enable, "scope") self.sync.scope += enable_d.eq(enable) length = Signal(max=depth) offset = Signal(max=depth) self.specials += [ MultiReg(self.length.storage, length, "scope"), MultiReg(self.offset.storage, offset, "scope") ] # status re-synchronization done = Signal() self.specials += MultiReg(done, self.done.status) # memory mem = stream.SyncFIFO([("data", data_width)], depth, buffered=True) mem = ClockDomainsRenamer("scope")(mem) cdc = stream.AsyncFIFO([("data", data_width)], 4) cdc = ClockDomainsRenamer( {"write": "scope", "read": "sys"})(cdc) self.submodules += mem, cdc # flush mem_flush = WaitTimer(depth) mem_flush = ClockDomainsRenamer("scope")(mem_flush) self.submodules += mem_flush # fsm fsm = FSM(reset_state="IDLE") fsm = ClockDomainsRenamer("scope")(fsm) self.submodules += fsm fsm.act("IDLE", done.eq(1), If(enable & ~enable_d, NextState("FLUSH") ), sink.ready.eq(1), mem.source.connect(cdc.sink) ) fsm.act("FLUSH", sink.ready.eq(1), mem_flush.wait.eq(1), mem.source.ready.eq(1), If(mem_flush.done, NextState("WAIT") ) ) fsm.act("WAIT", sink.connect(mem.sink, omit={"hit"}), If(sink.valid & sink.hit, NextState("RUN") ), mem.source.ready.eq(mem.level >= self.offset.storage) ) fsm.act("RUN", sink.connect(mem.sink, omit={"hit"}), If(mem.level >= self.length.storage, NextState("IDLE"), ) ) # memory read self.comb += [ self.mem_valid.status.eq(cdc.source.valid), cdc.source.ready.eq(self.mem_ready.re | ~self.enable.storage), self.mem_data.status.eq(cdc.source.data) ]
def __init__(self, comma, tx_clk_freq, check_period=6e-3): self.rxdata = Signal(20) self.restart = Signal() self.ready = Signal() check_max_val = ceil(check_period*tx_clk_freq) check_counter = Signal(max=check_max_val+1) check = Signal() reset_check_counter = Signal() self.sync.rtio_tx += [ check.eq(0), If(reset_check_counter, check_counter.eq(check_max_val) ).Else( If(check_counter == 0, check.eq(1), check_counter.eq(check_max_val) ).Else( check_counter.eq(check_counter-1) ) ) ] checks_reset = PulseSynchronizer("rtio_tx", "rtio_rx") self.submodules += checks_reset comma_n = ~comma & 0b1111111111 comma_seen_rxclk = Signal() comma_seen = Signal() comma_seen_rxclk.attr.add("no_retiming") self.specials += MultiReg(comma_seen_rxclk, comma_seen) self.sync.rtio_rx += \ If(checks_reset.o, comma_seen_rxclk.eq(0) ).Elif((self.rxdata[:10] == comma) | (self.rxdata[:10] == comma_n), comma_seen_rxclk.eq(1) ) error_seen_rxclk = Signal() error_seen = Signal() error_seen_rxclk.attr.add("no_retiming") self.specials += MultiReg(error_seen_rxclk, error_seen) rx1cnt = Signal(max=11) self.sync.rtio_rx += [ rx1cnt.eq(reduce(add, [self.rxdata[i] for i in range(10)])), If(checks_reset.o, error_seen_rxclk.eq(0) ).Elif((rx1cnt != 4) & (rx1cnt != 5) & (rx1cnt != 6), error_seen_rxclk.eq(1) ) ] fsm = ClockDomainsRenamer("rtio_tx")(FSM(reset_state="WAIT_COMMA")) self.submodules += fsm fsm.act("WAIT_COMMA", If(check, # Errors are still OK at this stage, as the transceiver # has just been reset and may output garbage data. If(comma_seen, NextState("WAIT_NOERROR") ).Else( self.restart.eq(1) ), checks_reset.i.eq(1) ) ) fsm.act("WAIT_NOERROR", If(check, If(comma_seen & ~error_seen, NextState("READY") ).Else( self.restart.eq(1), NextState("WAIT_COMMA") ), checks_reset.i.eq(1) ) ) fsm.act("READY", reset_check_counter.eq(1), self.ready.eq(1), If(error_seen, checks_reset.i.eq(1), self.restart.eq(1), NextState("WAIT_COMMA") ) )
def __init__(self, phys, jesd_settings, converter_data_width): self.enable = Signal() self.jsync = Signal() self.jref = Signal() self.ready = Signal() self.restart = Signal() self.prbs_config = Signal(4) self.stpl_enable = Signal() self.sink = Record([("converter" + str(i), converter_data_width) for i in range(jesd_settings.nconverters)]) # # # # restart when disabled or on re-synchronization request self.comb += self.restart.eq(~self.enable | self.ready & ~self.jsync) # transport layer transport = JESD204BTransportTX(jesd_settings, converter_data_width) self.submodules.transport = transport # stpl stpl = JESD204BSTPLGenerator(jesd_settings, converter_data_width) self.submodules += stpl stpl_enable = Signal() self.specials += \ MultiReg(self.stpl_enable, stpl_enable) self.comb += \ If(stpl_enable, transport.sink.eq(stpl.source) ).Else( transport.sink.eq(self.sink) ) links = [] for n, (phy, lane) in enumerate(zip(phys, transport.source.flatten())): phy_name = "phy{}".format(n) phy_cd = phy_name + "_tx" # claim the phy setattr(self.submodules, phy_name, phy) ebuf = ElasticBuffer(len(phy.data), 4, "sys", phy_cd) setattr(self.submodules, "ebuf{}".format(n), ebuf) link = JESD204BLinkTX(len(phy.data), jesd_settings, n) link = ClockDomainsRenamer(phy_cd)(link) links.append(link) self.comb += [link.jsync.eq(self.jsync), link.jref.eq(self.jref)] self.submodules += link # connect data self.comb += [ ebuf.din.eq(lane), link.sink.data.eq(ebuf.dout), phy.data.eq(link.source.data), phy.ctrl.eq(link.source.ctrl) ] # connect control self.comb += phy.transmitter.init.restart.eq( self.restart & (self.prbs_config == 0)) self.specials += MultiReg(self.prbs_config, phy.transmitter.prbs_config, phy_cd) ready = Signal() self.comb += ready.eq(reduce(and_, [link.ready for link in links])) self.specials += MultiReg(ready, self.ready)
def __init__(self, clk, cd_rst, ulpi_rst, ulpi_stp_ovr, ulpi_reg): # TESTING - UCFG_RST register # - BRST: reseting of code in the ULPI cd # - URST: reset of external phy # - FSTP: Force assert of STP during reset # (should be part of ULPI cd bringup code) # # Format: # # 7 6 5 4 3 2 1 0 # --------------------------------------- # 0 0 0 0 0 FSTP BRST URST # self._rst = CSRStorage(3) self.comb += ulpi_rst.eq(self._rst.storage[0]) self.comb += cd_rst.eq(self._rst.storage[1]) self.comb += ulpi_stp_ovr.eq(self._rst.storage[2]) # TESTING - UCFG_STAT register # # CKACT: single status bit that indicates whether # the ULPI phy is providing a 60mhz clock signal on clk # # Format: # # 7 6 5 4 3 2 1 0 # ---------------------------------------- # 0 0 0 0 0 0 0 CKACT # self._stat = CSRStatus(1) ulpi_clk_act_cd = Signal(8) # Resample ulpi_clk in sys sys_ulpi_clk = Signal(1) self.specials += MultiReg(clk, sys_ulpi_clk) # Edge detect the ULPI clk last_ulpi_clk = Signal(1) self.sync += [ last_ulpi_clk.eq(sys_ulpi_clk), # On detected transistions, reset the countdown If(last_ulpi_clk != sys_ulpi_clk, ulpi_clk_act_cd.eq(0)).Elif( ulpi_clk_act_cd < 0xFF, ulpi_clk_act_cd.eq(ulpi_clk_act_cd + 1)) ] self.comb += self._stat.status.eq(ulpi_clk_act_cd != 0xFF) # ULPI_xDATA and xCMD registers # # Used for reading/writing ULPI regs # # ULPI_xDATA Format: just 8 bit data reg # # ULPI_xCMD Format: # # 7 6 5 4 3 2 1 0 # ---------------------------------------- # GO 0 UA5 UA4 UA3 UA2 UA1 UA0 # # GO: # - write 1 to start ULPI reg transaction # - stays 1 while transaction in progress # - clears to 0 when transaction complete # # UA5..0 - ULPI register address # To do a write: write UCFG_WDATA with the value # and then write ULPI_WCMD with GO | addr # # To read: write ULPI_RCMD with GO | addr, poll # until GO clear, then read ULPI_RDATA self._wdata = CSRStorage(8) self._wcmd = _ULPI_cmd_reg(ulpi_reg.wreq, ulpi_reg.wack, ulpi_reg.waddr) self.submodules += self._wcmd self.comb += ulpi_reg.wdata.eq(self._wdata.storage) self._rdata = CSRStatus(8) self._rcmd = _ULPI_cmd_reg(ulpi_reg.rreq, ulpi_reg.rack, ulpi_reg.raddr) self.submodules += self._rcmd self.sync += If(ulpi_reg.rack, self._rdata.status.eq(ulpi_reg.rdata))
def __init__(self, platform): default_period = 325000000 self.intro = ModuleDoc("""Watch Dog Timer A watchdog timer for Betrusted. Once enabled, it cannot be disabled, and it must have the reset_wdt bit written periodically to avoid a watchdog reset event. If this does not happen, the WDT will attempt to toggle reset of the full chip via GSR. The timeout period is specified in 'approximately 65MHz' (15.38ns) periods by the period register. According to Xilinx, 'approximately' is +/-50%. The register cannot be updated once the WDT is running. """) self.gsr = Signal() # wire to the GSR line for the FPGA self.cfgmclk = Signal( ) # wire to FPGA's CFGMCLK ring oscillator, a "typical" 65MHz clock self.clock_domains.cd_wdt = ClockDomain() self.specials += Instance("BUFG", i_I=self.cfgmclk, o_O=self.cd_wdt.clk) platform.add_platform_command( "create_clock -name wdt -period 10.256 [get_nets {net}]", net=self.cd_wdt.clk) # 65MHz + 50% tolerance ### WATCHDOG RESET, uses the extcomm_div divider to save on gates self.watchdog = CSRStorage(fields=[ CSRField("reset_wdt", size=1, description= "Write to this register to reset the watchdog timer", pulse=True), CSRField( "enable", description= "Enable the watchdog timer. Cannot be disabled once enabled, except with a reset. Notably, a watchdog reset will disable the watchdog.", reset=0), ]) reset_wdt = Signal() w_stretch = Signal(3) reset_wdt_sys = Signal() self.sync += [ If(self.watchdog.fields.reset_wdt, w_stretch.eq(7)).Elif(w_stretch > 0, w_stretch.eq(w_stretch - 1)), reset_wdt_sys.eq(w_stretch != 0), ] self.submodules.reset_sync = BlindTransfer("sys", "wdt") self.comb += [ self.reset_sync.i.eq(reset_wdt_sys), reset_wdt.eq(self.reset_sync.o), ] self.period = CSRStorage( 32, fields=[ CSRField( "period", size=32, description= "Number of 'approximately 65MHz' CFGMCLK cycles before each reset_code must be entered. Defaults to a range of {:0.2f}-{:0.2f} seconds" .format((default_period * 10.256e-9) * 0.5, (default_period * 10.256e-9) * 1.5), reset=default_period) ]) self.state = CSRStatus( 4, fields=[ CSRField("enabled", size=1, description="WDT has been enabled"), CSRField("armed1", size=1, description="WDT in the armed1 state"), CSRField("armed2", size=1, description="WDT in the armed2 state"), CSRField("disarmed", size=1, description="WDT in the disarmed state"), ]) armed1 = Signal() armed2 = Signal() disarmed = Signal() self.specials += MultiReg(armed1, self.state.fields.armed1) self.specials += MultiReg(armed2, self.state.fields.armed2) self.specials += MultiReg(disarmed, self.state.fields.disarmed) wdog_enable_wdt = Signal() self.specials += MultiReg(self.watchdog.fields.enable, wdog_enable_wdt, odomain="wdt") wdog_enabled = Signal(reset=0) wdog_enabled_r = Signal(reset=0) self.sync.wdt += [ If(wdog_enable_wdt, wdog_enabled.eq(1)).Else(wdog_enabled.eq(wdog_enabled)), wdog_enabled_r.eq(wdog_enabled) ] self.specials += MultiReg(wdog_enabled, self.state.fields.enabled) self.submodules.period_sync = BusSynchronizer(32, "sys", "wdt") wdt_count = Signal(32) self.comb += [ self.period_sync.i.eq(self.period.fields.period), wdt_count.eq(self.period_sync.o) ] wdt_count_lock = Signal(32) wdt_start = Signal() self.sync.wdt += [ wdt_start.eq(~wdog_enabled_r & wdog_enabled), If(~wdog_enabled_r & wdog_enabled, wdt_count_lock.eq(wdt_count)).Else( wdt_count_lock.eq(wdt_count_lock), ) ] wdog_counter = Signal(32, reset=default_period) wdog_cycle = Signal() self.sync.wdt += [ If( wdt_start, wdog_counter.eq(wdt_count_lock), ).Else( If( wdog_enabled, If(wdog_counter == 0, wdog_cycle.eq(1), wdog_counter.eq(wdt_count_lock)).Else( wdog_counter.eq(wdog_counter - 1), wdog_cycle.eq(0), ))) ] do_reset = Signal() wdog = ClockDomainsRenamer("wdt")(FSM(reset_state="IDLE")) self.submodules += wdog wdog.act("IDLE", If(wdog_enabled, NextState("DISARMED"))) wdog.act( "ARMED_HOT", armed2.eq(1), If(reset_wdt, NextState("DISARMED")).Elif( wdog_cycle, do_reset.eq(1), ), ) # double-interlock: not having responded to the watchdog code immediately # is not cause for a reset: this could just be the wdog_cycle hitting at an # inopportune time relative to the watchdog reset routine. # instead, escalate to the ARMED_HOT state so that # the watchdog next period, if no action was taken, we do a reset wdog.act( "ARMED", armed1.eq(1), If(reset_wdt, NextState("DISARMED")).Elif(wdog_cycle, NextState("ARMED_HOT"))) wdog.act("DISARMED", disarmed.eq(1), If(wdog_cycle, NextState("ARMED"))) do_reset_sys = Signal() reset_stretch = Signal(5, reset=0) self.submodules.do_res_sync = BlindTransfer("wdt", "sys") self.comb += [ self.do_res_sync.i.eq(do_reset), do_reset_sys.eq(self.do_res_sync.o), ] self.sync += [ If( do_reset_sys, reset_stretch.eq(31), self.gsr.eq(0), ).Elif( reset_stretch != 0, self.gsr.eq(1), reset_stretch.eq(reset_stretch - 1), ).Else( reset_stretch.eq(0), self.gsr.eq(0), ) ]
def __init__(self, pll, tx_pads, rx_pads, dual=0, channel=0, data_width=20, tx_polarity=0, rx_polarity=0): assert (data_width == 20) assert dual in [0, 1] assert channel in [0, 1] self.dual = dual self.channel = channel # TX controls self.tx_enable = Signal(reset=1) self.tx_ready = Signal() self.tx_inhibit = Signal() # FIXME self.tx_produce_square_wave = Signal() self.tx_produce_pattern = Signal() self.tx_pattern = Signal(data_width) self.tx_prbs_config = Signal(2) self.tx_idle = Signal() self.tx_data = Signal(20) self.ldr_tx_data = Signal() # RX controls self.rx_enable = Signal(reset=1) self.rx_ready = Signal() self.rx_prbs_config = Signal(2) self.rx_prbs_errors = Signal(32) self.rx_idle = Signal() self.rx_data = Signal(20) # Loopback self.loopback = Signal( ) # FIXME: reconfigure lb_ctl to 0b0001 but does not seem enough # # # self.nwords = nwords = data_width // 10 # Transceiver direct clock outputs (useful to specify clock constraints) self.txoutclk = Signal() self.rxoutclk = Signal() self.tx_clk_freq = pll.config["linerate"] / data_width self.rx_clk_freq = pll.config["linerate"] / data_width # Internal signals ------------------------------------------------------------------------- rx_los = Signal() self.rx_lol = Signal() rx_lsm = Signal() rx_bus = Signal(24) tx_lol = Signal() tx_bus = Signal(24) # Control/Status CDC ----------------------------------------------------------------------- self.specials += [ MultiReg(rx_los, self.rx_idle, "sys"), ] # Clocking --------------------------------------------------------------------------------- self.clock_domains.cd_tx = ClockDomain() self.comb += self.cd_tx.clk.eq(self.txoutclk) self.specials += AsyncResetSynchronizer(self.cd_tx, ResetSignal("sys")) self.specials += MultiReg(~self.cd_tx.rst, self.tx_ready) self.clock_domains.cd_rx = ClockDomain() self.comb += self.cd_rx.clk.eq(self.rxoutclk) self.specials += AsyncResetSynchronizer(self.cd_rx, ResetSignal("sys")) self.specials += MultiReg(~self.cd_rx.rst, self.rx_ready) # DCU instance ----------------------------------------------------------------------------- self.serdes_params = dict( # ECP5's DCU parameters/signals/instance have been documented by whitequark as part of # Yumewatari project: https://github.com/whitequark/Yumewatari # Copyright (C) 2018 [email protected] # DCU ---------------------------------------------------------------------------------- # DCU — power management p_D_MACROPDB="0b1", p_D_IB_PWDNB="0b1", # undocumented (required for RX) p_D_TXPLL_PWDNB="0b1", p_D_XGE_MODE="0b0", i_D_FFC_MACROPDB=1, # DCU — reset i_D_FFC_MACRO_RST=ResetSignal("sys"), i_D_FFC_DUAL_RST=ResetSignal("sys"), # DCU — clocking i_D_REFCLKI=pll.refclk, o_D_FFS_PLOL=tx_lol, p_D_REFCK_MODE={ 25: "0b100", 20: "0b000", 16: "0b010", 10: "0b001", 8: "0b011" }[pll.config["mult"]], p_D_TX_MAX_RATE="2", # 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 p_D_CDR_LOL_SET="0b00", # After LOL do nothing # DCU — unknown 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="0b10", p_D_SETICONST_AUX="0b01", p_D_SETIRPOLY_CH="0b10", p_D_SETICONST_CH="0b10", p_D_SETPLLRC="0d1", p_D_SYNC_LOCAL_EN="0b1", p_D_RG_EN="0b0", p_D_RG_SET="0b00", p_D_REQ_ISET="0b001", p_D_PD_ISET="0b00", # 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", # Selects User Configured mode p_CHX_ENC_BYPASS="******", # Bypass 8b10b encoder p_CHX_DEC_BYPASS="******", # Bypass 8b10b encoder # 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, i_CHX_FFC_LANE_RX_RST=~self.rx_enable, # CHX RX — input i_CHX_HDINP=rx_pads.p, i_CHX_HDINN=rx_pads.n, p_CHX_REQ_EN="0b0", # Enable equalizer p_CHX_RX_RATE_SEL="0d10", # 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" }["50-ohms"], p_CHX_RXIN_CM="0b11", # CMFB (wizard value used) p_CHX_RXTERM_CM="0b11", # RX Input (wizard value used) p_CHX_RCV_DCC_EN="0b1", # Receiver DC coupling enable # CHX RX — clocking i_CHX_RX_REFCLK=pll.refclk, o_CHX_FF_RX_PCLK=self.rxoutclk, i_CHX_FF_RXI_CLK=ClockSignal("rx"), p_CHX_CDR_MAX_RATE="2", # 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_DCO_FACQ_RST 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="0b0", p_CHX_RX_LOS_EN="0b0", p_CHX_RX_LOS_LVL="0b100", # Lattice "TBD" (wizard value used) p_CHX_RX_LOS_CEQ="0b11", # Lattice "TBD" (wizard value used) # CHX RX — loss of lock o_CHX_FFS_RLOL=self.rx_lol, # CHx_RXLSM? CHx_RXWA? p_CHX_LSM_DISABLE="0b1", # CHX RX — link state machine i_CHX_FFC_SIGNAL_DETECT=0, o_CHX_FFS_LS_SYNC_STATUS=rx_lsm, p_CHX_ENABLE_CG_ALIGN="0b0", p_CHX_UDF_COMMA_MASK="0x0ff", # compare the 8 lsbs p_CHX_UDF_COMMA_A="0b0000000011", # K28.1, K28.5 and K28.7 p_CHX_UDF_COMMA_B="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(rx_bus.nbits)}, # 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, i_CHX_FFC_LANE_TX_RST=~self.tx_enable, # CHX TX — output o_CHX_HDOUTP=tx_pads.p, o_CHX_HDOUTN=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" }["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=self.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(tx_bus.nbits)}, # CHX TX LowDataRate data input i_CHX_FFC_LDR_CORE2TX_EN=1, p_CH1_LDR_CORE2TX_SEL="0b1", i_CHX_LDR_CORE2TX=self.ldr_tx_data, ) # SCI Reconfiguration ---------------------------------------------------------------------- sci_reconfig = SerDesECP5SCIReconfig(self) self.submodules.sci_reconfig = sci_reconfig # TX Datapath ------------------------------------------------------------------------------ self.comb += [ tx_bus[0:10].eq(self.tx_data[0:10]), tx_bus[12:22].eq(self.tx_data[10:20]), ] # RX Datapath ------------------------------------------------------------------------------ self.comb += [ self.rx_data[0:10].eq(rx_bus[0:10]), self.rx_data[10:20].eq(rx_bus[12:22]), ] self.analyzer_signals = [ tx_lol, self.tx_enable, self.tx_inhibit, self.tx_ready, self.rx_enable, self.rx_ready, self.rx_data, self.rx_lol, rx_los, sci_reconfig.fc2dco_floop, ]
def __init__(self, pll, pads, mode="master"): self.tx_data = Signal(32) self.rx_data = Signal(32) self.tx_idle = Signal() self.tx_comma = Signal() self.rx_idle = Signal() self.rx_comma = Signal() self.rx_bitslip_value = Signal(6) self.rx_delay_rst = Signal() self.rx_delay_inc = Signal() self.rx_delay_ce = Signal() self.rx_delay_en_vtc = Signal() # # # self.submodules.encoder = ClockDomainsRenamer("serwb_serdes")( Encoder(4, True)) self.decoders = [ClockDomainsRenamer("serwb_serdes")( Decoder(True)) for _ in range(4)] self.submodules += self.decoders # clocking: # In master mode: # - linerate/10 pll refclk provided by user # - linerate/10 slave refclk generated on clk_pads # In Slave mode: # - linerate/10 pll refclk provided by clk_pads self.clock_domains.cd_serwb_serdes = ClockDomain() self.clock_domains.cd_serwb_serdes_5x = ClockDomain() self.clock_domains.cd_serwb_serdes_20x = ClockDomain(reset_less=True) self.comb += [ self.cd_serwb_serdes.clk.eq(pll.serwb_serdes_clk), self.cd_serwb_serdes_5x.clk.eq(pll.serwb_serdes_5x_clk), self.cd_serwb_serdes_20x.clk.eq(pll.serwb_serdes_20x_clk) ] self.specials += AsyncResetSynchronizer(self.cd_serwb_serdes, ~pll.lock) self.comb += self.cd_serwb_serdes_5x.rst.eq(self.cd_serwb_serdes.rst) # control/status cdc tx_idle = Signal() tx_comma = Signal() rx_idle = Signal() rx_comma = Signal() rx_bitslip_value = Signal(6) rx_delay_rst = Signal() rx_delay_inc = Signal() rx_delay_en_vtc = Signal() rx_delay_ce = Signal() self.specials += [ MultiReg(self.tx_idle, tx_idle, "serwb_serdes"), MultiReg(self.tx_comma, tx_comma, "serwb_serdes"), MultiReg(rx_idle, self.rx_idle, "sys"), MultiReg(rx_comma, self.rx_comma, "sys"), MultiReg(self.rx_bitslip_value, rx_bitslip_value, "serwb_serdes"), MultiReg(self.rx_delay_inc, rx_delay_inc, "serwb_serdes_5x"), MultiReg(self.rx_delay_en_vtc, rx_delay_en_vtc, "serwb_serdes_5x") ] self.submodules.do_rx_delay_rst = PulseSynchronizer("sys", "serwb_serdes_5x") self.comb += [ rx_delay_rst.eq(self.do_rx_delay_rst.o), self.do_rx_delay_rst.i.eq(self.rx_delay_rst) ] self.submodules.do_rx_delay_ce = PulseSynchronizer("sys", "serwb_serdes_5x") self.comb += [ rx_delay_ce.eq(self.do_rx_delay_ce.o), self.do_rx_delay_ce.i.eq(self.rx_delay_ce) ] # tx clock (linerate/10) if mode == "master": self.submodules.tx_clk_gearbox = Gearbox(40, "serwb_serdes", 8, "serwb_serdes_5x") self.comb += self.tx_clk_gearbox.i.eq((0b1111100000 << 30) | (0b1111100000 << 20) | (0b1111100000 << 10) | (0b1111100000 << 0)) clk_o = Signal() self.specials += [ Instance("OSERDESE3", p_DATA_WIDTH=8, p_INIT=0, p_IS_CLK_INVERTED=0, p_IS_CLKDIV_INVERTED=0, p_IS_RST_INVERTED=0, o_OQ=clk_o, i_RST=ResetSignal("serwb_serdes"), i_CLK=ClockSignal("serwb_serdes_20x"), i_CLKDIV=ClockSignal("serwb_serdes_5x"), i_D=self.tx_clk_gearbox.o ), Instance("OBUFDS", i_I=clk_o, o_O=pads.clk_p, o_OB=pads.clk_n ) ] # tx datapath # tx_data -> encoders -> gearbox -> serdes self.submodules.tx_gearbox = Gearbox(40, "serwb_serdes", 8, "serwb_serdes_5x") self.comb += [ If(tx_comma, self.encoder.k[0].eq(1), self.encoder.d[0].eq(0xbc) ).Else( self.encoder.d[0].eq(self.tx_data[0:8]), self.encoder.d[1].eq(self.tx_data[8:16]), self.encoder.d[2].eq(self.tx_data[16:24]), self.encoder.d[3].eq(self.tx_data[24:32]) ) ] self.sync.serwb_serdes += \ If(tx_idle, self.tx_gearbox.i.eq(0) ).Else( self.tx_gearbox.i.eq(Cat(*[self.encoder.output[i] for i in range(4)])) ) serdes_o = Signal() self.specials += [ Instance("OSERDESE3", p_DATA_WIDTH=8, p_INIT=0, p_IS_CLK_INVERTED=0, p_IS_CLKDIV_INVERTED=0, p_IS_RST_INVERTED=0, o_OQ=serdes_o, i_RST=ResetSignal("serwb_serdes"), i_CLK=ClockSignal("serwb_serdes_20x"), i_CLKDIV=ClockSignal("serwb_serdes_5x"), i_D=self.tx_gearbox.o ), Instance("OBUFDS", i_I=serdes_o, o_O=pads.tx_p, o_OB=pads.tx_n ) ] # rx clock use_bufr = True if mode == "slave": clk_i = Signal() clk_i_bufg = Signal() self.specials += [ Instance("IBUFDS", i_I=pads.clk_p, i_IB=pads.clk_n, o_O=clk_i ) ] if use_bufr: clk_i_bufr = Signal() self.specials += [ Instance("BUFR", i_I=clk_i, o_O=clk_i_bufr), Instance("BUFG", i_I=clk_i_bufr, o_O=clk_i_bufg) ] else: self.specials += Instance("BUFG", i_I=clk_i, o_O=clk_i_bufg) self.comb += pll.refclk.eq(clk_i_bufg) # rx datapath # serdes -> gearbox -> bitslip -> decoders -> rx_data self.submodules.rx_gearbox = Gearbox(8, "serwb_serdes_5x", 40, "serwb_serdes") self.submodules.rx_bitslip = ClockDomainsRenamer("serwb_serdes")(BitSlip(40)) serdes_i_nodelay = Signal() self.specials += [ Instance("IBUFDS_DIFF_OUT", i_I=pads.rx_p, i_IB=pads.rx_n, o_O=serdes_i_nodelay ) ] serdes_i_delayed = Signal() serdes_q = Signal(8) self.specials += [ Instance("IDELAYE3", p_CASCADE="NONE", p_UPDATE_MODE="ASYNC", p_REFCLK_FREQUENCY=200.0, p_IS_CLK_INVERTED=0, p_IS_RST_INVERTED=0, p_DELAY_FORMAT="COUNT", p_DELAY_SRC="IDATAIN", p_DELAY_TYPE="VARIABLE", p_DELAY_VALUE=0, i_CLK=ClockSignal("serwb_serdes_5x"), i_RST=rx_delay_rst, i_LOAD=0, i_INC=rx_delay_inc, i_EN_VTC=rx_delay_en_vtc, i_CE=rx_delay_ce, i_IDATAIN=serdes_i_nodelay, o_DATAOUT=serdes_i_delayed ), Instance("ISERDESE3", p_DATA_WIDTH=8, i_D=serdes_i_delayed, i_RST=ResetSignal("serwb_serdes"), i_FIFO_RD_CLK=0, i_FIFO_RD_EN=0, i_CLK=ClockSignal("serwb_serdes_20x"), i_CLK_B=~ClockSignal("serwb_serdes_20x"), i_CLKDIV=ClockSignal("serwb_serdes_5x"), o_Q=serdes_q ) ] self.comb += [ self.rx_gearbox.i.eq(serdes_q), self.rx_bitslip.value.eq(rx_bitslip_value), self.rx_bitslip.i.eq(self.rx_gearbox.o), self.decoders[0].input.eq(self.rx_bitslip.o[0:10]), self.decoders[1].input.eq(self.rx_bitslip.o[10:20]), self.decoders[2].input.eq(self.rx_bitslip.o[20:30]), self.decoders[3].input.eq(self.rx_bitslip.o[30:40]), self.rx_data.eq(Cat(*[self.decoders[i].d for i in range(4)])), rx_idle.eq(self.rx_bitslip.o == 0), rx_comma.eq(((self.decoders[0].d == 0xbc) & (self.decoders[0].k == 1)) & ((self.decoders[1].d == 0x00) & (self.decoders[1].k == 0)) & ((self.decoders[2].d == 0x00) & (self.decoders[2].k == 0)) & ((self.decoders[3].d == 0x00) & (self.decoders[3].k == 0))) ]
def __init__(self, dram_port): ashift, awidth = get_ashift_awidth(dram_port) self.reset = CSR() self.start = CSR() self.done = CSRStatus() self.base = CSRStorage(awidth) self.length = CSRStorage(awidth) self.random = CSRStorage() self.ticks = CSRStatus(32) self.errors = CSRStatus(32) # # # clock_domain = dram_port.clock_domain core = _LiteDRAMBISTChecker(dram_port) core = ClockDomainsRenamer(clock_domain)(core) self.submodules += core if clock_domain != "sys": reset_sync = PulseSynchronizer("sys", clock_domain) start_sync = PulseSynchronizer("sys", clock_domain) self.submodules += reset_sync, start_sync self.comb += [ reset_sync.i.eq(self.reset.re), core.reset.eq(reset_sync.o), start_sync.i.eq(self.start.re), core.start.eq(start_sync.o) ] done_sync = BusSynchronizer(1, clock_domain, "sys") self.submodules += done_sync self.comb += [ done_sync.i.eq(core.done), self.done.status.eq(done_sync.o) ] base_sync = BusSynchronizer(awidth, "sys", clock_domain) length_sync = BusSynchronizer(awidth, "sys", clock_domain) self.submodules += base_sync, length_sync self.comb += [ base_sync.i.eq(self.base.storage), core.base.eq(base_sync.o), length_sync.i.eq(self.length.storage), core.length.eq(length_sync.o) ] self.specials += MultiReg(self.random.storage, core.random, clock_domain) ticks_sync = BusSynchronizer(32, clock_domain, "sys") self.submodules += ticks_sync self.comb += [ ticks_sync.i.eq(core.ticks), self.ticks.status.eq(ticks_sync.o) ] errors_sync = BusSynchronizer(32, clock_domain, "sys") self.submodules += errors_sync self.comb += [ errors_sync.i.eq(core.errors), self.errors.status.eq(errors_sync.o) ] else: self.comb += [ core.reset.eq(self.reset.re), core.start.eq(self.start.re), self.done.status.eq(core.done), core.base.eq(self.base.storage), core.length.eq(self.length.storage), core.random.eq(self.random.storage), self.ticks.status.eq(core.ticks), self.errors.status.eq(core.errors) ]