diff --git a/artiq/coredevice/ad5360.py b/artiq/coredevice/ad5360.py new file mode 100644 index 000000000..9e1b6781c --- /dev/null +++ b/artiq/coredevice/ad5360.py @@ -0,0 +1,95 @@ +from artiq.language.core import kernel, portable, delay +from artiq.language.units import ns +from artiq.coredevice import spi + + +_AD5360_SPI_CONFIG = (0*spi.SPI_OFFLINE | 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) + +_AD5360_CMD_DATA = 3 << 22 +_AD5360_CMD_OFFSET = 2 << 22 +_AD5360_CMD_GAIN = 1 << 22 +_AD5360_CMD_SPECIAL = 0 << 22 + + +@portable +def _AD5360_WRITE_CHANNEL(c): + return (c + 8) << 16 + +_AD5360_SPECIAL_NOP = 0 << 16 +_AD5360_SPECIAL_CONTROL = 1 << 16 +_AD5360_SPECIAL_OFS0 = 2 << 16 +_AD5360_SPECIAL_OFS1 = 3 << 16 +_AD5360_SPECIAL_READ = 3 << 16 + + +@portable +def _AD5360_READ_CHANNEL(ch): + return (ch + 8) << 7 + +_AD5360_READ_X1A = 0x000 << 7 +_AD5360_READ_X1B = 0x040 << 7 +_AD5360_READ_OFFSET = 0x080 << 7 +_AD5360_READ_GAIN = 0x0c0 << 7 +_AD5360_READ_CONTROL = 0x101 << 7 +_AD5360_READ_OFS0 = 0x102 << 7 +_AD5360_READ_OFS1 = 0x103 << 7 + + +class AD5360: + """ + Support for the Analog devices AD53[67][0123] + multi-channel Digital to Analog Converters + """ + + def __init__(self, dmgr, spi_bus, ldac=None, chip_select=0): + self.core = dmgr.get("core") + self.bus = dmgr.get(spi_bus) + if ldac is not None: + ldac = dmgr.get(ldac) + self.ldac = ldac + self.chip_select = chip_select + + @kernel + def setup_bus(self, write_div=4, read_div=7): + # 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) + + @kernel + def write_offsets(self, value=0x1fff): + value &= 0x3fff + self.bus.write((_AD5360_CMD_SPECIAL | _AD5360_SPECIAL_OFS0 | value + ) << 8) + self.bus.write((_AD5360_CMD_SPECIAL | _AD5360_SPECIAL_OFS1 | value + ) << 8) + + @kernel + def write_channel(self, channel=0, value=0, op=_AD5360_CMD_DATA): + channel &= 0x3f + value &= 0xffff + self.bus.write((op | _AD5360_WRITE_CHANNEL(channel) | value) << 8) + + @kernel + def write_channels(self, values, first=0, op=_AD5360_CMD_DATA): + for i in range(len(values)): + self.write_channel(i + first, values[i], op) + + @kernel + def read_channel_sync(self, channel=0, op=_AD5360_READ_X1A): + channel &= 0x3f + self.bus.write((_AD5360_CMD_SPECIAL | _AD5360_SPECIAL_READ | op | + _AD5360_READ_CHANNEL(channel)) << 8) + self.bus.set_xfer(self.chip_select, 0, 24) + self.bus.write((_AD5360_CMD_SPECIAL | _AD5360_SPECIAL_NOP) << 8) + self.bus.read_async() + self.bus.set_xfer(self.chip_select, 24, 0) + return self.bus.input_async() & 0xffff + + @kernel + def load(self): + self.ldac.off() + delay(24*ns) + self.ldac.on() diff --git a/artiq/coredevice/ad53xx.py b/artiq/coredevice/ad53xx.py deleted file mode 100644 index 6ab325717..000000000 --- a/artiq/coredevice/ad53xx.py +++ /dev/null @@ -1,71 +0,0 @@ -from artiq.language.core import (kernel, portable, delay, delay_mu, int) -from artiq.language.units import ns -from artiq.coredevice import spi - - -_AD53xx_SPI_CONFIG = (0*spi.SPI_OFFLINE | 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) - -_AD53xx_MODE_WRITE_X1 = 3 << 22 -_AD53xx_MODE_WRITE_C = 2 << 22 -_AD53xx_MODE_WRITE_M = 1 << 22 -_AD53xx_MODE_SPECIAL = 0 << 22 - -_AD53xx_GROUP = portable(lambda g: ((g + 1) << 19)) -_AD53xx_GROUP_ALL = 0 << 19 -_AD53xx_GROUP_01234 = 6 << 19 -_AD53xx_GROUP_1234 = 7 << 19 -_AD53xx_CHANNEL_ALL = 0 << 16 -_AD53xx_CHANNEL = portable(lambda g: g << 16) - -_AD53xx_SPECIAL_NOP = 0 << 16 -_AD53xx_SPECIAL_CONTROL = 1 << 16 -_AD53xx_SPECIAL_OFS0 = 2 << 16 -_AD53xx_SPECIAL_OFS1 = 3 << 16 -_AD53xx_SPECIAL_AB_SELECT = portable(lambda i: (i + 6) << 16) -_AD53xx_SPECIAL_AB_SELECT_ALL = 11 << 16 - -_AD53xx_READ_X1A = portable(lambda ch: (0x00 | (ch + 8)) << 7) -_AD53xx_READ_X1B = portable(lambda ch: (0x40 | (ch + 8)) << 7) -_AD53xx_READ_C = portable(lambda ch: (0x80 | (ch + 8)) << 7) -_AD53xx_READ_M = portable(lambda ch: (0xc0 | (ch + 8)) << 7) -_AD53xx_READ_CONTROL = 0x101 << 7 -_AD53xx_READ_OFS0 = 0x102 << 7 -_AD53xx_READ_OFS1 = 0x103 << 7 -_AD53xx_READ_AB_SELECT = portable(lambda i: (0x100 + (i + 6)) << 7) - - -class AD53xx: - def __init__(self, dmgr, spi_bus, ldac=None, - chip_select=0, write_div=4, read_div=6): - self.core = dmgr.get("core") - self.bus = dmgr.get(spi_bus) - if ldac is not None: - ldac = dmgr.get(ldac) - self.ldac = ldac - self.chip_select = chip_select - self.write_div = write_div - self.read_div = read_div - - @kernel - def bus_setup(self): - self.bus.set_config_mu(_AD53xx_SPI_CONFIG, self.write_div, - self.read_div) - self.bus.set_xfer(self.chip_select, 24, 0) - - @kernel - def _channel_address(self, channel=0): - return int((channel + 8) << 16) - - @kernel - def write_x1(self, channel=0, value=0): - ch = self._channel_address(channel) - self.bus.write(_AD53xx_MODE_WRITE_X1 | ch | value) - delay_mu(int(self.bus.xfer_period_mu + self.bus.write_period_mu)) - - @kernel - def load(self): - self.ldac.off() - delay(20*ns) - self.ldac.on() diff --git a/artiq/coredevice/spi.py b/artiq/coredevice/spi.py index 264481e21..159bd4da4 100644 --- a/artiq/coredevice/spi.py +++ b/artiq/coredevice/spi.py @@ -198,11 +198,11 @@ class SPIMaster: the previous transfer's read data is available in the ``data`` register. - This method advances the timeline by the duration of the - RTIO-to-Wishbone bus transaction (three RTIO clock cycles). + This method advances the timeline by the duration of the SPI transfer. + If a transfer is to be chained, the timeline needs to be rewound. """ rtio_output(now_mu(), self.channel, SPI_DATA_ADDR, data) - delay_mu(3*self.ref_period_mu) + delay_mu(self.xfer_period_mu + self.write_period_mu) @kernel def read_async(self): diff --git a/artiq/coredevice/ttl.py b/artiq/coredevice/ttl.py index b841bde28..2cd308f55 100644 --- a/artiq/coredevice/ttl.py +++ b/artiq/coredevice/ttl.py @@ -238,7 +238,7 @@ class TTLClockGen: # in RTIO cycles self.previous_timestamp = int(0, width=64) - self.acc_width = 24 + self.acc_width = int(24, width=64) @portable def frequency_to_ftw(self, frequency): diff --git a/artiq/gateware/targets/kc705.py b/artiq/gateware/targets/kc705.py index 778cdc68d..16ad41bf8 100755 --- a/artiq/gateware/targets/kc705.py +++ b/artiq/gateware/targets/kc705.py @@ -264,7 +264,7 @@ class NIST_CLOCK(_NIST_Ions): rtio_channels.append(rtio.Channel.from_phy( phy, ofifo_depth=4, ififo_depth=4)) - for i in range(1): # spi1 and spi2 collide in pinout with ttl + for i in range(3): phy = spi.SPIMaster(self.platform.request("spi", i)) self.submodules += phy rtio_channels.append(rtio.Channel.from_phy( diff --git a/doc/manual/core_drivers_reference.rst b/doc/manual/core_drivers_reference.rst index e88e077e5..afc41aee0 100644 --- a/doc/manual/core_drivers_reference.rst +++ b/doc/manual/core_drivers_reference.rst @@ -30,6 +30,11 @@ These drivers are for the core device and the peripherals closely integrated int .. automodule:: artiq.coredevice.spi :members: +:mod:`artiq.coredevice.ad5360` module +------------------------------------- + +.. automodule:: artiq.coredevice.ad5360 + :members: :mod:`artiq.coredevice.exceptions` module -----------------------------------------