ad5360: port to spi2

* kc705 nist_clock target gateware
* coredevice driver
* moninj code
* test/example/device_db

This is untested as we don't have a AD5360 board right now.
Will be tested with Zotino v1.1

m-labs/artiq#926
This commit is contained in:
Robert Jördens 2018-02-22 08:38:56 +00:00 committed by Robert Jordens
parent d83ae0bc6a
commit f8e6b4f4e3
5 changed files with 51 additions and 39 deletions

View File

@ -6,14 +6,16 @@ time is an error.
""" """
from artiq.language.core import (kernel, portable, delay_mu, delay) from artiq.language.core import (kernel, portable, delay_mu, delay, now_mu,
at_mu)
from artiq.language.units import ns, us from artiq.language.units import ns, us
from artiq.coredevice import spi from artiq.coredevice import spi2 as spi
# Designed from the data sheets and somewhat after the linux kernel # Designed from the data sheets and somewhat after the linux kernel
# iio driver. # iio driver.
_AD5360_SPI_CONFIG = (0*spi.SPI_OFFLINE | 0*spi.SPI_CS_POLARITY | _AD5360_SPI_CONFIG = (0*spi.SPI_OFFLINE | 1*spi.SPI_END |
0*spi.SPI_INPUT | 0*spi.SPI_CS_POLARITY |
0*spi.SPI_CLK_POLARITY | 1*spi.SPI_CLK_PHASE | 0*spi.SPI_CLK_POLARITY | 1*spi.SPI_CLK_PHASE |
0*spi.SPI_LSB_FIRST | 0*spi.SPI_HALF_DUPLEX) 0*spi.SPI_LSB_FIRST | 0*spi.SPI_HALF_DUPLEX)
@ -27,6 +29,7 @@ _AD5360_CMD_SPECIAL = 0 << 22
def _AD5360_WRITE_CHANNEL(c): def _AD5360_WRITE_CHANNEL(c):
return (c + 8) << 16 return (c + 8) << 16
_AD5360_SPECIAL_NOP = 0 << 16 _AD5360_SPECIAL_NOP = 0 << 16
_AD5360_SPECIAL_CONTROL = 1 << 16 _AD5360_SPECIAL_CONTROL = 1 << 16
_AD5360_SPECIAL_OFS0 = 2 << 16 _AD5360_SPECIAL_OFS0 = 2 << 16
@ -38,6 +41,7 @@ _AD5360_SPECIAL_READ = 5 << 16
def _AD5360_READ_CHANNEL(ch): def _AD5360_READ_CHANNEL(ch):
return (ch + 8) << 7 return (ch + 8) << 7
_AD5360_READ_X1A = 0x000 << 7 _AD5360_READ_X1A = 0x000 << 7
_AD5360_READ_X1B = 0x040 << 7 _AD5360_READ_X1B = 0x040 << 7
_AD5360_READ_OFFSET = 0x080 << 7 _AD5360_READ_OFFSET = 0x080 << 7
@ -57,31 +61,34 @@ class AD5360:
(optional). Needs to be explicitly initialized to high. (optional). Needs to be explicitly initialized to high.
:param chip_select: Value to drive on the chip select lines :param chip_select: Value to drive on the chip select lines
during transactions. during transactions.
:param div_write: SPI clock divider during writes
:param div_read: SPI clock divider during reads
""" """
kernel_invariants = {"bus", "core", "chip_select", "div_read", "div_write"}
def __init__(self, dmgr, spi_device, ldac_device=None, chip_select=1): def __init__(self, dmgr, spi_device, ldac_device=None, chip_select=1,
div_write=4, div_read=7):
self.core = dmgr.get("core") self.core = dmgr.get("core")
self.bus = dmgr.get(spi_device) self.bus = dmgr.get(spi_device)
if ldac_device is not None: if ldac_device is not None:
self.ldac = dmgr.get(ldac_device) self.ldac = dmgr.get(ldac_device)
self.chip_select = chip_select self.chip_select = chip_select
# write: 2*8ns >= 10ns = t_6 (clk falling to cs_n rising)
# 4*8ns >= 20ns = t_1 (clk cycle time)
self.div_write = div_write
# read: 4*8*ns >= 25ns = t_22 (clk falling to miso valid)
self.div_read = div_read
@kernel @kernel
def setup_bus(self, write_div=4, read_div=7): def setup_bus(self):
"""Configure the SPI bus and the SPI transaction parameters """Configure the SPI bus and the SPI transaction parameters
for this device. This method has to be called before any other method for this device. This method has to be called before any other method
if the bus has been used to access a different device in the meantime. if the bus has been used to access a different device in the meantime.
This method advances the timeline by the duration of two This method advances the timeline by one coarse RTIO cycle.
RTIO-to-Wishbone bus transactions.
:param write_div: Write clock divider.
:param read_div: Read clock divider.
""" """
# write: 2*8ns >= 10ns = t_6 (clk falling to cs_n rising) self.bus.set_config_mu(_AD5360_SPI_CONFIG, 24, self.div_write,
# read: 4*8*ns >= 25ns = t_22 (clk falling to miso valid) self.chip_select)
self.bus.set_config_mu(_AD5360_SPI_CONFIG, write_div, read_div)
self.bus.set_xfer(self.chip_select, 24, 0)
@kernel @kernel
def write(self, data): def write(self, data):
@ -126,8 +133,8 @@ class AD5360:
def read_channel_sync(self, channel=0, op=_AD5360_READ_X1A): def read_channel_sync(self, channel=0, op=_AD5360_READ_X1A):
"""Read a channel register. """Read a channel register.
This method advances the timeline by the duration of :meth:`write` plus This method advances the timeline by the duration of two :meth:`write`
three RTIO-to-Wishbone transactions. plus two coarse RTIO cycles.
:param channel: Channel number to read from. :param channel: Channel number to read from.
:param op: Operation to perform, one of :const:`_AD5360_READ_X1A`, :param op: Operation to perform, one of :const:`_AD5360_READ_X1A`,
@ -138,11 +145,13 @@ class AD5360:
channel &= 0x3f channel &= 0x3f
self.write(_AD5360_CMD_SPECIAL | _AD5360_SPECIAL_READ | op | self.write(_AD5360_CMD_SPECIAL | _AD5360_SPECIAL_READ | op |
_AD5360_READ_CHANNEL(channel)) _AD5360_READ_CHANNEL(channel))
self.bus.set_xfer(self.chip_select, 0, 24) self.bus.set_config_mu(_AD5360_SPI_CONFIG | spi.SPI_INPUT, 24,
self.div_read, self.chip_select)
delay(270*ns) # t_21 min sync high in readback
self.write(_AD5360_CMD_SPECIAL | _AD5360_SPECIAL_NOP) self.write(_AD5360_CMD_SPECIAL | _AD5360_SPECIAL_NOP)
self.bus.read_async() self.bus.set_config_mu(_AD5360_SPI_CONFIG, 24,
self.bus.set_xfer(self.chip_select, 24, 0) self.div_write, self.chip_select)
return self.bus.input_async() & 0xffff return self.bus.read() & 0xffff
@kernel @kernel
def load(self): def load(self):
@ -168,15 +177,13 @@ class AD5360:
:const:`_AD5360_CMD_OFFSET`, :const:`_AD5360_CMD_GAIN` :const:`_AD5360_CMD_OFFSET`, :const:`_AD5360_CMD_GAIN`
(default: :const:`_AD5360_CMD_DATA`). (default: :const:`_AD5360_CMD_DATA`).
""" """
t0 = now_mu()
# t10 max busy low for one channel
t_10 = self.core.seconds_to_mu(1.5*us)
# compensate all delays that will be applied # compensate all delays that will be applied
delay_mu(-len(values)*(self.bus.xfer_period_mu + delay_mu(-len(values)*self.bus.xfer_period_mu-t_10)
self.bus.write_period_mu +
self.bus.ref_period_mu) -
3*self.bus.ref_period_mu -
self.core.seconds_to_mu(1.5*us))
for i in range(len(values)): for i in range(len(values)):
self.write_channel(i, values[i], op) self.write_channel(i, values[i], op)
delay_mu(3*self.bus.ref_period_mu + # latency alignment ttl to spi delay_mu(t_10)
self.core.seconds_to_mu(1.5*us)) # t10 max busy low for one channel
self.load() self.load()
delay_mu(-2*self.bus.ref_period_mu) # load(), t13 at_mu(t0)

