forked from M-Labs/artiq
1
0
Fork 0

ad9910: add documentation

This commit is contained in:
Robert Jördens 2018-02-14 09:05:03 +01:00
parent b6395a809b
commit ede98679fc
1 changed files with 61 additions and 4 deletions

View File

@ -30,11 +30,21 @@ _AD9910_REG_RAM = 0x16
class AD9910: class AD9910:
""" """
Support for the AD9910 DDS on Urukul AD9910 DDS channel on Urukul.
:param chip_select: Chip select configuration. This class supports a single DDS channel and exposes the DDS,
the digital step attenuator, and the RF switch.
:param chip_select: Chip select configuration. On Urukul this is an
encoded chip select and not "one-hot".
:param cpld_device: Name of the Urukul CPLD this device is on. :param cpld_device: Name of the Urukul CPLD this device is on.
:param sw_device: Name of the RF switch device. :param sw_device: Name of the RF switch device. The RF switch is a
TTLOut channel available as the :attr:`sw` attribute of this instance.
:param pll_n: DDS PLL multiplier. The DDS sample clock is
f_ref/4*pll_n where f_ref is the reference frequency (set in the parent
Urukul CPLD instance).
:param pll_cp: DDS PLL charge pump setting.
:param pll_vco: DDS PLL VCO range selection.
""" """
kernel_invariants = {"chip_select", "cpld", "core", "bus", "sw", kernel_invariants = {"chip_select", "cpld", "core", "bus", "sw",
"ftw_per_hz", "sysclk", "pll_n", "pll_cp", "pll_vco"} "ftw_per_hz", "sysclk", "pll_n", "pll_cp", "pll_vco"}
@ -50,7 +60,9 @@ class AD9910:
self.sw = dmgr.get(sw_device) self.sw = dmgr.get(sw_device)
assert 12 <= pll_n <= 127 assert 12 <= pll_n <= 127
self.pll_n = pll_n self.pll_n = pll_n
assert self.cpld.refclk < 60e6
self.sysclk = self.cpld.refclk*pll_n/4 # Urukul clock fanout divider self.sysclk = self.cpld.refclk*pll_n/4 # Urukul clock fanout divider
assert self.sysclk < 1e9
self.ftw_per_hz = 1./self.sysclk*(int64(1) << 32) self.ftw_per_hz = 1./self.sysclk*(int64(1) << 32)
assert 0 <= pll_vco <= 5 assert 0 <= pll_vco <= 5
vco_min, vco_max = [(370, 510), (420, 590), (500, 700), vco_min, vco_max = [(370, 510), (420, 590), (500, 700),
@ -62,6 +74,11 @@ class AD9910:
@kernel @kernel
def write32(self, addr, data): def write32(self, addr, data):
"""Write to 32 bit register.
:param addr: Register address
:param data: Data to be written
"""
self.bus.set_xfer(self.chip_select, 8, 0) self.bus.set_xfer(self.chip_select, 8, 0)
self.bus.write(addr << 24) self.bus.write(addr << 24)
delay_mu(-self.bus.xfer_period_mu + 8) delay_mu(-self.bus.xfer_period_mu + 8)
@ -71,6 +88,12 @@ class AD9910:
@kernel @kernel
def write64(self, addr, data_high, data_low): def write64(self, addr, data_high, data_low):
"""Write to 64 bit register.
:param addr: Register address
:param data_high: High (MSB) 32 bits of the data
:param data_low: Low (LSB) 32 data bits
"""
self.bus.set_xfer(self.chip_select, 8, 0) self.bus.set_xfer(self.chip_select, 8, 0)
self.bus.write(addr << 24) self.bus.write(addr << 24)
t = self.bus.xfer_period_mu t = self.bus.xfer_period_mu
@ -82,6 +105,10 @@ class AD9910:
@kernel @kernel
def read32(self, addr): def read32(self, addr):
"""Read from 32 bit register.
:param addr: Register address
"""
self.bus.set_xfer(self.chip_select, 8, 0) self.bus.set_xfer(self.chip_select, 8, 0)
self.bus.write((addr | 0x80) << 24) self.bus.write((addr | 0x80) << 24)
delay_mu(-self.bus.xfer_period_mu + 8) delay_mu(-self.bus.xfer_period_mu + 8)
@ -93,13 +120,17 @@ class AD9910:
@kernel @kernel
def init(self): def init(self):
"""Initialize and configure the DDS."""
# Set SPI mode
self.write32(_AD9910_REG_CFR1, 0x00000002) self.write32(_AD9910_REG_CFR1, 0x00000002)
delay(100*ns) delay(100*us)
self.cpld.io_update.pulse(100*ns) self.cpld.io_update.pulse(100*ns)
# Use the AUX DAC setting to identify and confirm presence
aux_dac = self.read32(_AD9910_REG_AUX_DAC) aux_dac = self.read32(_AD9910_REG_AUX_DAC)
if aux_dac & 0xff != 0x7f: if aux_dac & 0xff != 0x7f:
raise ValueError("Urukul AD9910 AUX_DAC mismatch") raise ValueError("Urukul AD9910 AUX_DAC mismatch")
delay(100*us) delay(100*us)
# Configure PLL settings and bring up PLL
self.write32(_AD9910_REG_CFR2, 0x01400020) self.write32(_AD9910_REG_CFR2, 0x01400020)
cfr3 = (0x0807c100 | (self.pll_vco << 24) | cfr3 = (0x0807c100 | (self.pll_vco << 24) |
(self.pll_cp << 19) | (self.pll_n << 1)) (self.pll_cp << 19) | (self.pll_n << 1))
@ -109,6 +140,7 @@ class AD9910:
self.write32(_AD9910_REG_CFR3, cfr3) self.write32(_AD9910_REG_CFR3, cfr3)
delay(100*us) delay(100*us)
self.cpld.io_update.pulse(100*ns) self.cpld.io_update.pulse(100*ns)
# Wait for PLL lock, up to 100 ms
for i in range(100): for i in range(100):
lock = urukul_sta_pll_lock(self.cpld.sta_read()) lock = urukul_sta_pll_lock(self.cpld.sta_read())
delay(1*ms) delay(1*ms)
@ -118,6 +150,15 @@ class AD9910:
@kernel @kernel
def set_mu(self, ftw, pow=0, asf=0x3fff): def set_mu(self, ftw, pow=0, asf=0x3fff):
"""Set profile 0 data in machine units.
After the SPI transfer, the shared IO update pin is pulsed to
activate the data.
:param ftw: Frequency tuning word: 32 bit unsigned.
:param pow: Phase tuning word: 16 bit unsigned.
:param asf: Amplitude scale factor: 14 bit unsigned.
"""
self.write64(_AD9910_REG_PR0, (asf << 16) | pow, ftw) self.write64(_AD9910_REG_PR0, (asf << 16) | pow, ftw)
self.cpld.io_update.pulse(10*ns) self.cpld.io_update.pulse(10*ns)
@ -141,14 +182,30 @@ class AD9910:
@kernel @kernel
def set(self, frequency, phase=0.0, amplitude=1.0): def set(self, frequency, phase=0.0, amplitude=1.0):
"""Set profile 0 data in SI units.
.. seealso:: :meth:`set_mu`
:param ftw: Frequency in Hz
:param pow: Phase tuning word in turns
:param asf: Amplitude in units of full scale
"""
self.set_mu(self.frequency_to_ftw(frequency), self.set_mu(self.frequency_to_ftw(frequency),
self.turns_to_pow(phase), self.turns_to_pow(phase),
self.amplitude_to_asf(amplitude)) self.amplitude_to_asf(amplitude))
@kernel @kernel
def set_att_mu(self, att): def set_att_mu(self, att):
"""Set digital step attenuator in machine units.
:param att: Attenuation setting, 8 bit digital.
"""
self.cpld.set_att_mu(self.chip_select - 4, att) self.cpld.set_att_mu(self.chip_select - 4, att)
@kernel @kernel
def set_att(self, att): def set_att(self, att):
"""Set digital step attenuator in SI units.
:param att: Attenuation in dB.
"""
self.cpld.set_att(self.chip_select - 4, att) self.cpld.set_att(self.chip_select - 4, att)