coredevice: configurable initial backing state

Several core device drivers maintain a copy of some device state.
Since this copy is not transferred between experiments this backing state
can be different from device state when a new experiment is started.

This commit adds support for injecting initial backing state into
experiments via the device database and sets initial backing state where
known.

ad53xx (zotino): spi2 xfer_duration
novogorny: pgia gains
sampler: pgia gains, spi2 pgia and adc xfer_duration
suservo: pgia gains, spi2 pgia xfer_duration
urukul: cpld cfg (partial: rf_sw), attenuator register
spi2: div/length for xfer_duration

close #1003
This commit is contained in:
Robert Jördens 2018-05-21 18:28:19 +02:00
parent 4e5fe672e7
commit 4e863b32a1
6 changed files with 74 additions and 17 deletions

View File

@ -110,7 +110,8 @@ class AD53xx:
valid) valid)
:param vref: DAC reference voltage (default: 5.) :param vref: DAC reference voltage (default: 5.)
:param offset_dacs: Initial register value for the two offset DACs, device :param offset_dacs: Initial register value for the two offset DACs, device
dependent and must be set correctly for correct voltage to mu conversions dependent and must be set correctly for correct voltage to mu conversions.
Knowledge of his state is not transferred between experiments.
(default: 8192) (default: 8192)
:param core_device: Core device name (default: "core") :param core_device: Core device name (default: "core")
""" """
@ -121,6 +122,7 @@ class AD53xx:
chip_select=1, div_write=4, div_read=16, vref=5., chip_select=1, div_write=4, div_read=16, vref=5.,
offset_dacs=8192, core="core"): offset_dacs=8192, core="core"):
self.bus = dmgr.get(spi_device) self.bus = dmgr.get(spi_device)
self.bus.update_xfer_duration_mu(div_write, 24)
if ldac_device is None: if ldac_device is None:
self.ldac = _DummyTTL() self.ldac = _DummyTTL()
else: else:

View File

@ -69,17 +69,20 @@ class Novogorny:
:param spi_device: SPI bus device name :param spi_device: SPI bus device name
:param cnv_device: CNV RTIO TTLOut channel name :param cnv_device: CNV RTIO TTLOut channel name
:param div: SPI clock divider (default: 8) :param div: SPI clock divider (default: 8)
:param gains: Initial value for PGIA gains shift register
(default: 0x0000). Knowledge of this state is not transferred
between experiments.
:param core_device: Core device name :param core_device: Core device name
""" """
kernel_invariants = {"bus", "core", "cnv", "div", "v_ref"} kernel_invariants = {"bus", "core", "cnv", "div", "v_ref"}
def __init__(self, dmgr, spi_device, cnv_device, div=8, def __init__(self, dmgr, spi_device, cnv_device, div=8, gains=0x0000,
core_device="core"): core_device="core"):
self.bus = dmgr.get(spi_device) self.bus = dmgr.get(spi_device)
self.core = dmgr.get(core_device) self.core = dmgr.get(core_device)
self.cnv = dmgr.get(cnv_device) self.cnv = dmgr.get(cnv_device)
self.div = div self.div = div
self.gains = 0x0000 self.gains = gains
self.v_ref = 5. # 5 Volt reference self.v_ref = 5. # 5 Volt reference
@kernel @kernel

View File

@ -45,18 +45,23 @@ class Sampler:
:param spi_pgia_device: PGIA SPI bus device name :param spi_pgia_device: PGIA SPI bus device name
:param cnv_device: CNV RTIO TTLOut channel name :param cnv_device: CNV RTIO TTLOut channel name
:param div: SPI clock divider (default: 8) :param div: SPI clock divider (default: 8)
:param gains: Initial value for PGIA gains shift register
(default: 0x0000). Knowledge of this state is not transferred
between experiments.
:param core_device: Core device name :param core_device: Core device name
""" """
kernel_invariants = {"bus_adc", "bus_pgia", "core", "cnv", "div"} kernel_invariants = {"bus_adc", "bus_pgia", "core", "cnv", "div"}
def __init__(self, dmgr, spi_adc_device, spi_pgia_device, cnv_device, def __init__(self, dmgr, spi_adc_device, spi_pgia_device, cnv_device,
div=8, core_device="core"): div=8, gains=0x0000, core_device="core"):
self.bus_adc = dmgr.get(spi_adc_device) self.bus_adc = dmgr.get(spi_adc_device)
self.bus_adc.update_xfer_duration_mu(div, 32)
self.bus_pgia = dmgr.get(spi_pgia_device) self.bus_pgia = dmgr.get(spi_pgia_device)
self.bus_pgia.update_xfer_duration_mu(div, 16)
self.core = dmgr.get(core_device) self.core = dmgr.get(core_device)
self.cnv = dmgr.get(cnv_device) self.cnv = dmgr.get(cnv_device)
self.div = div self.div = div
self.gains = 0x0000 self.gains = gains
@kernel @kernel
def init(self): def init(self):

