Ejemplo n.º 1
0
 def __init__(self, platform, eem0, eem1):
     spip, spin = [[
         platform.request("{}_qspi_{}".format(eem, pol), 0)
         for eem in (eem0, eem1)
     ] for pol in "pn"]
     ioup = [
         platform.request("{}_io_update".format(eem), 0)
         for eem in (eem0, eem1)
     ]
     self.cs_n = Signal()
     self.clk = Signal()
     self.io_update = Signal()
     self.specials += [(DifferentialOutput(~self.cs_n, spip[i].cs,
                                           spin[i].cs),
                        DifferentialOutput(self.clk, spip[i].clk,
                                           spin[i].clk),
                        DifferentialOutput(self.io_update, ioup[i].p,
                                           ioup[i].n)) for i in range(2)]
     for i in range(8):
         mosi = Signal()
         setattr(self, "mosi{}".format(i), mosi)
         self.specials += [
             DifferentialOutput(
                 mosi, getattr(spip[i // 4], "mosi{}".format(i % 4)),
                 getattr(spin[i // 4], "mosi{}".format(i % 4)))
         ]
Ejemplo n.º 2
0
    def __init__(self, platform, eem):
        self.sck_en = Signal()
        self.cnv = Signal()
        self.clkout = Signal()

        spip = platform.request("{}_adc_spi_p".format(eem))
        spin = platform.request("{}_adc_spi_n".format(eem))
        cnv = platform.request("{}_cnv".format(eem))
        sdr = platform.request("{}_sdr".format(eem))
        dp = platform.request("{}_adc_data_p".format(eem))
        dn = platform.request("{}_adc_data_n".format(eem))

        clkout_se = Signal()
        clkout_inv = Signal()
        sck = Signal()

        self.specials += [
            DifferentialOutput(self.cnv, cnv.p, cnv.n),
            DifferentialOutput(1, sdr.p, sdr.n),
            DDROutput(self.sck_en, 0, sck, ClockSignal("sys")),
            DifferentialOutput(sck, spip.clk, spin.clk),
            DifferentialInput(dp.clkout, dn.clkout, clkout_se),
            # FIXME (hardware): CLKOUT is inverted
            # (Sampler v2.0, v2.1) out on rising, in on falling
            Instance("BUFR", i_I=clkout_se, o_O=clkout_inv)
        ]

        self.comb += self.clkout.eq(~clkout_inv)

        # define clock here before the input delays below
        self.clkout_p = dp.clkout  # available for false paths
        platform.add_platform_command(
            "create_clock -name {clk} -period 8 [get_nets {clk}]",
            clk=dp.clkout)
        # platform.add_period_constraint(sampler_pads.clkout_p, 8.)
        for i in "abcd":
            sdo = Signal()
            setattr(self, "sdo{}".format(i), sdo)
            if i != "a":
                # FIXME (hardware): sdob, sdoc, sdod are inverted
                # (Sampler v2.0, v2.1)
                sdo, sdo_inv = Signal(), sdo
                self.comb += sdo_inv.eq(~sdo)
            sdop = getattr(dp, "sdo{}".format(i))
            sdon = getattr(dn, "sdo{}".format(i))
            self.specials += [
                DifferentialInput(sdop, sdon, sdo),
            ]
            # -0+1.5 hold (t_HSDO_SDR), -0.5+0.5 skew
            platform.add_platform_command(
                "set_input_delay -clock {clk} -max 2 [get_ports {port}]\n"
                "set_input_delay -clock {clk} -min -0.5 [get_ports {port}]",
                clk=dp.clkout,
                port=sdop)
Ejemplo n.º 3
0
    def __init__(self, platform, eem):
        self.sdi = Signal()
        self.srclk = Signal()
        self.rclk = Signal()

        pgia_spip = platform.request("{}_pgia_spi_p".format(eem))
        pgia_spin = platform.request("{}_pgia_spi_n".format(eem))

        self.specials += [
            DifferentialOutput(self.sdi, pgia_spip.mosi, pgia_spin.mosi),
            DifferentialOutput(self.srclk, pgia_spip.clk, pgia_spin.clk),
            DifferentialOutput(self.rclk, pgia_spip.cs_n, pgia_spin.cs_n),
        ]
Ejemplo n.º 4
0
    def __init__(self, pad, pad_n=None, ftw_width=24, dci=False):
        self.rtlink = rtlink.Interface(rtlink.OInterface(ftw_width))

        # # #

        pad_o = Signal()
        if pad_n is None:
            self.comb += pad.eq(pad_o)
        else:
            self.specials += DifferentialOutput(pad_o, pad, pad_n)
        ftw = Signal(ftw_width)
        acc = Signal(ftw_width)
        self.sync.rio += If(self.rtlink.o.stb, ftw.eq(self.rtlink.o.data))
        self.sync.rio_phy += [
            acc.eq(acc + ftw),
            # rtlink takes precedence over regular acc increments
            If(
                self.rtlink.o.stb,
                If(
                    self.rtlink.o.data != 0,
                    # known phase on frequency write: at rising edge
                    acc.eq(2**(ftw_width - 1))).Else(
                        # set output to 0 on stop
                        acc.eq(0))),
            pad_o.eq(acc[-1])
        ]
Ejemplo n.º 5
0
    def add_std(cls,
                target,
                eem,
                eem_aux,
                ttl_out_cls,
                sync_gen_cls=None,
                iostandard="LVDS_25"):
        cls.add_extension(target, eem, eem_aux, iostandard=iostandard)

        phy = spi2.SPIMaster(
            target.platform.request("urukul{}_spi_p".format(eem)),
            target.platform.request("urukul{}_spi_n".format(eem)))
        target.submodules += phy
        target.rtio_channels.append(rtio.Channel.from_phy(phy, ififo_depth=4))

        pads = target.platform.request(
            "urukul{}_dds_reset_sync_in".format(eem))
        pad = Signal(reset=0)
        target.specials += DifferentialOutput(pad, pads.p, pads.n)
        if sync_gen_cls is not None:  # AD9910 variant and SYNC_IN from EEM
            phy = sync_gen_cls(pad, ftw_width=4)
            target.submodules += phy
            target.rtio_channels.append(rtio.Channel.from_phy(phy))

        pads = target.platform.request("urukul{}_io_update".format(eem))
        phy = ttl_out_cls(pads.p, pads.n)
        target.submodules += phy
        target.rtio_channels.append(rtio.Channel.from_phy(phy))
        if eem_aux is not None:
            for signal in "sw0 sw1 sw2 sw3".split():
                pads = target.platform.request("urukul{}_{}".format(
                    eem, signal))
                phy = ttl_out_cls(pads.p, pads.n)
                target.submodules += phy
                target.rtio_channels.append(rtio.Channel.from_phy(phy))
Ejemplo n.º 6
0
 def register_jsync(self, jsync):
     self.jsync_registered = True
     if isinstance(jsync, Signal):
         self.comb += jsync.eq(self.jsync)
     elif isinstance(jsync, Record):
         self.specials += DifferentialOutput(self.jsync, jsync.p, jsync.n)
     else:
         raise ValueError
Ejemplo n.º 7
0
    def __init__(self, platform, eem):
        self.sck_en = Signal()
        self.cnv = Signal()
        self.clkout = Signal()

        spip = platform.request("{}_adc_spi_p".format(eem))
        spin = platform.request("{}_adc_spi_n".format(eem))
        cnv = platform.request("{}_cnv".format(eem))
        sdr = platform.request("{}_sdr".format(eem))
        dp = platform.request("{}_adc_data_p".format(eem))
        dn = platform.request("{}_adc_data_n".format(eem))

        clkout_se = Signal()
        sck = Signal()

        self.specials += [
                DifferentialOutput(self.cnv, cnv.p, cnv.n),
                DifferentialOutput(1, sdr.p, sdr.n),
                DDROutput(0, self.sck_en, sck, ClockSignal("rio_phy")),
                DifferentialOutput(sck, spip.clk, spin.clk),
                DifferentialInput(dp.clkout, dn.clkout, clkout_se),
                Instance("BUFR", i_I=clkout_se, o_O=self.clkout)
        ]

        # here to be early before the input delays below to have the clock
        # available
        self.clkout_p = dp.clkout  # availabel for false paths
        platform.add_platform_command(
                "create_clock -name {clk} -period 8 [get_nets {clk}]",
                clk=dp.clkout)
        # platform.add_period_constraint(sampler_pads.clkout_p, 8.)
        for i in "abcd":
            sdo = Signal()
            setattr(self, "sdo{}".format(i), sdo)
            sdop = getattr(dp, "sdo{}".format(i))
            sdon = getattr(dn, "sdo{}".format(i))
            self.specials += [
                DifferentialInput(sdop, sdon, sdo),
            ]
            # 8, -0+1.5 hold (t_HSDO_DDR), -0.5+0.5 skew
            platform.add_platform_command(
                "set_input_delay -clock {clk} "
                "-max 2 [get_ports {port}] -clock_fall\n"
                "set_input_delay -clock {clk} "
                "-min -0.5 [get_ports {port}] -clock_fall",
                clk=dp.clkout, port=sdop)
Ejemplo n.º 8
0
    def __init__(self, **kwargs):
        _StandaloneBase.__init__(self, **kwargs)

        self.config["SI5324_AS_SYNTHESIZER"] = None
        self.config["RTIO_FREQUENCY"] = "125.0"

        platform = self.platform
        platform.add_extension(_urukul("eem1", "eem0"))
        platform.add_extension(_dio("eem2"))
        platform.add_extension(_dio("eem3"))
        platform.add_extension(_dio("eem4"))
        platform.add_extension(_dio("eem5"))
        platform.add_extension(_dio("eem6"))

        # EEM clock fan-out from Si5324, not MMCX
        self.comb += platform.request("clk_sel").eq(1)

        # EEM2-6: TTL
        rtio_channels = []
        for i in range(40):
            eem_offset, port = divmod(i, 8)
            pads = platform.request("eem{}".format(2 + eem_offset), port)
            phy = ttl_serdes_7series.InOut_8X(pads.p, pads.n)
            self.submodules += phy
            rtio_channels.append(rtio.Channel.from_phy(phy))

        # EEM0, EEM1: Urukul
        phy = spi2.SPIMaster(self.platform.request("eem1_spi_p"),
                             self.platform.request("eem1_spi_n"))
        self.submodules += phy
        rtio_channels.append(rtio.Channel.from_phy(phy, ififo_depth=4))

        pads = platform.request("eem1_dds_reset")
        self.specials += DifferentialOutput(0, pads.p, pads.n)

        for signal in "io_update sw0 sw1 sw2 sw3".split():
            pads = platform.request("eem1_{}".format(signal))
            phy = ttl_serdes_7series.Output_8X(pads.p, pads.n)
            self.submodules += phy
            rtio_channels.append(rtio.Channel.from_phy(phy))

        for i in (1, 2):
            sfp_ctl = platform.request("sfp_ctl", i)
            phy = ttl_simple.Output(sfp_ctl.led)
            self.submodules += phy
            rtio_channels.append(rtio.Channel.from_phy(phy))

        self.config["HAS_RTIO_LOG"] = None
        self.config["RTIO_LOG_CHANNEL"] = len(rtio_channels)
        rtio_channels.append(rtio.LogChannel())

        self.add_rtio(rtio_channels)
Ejemplo n.º 9
0
 def _oserdes(self, data, pin_p, pin_n, clk="sys2", attr=set()):
     pin = Signal()
     self.specials += [
         Instance("OSERDESE2", attr=attr,
             p_DATA_RATE_OQ="DDR", p_DATA_RATE_TQ="BUF",
             p_DATA_WIDTH=4, p_TRISTATE_WIDTH=1,
             i_RST=ResetSignal(),
             i_CLK=ClockSignal(clk),
             i_CLKDIV=ClockSignal(),
             # LSB first, D1 is closest to Q
             i_D1=data[0], i_D2=data[1], i_D3=data[2], i_D4=data[3],
             i_TCE=1, i_OCE=1, i_T1=0,
             o_OQ=pin),
         DifferentialOutput(pin, pin_p, pin_n),
     ]
Ejemplo n.º 10
0
    def __init__(self, platform, eem):

        self.sdi = Signal()
        self.ldac = Signal()
        self.busy = Signal(reset=1)
        self.syncr = Signal(reset=1)
        self.rst = Signal(reset=1)
        self.clr = Signal(reset=1)
        self.sclk = Signal()

        spip = platform.request("{}_spi_p".format(eem))
        spin = platform.request("{}_spi_n".format(eem))
        ldacn = platform.request("{}_ldac_n".format(eem))
        busy = platform.request("{}_busy".format(eem))
        clrn = platform.request("{}_clr_n".format(eem))

        self.specials += [
            DifferentialOutput(self.ldac, ldacn.p, ldacn.n),
            DifferentialOutput(self.sdi, spip.mosi, spin.mosi),
            DifferentialOutput(self.sclk, spip.clk, spin.clk),
            DifferentialOutput(self.syncr, spip.cs_n, spin.cs_n),
            DifferentialOutput(self.clr, clrn.p, clrn.n),
            DifferentialInput(busy.p, busy.n, self.busy),
        ]
Ejemplo n.º 11
0
    def __init__(self, pins, pins_n):
        self.data = [Signal(2) for _ in range(2 + len(pins.mosi))]

        for d, pp, pn in zip(self.data,
                             [pins.clk] + list(pins.mosi),
                             [pins_n.clk] + list(pins_n.mosi)):
            ddr = Signal()
            self.specials += [
                # d1 closer to q
                DDROutput(d[1], d[0], ddr, ClockSignal("rio_phy")),
                DifferentialOutput(ddr, pp, pn),
            ]
        ddr = Signal()
        self.specials += [
            DifferentialInput(pins.miso, pins_n.miso, ddr),
            # q1 closer to d
            DDRInput(ddr, self.data[-1][0], self.data[-1][1],
                     ClockSignal("rio_phy")),
        ]
Ejemplo n.º 12
0
    def __init__(self, pad, pad_n=None):
        self.rtlink = rtlink.Interface(rtlink.OInterface(1))
        pad_o = Signal(reset_less=True)
        self.probes = [pad_o]
        override_en = Signal()
        override_o = Signal()
        self.overrides = [override_en, override_o]

        # # #

        pad_k = Signal()
        self.sync.rio_phy += [
            If(self.rtlink.o.stb, pad_k.eq(self.rtlink.o.data)),
            If(override_en, pad_o.eq(override_o)).Else(pad_o.eq(pad_k))
        ]
        if pad_n is None:
            self.comb += pad.eq(pad_o)
        else:
            self.specials += DifferentialOutput(pad_o, pad, pad_n)
Ejemplo n.º 13
0
 def __init__(self, p, sys_clk_freq, f_sample_tx=None):
     # ----------------------------
     #  Clock and Reset Generation
     # ----------------------------
     xtal_pads = p.request(p.default_clk_name)
     xtal = Signal()
     self.specials += DifferentialInput(xtal_pads.p, xtal_pads.n, xtal)
     xtal_f = 1e9 / p.default_clk_period
     rst = p.request("cpu_reset")
     cds = [("cd_sys", sys_clk_freq)]
     if f_sample_tx is not None:
         cds.append(("cd_sample_tx", f_sample_tx))
     for cd, f in cds:
         setattr(self.clock_domains, cd, ClockDomain(cd))
         pll = S6PLL(speedgrade=-3)
         self.comb += pll.reset.eq(rst)
         pll.register_clkin(xtal, xtal_f)
         pll.create_clkout(getattr(self, cd), f)
         self.submodules += pll
     if f_sample_tx is not None:
         # Provide a ENC clock signal on SMA_GPIO
         enc_out = Signal()
         self.specials += Instance(
             "ODDR2",
             o_Q=enc_out,
             i_C0=ClockSignal("sample_tx"),
             i_C1=~ClockSignal("sample_tx"),
             i_CE=1,
             i_D0=1,
             i_D1=0
         )
         p.add_extension(
             # Connect GPIO_SMA on SP605 with CLK input on 1525A
             [("ENC_CLK", 0,
                 Subsignal("p", Pins("SMA_GPIO:P")),
                 Subsignal("n", Pins("SMA_GPIO:N")),
                 # Note: the LTC eval board needs to be modded
                 # to accept a differential clock
                 IOStandard("LVDS_25")
             )]
         )
         gpio_pads = p.request("ENC_CLK")
         self.specials += DifferentialOutput(enc_out, gpio_pads.p, gpio_pads.n)
Ejemplo n.º 14
0
    def add_std(cls, target, eem, eem_aux, ttl_out_cls, iostandard="LVDS_25"):
        cls.add_extension(target, eem, eem_aux, iostandard=iostandard)

        phy = spi2.SPIMaster(
            target.platform.request("sampler{}_adc_spi_p".format(eem)),
            target.platform.request("sampler{}_adc_spi_n".format(eem)))
        target.submodules += phy
        target.rtio_channels.append(rtio.Channel.from_phy(phy, ififo_depth=4))
        phy = spi2.SPIMaster(
            target.platform.request("sampler{}_pgia_spi_p".format(eem)),
            target.platform.request("sampler{}_pgia_spi_n".format(eem)))
        target.submodules += phy

        target.rtio_channels.append(rtio.Channel.from_phy(phy, ififo_depth=4))
        pads = target.platform.request("sampler{}_cnv".format(eem))
        phy = ttl_out_cls(pads.p, pads.n)
        target.submodules += phy

        target.rtio_channels.append(rtio.Channel.from_phy(phy))
        sdr = target.platform.request("sampler{}_sdr".format(eem))
        target.specials += DifferentialOutput(1, sdr.p, sdr.n)
Ejemplo n.º 15
0
    def add_std(cls, target, eem, eem_aux, ttl_out_cls):
        cls.add_extension(target, eem, eem_aux)

        phy = spi2.SPIMaster(
            target.platform.request("urukul{}_spi_p".format(eem)),
            target.platform.request("urukul{}_spi_n".format(eem)))
        target.submodules += phy
        target.rtio_channels.append(rtio.Channel.from_phy(phy, ififo_depth=4))

        pads = target.platform.request("urukul{}_dds_reset".format(eem))
        target.specials += DifferentialOutput(0, pads.p, pads.n)

        pads = target.platform.request("urukul{}_io_update".format(eem))
        phy = ttl_out_cls(pads.p, pads.n)
        target.submodules += phy
        target.rtio_channels.append(rtio.Channel.from_phy(phy))
        if eem_aux is not None:
            for signal in "sw0 sw1 sw2 sw3".split():
                pads = target.platform.request("urukul{}_{}".format(
                    eem, signal))
                phy = ttl_out_cls(pads.p, pads.n)
                target.submodules += phy
                target.rtio_channels.append(rtio.Channel.from_phy(phy))
Ejemplo n.º 16
0
    def add_std(cls,
                target,
                eems_sampler,
                eems_urukul0,
                eems_urukul1,
                t_rtt=4,
                clk=1,
                shift=11,
                profile=5):
        """Add a 8-channel Sampler-Urukul Servo

        :param t_rtt: upper estimate for clock round-trip propagation time from
            ``sck`` at the FPGA to ``clkout`` at the FPGA, measured in RTIO
            coarse cycles (default: 4). This is the sum of the round-trip
            cabling delay and the 8 ns max propagation delay on Sampler (ADC
            and LVDS drivers). Increasing ``t_rtt`` increases servo latency.
            With all other parameters at their default values, ``t_rtt`` values
            above 4 also increase the servo period (reduce servo bandwidth).
        :param clk: DDS SPI clock cycle half-width in RTIO coarse cycles
            (default: 1)
        :param shift: fixed-point scaling factor for IIR coefficients
            (default: 11)
        :param profile: log2 of the number of profiles for each DDS channel
            (default: 5)
        """
        cls.add_extension(target,
                          *(eems_sampler + eems_urukul0 + eems_urukul1))
        eem_sampler = "sampler{}".format(eems_sampler[0])
        eem_urukul0 = "urukul{}".format(eems_urukul0[0])
        eem_urukul1 = "urukul{}".format(eems_urukul1[0])

        sampler_pads = servo_pads.SamplerPads(target.platform, eem_sampler)
        urukul_pads = servo_pads.UrukulPads(target.platform, eem_urukul0,
                                            eem_urukul1)
        # timings in units of RTIO coarse period
        adc_p = servo.ADCParams(
            width=16,
            channels=8,
            lanes=4,
            t_cnvh=4,
            # account for SCK DDR to CONV latency
            # difference (4 cycles measured)
            t_conv=57 - 4,
            t_rtt=t_rtt + 4)
        iir_p = servo.IIRWidths(state=25,
                                coeff=18,
                                adc=16,
                                asf=14,
                                word=16,
                                accu=48,
                                shift=shift,
                                channel=3,
                                profile=profile)
        dds_p = servo.DDSParams(width=8 + 32 + 16 + 16,
                                channels=adc_p.channels,
                                clk=clk)
        su = servo.Servo(sampler_pads, urukul_pads, adc_p, iir_p, dds_p)
        su = ClockDomainsRenamer("rio_phy")(su)
        target.submodules += sampler_pads, urukul_pads, su

        ctrls = [rtservo.RTServoCtrl(ctrl) for ctrl in su.iir.ctrl]
        target.submodules += ctrls
        target.rtio_channels.extend(
            rtio.Channel.from_phy(ctrl) for ctrl in ctrls)
        mem = rtservo.RTServoMem(iir_p, su)
        target.submodules += mem
        target.rtio_channels.append(rtio.Channel.from_phy(mem, ififo_depth=4))

        phy = spi2.SPIMaster(
            target.platform.request("{}_pgia_spi_p".format(eem_sampler)),
            target.platform.request("{}_pgia_spi_n".format(eem_sampler)))
        target.submodules += phy
        target.rtio_channels.append(rtio.Channel.from_phy(phy, ififo_depth=4))

        phy = spi2.SPIMaster(
            target.platform.request("{}_spi_p".format(eem_urukul0)),
            target.platform.request("{}_spi_n".format(eem_urukul0)))
        target.submodules += phy
        target.rtio_channels.append(rtio.Channel.from_phy(phy, ififo_depth=4))

        pads = target.platform.request("{}_dds_reset".format(eem_urukul0))
        target.specials += DifferentialOutput(0, pads.p, pads.n)

        for i, signal in enumerate("sw0 sw1 sw2 sw3".split()):
            pads = target.platform.request("{}_{}".format(eem_urukul0, signal))
            target.specials += DifferentialOutput(su.iir.ctrl[i].en_out,
                                                  pads.p, pads.n)

        phy = spi2.SPIMaster(
            target.platform.request("{}_spi_p".format(eem_urukul1)),
            target.platform.request("{}_spi_n".format(eem_urukul1)))
        target.submodules += phy
        target.rtio_channels.append(rtio.Channel.from_phy(phy, ififo_depth=4))

        pads = target.platform.request("{}_dds_reset".format(eem_urukul1))
        target.specials += DifferentialOutput(0, pads.p, pads.n)

        for i, signal in enumerate("sw0 sw1 sw2 sw3".split()):
            pads = target.platform.request("{}_{}".format(eem_urukul1, signal))
            target.specials += DifferentialOutput(su.iir.ctrl[i + 4].en_out,
                                                  pads.p, pads.n)
Ejemplo n.º 17
0
    def __init__(self, pins, pins_n):
        n_bits = 16  # bits per dac data word
        n_channels = 32  # channels per fastino
        n_div = 7  # bits per lane and word
        assert n_div == 7
        n_frame = 14  # word per frame
        n_lanes = len(pins.mosi)  # number of data lanes
        n_checksum = 12  # checksum bits
        n_addr = 4  # readback address bits
        n_word = n_lanes*n_div
        n_body = n_word*n_frame - (n_frame//2 + 1) - n_checksum

        # dac data words
        self.dacs = [Signal(n_bits) for i in range(n_channels)]
        # dac update enable
        self.enable = Signal(n_channels)
        # configuration word
        self.cfg = Signal(20)
        # readback data
        self.dat_r = Signal(n_frame//2*(1 << n_addr))
        # data load synchronization event
        self.stb = Signal()

        # # #

        # crc-12 telco
        self.submodules.crc = LiteEthMACCRCEngine(
            data_width=2*n_lanes, width=n_checksum, polynom=0x80f)

        addr = Signal(4)
        body_ = Cat(self.cfg, addr, self.enable, self.dacs)
        assert len(body_) == n_body
        body = Signal(n_body)
        self.comb += body.eq(body_)

        words_ = []
        j = 0
        for i in range(n_frame):  # iterate over words
            if i == 0:  # data and checksum
                k = n_word - n_checksum
            elif i == 1:  # marker
                words_.append(C(1))
                k = n_word - 1
            elif i < n_frame//2 + 2:  # marker
                words_.append(C(0))
                k = n_word - 1
            else:  # full word
                k = n_word
            # append corresponding frame body bits
            words_.append(body[j:j + k])
            j += k
        words_ = Cat(words_)
        assert len(words_) == n_frame*n_word - n_checksum
        words = Signal(len(words_))
        self.comb += words.eq(words_)

        clk = Signal(n_div, reset=0b1100011)
        clk_stb = Signal()
        i_frame = Signal(max=n_div*n_frame//2)  # DDR
        frame_stb = Signal()
        sr = [Signal(n_frame*n_div - n_checksum//n_lanes, reset_less=True)
                for i in range(n_lanes)]
        assert len(Cat(sr)) == len(words)
        # DDR bits for each register
        ddr_data = Cat([sri[-2] for sri in sr], [sri[-1] for sri in sr])
        self.comb += [
            # assert one cycle ahead
            clk_stb.eq(~clk[0] & clk[-1]),
            # double period because of DDR
            frame_stb.eq(i_frame == n_div*n_frame//2 - 1),

            # LiteETHMACCRCEngine takes data LSB first
            self.crc.data[::-1].eq(ddr_data),
            self.stb.eq(frame_stb & clk_stb),
        ]
        miso = Signal()
        miso_sr = Signal(n_frame, reset_less=True)
        self.sync.rio_phy += [
            # shift 7 bit clock pattern by two bits each DDR cycle
            clk.eq(Cat(clk[-2:], clk)),
            [sri[2:].eq(sri) for sri in sr],
            self.crc.last.eq(self.crc.next),
            If(clk[:2] == 0,  # TODO: tweak MISO sampling
                miso_sr.eq(Cat(miso, miso_sr)),
            ),
            If(~frame_stb,
                i_frame.eq(i_frame + 1),
            ),
            If(frame_stb & clk_stb,
                i_frame.eq(0),
                self.crc.last.eq(0),
                # transpose, load
                Cat(sr).eq(Cat(words[mm::n_lanes] for mm in range(n_lanes))),
                Array([self.dat_r[i*n_frame//2:(i + 1)*n_frame//2]
                    for i in range(1 << len(addr))])[addr].eq(miso_sr),
                addr.eq(addr + 1),
            ),
            If(i_frame == n_div*n_frame//2 - 2,
                # inject crc
                ddr_data.eq(self.crc.next),
            ),
        ]

        clk_ddr = Signal()
        miso0 = Signal()
        self.specials += [
            DDROutput(clk[-1], clk[-2], clk_ddr, ClockSignal("rio_phy")),
            DifferentialOutput(clk_ddr, pins.clk, pins_n.clk),
            DifferentialInput(pins.miso, pins_n.miso, miso0),
            MultiReg(miso0, miso, "rio_phy"),
        ]
        for sri, ddr, mp, mn in zip(
                sr, Signal(n_lanes), pins.mosi, pins_n.mosi):
            self.specials += [
                DDROutput(sri[-1], sri[-2], ddr, ClockSignal("rio_phy")),
                DifferentialOutput(ddr, mp, mn),
            ]
Ejemplo n.º 18
0
    def __init__(self, **kwargs):
        _StandaloneBase.__init__(self, **kwargs)

        self.config["SI5324_AS_SYNTHESIZER"] = None
        # self.config["SI5324_EXT_REF"] = None
        self.config["RTIO_FREQUENCY"] = "125.0"

        platform = self.platform
        platform.add_extension(_dio("eem0"))
        platform.add_extension(_dio("eem1"))
        platform.add_extension(_dio("eem2"))
        platform.add_extension(_novogorny("eem3"))
        platform.add_extension(_urukul("eem5", "eem4"))
        platform.add_extension(_urukul("eem6"))
        platform.add_extension(_zotino("eem7"))

        # EEM clock fan-out from Si5324, not MMCX
        try:
            self.comb += platform.request("clk_sel").eq(1)
        except ConstraintError:
            pass

        rtio_channels = []
        for i in range(24):
            eem, port = divmod(i, 8)
            pads = platform.request("eem{}".format(eem), port)
            if i < 4:
                cls = ttl_serdes_7series.InOut_8X
            else:
                cls = ttl_serdes_7series.Output_8X
            phy = cls(pads.p, pads.n)
            self.submodules += phy
            rtio_channels.append(rtio.Channel.from_phy(phy))

        # EEM3: Novogorny
        phy = spi2.SPIMaster(self.platform.request("eem3_spi_p"),
                self.platform.request("eem3_spi_n"))
        self.submodules += phy
        rtio_channels.append(rtio.Channel.from_phy(phy, ififo_depth=16))

        for signal in "conv".split():
            pads = platform.request("eem3_{}".format(signal))
            phy = ttl_serdes_7series.Output_8X(pads.p, pads.n)
            self.submodules += phy
            rtio_channels.append(rtio.Channel.from_phy(phy))

        # EEM5 + EEM4: Urukul
        phy = spi2.SPIMaster(self.platform.request("eem5_spi_p"),
                self.platform.request("eem5_spi_n"))
        self.submodules += phy
        rtio_channels.append(rtio.Channel.from_phy(phy, ififo_depth=4))

        pads = platform.request("eem5_dds_reset")
        self.specials += DifferentialOutput(0, pads.p, pads.n)

        for signal in "io_update sw0 sw1 sw2 sw3".split():
            pads = platform.request("eem5_{}".format(signal))
            phy = ttl_serdes_7series.Output_8X(pads.p, pads.n)
            self.submodules += phy
            rtio_channels.append(rtio.Channel.from_phy(phy))

        for i in (1, 2):
            sfp_ctl = platform.request("sfp_ctl", i)
            phy = ttl_simple.Output(sfp_ctl.led)
            self.submodules += phy
            rtio_channels.append(rtio.Channel.from_phy(phy))

        # EEM6: Urukul
        phy = spi2.SPIMaster(self.platform.request("eem6_spi_p"),
                self.platform.request("eem6_spi_n"))
        self.submodules += phy
        rtio_channels.append(rtio.Channel.from_phy(phy, ififo_depth=4))

        for signal in "io_update".split():
            pads = platform.request("eem6_{}".format(signal))
            phy = ttl_serdes_7series.Output_8X(pads.p, pads.n)
            self.submodules += phy
            rtio_channels.append(rtio.Channel.from_phy(phy))

        pads = platform.request("eem6_dds_reset")
        self.specials += DifferentialOutput(0, pads.p, pads.n)

        # EEM7: Zotino
        phy = spi2.SPIMaster(self.platform.request("eem7_spi_p"),
                self.platform.request("eem7_spi_n"))
        self.submodules += phy
        rtio_channels.append(rtio.Channel.from_phy(phy, ififo_depth=4))

        for signal in "ldac_n clr_n".split():
            pads = platform.request("eem7_{}".format(signal))
            phy = ttl_serdes_7series.Output_8X(pads.p, pads.n)
            self.submodules += phy
            rtio_channels.append(rtio.Channel.from_phy(phy))

        self.config["HAS_RTIO_LOG"] = None
        self.config["RTIO_LOG_CHANNEL"] = len(rtio_channels)
        rtio_channels.append(rtio.LogChannel())

        self.add_rtio(rtio_channels)
Ejemplo n.º 19
0
    def add_std(cls,
                target,
                eems_sampler,
                eems_urukul,
                t_rtt=4,
                clk=1,
                shift=11,
                profile=5,
                iostandard="LVDS_25"):
        """Add a 8-channel Sampler-Urukul Servo

        :param t_rtt: upper estimate for clock round-trip propagation time from
            ``sck`` at the FPGA to ``clkout`` at the FPGA, measured in RTIO
            coarse cycles (default: 4). This is the sum of the round-trip
            cabling delay and the 8 ns max propagation delay on Sampler (ADC
            and LVDS drivers). Increasing ``t_rtt`` increases servo latency.
            With all other parameters at their default values, ``t_rtt`` values
            above 4 also increase the servo period (reduce servo bandwidth).
        :param clk: DDS SPI clock cycle half-width in RTIO coarse cycles
            (default: 1)
        :param shift: fixed-point scaling factor for IIR coefficients
            (default: 11)
        :param profile: log2 of the number of profiles for each DDS channel
            (default: 5)
        """
        cls.add_extension(target,
                          *(eems_sampler + sum(eems_urukul, [])),
                          iostandard=iostandard)
        eem_sampler = "sampler{}".format(eems_sampler[0])
        eem_urukul = ["urukul{}".format(i[0]) for i in eems_urukul]

        sampler_pads = servo_pads.SamplerPads(target.platform, eem_sampler)
        urukul_pads = servo_pads.UrukulPads(target.platform, *eem_urukul)
        target.submodules += sampler_pads, urukul_pads
        # timings in units of RTIO coarse period
        adc_p = servo.ADCParams(
            width=16,
            channels=8,
            lanes=4,
            t_cnvh=4,
            # account for SCK DDR to CONV latency
            # difference (4 cycles measured)
            t_conv=57 - 4,
            t_rtt=t_rtt + 4)
        iir_p = servo.IIRWidths(state=25,
                                coeff=18,
                                adc=16,
                                asf=14,
                                word=16,
                                accu=48,
                                shift=shift,
                                channel=3,
                                profile=profile,
                                dly=8)
        dds_p = servo.DDSParams(width=8 + 32 + 16 + 16,
                                channels=adc_p.channels,
                                clk=clk)
        su = servo.Servo(sampler_pads, urukul_pads, adc_p, iir_p, dds_p)
        su = ClockDomainsRenamer("rio_phy")(su)
        # explicitly name the servo submodule to enable the migen namer to derive
        # a name for the adc return clock domain
        setattr(target.submodules, "suservo_eem{}".format(eems_sampler[0]), su)

        ctrls = [rtservo.RTServoCtrl(ctrl) for ctrl in su.iir.ctrl]
        target.submodules += ctrls
        target.rtio_channels.extend(
            rtio.Channel.from_phy(ctrl) for ctrl in ctrls)
        mem = rtservo.RTServoMem(iir_p, su)
        target.submodules += mem
        target.rtio_channels.append(rtio.Channel.from_phy(mem, ififo_depth=4))

        phy = spi2.SPIMaster(
            target.platform.request("{}_pgia_spi_p".format(eem_sampler)),
            target.platform.request("{}_pgia_spi_n".format(eem_sampler)))
        target.submodules += phy
        target.rtio_channels.append(rtio.Channel.from_phy(phy, ififo_depth=4))

        for i in range(2):
            if len(eem_urukul) > i:
                spi_p, spi_n = (target.platform.request("{}_spi_p".format(
                    eem_urukul[i])),
                                target.platform.request("{}_spi_n".format(
                                    eem_urukul[i])))
            else:  # create a dummy bus
                spi_p = Record([("clk", 1), ("cs_n", 1)])  # mosi, cs_n
                spi_n = None

            phy = spi2.SPIMaster(spi_p, spi_n)
            target.submodules += phy
            target.rtio_channels.append(
                rtio.Channel.from_phy(phy, ififo_depth=4))

        for j, eem_urukuli in enumerate(eem_urukul):
            pads = target.platform.request(
                "{}_dds_reset_sync_in".format(eem_urukuli))
            target.specials += DifferentialOutput(0, pads.p, pads.n)

            for i, signal in enumerate("sw0 sw1 sw2 sw3".split()):
                pads = target.platform.request("{}_{}".format(
                    eem_urukuli, signal))
                target.specials += DifferentialOutput(
                    su.iir.ctrl[j * 4 + i].en_out, pads.p, pads.n)
Ejemplo n.º 20
0
    def __init__(self, hw_rev=None, **kwargs):
        if hw_rev is None:
            hw_rev = "v1.1"
        _StandaloneBase.__init__(self, hw_rev=hw_rev, **kwargs)

        self.config["SI5324_AS_SYNTHESIZER"] = None
        # self.config["SI5324_EXT_REF"] = None
        self.config["RTIO_FREQUENCY"] = "125.0"
        if hw_rev == "v1.0":
            # EEM clock fan-out from Si5324, not MMCX
            self.comb += self.platform.request("clk_sel").eq(1)

        self.rtio_channels = []
        # EEM0, EEM1: DIO
        eem.DIO.add_std(self, 0,
            ttl_serdes_7series.InOut_8X, ttl_serdes_7series.Output_8X)
        eem.DIO.add_std(self, 1,
            ttl_serdes_7series.Output_8X, ttl_serdes_7series.Output_8X)

        # EEM3, EEM2: Sampler
        self.platform.add_extension(eem.Sampler.io(3, 2))
        sampler_pads = servo_pads.SamplerPads(self.platform, "sampler3")
        # EEM5, EEM4 and EEM7, EEM6: Urukul
        self.platform.add_extension(eem.Urukul.io_qspi(5, 4))
        self.platform.add_extension(eem.Urukul.io_qspi(7, 6))
        urukul_pads = servo_pads.UrukulPads(self.platform,
                "urukul5", "urukul7")
        adc_p = servo.ADCParams(width=16, channels=8, lanes=4, t_cnvh=4,
                # account for SCK pipeline latency
                t_conv=57 - 4, t_rtt=4 + 4)
        iir_p = servo.IIRWidths(state=25, coeff=18, adc=16, asf=14, word=16,
                accu=48, shift=11, channel=3, profile=5)
        dds_p = servo.DDSParams(width=8 + 32 + 16 + 16,
                channels=adc_p.channels, clk=1)
        su = servo.Servo(sampler_pads, urukul_pads, adc_p, iir_p, dds_p)
        su = ClockDomainsRenamer("rio_phy")(su)
        self.submodules += sampler_pads, urukul_pads, su

        ctrls = [rtservo.RTServoCtrl(ctrl) for ctrl in su.iir.ctrl]
        self.submodules += ctrls
        self.rtio_channels.extend(rtio.Channel.from_phy(ctrl) for ctrl in ctrls)
        mem = rtservo.RTServoMem(iir_p, su)
        self.submodules += mem
        self.rtio_channels.append(rtio.Channel.from_phy(mem, ififo_depth=4))

        # EEM3: Sampler
        phy = spi2.SPIMaster(self.platform.request("sampler3_pgia_spi_p"),
                self.platform.request("sampler3_pgia_spi_n"))
        self.submodules += phy
        self.rtio_channels.append(rtio.Channel.from_phy(phy, ififo_depth=4))

        # EEM5 + EEM4: Urukul
        phy = spi2.SPIMaster(self.platform.request("urukul5_spi_p"),
                self.platform.request("urukul5_spi_n"))
        self.submodules += phy
        self.rtio_channels.append(rtio.Channel.from_phy(phy, ififo_depth=4))

        pads = self.platform.request("urukul5_dds_reset")
        self.specials += DifferentialOutput(0, pads.p, pads.n)

        for i, signal in enumerate("sw0 sw1 sw2 sw3".split()):
            pads = self.platform.request("urukul5_{}".format(signal))
            self.specials += DifferentialOutput(
                    su.iir.ctrl[i].en_out,
                    pads.p, pads.n)

        # EEM7 + EEM6: Urukul
        phy = spi2.SPIMaster(self.platform.request("urukul7_spi_p"),
                self.platform.request("urukul7_spi_n"))
        self.submodules += phy
        self.rtio_channels.append(rtio.Channel.from_phy(phy, ififo_depth=4))

        pads = self.platform.request("urukul7_dds_reset")
        self.specials += DifferentialOutput(0, pads.p, pads.n)

        for i, signal in enumerate("sw0 sw1 sw2 sw3".split()):
            pads = self.platform.request("urukul7_{}".format(signal))
            self.specials += DifferentialOutput(
                    su.iir.ctrl[i + 4].en_out,
                    pads.p, pads.n)

        for i in (1, 2):
            sfp_ctl = self.platform.request("sfp_ctl", i)
            phy = ttl_simple.Output(sfp_ctl.led)
            self.submodules += phy
            self.rtio_channels.append(rtio.Channel.from_phy(phy))

        self.config["HAS_RTIO_LOG"] = None
        self.config["RTIO_LOG_CHANNEL"] = len(self.rtio_channels)
        self.rtio_channels.append(rtio.LogChannel())

        self.add_rtio(self.rtio_channels)

        self.platform.add_false_path_constraints(
            sampler_pads.clkout_p,
            self.rtio_crg.cd_rtio.clk)
        self.platform.add_false_path_constraints(
            sampler_pads.clkout_p,
            self.crg.cd_sys.clk)
Ejemplo n.º 21
0
    def __init__(self, hw_rev=None, **kwargs):
        if hw_rev is None:
            hw_rev = "v1.1"
        _StandaloneBase.__init__(self, hw_rev=hw_rev, **kwargs)

        self.config["SI5324_AS_SYNTHESIZER"] = None
        self.config["RTIO_FREQUENCY"] = "125.0"

        platform = self.platform
        # TODO: grabber on eem0->eemB eem1->eemA
        platform.add_extension(_urukul("eem3", "eem2"))
        platform.add_extension(_dio("eem4"))
        platform.add_extension(_zotino("eem5"))
        platform.add_extension(_zotino("eem6"))

        # EEM4: TTL
        rtio_channels = []
        for i in range(8):
            pads = platform.request("eem4", i)
            phy = ttl_serdes_7series.InOut_8X(pads.p, pads.n)
            self.submodules += phy
            rtio_channels.append(rtio.Channel.from_phy(phy))

        # EEM2, EEM3: Urukul
        phy = spi2.SPIMaster(self.platform.request("eem3_spi_p"),
                             self.platform.request("eem3_spi_n"))
        self.submodules += phy
        rtio_channels.append(rtio.Channel.from_phy(phy, ififo_depth=4))

        pads = platform.request("eem3_dds_reset")
        self.specials += DifferentialOutput(0, pads.p, pads.n)

        for signal in "io_update sw0 sw1 sw2 sw3".split():
            pads = platform.request("eem3_{}".format(signal))
            phy = ttl_serdes_7series.Output_8X(pads.p, pads.n)
            self.submodules += phy
            rtio_channels.append(rtio.Channel.from_phy(phy))

        # EEM5, EEM6: Zotino
        for i in (5, 6):
            phy = spi2.SPIMaster(
                self.platform.request("eem{}_spi_p".format(i)),
                self.platform.request("eem{}_spi_n".format(i)))
            self.submodules += phy
            rtio_channels.append(rtio.Channel.from_phy(phy, ififo_depth=4))

            for signal in "ldac_n clr_n".split():
                pads = platform.request("eem{}_{}".format(i, signal))
                phy = ttl_serdes_7series.Output_8X(pads.p, pads.n)
                self.submodules += phy
                rtio_channels.append(rtio.Channel.from_phy(phy))

        for i in (1, 2):
            sfp_ctl = platform.request("sfp_ctl", i)
            phy = ttl_simple.Output(sfp_ctl.led)
            self.submodules += phy
            rtio_channels.append(rtio.Channel.from_phy(phy))

        self.config["HAS_RTIO_LOG"] = None
        self.config["RTIO_LOG_CHANNEL"] = len(rtio_channels)
        rtio_channels.append(rtio.LogChannel())

        self.add_rtio(rtio_channels)
Ejemplo n.º 22
0
    def __init__(self, hw_rev=None, **kwargs):
        if hw_rev is None:
            hw_rev = "v1.1"
        _StandaloneBase.__init__(self, hw_rev=hw_rev, **kwargs)

        self.config["SI5324_AS_SYNTHESIZER"] = None
        # self.config["SI5324_EXT_REF"] = None
        self.config["RTIO_FREQUENCY"] = "125.0"

        platform = self.platform
        platform.add_extension(_dio("eem0"))
        platform.add_extension(_dio("eem1"))
        platform.add_extension(_sampler("eem3", "eem2"))
        platform.add_extension(_urukul_qspi("eem5", "eem4"))
        platform.add_extension(_urukul_qspi("eem7", "eem6"))

        try:
            # EEM clock fan-out from Si5324, not MMCX, only Kasli/v1.0
            self.comb += platform.request("clk_sel").eq(1)
        except ConstraintError:
            pass

        rtio_channels = []
        for i in range(16):
            eem, port = divmod(i, 8)
            pads = platform.request("eem{}".format(eem), port)
            if i < 4:
                cls = ttl_serdes_7series.InOut_8X
            else:
                cls = ttl_serdes_7series.Output_8X
            phy = cls(pads.p, pads.n)
            self.submodules += phy
            rtio_channels.append(rtio.Channel.from_phy(phy))

        # EEM3, EEM2: Sampler
        sampler_pads = servo_pads.SamplerPads(self.platform, "eem3")
        # EEM5, EEM4 and EEM7, EEM6: Urukul
        urukul_pads = servo_pads.UrukulPads(self.platform, "eem5", "eem7")
        adc_p = servo.ADCParams(
            width=16,
            channels=8,
            lanes=4,
            t_cnvh=4,
            # account for SCK pipeline latency
            t_conv=57 - 4,
            t_rtt=4 + 4)
        iir_p = servo.IIRWidths(state=25,
                                coeff=18,
                                adc=16,
                                asf=14,
                                word=16,
                                accu=48,
                                shift=11,
                                channel=3,
                                profile=5)
        dds_p = servo.DDSParams(width=8 + 32 + 16 + 16,
                                channels=adc_p.channels,
                                clk=1)
        su = servo.Servo(sampler_pads, urukul_pads, adc_p, iir_p, dds_p)
        su = ClockDomainsRenamer("rio_phy")(su)
        self.submodules += sampler_pads, urukul_pads, su

        ctrls = [rtservo.RTServoCtrl(ctrl) for ctrl in su.iir.ctrl]
        self.submodules += ctrls
        rtio_channels.extend(rtio.Channel.from_phy(ctrl) for ctrl in ctrls)
        mem = rtservo.RTServoMem(iir_p, su)
        self.submodules += mem
        rtio_channels.append(rtio.Channel.from_phy(mem, ififo_depth=4))

        # EEM3: Sampler
        phy = spi2.SPIMaster(self.platform.request("eem3_pgia_spi_p"),
                             self.platform.request("eem3_pgia_spi_n"))
        self.submodules += phy
        rtio_channels.append(rtio.Channel.from_phy(phy, ififo_depth=4))

        # EEM5 + EEM4: Urukul
        phy = spi2.SPIMaster(self.platform.request("eem5_spi_p"),
                             self.platform.request("eem5_spi_n"))
        self.submodules += phy
        rtio_channels.append(rtio.Channel.from_phy(phy, ififo_depth=4))

        pads = platform.request("eem5_dds_reset")
        self.specials += DifferentialOutput(0, pads.p, pads.n)

        for i, signal in enumerate("sw0 sw1 sw2 sw3".split()):
            pads = platform.request("eem5_{}".format(signal))
            self.specials += DifferentialOutput(su.iir.ctrl[i].en_out, pads.p,
                                                pads.n)

        # EEM7 + EEM6: Urukul
        phy = spi2.SPIMaster(self.platform.request("eem7_spi_p"),
                             self.platform.request("eem7_spi_n"))
        self.submodules += phy
        rtio_channels.append(rtio.Channel.from_phy(phy, ififo_depth=4))

        pads = platform.request("eem7_dds_reset")
        self.specials += DifferentialOutput(0, pads.p, pads.n)

        for i, signal in enumerate("sw0 sw1 sw2 sw3".split()):
            pads = platform.request("eem7_{}".format(signal))
            self.specials += DifferentialOutput(su.iir.ctrl[i + 4].en_out,
                                                pads.p, pads.n)

        for i in (1, 2):
            sfp_ctl = platform.request("sfp_ctl", i)
            phy = ttl_simple.Output(sfp_ctl.led)
            self.submodules += phy
            rtio_channels.append(rtio.Channel.from_phy(phy))

        self.config["HAS_RTIO_LOG"] = None
        self.config["RTIO_LOG_CHANNEL"] = len(rtio_channels)
        rtio_channels.append(rtio.LogChannel())

        self.add_rtio(rtio_channels)

        platform.add_false_path_constraints(sampler_pads.clkout_p,
                                            self.rtio_crg.cd_rtio.clk)
        platform.add_false_path_constraints(sampler_pads.clkout_p,
                                            self.crg.cd_sys.clk)
Ejemplo n.º 23
0
    def __init__(self, pads):
        self.pads = pads
        self.bus = bus = wishbone.Interface()

        # # #

        clk = Signal()
        clk_phase = Signal(2)
        cs = Signal()
        ca = Signal(48)
        sr = Signal(48)
        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

        # Drive rst_n, cs_n, clk from internal signals ---------------------------------------------
        if hasattr(pads, "rst_n"):
            self.comb += pads.rst_n.eq(1)
        self.comb += pads.cs_n[0].eq(~cs)
        assert len(pads.cs_n) <= 2
        if len(pads.cs_n) == 2:
            self.comb += pads.cs_n[1].eq(1)
        if hasattr(pads, "clk"):
            self.comb += pads.clk.eq(clk)
        else:
            self.specials += DifferentialOutput(clk, pads.clk_p, pads.clk_n)

        # Clock Generation (sys_clk/4) -------------------------------------------------------------
        self.sync += clk_phase.eq(clk_phase + 1)
        cases = {}
        cases[1] = clk.eq(cs)  # Set pads clk on 90° (if cs is set)
        cases[3] = clk.eq(0)  # Clear pads clk on 270°
        self.sync += Case(clk_phase, cases)

        # Data Shift Register (for write and read) -------------------------------------------------
        dqi = Signal(8)
        self.sync += dqi.eq(dq.i)  # Sample on 90° and 270°
        cases = {}
        cases[0] = sr.eq(Cat(dqi, sr[:-8]))  # Shift on 0°
        cases[2] = sr.eq(Cat(dqi, sr[:-8]))  # Shift on 180°
        self.sync += Case(clk_phase, cases)
        self.comb += [
            bus.dat_r.eq(sr),  # To Wisbone
            dq.o.eq(sr[-8:]),  # To HyperRAM
        ]

        # Command generation -----------------------------------------------------------------------
        self.comb += [
            ca[47].eq(~self.bus.we),  # R/W#
            ca[45].eq(1),  # Burst Type (Linear)
            ca[16:35].eq(self.bus.adr[2:21]),  # Row & Upper Column Address
            ca[1:3].eq(self.bus.adr[0:2]),  # Lower Column Address
            ca[0].eq(0),  # Lower Column Address
        ]

        # Sequencer --------------------------------------------------------------------------------
        dt_seq = [
            # DT,  Action
            (3, []),
            (12, [cs.eq(1), dq.oe.eq(1), sr.eq(ca)]),  # Command: 6 clk
            (44, [dq.oe.eq(0)]),  # Latency(default): 2*6 clk
            (
                2,
                [
                    dq.oe.eq(self.bus.we),  # Write/Read data byte: 2 clk
                    sr[:16].eq(0),
                    sr[16:].eq(self.bus.dat_w),
                    rwds.oe.eq(self.bus.we),
                    rwds.o.eq(~bus.sel[0])
                ]),
            (2, [rwds.o.eq(~bus.sel[1])]),  # Write/Read data byte: 2 clk
            (2, [rwds.o.eq(~bus.sel[2])]),  # Write/Read data byte: 2 clk
            (2, [rwds.o.eq(~bus.sel[3])]),  # Write/Read data byte: 2 clk
            (2, [cs.eq(0), rwds.oe.eq(0), dq.oe.eq(0)]),
            (1, [bus.ack.eq(1)]),
            (1, [bus.ack.eq(0)]),
            (0, []),
        ]
        # Convert delta-time sequencer to time sequencer
        t_seq = []
        t_seq_start = (clk_phase == 1)
        t = 0
        for dt, a in dt_seq:
            t_seq.append((t, a))
            t += dt
        self.sync += timeline(bus.cyc & bus.stb & t_seq_start, t_seq)