From c4292770f87cefa3aa9b3ca913033a606a866018 Mon Sep 17 00:00:00 2001 From: Leon Riesebos <28567817+lriesebos@users.noreply.github.com> Date: Fri, 25 Feb 2022 18:36:00 -0500 Subject: [PATCH] Kasli JSON description for SPI over DIO cards (#1800) --- RELEASE_NOTES.rst | 2 + .../coredevice/coredevice_generic.schema.json | 85 ++++++++++++++++++- artiq/frontend/artiq_ddb_template.py | 52 ++++++++++-- artiq/gateware/eem.py | 59 +++++++++++++ artiq/gateware/eem_7series.py | 16 ++++ 5 files changed, 206 insertions(+), 8 deletions(-) diff --git a/RELEASE_NOTES.rst b/RELEASE_NOTES.rst index 3cba6ae3e..5d890b590 100644 --- a/RELEASE_NOTES.rst +++ b/RELEASE_NOTES.rst @@ -33,6 +33,8 @@ Highlights: warning is logged. The warning is additional to the one already printed in the core device log upon detection of the error. * Removed worker DB warning for writing a dataset that is also in the archive +* Extended Kasli gateware JSON description with configuration for SPI over DIO. + See: https://github.com/m-labs/artiq/pull/1800 Breaking changes: diff --git a/artiq/coredevice/coredevice_generic.schema.json b/artiq/coredevice/coredevice_generic.schema.json index f5d096788..800f0863e 100644 --- a/artiq/coredevice/coredevice_generic.schema.json +++ b/artiq/coredevice/coredevice_generic.schema.json @@ -134,7 +134,7 @@ "properties": { "type": { "type": "string", - "enum": ["dio", "urukul", "novogorny", "sampler", "suservo", "zotino", "grabber", "mirny", "fastino", "phaser", "hvamp"] + "enum": ["dio", "dio_spi", "urukul", "novogorny", "sampler", "suservo", "zotino", "grabber", "mirny", "fastino", "phaser", "hvamp"] }, "board": { "type": "string" @@ -179,6 +179,89 @@ }, "required": ["ports", "bank_direction_low", "bank_direction_high"] } + }, { + "title": "DIO_SPI", + "if": { + "properties": { + "type": { + "const": "dio_spi" + } + } + }, + "then": { + "properties": { + "ports": { + "type": "array", + "items": { + "type": "integer" + }, + "minItems": 1, + "maxItems": 1 + }, + "spi": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "clk": { + "type": "integer", + "minimum": 0, + "maximum": 7 + }, + "mosi": { + "type": "integer", + "minimum": 0, + "maximum": 7 + }, + "miso": { + "type": "integer", + "minimum": 0, + "maximum": 7 + }, + "cs": { + "type": "array", + "items": { + "type": "integer", + "minimum": 0, + "maximum": 7 + } + } + }, + "required": ["clk"] + }, + "minItems": 1 + }, + "ttl": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "pin": { + "type": "integer", + "minimum": 0, + "maximum": 7 + }, + "direction": { + "type": "string", + "enum": ["input", "output"] + }, + "edge_counter": { + "type": "boolean", + "default": false + } + }, + "required": ["pin", "direction"] + } + } + }, + "required": ["ports", "spi"] + } }, { "title": "Urukul", "if": { diff --git a/artiq/frontend/artiq_ddb_template.py b/artiq/frontend/artiq_ddb_template.py index b24f63315..3530cf996 100755 --- a/artiq/frontend/artiq_ddb_template.py +++ b/artiq/frontend/artiq_ddb_template.py @@ -30,7 +30,7 @@ def process_header(output, description): "type": "local", "module": "artiq.coredevice.core", "class": "Core", - "arguments": {{"host": core_addr, "ref_period": {ref_period}, "target": "{cpu_target}"}}, + "arguments": {{"host": core_addr, "ref_period": {ref_period}, "target": "{cpu_target}"}}, }}, "core_log": {{ "type": "controller", @@ -102,8 +102,7 @@ class PeripheralManager: "module": "artiq.coredevice.ttl", "class": "{class_name}", "arguments": {{"channel": 0x{channel:06x}}}, - }} - """, + }}""", name=name[i], class_name=classes[i // 4], channel=rtio_offset + next(channel)) @@ -117,25 +116,64 @@ class PeripheralManager: "module": "artiq.coredevice.edge_counter", "class": "EdgeCounter", "arguments": {{"channel": 0x{channel:06x}}}, - }} - """, + }}""", name=name[i], channel=rtio_offset + next(channel)) return next(channel) + def process_dio_spi(self, rtio_offset, peripheral): + channel = count(0) + for spi in peripheral["spi"]: + self.gen(""" + device_db["{name}"] = {{ + "type": "local", + "module": "artiq.coredevice.spi2", + "class": "SPIMaster", + "arguments": {{"channel": 0x{channel:06x}}} + }}""", + name=self.get_name(spi.get("name", "dio_spi")), + channel=rtio_offset + next(channel)) + for ttl in peripheral.get("ttl", []): + ttl_class_names = { + "input": "TTLInOut", + "output": "TTLOut" + } + name = self.get_name(ttl.get("name", "ttl")) + self.gen(""" + device_db["{name}"] = {{ + "type": "local", + "module": "artiq.coredevice.ttl", + "class": "{class_name}", + "arguments": {{"channel": 0x{channel:06x}}}, + }}""", + name=name, + class_name=ttl_class_names[ttl["direction"]], + channel=rtio_offset + next(channel)) + if ttl.get("edge_counter", False): + self.gen(""" + device_db["{name}_counter"] = {{ + "type": "local", + "module": "artiq.coredevice.edge_counter", + "class": "EdgeCounter", + "arguments": {{"channel": 0x{channel:06x}}}, + }}""", + name=name, + channel=rtio_offset + next(channel)) + return next(channel) + def process_urukul(self, rtio_offset, peripheral): urukul_name = self.get_name("urukul") synchronization = peripheral["synchronization"] channel = count(0) self.gen(""" - device_db["eeprom_{name}"]={{ + device_db["eeprom_{name}"] = {{ "type": "local", "module": "artiq.coredevice.kasli_i2c", "class": "KasliEEPROM", "arguments": {{"port": "EEM{eem}"}} }} - device_db["spi_{name}"]={{ + device_db["spi_{name}"] = {{ "type": "local", "module": "artiq.coredevice.spi2", "class": "SPIMaster", diff --git a/artiq/gateware/eem.py b/artiq/gateware/eem.py index 7f5fe3fdf..6c588b476 100644 --- a/artiq/gateware/eem.py +++ b/artiq/gateware/eem.py @@ -70,6 +70,65 @@ class DIO(_EEM): target.rtio_channels.append(rtio.Channel.from_phy(counter)) +class DIO_SPI(_EEM): + @staticmethod + def io(eem, spi, ttl, iostandard): + def spi_subsignals(clk, mosi, miso, cs, pol): + signals = [Subsignal("clk", Pins(_eem_pin(eem, clk, pol)))] + if mosi is not None: + signals.append(Subsignal("mosi", + Pins(_eem_pin(eem, mosi, pol)))) + if miso is not None: + signals.append(Subsignal("miso", + Pins(_eem_pin(eem, miso, pol)))) + if cs: + signals.append(Subsignal("cs_n", Pins( + *(_eem_pin(eem, pin, pol) for pin in cs)))) + return signals + + spi = [ + ("dio{}_spi{}_{}".format(eem, i, pol), i, + *spi_subsignals(clk, mosi, miso, cs, pol), + iostandard(eem)) + for i, (clk, mosi, miso, cs) in enumerate(spi) for pol in "pn" + ] + ttl = [ + ("dio{}".format(eem), i, + Subsignal("p", Pins(_eem_pin(eem, pin, "p"))), + Subsignal("n", Pins(_eem_pin(eem, pin, "n"))), + iostandard(eem)) + for i, (pin, _, _) in enumerate(ttl) + ] + return spi + ttl + + @classmethod + def add_std(cls, target, eem, spi, ttl, iostandard=default_iostandard): + cls.add_extension(target, eem, spi, ttl, iostandard=iostandard) + + for i in range(len(spi)): + phy = spi2.SPIMaster( + target.platform.request("dio{}_spi{}_p".format(eem, i)), + target.platform.request("dio{}_spi{}_n".format(eem, i)) + ) + target.submodules += phy + target.rtio_channels.append( + rtio.Channel.from_phy(phy, ififo_depth=4)) + + dci = iostandard(eem).name == "LVDS" + for i, (_, ttl_cls, edge_counter_cls) in enumerate(ttl): + pads = target.platform.request("dio{}".format(eem), i) + phy = ttl_cls(pads.p, pads.n, dci=dci) + target.submodules += phy + target.rtio_channels.append(rtio.Channel.from_phy(phy)) + + if edge_counter_cls is not None: + state = getattr(phy, "input_state", None) + if state is not None: + counter = edge_counter_cls(state) + target.submodules += counter + target.rtio_channels.append(rtio.Channel.from_phy(counter)) + + class Urukul(_EEM): @staticmethod def io(eem, eem_aux, iostandard): diff --git a/artiq/gateware/eem_7series.py b/artiq/gateware/eem_7series.py index 232150211..06a2d9b1d 100644 --- a/artiq/gateware/eem_7series.py +++ b/artiq/gateware/eem_7series.py @@ -19,6 +19,21 @@ def peripheral_dio(module, peripheral, **kwargs): edge_counter_cls=edge_counter_cls, **kwargs) +def peripheral_dio_spi(module, peripheral, **kwargs): + ttl_classes = { + "input": ttl_serdes_7series.InOut_8X, + "output": ttl_serdes_7series.Output_8X + } + if len(peripheral["ports"]) != 1: + raise ValueError("peripheral dio_spi must be assigned one port") + spi = [(s["clk"], s.get("mosi"), s.get("miso"), s.get("cs", [])) + for s in peripheral["spi"]] + ttl = [(t["pin"], ttl_classes[t["direction"]], + edge_counter.SimpleEdgeCounter if t.get("edge_counter") else None) + for t in peripheral.get("ttl", [])] + eem.DIO_SPI.add_std(module, peripheral["ports"][0], spi, ttl, **kwargs) + + def peripheral_urukul(module, peripheral, **kwargs): if len(peripheral["ports"]) == 1: port, port_aux = peripheral["ports"][0], None @@ -119,6 +134,7 @@ def peripheral_hvamp(module, peripheral, **kwargs): peripheral_processors = { "dio": peripheral_dio, + "dio_spi": peripheral_dio_spi, "urukul": peripheral_urukul, "novogorny": peripheral_novogorny, "sampler": peripheral_sampler,