diff --git a/artiq/coredevice/phaser.py b/artiq/coredevice/phaser.py index 2e780ecf4..1251173de 100644 --- a/artiq/coredevice/phaser.py +++ b/artiq/coredevice/phaser.py @@ -21,12 +21,14 @@ PHASER_ADDR_SPI_SEL = 0x0c PHASER_ADDR_SPI_DATW = 0x0d PHASER_ADDR_SPI_DATR = 0x0e PHASER_ADDR_SYNC_DLY = 0x0f + PHASER_ADDR_DUC0_CFG = 0x10 # PHASER_ADDR_DUC0_RESERVED0 = 0x11 PHASER_ADDR_DUC0_F = 0x12 PHASER_ADDR_DUC0_P = 0x16 PHASER_ADDR_DAC0_DATA = 0x18 PHASER_ADDR_DAC0_TEST = 0x1c + PHASER_ADDR_DUC1_CFG = 0x20 # PHASER_ADDR_DUC1_RESERVED0 = 0x21 PHASER_ADDR_DUC1_F = 0x22 @@ -152,6 +154,7 @@ class Phaser: self.set_leds(0x00) self.set_fan_mu(0) self.set_cfg(clk_sel=clk_sel) # bring everything out of reset + self.set_sync_dly(4) # tune? delay(.1*ms) # slack # 4 wire SPI, sif4_enable @@ -175,7 +178,6 @@ class Phaser: self.dac_write(0x03, 0x4000) # coarse dac 20.6 mA self.dac_write(0x07, 0x40c1) # alarm mask self.dac_write(0x09, 0x4000) # fifo_offset - self.set_sync_dly(0) self.dac_write(0x0d, 0x0000) # fmix, no cmix self.dac_write(0x14, 0x5431) # fine nco ab self.dac_write(0x15, 0x0323) # coarse nco ab @@ -183,7 +185,7 @@ class Phaser: self.dac_write(0x17, 0x0323) # coarse nco cd self.dac_write(0x18, 0x2c60) # P=4, pll run, single cp, pll_ndivsync self.dac_write(0x19, 0x8814) # M=16 N=2 - self.dac_write(0x1a, 0xfc00) # pll_vco=63 + self.dac_write(0x1a, 0xfc00) # pll_vco=63, 4 GHz delay(.2*ms) # slack self.dac_write(0x1b, 0x0800) # int ref, fuse self.dac_write(0x1e, 0x9999) # qmc sync from sif and reg @@ -192,19 +194,20 @@ class Phaser: self.dac_write(0x22, 0x1be4) # reverse dacs for spectral inversion and layout self.dac_write(0x24, 0x0000) # clk and data delays + self.clear_dac_alarms() delay(1*ms) # lock pll lvolt = self.dac_read(0x18) & 7 delay(.1*ms) if lvolt < 2 or lvolt > 5: raise ValueError("DAC PLL tuning voltage out of bounds") # self.dac_write(0x20, 0x0000) # stop fifo sync - self.dac_write(0x05, 0x0000) # clear alarms - delay(1*ms) # run it - alarm = self.get_sta() & 1 - delay(.1*ms) - if alarm: - alarm = self.dac_read(0x05) + # alarm = self.get_sta() & 1 + # delay(.1*ms) + alarm = self.get_dac_alarms() + if alarm & ~0x0040: # ignore PLL alarms (see DS) + print(alarm) raise ValueError("DAC alarm") + delay(.5*ms) patterns = [ [0xffff, 0xffff, 0x0000, 0x0000], # test channel @@ -213,11 +216,10 @@ class Phaser: [0x7a7a, 0xb6b6, 0xeaea, 0x4545], # ds pattern a [0x1a1a, 0x1616, 0xaaaa, 0xc6c6], # ds pattern b ] - delay(.5*ms) - # A data delay of 3*50 ps heuristically matches FPGA+board+DAC skews. + # A data delay of 2*50 ps heuristically matches FPGA+board+DAC skews. # There is plenty of margin and no need to tune at runtime. # Parity provides another level of safety. - for dly in [-3]: # range(-7, 8) + for dly in [-2]: # range(-7, 8) if dly < 0: dly = -dly << 3 # data delay, else clock delay self.dac_write(0x24, dly << 10) @@ -226,6 +228,7 @@ class Phaser: if errors: raise ValueError("iotest error") delay(.5*ms) + self.clear_dac_alarms() hw_rev = self.read8(PHASER_ADDR_HW_REV) has_upconverter = hw_rev & PHASER_HW_REV_VARIANT @@ -362,6 +365,16 @@ class Phaser: """ return self.read8(PHASER_ADDR_CRC_ERR) + @kernel + def set_sync_dly(self, dly): + """Set SYNC delay. + + :param dly: DAC SYNC delay setting (0 to 7) + """ + if dly < 0 or dly > 7: + raise ValueError("SYNC delay out of bounds") + self.write8(PHASER_ADDR_SYNC_DLY, dly) + @kernel def duc_stb(self): """Strobe the DUC configuration register update. @@ -456,6 +469,19 @@ class Phaser: """ return self.dac_read(0x06, div=257) >> 8 + @kernel + def get_dac_alarms(self): + """Read the DAC alarm flags. + + :return: DAC alarm flags (see datasheet for bit meaning) + """ + return self.dac_read(0x05) + + @kernel + def clear_dac_alarms(self): + """Clear DAC alarm flags.""" + self.dac_write(0x05, 0x0000) + @kernel def dac_iotest(self, pattern) -> TInt32: """Performs a DAC IO test according to the datasheet. @@ -479,7 +505,7 @@ class Phaser: delay(.2*ms) # let it rip # no need to go through the alarm register, # just read the error mask - # self.dac_write(0x05, 0x0000) # clear alarms + # self.clear_dac_alarms() # alarm = self.dac_read(0x05) # delay(.1*ms) # slack # if alarm & 0x0080: # alarm_from_iotest @@ -489,16 +515,6 @@ class Phaser: self.dac_write(0x04, 0x0000) # clear iotest_result return errors - @kernel - def set_sync_dly(self, dly): - """Set SYNC delay. - - :param dly: DAC SYNC delay setting (0 to 7) - """ - if dly < 0 or dly > 7: - raise ValueError("SYNC delay out of bounds") - self.write8(PHASER_ADDR_SYNC_DLY, dly) - class PhaserChannel: """Phaser channel IQ pair. @@ -702,7 +718,7 @@ class PhaserOscillator: def __init__(self, channel, index): self.channel = channel self.base_addr = ((self.channel.phaser.channel_base + 1 + - self.channel.index) << 8) | (index << 1) + 2*self.channel.index) << 8) | index @kernel def set_frequency_mu(self, ftw): @@ -731,7 +747,7 @@ class PhaserOscillator: :param clr: Clear the phase accumulator (persistent) """ data = (asf & 0x7fff) | ((clr & 1) << 15) | (pow << 16) - rtio_output(self.base_addr | 1, data) + rtio_output(self.base_addr | (1 << 8), data) @kernel def set_amplitude_phase(self, amplitude, phase=0., clr=0): diff --git a/artiq/gateware/eem.py b/artiq/gateware/eem.py index 447dd1fe6..d6314e9be 100644 --- a/artiq/gateware/eem.py +++ b/artiq/gateware/eem.py @@ -652,6 +652,8 @@ class Phaser(_EEM): target.submodules += phy target.rtio_channels.extend([ rtio.Channel.from_phy(phy, ififo_depth=4), - rtio.Channel.from_phy(phy.ch0), - rtio.Channel.from_phy(phy.ch1), + rtio.Channel.from_phy(phy.ch0.frequency), + rtio.Channel.from_phy(phy.ch0.phase_amplitude), + rtio.Channel.from_phy(phy.ch1.frequency), + rtio.Channel.from_phy(phy.ch1.phase_amplitude), ]) diff --git a/artiq/gateware/rtio/phy/phaser.py b/artiq/gateware/rtio/phy/phaser.py index 46dfc8005..634d4f202 100644 --- a/artiq/gateware/rtio/phy/phaser.py +++ b/artiq/gateware/rtio/phy/phaser.py @@ -5,24 +5,28 @@ from artiq.gateware.rtio import rtlink from .fastlink import SerDes, SerInterface -class DDSChannel(Module): - def __init__(self, share_lut=None): +class Phy(Module): + def __init__(self, regs): self.rtlink = rtlink.Interface( rtlink.OInterface(data_width=32, address_width=4, enable_replace=True)) - to_rio_phy = ClockDomainsRenamer("rio_phy") - self.submodules.dds = to_rio_phy(MultiDDS( - n=5, fwidth=32, xwidth=16, z=19, zl=10, share_lut=share_lut)) - regs = [] - for i in self.dds.i: - regs.extend([i.f, Cat(i.a, i.clr, i.p)]) - self.sync.rio_phy += [ + self.sync.rtio += [ If(self.rtlink.o.stb, Array(regs)[self.rtlink.o.address].eq(self.rtlink.o.data) ) ] +class DDSChannel(Module): + def __init__(self, share_lut=None): + to_rio_phy = ClockDomainsRenamer("rio_phy") + self.submodules.dds = to_rio_phy(MultiDDS( + n=5, fwidth=32, xwidth=16, z=19, zl=10, share_lut=share_lut)) + self.submodules.frequency = Phy([i.f for i in self.dds.i]) + self.submodules.phase_amplitude = Phy( + [Cat(i.a, i.clr, i.p) for i in self.dds.i]) + + class Phaser(Module): def __init__(self, pins, pins_n): self.rtlink = rtlink.Interface(