diff --git a/artiq/coredevice/urukul.py b/artiq/coredevice/urukul.py index fe9c1a68c..459f10f58 100644 --- a/artiq/coredevice/urukul.py +++ b/artiq/coredevice/urukul.py @@ -115,6 +115,7 @@ class CPLD: :param spi_device: SPI bus device name :param io_update_device: IO update RTIO TTLOut channel name :param dds_reset_device: DDS reset RTIO TTLOut channel name + :param sync_device: AD9910 SYNC_IN RTIO TTLClockGen channel name :param refclk: Reference clock (SMA, MMCX or on-board 100 MHz oscillator) frequency in Hz :param clk_sel: Reference clock selection. For hardware revision >= 1.3 @@ -122,8 +123,8 @@ class CPLD: internal MMCX. For hardware revision <= v1.2 valid options are: 0 - either XO or MMCX dependent on component population; 1 SMA. Unsupported clocking options are silently ignored. - :param sync_sel: SYNC clock selection. 0 corresponds to SYNC clock over EEM - from FPGA. 1 corresponds to SYNC clock from DDS0. + :param sync_sel: SYNC_IN selection. 0 corresponds to SYNC_IN over EEM + from FPGA. 1 corresponds to SYNC_IN from DDS0. :param rf_sw: Initial CPLD RF switch register setting (default: 0x0). Knowledge of this state is not transferred between experiments. :param att: Initial attenuator setting shift register (default: @@ -134,7 +135,8 @@ class CPLD: kernel_invariants = {"refclk", "bus", "core", "io_update"} def __init__(self, dmgr, spi_device, io_update_device=None, - dds_reset_device=None, sync_sel=0, clk_sel=0, rf_sw=0, + dds_reset_device=None, sync_device=None, + sync_sel=0, clk_sel=0, rf_sw=0, refclk=125e6, att=0x00000000, core_device="core"): self.core = dmgr.get(core_device) @@ -147,6 +149,8 @@ class CPLD: self.io_update = _RegIOUpdate(self) if dds_reset_device is not None: self.dds_reset = dmgr.get(dds_reset_device) + if sync_device is not None: + self.sync = dmgr.get(sync_device) self.cfg_reg = urukul_cfg(rf_sw=rf_sw, led=0, profile=0, io_update=0, mask_nu=0, clk_sel=clk_sel, @@ -289,3 +293,20 @@ class CPLD: SPIT_ATT_RD, CS_ATT) self.bus.write(self.att_reg) return self.bus.read() + + @kernel + def set_sync_div(self, div): + """Set the SYNC_IN AD9910 pulse generator frequency + and align it to the current RTIO timestamp. + + The SYNC_IN signal is derived from the coarse RTIO clock + and the divider must be a power of two two. + Configure ``sync_sel == 0``. + + :param div: SYNC_IN frequency divider. Must be a power of two. + Minimum division ratio is 2. Maximum division ratio is 16. + """ + ftw_max = 1 << 4 + ftw = ftw_max//div + assert ftw*div == ftw_max + self.sync.set_mu(ftw) diff --git a/artiq/examples/kasli_basic/device_db_ptb.py b/artiq/examples/kasli_basic/device_db_ptb.py index 5aa1c6e6a..a3c64bba7 100644 --- a/artiq/examples/kasli_basic/device_db_ptb.py +++ b/artiq/examples/kasli_basic/device_db_ptb.py @@ -87,36 +87,42 @@ device_db.update({ "class": "SPIMaster", "arguments": {"channel": 27} }, - "ttl_urukul0_io_update": { + "ttl_urukul0_sync": { "type": "local", "module": "artiq.coredevice.ttl", - "class": "TTLOut", - "arguments": {"channel": 28} + "class": "TTLClockGen", + "arguments": {"channel": 28, "acc_width": 4} }, - "ttl_urukul0_sw0": { + "ttl_urukul0_io_update": { "type": "local", "module": "artiq.coredevice.ttl", "class": "TTLOut", "arguments": {"channel": 29} }, - "ttl_urukul0_sw1": { + "ttl_urukul0_sw0": { "type": "local", "module": "artiq.coredevice.ttl", "class": "TTLOut", "arguments": {"channel": 30} }, - "ttl_urukul0_sw2": { + "ttl_urukul0_sw1": { "type": "local", "module": "artiq.coredevice.ttl", "class": "TTLOut", "arguments": {"channel": 31} }, - "ttl_urukul0_sw3": { + "ttl_urukul0_sw2": { "type": "local", "module": "artiq.coredevice.ttl", "class": "TTLOut", "arguments": {"channel": 32} }, + "ttl_urukul0_sw3": { + "type": "local", + "module": "artiq.coredevice.ttl", + "class": "TTLOut", + "arguments": {"channel": 33} + }, "urukul0_cpld": { "type": "local", "module": "artiq.coredevice.urukul", @@ -124,6 +130,7 @@ device_db.update({ "arguments": { "spi_device": "spi_urukul0", "io_update_device": "ttl_urukul0_io_update", + "sync_device": "ttl_urukul0_sync", "refclk": 100e6, "clk_sel": 0 } @@ -150,13 +157,19 @@ device_db.update({ "type": "local", "module": "artiq.coredevice.spi2", "class": "SPIMaster", - "arguments": {"channel": 33} + "arguments": {"channel": 34} + }, + "ttl_urukul1_sync": { + "type": "local", + "module": "artiq.coredevice.ttl", + "class": "TTLClockGen", + "arguments": {"channel": 35, "acc_width": 4} }, "ttl_urukul1_io_update": { "type": "local", "module": "artiq.coredevice.ttl", "class": "TTLOut", - "arguments": {"channel": 34} + "arguments": {"channel": 36} }, "urukul1_cpld": { "type": "local", @@ -190,13 +203,13 @@ device_db.update({ "type": "local", "module": "artiq.coredevice.ttl", "class": "TTLOut", - "arguments": {"channel": 35} + "arguments": {"channel": 37} }, "led1": { "type": "local", "module": "artiq.coredevice.ttl", "class": "TTLOut", - "arguments": {"channel": 36} + "arguments": {"channel": 38} } }) @@ -206,19 +219,19 @@ device_db.update({ "type": "local", "module": "artiq.coredevice.spi2", "class": "SPIMaster", - "arguments": {"channel": 37} + "arguments": {"channel": 39} }, "ttl_zotino0_ldac": { "type": "local", "module": "artiq.coredevice.ttl", "class": "TTLOut", - "arguments": {"channel": 38} + "arguments": {"channel": 40} }, "ttl_zotino0_clr": { "type": "local", "module": "artiq.coredevice.ttl", "class": "TTLOut", - "arguments": {"channel": 40} + "arguments": {"channel": 41} }, "zotino0": { "type": "local", diff --git a/artiq/gateware/eem.py b/artiq/gateware/eem.py index 34680a714..3c556a4ce 100644 --- a/artiq/gateware/eem.py +++ b/artiq/gateware/eem.py @@ -148,7 +148,8 @@ class Urukul(_EEM): return ios @classmethod - def add_std(cls, target, eem, eem_aux, ttl_out_cls, iostandard="LVDS_25"): + 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)), @@ -157,7 +158,12 @@ class Urukul(_EEM): 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) + 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) @@ -170,7 +176,6 @@ class Urukul(_EEM): target.submodules += phy target.rtio_channels.append(rtio.Channel.from_phy(phy)) - class Sampler(_EEM): @staticmethod def io(eem, eem_aux, iostandard="LVDS_25"): diff --git a/artiq/gateware/targets/kasli.py b/artiq/gateware/targets/kasli.py index 79c93dfcc..b061ef648 100755 --- a/artiq/gateware/targets/kasli.py +++ b/artiq/gateware/targets/kasli.py @@ -516,8 +516,10 @@ class PTB(_StandaloneBase): eem.DIO.add_std(self, 2, ttl_serdes_7series.Output_8X, ttl_serdes_7series.Output_8X) eem.Sampler.add_std(self, 3, None, ttl_serdes_7series.Output_8X) - eem.Urukul.add_std(self, 5, 4, ttl_serdes_7series.Output_8X) - eem.Urukul.add_std(self, 6, None, ttl_serdes_7series.Output_8X) + eem.Urukul.add_std(self, 5, 4, ttl_serdes_7series.Output_8X, + ttl_simple.ClockGen) + eem.Urukul.add_std(self, 6, None, ttl_serdes_7series.Output_8X, + ttl_simple.ClockGen) for i in (1, 2): sfp_ctl = self.platform.request("sfp_ctl", i)