diff --git a/src/gateware/kasli_soc.py b/src/gateware/kasli_soc.py index a209bfd..f5e2f42 100755 --- a/src/gateware/kasli_soc.py +++ b/src/gateware/kasli_soc.py @@ -86,6 +86,8 @@ class GenericStandalone(SoCCore): self.acpki = acpki self.rustc_cfg = dict() + self.rustc_cfg["hw_rev"] = description["hw_rev"] + platform = kasli_soc.Platform() platform.toolchain.bitstream_commands.extend([ "set_property BITSTREAM.GENERAL.COMPRESS True [current_design]", @@ -178,6 +180,8 @@ class GenericMaster(SoCCore): self.acpki = acpki self.rustc_cfg = dict() + self.rustc_cfg["hw_rev"] = description["hw_rev"] + platform = kasli_soc.Platform() platform.toolchain.bitstream_commands.extend([ "set_property BITSTREAM.GENERAL.COMPRESS True [current_design]", @@ -312,6 +316,8 @@ class GenericSatellite(SoCCore): self.acpki = acpki self.rustc_cfg = dict() + self.rustc_cfg["hw_rev"] = description["hw_rev"] + platform = kasli_soc.Platform() platform.toolchain.bitstream_commands.extend([ "set_property BITSTREAM.GENERAL.COMPRESS True [current_design]", diff --git a/src/libboard_artiq/src/io_expander.rs b/src/libboard_artiq/src/io_expander.rs index 918b773..d66d135 100644 --- a/src/libboard_artiq/src/io_expander.rs +++ b/src/libboard_artiq/src/io_expander.rs @@ -10,6 +10,27 @@ 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; +//SFP0 LED has different place in v1.1 +#[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, @@ -17,16 +38,25 @@ pub struct IoExpander<'a> { out_current: [u8; 2], out_target: [u8; 2], registers: Registers, + virtual_led_mapping: &'static [(u8, u8, u8)], + channel: usize, } impl<'a> IoExpander<'a> { pub fn new(i2c: &'a mut i2c::I2c, index: u8) -> Result { + #[cfg(hw_rev = "v1.0")] + const VIRTUAL_LED_MAPPING0: [(u8, u8, u8); 2] = [(0, 0, 6), (1, 1, 6)]; + #[cfg(hw_rev = "v1.1")] + const VIRTUAL_LED_MAPPING0: [(u8, u8, u8); 2] = [(0, 0, 7), (1, 1, 6)]; + + const VIRTUAL_LED_MAPPING1: [(u8, u8, u8); 2] = [(2, 0, 6), (3, 1, 6)]; + // Both expanders on SHARED I2C bus let mut io_expander = match index { 0 => IoExpander { i2c, address: 0x40, - iodir: [0xff; 2], + iodir: IO_DIR_MAPPING0, out_current: [0; 2], out_target: [0; 2], registers: Registers { @@ -35,11 +65,13 @@ impl<'a> IoExpander<'a> { gpioa: 0x12, gpiob: 0x13, }, + virtual_led_mapping: &VIRTUAL_LED_MAPPING0, + channel: 0, }, 1 => IoExpander { i2c, address: 0x42, - iodir: [0xff; 2], + iodir: IO_DIR_MAPPING1, out_current: [0; 2], out_target: [0; 2], registers: Registers { @@ -48,6 +80,8 @@ impl<'a> IoExpander<'a> { gpioa: 0x12, gpiob: 0x13, }, + virtual_led_mapping: &VIRTUAL_LED_MAPPING1, + channel: 1, }, _ => return Err("incorrect I/O expander index"), }; @@ -99,6 +133,13 @@ impl<'a> IoExpander<'a> { pub fn init(&mut self) -> Result<(), &'static str> { self.select()?; + + self.iodir = match self.channel { + 0 => IO_DIR_MAPPING0, + 1 => IO_DIR_MAPPING1, + _ => [IO_DIR_INPUT_ALL; 2], + }; + self.update_iodir()?; self.out_current[0] = 0x00; @@ -122,6 +163,14 @@ impl<'a> IoExpander<'a> { } } + pub fn led_update(&mut self, led_state: u8) { + for (led_num, port, bit) in self.virtual_led_mapping.iter() { + let level = (led_state >> led_num) & 1; + self.set(*port, *bit, level != 0); + } + self.service().unwrap(); + } + pub fn service(&mut self) -> Result<(), &'static str> { if self.out_target != self.out_current { self.select()?; diff --git a/src/runtime/src/main.rs b/src/runtime/src/main.rs index 3789888..98d271e 100644 --- a/src/runtime/src/main.rs +++ b/src/runtime/src/main.rs @@ -102,6 +102,42 @@ 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 { + //let linkno = linkno as usize; + 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() { + loop { + 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.led_update(READ_RTIO_LED); + } + SEEN_RTIO_LED = READ_RTIO_LED; + }; + } +} + static mut LOG_BUFFER: [u8; 1 << 17] = [0; 1 << 17]; #[no_mangle] @@ -130,8 +166,6 @@ pub fn main_core0() { let mut io_expander = io_expander::IoExpander::new(i2c, expander_i).unwrap(); io_expander.init().expect("I2C I/O expander #0 initialization failed"); // Actively drive TX_DISABLE to false on SFP0..3 - io_expander.set_oe(0, 1 << 1).unwrap(); - io_expander.set_oe(1, 1 << 1).unwrap(); io_expander.set(0, 1, false); io_expander.set(1, 1, false); io_expander.service().unwrap(); @@ -150,5 +184,8 @@ pub fn main_core0() { task::spawn(report_async_rtio_errors()); + #[cfg(all(feature = "target_kasli_soc", has_drtio))] + task::spawn(async_rtio_led()); + comms::main(timer, cfg); } diff --git a/src/satman/src/main.rs b/src/satman/src/main.rs index 4af58b2..908b768 100644 --- a/src/satman/src/main.rs +++ b/src/satman/src/main.rs @@ -591,6 +591,23 @@ const SI5324_SETTINGS: si5324::FrequencySettings = si5324::FrequencySettings { crystal_as_ckin2: true, }; +#[cfg(all(feature = "target_kasli_soc", has_drtio))] +fn sfp_leds_update(i2c: &mut I2c) { + let mut virtual_leds; + unsafe { + virtual_leds = csr::drtiosat::rx_up_read(); + let len: usize = csr::DRTIOREP.len(); + for linkno in 0..len { + virtual_leds |= (csr::DRTIOREP[linkno].rx_up_read)() << (linkno + 1); + } + } + + for expander_i in 0..=1 { + let mut io_expander = io_expander::IoExpander::new(i2c, expander_i).unwrap(); + io_expander.led_update(virtual_leds); + } +} + static mut LOG_BUFFER: [u8; 1 << 17] = [0; 1 << 17]; #[no_mangle] @@ -617,8 +634,6 @@ pub extern "C" fn main_core0() -> i32 { let mut io_expander = io_expander::IoExpander::new(&mut i2c, expander_i).unwrap(); io_expander.init().expect("I2C I/O expander #0 initialization failed"); // Actively drive TX_DISABLE to false on SFP0..3 - io_expander.set_oe(0, 1 << 1).unwrap(); - io_expander.set_oe(1, 1 << 1).unwrap(); io_expander.set(0, 1, false); io_expander.set(1, 1, false); io_expander.service().unwrap(); @@ -664,6 +679,8 @@ 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))] + sfp_leds_update(&mut i2c); hardware_tick(&mut hardware_tick_ts, &mut timer); } @@ -700,6 +717,8 @@ 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))] + sfp_leds_update(&mut i2c); hardware_tick(&mut hardware_tick_ts, &mut timer); if drtiosat_tsc_loaded() { info!("TSC loaded from uplink");