diff --git a/src/gateware/kasli_soc.py b/src/gateware/kasli_soc.py index 102d672..b308304 100755 --- a/src/gateware/kasli_soc.py +++ b/src/gateware/kasli_soc.py @@ -10,6 +10,7 @@ from migen.genlib.cdc import MultiReg from migen_axi.integration.soc_core import SoCCore from migen_axi.platforms import kasli_soc from misoc.interconnect.csr import * +from misoc.cores import virtual_leds from misoc.integration import cpu_interface from artiq.coredevice import jsondesc @@ -303,6 +304,14 @@ class GenericMaster(SoCCore): if has_grabber: self.rustc_cfg["has_grabber"] = None self.add_csr_group("grabber", self.grabber_csr_group) + + + self.submodules.virtual_leds = virtual_leds.VirtualLeds() + self.csr_devices.append("virtual_leds") + + self.comb += [self.virtual_leds.get(i).eq(channel.rx_ready) + for i, channel in enumerate(self.drtio_transceiver.channels)] + class GenericSatellite(SoCCore): @@ -466,7 +475,13 @@ class GenericSatellite(SoCCore): self.rustc_cfg["has_grabber"] = None self.add_csr_group("grabber", self.grabber_csr_group) # no RTIO CRG here - + + self.submodules.virtual_leds = virtual_leds.VirtualLeds() + self.csr_devices.append("virtual_leds") + + self.comb += [self.virtual_leds.get(i).eq(channel.rx_ready) + for i, channel in enumerate(self.drtio_transceiver.channels)] + def write_mem_file(soc, filename): with open(filename, "w") as f: diff --git a/src/libboard_artiq/src/io_expander.rs b/src/libboard_artiq/src/io_expander.rs index 918b773..4312bc0 100644 --- a/src/libboard_artiq/src/io_expander.rs +++ b/src/libboard_artiq/src/io_expander.rs @@ -1,6 +1,8 @@ use libboard_zynq::i2c; use log::info; +use crate::pl::csr; + // Only the bare minimum registers. Bits/IO connections equivalent between IC types. struct Registers { // PCA9539 equivalent register names in comments @@ -10,6 +12,26 @@ struct Registers { gpiob: u8, // Output Port 1 } +//IO expanders pins +const IO_DIR_INPUT_ALL: u8 = 0xFF; +const IO_DIR_OUT_SFP_TX_DISABLE: u8 = !0x02; +const IO_DIR_OUT_SFP_LED: u8 = !0x40; +#[cfg(hw_rev = "v1.0")] +const IO_DIR_OUT_SFP0_LED: u8 = !0x40; +#[cfg(hw_rev = "v1.1")] +const IO_DIR_OUT_SFP0_LED: u8 = !0x80; + +//IO expander port direction +const IO_DIR_MAPPING0: [u8; 2] = [ + IO_DIR_INPUT_ALL & IO_DIR_OUT_SFP_TX_DISABLE & (IO_DIR_OUT_SFP0_LED), + IO_DIR_INPUT_ALL & IO_DIR_OUT_SFP_TX_DISABLE & IO_DIR_OUT_SFP_LED, +]; + +const IO_DIR_MAPPING1: [u8; 2] = [ + IO_DIR_INPUT_ALL & IO_DIR_OUT_SFP_TX_DISABLE & IO_DIR_OUT_SFP_LED, + IO_DIR_INPUT_ALL & IO_DIR_OUT_SFP_TX_DISABLE & IO_DIR_OUT_SFP_LED, +]; + pub struct IoExpander<'a> { i2c: &'a mut i2c::I2c, address: u8, @@ -123,6 +145,11 @@ impl<'a> IoExpander<'a> { } pub fn service(&mut self) -> Result<(), &'static str> { + for (led, port, bit) in self.virtual_led_mapping.iter() { + let level = unsafe { csr::virtual_leds::status_read() >> led & 1 }; + self.set(*port, *bit, level != 0); + } + if self.out_target != self.out_current { self.select()?; if self.out_target[0] != self.out_current[0] { diff --git a/src/runtime/src/main.rs b/src/runtime/src/main.rs index 3789888..efc44a9 100644 --- a/src/runtime/src/main.rs +++ b/src/runtime/src/main.rs @@ -102,6 +102,39 @@ async fn report_async_rtio_errors() { } } +#[cfg(all(feature = "target_kasli_soc", has_drtio))] +static mut SEEN_RTIO_LED: u8 = 0; +#[cfg(all(feature = "target_kasli_soc", has_drtio))] +static mut READ_RTIO_LED: u8 = 0; + +#[cfg(all(feature = "target_kasli_soc", has_drtio))] +fn wait_for_rtio_led_change() -> nb::Result<(), Void> { + unsafe { + let len: usize = pl::csr::DRTIO.len(); + READ_RTIO_LED = 0; + for linkno in 0..len { + READ_RTIO_LED |= (pl::csr::DRTIO[linkno].rx_up_read)() << linkno; + } + if READ_RTIO_LED != SEEN_RTIO_LED { + Ok(()) + } else { + Err(nb::Error::WouldBlock) + } + } +} +#[cfg(all(feature = "target_kasli_soc", has_drtio))] +async fn async_rtio_led() { + let _ = block_async!(wait_for_rtio_led_change()).await; + unsafe { + let i2c = (&mut i2c::I2C_BUS).as_mut().unwrap(); + for expander_i in 0..=1 { + let mut io_expander = io_expander::IoExpander::new(i2c, expander_i).unwrap(); + io_expander.service().expect("I2C I/O expander service failed"); + } + SEEN_RTIO_LED = READ_RTIO_LED; + }; +} + static mut LOG_BUFFER: [u8; 1 << 17] = [0; 1 << 17]; #[no_mangle] diff --git a/src/satman/src/main.rs b/src/satman/src/main.rs index 0938e18..03ba48f 100644 --- a/src/satman/src/main.rs +++ b/src/satman/src/main.rs @@ -664,6 +664,12 @@ pub extern "C" fn main_core0() -> i32 { for mut rep in repeaters.iter_mut() { rep.service(&routing_table, rank, &mut timer); } + #[cfg(all(feature = "target_kasli_soc", has_drtio))] + for expander_i in 0..=1 { + let mut io_expander = io_expander::IoExpander::new(&mut i2c, expander_i).unwrap(); + io_expander.service().expect("I2C I/O expander service failed") + } + hardware_tick(&mut hardware_tick_ts, &mut timer); } @@ -700,6 +706,11 @@ pub extern "C" fn main_core0() -> i32 { for mut rep in repeaters.iter_mut() { rep.service(&routing_table, rank, &mut timer); } + #[cfg(all(feature = "target_kasli_soc", has_drtio))] + for expander_i in 0..=1 { + let mut io_expander = io_expander::IoExpander::new(&mut i2c, expander_i).unwrap(); + io_expander.service().expect("I2C I/O expander service failed") + } hardware_tick(&mut hardware_tick_ts, &mut timer); if drtiosat_tsc_loaded() { info!("TSC loaded from uplink");