novogorny: add coredevice driver and test with Kasli

m-labs/artiq#687
This commit is contained in:
Robert Jördens 2018-02-22 15:41:30 +00:00 committed by Robert Jordens
parent 74517107f0
commit 1452cd7447
4 changed files with 182 additions and 2 deletions

View File

@ -0,0 +1,174 @@
from numpy import int32, int64
from artiq.language.core import kernel, delay, portable
from artiq.language.units import us, ns
from artiq.coredevice.ad9912_reg import *
from artiq.coredevice import spi2 as spi
from artiq.coredevice import urukul
SPI_CONFIG = (0*spi.SPI_OFFLINE | 0*spi.SPI_END |
0*spi.SPI_INPUT | 0*spi.SPI_CS_POLARITY |
0*spi.SPI_CLK_POLARITY | 0*spi.SPI_CLK_PHASE |
0*spi.SPI_LSB_FIRST | 0*spi.SPI_HALF_DUPLEX)
SPI_CS_ADC = 1
SPI_CS_SR = 2
@portable
def adc_ctrl(channel=1, softspan=0b111, valid=1):
"""Build a LTC2335-16 control word"""
return (valid << 7) | (channel << 3) | softspan
@portable
def adc_softspan(data):
"""Return the softspan configuration index from a result packet"""
return data & 0x7
@portable
def adc_channel(data):
"""Return the channel index from a result packet"""
return (data >> 3) & 0x7
@portable
def adc_data(data):
"""Return the ADC value from a result packet"""
return (data >> 8) & 0xffff
@portable
def adc_value(data, v_ref=5.):
"""Convert a ADC result packet to SI units (Volt)"""
softspan = adc_softspan(data)
data = adc_data(data)
if softspan & 4:
g3 = 2
else:
g3 = 1
if softspan & 2:
g2 = 1 << 15
else:
g2 = 1 << 16
if softspan & 1:
g1 = 1000
else:
g1 = 1023
data = -(data & g2) + (data & ~g2)
v_per_lsb = v_ref*(1250*g3)/(g1*g2)
return data*v_per_lsb
class Novogorny:
"""Novogorny ADC.
Controls the LTC2335-16 8 channel ADC with SPI interface and
the switchable gain instrumentation amplifiers using a shift
register.
:param spi_device: SPI bus device name
:param conv_device: CONV RTIO TTLOut channel name
:param div: SPI clock divider (default: 8)
:param core_device: Core device name
"""
kernel_invariants = {"bus", "core", "conv", "div", "v_ref"}
def __init__(self, dmgr, spi_device, conv_device, div=8,
core_device="core"):
self.bus = dmgr.get(spi_device)
self.core = dmgr.get(core_device)
self.conv = dmgr.get(conv_device)
self.div = div
self.gains = 0x0000
self.v_ref = 5. # 5 Volt reference
@kernel
def set_gain_mu(self, channel, gain):
"""Set instrumentation amplifier gain of a channel.
The four gain settings (0, 1, 2, 3) corresponds to gains of
(1, 10, 100, 1000) respectively.
:param channel: Channel index
:param gain: Gain setting
"""
self.gains &= ~(0b11 << (channel*2))
self.gains |= gain << (channel*2)
self.bus.set_config_mu(SPI_CONFIG | spi.SPI_END,
2, self.div, SPI_CS_SR)
self.bus.write(self.gains << 16)
@kernel
def configure(self, data):
"""Set up the ADC sequencer.
:param data: List of 8 bit control words to write into the sequencer
table.
"""
if len(data) > 1:
self.bus.set_config_mu(SPI_CONFIG,
8, self.div, SPI_CS_ADC)
for i in range(len(data) - 1):
self.bus.write(data[i] << 24)
self.bus.set_config_mu(SPI_CONFIG | spi.SPI_END,
8, self.div, SPI_CS_ADC)
self.bus.write(data[len(data) - 1] << 24)
@kernel
def sample_mu(self, next_ctrl=0):
"""Acquire a sample:
Perform a conversion and transfer the sample.
:param next_ctrl: ADC control word for the next sample
:return: The ADC result packet (machine units)
"""
self.conv.pulse(40*ns) # t_CNVH
delay(560*ns) # t_CONV max
self.bus.set_config_mu(SPI_CONFIG | spi.SPI_INPUT | spi.SPI_END,
24, self.div, SPI_CS_ADC)
self.bus.write(next_ctrl << 24)
return self.bus.read()
@kernel
def sample(self, next_ctrl=0):
"""Acquire a sample
.. seealso:: :meth:`sample_mu`
:param next_ctrl: ADC control word for the next sample
:return: The ADC result packet (Volt)
"""
return adc_value(self.sample_mu(), self.v_ref)
@kernel
def burst_mu(self, data, dt_mu, ctrl=0):
"""Acquire a burst of samples.
If the burst is too long and the sample rate too high, there will be
RTIO input overflows.
High sample rates lead to gain errors since the impedance between the
instrumentation amplifier and the ADC is high.
:param data: List of data values to write result packets into.
In machine units.
:param dt: Sample interval in machine units.
:param ctrl: ADC control word to write during each result packet
transfer.
"""
self.bus.set_config_mu(SPI_CONFIG | spi.SPI_INPUT | spi.SPI_END,
24, self.div, SPI_CS_ADC)
for i in range(len(data)):
t0 = now_mu()
self.conv.pulse(40*ns) # t_CNVH
delay(560*ns) # t_CONV max
self.bus.write(ctrl << 24)
at_mu(t0 + dt_mu)
for i in range(len(data)):
data[i] = self.bus.read()

View File

@ -16,7 +16,7 @@ __all__ = [
"SPI_OFFLINE", "SPI_END", "SPI_INPUT", "SPI_OFFLINE", "SPI_END", "SPI_INPUT",
"SPI_CS_POLARITY", "SPI_CLK_POLARITY", "SPI_CLK_PHASE", "SPI_CS_POLARITY", "SPI_CLK_POLARITY", "SPI_CLK_PHASE",
"SPI_LSB_FIRST", "SPI_HALF_DUPLEX", "SPI_LSB_FIRST", "SPI_HALF_DUPLEX",
"SPIMaster", "NRTSPIMaster" "SPIMaster"
] ]
SPI_DATA_ADDR = 0 SPI_DATA_ADDR = 0

View File

@ -269,7 +269,7 @@ class Opticlock(_StandaloneBase):
phy = spi2.SPIMaster(self.platform.request("eem3_spi_p"), phy = spi2.SPIMaster(self.platform.request("eem3_spi_p"),
self.platform.request("eem3_spi_n")) self.platform.request("eem3_spi_n"))
self.submodules += phy self.submodules += phy
rtio_channels.append(rtio.Channel.from_phy(phy, ififo_depth=4)) rtio_channels.append(rtio.Channel.from_phy(phy, ififo_depth=16))
for signal in "conv".split(): for signal in "conv".split():
pads = platform.request("eem3_{}".format(signal)) pads = platform.request("eem3_{}".format(signal))

View File

@ -75,6 +75,12 @@ These drivers are for the core device and the peripherals closely integrated int
.. automodule:: artiq.coredevice.sawg .. automodule:: artiq.coredevice.sawg
:members: :members:
:mod:`artiq.coredevice.novogorny` module
----------------------------------------
.. automodule:: artiq.coredevice.novogorny
:members:
:mod:`artiq.coredevice.urukul` module :mod:`artiq.coredevice.urukul` module
------------------------------------- -------------------------------------