mirror of https://github.com/m-labs/artiq.git
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:
parent
d83ae0bc6a
commit
f8e6b4f4e3
|
@ -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)
|
||||||
|
|
|
@ -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": {
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
|
|
||||||
|
@ -22,20 +22,20 @@ class AD5360Monitor(Module):
|
||||||
If(ldac_oif.stb & ttl_level_adr & ~ldac_oif.data[0],
|
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)]
|
[probe.eq(write_target) for probe, write_target in zip(self.probes, write_targets)]
|
||||||
)
|
)
|
||||||
|
|
||||||
spi_oif = spi_rtlink.o
|
spi_oif = spi_rtlink.o
|
||||||
|
|
||||||
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)
|
||||||
)
|
)
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
|
@ -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))
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue