phaser: rework rtio channels, sync_dly, init()

This commit is contained in:
Robert Jördens 2020-09-16 11:49:46 +00:00
parent f3b0398720
commit c18f515bf9
3 changed files with 57 additions and 35 deletions

View File

@ -21,12 +21,14 @@ PHASER_ADDR_SPI_SEL = 0x0c
PHASER_ADDR_SPI_DATW = 0x0d PHASER_ADDR_SPI_DATW = 0x0d
PHASER_ADDR_SPI_DATR = 0x0e PHASER_ADDR_SPI_DATR = 0x0e
PHASER_ADDR_SYNC_DLY = 0x0f PHASER_ADDR_SYNC_DLY = 0x0f
PHASER_ADDR_DUC0_CFG = 0x10 PHASER_ADDR_DUC0_CFG = 0x10
# PHASER_ADDR_DUC0_RESERVED0 = 0x11 # PHASER_ADDR_DUC0_RESERVED0 = 0x11
PHASER_ADDR_DUC0_F = 0x12 PHASER_ADDR_DUC0_F = 0x12
PHASER_ADDR_DUC0_P = 0x16 PHASER_ADDR_DUC0_P = 0x16
PHASER_ADDR_DAC0_DATA = 0x18 PHASER_ADDR_DAC0_DATA = 0x18
PHASER_ADDR_DAC0_TEST = 0x1c PHASER_ADDR_DAC0_TEST = 0x1c
PHASER_ADDR_DUC1_CFG = 0x20 PHASER_ADDR_DUC1_CFG = 0x20
# PHASER_ADDR_DUC1_RESERVED0 = 0x21 # PHASER_ADDR_DUC1_RESERVED0 = 0x21
PHASER_ADDR_DUC1_F = 0x22 PHASER_ADDR_DUC1_F = 0x22
@ -152,6 +154,7 @@ class Phaser:
self.set_leds(0x00) self.set_leds(0x00)
self.set_fan_mu(0) self.set_fan_mu(0)
self.set_cfg(clk_sel=clk_sel) # bring everything out of reset self.set_cfg(clk_sel=clk_sel) # bring everything out of reset
self.set_sync_dly(4) # tune?
delay(.1*ms) # slack delay(.1*ms) # slack
# 4 wire SPI, sif4_enable # 4 wire SPI, sif4_enable
@ -175,7 +178,6 @@ class Phaser:
self.dac_write(0x03, 0x4000) # coarse dac 20.6 mA self.dac_write(0x03, 0x4000) # coarse dac 20.6 mA
self.dac_write(0x07, 0x40c1) # alarm mask self.dac_write(0x07, 0x40c1) # alarm mask
self.dac_write(0x09, 0x4000) # fifo_offset self.dac_write(0x09, 0x4000) # fifo_offset
self.set_sync_dly(0)
self.dac_write(0x0d, 0x0000) # fmix, no cmix self.dac_write(0x0d, 0x0000) # fmix, no cmix
self.dac_write(0x14, 0x5431) # fine nco ab self.dac_write(0x14, 0x5431) # fine nco ab
self.dac_write(0x15, 0x0323) # coarse 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(0x17, 0x0323) # coarse nco cd
self.dac_write(0x18, 0x2c60) # P=4, pll run, single cp, pll_ndivsync 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(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 delay(.2*ms) # slack
self.dac_write(0x1b, 0x0800) # int ref, fuse self.dac_write(0x1b, 0x0800) # int ref, fuse
self.dac_write(0x1e, 0x9999) # qmc sync from sif and reg 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(0x22, 0x1be4) # reverse dacs for spectral inversion and layout
self.dac_write(0x24, 0x0000) # clk and data delays self.dac_write(0x24, 0x0000) # clk and data delays
self.clear_dac_alarms()
delay(1*ms) # lock pll delay(1*ms) # lock pll
lvolt = self.dac_read(0x18) & 7 lvolt = self.dac_read(0x18) & 7
delay(.1*ms) delay(.1*ms)
if lvolt < 2 or lvolt > 5: if lvolt < 2 or lvolt > 5:
raise ValueError("DAC PLL tuning voltage out of bounds") raise ValueError("DAC PLL tuning voltage out of bounds")
# self.dac_write(0x20, 0x0000) # stop fifo sync # self.dac_write(0x20, 0x0000) # stop fifo sync
self.dac_write(0x05, 0x0000) # clear alarms # alarm = self.get_sta() & 1
delay(1*ms) # run it # delay(.1*ms)
alarm = self.get_sta() & 1 alarm = self.get_dac_alarms()
delay(.1*ms) if alarm & ~0x0040: # ignore PLL alarms (see DS)
if alarm: print(alarm)
alarm = self.dac_read(0x05)
raise ValueError("DAC alarm") raise ValueError("DAC alarm")
delay(.5*ms)
patterns = [ patterns = [
[0xffff, 0xffff, 0x0000, 0x0000], # test channel [0xffff, 0xffff, 0x0000, 0x0000], # test channel
@ -213,11 +216,10 @@ class Phaser:
[0x7a7a, 0xb6b6, 0xeaea, 0x4545], # ds pattern a [0x7a7a, 0xb6b6, 0xeaea, 0x4545], # ds pattern a
[0x1a1a, 0x1616, 0xaaaa, 0xc6c6], # ds pattern b [0x1a1a, 0x1616, 0xaaaa, 0xc6c6], # ds pattern b
] ]
delay(.5*ms) # A data delay of 2*50 ps heuristically matches FPGA+board+DAC skews.
# A data delay of 3*50 ps heuristically matches FPGA+board+DAC skews.
# There is plenty of margin and no need to tune at runtime. # There is plenty of margin and no need to tune at runtime.
# Parity provides another level of safety. # Parity provides another level of safety.
for dly in [-3]: # range(-7, 8) for dly in [-2]: # range(-7, 8)
if dly < 0: if dly < 0:
dly = -dly << 3 # data delay, else clock delay dly = -dly << 3 # data delay, else clock delay
self.dac_write(0x24, dly << 10) self.dac_write(0x24, dly << 10)
@ -226,6 +228,7 @@ class Phaser:
if errors: if errors:
raise ValueError("iotest error") raise ValueError("iotest error")
delay(.5*ms) delay(.5*ms)
self.clear_dac_alarms()
hw_rev = self.read8(PHASER_ADDR_HW_REV) hw_rev = self.read8(PHASER_ADDR_HW_REV)
has_upconverter = hw_rev & PHASER_HW_REV_VARIANT has_upconverter = hw_rev & PHASER_HW_REV_VARIANT
@ -362,6 +365,16 @@ class Phaser:
""" """
return self.read8(PHASER_ADDR_CRC_ERR) 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 @kernel
def duc_stb(self): def duc_stb(self):
"""Strobe the DUC configuration register update. """Strobe the DUC configuration register update.
@ -456,6 +469,19 @@ class Phaser:
""" """
return self.dac_read(0x06, div=257) >> 8 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 @kernel
def dac_iotest(self, pattern) -> TInt32: def dac_iotest(self, pattern) -> TInt32:
"""Performs a DAC IO test according to the datasheet. """Performs a DAC IO test according to the datasheet.
@ -479,7 +505,7 @@ class Phaser:
delay(.2*ms) # let it rip delay(.2*ms) # let it rip
# no need to go through the alarm register, # no need to go through the alarm register,
# just read the error mask # just read the error mask
# self.dac_write(0x05, 0x0000) # clear alarms # self.clear_dac_alarms()
# alarm = self.dac_read(0x05) # alarm = self.dac_read(0x05)
# delay(.1*ms) # slack # delay(.1*ms) # slack
# if alarm & 0x0080: # alarm_from_iotest # if alarm & 0x0080: # alarm_from_iotest
@ -489,16 +515,6 @@ class Phaser:
self.dac_write(0x04, 0x0000) # clear iotest_result self.dac_write(0x04, 0x0000) # clear iotest_result
return errors 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: class PhaserChannel:
"""Phaser channel IQ pair. """Phaser channel IQ pair.
@ -702,7 +718,7 @@ class PhaserOscillator:
def __init__(self, channel, index): def __init__(self, channel, index):
self.channel = channel self.channel = channel
self.base_addr = ((self.channel.phaser.channel_base + 1 + self.base_addr = ((self.channel.phaser.channel_base + 1 +
self.channel.index) << 8) | (index << 1) 2*self.channel.index) << 8) | index
@kernel @kernel
def set_frequency_mu(self, ftw): def set_frequency_mu(self, ftw):
@ -731,7 +747,7 @@ class PhaserOscillator:
:param clr: Clear the phase accumulator (persistent) :param clr: Clear the phase accumulator (persistent)
""" """
data = (asf & 0x7fff) | ((clr & 1) << 15) | (pow << 16) data = (asf & 0x7fff) | ((clr & 1) << 15) | (pow << 16)
rtio_output(self.base_addr | 1, data) rtio_output(self.base_addr | (1 << 8), data)
@kernel @kernel
def set_amplitude_phase(self, amplitude, phase=0., clr=0): def set_amplitude_phase(self, amplitude, phase=0., clr=0):