View File

@ -173,7 +173,7 @@ device_db = {
}, },
"spi_zotino": { "spi_zotino": {
"type": "local", "type": "local",
"module": "artiq.coredevice.spi", "module": "artiq.coredevice.spi2",
"class": "SPIMaster", "class": "SPIMaster",
"arguments": {"channel": 30} "arguments": {"channel": 30}
}, },
@ -187,7 +187,12 @@ device_db = {
"type": "local", "type": "local",
"module": "artiq.coredevice.ad5360", "module": "artiq.coredevice.ad5360",
"class": "AD5360", "class": "AD5360",
"arguments": {"spi_device": "spi_zotino", "ldac_device": "ttl_zotino_ldac"} "arguments": {
"spi_device": "spi_zotino",
"ldac_device": "ttl_zotino_ldac",
"div_write": 30,
"div_read": 40
}
}, },
"spi_urukul": { "spi_urukul": {

View File

@ -13,7 +13,7 @@ class AD5360Test(EnvExperiment):
self.core.reset() self.core.reset()
delay(5*ms) # build slack for shift register set delay(5*ms) # build slack for shift register set
self.fmcdio_dirctl.set(self, 0x00008800) self.fmcdio_dirctl.set(self, 0x00008800)
self.dac.setup_bus(write_div=30, read_div=40) self.dac.setup_bus()
self.dac.write_offsets() self.dac.write_offsets()
self.led.on() self.led.on()
delay(400*us) delay(400*us)

View File

@ -1,6 +1,6 @@
from migen import * from migen import *
from artiq.coredevice.spi import SPI_XFER_ADDR, SPI_DATA_ADDR from artiq.coredevice.spi2 import SPI_CONFIG_ADDR, SPI_DATA_ADDR
from artiq.coredevice.ad5360 import _AD5360_CMD_DATA, _AD5360_WRITE_CHANNEL from artiq.coredevice.ad5360 import _AD5360_CMD_DATA, _AD5360_WRITE_CHANNEL
@ -28,14 +28,14 @@ class AD5360Monitor(Module):
selected = Signal() selected = Signal()
if cs_onehot: if cs_onehot:
self.sync.rio_phy += [ self.sync.rio_phy += [
If(spi_oif.stb & (spi_oif.address == SPI_XFER_ADDR), If(spi_oif.stb & (spi_oif.address == SPI_CONFIG_ADDR),
selected.eq(spi_oif.data[cs_no]) selected.eq(spi_oif.data[24 + cs_no])
) )
] ]
else: else:
self.sync.rio_phy += [ self.sync.rio_phy += [
If(spi_oif.stb & (spi_oif.address == SPI_XFER_ADDR), If(spi_oif.stb & (spi_oif.address == SPI_CONFIG_ADDR),
selected.eq(spi_oif.data[:16] == cs_no) selected.eq(spi_oif.data[24:] == cs_no)
) )
] ]

View File

@ -341,8 +341,8 @@ class NIST_CLOCK(_StandaloneBase):
self.submodules += phy self.submodules += phy
rtio_channels.append(rtio.Channel.from_phy(phy)) rtio_channels.append(rtio.Channel.from_phy(phy))
sdac_phy = spi.SPIMaster(self.platform.request("zotino_spi_p"), sdac_phy = spi2.SPIMaster(self.platform.request("zotino_spi_p"),
self.platform.request("zotino_spi_n")) self.platform.request("zotino_spi_n"))
self.submodules += sdac_phy self.submodules += sdac_phy
rtio_channels.append(rtio.Channel.from_phy(sdac_phy, ififo_depth=4)) rtio_channels.append(rtio.Channel.from_phy(sdac_phy, ififo_depth=4))