Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[WIP] mirny: add #1405

Merged
merged 4 commits into from
Jan 20, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
65 changes: 65 additions & 0 deletions artiq/coredevice/adf5355.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
"""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 artiq.language.core import kernel, delay
from artiq.language.units import us
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)


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)
81 changes: 81 additions & 0 deletions artiq/coredevice/mirny.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
"""RTIO driver for Mirny (4 channel GHz PLLs)
"""

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

WE = 1 << 24


class Mirny:
"""Mirny PLL-based RF generator.

:param spi_device: SPI bus device
:param core_device: Core device name (default: "core")
"""
kernel_invariants = {"bus", "core"}

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):
"""Read a register"""
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):
"""Write a register"""
self.bus.set_config_mu(SPI_CONFIG | spi.SPI_END, 24, SPIT_WR, SPI_CS)
self.bus.write((addr << 25) | WE | ((data & 0xffff) << 8))

@kernel
def init(self):
"""Initialize Mirny by reading the status register and verifying
compatible hardware and protocol revisions"""
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):
"""Perform SPI write to a prefixed address"""
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)
51 changes: 51 additions & 0 deletions artiq/frontend/artiq_ddb_template.py
Original file line number Diff line number Diff line change
Expand Up @@ -228,6 +228,57 @@ def process_urukul(self, rtio_offset, peripheral):
raise ValueError
return next(channel)

def process_mirny(self, rtio_offset, peripheral):
mirny_name = self.get_name("mirny")
channel = count(0)
self.gen("""
device_db["spi_{name}"]={{
"type": "local",
"module": "artiq.coredevice.spi2",
"class": "SPIMaster",
"arguments": {{"channel": 0x{channel:06x}}}
}}""",
name=mirny_name,
channel=rtio_offset+next(channel))

for i in range(4):
self.gen("""
device_db["ttl_{name}_sw{mchn}"] = {{
"type": "local",
"module": "artiq.coredevice.ttl",
"class": "TTLOut",
"arguments": {{"channel": 0x{ttl_channel:06x}}}
}}""",
name=mirny_name,
mchn=i,
ttl_channel=rtio_offset+next(channel))

for i in range(4):
self.gen("""
device_db["{name}_ch{mchn}"] = {{
"type": "local",
"module": "artiq.coredevice.adf5355",
"class": "ADF5355",
"arguments": {{
"channel": {mchn},
"sw_device": "ttl_{name}_sw{mchn}",
"cpld_device": "{name}_cpld",
}}
}}""",
name=mirny_name,
mchn=i)

self.gen("""
device_db["{name}_cpld"] = {{
"type": "local",
"module": "artiq.coredevice.mirny",
"class": "Mirny",
"arguments": {{"spi_device": "spi_{name}"}},
}}""",
name=mirny_name)

return next(channel)

def process_novogorny(self, rtio_offset, peripheral):
self.gen("""
device_db["spi_{name}_adc"] = {{
Expand Down
45 changes: 45 additions & 0 deletions artiq/gateware/eem.py
Original file line number Diff line number Diff line change
Expand Up @@ -558,3 +558,48 @@ def add_std(cls, target, eems_sampler, eems_urukul,
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))
8 changes: 8 additions & 0 deletions artiq/gateware/targets/kasli_generic.py
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -107,6 +114,7 @@ def peripheral_grabber(module, peripheral):
"suservo": peripheral_suservo,
"zotino": peripheral_zotino,
"grabber": peripheral_grabber,
"mirny": peripheral_mirny,
}


Expand Down
12 changes: 12 additions & 0 deletions doc/manual/core_drivers_reference.rst
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,18 @@ RF generation drivers
.. automodule:: artiq.coredevice.ad9914
:members:

:mod:`artiq.coredevice.mirny` module
+++++++++++++++++++++++++++++++++++++

.. automodule:: artiq.coredevice.mirny
:members:

:mod:`artiq.coredevice.adf5355` module
+++++++++++++++++++++++++++++++++++++++

.. automodule:: artiq.coredevice.adf5355
:members:

:mod:`artiq.coredevice.spline` module
+++++++++++++++++++++++++++++++++++++

Expand Down