View File

@ -57,16 +57,20 @@ class SPIMaster:
register. register.
:param channel: RTIO channel number of the SPI bus to control. :param channel: RTIO channel number of the SPI bus to control.
:param div: Initial CLK divider, see also: :meth:`update_xfer_duration_mu`
:param length: Initial transfer length, see also:
:meth:`update_xfer_duration_mu`
:param core_device: Core device name
""" """
kernel_invariants = {"core", "ref_period_mu", "channel"} kernel_invariants = {"core", "ref_period_mu", "channel"}
def __init__(self, dmgr, channel, core_device="core"): def __init__(self, dmgr, channel, div=0, length=0, core_device="core"):
self.core = dmgr.get(core_device) self.core = dmgr.get(core_device)
self.ref_period_mu = self.core.seconds_to_mu( self.ref_period_mu = self.core.seconds_to_mu(
self.core.coarse_ref_period) self.core.coarse_ref_period)
assert self.ref_period_mu == self.core.ref_multiplier assert self.ref_period_mu == self.core.ref_multiplier
self.channel = channel self.channel = channel
self.xfer_duration_mu = 2*self.ref_period_mu self.update_xfer_duration_mu(div, length)
@portable @portable
def frequency_to_div(self, f): def frequency_to_div(self, f):
@ -162,11 +166,31 @@ class SPIMaster:
raise ValueError("Invalid SPI transfer length") raise ValueError("Invalid SPI transfer length")
if div > 257 or div < 2: if div > 257 or div < 2:
raise ValueError("Invalid SPI clock divider") raise ValueError("Invalid SPI clock divider")
self.xfer_duration_mu = ((length + 1)*div + 1)*self.ref_period_mu
rtio_output(now_mu(), self.channel, SPI_CONFIG_ADDR, flags | rtio_output(now_mu(), self.channel, SPI_CONFIG_ADDR, flags |
((length - 1) << 8) | ((div - 2) << 16) | (cs << 24)) ((length - 1) << 8) | ((div - 2) << 16) | (cs << 24))
self.update_xfer_duration_mu(div, length)
delay_mu(self.ref_period_mu) delay_mu(self.ref_period_mu)
@portable
def update_xfer_duration_mu(self, div, length):
"""Calculate and set the transfer duration.
This method updates the SPI transfer duration which is used
in :meth:`write` to advance the timeline.
Use this method (and avoid having to call :meth:`set_config_mu`)
when the divider and transfer length have been configured
(using :meth:`set_config` or :meth:`set_config_mu`) by previous
experiments and are known.
This method is portable and can also be called from e.g.
``__init__``.
:param div: SPI clock divider (see: :meth:`set_config_mu`)
:param length: SPI transfer length (see: :meth:`set_config_mu`)
"""
self.xfer_duration_mu = ((length + 1)*div + 1)*self.ref_period_mu
@kernel @kernel
def write(self, data): def write(self, data):
"""Write SPI data to shift register register and start transfer. """Write SPI data to shift register register and start transfer.

View File

