Ejemplo n.º 1
0
Archivo: stream.py Proyecto: hplp/litex
    def __init__(self,
                 layout,
                 cd_from="sys",
                 cd_to="sys",
                 depth=None,
                 with_common_rst=False):
        self.sink = Endpoint(layout)
        self.source = Endpoint(layout)

        # # #

        # Same Clk Domains.
        if cd_from == cd_to:
            # No adaptation.
            self.comb += self.sink.connect(self.source)
        # Different Clk Domains.
        else:
            if with_common_rst:
                # Create intermediate Clk Domains and generate a common Rst.
                _cd_id = id(
                    self
                )  # FIXME: Improve, used to allow build with anonymous modules.
                _cd_rst = Signal()
                _cd_from = ClockDomain(f"from{_cd_id}")
                _cd_to = ClockDomain(f"to{_cd_id}")
                self.clock_domains += _cd_from, _cd_to
                self.comb += [
                    _cd_from.clk.eq(ClockSignal(cd_from)),
                    _cd_to.clk.eq(ClockSignal(cd_to)),
                    _cd_rst.eq(ResetSignal(cd_from) | ResetSignal(cd_to))
                ]
                cd_from = _cd_from.name
                cd_to = _cd_to.name
                # Use common Rst on both Clk Domains (through AsyncResetSynchronizer).
                self.specials += [
                    AsyncResetSynchronizer(_cd_from, _cd_rst),
                    AsyncResetSynchronizer(_cd_to, _cd_rst),
                ]

            # Add Asynchronous FIFO
            cdc = AsyncFIFO(layout, depth)
            cdc = ClockDomainsRenamer({"write": cd_from, "read": cd_to})(cdc)
            self.submodules += cdc

            # Sink -> AsyncFIFO -> Source.
            self.comb += self.sink.connect(cdc.sink)
            self.comb += cdc.source.connect(self.source)
Ejemplo n.º 2
0
Archivo: jtag.py Proyecto: hplp/litex
    def __init__(self,
                 jtag=None,
                 device=None,
                 data_width=8,
                 clock_domain="sys",
                 chain=1,
                 platform=None):
        """JTAG PHY

        Provides a simple JTAG to LiteX stream module to easily stream data to/from the FPGA
        over JTAG.

        Wire format: data_width + 2 bits, LSB first.

        Host to Target:
          - TX ready : bit 0
          - RX data: : bit 1 to data_width
          - RX valid : bit data_width + 1

        Target to Host:
          - RX ready : bit 0
          - TX data  : bit 1 to data_width
          - TX valid : bit data_width + 1
        """
        self.sink = sink = stream.Endpoint([("data", data_width)])
        self.source = source = stream.Endpoint([("data", data_width)])

        # # #

        # JTAG TAP ---------------------------------------------------------------------------------
        if jtag is None:
            jtag_tdi_delay = 0
            # Xilinx.
            if XilinxJTAG.get_primitive(device) is not None:
                jtag = XilinxJTAG(primitive=XilinxJTAG.get_primitive(device),
                                  chain=chain)
                jtag_tdi_delay = XilinxJTAG.get_tdi_delay(device)
            # Lattice.
            elif device[:5] == "LFE5U":
                jtag = ECP5JTAG()
            # Altera/Intel.
            elif AlteraJTAG.get_primitive(device) is not None:
                platform.add_reserved_jtag_decls()
                jtag = AlteraJTAG(primitive=AlteraJTAG.get_primitive(device),
                                  pads=platform.get_reserved_jtag_pads())
            else:
                print(device)
                raise NotImplementedError
            self.submodules.jtag = jtag

        # JTAG clock domain ------------------------------------------------------------------------
        self.clock_domains.cd_jtag = ClockDomain()
        self.comb += ClockSignal("jtag").eq(jtag.tck)
        self.specials += AsyncResetSynchronizer(self.cd_jtag,
                                                ResetSignal(clock_domain))

        # JTAG clock domain crossing ---------------------------------------------------------------
        if clock_domain != "jtag":
            tx_cdc = stream.AsyncFIFO([("data", data_width)], 4)
            tx_cdc = ClockDomainsRenamer({
                "write": clock_domain,
                "read": "jtag"
            })(tx_cdc)
            rx_cdc = stream.AsyncFIFO([("data", data_width)], 4)
            rx_cdc = ClockDomainsRenamer({
                "write": "jtag",
                "read": clock_domain
            })(rx_cdc)
            self.submodules.tx_cdc = tx_cdc
            self.submodules.rx_cdc = rx_cdc
            self.comb += [
                sink.connect(tx_cdc.sink),
                rx_cdc.source.connect(source)
            ]
            sink, source = tx_cdc.source, rx_cdc.sink

        # JTAG TDI/TDO Delay -----------------------------------------------------------------------
        jtag_tdi = jtag.tdi
        jtag_tdo = jtag.tdo
        if jtag_tdi_delay:
            jtag_tdi_sr = Signal(data_width + 2 - jtag_tdi_delay)
            self.sync.jtag += If(jtag.shift,
                                 jtag_tdi_sr.eq(Cat(jtag.tdi, jtag_tdi_sr)))
            jtag_tdi = jtag_tdi_sr[-1]

        # JTAG Xfer FSM ----------------------------------------------------------------------------
        valid = Signal()
        ready = Signal()
        data = Signal(data_width)
        count = Signal(max=data_width)

        fsm = FSM(reset_state="XFER-READY")
        fsm = ClockDomainsRenamer("jtag")(fsm)
        fsm = ResetInserter()(fsm)
        self.submodules += fsm
        self.comb += fsm.reset.eq(jtag.reset | jtag.capture)
        fsm.act(
            "XFER-READY", jtag_tdo.eq(ready),
            If(jtag.shift, sink.ready.eq(jtag_tdi),
               NextValue(valid, sink.valid), NextValue(data, sink.data),
               NextValue(count, 0), NextState("XFER-DATA")))
        fsm.act(
            "XFER-DATA", jtag_tdo.eq(data),
            If(jtag.shift, NextValue(count, count + 1),
               NextValue(data, Cat(data[1:], jtag_tdi)),
               If(count == (data_width - 1), NextState("XFER-VALID"))))
        fsm.act(
            "XFER-VALID", jtag_tdo.eq(valid),
            If(jtag.shift, source.valid.eq(jtag_tdi), source.data.eq(data),
               NextValue(ready, source.ready), NextState("XFER-READY")))
