forked from M-Labs/artiq
phaser: nco, settings and init tweaks
This commit is contained in:
parent
fdb2867757
commit
3e036e365a
@ -151,15 +151,17 @@ class Phaser:
|
||||
delay(.1*ms) # slack
|
||||
|
||||
# reset
|
||||
self.set_cfg(dac_resetb=0, att0_rstn=0, att1_rstn=0)
|
||||
self.set_cfg(dac_resetb=0, att0_rstn=0, att1_rstn=0, dac_txena=0)
|
||||
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) # TODO: tune this?
|
||||
self.set_cfg(clk_sel=clk_sel, dac_txena=0) # bring everything out of reset
|
||||
# TODO: crossing dac_clk (125 MHz) edges with sync_dly (0-7 ns)
|
||||
# should change the optimal fifo_offset
|
||||
self.set_sync_dly(4)
|
||||
delay(.1*ms) # slack
|
||||
|
||||
# 4 wire SPI, sif4_enable
|
||||
self.dac_write(0x02, 0x0082)
|
||||
self.dac_write(0x02, 0x0080)
|
||||
if self.dac_read(0x7f) != 0x5409:
|
||||
raise ValueError("DAC version readback invalid")
|
||||
delay(.1*ms)
|
||||
@ -168,9 +170,9 @@ class Phaser:
|
||||
delay(.1*ms)
|
||||
|
||||
t = self.get_dac_temperature()
|
||||
delay(.5*ms)
|
||||
if t < 10 or t > 90:
|
||||
raise ValueError("DAC temperature out of bounds")
|
||||
delay(.5*ms)
|
||||
|
||||
patterns = [
|
||||
[0xf05a, 0x05af, 0x5af0, 0xaf05], # test channel/iq/byte/nibble
|
||||
@ -182,8 +184,8 @@ class Phaser:
|
||||
# either side) and no need to tune at runtime.
|
||||
# Parity provides another level of safety.
|
||||
for dly in [-2]: # range(-7, 8)
|
||||
if dly < 0:
|
||||
dly = -dly << 3 # data delay, else clock delay
|
||||
if dly < 0: # use data delay, else use clock delay
|
||||
dly = -dly << 3
|
||||
self.dac_write(0x24, dly << 10)
|
||||
for i in range(len(patterns)):
|
||||
errors = self.dac_iotest(patterns[i])
|
||||
@ -191,38 +193,195 @@ class Phaser:
|
||||
raise ValueError("DAC iotest failure")
|
||||
delay(.5*ms)
|
||||
|
||||
self.dac_write(0x00, 0x019c) # I=2, fifo, clkdiv_sync, qmc off
|
||||
self.dac_write(0x01, 0x040e) # fifo alarms, parity
|
||||
self.dac_write(0x02, 0x70a2) # clk alarms, sif4, nco off, mix, mix_gain, 2s
|
||||
self.dac_write(0x03, 0xa000) # coarse dac 20.6 mA
|
||||
self.dac_write(0x07, 0x40c1) # alarm mask
|
||||
self.dac_write(0x09, 0x4000) # fifo_offset
|
||||
self.dac_write(0x0d, 0x0000) # fmix, no cmix
|
||||
self.dac_write(0x14, 0x5431) # fine nco ab
|
||||
self.dac_write(0x15, 0x0323) # coarse nco ab
|
||||
self.dac_write(0x16, 0x5431) # fine 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(0x19, 0x8814) # M=16 N=2
|
||||
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
|
||||
self.dac_write(0x1f, 0x9982) # mix sync, nco sync, istr is istr, sif_sync
|
||||
self.dac_write(0x20, 0x2400) # fifo sync ISTR-OSTR
|
||||
self.dac_write(0x22, 0x1be4) # reverse dacs for spectral inversion and layout
|
||||
self.dac_write(0x24, 0x0000) # clk and data delays
|
||||
qmc_corr_ena = 0 # msb ab
|
||||
qmc_offset_ena = 0 # msb ab
|
||||
invsinc_ena = 0 # msb ab
|
||||
|
||||
interpolation = 1 # 2x
|
||||
fifo_ena = 1
|
||||
alarm_out_ena = 1
|
||||
alarm_out_pol = 1
|
||||
clkdiv_sync_ena = 1
|
||||
self.dac_write(0x00,
|
||||
(qmc_offset_ena << 14) | (qmc_corr_ena << 12) |
|
||||
(interpolation << 8) | (fifo_ena << 7) |
|
||||
(alarm_out_ena << 4) | (alarm_out_pol << 3) |
|
||||
(clkdiv_sync_ena << 2) | (invsinc_ena << 0))
|
||||
iotest_ena = 0
|
||||
cnt64_ena = 0
|
||||
oddeven_parity = 0 # even
|
||||
single_parity_ena = 1
|
||||
dual_parity_ena = 0
|
||||
rev_interface = 0
|
||||
dac_complement = 0b0000 # msb A
|
||||
alarm_fifo = 0b111 # msb 2-away
|
||||
self.dac_write(0x01,
|
||||
(iotest_ena << 15) | (cnt64_ena << 12) |
|
||||
(oddeven_parity << 11) | (single_parity_ena << 10) |
|
||||
(dual_parity_ena << 9) | (rev_interface << 8) |
|
||||
(dac_complement << 4) | (alarm_fifo << 1))
|
||||
dacclkgone_ena = 1
|
||||
dataclkgone_ena = 1
|
||||
collisiongone_ena = 1
|
||||
sif4_ena = 1
|
||||
mixer_ena = 0
|
||||
mixer_gain = 1
|
||||
nco_ena = 0
|
||||
revbus = 0
|
||||
twos = 1
|
||||
self.dac_write(0x02,
|
||||
(dacclkgone_ena << 14) | (dataclkgone_ena << 13) |
|
||||
(collisiongone_ena << 12) | (sif4_ena << 7) |
|
||||
(mixer_ena << 6) | (mixer_gain << 5) |
|
||||
(nco_ena << 4) | (revbus << 3) | (twos << 1))
|
||||
coarse_dac = 0xa # 20.6 mA, 0-15
|
||||
sif_txenable = 0
|
||||
self.dac_write(0x03, (coarse_dac << 12) | (sif_txenable << 0))
|
||||
mask_alarm_from_zerochk = 0
|
||||
mask_alarm_fifo_collision = 0
|
||||
mask_alarm_fifo_1away = 0
|
||||
mask_alarm_fifo_2away = 0
|
||||
mask_alarm_dacclk_gone = 0
|
||||
mask_alarm_dataclk_gone = 0
|
||||
mask_alarm_output_gone = 0
|
||||
mask_alarm_from_iotest = 0
|
||||
mask_alarm_from_pll = 0
|
||||
mask_alarm_parity = 0b0000 # msb a
|
||||
self.dac_write(0x07,
|
||||
(mask_alarm_from_zerochk << 15) | (1 << 14) |
|
||||
(mask_alarm_fifo_collision << 13) | (mask_alarm_fifo_1away << 12) |
|
||||
(mask_alarm_fifo_2away << 11) | (mask_alarm_dacclk_gone << 10) |
|
||||
(mask_alarm_dataclk_gone << 9) | (mask_alarm_output_gone << 8) |
|
||||
(mask_alarm_from_iotest << 7) | (1 << 6) |
|
||||
(mask_alarm_from_pll << 5) | (mask_alarm_parity << 1))
|
||||
qmc_offseta = 0 # 12b
|
||||
self.dac_write(0x08, qmc_offseta)
|
||||
fifo_offset = 2 # 0-7
|
||||
qmc_offsetb = 0 # 12b
|
||||
self.dac_write(0x09, (fifo_offset << 13) | qmc_offsetb)
|
||||
qmc_offsetc = 0 # 12b
|
||||
self.dac_write(0x0a, qmc_offsetc)
|
||||
qmc_offsetd = 0 # 12b
|
||||
self.dac_write(0x0b, qmc_offsetd)
|
||||
qmc_gaina = 0 # 11b
|
||||
self.dac_write(0x0c, qmc_gaina)
|
||||
cmix_fs8 = 0
|
||||
cmix_fs4 = 0
|
||||
cmix_fs2 = 0
|
||||
cmix_nfs4 = 0
|
||||
qmc_gainb = 0 # 11b
|
||||
self.dac_write(0x0d,
|
||||
(cmix_fs8 << 15) | (cmix_fs4 << 14) | (cmix_fs2 << 12) |
|
||||
(cmix_nfs4 << 11) | qmc_gainb)
|
||||
qmc_gainc = 0 # 11b
|
||||
self.dac_write(0x0e, qmc_gainc)
|
||||
output_delayab = 0b00
|
||||
output_delaycd = 0b00
|
||||
qmc_gaind = 0 # 11b
|
||||
self.dac_write(0x0f, (output_delayab << 14) | (output_delaycd << 12) |
|
||||
qmc_gaind)
|
||||
qmc_phaseab = 0 # 12b
|
||||
self.dac_write(0x10, qmc_phaseab)
|
||||
qmc_phasecd = 0 # 12b
|
||||
self.dac_write(0x11, qmc_phasecd)
|
||||
pll_reset = 0
|
||||
pll_ndivsync_ena = 1
|
||||
pll_ena = 1
|
||||
pll_cp = 0b01 # single charge pump
|
||||
pll_p = 0b100 # p=4
|
||||
self.dac_write(0x18,
|
||||
(0b001 << 13) | (pll_reset << 12) |
|
||||
(pll_ndivsync_ena << 11) | (pll_ena << 10) |
|
||||
(pll_cp << 6) | (pll_p << 3))
|
||||
pll_m2 = 1 # x2
|
||||
pll_m = 8 # m = 8
|
||||
pll_n = 0b0001 # n = 2
|
||||
pll_vcotune = 0b01
|
||||
self.dac_write(0x19,
|
||||
(pll_m2 << 15) | (pll_m << 8) | (pll_n << 4) | (pll_vcotune << 2))
|
||||
delay(.5*ms) # slack
|
||||
pll_vco = 0x3f # 4 GHz
|
||||
bias_sleep = 0
|
||||
tsense_sleep = 0
|
||||
pll_sleep = 0
|
||||
clkrecv_sleep = 0
|
||||
dac_sleep = 0b0000 # msb a
|
||||
self.dac_write(0x1a,
|
||||
(pll_vco << 10) | (bias_sleep << 7) | (tsense_sleep << 6) |
|
||||
(pll_sleep << 5) | (clkrecv_sleep << 4) | (dac_sleep << 0))
|
||||
extref_ena = 0
|
||||
fuse_sleep = 1
|
||||
atest = 0b00000 # atest mode
|
||||
self.dac_write(0x1b,
|
||||
(extref_ena << 15) | (fuse_sleep << 11) | (atest << 0))
|
||||
syncsel_qmcoffsetab = 0b1001 # sif_sync and register write
|
||||
syncsel_qmcoffsetcd = 0b1001 # sif_sync and register write
|
||||
syncsel_qmccorrab = 0b1001 # sif_sync and register write
|
||||
syncsel_qmccorrcd = 0b1001 # sif_sync and register write
|
||||
self.dac_write(0x1e,
|
||||
(syncsel_qmcoffsetab << 12) | (syncsel_qmcoffsetcd << 8) |
|
||||
(syncsel_qmccorrab << 4) | (syncsel_qmccorrcd << 0))
|
||||
syncsel_mixerab = 0b1001 # sif_sync and register write
|
||||
syncsel_mixercd = 0b1001 # sif_sync and register write
|
||||
syncsel_nco = 0b1000 # sif_sync
|
||||
syncsel_fifo_input = 0b10 # external lvds istr
|
||||
sif_sync = 1
|
||||
self.dac_write(0x1e,
|
||||
(syncsel_mixerab << 12) | (syncsel_mixercd << 8) |
|
||||
(syncsel_nco << 4) | (syncsel_fifo_input << 2) |
|
||||
(sif_sync << 1))
|
||||
syncsel_fifoin = 0b0010 # istr
|
||||
syncsel_fifoout = 0b0100 # ostr
|
||||
clkdiv_sync_sel = 0 # ostr
|
||||
self.dac_write(0x20,
|
||||
(syncsel_fifoin << 12) | (syncsel_fifoout << 8) |
|
||||
(clkdiv_sync_sel << 0))
|
||||
path_a_sel = 0b00
|
||||
path_b_sel = 0b01
|
||||
path_c_sel = 0b10
|
||||
path_d_sel = 0b11
|
||||
# reverse dacs (DCBA) for spectral inversion and layout
|
||||
dac_a_sel = 0b11
|
||||
dac_b_sel = 0b10
|
||||
dac_c_sel = 0b01
|
||||
dac_d_sel = 0b00
|
||||
self.dac_write(0x22,
|
||||
(path_a_sel << 14) | (path_b_sel << 12) |
|
||||
(path_c_sel << 10) | (path_d_sel << 8) |
|
||||
(dac_a_sel << 6) | (dac_b_sel << 4) |
|
||||
(dac_c_sel << 2) | (dac_d_sel << 0))
|
||||
dac_sleep_en = 0b1111 # msb a
|
||||
clkrecv_sleep_en = 1
|
||||
pll_sleep_en = 1
|
||||
lvds_data_sleep_en = 1
|
||||
lvds_control_sleep_en = 1
|
||||
temp_sense_sleep_en = 1
|
||||
bias_sleep_en = 1
|
||||
self.dac_write(0x23,
|
||||
(dac_sleep_en << 12) | (clkrecv_sleep_en << 11) |
|
||||
(pll_sleep_en << 10) | (lvds_data_sleep_en << 9) |
|
||||
(lvds_control_sleep_en << 8) | (temp_sense_sleep_en << 7) |
|
||||
(1 << 6) | (bias_sleep_en << 5) | (0x1f << 0))
|
||||
# self.dac_write(0x24, 0x0000) # clk and data delays (tuned above)
|
||||
ostrtodig_sel = 0
|
||||
ramp_ena = 0
|
||||
sifdac_ena = 0
|
||||
self.dac_write(0x2d,
|
||||
(ostrtodig_sel << 14) | (ramp_ena << 13) | (0x002 << 1) |
|
||||
(sifdac_ena << 0))
|
||||
grp_delaya = 0x00
|
||||
grp_delayb = 0x00
|
||||
self.dac_write(0x2e, (grp_delaya << 8) | (grp_delayb << 0))
|
||||
grp_delayc = 0x00
|
||||
grp_delayd = 0x00
|
||||
self.dac_write(0x2f, (grp_delayc << 8) | (grp_delayd << 0))
|
||||
sifdac = 0
|
||||
self.dac_write(0x30, sifdac)
|
||||
|
||||
delay(2*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.clear_dac_alarms()
|
||||
|
||||
hw_rev = self.read8(PHASER_ADDR_HW_REV)
|
||||
has_upconverter = hw_rev & PHASER_HW_REV_VARIANT
|
||||
delay(.1*ms) # slack
|
||||
|
||||
for ch in range(2):
|
||||
channel = self.channel[ch]
|
||||
@ -233,38 +392,43 @@ class Phaser:
|
||||
delay(.1*ms)
|
||||
channel.set_att(31.5*dB)
|
||||
|
||||
# test oscillators and DUC
|
||||
for i in range(len(channel.oscillator)):
|
||||
oscillator = channel.oscillator[i]
|
||||
asf = 0
|
||||
if i == 0:
|
||||
asf = 0x7fff
|
||||
else:
|
||||
asf = 0
|
||||
# pi/4 phase
|
||||
oscillator.set_amplitude_phase_mu(asf=asf, pow=0x2000, clr=1)
|
||||
# 6pi/4 phase
|
||||
oscillator.set_amplitude_phase_mu(asf=asf, pow=0xc000, clr=1)
|
||||
delay_mu(8)
|
||||
delay(1*us) # settle link, pipeline and impulse response
|
||||
# test oscillator and DUC and their phase sign
|
||||
channel.set_duc_phase_mu(0)
|
||||
# 3pi/4
|
||||
channel.set_duc_phase_mu(0x6000)
|
||||
channel.set_duc_cfg(select=0, clr=1)
|
||||
self.duc_stb()
|
||||
delay(.1*ms) # settle link, pipeline and impulse response
|
||||
data = channel.get_dac_data()
|
||||
delay(.1*ms)
|
||||
if data != 0x4a124a12:
|
||||
sqrt2 = 0x5a81 # 0x7fff/sqrt(2)
|
||||
data_i = data & 0xffff
|
||||
data_q = (data >> 16) & 0xffff
|
||||
# allow ripple
|
||||
if (data_i < sqrt2 - 30 or data_i > sqrt2 or
|
||||
abs(data_i - data_q) > 2):
|
||||
print(data)
|
||||
raise ValueError("DUC+oscillator phase/amplitude test failed")
|
||||
|
||||
# self.dac_write(0x20, 0x0000) # stop fifo sync
|
||||
# alarm = self.get_sta() & 1
|
||||
# delay(.1*ms)
|
||||
self.clear_dac_alarms()
|
||||
delay(2*ms) # let it run a bit
|
||||
self.check_dac_alarms()
|
||||
|
||||
@kernel
|
||||
def check_dac_alarms(self):
|
||||
alarm = self.get_dac_alarms()
|
||||
hw_rev = self.read8(PHASER_ADDR_HW_REV)
|
||||
has_upconverter = hw_rev & PHASER_HW_REV_VARIANT
|
||||
delay(.1*ms) # slack
|
||||
if alarm & ~0x0040: # ignore PLL alarms (see DS)
|
||||
print(alarm)
|
||||
raise ValueError("DAC alarm")
|
||||
|
||||
self.set_cfg(clk_sel=clk_sel) # txena
|
||||
|
||||
@kernel
|
||||
def write8(self, addr, data):
|
||||
@ -493,6 +657,14 @@ class Phaser:
|
||||
"""
|
||||
return self.dac_read(0x05)
|
||||
|
||||
@kernel
|
||||
def check_dac_alarms(self):
|
||||
alarm = self.get_dac_alarms()
|
||||
delay(.1*ms) # slack
|
||||
if alarm & ~0x0040: # ignore PLL alarms (see DS)
|
||||
print(alarm)
|
||||
raise ValueError("DAC alarm")
|
||||
|
||||
@kernel
|
||||
def clear_dac_alarms(self):
|
||||
"""Clear DAC alarm flags."""
|
||||
@ -542,6 +714,17 @@ class Phaser:
|
||||
class PhaserChannel:
|
||||
"""Phaser channel IQ pair.
|
||||
|
||||
A Phaser channel contains:
|
||||
|
||||
* multiple oscillators (in the coredevice phy),
|
||||
* an interpolation chain and digital upconverter (DUC) on Phaser,
|
||||
* several channel-specific settings in the DAC:
|
||||
* quadrature modulation compensation QMC
|
||||
* numerically controlled oscillator NCO or coarse mixer CMIX,
|
||||
* the analog quadrature upconverter (in the Phaser-Upconverter hardware
|
||||
variant), and
|
||||
* a digitally controlled step attenuator.
|
||||
|
||||
Attributes:
|
||||
|
||||
* :attr:`oscillator`: List of five :class:`PhaserOscillator`.
|
||||
@ -636,6 +819,42 @@ class PhaserChannel:
|
||||
pow = int32(round(phase*(1 << 16)))
|
||||
self.set_duc_phase_mu(pow)
|
||||
|
||||
@kernel
|
||||
def set_nco_frequency_mu(self, ftw):
|
||||
"""Set the NCO frequency.
|
||||
|
||||
:param ftw: NCO frequency tuning word (32 bit)
|
||||
"""
|
||||
self.phaser.dac_write(0x15 + (self.index << 1), ftw >> 16)
|
||||
self.phaser.dac_write(0x14 + (self.index << 1), ftw)
|
||||
|
||||
@kernel
|
||||
def set_nco_frequency(self, frequency):
|
||||
"""Set the NCO frequency in SI units.
|
||||
|
||||
:param frequency: NCO frequency in Hz (passband from -400 MHz
|
||||
to 400 MHz, wrapping around at +- 500 MHz)
|
||||
"""
|
||||
ftw = int32(round(frequency*((1 << 31)/(500*MHz))))
|
||||
self.set_nco_frequency_mu(ftw)
|
||||
|
||||
@kernel
|
||||
def set_nco_phase_mu(self, pow):
|
||||
"""Set the NCO phase offset.
|
||||
|
||||
:param pow: NCO phase offset word (16 bit)
|
||||
"""
|
||||
self.phaser.dac_write(0x12 + self.index, pow)
|
||||
|
||||
@kernel
|
||||
def set_nco_phase(self, phase):
|
||||
"""Set the NCO phase in SI units.
|
||||
|
||||
:param phase: NCO phase in turns
|
||||
"""
|
||||
pow = int32(round(phase*(1 << 16)))
|
||||
self.set_duc_phase_mu(pow)
|
||||
|
||||
@kernel
|
||||
def set_att_mu(self, data):
|
||||
"""Set channel attenuation.
|
||||
|
Loading…
Reference in New Issue
Block a user