forked from M-Labs/artiq
mirny: add
* This targets unrelease CPLD gateware (https://github.com/quartiq/mirny/issues/1) * includes initial coredevice driver, eem shims, and kasli_generic tooling * addresses the ARTIQ side of #1130 * Register abstraction to be written Signed-off-by: Robert Jördens <rj@quartiq.de>
This commit is contained in:
parent
ec03767dcf
commit
01a6e77d89
|
@ -0,0 +1,68 @@
|
|||
""""RTIO driver for the Analog Devices ADF[45]35[56] family of GHz PLLs
|
||||
on Mirny-style prefixed SPI buses
|
||||
"""
|
||||
|
||||
# https://github.com/analogdevicesinc/linux/blob/master/Documentation/devicetree/bindings/iio/frequency/adf5355.txt
|
||||
# https://github.com/analogdevicesinc/linux/blob/master/drivers/iio/frequency/adf5355.c
|
||||
# https://www.analog.com/media/en/technical-documentation/data-sheets/ADF5356.pdf
|
||||
# https://www.analog.com/media/en/technical-documentation/data-sheets/ADF5355.pdf
|
||||
# https://www.analog.com/media/en/technical-documentation/user-guides/EV-ADF5356SD1Z-UG-1087.pdf
|
||||
|
||||
|
||||
from numpy import int32
|
||||
|
||||
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 spi2 as spi, mirny
|
||||
|
||||
SPI_CONFIG = (0*spi.SPI_OFFLINE | 0*spi.SPI_END |
|
||||
0*spi.SPI_INPUT | 1*spi.SPI_CS_POLARITY |
|
||||
0*spi.SPI_CLK_POLARITY | 0*spi.SPI_CLK_PHASE |
|
||||
0*spi.SPI_LSB_FIRST | 0*spi.SPI_HALF_DUPLEX)
|
||||
|
||||
|
||||
class ADF5355:
|
||||
"""Analog Devices AD[45]35[56] family of GHz PLLs.
|
||||
|
||||
:param cpld_device: Mirny CPLD device name
|
||||
:param sw_device: Mirny RF switch device name
|
||||
:param channel: Mirny RF channel index
|
||||
:param core_device: Core device name (default: "core")
|
||||
"""
|
||||
kernel_invariants = {"cpld", "sw", "channel", "core"}
|
||||
|
||||
def __init__(self, dmgr, cpld_device, sw_device, channel,
|
||||
core="core"):
|
||||
self.cpld = dmgr.get(cpld_device)
|
||||
self.sw = dmgr.get(sw_device)
|
||||
self.channel = channel
|
||||
self.core = dmgr.get(core)
|
||||
|
||||
@kernel
|
||||
def set_att_mu(self, att):
|
||||
"""Set digital step attenuator in machine units.
|
||||
|
||||
:param att: Attenuation setting, 8 bit digital.
|
||||
"""
|
||||
self.cpld.set_att_mu(self.channel, att)
|
||||
|
||||
@kernel
|
||||
def write(self, data):
|
||||
self.cpld.write_ext(self.channel | 4, 32, data)
|
||||
|
||||
@kernel
|
||||
def read_muxout(self):
|
||||
return bool(self.cpld.read_reg(0) & (1 << (self.channel + 8)))
|
||||
|
||||
@kernel
|
||||
def init(self):
|
||||
self.write((1 << 27) | 4)
|
||||
if not self.read_muxout():
|
||||
raise ValueError("MUXOUT not high")
|
||||
delay(100*us)
|
||||
self.write((2 << 27) | 4)
|
||||
if self.read_muxout():
|
||||
raise ValueError("MUXOUT not low")
|
||||
delay(100*us)
|
||||
self.write((6 << 27) | 4)
|
|
@ -0,0 +1,66 @@
|
|||
from artiq.language.core import kernel, delay
|
||||
from artiq.language.units import us
|
||||
|
||||
from numpy import int32
|
||||
|
||||
from artiq.coredevice import spi2 as spi
|
||||
|
||||
|
||||
SPI_CONFIG = (0*spi.SPI_OFFLINE | 0*spi.SPI_END |
|
||||
0*spi.SPI_INPUT | 1*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 clock write and read dividers
|
||||
SPIT_WR = 4
|
||||
SPIT_RD = 16
|
||||
|
||||
SPI_CS = 1
|
||||
|
||||
class Mirny:
|
||||
WE = 1 << 24
|
||||
kernel_invariants = {"bus", "core", "WE"}
|
||||
|
||||
def __init__(self, dmgr, spi_device, core_device="core"):
|
||||
self.core = dmgr.get(core_device)
|
||||
self.bus = dmgr.get(spi_device)
|
||||
|
||||
@kernel
|
||||
def read_reg(self, addr):
|
||||
self.bus.set_config_mu(SPI_CONFIG | spi.SPI_INPUT | spi.SPI_END, 24,
|
||||
SPIT_RD, SPI_CS)
|
||||
self.bus.write((addr << 25))
|
||||
return self.bus.read() & int32(0xffff)
|
||||
|
||||
@kernel
|
||||
def write_reg(self, addr, data):
|
||||
self.bus.set_config_mu(SPI_CONFIG | spi.SPI_END, 24, SPIT_WR, SPI_CS)
|
||||
self.bus.write((addr << 25) | self.WE | ((data & 0xffff) << 8))
|
||||
|
||||
@kernel
|
||||
def init(self):
|
||||
reg0 = self.read_reg(0)
|
||||
if reg0 & 0b11 != 0b11:
|
||||
raise ValueError("Mirny HW_REV mismatch")
|
||||
if (reg0 >> 2) & 0b11 != 0b00:
|
||||
raise ValueError("Mirny PROTO_REV mismatch")
|
||||
delay(100*us) # slack
|
||||
|
||||
@kernel
|
||||
def set_att_mu(self, channel, att):
|
||||
"""Set digital step attenuator in machine units.
|
||||
|
||||
:param att: Attenuation setting, 8 bit digital.
|
||||
"""
|
||||
self.bus.set_config_mu(SPI_CONFIG | spi.SPI_END, 16, SPIT_WR, SPI_CS)
|
||||
self.bus.write(((channel | 8) << 25) | (att << 16))
|
||||
|
||||
@kernel
|
||||
def write_ext(self, addr, length, data):
|
||||
self.bus.set_config_mu(SPI_CONFIG, 8, SPIT_WR, SPI_CS)
|
||||
self.bus.write(addr << 25)
|
||||
self.bus.set_config_mu(SPI_CONFIG | spi.SPI_END, length,
|
||||
SPIT_WR, SPI_CS)
|
||||
if length < 32:
|
||||
data <<= 32 - length
|
||||
self.bus.write(data)
|
|
@ -558,3 +558,48 @@ class SUServo(_EEM):
|
|||
pads = target.platform.request("{}_{}".format(eem_urukuli, signal))
|
||||
target.specials += DifferentialOutput(
|
||||
su.iir.ctrl[j*4 + i].en_out, pads.p, pads.n)
|
||||
|
||||
|
||||
class Mirny(_EEM):
|
||||
@staticmethod
|
||||
def io(eem, iostandard="LVDS_25"):
|
||||
ios = [
|
||||
("mirny{}_spi_p".format(eem), 0,
|
||||
Subsignal("clk", Pins(_eem_pin(eem, 0, "p"))),
|
||||
Subsignal("mosi", Pins(_eem_pin(eem, 1, "p"))),
|
||||
Subsignal("miso", Pins(_eem_pin(eem, 2, "p"))),
|
||||
Subsignal("cs_n", Pins(_eem_pin(eem, 3, "p"))),
|
||||
IOStandard(iostandard),
|
||||
),
|
||||
("mirny{}_spi_n".format(eem), 0,
|
||||
Subsignal("clk", Pins(_eem_pin(eem, 0, "n"))),
|
||||
Subsignal("mosi", Pins(_eem_pin(eem, 1, "n"))),
|
||||
Subsignal("miso", Pins(_eem_pin(eem, 2, "n"))),
|
||||
Subsignal("cs_n", Pins(_eem_pin(eem, 3, "n"))),
|
||||
IOStandard(iostandard),
|
||||
),
|
||||
]
|
||||
for i in range(4):
|
||||
ios.append(
|
||||
("mirny{}_io{}".format(eem, i), 0,
|
||||
Subsignal("p", Pins(_eem_pin(eem, 4 + i, "p"))),
|
||||
Subsignal("n", Pins(_eem_pin(eem, 4 + i, "n"))),
|
||||
IOStandard(iostandard)
|
||||
))
|
||||
return ios
|
||||
|
||||
@classmethod
|
||||
def add_std(cls, target, eem, ttl_out_cls, iostandard="LVDS_25"):
|
||||
cls.add_extension(target, eem, iostandard=iostandard)
|
||||
|
||||
phy = spi2.SPIMaster(
|
||||
target.platform.request("mirny{}_spi_p".format(eem)),
|
||||
target.platform.request("mirny{}_spi_n".format(eem)))
|
||||
target.submodules += phy
|
||||
target.rtio_channels.append(rtio.Channel.from_phy(phy, ififo_depth=4))
|
||||
|
||||
for i in range(4):
|
||||
pads = target.platform.request("mirny{}_io{}".format(eem, i))
|
||||
phy = ttl_out_cls(pads.p, pads.n)
|
||||
target.submodules += phy
|
||||
target.rtio_channels.append(rtio.Channel.from_phy(phy))
|
||||
|
|
|
@ -99,6 +99,13 @@ def peripheral_grabber(module, peripheral):
|
|||
eem.Grabber.add_std(module, port, port_aux, port_aux2)
|
||||
|
||||
|
||||
def peripheral_mirny(module, peripheral):
|
||||
if len(peripheral["ports"]) != 1:
|
||||
raise ValueError("wrong number of ports")
|
||||
eem.Mirny.add_std(module, peripheral["ports"][0],
|
||||
ttl_serdes_7series.Output_8X)
|
||||
|
||||
|
||||
peripheral_processors = {
|
||||
"dio": peripheral_dio,
|
||||
"urukul": peripheral_urukul,
|
||||
|
@ -107,6 +114,7 @@ peripheral_processors = {
|
|||
"suservo": peripheral_suservo,
|
||||
"zotino": peripheral_zotino,
|
||||
"grabber": peripheral_grabber,
|
||||
"mirny": peripheral_mirny,
|
||||
}
|
||||
|
||||
|
||||
|
|
Loading…
Reference in New Issue