Ejemplo n.º 3
0
    def __init__(self,
                 jtag=None,
                 device=None,
                 data_width=8,
                 clock_domain="sys"):
        """JTAG PHY

        Provides a simple JTAG to LiteX stream module to easily stream data to/from the FPGA
        over JTAG.

        Wire format: data_width + 2 bits, LSB first.

        Host to Target:
          - TX ready : bit 0
          - RX data: : bit 1 to data_width
          - RX valid : bit data_width + 1

        Target to Host:
          - RX ready : bit 0
          - TX data  : bit 1 to data_width
          - TX valid : bit data_width + 1
        """
        self.sink = sink = stream.Endpoint([("data", data_width)])
        self.source = source = stream.Endpoint([("data", data_width)])

        # # #

        valid = Signal()
        data = Signal(data_width)
        count = Signal(max=data_width)

        # JTAG TAP ---------------------------------------------------------------------------------
        if jtag is None:
            if device[:3] == "xc6":
                jtag = S6JTAG()
            elif device[:3] == "xc7":
                jtag = S7JTAG()
            elif device[:4] in ["xcku", "xcvu"]:
                jtag = USJTAG()
            else:
                raise NotImplementedError
            self.submodules += jtag

        # JTAG clock domain ------------------------------------------------------------------------
        self.clock_domains.cd_jtag = ClockDomain()
        self.comb += ClockSignal("jtag").eq(jtag.tck)
        self.specials += AsyncResetSynchronizer(self.cd_jtag,
                                                ResetSignal("sys"))

        # JTAG clock domain crossing ---------------------------------------------------------------
        if clock_domain != "jtag":
            tx_cdc = stream.AsyncFIFO([("data", data_width)], 4)
            tx_cdc = ClockDomainsRenamer({
                "write": clock_domain,
                "read": "jtag"
            })(tx_cdc)
            rx_cdc = stream.AsyncFIFO([("data", data_width)], 4)
            rx_cdc = ClockDomainsRenamer({
                "write": "jtag",
                "read": clock_domain
            })(rx_cdc)
            self.submodules += tx_cdc, rx_cdc
            self.comb += [
                sink.connect(tx_cdc.sink),
                rx_cdc.source.connect(source)
            ]
            sink, source = tx_cdc.source, rx_cdc.sink

        # JTAG Xfer FSM ----------------------------------------------------------------------------
        fsm = FSM(reset_state="XFER-READY")
        fsm = ClockDomainsRenamer("jtag")(fsm)
        fsm = ResetInserter()(fsm)
        self.submodules += fsm
        self.comb += fsm.reset.eq(jtag.reset | jtag.capture)
        fsm.act(
            "XFER-READY", jtag.tdo.eq(source.ready),
            If(jtag.shift, sink.ready.eq(jtag.tdi),
               NextValue(valid, sink.valid), NextValue(data, sink.data),
               NextValue(count, 0), NextState("XFER-DATA")))
        fsm.act(
            "XFER-DATA", jtag.tdo.eq(data),
            If(jtag.shift, NextValue(count, count + 1),
               NextValue(data, Cat(data[1:], jtag.tdi)),
               If(count == (data_width - 1), NextState("XFER-VALID"))))
        fsm.act(
            "XFER-VALID", jtag.tdo.eq(valid),
            If(jtag.shift, source.valid.eq(jtag.tdi), NextState("XFER-READY")))
        self.comb += source.data.eq(data)
Ejemplo n.º 4
0
    def __init__(self,
                 S=8,
                 D=2,
                 MIRROR_BITS=False,
                 CLK_EDGE_ALIGNED=True,
                 BITSLIPS=0):
        """
        S = serialization factor (bits per frame)
        D = number of parallel lanes

        MIRROR_BITS = False
            first bit of the serial stream clocked in ends up in the
            LSB of data_outs

        CLK_EDGE_ALIGNED = True
            Clock and data lanes are in-phase

        CLK_EDGE_ALIGNED = False
            Clock is 90 deg shifted to data
            (clock transitions in middle of data-eye)

        BITSLIPS:
            Number of bitslips to trigger after initialization

        See Sp6Common.py for input output ports
        """

        ###

        # Add data lanes and control signals
        Sp6Common.__init__(self, S, D, MIRROR_BITS, BITSLIPS,
                           {"p_DATA_RATE": "DDR"}, {"p_DATA_RATE": "DDR"})

        self.specials += AsyncResetSynchronizer(self.sample, self.reset)

        # -------------------------------------
        #  Iserdes DDR clocking scheme
        # -------------------------------------
        dcoo_p = Signal()
        dcoo_n = Signal()
        self.specials += Instance("IBUFDS_DIFF_OUT",
                                  i_I=self.dco_p,
                                  i_IB=self.dco_n,
                                  o_O=dcoo_p,
                                  o_OB=dcoo_n)
        dco_del_p = Signal()
        dco_del_n = Signal()
        # Add 90 degree phase shift to clock if it is not edge aligned
        self.idelay_default["p_IDELAY_TYPE"] = \
            "FIXED" if CLK_EDGE_ALIGNED else "VARIABLE_FROM_HALF_MAX"
        self.specials += Instance("IODELAY2",
                                  p_SERDES_MODE="MASTER",
                                  i_IDATAIN=dcoo_p,
                                  i_CAL=self.idelay_cal_m,
                                  o_DATAOUT=dco_del_p,
                                  **self.idelay_default)
        self.specials += Instance("IODELAY2",
                                  p_SERDES_MODE="SLAVE",
                                  i_IDATAIN=dcoo_n,
                                  i_CAL=self.idelay_cal_s,
                                  o_DATAOUT=dco_del_n,
                                  **self.idelay_default)
        divclk = Signal()
        self.specials += Instance("BUFIO2_2CLK",
                                  p_DIVIDE=S,
                                  i_I=dco_del_p,
                                  i_IB=dco_del_n,
                                  o_IOCLK=self.ioclk_p,
                                  o_DIVCLK=divclk,
                                  o_SERDESSTROBE=self.serdesstrobe)
        self.specials += Instance("BUFG",
                                  i_I=divclk,
                                  o_O=ClockSignal("sample"))
        self.specials += Instance("BUFIO2",
                                  p_I_INVERT="FALSE",
                                  p_DIVIDE_BYPASS="******",
                                  p_USE_DOUBLER="FALSE",
                                  i_I=dco_del_n,
                                  o_IOCLK=self.ioclk_n)
