forked from M-Labs/artiq
1
0
Fork 0

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_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):

View File

@ -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),
])

View File

@ -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(