Merge branch 'master' into nac3

This commit is contained in:
Sebastien Bourdeauducq 2022-03-01 16:58:19 +08:00
commit e6f26b5c74
21 changed files with 313 additions and 46 deletions

View File

@ -33,6 +33,11 @@ Highlights:
warning is logged. The warning is additional to the one already printed in the core device log upon warning is logged. The warning is additional to the one already printed in the core device log upon
detection of the error. detection of the error.
* Removed worker DB warning for writing a dataset that is also in the archive * 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: Breaking changes:

View File

@ -134,7 +134,7 @@
"properties": { "properties": {
"type": { "type": {
"type": "string", "type": "string",
"enum": ["dio", "urukul", "sampler", "suservo", "zotino", "grabber", "mirny", "fastino", "phaser", "hvamp"] "enum": ["dio", "dio_spi", "urukul", "sampler", "suservo", "zotino", "grabber", "mirny", "fastino", "phaser", "hvamp"]
}, },
"board": { "board": {
"type": "string" "type": "string"
@ -179,6 +179,89 @@
}, },
"required": ["ports", "bank_direction_low", "bank_direction_high"] "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", "title": "Urukul",
"if": { "if": {

View File

@ -32,6 +32,11 @@ def i2c_read(busno: int32, ack: bool) -> int32:
raise NotImplementedError("syscall not simulated") 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 @kernel
def i2c_poll(busno, busaddr): def i2c_poll(busno, busaddr):
"""Poll I2C device at address. """Poll I2C device at address.
@ -136,8 +141,10 @@ def i2c_read_many(busno, busaddr, addr, data):
i2c_stop(busno) i2c_stop(busno)
class PCA9548: class I2CSwitch:
"""Driver for the PCA9548 I2C bus switch. """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 I2C transactions not real-time, and are performed by the CPU without
involving RTIO. involving RTIO.
@ -150,25 +157,18 @@ class PCA9548:
self.busno = busno self.busno = busno
self.address = address 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 @kernel
def set(self, channel): def set(self, channel):
"""Enable one channel. """Enable one channel.
:param channel: channel number (0-7) :param channel: channel number (0-7)
""" """
self.select(1 << channel) i2c_switch_select(self.busno, self.address >> 1, 1 << channel)
@kernel @kernel
def readback(self): def unset(self):
return i2c_read_byte(self.busno, self.address) """Disable output of the I2C switch.
"""
i2c_switch_select(self.busno, self.address >> 1, 0)
class TCA6424A: class TCA6424A:

View File

@ -37,13 +37,17 @@ class KasliEEPROM:
@kernel @kernel
def select(self): def select(self):
mask = 1 << self.port mask = 1 << self.port
self.sw0.select(mask) if self.port < 8:
self.sw1.select(mask >> 8) self.sw0.set(self.port)
self.sw1.unset()
else:
self.sw0.unset()
self.sw1.set(self.port - 8)
@kernel @kernel
def deselect(self): def deselect(self):
self.sw0.select(0) self.sw0.unset()
self.sw1.select(0) self.sw1.unset()
@kernel @kernel
def write_i32(self, addr, value): def write_i32(self, addr, value):

View File

@ -226,7 +226,7 @@ def setup_from_ddb(ddb):
dds_sysclk = v["arguments"]["sysclk"] dds_sysclk = v["arguments"]["sysclk"]
widget = _WidgetDesc(k, comment, _DDSWidget, (bus_channel, channel, k)) widget = _WidgetDesc(k, comment, _DDSWidget, (bus_channel, channel, k))
description.add(widget) 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")): or (v["module"] == "artiq.coredevice.zotino" and v["class"] == "Zotino")):
spi_device = v["arguments"]["spi_device"] spi_device = v["arguments"]["spi_device"]
spi_device = ddb[spi_device] spi_device = ddb[spi_device]

View File

@ -27,13 +27,13 @@ device_db = {
"i2c_switch0": { "i2c_switch0": {
"type": "local", "type": "local",
"module": "artiq.coredevice.i2c", "module": "artiq.coredevice.i2c",
"class": "PCA9548", "class": "I2CSwitch",
"arguments": {"address": 0xe0} "arguments": {"address": 0xe0}
}, },
"i2c_switch1": { "i2c_switch1": {
"type": "local", "type": "local",
"module": "artiq.coredevice.i2c", "module": "artiq.coredevice.i2c",
"class": "PCA9548", "class": "I2CSwitch",
"arguments": {"address": 0xe2} "arguments": {"address": 0xe2}
}, },

View File

@ -31,7 +31,7 @@ device_db = {
"i2c_switch": { "i2c_switch": {
"type": "local", "type": "local",
"module": "artiq.coredevice.i2c", "module": "artiq.coredevice.i2c",
"class": "PCA9548" "class": "I2CSwitch"
}, },
# Generic TTL # Generic TTL

View File

@ -159,6 +159,7 @@ static mut API: &'static [(&'static str, *const ())] = &[
api!(i2c_stop = ::nrt_bus::i2c::stop), api!(i2c_stop = ::nrt_bus::i2c::stop),
api!(i2c_write = ::nrt_bus::i2c::write), api!(i2c_write = ::nrt_bus::i2c::write),
api!(i2c_read = ::nrt_bus::i2c::read), 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_set_config = ::nrt_bus::spi::set_config),
api!(spi_write = ::nrt_bus::spi::write), api!(spi_write = ::nrt_bus::spi::write),

View File

@ -43,6 +43,17 @@ pub mod i2c {
data data
}) as i32 }) 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 { pub mod spi {

View File

@ -179,11 +179,11 @@ fn init() -> Result<()> {
#[cfg(soc_platform = "kasli")] #[cfg(soc_platform = "kasli")]
{ {
i2c::pca9548_select(BUSNO, 0x70, 0)?; i2c::switch_select(BUSNO, 0x70, 0)?;
i2c::pca9548_select(BUSNO, 0x71, 1 << 3)?; i2c::switch_select(BUSNO, 0x71, 1 << 3)?;
} }
#[cfg(soc_platform = "kc705")] #[cfg(soc_platform = "kc705")]
i2c::pca9548_select(BUSNO, 0x74, 1 << 7)?; i2c::switch_select(BUSNO, 0x74, 1 << 7)?;
if ident()? != 0x0182 { if ident()? != 0x0182 {
return Err("Si5324 does not have expected product number"); return Err("Si5324 does not have expected product number");

View File

@ -188,12 +188,15 @@ mod imp {
Ok(data) 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)?; start(busno)?;
if !write(busno, address << 1)? { if !write(busno, address << 1)? {
return Err("PCA9548 failed to ack write address") return Err("PCA9548 failed to ack write address")
} }
if !write(busno, channels)? { if !write(busno, mask)? {
return Err("PCA9548 failed to ack control word") return Err("PCA9548 failed to ack control word")
} }
stop(busno)?; stop(busno)?;
@ -210,7 +213,7 @@ mod imp {
pub fn stop(_busno: u8) -> Result<(), &'static str> { Err(NO_I2C) } pub fn stop(_busno: u8) -> Result<(), &'static str> { Err(NO_I2C) }
pub fn write(_busno: u8, _data: u8) -> Result<bool, &'static str> { Err(NO_I2C) } pub fn write(_busno: u8, _data: u8) -> Result<bool, &'static str> { Err(NO_I2C) }
pub fn read(_busno: u8, _ack: bool) -> Result<u8, &'static str> { Err(NO_I2C) } pub fn read(_busno: u8, _ack: bool) -> Result<u8, &'static str> { 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::*; pub use self::imp::*;

View File

@ -31,8 +31,8 @@ impl EEPROM {
#[cfg(soc_platform = "kasli")] #[cfg(soc_platform = "kasli")]
fn select(&self) -> Result<(), &'static str> { fn select(&self) -> Result<(), &'static str> {
let mask: u16 = 1 << self.port; let mask: u16 = 1 << self.port;
i2c::pca9548_select(self.busno, 0x70, mask as u8)?; i2c::switch_select(self.busno, 0x70, mask as u8)?;
i2c::pca9548_select(self.busno, 0x71, (mask >> 8) as u8)?; i2c::switch_select(self.busno, 0x71, (mask >> 8) as u8)?;
Ok(()) Ok(())
} }

View File

@ -43,8 +43,8 @@ impl IoExpander {
#[cfg(soc_platform = "kasli")] #[cfg(soc_platform = "kasli")]
fn select(&self) -> Result<(), &'static str> { fn select(&self) -> Result<(), &'static str> {
let mask: u16 = 1 << self.port; let mask: u16 = 1 << self.port;
i2c::pca9548_select(self.busno, 0x70, mask as u8)?; i2c::switch_select(self.busno, 0x70, mask as u8)?;
i2c::pca9548_select(self.busno, 0x71, (mask >> 8) as u8)?; i2c::switch_select(self.busno, 0x71, (mask >> 8) as u8)?;
Ok(()) Ok(())
} }

View File

@ -47,6 +47,7 @@ pub enum Packet {
I2cReadRequest { destination: u8, busno: u8, ack: bool }, I2cReadRequest { destination: u8, busno: u8, ack: bool },
I2cReadReply { succeeded: bool, data: u8 }, I2cReadReply { succeeded: bool, data: u8 },
I2cBasicReply { succeeded: bool }, 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 }, SpiSetConfigRequest { destination: u8, busno: u8, flags: u8, length: u8, div: u8, cs: u8 },
SpiWriteRequest { destination: u8, busno: u8, data: u32 }, SpiWriteRequest { destination: u8, busno: u8, data: u32 },
@ -154,6 +155,12 @@ impl Packet {
0x87 => Packet::I2cBasicReply { 0x87 => Packet::I2cBasicReply {
succeeded: reader.read_bool()? 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 { 0x90 => Packet::SpiSetConfigRequest {
destination: reader.read_u8()?, destination: reader.read_u8()?,
@ -313,6 +320,13 @@ impl Packet {
writer.write_u8(0x87)?; writer.write_u8(0x87)?;
writer.write_bool(succeeded)?; 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 } => { Packet::SpiSetConfigRequest { destination, busno, flags, length, div, cs } => {
writer.write_u8(0x90)?; writer.write_u8(0x90)?;

View File

@ -65,6 +65,7 @@ pub enum Message<'a> {
I2cReadRequest { busno: u32, ack: bool }, I2cReadRequest { busno: u32, ack: bool },
I2cReadReply { succeeded: bool, data: u8 }, I2cReadReply { succeeded: bool, data: u8 },
I2cBasicReply { succeeded: bool }, I2cBasicReply { succeeded: bool },
I2cSwitchSelectRequest { busno: u32, address: u8, mask: u8 },
SpiSetConfigRequest { busno: u32, flags: u8, length: u8, div: u8, cs: u8 }, SpiSetConfigRequest { busno: u32, flags: u8, length: u8, div: u8, cs: u8 },
SpiWriteRequest { busno: u32, data: u32 }, SpiWriteRequest { busno: u32, data: u32 },

View File

@ -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<u8, &'static str> {
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)] #[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 }) 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 } => { &kern::SpiSetConfigRequest { busno, flags, length, div, cs } => {
let succeeded = dispatch!(io, aux_mutex, local_spi, remote_spi, _routing_table, busno, let succeeded = dispatch!(io, aux_mutex, local_spi, remote_spi, _routing_table, busno,

View File

@ -269,6 +269,11 @@ fn process_aux_packet(_repeaters: &mut [repeater::Repeater],
&drtioaux::Packet::I2cReadReply { succeeded: false, data: 0xff }) &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 } => { drtioaux::Packet::SpiSetConfigRequest { destination: _destination, busno, flags, length, div, cs } => {
forward!(_routing_table, _destination, *_rank, _repeaters, &packet); forward!(_routing_table, _destination, *_rank, _repeaters, &packet);

View File

@ -30,7 +30,7 @@ def process_header(output, description):
"type": "local", "type": "local",
"module": "artiq.coredevice.core", "module": "artiq.coredevice.core",
"class": "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": {{ "core_log": {{
"type": "controller", "type": "controller",
@ -52,13 +52,13 @@ def process_header(output, description):
"i2c_switch0": {{ "i2c_switch0": {{
"type": "local", "type": "local",
"module": "artiq.coredevice.i2c", "module": "artiq.coredevice.i2c",
"class": "PCA9548", "class": "I2CSwitch",
"arguments": {{"address": 0xe0}} "arguments": {{"address": 0xe0}}
}}, }},
"i2c_switch1": {{ "i2c_switch1": {{
"type": "local", "type": "local",
"module": "artiq.coredevice.i2c", "module": "artiq.coredevice.i2c",
"class": "PCA9548", "class": "I2CSwitch",
"arguments": {{"address": 0xe2}} "arguments": {{"address": 0xe2}}
}}, }},
}} }}
@ -102,8 +102,7 @@ class PeripheralManager:
"module": "artiq.coredevice.ttl", "module": "artiq.coredevice.ttl",
"class": "{class_name}", "class": "{class_name}",
"arguments": {{"channel": 0x{channel:06x}}}, "arguments": {{"channel": 0x{channel:06x}}},
}} }}""",
""",
name=name[i], name=name[i],
class_name=classes[i // 4], class_name=classes[i // 4],
channel=rtio_offset + next(channel)) channel=rtio_offset + next(channel))
@ -117,25 +116,64 @@ class PeripheralManager:
"module": "artiq.coredevice.edge_counter", "module": "artiq.coredevice.edge_counter",
"class": "EdgeCounter", "class": "EdgeCounter",
"arguments": {{"channel": 0x{channel:06x}}}, "arguments": {{"channel": 0x{channel:06x}}},
}} }}""",
""",
name=name[i], name=name[i],
channel=rtio_offset + next(channel)) channel=rtio_offset + next(channel))
return 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): def process_urukul(self, rtio_offset, peripheral):
urukul_name = self.get_name("urukul") urukul_name = self.get_name("urukul")
synchronization = peripheral["synchronization"] synchronization = peripheral["synchronization"]
channel = count(0) channel = count(0)
self.gen(""" self.gen("""
device_db["eeprom_{name}"]={{ device_db["eeprom_{name}"] = {{
"type": "local", "type": "local",
"module": "artiq.coredevice.kasli_i2c", "module": "artiq.coredevice.kasli_i2c",
"class": "KasliEEPROM", "class": "KasliEEPROM",
"arguments": {{"port": "EEM{eem}"}} "arguments": {{"port": "EEM{eem}"}}
}} }}
device_db["spi_{name}"]={{ device_db["spi_{name}"] = {{
"type": "local", "type": "local",
"module": "artiq.coredevice.spi2", "module": "artiq.coredevice.spi2",
"class": "SPIMaster", "class": "SPIMaster",

View File

@ -70,6 +70,65 @@ class DIO(_EEM):
target.rtio_channels.append(rtio.Channel.from_phy(counter)) 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): class Urukul(_EEM):
@staticmethod @staticmethod
def io(eem, eem_aux, iostandard): def io(eem, eem_aux, iostandard):

View File

@ -19,6 +19,21 @@ def peripheral_dio(module, peripheral, **kwargs):
edge_counter_cls=edge_counter_cls, **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): def peripheral_urukul(module, peripheral, **kwargs):
if len(peripheral["ports"]) == 1: if len(peripheral["ports"]) == 1:
port, port_aux = peripheral["ports"][0], None port, port_aux = peripheral["ports"][0], None
@ -112,6 +127,7 @@ def peripheral_hvamp(module, peripheral, **kwargs):
peripheral_processors = { peripheral_processors = {
"dio": peripheral_dio, "dio": peripheral_dio,
"dio_spi": peripheral_dio_spi,
"urukul": peripheral_urukul, "urukul": peripheral_urukul,
"sampler": peripheral_sampler, "sampler": peripheral_sampler,
"suservo": peripheral_suservo, "suservo": peripheral_suservo,

View File

@ -3,10 +3,10 @@ import os, unittest
from artiq.experiment import * from artiq.experiment import *
from artiq.test.hardware_testbench import ExperimentCase from artiq.test.hardware_testbench import ExperimentCase
from artiq.coredevice.exceptions import I2CError 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): def build(self):
self.setattr_device("core") self.setattr_device("core")
self.setattr_device("i2c_switch") self.setattr_device("i2c_switch")
@ -25,7 +25,7 @@ class NonexistentI2CBus(EnvExperiment):
def build(self): def build(self):
self.setattr_device("core") self.setattr_device("core")
self.setattr_device("i2c_switch") # HACK: only run this test on boards with I2C 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 @kernel
def run(self): def run(self):
@ -34,7 +34,7 @@ class NonexistentI2CBus(EnvExperiment):
class I2CTest(ExperimentCase): class I2CTest(ExperimentCase):
def test_i2c_switch(self): def test_i2c_switch(self):
self.execute(I2CSwitch) self.execute(I2CSwitchTest)
self.assertTrue(self.dataset_mgr.get("passed")) self.assertTrue(self.dataset_mgr.get("passed"))
def test_nonexistent_bus(self): def test_nonexistent_bus(self):