Ejemplo n.º 5
0
    def __init__(self, platform):
        n_bits = 16
        n_frame = 14
        t_clk = 4.

        self.submodules.link = Link(
            clk=platform.request("eem0_p", 0),
            data=[platform.request("eem0_p", i + 1) for i in range(6)],
            platform=platform,
            t_clk=t_clk)

        cd_sys = ClockDomain("sys")
        self.clock_domains += cd_sys
        self.comb += [
            cd_sys.rst.eq(ResetSignal("word")),
            cd_sys.clk.eq(ClockSignal("word")),
        ]
        # platform.add_period_constraint(cd_sys.clk, t_clk*link.n_div)

        self.submodules.frame = Frame()
        self.comb += self.frame.word.eq(self.link.word)

        adr = Signal(4)
        cfg = Record([
            ("rst", 1),
            ("afe_pwr_n", 1),
            ("dac_clr", 1),
            ("clr_err", 1),
            ("led", 8),
            ("typ", 1),
            ("reserved", 7),
        ])
        cfg_comb = Record(cfg.layout)
        unlock = Signal(reset=1)

        # slow MISO lane, TBD
        sdo = Signal()
        self.specials += [
            Instance(
                "SB_IO",
                p_PIN_TYPE=C(0b010100, 6),  # output registered
                p_IO_STANDARD="SB_LVCMOS",
                i_OUTPUT_CLK=ClockSignal("word"),
                o_PACKAGE_PIN=platform.request("eem0_p", 7),
                i_D_OUT_0=sdo),  # falling SCK
            Instance(
                "SB_IO",
                p_PIN_TYPE=C(0b011100, 6),  # output registered inverted
                p_IO_STANDARD="SB_LVCMOS",
                i_OUTPUT_CLK=ClockSignal("word"),
                o_PACKAGE_PIN=platform.request("eem0_n", 7),
                i_D_OUT_0=sdo),  # falling SCK
        ]
        sr = Signal(n_frame, reset_less=True)
        # status register
        status = Signal((1 << len(adr)) * len(sr))
        status_ = Cat(
            C(0xfa, 8),  # ID
            platform.request("cbsel"),
            platform.request("sw"),
            platform.request("hw_rev"),
            C(0, 3),  # gw version
            unlock,
            self.link.delay,
            self.link.align_err,
            self.frame.crc_err,
            cfg.raw_bits())
        assert len(status_) <= len(status)

        self.comb += [
            cfg_comb.raw_bits().eq(self.frame.body),
            status.eq(status_),
            sdo.eq(sr[-1]),  # MSB first
            adr.eq(self.frame.body[len(cfg):]),
        ]
        have_crc_err = Signal()
        self.sync += [
            sr[1:].eq(sr),
            If(
                self.frame.stb,
                cfg.raw_bits().eq(cfg_comb.raw_bits()),
                # grab data from status register according to address
                sr.eq(
                    Array([
                        status[i * n_frame:(i + 1) * n_frame]
                        for i in range(1 << len(adr))
                    ])[adr]),
            ),
            If(
                cfg.clr_err,
                unlock.eq(0),
                self.frame.crc_err.eq(0),
            ),
            have_crc_err.eq(self.frame.crc_err != 0),
        ]
        have_align_err = Signal()
        self.sync.word += [
            If(
                cfg.clr_err,
                self.link.align_err.eq(0),
            ),
            have_align_err.eq(self.link.align_err != 0)
        ]

        # set up spi clock to 7*t_clk*3/4 = 21 ns
        cd_spi = ClockDomain("spi")
        self.clock_domains += cd_spi
        divr = 3 - 1
        divf = 4 - 1
        divq = 4
        t_out = t_clk * self.link.n_div * (divr + 1) / (divf + 1)  # *2**divq
        assert t_out >= 20., t_out
        assert t_out < t_clk * self.link.n_div
        platform.add_period_constraint(cd_spi.clk, t_out)
        # calculate the minimum delay between the 28 ns word clock
        # and the 21 ns SPI clock: 7ns, if this is large enough we don't need a
        # synchronizer (see below)
        print(
            "Check 'Max delay posedge sys_clk -> posedge spi_clk' <",
            min((i * t_out) % (t_clk * self.link.n_div)
                for i in range(1, (divf + 1) // gcd(divr + 1, divf + 1))))
        t_idle = n_frame * t_clk * self.link.n_div - (n_bits + 1) * t_out
        assert t_idle >= 0, t_idle
        t_vco = t_out / 2**divq
        assert .533 <= 1 / t_vco <= 1.066, 1 / t_vco

        locked = Signal()
        self.specials += [
            Instance(
                "SB_PLL40_CORE",
                p_FEEDBACK_PATH="DELAY",
                p_DIVR=divr,  # input
                p_DIVF=divf,  # feedback
                p_DIVQ=divq,  # vco
                p_FILTER_RANGE=1,
                p_PLLOUT_SELECT="GENCLK",
                p_ENABLE_ICEGATE=1,
                p_DELAY_ADJUSTMENT_MODE_FEEDBACK="DYNAMIC",
                p_FDA_FEEDBACK=0xf,
                p_DELAY_ADJUSTMENT_MODE_RELATIVE="DYNAMIC",
                p_FDA_RELATIVE=0xf,
                i_BYPASS=0,
                i_RESETB=~(cfg.rst | ResetSignal("word")),
                i_DYNAMICDELAY=Cat(self.link.delay, self.link.delay_relative),
                i_REFERENCECLK=ClockSignal("link"),
                i_LATCHINPUTVALUE=0,
                o_LOCK=locked,
                o_PLLOUTGLOBAL=cd_spi.clk,
                # o_PLLOUTCORE=,
            ),
            AsyncResetSynchronizer(cd_spi, ~locked),
        ]

        self.submodules.int0 = ClockDomainsRenamer("spi")(Interpolator)(
            n_channels=16)
        self.submodules.int1 = ClockDomainsRenamer("spi")(Interpolator)(
            n_channels=16)

        # no cdc, assume timing is synchronous and comensurate such that
        # max data delay sys-spi < min sys-spi clock delay over all alignments
        body = Cat(
            self.int0.en_in,
            self.int1.en_in,
            self.int0.x,
            self.int1.x,
        )
        self.sync.spi += [  # this should be comb but the stb path is long
            self.int0.stb_in.eq(self.frame.stb),
            self.int1.stb_in.eq(self.frame.stb),
            body.eq(self.frame.body[-len(body):]),
            self.int0.typ.eq(cfg_comb.typ),
            self.int1.typ.eq(cfg_comb.typ),
        ]

        self.submodules.spi = MultiSPI(platform)
        assert len(body) == len(self.spi.data)
        assert len(cfg) + len(adr) + len(self.spi.data) == len(self.frame.body)

        body = Cat(
            self.int0.en_out,
            self.int1.en_out,
            self.int0.y,
            self.int1.y,
        )
        assert len(body) == len(self.spi.data)
        self.comb += [
            self.spi.stb.eq(self.int0.stb_out & self.int0.cic.ce),
            self.spi.data.eq(body),
            #    self.spi.data.eq(self.frame.body[-len(self.spi.data):]),
            #    self.spi.stb.eq(self.frame.stb),
        ]

        self.comb += [
            platform.request("dac_clr_n").eq(~cfg.dac_clr),
            platform.request("en_afe_pwr").eq(~cfg.afe_pwr_n),
            #platform.request("test_point", 2).eq(self.link.delay[0]),
            #platform.request("test_point", 3).eq(self.link.delay[1]),
            #platform.request("test_point", 3).eq(self.spi.busy),
            #platform.request("test_point", 4).eq(self.frame.stb),
            Cat(platform.request("user_led", i) for i in range(9)).eq(
                Cat(
                    cfg.led,
                    ResetSignal("spi") | have_align_err | have_crc_err,  # RED
                )),
        ]
        self.specials += [
            Instance(
                "SB_IO",
                p_PIN_TYPE=C(0b010000, 6),  # output registered DDR
                p_IO_STANDARD="SB_LVCMOS",
                i_OUTPUT_CLK=ClockSignal("link"),
                #i_CLOCK_ENABLE=1,
                o_PACKAGE_PIN=platform.request("test_point", 0),
                i_D_OUT_0=1,
                i_D_OUT_1=0),
            Instance(
                "SB_IO",
                p_PIN_TYPE=C(0b010000, 6),  # output registered DDR
                p_IO_STANDARD="SB_LVCMOS",
                i_OUTPUT_CLK=ClockSignal("word"),
                #i_CLOCK_ENABLE=1,
                o_PACKAGE_PIN=platform.request("test_point", 1),
                i_D_OUT_0=1,
                i_D_OUT_1=0),
        ]
Ejemplo n.º 6
0
    def __init__(self, platform):
        clk48_raw = platform.request("clk48")
        clk12 = Signal()

        reset_delay = Signal(12, reset=4095)
        self.clock_domains.cd_por = ClockDomain()
        self.reset = Signal()

        self.clock_domains.cd_sys = ClockDomain()
        self.clock_domains.cd_usb_12 = ClockDomain()
        self.clock_domains.cd_usb_48 = ClockDomain()

        platform.add_period_constraint(self.cd_usb_48.clk, 1e9 / 48e6)
        platform.add_period_constraint(self.cd_sys.clk, 1e9 / 12e6)
        platform.add_period_constraint(self.cd_usb_12.clk, 1e9 / 12e6)
        platform.add_period_constraint(clk48_raw, 1e9 / 48e6)

        # POR reset logic- POR generated from sys clk, POR logic feeds sys clk
        # reset.
        self.comb += [
            self.cd_por.clk.eq(self.cd_sys.clk),
            self.cd_sys.rst.eq(reset_delay != 0),
            self.cd_usb_12.rst.eq(reset_delay != 0),
        ]

        # POR reset logic- POR generated from sys clk, POR logic feeds sys clk
        # reset.
        self.comb += [
            self.cd_usb_48.rst.eq(reset_delay != 0),
        ]

        self.comb += self.cd_usb_48.clk.eq(clk48_raw)

        self.specials += Instance(
            "SB_PLL40_CORE",
            # Parameters
            p_DIVR=0,
            p_DIVF=15,
            p_DIVQ=5,
            p_FILTER_RANGE=1,
            p_FEEDBACK_PATH="SIMPLE",
            p_DELAY_ADJUSTMENT_MODE_FEEDBACK="FIXED",
            p_FDA_FEEDBACK=15,
            p_DELAY_ADJUSTMENT_MODE_RELATIVE="FIXED",
            p_FDA_RELATIVE=0,
            p_SHIFTREG_DIV_MODE=1,
            p_PLLOUT_SELECT="GENCLK_HALF",
            p_ENABLE_ICEGATE=0,
            # IO
            i_REFERENCECLK=clk48_raw,
            o_PLLOUTCORE=clk12,
            # o_PLLOUTGLOBAL = clk12,
            #i_EXTFEEDBACK,
            #i_DYNAMICDELAY,
            #o_LOCK,
            i_BYPASS=0,
            i_RESETB=1,
            #i_LATCHINPUTVALUE,
            #o_SDO,
            #i_SDI,
        )

        self.comb += self.cd_sys.clk.eq(clk12)
        self.comb += self.cd_usb_12.clk.eq(clk12)

        self.sync.por += \
            If(reset_delay != 0,
                reset_delay.eq(reset_delay - 1)
            )
        self.specials += AsyncResetSynchronizer(self.cd_por, self.reset)
Ejemplo n.º 7
0
    def __init__(
        self, S=8, D=2, M=2, MIRROR_BITS=False, CLK_EDGE_ALIGNED=True,
        BITSLIPS=0, DCO_PERIOD=2.0, **kwargs
    ):
        """
        Clock and data lanes must be in-phase (edge aligned)

        S = serialization factor (bits per frame)
        D = number of parallel lanes

        M = clock multiplier (bits per DCO period)
            1 for sdr, 2 for ddr, higher for a divided clock

        BITSLIPS:
            Number of bitslips to trigger after initialization

        DCO_PERIOD = period of dco_p/n in [ns] for PLL_ADV

        MIRROR_BITS = False:
            first bit of the serial stream clocked in ends up in the
            LSB of data_outs

        See Sp6Common.py for input output ports
        """
        # Data recovered from the clock lane for frame alignment (in case of a divided clock)
        self.clk_data_out = Signal(8)

        # High when sample clock is stable
        self.pll_locked = Signal()

        ###

        # Add data lanes and control signals
        Sp6Common.__init__(
            self, S, D, MIRROR_BITS, BITSLIPS,
            {"p_DATA_RATE": "SDR"},
            {"p_DATA_RATE": "SDR"}
        )

        # Sync resets
        self.specials += AsyncResetSynchronizer(self.sample, ~self.pll_locked)

        # -------------------------------------
        #  Iserdes PLL clocking scheme
        # -------------------------------------
        # ... with clk-data recovery
        self.dco = Signal()
        self.specials += DifferentialInput(self.dco_p, self.dco_n, self.dco)
        dco_m = Signal()
        dco_s = Signal()
        self.specials += Instance(
            "IODELAY2",
            p_SERDES_MODE="MASTER",
            p_IDELAY_TYPE="VARIABLE_FROM_HALF_MAX" if CLK_EDGE_ALIGNED else "FIXED",
            i_IDATAIN=self.dco,
            i_CAL=self.idelay_cal_m,
            i_CE=0,
            i_INC=0,
            o_DATAOUT=dco_m,
            **self.idelay_default
        )
        self.specials += Instance(
            "IODELAY2",
            p_SERDES_MODE="SLAVE",
            p_IDELAY_TYPE="FIXED" if CLK_EDGE_ALIGNED else "VARIABLE_FROM_HALF_MAX",
            i_IDATAIN=self.dco,
            # We don't want periodic calibration here, would lead to clock glitches
            i_CAL=self.idelay_cal_m,
            i_CE=0,
            i_INC=0,
            o_DATAOUT=dco_s,
            **self.idelay_default
        )
        cascade_up = Signal()
        cascade_down = Signal()
        dfb = Signal()
        cfb0 = Signal()
        self.specials += Instance(
            "ISERDES2",
            p_SERDES_MODE="MASTER",
            i_D=dco_m,
            i_SHIFTIN=cascade_up,
            o_Q4=self.clk_data_out[7],
            o_Q3=self.clk_data_out[6],
            o_Q2=self.clk_data_out[5],
            o_Q1=self.clk_data_out[4],
            o_SHIFTOUT=cascade_down,
            **self.iserdes_default
        )
        # Using the delay matched BUFIO2 and BUFIO2FB,
        # the PLL will generate a ioclock which is phase-
        # aligned with the dco clock at the input of the
        # ISERDES
        self.specials += Instance(
            "ISERDES2",
            p_SERDES_MODE="SLAVE",
            i_D=dco_s,
            i_SHIFTIN=cascade_down,
            o_Q4=self.clk_data_out[3],
            o_Q3=self.clk_data_out[2],
            o_Q2=self.clk_data_out[1],
            o_Q1=self.clk_data_out[0],
            o_SHIFTOUT=cascade_up,
            o_DFB=dfb,
            o_CFB0=cfb0,
            **self.iserdes_default
        )
        pll_clkin = Signal()
        pll_clkfbin = Signal()
        self.specials += Instance(
            "BUFIO2",
            p_DIVIDE_BYPASS="******",
            i_I=dfb,
            o_DIVCLK=pll_clkin
        )
        self.specials += Instance(
            "BUFIO2FB",
            p_DIVIDE_BYPASS="******",
            i_I=cfb0,
            o_O=pll_clkfbin
        )
        pll_clk0 = Signal()
        pll_clk2 = Signal()
        self.specials += Instance(
            "PLL_ADV",
            name="PLL_IOCLOCK",
            p_BANDWIDTH="OPTIMIZED",
            p_SIM_DEVICE="SPARTAN6",
            p_CLKIN1_PERIOD=DCO_PERIOD,
            p_CLKIN2_PERIOD=DCO_PERIOD,
            p_DIVCLK_DIVIDE=1,
            p_CLKFBOUT_MULT=M,
            p_CLKFBOUT_PHASE=0.0,
            p_CLKOUT0_DIVIDE=1,
            p_CLKOUT2_DIVIDE=S,
            p_CLKOUT0_DUTY_CYCLE=0.5,
            p_CLKOUT2_DUTY_CYCLE=0.5,
            p_CLKOUT0_PHASE=0.0,
            p_CLKOUT2_PHASE=0.0,
            p_COMPENSATION="SOURCE_SYNCHRONOUS",
            # p_COMPENSATION="INTERNAL",
            p_CLK_FEEDBACK="CLKOUT0",

            i_RST=self.reset,
            i_CLKINSEL=1,
            i_CLKIN1=pll_clkin,
            i_CLKIN2=0,
            i_CLKFBIN=pll_clkfbin,
            # o_CLKFBOUT=clkfbout, i_CLKFBIN=clkfbout,

            i_DADDR=Signal(5),
            i_DI=Signal(16),
            i_DEN=0,
            i_DWE=0,
            i_DCLK=0,

            o_CLKOUT0=pll_clk0,
            o_CLKOUT2=pll_clk2,
            o_LOCKED=self.pll_locked
        )
        self.specials += Instance(
            "BUFPLL",
            p_DIVIDE=S,
            i_PLLIN=pll_clk0,
            i_GCLK=ClockSignal("sample"),
            i_LOCKED=self.pll_locked,
            o_IOCLK=self.ioclk_p,
            o_SERDESSTROBE=self.serdesstrobe
        )
        self.specials += Instance(
            "BUFG",
            i_I=pll_clk2,
            o_O=ClockSignal("sample")
        )
        # ioclk_n is not needed, hardwire it to zero
        self.comb += self.ioclk_n.eq(0)
Ejemplo n.º 8
0
    def __init__(self, clk, data, platform, t_clk=4.):
        self.n_div = 7
        n_lanes = len(data)
        # output word
        self.word = Signal(n_lanes * self.n_div)
        # clock alignment error counter
        self.align_err = Signal(8)
        # currently tuned sampling delay
        self.delay = Signal(4, reset=0x8)
        # clock aligned, word available
        self.stb = Signal()

        # link clocking
        cd_link = ClockDomain("link", reset_less=True)  # t_clk*7, 3:4 duty
        platform.add_period_constraint(cd_link.clk, t_clk * self.n_div)
        self.clock_domains += cd_link

        cd_word = ClockDomain("word")  # t_clk*7, 1:1 duty
        platform.add_period_constraint(cd_word.clk, t_clk * self.n_div)
        self.clock_domains += cd_word

        cd_sr = ClockDomain("sr", reset_less=True)  # t_clk
        self.clock_domains += cd_sr
        divr = 1 - 1
        divf = 1 - 1
        divq = 2
        t_out = t_clk * (divr + 1) / (divf + 1)
        assert t_out == t_clk
        platform.add_period_constraint(cd_sr.clk, t_out)
        t_vco = t_out / 2**divq
        assert .533 <= 1 / t_vco <= 1.066, 1 / t_vco

        # input clock PLL
        locked = Signal()

        self.delay_relative = Signal(4)
        self.specials += [
            Instance(
                "SB_PLL40_2F_CORE",
                p_FEEDBACK_PATH=
                "PHASE_AND_DELAY",  # out = in/r*f, vco = out*2**q
                p_DIVR=divr,  # input
                p_DIVF=divf,  # feedback
                p_DIVQ=divq,  # vco
                p_FILTER_RANGE=3,
                p_SHIFTREG_DIV_MODE=int(self.n_div == 7),  # div-by-7
                p_DELAY_ADJUSTMENT_MODE_FEEDBACK="DYNAMIC",
                p_FDA_FEEDBACK=0xf,
                p_DELAY_ADJUSTMENT_MODE_RELATIVE="DYNAMIC",
                p_FDA_RELATIVE=0xf,
                p_PLLOUT_SELECT_PORTA="SHIFTREG_0deg",
                p_PLLOUT_SELECT_PORTB="GENCLK",
                p_ENABLE_ICEGATE_PORTA=0,
                p_ENABLE_ICEGATE_PORTB=0,
                i_BYPASS=0,
                i_RESETB=1,
                i_DYNAMICDELAY=Cat(self.delay, self.delay_relative),
                i_REFERENCECLK=ClockSignal("link"),
                i_LATCHINPUTVALUE=0,
                o_LOCK=locked,
                o_PLLOUTGLOBALA=cd_word.clk,
                o_PLLOUTGLOBALB=cd_sr.clk,
                # o_PLLOUTCOREA=,
                # o_PLLOUTCOREB=,
            ),
            AsyncResetSynchronizer(cd_word, ~locked),
        ]

        # input PIO registers
        # 2 bits per lane, data lanes plus one clock lane
        self.submodules.sr = SlipSR(n_lanes=2 * (n_lanes + 1),
                                    n_div=self.n_div)

        helper = Signal(n_lanes + 1)
        self.specials += [
            # buffer it to the `link` domain and DDR-sample it with the `sr` clock
            Instance(
                "SB_GB_IO",
                p_PIN_TYPE=0b000000,  # no output, i registered
                p_IO_STANDARD="SB_LVDS_INPUT",
                i_INPUT_CLK=cd_sr.clk,
                o_D_IN_0=self.sr.data[0],  # rising
                o_D_IN_1=helper[0],  # falling
                o_GLOBAL_BUFFER_OUTPUT=cd_link.clk,
                i_PACKAGE_PIN=clk,
            ),
            # help relax timings a bit to meet the t_clk/2 delay from
            # negedge sr to posedge sr: easier met in fabric than right after
            # PIO
            Instance(
                "SB_DFFN",
                i_D=helper[0],
                i_C=cd_sr.clk,
                o_Q=self.sr.data[n_lanes + 1],
            ),
        ] + [
            [
                Instance(
                    "SB_IO",
                    p_PIN_TYPE=0b000000,
                    p_IO_STANDARD="SB_LVDS_INPUT",
                    i_INPUT_CLK=cd_sr.clk,
                    o_D_IN_0=self.sr.data[i + 1],  # rising
                    o_D_IN_1=helper[i + 1],  # falling
                    i_PACKAGE_PIN=data[i],
                ),
                # relax timing closure on falling edge samples,
                # same as clk lane above
                Instance(
                    "SB_DFFN",
                    i_D=helper[i + 1],
                    i_C=cd_sr.clk,
                    o_Q=self.sr.data[n_lanes + i + 2],
                )
            ] for i in range(n_lanes)
        ]

        # clock aligned
        slip_good = Signal()
        mean_edges = 1 << 6
        sigma_edges = 6 * mean_edges**.5
        assert mean_edges > sigma_edges  # need SNR
        # number of edges seen, one word headroom
        edges = Signal(max=2 * mean_edges + len(self.sr.word))
        # number of edge samples matching the later non-edge value
        # i.e. number of edge samples biased to late sampling
        edges_late = Signal.like(edges)
        # timer to block slips and delay adjustments while others are
        # pending/pll settling
        settle = Signal(max=64, reset=63)
        settle_done = Signal()

        n = len(self.sr.data)
        lanes = [Signal(self.n_div) for i in range(n)]

        self.comb += [
            # transpose link word for delay checking
            Cat(lanes).eq(Cat(self.sr.word[i::n] for i in range(n))),
            self.word.eq(
                Cat(self.sr.word[i * n + 1:i * n + 1 + n_lanes]
                    for i in range(self.n_div))),
            settle_done.eq(settle == 0),
            self.stb.eq(slip_good),
            # be robust, just look for rising clock edge between 1 and 2
            slip_good.eq(lanes[0][1:3] == 0b01),
        ]
        self.sync.word += [
            # delay adjustment:
            # (indices are bit indices, youngest first,
            # reversed from clock cycle numbers)
            # With the timing helper DFFNs on the edge samples (see
            # above):
            # edge sample i is half a cycle older than ref sample i
            # and half a cycle younger than ref sample i + 1
            edges.eq(edges + sum(ref[i + 1] ^ ref[i]
                                 for ref in lanes[:n_lanes + 1]
                                 for i in range(self.n_div - 1))),
            edges_late.eq(edges_late + sum(
                (ref[i + 1] ^ ref[i]) & ~(edge[i] ^ ref[i])
                for ref, edge in zip(lanes[:n_lanes + 1], lanes[n_lanes + 1:])
                for i in range(self.n_div - 1))),
            self.sr.slip_req.eq(0),
            If(
                ~settle_done,
                settle.eq(settle - 1),
            ).Else(
                # slip adjustment and delay adjustment can happen in parallel
                # share a hold-off timer for convenience
                If(
                    edges >= 2 * mean_edges,
                    edges.eq(0),
                    edges_late.eq(0),
                    # many late samples
                    If(
                        (edges_late >= int(mean_edges + sigma_edges)) &
                        # saturate delay
                        (self.delay != 0xf),
                        # if the edge sample matches the sample after the edge
                        # then sampling is late:
                        # increment the feedback delay for earlier sampling
                        self.delay.eq(self.delay + 1),
                        settle.eq(settle.reset),
                    ),
                    # few late samples
                    If(
                        (edges_late < int(mean_edges - sigma_edges)) &
                        (self.delay != 0),
                        self.delay.eq(self.delay - 1),
                        settle.eq(settle.reset),
                    ),
                ),
                If(
                    ~slip_good,
                    self.sr.slip_req.eq(1),
                    self.align_err.eq(self.align_err + 1),
                    settle.eq(settle.reset),
                ))
        ]