View File

@ -652,6 +652,8 @@ class Phaser(_EEM):
target.submodules += phy target.submodules += phy
target.rtio_channels.extend([ target.rtio_channels.extend([
rtio.Channel.from_phy(phy, ififo_depth=4), rtio.Channel.from_phy(phy, ififo_depth=4),
rtio.Channel.from_phy(phy.ch0), rtio.Channel.from_phy(phy.ch0.frequency),
rtio.Channel.from_phy(phy.ch1), rtio.Channel.from_phy(phy.ch0.phase_amplitude),
rtio.Channel.from_phy(phy.ch1.frequency),
rtio.Channel.from_phy(phy.ch1.phase_amplitude),
]) ])

View File

@ -5,24 +5,28 @@ from artiq.gateware.rtio import rtlink
from .fastlink import SerDes, SerInterface from .fastlink import SerDes, SerInterface
class DDSChannel(Module): class Phy(Module):
def __init__(self, share_lut=None): def __init__(self, regs):
self.rtlink = rtlink.Interface( self.rtlink = rtlink.Interface(
rtlink.OInterface(data_width=32, address_width=4, rtlink.OInterface(data_width=32, address_width=4,
enable_replace=True)) enable_replace=True))
to_rio_phy = ClockDomainsRenamer("rio_phy") self.sync.rtio += [
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 += [
If(self.rtlink.o.stb, If(self.rtlink.o.stb,
Array(regs)[self.rtlink.o.address].eq(self.rtlink.o.data) 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): class Phaser(Module):
def __init__(self, pins, pins_n): def __init__(self, pins, pins_n):
self.rtlink = rtlink.Interface( self.rtlink = rtlink.Interface(