From 8bba04b921b0ce175a5b88257ab2f547d409a9f9 Mon Sep 17 00:00:00 2001 From: den512 Date: Fri, 9 Jun 2023 11:36:06 +0800 Subject: [PATCH] kasli_soc: add SFP0..3 LED indication io_expander SFP0 LED map depend on hw_rev. --- src/gateware/kasli_soc.py | 8 ++ src/libboard_artiq/src/io_expander.rs | 128 +++++++++++++++++++------- src/runtime/src/main.rs | 41 ++++++++- src/satman/src/main.rs | 23 ++++- 4 files changed, 162 insertions(+), 38 deletions(-) diff --git a/src/gateware/kasli_soc.py b/src/gateware/kasli_soc.py index 63cb0a0..34c5372 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]", @@ -208,6 +212,7 @@ class GenericMaster(SoCCore): txout_buf, clk_sw=gtx0.tx_init.done) self.csr_devices.append("sys_crg") + self.crg = self.ps7 # HACK for eem_7series to find the clock self.crg.cd_sys = self.sys_crg.cd_sys platform.add_false_path_constraints( @@ -312,6 +317,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]", @@ -342,6 +349,7 @@ class GenericSatellite(SoCCore): platform.add_false_path_constraints( self.bootstrap.cd_bootstrap.clk, self.sys_crg.cd_sys.clk) self.csr_devices.append("sys_crg") + self.crg = self.ps7 # HACK for eem_7series to find the clock self.crg.cd_sys = self.sys_crg.cd_sys diff --git a/src/libboard_artiq/src/io_expander.rs b/src/libboard_artiq/src/io_expander.rs index 918b773..8e75711 100644 --- a/src/libboard_artiq/src/io_expander.rs +++ b/src/libboard_artiq/src/io_expander.rs @@ -10,44 +10,84 @@ 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]; + +//state of input/output pins +struct IoState { + iodir: [u8; 2], + out_current: [u8; 2], + out_target: [u8; 2] +} +static mut IO_STATE: [IoState; 2] = [ + IoState { + iodir: IO_DIR_MAPPING0, + out_current: [0; 2], + out_target: [0; 2], + }, + IoState { + iodir: IO_DIR_MAPPING1, + out_current: [0; 2], + out_target: [0; 2], + }, +]; + pub struct IoExpander<'a> { i2c: &'a mut i2c::I2c, address: u8, - iodir: [u8; 2], - 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], - out_current: [0; 2], - out_target: [0; 2], registers: Registers { iodira: 0x00, iodirb: 0x01, gpioa: 0x12, gpiob: 0x13, }, + virtual_led_mapping: &VIRTUAL_LED_MAPPING0, + channel: 0, }, 1 => IoExpander { i2c, address: 0x42, - iodir: [0xff; 2], - out_current: [0; 2], - out_target: [0; 2], registers: Registers { iodira: 0x00, iodirb: 0x01, gpioa: 0x12, gpiob: 0x13, }, + virtual_led_mapping: &VIRTUAL_LED_MAPPING1, + channel: 1, }, _ => return Err("incorrect I/O expander index"), }; @@ -92,49 +132,69 @@ impl<'a> IoExpander<'a> { } fn update_iodir(&mut self) -> Result<(), &'static str> { - self.write(self.registers.iodira, self.iodir[0])?; - self.write(self.registers.iodirb, self.iodir[1])?; + unsafe { + self.write(self.registers.iodira, IO_STATE[self.channel].iodir[0])?; + self.write(self.registers.iodirb, IO_STATE[self.channel].iodir[1])?; + }; Ok(()) } pub fn init(&mut self) -> Result<(), &'static str> { self.select()?; + unsafe { + IO_STATE[self.channel].iodir[0] = match self.channel { + 0 => IO_DIR_MAPPING0[0], + 1 => IO_DIR_MAPPING1[0], + _ => IO_DIR_INPUT_ALL, + }; + IO_STATE[self.channel].iodir[1] = match self.channel { + 0 => IO_DIR_MAPPING0[1], + 1 => IO_DIR_MAPPING1[1], + _ => IO_DIR_INPUT_ALL, + }; + IO_STATE[self.channel].out_target[0] = 0x00; + IO_STATE[self.channel].out_target[1] = 0x00; + IO_STATE[self.channel].out_current[0] = 0x00; + IO_STATE[self.channel].out_current[1] = 0x00; + }; self.update_iodir()?; - - self.out_current[0] = 0x00; self.write(self.registers.gpioa, 0x00)?; - self.out_current[1] = 0x00; self.write(self.registers.gpiob, 0x00)?; Ok(()) } - pub fn set_oe(&mut self, port: u8, outputs: u8) -> Result<(), &'static str> { - self.iodir[port as usize] &= !outputs; - self.update_iodir()?; - Ok(()) + pub fn set(&mut self, port: u8, bit: u8, high: bool) { + unsafe { + if high { + IO_STATE[self.channel].out_target[port as usize] |= 1 << bit; + } else { + IO_STATE[self.channel].out_target[port as usize] &= !(1 << bit); + } + }; } - pub fn set(&mut self, port: u8, bit: u8, high: bool) { - if high { - self.out_target[port as usize] |= 1 << bit; - } else { - self.out_target[port as usize] &= !(1 << bit); + 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()?; - if self.out_target[0] != self.out_current[0] { - self.write(self.registers.gpioa, self.out_target[0])?; - self.out_current[0] = self.out_target[0]; + unsafe { + if IO_STATE[self.channel].out_target != IO_STATE[self.channel].out_current { + self.select()?; + if IO_STATE[self.channel].out_target[0] != IO_STATE[self.channel].out_current[0] { + self.write(self.registers.gpioa, IO_STATE[self.channel].out_target[0])?; + IO_STATE[self.channel].out_current[0] = IO_STATE[self.channel].out_target[0]; + } + if IO_STATE[self.channel].out_target[1] != IO_STATE[self.channel].out_current[1] { + self.write(self.registers.gpiob, IO_STATE[self.channel].out_target[1])?; + IO_STATE[self.channel].out_current[1] = IO_STATE[self.channel].out_target[1]; + } } - if self.out_target[1] != self.out_current[1] { - self.write(self.registers.gpiob, self.out_target[1])?; - self.out_current[1] = self.out_target[1]; - } - } - + }; Ok(()) } } 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");