@ -26,6 +26,9 @@ class SUServo:
Urukul Urukul
:param dds1_device: Name of the AD9910 device for the DDS on the second :param dds1_device: Name of the AD9910 device for the DDS on the second
Urukul Urukul
:param gains: Initial value for PGIA gains shift register
(default: 0x0000). Knowledge of this state is not transferred
between experiments.
:param core_device: Core device name :param core_device: Core device name
""" """
kernel_invariants = {"channel", "core", "pgia", "cpld0", "cpld1", kernel_invariants = {"channel", "core", "pgia", "cpld0", "cpld1",
@ -34,16 +37,17 @@ class SUServo:
def __init__(self, dmgr, channel, pgia_device, def __init__(self, dmgr, channel, pgia_device,
cpld0_device, cpld1_device, cpld0_device, cpld1_device,
dds0_device, dds1_device, dds0_device, dds1_device,
core_device="core"): gains=0x0000, core_device="core"):
self.core = dmgr.get(core_device) self.core = dmgr.get(core_device)
self.pgia = dmgr.get(pgia_device) self.pgia = dmgr.get(pgia_device)
self.pgia.update_xfer_duration_mu(div=4, length=16)
self.dds0 = dmgr.get(dds0_device) self.dds0 = dmgr.get(dds0_device)
self.dds1 = dmgr.get(dds1_device) self.dds1 = dmgr.get(dds1_device)
self.cpld0 = dmgr.get(cpld0_device) self.cpld0 = dmgr.get(cpld0_device)
self.cpld1 = dmgr.get(cpld1_device) self.cpld1 = dmgr.get(cpld1_device)
self.channel = channel self.channel = channel
self.gains = 0x0000 self.gains = gains
self.ref_period_mu = self.core.seconds_to_mu( self.ref_period_mu = self.core.seconds_to_mu(
self.core.coarse_ref_period) self.core.coarse_ref_period)
assert self.ref_period_mu == self.core.ref_multiplier assert self.ref_period_mu == self.core.ref_multiplier

View File

@ -119,14 +119,18 @@ class CPLD:
MMCX or ob-board XO clock. 1 corresponds to the front panel SMA. MMCX or ob-board XO clock. 1 corresponds to the front panel SMA.
:param sync_sel: SYNC clock selection. 0 corresponds to SYNC clock over EEM :param sync_sel: SYNC clock selection. 0 corresponds to SYNC clock over EEM
from FPGA. 1 corresponds to SYNC clock from DDS0. from FPGA. 1 corresponds to SYNC clock from DDS0.
:param rf_sw: Initial CPLD RF switch register setting (default: 0x0).
Knowledge of this state is not transferred between experiments.
:param att: Initial attenuator setting shift register (default:
0x00000000). See also: :meth:`set_all_att_mu`. Knowledge of this state
is not transferred between experiments.
:param core_device: Core device name :param core_device: Core device name
""" """
kernel_invariants = {"refclk", "bus", "core", "io_update"} kernel_invariants = {"refclk", "bus", "core", "io_update"}
def __init__(self, dmgr, spi_device, io_update_device=None, def __init__(self, dmgr, spi_device, io_update_device=None,
dds_reset_device=None, dds_reset_device=None, sync_sel=0, clk_sel=0, rf_sw=0,
sync_sel=0, clk_sel=0, refclk=125e6, att=0x00000000, core_device="core"):
refclk=125e6, core_device="core"):
self.core = dmgr.get(core_device) self.core = dmgr.get(core_device)
self.refclk = refclk self.refclk = refclk
@ -139,10 +143,10 @@ class CPLD:
if dds_reset_device is not None: if dds_reset_device is not None:
self.dds_reset = dmgr.get(dds_reset_device) self.dds_reset = dmgr.get(dds_reset_device)
self.cfg_reg = urukul_cfg(rf_sw=0, led=0, profile=0, self.cfg_reg = urukul_cfg(rf_sw=rf_sw, led=0, profile=0,
io_update=0, mask_nu=0, clk_sel=clk_sel, io_update=0, mask_nu=0, clk_sel=clk_sel,
sync_sel=sync_sel, rst=0, io_rst=0) sync_sel=sync_sel, rst=0, io_rst=0)
self.att_reg = 0 self.att_reg = att
@kernel @kernel
def cfg_write(self, cfg): def cfg_write(self, cfg):
@ -226,16 +230,28 @@ class CPLD:
def set_att_mu(self, channel, att): def set_att_mu(self, channel, att):
"""Set digital step attenuator in machine units. """Set digital step attenuator in machine units.
This method will write the attenuator settings of all four channels.
:param channel: Attenuator channel (0-3). :param channel: Attenuator channel (0-3).
:param att: Digital attenuation setting: :param att: Digital attenuation setting:
255 minimum attenuation, 0 maximum attenuation (31.5 dB) 255 minimum attenuation, 0 maximum attenuation (31.5 dB)
""" """
a = self.att_reg & ~(0xff << (channel * 8)) a = self.att_reg & ~(0xff << (channel * 8))
a |= att << (channel * 8) a |= att << (channel * 8)
self.set_all_att_mu(a)
@kernel
def set_all_att_mu(self, att_reg):
"""Set all four digital step attenuators (in machine units).
.. seealso:: :meth:`set_att_mu`
:param att_reg: Attenuator setting string (32 bit)
"""
self.bus.set_config_mu(SPI_CONFIG | spi.SPI_END, 32, self.bus.set_config_mu(SPI_CONFIG | spi.SPI_END, 32,
SPIT_ATT_WR, CS_ATT) SPIT_ATT_WR, CS_ATT)
self.bus.write(a) self.bus.write(att_reg)
self.att_reg = a self.att_reg = att_reg
@kernel @kernel
def set_att(self, channel, att): def set_att(self, channel, att):
@ -251,6 +267,9 @@ class CPLD:
def get_att_mu(self): def get_att_mu(self):
"""Return the digital step attenuator settings in machine units. """Return the digital step attenuator settings in machine units.
This method will also (as a side effect) write the attenuator
settings of all four channels.
:return: 32 bit attenuator settings :return: 32 bit attenuator settings
""" """
self.bus.set_config_mu(SPI_CONFIG | spi.SPI_END | spi.SPI_INPUT, 32, self.bus.set_config_mu(SPI_CONFIG | spi.SPI_END | spi.SPI_INPUT, 32,