From 01a6e77d89dfe430d7f241eb58f8537b5f57777d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20J=C3=B6rdens?= Date: Sun, 16 Jun 2019 17:17:42 +0000 Subject: [PATCH] mirny: add MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * 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 --- artiq/coredevice/adf5355.py | 68 +++++++++++++++++++++++++ artiq/coredevice/mirny.py | 66 ++++++++++++++++++++++++ artiq/gateware/eem.py | 45 ++++++++++++++++ artiq/gateware/targets/kasli_generic.py | 8 +++ 4 files changed, 187 insertions(+) create mode 100644 artiq/coredevice/adf5355.py create mode 100644 artiq/coredevice/mirny.py diff --git a/artiq/coredevice/adf5355.py b/artiq/coredevice/adf5355.py new file mode 100644 index 000000000..26c71db69 --- /dev/null +++ b/artiq/coredevice/adf5355.py @@ -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) diff --git a/artiq/coredevice/mirny.py b/artiq/coredevice/mirny.py new file mode 100644 index 000000000..f0605eed2 --- /dev/null +++ b/artiq/coredevice/mirny.py @@ -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) diff --git a/artiq/gateware/eem.py b/artiq/gateware/eem.py index b5e50c0ba..84f4526ce 100644 --- a/artiq/gateware/eem.py +++ b/artiq/gateware/eem.py @@ -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)) diff --git a/artiq/gateware/targets/kasli_generic.py b/artiq/gateware/targets/kasli_generic.py index f500d2946..2f593459a 100755 --- a/artiq/gateware/targets/kasli_generic.py +++ b/artiq/gateware/targets/kasli_generic.py @@ -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, }