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 1/4] 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, From 338bb189b4f896f0cce09b2855f6e95d758ac661 Mon Sep 17 00:00:00 2001 From: Sebastien Bourdeauducq Date: Sat, 26 Feb 2022 08:56:52 +0800 Subject: [PATCH 2/4] dashboard: fix typo (#1858) --- artiq/dashboard/moninj.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/artiq/dashboard/moninj.py b/artiq/dashboard/moninj.py index 0233fd8d7..2a2d420e5 100644 --- a/artiq/dashboard/moninj.py +++ b/artiq/dashboard/moninj.py @@ -226,7 +226,7 @@ def setup_from_ddb(ddb): dds_sysclk = v["arguments"]["sysclk"] widget = _WidgetDesc(k, comment, _DDSWidget, (bus_channel, channel, k)) description.add(widget) - elif ( (v["module"] == "artiq.coredevice.ad53xx" and v["class"] == "AD53XX") + elif ( (v["module"] == "artiq.coredevice.ad53xx" and v["class"] == "AD53xx") or (v["module"] == "artiq.coredevice.zotino" and v["class"] == "Zotino")): spi_device = v["arguments"]["spi_device"] spi_device = ddb[spi_device] From 9bfbd39fa34aeb50ada08b244e3d02971a6f95c8 Mon Sep 17 00:00:00 2001 From: David Nadlinger Date: Wed, 23 Feb 2022 17:54:59 +0000 Subject: [PATCH 3/4] flake.nix: Use upstream llvmlite 0.38.0, which already has the patches --- flake.nix | 16 +++------------- 1 file changed, 3 insertions(+), 13 deletions(-) diff --git a/flake.nix b/flake.nix index 7d796bbf5..842c16a07 100644 --- a/flake.nix +++ b/flake.nix @@ -113,22 +113,12 @@ llvmlite-new = pkgs.python3Packages.buildPythonPackage rec { pname = "llvmlite"; - version = "0.37.0-artiq"; + version = "0.38.0"; src = pkgs.python3Packages.fetchPypi { inherit pname; - version = "0.37.0"; - sha256 = "sha256-Y5K4cM0BjsDGRda7uRjWqg7sqMYmdLqu4whi1raGWxU="; + version = "0.38.0"; + sha256 = "qZ0WbM87EW87ntI7m3C6JBVkCpyXjzqqE/rUnFj0llw="; }; - patches = [ - (pkgs.fetchurl { - url = "https://git.m-labs.hk/M-Labs/nix-scripts/raw/branch/master/artiq-fast/pkgs/llvmlite-abiname.diff"; - sha256 = "1zlss9vlhjgch6gf5gc0647kkjdwjk0833ld88xwd9vmwvkdmp3v"; - }) - (pkgs.fetchurl { - url = "https://git.m-labs.hk/M-Labs/nix-scripts/raw/branch/master/artiq-fast/pkgs/llvmlite-callsite.diff"; - sha256 = "sha256-JrIXPnI7E7Y5NIFxswVBmRfQvv61lqKDDnNJrr+nDCg="; - }) - ]; nativeBuildInputs = [ pkgs.llvm_11 ]; # Disable static linking # https://github.com/numba/llvmlite/issues/93 From a85b4d5f5ed80e50f1005530b16570057672d770 Mon Sep 17 00:00:00 2001 From: Spaqin Date: Tue, 1 Mar 2022 15:07:53 +0800 Subject: [PATCH 4/4] I2C API for PCA9547 support (#1860) --- RELEASE_NOTES.rst | 3 ++ artiq/coredevice/i2c.py | 28 +++++++++---------- artiq/coredevice/kasli_i2c.py | 12 +++++--- artiq/examples/kasli/device_db.py | 4 +-- artiq/examples/kasli_suservo/device_db.py | 4 +-- artiq/examples/kc705_nist_clock/device_db.py | 2 +- artiq/firmware/ksupport/api.rs | 1 + artiq/firmware/ksupport/nrt_bus.rs | 11 ++++++++ artiq/firmware/libboard_artiq/si5324.rs | 12 ++++---- artiq/firmware/libboard_misoc/i2c.rs | 9 ++++-- artiq/firmware/libboard_misoc/i2c_eeprom.rs | 4 +-- artiq/firmware/libboard_misoc/io_expander.rs | 4 +-- .../firmware/libproto_artiq/drtioaux_proto.rs | 14 ++++++++++ artiq/firmware/libproto_artiq/kernel_proto.rs | 1 + artiq/firmware/runtime/kern_hwreq.rs | 27 ++++++++++++++++++ artiq/firmware/satman/main.rs | 5 ++++ artiq/frontend/artiq_ddb_template.py | 4 +-- artiq/test/coredevice/test_i2c.py | 8 +++--- 18 files changed, 111 insertions(+), 42 deletions(-) diff --git a/RELEASE_NOTES.rst b/RELEASE_NOTES.rst index 5d890b590..de797aaff 100644 --- a/RELEASE_NOTES.rst +++ b/RELEASE_NOTES.rst @@ -35,6 +35,9 @@ Highlights: * 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 +* ``PCA9548`` I2C switch class renamed to ``I2CSwitch``, to accomodate support for PCA9547, and + possibly other switches in future. Readback has been removed, and now only one channel per + switch is supported. Breaking changes: diff --git a/artiq/coredevice/i2c.py b/artiq/coredevice/i2c.py index 3608f4dc8..2b3be2bc7 100644 --- a/artiq/coredevice/i2c.py +++ b/artiq/coredevice/i2c.py @@ -33,6 +33,11 @@ def i2c_read(busno: TInt32, ack: TBool) -> TInt32: raise NotImplementedError("syscall not simulated") +@syscall(flags={"nounwind", "nowrite"}) +def i2c_switch_select(busno: TInt32, address: TInt32, mask: TInt32) -> TNone: + raise NotImplementedError("syscall not simulated") + + @kernel def i2c_poll(busno, busaddr): """Poll I2C device at address. @@ -137,8 +142,10 @@ def i2c_read_many(busno, busaddr, addr, data): i2c_stop(busno) -class PCA9548: - """Driver for the PCA9548 I2C bus switch. +class I2CSwitch: + """Driver for the I2C bus switch. + + PCA954X (or other) type detection is done by the CPU during I2C init. I2C transactions not real-time, and are performed by the CPU without involving RTIO. @@ -151,25 +158,18 @@ class PCA9548: self.busno = busno self.address = address - @kernel - def select(self, mask): - """Enable/disable channels. - - :param mask: Bit mask of enabled channels - """ - i2c_write_byte(self.busno, self.address, mask) - @kernel def set(self, channel): """Enable one channel. - :param channel: channel number (0-7) """ - self.select(1 << channel) + i2c_switch_select(self.busno, self.address >> 1, 1 << channel) @kernel - def readback(self): - return i2c_read_byte(self.busno, self.address) + def unset(self): + """Disable output of the I2C switch. + """ + i2c_switch_select(self.busno, self.address >> 1, 0) class TCA6424A: diff --git a/artiq/coredevice/kasli_i2c.py b/artiq/coredevice/kasli_i2c.py index 11c69172f..1e031fa2b 100644 --- a/artiq/coredevice/kasli_i2c.py +++ b/artiq/coredevice/kasli_i2c.py @@ -37,13 +37,17 @@ class KasliEEPROM: @kernel def select(self): mask = 1 << self.port - self.sw0.select(mask) - self.sw1.select(mask >> 8) + if self.port < 8: + self.sw0.set(self.port) + self.sw1.unset() + else: + self.sw0.unset() + self.sw1.set(self.port - 8) @kernel def deselect(self): - self.sw0.select(0) - self.sw1.select(0) + self.sw0.unset() + self.sw1.unset() @kernel def write_i32(self, addr, value): diff --git a/artiq/examples/kasli/device_db.py b/artiq/examples/kasli/device_db.py index 4b20c7a28..f4d39293e 100644 --- a/artiq/examples/kasli/device_db.py +++ b/artiq/examples/kasli/device_db.py @@ -29,13 +29,13 @@ device_db = { "i2c_switch0": { "type": "local", "module": "artiq.coredevice.i2c", - "class": "PCA9548", + "class": "I2CSwitch", "arguments": {"address": 0xe0} }, "i2c_switch1": { "type": "local", "module": "artiq.coredevice.i2c", - "class": "PCA9548", + "class": "I2CSwitch", "arguments": {"address": 0xe2} }, } diff --git a/artiq/examples/kasli_suservo/device_db.py b/artiq/examples/kasli_suservo/device_db.py index fdb85dc47..f0d3a48a2 100644 --- a/artiq/examples/kasli_suservo/device_db.py +++ b/artiq/examples/kasli_suservo/device_db.py @@ -27,13 +27,13 @@ device_db = { "i2c_switch0": { "type": "local", "module": "artiq.coredevice.i2c", - "class": "PCA9548", + "class": "I2CSwitch", "arguments": {"address": 0xe0} }, "i2c_switch1": { "type": "local", "module": "artiq.coredevice.i2c", - "class": "PCA9548", + "class": "I2CSwitch", "arguments": {"address": 0xe2} }, diff --git a/artiq/examples/kc705_nist_clock/device_db.py b/artiq/examples/kc705_nist_clock/device_db.py index 1b3c3e615..bf9637d32 100644 --- a/artiq/examples/kc705_nist_clock/device_db.py +++ b/artiq/examples/kc705_nist_clock/device_db.py @@ -31,7 +31,7 @@ device_db = { "i2c_switch": { "type": "local", "module": "artiq.coredevice.i2c", - "class": "PCA9548" + "class": "I2CSwitch" }, # Generic TTL diff --git a/artiq/firmware/ksupport/api.rs b/artiq/firmware/ksupport/api.rs index 06d7c1d89..3e99731b4 100644 --- a/artiq/firmware/ksupport/api.rs +++ b/artiq/firmware/ksupport/api.rs @@ -159,6 +159,7 @@ static mut API: &'static [(&'static str, *const ())] = &[ api!(i2c_stop = ::nrt_bus::i2c::stop), api!(i2c_write = ::nrt_bus::i2c::write), api!(i2c_read = ::nrt_bus::i2c::read), + api!(i2c_switch_select = ::nrt_bus::i2c::switch_select), api!(spi_set_config = ::nrt_bus::spi::set_config), api!(spi_write = ::nrt_bus::spi::write), diff --git a/artiq/firmware/ksupport/nrt_bus.rs b/artiq/firmware/ksupport/nrt_bus.rs index 9e226dd96..84927262c 100644 --- a/artiq/firmware/ksupport/nrt_bus.rs +++ b/artiq/firmware/ksupport/nrt_bus.rs @@ -43,6 +43,17 @@ pub mod i2c { data }) as i32 } + + pub extern fn switch_select(busno: i32, address: i32, mask: i32) { + send(&I2cSwitchSelectRequest { + busno: busno as u32, + address: address as u8, + mask: mask as u8 }); + recv!(&I2cBasicReply { succeeded } => { if !succeeded { + raise!("I2CError", "I2C bus could not be accessed"); + } + }); + } } pub mod spi { diff --git a/artiq/firmware/libboard_artiq/si5324.rs b/artiq/firmware/libboard_artiq/si5324.rs index f5e816f14..3b1103d99 100644 --- a/artiq/firmware/libboard_artiq/si5324.rs +++ b/artiq/firmware/libboard_artiq/si5324.rs @@ -179,17 +179,17 @@ fn init() -> Result<()> { #[cfg(soc_platform = "kasli")] { - i2c::pca9548_select(BUSNO, 0x70, 0)?; - i2c::pca9548_select(BUSNO, 0x71, 1 << 3)?; + i2c::switch_select(BUSNO, 0x70, 0)?; + i2c::switch_select(BUSNO, 0x71, 1 << 3)?; } #[cfg(soc_platform = "sayma_amc")] - i2c::pca9548_select(BUSNO, 0x70, 1 << 4)?; + i2c::switch_select(BUSNO, 0x70, 1 << 4)?; #[cfg(soc_platform = "sayma_rtm")] - i2c::pca9548_select(BUSNO, 0x77, 1 << 5)?; + i2c::switch_select(BUSNO, 0x77, 1 << 5)?; #[cfg(soc_platform = "metlino")] - i2c::pca9548_select(BUSNO, 0x70, 1 << 4)?; + i2c::switch_select(BUSNO, 0x70, 1 << 4)?; #[cfg(soc_platform = "kc705")] - i2c::pca9548_select(BUSNO, 0x74, 1 << 7)?; + i2c::switch_select(BUSNO, 0x74, 1 << 7)?; if ident()? != 0x0182 { return Err("Si5324 does not have expected product number"); diff --git a/artiq/firmware/libboard_misoc/i2c.rs b/artiq/firmware/libboard_misoc/i2c.rs index 19ff3195a..c7f8f063c 100644 --- a/artiq/firmware/libboard_misoc/i2c.rs +++ b/artiq/firmware/libboard_misoc/i2c.rs @@ -188,12 +188,15 @@ mod imp { Ok(data) } - pub fn pca9548_select(busno: u8, address: u8, channels: u8) -> Result<(), &'static str> { + pub fn switch_select(busno: u8, address: u8, mask: u8) -> Result<(), &'static str> { + // address in 7-bit form + // mask in format of 1 << channel (or 0 for disabling output) + // PCA9548 support only for now start(busno)?; if !write(busno, address << 1)? { return Err("PCA9548 failed to ack write address") } - if !write(busno, channels)? { + if !write(busno, mask)? { return Err("PCA9548 failed to ack control word") } stop(busno)?; @@ -210,7 +213,7 @@ mod imp { pub fn stop(_busno: u8) -> Result<(), &'static str> { Err(NO_I2C) } pub fn write(_busno: u8, _data: u8) -> Result { Err(NO_I2C) } pub fn read(_busno: u8, _ack: bool) -> Result { Err(NO_I2C) } - pub fn pca9548_select(_busno: u8, _address: u8, _channels: u8) -> Result<(), &'static str> { Err(NO_I2C) } + pub fn switch_select(_busno: u8, _address: u8, _mask: u8) -> Result<(), &'static str> { Err(NO_I2C) } } pub use self::imp::*; diff --git a/artiq/firmware/libboard_misoc/i2c_eeprom.rs b/artiq/firmware/libboard_misoc/i2c_eeprom.rs index 6f2dee15e..aabd13502 100644 --- a/artiq/firmware/libboard_misoc/i2c_eeprom.rs +++ b/artiq/firmware/libboard_misoc/i2c_eeprom.rs @@ -31,8 +31,8 @@ impl EEPROM { #[cfg(soc_platform = "kasli")] fn select(&self) -> Result<(), &'static str> { let mask: u16 = 1 << self.port; - i2c::pca9548_select(self.busno, 0x70, mask as u8)?; - i2c::pca9548_select(self.busno, 0x71, (mask >> 8) as u8)?; + i2c::switch_select(self.busno, 0x70, mask as u8)?; + i2c::switch_select(self.busno, 0x71, (mask >> 8) as u8)?; Ok(()) } diff --git a/artiq/firmware/libboard_misoc/io_expander.rs b/artiq/firmware/libboard_misoc/io_expander.rs index d2d2acd2f..19baf5364 100644 --- a/artiq/firmware/libboard_misoc/io_expander.rs +++ b/artiq/firmware/libboard_misoc/io_expander.rs @@ -43,8 +43,8 @@ impl IoExpander { #[cfg(soc_platform = "kasli")] fn select(&self) -> Result<(), &'static str> { let mask: u16 = 1 << self.port; - i2c::pca9548_select(self.busno, 0x70, mask as u8)?; - i2c::pca9548_select(self.busno, 0x71, (mask >> 8) as u8)?; + i2c::switch_select(self.busno, 0x70, mask as u8)?; + i2c::switch_select(self.busno, 0x71, (mask >> 8) as u8)?; Ok(()) } diff --git a/artiq/firmware/libproto_artiq/drtioaux_proto.rs b/artiq/firmware/libproto_artiq/drtioaux_proto.rs index bd4875655..36e2cb45b 100644 --- a/artiq/firmware/libproto_artiq/drtioaux_proto.rs +++ b/artiq/firmware/libproto_artiq/drtioaux_proto.rs @@ -47,6 +47,7 @@ pub enum Packet { I2cReadRequest { destination: u8, busno: u8, ack: bool }, I2cReadReply { succeeded: bool, data: u8 }, I2cBasicReply { succeeded: bool }, + I2cSwitchSelectRequest { destination: u8, busno: u8, address: u8, mask: u8 }, SpiSetConfigRequest { destination: u8, busno: u8, flags: u8, length: u8, div: u8, cs: u8 }, SpiWriteRequest { destination: u8, busno: u8, data: u32 }, @@ -154,6 +155,12 @@ impl Packet { 0x87 => Packet::I2cBasicReply { succeeded: reader.read_bool()? }, + 0x88 => Packet::I2cSwitchSelectRequest { + destination: reader.read_u8()?, + busno: reader.read_u8()?, + address: reader.read_u8()?, + mask: reader.read_u8()?, + }, 0x90 => Packet::SpiSetConfigRequest { destination: reader.read_u8()?, @@ -313,6 +320,13 @@ impl Packet { writer.write_u8(0x87)?; writer.write_bool(succeeded)?; }, + Packet::I2cSwitchSelectRequest { destination, busno, address, mask } => { + writer.write_u8(0x88)?; + writer.write_u8(destination)?; + writer.write_u8(busno)?; + writer.write_u8(address)?; + writer.write_u8(mask)?; + }, Packet::SpiSetConfigRequest { destination, busno, flags, length, div, cs } => { writer.write_u8(0x90)?; diff --git a/artiq/firmware/libproto_artiq/kernel_proto.rs b/artiq/firmware/libproto_artiq/kernel_proto.rs index a16aaa09b..3021447ec 100644 --- a/artiq/firmware/libproto_artiq/kernel_proto.rs +++ b/artiq/firmware/libproto_artiq/kernel_proto.rs @@ -65,6 +65,7 @@ pub enum Message<'a> { I2cReadRequest { busno: u32, ack: bool }, I2cReadReply { succeeded: bool, data: u8 }, I2cBasicReply { succeeded: bool }, + I2cSwitchSelectRequest { busno: u32, address: u8, mask: u8 }, SpiSetConfigRequest { busno: u32, flags: u8, length: u8, div: u8, cs: u8 }, SpiWriteRequest { busno: u32, data: u32 }, diff --git a/artiq/firmware/runtime/kern_hwreq.rs b/artiq/firmware/runtime/kern_hwreq.rs index 53e0daa8a..a80a7b71a 100644 --- a/artiq/firmware/runtime/kern_hwreq.rs +++ b/artiq/firmware/runtime/kern_hwreq.rs @@ -115,6 +115,28 @@ mod remote_i2c { } } } + + pub fn switch_select(io: &Io, aux_mutex: &Mutex, linkno: u8, destination: u8, busno: u8, address: u8, mask: u8) -> Result { + let reply = drtio::aux_transact(io, aux_mutex, linkno, &drtioaux::Packet::I2cPca954xSelectRequest { + destination: destination, + busno: busno, + address: address, + mask: mask, + }); + match reply { + Ok(drtioaux::Packet::I2cBasicReply { succeeded }) => { + if succeeded { Ok(()) } else { Err("i2c basic reply error") } + } + Ok(packet) => { + error!("received unexpected aux packet: {:?}", packet); + Err("received unexpected aux packet") + } + Err(e) => { + error!("aux packet error ({})", e); + Err(e) + } + } + } } #[cfg(has_drtio)] @@ -259,6 +281,11 @@ pub fn process_kern_hwreq(io: &Io, aux_mutex: &Mutex, Err(_) => kern_send(io, &kern::I2cReadReply { succeeded: false, data: 0xff }) } } + &kern::I2cSwitchSelectRequest { busno, address, mask } => { + let succeeded = dispatch!(io, aux_mutex, local_i2c, remote_i2c, _routing_table, busno, + switch_select, address, mask).is_ok(); + kern_send(io, &kern::I2cBasicReply { succeeded: succeeded }) + } &kern::SpiSetConfigRequest { busno, flags, length, div, cs } => { let succeeded = dispatch!(io, aux_mutex, local_spi, remote_spi, _routing_table, busno, diff --git a/artiq/firmware/satman/main.rs b/artiq/firmware/satman/main.rs index c6685f895..a99e3d5b3 100644 --- a/artiq/firmware/satman/main.rs +++ b/artiq/firmware/satman/main.rs @@ -275,6 +275,11 @@ fn process_aux_packet(_repeaters: &mut [repeater::Repeater], &drtioaux::Packet::I2cReadReply { succeeded: false, data: 0xff }) } } + drtioaux::Packet::I2cSwitchSelectRequest { destination: _destination, busno, address, mask } => { + forward!(_routing_table, _destination, *_rank, _repeaters, &packet); + let succeeded = i2c::switch_select(busno, address, mask).is_ok(); + drtioaux::send(0, &drtioaux::Packet::I2cBasicReply { succeeded: succeeded }) + } drtioaux::Packet::SpiSetConfigRequest { destination: _destination, busno, flags, length, div, cs } => { forward!(_routing_table, _destination, *_rank, _repeaters, &packet); diff --git a/artiq/frontend/artiq_ddb_template.py b/artiq/frontend/artiq_ddb_template.py index 3530cf996..3d5b639ca 100755 --- a/artiq/frontend/artiq_ddb_template.py +++ b/artiq/frontend/artiq_ddb_template.py @@ -52,13 +52,13 @@ def process_header(output, description): "i2c_switch0": {{ "type": "local", "module": "artiq.coredevice.i2c", - "class": "PCA9548", + "class": "I2CSwitch", "arguments": {{"address": 0xe0}} }}, "i2c_switch1": {{ "type": "local", "module": "artiq.coredevice.i2c", - "class": "PCA9548", + "class": "I2CSwitch", "arguments": {{"address": 0xe2}} }}, }} diff --git a/artiq/test/coredevice/test_i2c.py b/artiq/test/coredevice/test_i2c.py index e5424efc6..7b589cb4e 100644 --- a/artiq/test/coredevice/test_i2c.py +++ b/artiq/test/coredevice/test_i2c.py @@ -3,10 +3,10 @@ import os, unittest from artiq.experiment import * from artiq.test.hardware_testbench import ExperimentCase from artiq.coredevice.exceptions import I2CError -from artiq.coredevice.i2c import PCA9548 +from artiq.coredevice.i2c import I2CSwitch -class I2CSwitch(EnvExperiment): +class I2CSwitchTest(EnvExperiment): def build(self): self.setattr_device("core") self.setattr_device("i2c_switch") @@ -25,7 +25,7 @@ class NonexistentI2CBus(EnvExperiment): def build(self): self.setattr_device("core") self.setattr_device("i2c_switch") # HACK: only run this test on boards with I2C - self.broken_switch = PCA9548(self._HasEnvironment__device_mgr, 255) + self.broken_switch = I2CSwitch(self._HasEnvironment__device_mgr, 255) @kernel def run(self): @@ -34,7 +34,7 @@ class NonexistentI2CBus(EnvExperiment): class I2CTest(ExperimentCase): def test_i2c_switch(self): - self.execute(I2CSwitch) + self.execute(I2CSwitchTest) self.assertTrue(self.dataset_mgr.get("passed")) def test_nonexistent_bus(self):