mirror of https://github.com/m-labs/artiq.git
ad9910: port to NAC3
This commit is contained in:
parent
0256c91d53
commit
be07481eb5
|
@ -1,15 +1,35 @@
|
||||||
from numpy import int32, int64
|
from numpy import int32, int64
|
||||||
|
|
||||||
from artiq.language.core import kernel, portable
|
from artiq.language.core import *
|
||||||
from artiq.language.units import us, ms
|
from artiq.language.units import us, ms
|
||||||
|
|
||||||
from artiq.coredevice import spi2 as spi
|
from artiq.coredevice.spi2 import *
|
||||||
from artiq.coredevice import urukul
|
from artiq.coredevice.urukul import *
|
||||||
from artiq.coredevice.urukul import DEFAULT_PROFILE
|
from artiq.coredevice.urukul import *
|
||||||
|
from artiq.coredevice.ttl import TTLOut
|
||||||
|
from artiq.coredevice.kasli_i2c import KasliEEPROM # NAC3TODO
|
||||||
|
|
||||||
|
|
||||||
|
# NAC3TODO work around https://git.m-labs.hk/M-Labs/nac3/issues/189
|
||||||
|
@nac3
|
||||||
|
class ValueError(Exception):
|
||||||
|
pass
|
||||||
|
|
||||||
|
# NAC3TODO work around https://git.m-labs.hk/M-Labs/nac3/issues/206
|
||||||
|
@portable
|
||||||
|
def min(a: int32, b: int32) -> int32:
|
||||||
|
if a > b:
|
||||||
|
return b
|
||||||
|
else:
|
||||||
|
return a
|
||||||
|
|
||||||
|
@portable
|
||||||
|
def max(a: int32, b: int32) -> int32:
|
||||||
|
if a > b:
|
||||||
|
return a
|
||||||
|
else:
|
||||||
|
return b
|
||||||
|
|
||||||
# Work around ARTIQ-Python import machinery
|
|
||||||
urukul_sta_pll_lock = urukul.urukul_sta_pll_lock
|
|
||||||
urukul_sta_smp_err = urukul.urukul_sta_smp_err
|
|
||||||
|
|
||||||
__all__ = [
|
__all__ = [
|
||||||
"AD9910",
|
"AD9910",
|
||||||
|
@ -62,8 +82,12 @@ RAM_MODE_CONT_RAMPUP = 4
|
||||||
# Default profile for RAM mode
|
# Default profile for RAM mode
|
||||||
_DEFAULT_PROFILE_RAM = 0
|
_DEFAULT_PROFILE_RAM = 0
|
||||||
|
|
||||||
|
@nac3
|
||||||
class SyncDataUser:
|
class SyncDataUser:
|
||||||
|
core: KernelInvariant[Core]
|
||||||
|
sync_delay_seed: Kernel[int32]
|
||||||
|
io_update_delay: Kernel[int32]
|
||||||
|
|
||||||
def __init__(self, core, sync_delay_seed, io_update_delay):
|
def __init__(self, core, sync_delay_seed, io_update_delay):
|
||||||
self.core = core
|
self.core = core
|
||||||
self.sync_delay_seed = sync_delay_seed
|
self.sync_delay_seed = sync_delay_seed
|
||||||
|
@ -74,7 +98,14 @@ class SyncDataUser:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
@nac3
|
||||||
class SyncDataEeprom:
|
class SyncDataEeprom:
|
||||||
|
core: KernelInvariant[Core]
|
||||||
|
eeprom_device: KernelInvariant[KasliEEPROM] # NAC3TODO support generic EEPROM driver
|
||||||
|
eeprom_offset: KernelInvariant[int32]
|
||||||
|
sync_delay_seed: Kernel[int32]
|
||||||
|
io_update_delay: Kernel[int32]
|
||||||
|
|
||||||
def __init__(self, dmgr, core, eeprom_str):
|
def __init__(self, dmgr, core, eeprom_str):
|
||||||
self.core = core
|
self.core = core
|
||||||
|
|
||||||
|
@ -100,6 +131,7 @@ class SyncDataEeprom:
|
||||||
self.io_update_delay = int32(io_update_delay)
|
self.io_update_delay = int32(io_update_delay)
|
||||||
|
|
||||||
|
|
||||||
|
@nac3
|
||||||
class AD9910:
|
class AD9910:
|
||||||
"""
|
"""
|
||||||
AD9910 DDS channel on Urukul.
|
AD9910 DDS channel on Urukul.
|
||||||
|
@ -136,9 +168,24 @@ class AD9910:
|
||||||
to the same string value.
|
to the same string value.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
core: KernelInvariant[Core]
|
||||||
|
cpld: KernelInvariant[CPLD]
|
||||||
|
bus: KernelInvariant[SPIMaster]
|
||||||
|
chip_select: KernelInvariant[int32]
|
||||||
|
pll_en: KernelInvariant[bool]
|
||||||
|
pll_n: KernelInvariant[int32]
|
||||||
|
pll_vco: KernelInvariant[int32]
|
||||||
|
pll_cp: KernelInvariant[int32]
|
||||||
|
ftw_per_hz: KernelInvariant[float]
|
||||||
|
sysclk_per_mu: KernelInvariant[int32]
|
||||||
|
sysclk: KernelInvariant[float]
|
||||||
|
sw: KernelInvariant[TTLOut]
|
||||||
|
sync_data: KernelInvariant[SyncDataUser]
|
||||||
|
phase_mode: Kernel[int32]
|
||||||
|
|
||||||
def __init__(self, dmgr, chip_select, cpld_device, sw_device=None,
|
def __init__(self, dmgr, chip_select, cpld_device, sw_device=None,
|
||||||
pll_n=40, pll_cp=7, pll_vco=5, sync_delay_seed=-1,
|
pll_n=40, pll_cp=7, pll_vco=5, sync_delay_seed=-1,
|
||||||
io_update_delay=0, pll_en=1):
|
io_update_delay=0, pll_en=True):
|
||||||
self.kernel_invariants = {"cpld", "core", "bus", "chip_select",
|
self.kernel_invariants = {"cpld", "core", "bus", "chip_select",
|
||||||
"pll_en", "pll_n", "pll_vco", "pll_cp",
|
"pll_en", "pll_n", "pll_vco", "pll_cp",
|
||||||
"ftw_per_hz", "sysclk_per_mu", "sysclk",
|
"ftw_per_hz", "sysclk_per_mu", "sysclk",
|
||||||
|
@ -150,7 +197,7 @@ class AD9910:
|
||||||
self.chip_select = chip_select
|
self.chip_select = chip_select
|
||||||
if sw_device:
|
if sw_device:
|
||||||
self.sw = dmgr.get(sw_device)
|
self.sw = dmgr.get(sw_device)
|
||||||
self.kernel_invariants.add("sw")
|
# NAC3TODO: support no sw
|
||||||
clk = self.cpld.refclk / [4, 1, 2, 4][self.cpld.clk_div]
|
clk = self.cpld.refclk / [4, 1, 2, 4][self.cpld.clk_div]
|
||||||
self.pll_en = pll_en
|
self.pll_en = pll_en
|
||||||
self.pll_n = pll_n
|
self.pll_n = pll_n
|
||||||
|
@ -172,6 +219,7 @@ class AD9910:
|
||||||
self.sysclk_per_mu = int(round(sysclk * self.core.ref_period))
|
self.sysclk_per_mu = int(round(sysclk * self.core.ref_period))
|
||||||
self.sysclk = sysclk
|
self.sysclk = sysclk
|
||||||
|
|
||||||
|
# NAC3TODO
|
||||||
if isinstance(sync_delay_seed, str) or isinstance(io_update_delay,
|
if isinstance(sync_delay_seed, str) or isinstance(io_update_delay,
|
||||||
str):
|
str):
|
||||||
if sync_delay_seed != io_update_delay:
|
if sync_delay_seed != io_update_delay:
|
||||||
|
@ -236,8 +284,8 @@ class AD9910:
|
||||||
:param addr: Register address
|
:param addr: Register address
|
||||||
:param data: Data to be written
|
:param data: Data to be written
|
||||||
"""
|
"""
|
||||||
self.bus.set_config_mu(urukul.SPI_CONFIG | spi.SPI_END, 24,
|
self.bus.set_config_mu(SPI_CONFIG | SPI_END, 24,
|
||||||
urukul.SPIT_DDS_WR, self.chip_select)
|
SPIT_DDS_WR, self.chip_select)
|
||||||
self.bus.write((addr << 24) | ((data & 0xffff) << 8))
|
self.bus.write((addr << 24) | ((data & 0xffff) << 8))
|
||||||
|
|
||||||
@kernel
|
@kernel
|
||||||
|
@ -247,11 +295,11 @@ class AD9910:
|
||||||
:param addr: Register address
|
:param addr: Register address
|
||||||
:param data: Data to be written
|
:param data: Data to be written
|
||||||
"""
|
"""
|
||||||
self.bus.set_config_mu(urukul.SPI_CONFIG, 8,
|
self.bus.set_config_mu(SPI_CONFIG, 8,
|
||||||
urukul.SPIT_DDS_WR, self.chip_select)
|
SPIT_DDS_WR, self.chip_select)
|
||||||
self.bus.write(addr << 24)
|
self.bus.write(addr << 24)
|
||||||
self.bus.set_config_mu(urukul.SPI_CONFIG | spi.SPI_END, 32,
|
self.bus.set_config_mu(SPI_CONFIG | SPI_END, 32,
|
||||||
urukul.SPIT_DDS_WR, self.chip_select)
|
SPIT_DDS_WR, self.chip_select)
|
||||||
self.bus.write(data)
|
self.bus.write(data)
|
||||||
|
|
||||||
@kernel
|
@kernel
|
||||||
|
@ -260,12 +308,12 @@ class AD9910:
|
||||||
|
|
||||||
:param addr: Register address
|
:param addr: Register address
|
||||||
"""
|
"""
|
||||||
self.bus.set_config_mu(urukul.SPI_CONFIG, 8,
|
self.bus.set_config_mu(SPI_CONFIG, 8,
|
||||||
urukul.SPIT_DDS_WR, self.chip_select)
|
SPIT_DDS_WR, self.chip_select)
|
||||||
self.bus.write((addr | 0x80) << 24)
|
self.bus.write((addr | 0x80) << 24)
|
||||||
self.bus.set_config_mu(
|
self.bus.set_config_mu(
|
||||||
urukul.SPI_CONFIG | spi.SPI_END | spi.SPI_INPUT,
|
SPI_CONFIG | SPI_END | SPI_INPUT,
|
||||||
16, urukul.SPIT_DDS_RD, self.chip_select)
|
16, SPIT_DDS_RD, self.chip_select)
|
||||||
self.bus.write(0)
|
self.bus.write(0)
|
||||||
return self.bus.read()
|
return self.bus.read()
|
||||||
|
|
||||||
|
@ -275,12 +323,12 @@ class AD9910:
|
||||||
|
|
||||||
:param addr: Register address
|
:param addr: Register address
|
||||||
"""
|
"""
|
||||||
self.bus.set_config_mu(urukul.SPI_CONFIG, 8,
|
self.bus.set_config_mu(SPI_CONFIG, 8,
|
||||||
urukul.SPIT_DDS_WR, self.chip_select)
|
SPIT_DDS_WR, self.chip_select)
|
||||||
self.bus.write((addr | 0x80) << 24)
|
self.bus.write((addr | 0x80) << 24)
|
||||||
self.bus.set_config_mu(
|
self.bus.set_config_mu(
|
||||||
urukul.SPI_CONFIG | spi.SPI_END | spi.SPI_INPUT,
|
SPI_CONFIG | SPI_END | SPI_INPUT,
|
||||||
32, urukul.SPIT_DDS_RD, self.chip_select)
|
32, SPIT_DDS_RD, self.chip_select)
|
||||||
self.bus.write(0)
|
self.bus.write(0)
|
||||||
return self.bus.read()
|
return self.bus.read()
|
||||||
|
|
||||||
|
@ -292,20 +340,20 @@ class AD9910:
|
||||||
:return: 64 bit integer register value
|
:return: 64 bit integer register value
|
||||||
"""
|
"""
|
||||||
self.bus.set_config_mu(
|
self.bus.set_config_mu(
|
||||||
urukul.SPI_CONFIG, 8,
|
SPI_CONFIG, 8,
|
||||||
urukul.SPIT_DDS_WR, self.chip_select)
|
SPIT_DDS_WR, self.chip_select)
|
||||||
self.bus.write((addr | 0x80) << 24)
|
self.bus.write((addr | 0x80) << 24)
|
||||||
self.bus.set_config_mu(
|
self.bus.set_config_mu(
|
||||||
urukul.SPI_CONFIG | spi.SPI_INPUT, 32,
|
SPI_CONFIG | SPI_INPUT, 32,
|
||||||
urukul.SPIT_DDS_RD, self.chip_select)
|
SPIT_DDS_RD, self.chip_select)
|
||||||
self.bus.write(0)
|
self.bus.write(0)
|
||||||
self.bus.set_config_mu(
|
self.bus.set_config_mu(
|
||||||
urukul.SPI_CONFIG | spi.SPI_END | spi.SPI_INPUT, 32,
|
SPI_CONFIG | SPI_END | SPI_INPUT, 32,
|
||||||
urukul.SPIT_DDS_RD, self.chip_select)
|
SPIT_DDS_RD, self.chip_select)
|
||||||
self.bus.write(0)
|
self.bus.write(0)
|
||||||
hi = self.bus.read()
|
hi = self.bus.read()
|
||||||
lo = self.bus.read()
|
lo = self.bus.read()
|
||||||
return (int64(hi) << 32) | lo
|
return (int64(hi) << int64(32)) | int64(lo)
|
||||||
|
|
||||||
@kernel
|
@kernel
|
||||||
def write64(self, addr: int32, data_high: int32, data_low: int32):
|
def write64(self, addr: int32, data_high: int32, data_low: int32):
|
||||||
|
@ -315,14 +363,14 @@ class AD9910:
|
||||||
:param data_high: High (MSB) 32 bits of the data
|
:param data_high: High (MSB) 32 bits of the data
|
||||||
:param data_low: Low (LSB) 32 data bits
|
:param data_low: Low (LSB) 32 data bits
|
||||||
"""
|
"""
|
||||||
self.bus.set_config_mu(urukul.SPI_CONFIG, 8,
|
self.bus.set_config_mu(SPI_CONFIG, 8,
|
||||||
urukul.SPIT_DDS_WR, self.chip_select)
|
SPIT_DDS_WR, self.chip_select)
|
||||||
self.bus.write(addr << 24)
|
self.bus.write(addr << 24)
|
||||||
self.bus.set_config_mu(urukul.SPI_CONFIG, 32,
|
self.bus.set_config_mu(SPI_CONFIG, 32,
|
||||||
urukul.SPIT_DDS_WR, self.chip_select)
|
SPIT_DDS_WR, self.chip_select)
|
||||||
self.bus.write(data_high)
|
self.bus.write(data_high)
|
||||||
self.bus.set_config_mu(urukul.SPI_CONFIG | spi.SPI_END, 32,
|
self.bus.set_config_mu(SPI_CONFIG | SPI_END, 32,
|
||||||
urukul.SPIT_DDS_WR, self.chip_select)
|
SPIT_DDS_WR, self.chip_select)
|
||||||
self.bus.write(data_low)
|
self.bus.write(data_low)
|
||||||
|
|
||||||
@kernel
|
@kernel
|
||||||
|
@ -335,15 +383,15 @@ class AD9910:
|
||||||
|
|
||||||
:param data: Data to be written to RAM.
|
:param data: Data to be written to RAM.
|
||||||
"""
|
"""
|
||||||
self.bus.set_config_mu(urukul.SPI_CONFIG, 8, urukul.SPIT_DDS_WR,
|
self.bus.set_config_mu(SPI_CONFIG, 8, SPIT_DDS_WR,
|
||||||
self.chip_select)
|
self.chip_select)
|
||||||
self.bus.write(_AD9910_REG_RAM << 24)
|
self.bus.write(_AD9910_REG_RAM << 24)
|
||||||
self.bus.set_config_mu(urukul.SPI_CONFIG, 32,
|
self.bus.set_config_mu(SPI_CONFIG, 32,
|
||||||
urukul.SPIT_DDS_WR, self.chip_select)
|
SPIT_DDS_WR, self.chip_select)
|
||||||
for i in range(len(data) - 1):
|
for i in range(len(data) - 1):
|
||||||
self.bus.write(data[i])
|
self.bus.write(data[i])
|
||||||
self.bus.set_config_mu(urukul.SPI_CONFIG | spi.SPI_END, 32,
|
self.bus.set_config_mu(SPI_CONFIG | SPI_END, 32,
|
||||||
urukul.SPIT_DDS_WR, self.chip_select)
|
SPIT_DDS_WR, self.chip_select)
|
||||||
self.bus.write(data[len(data) - 1])
|
self.bus.write(data[len(data) - 1])
|
||||||
|
|
||||||
@kernel
|
@kernel
|
||||||
|
@ -356,21 +404,21 @@ class AD9910:
|
||||||
|
|
||||||
:param data: List to be filled with data read from RAM.
|
:param data: List to be filled with data read from RAM.
|
||||||
"""
|
"""
|
||||||
self.bus.set_config_mu(urukul.SPI_CONFIG, 8, urukul.SPIT_DDS_WR,
|
self.bus.set_config_mu(SPI_CONFIG, 8, SPIT_DDS_WR,
|
||||||
self.chip_select)
|
self.chip_select)
|
||||||
self.bus.write((_AD9910_REG_RAM | 0x80) << 24)
|
self.bus.write((_AD9910_REG_RAM | 0x80) << 24)
|
||||||
n = len(data) - 1
|
n = len(data) - 1
|
||||||
if n > 0:
|
if n > 0:
|
||||||
self.bus.set_config_mu(urukul.SPI_CONFIG | spi.SPI_INPUT, 32,
|
self.bus.set_config_mu(SPI_CONFIG | SPI_INPUT, 32,
|
||||||
urukul.SPIT_DDS_RD, self.chip_select)
|
SPIT_DDS_RD, self.chip_select)
|
||||||
preload = min(n, 8)
|
preload = min(n, 8)
|
||||||
for i in range(n):
|
for i in range(n):
|
||||||
self.bus.write(0)
|
self.bus.write(0)
|
||||||
if i >= preload:
|
if i >= preload:
|
||||||
data[i - preload] = self.bus.read()
|
data[i - preload] = self.bus.read()
|
||||||
self.bus.set_config_mu(
|
self.bus.set_config_mu(
|
||||||
urukul.SPI_CONFIG | spi.SPI_INPUT | spi.SPI_END, 32,
|
SPI_CONFIG | SPI_INPUT | SPI_END, 32,
|
||||||
urukul.SPIT_DDS_RD, self.chip_select)
|
SPIT_DDS_RD, self.chip_select)
|
||||||
self.bus.write(0)
|
self.bus.write(0)
|
||||||
for i in range(preload + 1):
|
for i in range(preload + 1):
|
||||||
data[(n - preload) + i] = self.bus.read()
|
data[(n - preload) + i] = self.bus.read()
|
||||||
|
@ -463,50 +511,50 @@ class AD9910:
|
||||||
if self.sync_data.sync_delay_seed >= 0 and not self.cpld.sync_div:
|
if self.sync_data.sync_delay_seed >= 0 and not self.cpld.sync_div:
|
||||||
raise ValueError("parent cpld does not drive SYNC")
|
raise ValueError("parent cpld does not drive SYNC")
|
||||||
if self.sync_data.sync_delay_seed >= 0:
|
if self.sync_data.sync_delay_seed >= 0:
|
||||||
if self.sysclk_per_mu != self.sysclk * self.core.ref_period:
|
if float(self.sysclk_per_mu) != self.sysclk * self.core.ref_period:
|
||||||
raise ValueError("incorrect clock ratio for synchronization")
|
raise ValueError("incorrect clock ratio for synchronization")
|
||||||
delay(50 * ms) # slack
|
self.core.delay(50. * ms) # slack
|
||||||
|
|
||||||
# Set SPI mode
|
# Set SPI mode
|
||||||
self.set_cfr1()
|
self.set_cfr1()
|
||||||
self.cpld.io_update.pulse(1 * us)
|
self.cpld.io_update.pulse(1. * us)
|
||||||
delay(1 * ms)
|
self.core.delay(1. * ms)
|
||||||
if not blind:
|
if not blind:
|
||||||
# Use the AUX DAC setting to identify and confirm presence
|
# 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(50 * us) # slack
|
self.core.delay(50. * us) # slack
|
||||||
# Configure PLL settings and bring up PLL
|
# Configure PLL settings and bring up PLL
|
||||||
# enable amplitude scale from profiles
|
# enable amplitude scale from profiles
|
||||||
# read effective FTW
|
# read effective FTW
|
||||||
# sync timing validation disable (enabled later)
|
# sync timing validation disable (enabled later)
|
||||||
self.set_cfr2(sync_validation_disable=1)
|
self.set_cfr2(sync_validation_disable=1)
|
||||||
self.cpld.io_update.pulse(1 * us)
|
self.cpld.io_update.pulse(1. * us)
|
||||||
cfr3 = (0x0807c000 | (self.pll_vco << 24) |
|
cfr3 = (0x0807c000 | (self.pll_vco << 24) |
|
||||||
(self.pll_cp << 19) | (self.pll_en << 8) |
|
(self.pll_cp << 19) | (int32(self.pll_en) << 8) |
|
||||||
(self.pll_n << 1))
|
(int32(self.pll_n) << 1))
|
||||||
self.write32(_AD9910_REG_CFR3, cfr3 | 0x400) # PFD reset
|
self.write32(_AD9910_REG_CFR3, cfr3 | 0x400) # PFD reset
|
||||||
self.cpld.io_update.pulse(1 * us)
|
self.cpld.io_update.pulse(1. * us)
|
||||||
if self.pll_en:
|
if self.pll_en:
|
||||||
self.write32(_AD9910_REG_CFR3, cfr3)
|
self.write32(_AD9910_REG_CFR3, cfr3)
|
||||||
self.cpld.io_update.pulse(1 * us)
|
self.cpld.io_update.pulse(1. * us)
|
||||||
if blind:
|
if blind:
|
||||||
delay(100 * ms)
|
self.core.delay(100. * ms)
|
||||||
else:
|
else:
|
||||||
# Wait for PLL lock, up to 100 ms
|
# Wait for PLL lock, up to 100 ms
|
||||||
for i in range(100):
|
for i in range(100):
|
||||||
sta = self.cpld.sta_read()
|
sta = self.cpld.sta_read()
|
||||||
lock = urukul_sta_pll_lock(sta)
|
lock = urukul_sta_pll_lock(sta)
|
||||||
delay(1 * ms)
|
self.core.delay(1. * ms)
|
||||||
if lock & (1 << self.chip_select - 4):
|
if lock & (1 << self.chip_select - 4) != 0:
|
||||||
break
|
break
|
||||||
if i >= 100 - 1:
|
if i >= 100 - 1:
|
||||||
raise ValueError("PLL lock timeout")
|
raise ValueError("PLL lock timeout")
|
||||||
delay(10 * us) # slack
|
self.core.delay(10. * us) # slack
|
||||||
if self.sync_data.sync_delay_seed >= 0 and not blind:
|
if self.sync_data.sync_delay_seed >= 0 and not blind:
|
||||||
self.tune_sync_delay(self.sync_data.sync_delay_seed)
|
self.tune_sync_delay(self.sync_data.sync_delay_seed)
|
||||||
delay(1 * ms)
|
self.core.delay(1. * ms)
|
||||||
|
|
||||||
@kernel
|
@kernel
|
||||||
def power_down(self, bits: int32 = 0b1111):
|
def power_down(self, bits: int32 = 0b1111):
|
||||||
|
@ -515,7 +563,7 @@ class AD9910:
|
||||||
:param bits: Power down bits, see datasheet
|
:param bits: Power down bits, see datasheet
|
||||||
"""
|
"""
|
||||||
self.set_cfr1(power_down=bits)
|
self.set_cfr1(power_down=bits)
|
||||||
self.cpld.io_update.pulse(1 * us)
|
self.cpld.io_update.pulse(1. * us)
|
||||||
|
|
||||||
@kernel
|
@kernel
|
||||||
def set_mu(self, ftw: int32 = 0, pow_: int32 = 0, asf: int32 = 0x3fff,
|
def set_mu(self, ftw: int32 = 0, pow_: int32 = 0, asf: int32 = 0x3fff,
|
||||||
|
@ -557,15 +605,15 @@ class AD9910:
|
||||||
phase_mode = self.phase_mode
|
phase_mode = self.phase_mode
|
||||||
# Align to coarse RTIO which aligns SYNC_CLK. I.e. clear fine TSC
|
# Align to coarse RTIO which aligns SYNC_CLK. I.e. clear fine TSC
|
||||||
# This will not cause a collision or sequence error.
|
# This will not cause a collision or sequence error.
|
||||||
at_mu(now_mu() & ~7)
|
at_mu(now_mu() & int64(~7))
|
||||||
if phase_mode != PHASE_MODE_CONTINUOUS:
|
if phase_mode != PHASE_MODE_CONTINUOUS:
|
||||||
# Auto-clear phase accumulator on IO_UPDATE.
|
# Auto-clear phase accumulator on IO_UPDATE.
|
||||||
# This is active already for the next IO_UPDATE
|
# This is active already for the next IO_UPDATE
|
||||||
self.set_cfr1(phase_autoclear=1)
|
self.set_cfr1(phase_autoclear=1)
|
||||||
if phase_mode == PHASE_MODE_TRACKING and ref_time_mu < 0:
|
if phase_mode == PHASE_MODE_TRACKING and ref_time_mu < int64(0):
|
||||||
# set default fiducial time stamp
|
# set default fiducial time stamp
|
||||||
ref_time_mu = 0
|
ref_time_mu = int64(0)
|
||||||
if ref_time_mu >= 0:
|
if ref_time_mu >= int64(0):
|
||||||
# 32 LSB are sufficient.
|
# 32 LSB are sufficient.
|
||||||
# Also no need to use IO_UPDATE time as this
|
# Also no need to use IO_UPDATE time as this
|
||||||
# is equivalent to an output pipeline latency.
|
# is equivalent to an output pipeline latency.
|
||||||
|
@ -583,8 +631,8 @@ class AD9910:
|
||||||
if not ram_destination == RAM_DEST_POW:
|
if not ram_destination == RAM_DEST_POW:
|
||||||
self.set_pow(pow_)
|
self.set_pow(pow_)
|
||||||
delay_mu(int64(self.sync_data.io_update_delay))
|
delay_mu(int64(self.sync_data.io_update_delay))
|
||||||
self.cpld.io_update.pulse_mu(8) # assumes 8 mu > t_SYN_CCLK
|
self.cpld.io_update.pulse_mu(int64(8)) # assumes 8 mu > t_SYN_CCLK
|
||||||
at_mu(now_mu() & ~7) # clear fine TSC again
|
at_mu(now_mu() & int64(~7)) # clear fine TSC again
|
||||||
if phase_mode != PHASE_MODE_CONTINUOUS:
|
if phase_mode != PHASE_MODE_CONTINUOUS:
|
||||||
self.set_cfr1()
|
self.set_cfr1()
|
||||||
# future IO_UPDATE will activate
|
# future IO_UPDATE will activate
|
||||||
|
@ -606,8 +654,8 @@ class AD9910:
|
||||||
data = int64(self.read64(_AD9910_REG_PROFILE0 + profile))
|
data = int64(self.read64(_AD9910_REG_PROFILE0 + profile))
|
||||||
# Extract and return fields
|
# Extract and return fields
|
||||||
ftw = int32(data)
|
ftw = int32(data)
|
||||||
pow_ = int32((data >> 32) & 0xffff)
|
pow_ = int32(data >> int64(32)) & 0xffff
|
||||||
asf = int32((data >> 48) & 0x3fff)
|
asf = int32(data >> int64(48)) & 0x3fff
|
||||||
return ftw, pow_, asf
|
return ftw, pow_, asf
|
||||||
|
|
||||||
@kernel
|
@kernel
|
||||||
|
@ -703,13 +751,13 @@ class AD9910:
|
||||||
"""Return the frequency corresponding to the given frequency tuning
|
"""Return the frequency corresponding to the given frequency tuning
|
||||||
word.
|
word.
|
||||||
"""
|
"""
|
||||||
return ftw / self.ftw_per_hz
|
return float(ftw) / self.ftw_per_hz
|
||||||
|
|
||||||
@portable
|
@portable
|
||||||
def turns_to_pow(self, turns: float) -> int32:
|
def turns_to_pow(self, turns: float) -> int32:
|
||||||
"""Return the 16-bit phase offset word corresponding to the given phase
|
"""Return the 16-bit phase offset word corresponding to the given phase
|
||||||
in turns."""
|
in turns."""
|
||||||
return int32(round(turns * 0x10000)) & int32(0xffff)
|
return round(turns * float(0x10000)) & 0xffff
|
||||||
|
|
||||||
@portable
|
@portable
|
||||||
def pow_to_turns(self, pow_: int32) -> float:
|
def pow_to_turns(self, pow_: int32) -> float:
|
||||||
|
@ -721,7 +769,7 @@ class AD9910:
|
||||||
def amplitude_to_asf(self, amplitude: float) -> int32:
|
def amplitude_to_asf(self, amplitude: float) -> int32:
|
||||||
"""Return 14-bit amplitude scale factor corresponding to given
|
"""Return 14-bit amplitude scale factor corresponding to given
|
||||||
fractional amplitude."""
|
fractional amplitude."""
|
||||||
code = int32(round(amplitude * 0x3fff))
|
code = round(amplitude * float(0x3fff))
|
||||||
if code < 0 or code > 0x3fff:
|
if code < 0 or code > 0x3fff:
|
||||||
raise ValueError("Invalid AD9910 fractional amplitude!")
|
raise ValueError("Invalid AD9910 fractional amplitude!")
|
||||||
return code
|
return code
|
||||||
|
@ -730,7 +778,7 @@ class AD9910:
|
||||||
def asf_to_amplitude(self, asf: int32) -> float:
|
def asf_to_amplitude(self, asf: int32) -> float:
|
||||||
"""Return amplitude as a fraction of full scale corresponding to given
|
"""Return amplitude as a fraction of full scale corresponding to given
|
||||||
amplitude scale factor."""
|
amplitude scale factor."""
|
||||||
return asf / float(0x3fff)
|
return float(asf) / float(0x3fff)
|
||||||
|
|
||||||
@portable
|
@portable
|
||||||
def frequency_to_ram(self, frequency: list[float], ram: list[int32]):
|
def frequency_to_ram(self, frequency: list[float], ram: list[int32]):
|
||||||
|
@ -972,10 +1020,10 @@ class AD9910:
|
||||||
Also modifies CFR2.
|
Also modifies CFR2.
|
||||||
"""
|
"""
|
||||||
self.set_cfr2(sync_validation_disable=1) # clear SMP_ERR
|
self.set_cfr2(sync_validation_disable=1) # clear SMP_ERR
|
||||||
self.cpld.io_update.pulse(1 * us)
|
self.cpld.io_update.pulse(1. * us)
|
||||||
delay(10 * us) # slack
|
self.core.delay(10. * us) # slack
|
||||||
self.set_cfr2(sync_validation_disable=0) # enable SMP_ERR
|
self.set_cfr2(sync_validation_disable=0) # enable SMP_ERR
|
||||||
self.cpld.io_update.pulse(1 * us)
|
self.cpld.io_update.pulse(1. * us)
|
||||||
|
|
||||||
@kernel
|
@kernel
|
||||||
def tune_sync_delay(self,
|
def tune_sync_delay(self,
|
||||||
|
@ -1006,7 +1054,7 @@ class AD9910:
|
||||||
next_seed = -1
|
next_seed = -1
|
||||||
for in_delay in range(search_span - 2 * window):
|
for in_delay in range(search_span - 2 * window):
|
||||||
# alternate search direction around search_seed
|
# alternate search direction around search_seed
|
||||||
if in_delay & 1:
|
if in_delay & 1 != 0:
|
||||||
in_delay = -in_delay
|
in_delay = -in_delay
|
||||||
in_delay = search_seed + (in_delay >> 1)
|
in_delay = search_seed + (in_delay >> 1)
|
||||||
if in_delay < 0 or in_delay > 31:
|
if in_delay < 0 or in_delay > 31:
|
||||||
|
@ -1014,9 +1062,9 @@ class AD9910:
|
||||||
self.set_sync(in_delay, window)
|
self.set_sync(in_delay, window)
|
||||||
self.clear_smp_err()
|
self.clear_smp_err()
|
||||||
# integrate SMP_ERR statistics for a few hundred cycles
|
# integrate SMP_ERR statistics for a few hundred cycles
|
||||||
delay(100 * us)
|
self.core.delay(100. * us)
|
||||||
err = urukul_sta_smp_err(self.cpld.sta_read())
|
err = urukul_sta_smp_err(self.cpld.sta_read())
|
||||||
delay(100 * us) # slack
|
self.core.delay(100. * us) # slack
|
||||||
if not (err >> (self.chip_select - 4)) & 1:
|
if not (err >> (self.chip_select - 4)) & 1:
|
||||||
next_seed = in_delay
|
next_seed = in_delay
|
||||||
break
|
break
|
||||||
|
@ -1028,7 +1076,7 @@ class AD9910:
|
||||||
window = max(min_window, window - 1 - margin)
|
window = max(min_window, window - 1 - margin)
|
||||||
self.set_sync(search_seed, window)
|
self.set_sync(search_seed, window)
|
||||||
self.clear_smp_err()
|
self.clear_smp_err()
|
||||||
delay(100 * us) # slack
|
self.core.delay(100. * us) # slack
|
||||||
return search_seed, window
|
return search_seed, window
|
||||||
else:
|
else:
|
||||||
break
|
break
|
||||||
|
@ -1060,21 +1108,21 @@ class AD9910:
|
||||||
# dFTW = 1, (work around negative slope)
|
# dFTW = 1, (work around negative slope)
|
||||||
self.write64(_AD9910_REG_RAMP_STEP, -1, 0)
|
self.write64(_AD9910_REG_RAMP_STEP, -1, 0)
|
||||||
# delay io_update after RTIO edge
|
# delay io_update after RTIO edge
|
||||||
t = now_mu() + 8 & ~7
|
t = now_mu() + int64(8) & int64(~7)
|
||||||
at_mu(t + delay_start)
|
at_mu(t + delay_start)
|
||||||
# assumes a maximum t_SYNC_CLK period
|
# assumes a maximum t_SYNC_CLK period
|
||||||
self.cpld.io_update.pulse_mu(16 - delay_start) # realign
|
self.cpld.io_update.pulse_mu(int64(16) - delay_start) # realign
|
||||||
# disable DRG autoclear and LRR on io_update
|
# disable DRG autoclear and LRR on io_update
|
||||||
self.set_cfr1()
|
self.set_cfr1()
|
||||||
# stop DRG
|
# stop DRG
|
||||||
self.write64(_AD9910_REG_RAMP_STEP, 0, 0)
|
self.write64(_AD9910_REG_RAMP_STEP, 0, 0)
|
||||||
at_mu(t + 0x1000 + delay_stop)
|
at_mu(t + int64(0x1000) + delay_stop)
|
||||||
self.cpld.io_update.pulse_mu(16 - delay_stop) # realign
|
self.cpld.io_update.pulse_mu(int64(16) - delay_stop) # realign
|
||||||
ftw = self.read32(_AD9910_REG_FTW) # read out effective FTW
|
ftw = self.read32(_AD9910_REG_FTW) # read out effective FTW
|
||||||
delay(100 * us) # slack
|
self.core.delay(100. * us) # slack
|
||||||
# disable DRG
|
# disable DRG
|
||||||
self.set_cfr2(drg_enable=0)
|
self.set_cfr2(drg_enable=0)
|
||||||
self.cpld.io_update.pulse_mu(8)
|
self.cpld.io_update.pulse_mu(int64(8))
|
||||||
return ftw & 1
|
return ftw & 1
|
||||||
|
|
||||||
@kernel
|
@kernel
|
||||||
|
@ -1100,14 +1148,14 @@ class AD9910:
|
||||||
t = 0
|
t = 0
|
||||||
# check whether the sync edge is strictly between i, i+2
|
# check whether the sync edge is strictly between i, i+2
|
||||||
for j in range(repeat):
|
for j in range(repeat):
|
||||||
t += self.measure_io_update_alignment(i, i + 2)
|
t += self.measure_io_update_alignment(int64(i), int64(i + 2))
|
||||||
if t != 0: # no certain edge
|
if t != 0: # no certain edge
|
||||||
continue
|
continue
|
||||||
# check left/right half: i,i+1 and i+1,i+2
|
# check left/right half: i,i+1 and i+1,i+2
|
||||||
t1 = [0, 0]
|
t1 = [0, 0]
|
||||||
for j in range(repeat):
|
for j in range(repeat):
|
||||||
t1[0] += self.measure_io_update_alignment(i, i + 1)
|
t1[0] += self.measure_io_update_alignment(int64(i), int64(i + 1))
|
||||||
t1[1] += self.measure_io_update_alignment(i + 1, i + 2)
|
t1[1] += self.measure_io_update_alignment(int64(i + 1), int64(i + 2))
|
||||||
if ((t1[0] == 0 and t1[1] == 0) or
|
if ((t1[0] == 0 and t1[1] == 0) or
|
||||||
(t1[0] == repeat and t1[1] == repeat)):
|
(t1[0] == repeat and t1[1] == repeat)):
|
||||||
# edge is not close to i + 1, can't interpret result
|
# edge is not close to i + 1, can't interpret result
|
||||||
|
|
|
@ -22,28 +22,34 @@
|
||||||
"ports": [2, 3],
|
"ports": [2, 3],
|
||||||
"clk_sel": 2
|
"clk_sel": 2
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"type": "urukul",
|
||||||
|
"dds": "ad9910",
|
||||||
|
"ports": [4, 5],
|
||||||
|
"clk_sel": 2
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"type": "sampler",
|
"type": "sampler",
|
||||||
"ports": [4]
|
"ports": [6]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"type": "dio",
|
"type": "dio",
|
||||||
"ports": [5],
|
"ports": [7],
|
||||||
"bank_direction_low": "input",
|
"bank_direction_low": "input",
|
||||||
"bank_direction_high": "output",
|
"bank_direction_high": "output",
|
||||||
"edge_counter": true
|
"edge_counter": true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"type": "grabber",
|
"type": "grabber",
|
||||||
"ports": [6]
|
"ports": [8]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"type": "fastino",
|
"type": "fastino",
|
||||||
"ports": [7]
|
"ports": [9]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"type": "phaser",
|
"type": "phaser",
|
||||||
"ports": [8]
|
"ports": [10]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,6 +7,7 @@ from artiq.coredevice.mirny import Mirny as MirnyCPLD
|
||||||
from artiq.coredevice.adf5356 import ADF5356
|
from artiq.coredevice.adf5356 import ADF5356
|
||||||
from artiq.coredevice.urukul import CPLD as UrukulCPLD
|
from artiq.coredevice.urukul import CPLD as UrukulCPLD
|
||||||
from artiq.coredevice.ad9912 import AD9912
|
from artiq.coredevice.ad9912 import AD9912
|
||||||
|
from artiq.coredevice.ad9910 import AD9910
|
||||||
from artiq.coredevice.sampler import Sampler
|
from artiq.coredevice.sampler import Sampler
|
||||||
from artiq.coredevice.edge_counter import EdgeCounter
|
from artiq.coredevice.edge_counter import EdgeCounter
|
||||||
from artiq.coredevice.grabber import Grabber
|
from artiq.coredevice.grabber import Grabber
|
||||||
|
@ -24,6 +25,8 @@ class NAC3Devices(EnvExperiment):
|
||||||
urukul0_cpld: KernelInvariant[UrukulCPLD]
|
urukul0_cpld: KernelInvariant[UrukulCPLD]
|
||||||
eeprom_urukul0: KernelInvariant[KasliEEPROM]
|
eeprom_urukul0: KernelInvariant[KasliEEPROM]
|
||||||
urukul0_ch0: KernelInvariant[AD9912]
|
urukul0_ch0: KernelInvariant[AD9912]
|
||||||
|
urukul1_cpld: KernelInvariant[UrukulCPLD]
|
||||||
|
urukul1_ch0: KernelInvariant[AD9910]
|
||||||
sampler0: KernelInvariant[Sampler]
|
sampler0: KernelInvariant[Sampler]
|
||||||
ttl0_counter: KernelInvariant[EdgeCounter]
|
ttl0_counter: KernelInvariant[EdgeCounter]
|
||||||
grabber0: KernelInvariant[Grabber]
|
grabber0: KernelInvariant[Grabber]
|
||||||
|
@ -39,6 +42,8 @@ class NAC3Devices(EnvExperiment):
|
||||||
self.setattr_device("urukul0_cpld")
|
self.setattr_device("urukul0_cpld")
|
||||||
self.setattr_device("eeprom_urukul0")
|
self.setattr_device("eeprom_urukul0")
|
||||||
self.setattr_device("urukul0_ch0")
|
self.setattr_device("urukul0_ch0")
|
||||||
|
self.setattr_device("urukul1_cpld")
|
||||||
|
self.setattr_device("urukul1_ch0")
|
||||||
self.setattr_device("sampler0")
|
self.setattr_device("sampler0")
|
||||||
self.setattr_device("ttl0_counter")
|
self.setattr_device("ttl0_counter")
|
||||||
self.setattr_device("grabber0")
|
self.setattr_device("grabber0")
|
||||||
|
|
Loading…
Reference in New Issue