From f8e6b4f4e3d96dfca4464f009e588825efcf8a6a Mon Sep 17 00:00:00 2001 From: Robert Jordens Date: Thu, 22 Feb 2018 08:38:56 +0000 Subject: [PATCH] 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 --- artiq/coredevice/ad5360.py | 63 ++++++++++--------- artiq/examples/kc705_nist_clock/device_db.py | 9 ++- .../kc705_nist_clock/repository/ad5360.py | 2 +- artiq/gateware/rtio/phy/ad5360_monitor.py | 12 ++-- artiq/gateware/targets/kc705.py | 4 +- 5 files changed, 51 insertions(+), 39 deletions(-) diff --git a/artiq/coredevice/ad5360.py b/artiq/coredevice/ad5360.py index c470fbec2..65e67aa07 100644 --- a/artiq/coredevice/ad5360.py +++ b/artiq/coredevice/ad5360.py @@ -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.coredevice import spi +from artiq.coredevice import spi2 as spi # Designed from the data sheets and somewhat after the linux kernel # 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_LSB_FIRST | 0*spi.SPI_HALF_DUPLEX) @@ -27,6 +29,7 @@ _AD5360_CMD_SPECIAL = 0 << 22 def _AD5360_WRITE_CHANNEL(c): return (c + 8) << 16 + _AD5360_SPECIAL_NOP = 0 << 16 _AD5360_SPECIAL_CONTROL = 1 << 16 _AD5360_SPECIAL_OFS0 = 2 << 16 @@ -38,6 +41,7 @@ _AD5360_SPECIAL_READ = 5 << 16 def _AD5360_READ_CHANNEL(ch): return (ch + 8) << 7 + _AD5360_READ_X1A = 0x000 << 7 _AD5360_READ_X1B = 0x040 << 7 _AD5360_READ_OFFSET = 0x080 << 7 @@ -57,31 +61,34 @@ class AD5360: (optional). Needs to be explicitly initialized to high. :param chip_select: Value to drive on the chip select lines 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.bus = dmgr.get(spi_device) if ldac_device is not None: self.ldac = dmgr.get(ldac_device) 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 - def setup_bus(self, write_div=4, read_div=7): + def setup_bus(self): """Configure the SPI bus and the SPI transaction parameters 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. - This method advances the timeline by the duration of two - RTIO-to-Wishbone bus transactions. - - :param write_div: Write clock divider. - :param read_div: Read clock divider. + This method advances the timeline by one coarse RTIO cycle. """ - # write: 2*8ns >= 10ns = t_6 (clk falling to cs_n rising) - # read: 4*8*ns >= 25ns = t_22 (clk falling to miso valid) - self.bus.set_config_mu(_AD5360_SPI_CONFIG, write_div, read_div) - self.bus.set_xfer(self.chip_select, 24, 0) + self.bus.set_config_mu(_AD5360_SPI_CONFIG, 24, self.div_write, + self.chip_select) @kernel def write(self, data): @@ -126,8 +133,8 @@ class AD5360: def read_channel_sync(self, channel=0, op=_AD5360_READ_X1A): """Read a channel register. - This method advances the timeline by the duration of :meth:`write` plus - three RTIO-to-Wishbone transactions. + This method advances the timeline by the duration of two :meth:`write` + plus two coarse RTIO cycles. :param channel: Channel number to read from. :param op: Operation to perform, one of :const:`_AD5360_READ_X1A`, @@ -138,11 +145,13 @@ class AD5360: channel &= 0x3f self.write(_AD5360_CMD_SPECIAL | _AD5360_SPECIAL_READ | op | _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.bus.read_async() - self.bus.set_xfer(self.chip_select, 24, 0) - return self.bus.input_async() & 0xffff + self.bus.set_config_mu(_AD5360_SPI_CONFIG, 24, + self.div_write, self.chip_select) + return self.bus.read() & 0xffff @kernel def load(self): @@ -168,15 +177,13 @@ class AD5360: :const:`_AD5360_CMD_OFFSET`, :const:`_AD5360_CMD_GAIN` (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 - delay_mu(-len(values)*(self.bus.xfer_period_mu + - self.bus.write_period_mu + - self.bus.ref_period_mu) - - 3*self.bus.ref_period_mu - - self.core.seconds_to_mu(1.5*us)) + delay_mu(-len(values)*self.bus.xfer_period_mu-t_10) for i in range(len(values)): self.write_channel(i, values[i], op) - delay_mu(3*self.bus.ref_period_mu + # latency alignment ttl to spi - self.core.seconds_to_mu(1.5*us)) # t10 max busy low for one channel + delay_mu(t_10) self.load() - delay_mu(-2*self.bus.ref_period_mu) # load(), t13 + at_mu(t0) diff --git a/artiq/examples/kc705_nist_clock/device_db.py b/artiq/examples/kc705_nist_clock/device_db.py index a0a2fa679..e8e0901f1 100644 --- a/artiq/examples/kc705_nist_clock/device_db.py +++ b/artiq/examples/kc705_nist_clock/device_db.py @@ -173,7 +173,7 @@ device_db = { }, "spi_zotino": { "type": "local", - "module": "artiq.coredevice.spi", + "module": "artiq.coredevice.spi2", "class": "SPIMaster", "arguments": {"channel": 30} }, @@ -187,7 +187,12 @@ device_db = { "type": "local", "module": "artiq.coredevice.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": { diff --git a/artiq/examples/kc705_nist_clock/repository/ad5360.py b/artiq/examples/kc705_nist_clock/repository/ad5360.py index 79d1f01c6..88ba87631 100644 --- a/artiq/examples/kc705_nist_clock/repository/ad5360.py +++ b/artiq/examples/kc705_nist_clock/repository/ad5360.py @@ -13,7 +13,7 @@ class AD5360Test(EnvExperiment): self.core.reset() delay(5*ms) # build slack for shift register set 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.led.on() delay(400*us) diff --git a/artiq/gateware/rtio/phy/ad5360_monitor.py b/artiq/gateware/rtio/phy/ad5360_monitor.py index fad34ecc6..c112d6649 100644 --- a/artiq/gateware/rtio/phy/ad5360_monitor.py +++ b/artiq/gateware/rtio/phy/ad5360_monitor.py @@ -1,6 +1,6 @@ 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 @@ -22,20 +22,20 @@ class AD5360Monitor(Module): If(ldac_oif.stb & ttl_level_adr & ~ldac_oif.data[0], [probe.eq(write_target) for probe, write_target in zip(self.probes, write_targets)] ) - + spi_oif = spi_rtlink.o selected = Signal() if cs_onehot: self.sync.rio_phy += [ - If(spi_oif.stb & (spi_oif.address == SPI_XFER_ADDR), - selected.eq(spi_oif.data[cs_no]) + If(spi_oif.stb & (spi_oif.address == SPI_CONFIG_ADDR), + selected.eq(spi_oif.data[24 + cs_no]) ) ] else: self.sync.rio_phy += [ - If(spi_oif.stb & (spi_oif.address == SPI_XFER_ADDR), - selected.eq(spi_oif.data[:16] == cs_no) + If(spi_oif.stb & (spi_oif.address == SPI_CONFIG_ADDR), + selected.eq(spi_oif.data[24:] == cs_no) ) ] diff --git a/artiq/gateware/targets/kc705.py b/artiq/gateware/targets/kc705.py index c73b73ca5..e19ce222b 100755 --- a/artiq/gateware/targets/kc705.py +++ b/artiq/gateware/targets/kc705.py @@ -341,8 +341,8 @@ class NIST_CLOCK(_StandaloneBase): self.submodules += phy rtio_channels.append(rtio.Channel.from_phy(phy)) - sdac_phy = spi.SPIMaster(self.platform.request("zotino_spi_p"), - self.platform.request("zotino_spi_n")) + sdac_phy = spi2.SPIMaster(self.platform.request("zotino_spi_p"), + self.platform.request("zotino_spi_n")) self.submodules += sdac_phy rtio_channels.append(rtio.Channel.from_phy(sdac_phy, ififo_depth=4))