forked from M-Labs/artiq
ad9910: add documentation
This commit is contained in:
parent
b6395a809b
commit
ede98679fc
|
@ -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)
|
||||||
|
|
Loading…
Reference in New Issue