ad9910: add [wip]

This commit is contained in:
Robert Jördens 2018-01-03 18:42:22 +00:00
parent 7ac809f8b3
commit eae7584432
1 changed files with 159 additions and 0 deletions

159
artiq/coredevice/ad9910.py Normal file
View File

@ -0,0 +1,159 @@
from artiq.language.core import kernel, delay_mu, delay, portable
from artiq.language.units import us, ns, ms
from artiq.coredevice.urukul import urukul_sta_pll_lock
from numpy import int32, int64
_AD9910_REG_CFR1 = 0x00
_AD9910_REG_CFR2 = 0x01
_AD9910_REG_CFR3 = 0x02
_AD9910_REG_AUX_DAC = 0x03
_AD9910_REG_IO_UPD = 0x04
_AD9910_REG_FTW = 0x07
_AD9910_REG_POW = 0x08
_AD9910_REG_ASF = 0x09
_AD9910_REG_MSYNC = 0x0A
_AD9910_REG_DRAMPL = 0x0B
_AD9910_REG_DRAMPS = 0x0C
_AD9910_REG_DRAMPR = 0x0D
_AD9910_REG_PR0 = 0x0E
_AD9910_REG_PR1 = 0x0F
_AD9910_REG_PR2 = 0x10
_AD9910_REG_PR3 = 0x11
_AD9910_REG_PR4 = 0x12
_AD9910_REG_PR5 = 0x13
_AD9910_REG_PR6 = 0x14
_AD9910_REG_PR7 = 0x15
_AD9910_REG_RAM = 0x16
class AD9910:
"""
Support for the AD9910 DDS on Urukul
:param chip_select: Chip select configuration.
:param cpld_device: Name of the Urukul CPLD this device is on.
:param sw_device: Name of the RF switch device.
"""
kernel_invariants = {"chip_select", "cpld", "core", "bus", "sw",
"ftw_per_hz", "sysclk", "pll_n", "pll_cp", "pll_vco"}
def __init__(self, dmgr, chip_select, cpld_device, sw_device=None,
pll_n=40, pll_cp=7, pll_vco=5):
self.cpld = dmgr.get(cpld_device)
self.core = self.cpld.core
self.bus = self.cpld.bus
assert chip_select >= 4
self.chip_select = chip_select
if sw_device:
self.sw = dmgr.get(sw_device)
assert 12 <= pll_n <= 127
self.pll_n = pll_n
self.sysclk = self.cpld.refclk*pll_n/4 # Urukul clock fanout divider
self.ftw_per_hz = 1./self.sysclk*(int64(1) << 32)
assert 0 <= pll_vco <= 5
vco_min, vco_max = [(370, 510), (420, 590), (500, 700),
(600, 880), (700, 950), (820, 1150)][pll_vco]
assert vco_min <= self.sysclk/1e6 <= vco_max
self.pll_vco = pll_vco
assert 0 <= pll_cp <= 7
self.pll_cp = pll_cp
@kernel
def write(self, addr, data, length=4):
assert (length == 2) or (length == 4)
self.bus.set_xfer(self.chip_select, 8, 0)
self.bus.write(addr << 24)
delay_mu(-self.bus.xfer_period_mu)
self.bus.set_xfer(self.chip_select, length*8, 0)
self.bus.write(data << (32 - length*8))
delay_mu(self.bus.xfer_period_mu - self.bus.write_period_mu)
@kernel
def write64(self, addr, data_high, data_low):
self.bus.set_xfer(self.chip_select, 8, 0)
self.bus.write(addr << 24)
t = self.bus.xfer_period_mu
delay_mu(-t)
self.bus.set_xfer(self.chip_select, 32, 0)
self.bus.write(data_high)
self.bus.write(data_low)
delay_mu(t - 2*self.bus.write_period_mu)
@kernel
def read(self, addr, length=4):
assert length >= 2
assert length <= 4
self.bus.set_xfer(self.chip_select, 8, 0)
self.bus.write((addr | 0x80) << 24)
delay_mu(-self.bus.xfer_period_mu)
self.bus.set_xfer(self.chip_select, 0, length*8)
self.bus.write(0)
delay_mu(2*self.bus.xfer_period_mu)
data = self.bus.read_sync()
if length < 4:
data &= (1 << (length*8)) - 1
return data
@kernel
def init(self):
# self.cpld.io_rst()
self.write(_AD9910_REG_CFR1, 0x00000002)
delay(100*ns)
self.cpld.io_update.pulse(100*ns)
aux_dac = self.read(_AD9910_REG_AUX_DAC)
assert aux_dac & 0xff == 0x7f
delay(10*us)
self.write(_AD9910_REG_CFR2, 0x01400020)
cfr3 = (0x0807c100 | (self.pll_vco << 24) |
(self.pll_cp << 19) | (self.pll_n << 1))
self.write(_AD9910_REG_CFR3, cfr3 | 0x400) # PFD reset
delay(10*us)
self.cpld.io_update.pulse(100*ns)
self.write(_AD9910_REG_CFR3, cfr3)
delay(10*us)
self.cpld.io_update.pulse(100*ns)
for i in range(100):
lock = urukul_sta_pll_lock(self.cpld.sta_read())
delay(1*ms)
if lock & (1 << self.chip_select - 4) != 0:
return
raise ValueError("PLL failed to lock")
@kernel
def set_mu(self, ftw, pow=0, asf=0x3fff):
self.write64(_AD9910_REG_PR0, (asf << 16) | pow, ftw)
self.cpld.io_update.pulse(10*ns)
@portable(flags={"fast-math"})
def frequency_to_ftw(self, frequency):
"""Returns the frequency tuning word corresponding to the given
frequency.
"""
return int32(round(self.ftw_per_hz*frequency))
@portable(flags={"fast-math"})
def turns_to_pow(self, turns):
"""Returns the phase offset word corresponding to the given phase
in turns."""
return int32(round(turns*0x10000))
@portable(flags={"fast-math"})
def amplitude_to_asf(self, amplitude):
"""Returns amplitude scale factor corresponding to given amplitude."""
return int32(round(amplitude*0x3ffe))
@kernel
def set(self, frequency, phase=0.0, amplitude=1.0):
self.set_mu(self.frequency_to_ftw(frequency),
self.turns_to_pow(phase),
self.amplitude_to_asf(amplitude))
@kernel
def set_att_mu(self, att):
self.cpld.set_att_mu(self.chip_select - 4, att)
@kernel
def set_att(self, att):
self.cpld.set_att(self.chip_select - 4, att)