use csr::virtual_leds for SFP0..3 LED indication #244

Merged
sb10q merged 10 commits from morgan/artiq-zynq:feature into master 2023-08-28 16:08:11 +08:00
4 changed files with 87 additions and 1 deletions
Showing only changes of commit 9c6a993a7f - Show all commits

View File

@ -10,6 +10,7 @@ from migen.genlib.cdc import MultiReg
from migen_axi.integration.soc_core import SoCCore from migen_axi.integration.soc_core import SoCCore
from migen_axi.platforms import kasli_soc from migen_axi.platforms import kasli_soc
from misoc.interconnect.csr import * from misoc.interconnect.csr import *
from misoc.cores import virtual_leds
from misoc.integration import cpu_interface from misoc.integration import cpu_interface
from artiq.coredevice import jsondesc from artiq.coredevice import jsondesc
@ -305,6 +306,14 @@ class GenericMaster(SoCCore):
self.add_csr_group("grabber", self.grabber_csr_group) 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): class GenericSatellite(SoCCore):
def __init__(self, description, acpki=False): def __init__(self, description, acpki=False):
clk_freq = description["rtio_frequency"] clk_freq = description["rtio_frequency"]
@ -467,6 +476,12 @@ class GenericSatellite(SoCCore):
self.add_csr_group("grabber", self.grabber_csr_group) self.add_csr_group("grabber", self.grabber_csr_group)
# no RTIO CRG here # 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): def write_mem_file(soc, filename):
with open(filename, "w") as f: with open(filename, "w") as f:

View File

@ -1,6 +1,8 @@
use libboard_zynq::i2c; use libboard_zynq::i2c;
use log::info; use log::info;
use crate::pl::csr;
// Only the bare minimum registers. Bits/IO connections equivalent between IC types. // Only the bare minimum registers. Bits/IO connections equivalent between IC types.
struct Registers { struct Registers {
// PCA9539 equivalent register names in comments // PCA9539 equivalent register names in comments
@ -10,6 +12,26 @@ struct Registers {
gpiob: u8, // Output Port 1 gpiob: u8, // Output Port 1
} }
//IO expanders pins
const IO_DIR_INPUT_ALL: u8 = 0xFF;
morgan marked this conversation as resolved Outdated
Outdated
Review

Doesn't seem like this constant adds anything to the code.

Doesn't seem like this constant adds anything to the code.
const IO_DIR_OUT_SFP_TX_DISABLE: u8 = !0x02;
morgan marked this conversation as resolved Outdated
Outdated
Review

Move the inversions into the iodir definition.

Move the inversions into the iodir definition.
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] = [
morgan marked this conversation as resolved Outdated
Outdated
Review

It's just called "iodir", not "mapping"

It's just called "iodir", not "mapping"
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> { pub struct IoExpander<'a> {
i2c: &'a mut i2c::I2c, i2c: &'a mut i2c::I2c,
address: u8, address: u8,
@ -123,6 +145,11 @@ impl<'a> IoExpander<'a> {
} }
pub fn service(&mut self) -> Result<(), &'static str> { 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 { if self.out_target != self.out_current {
self.select()?; self.select()?;
if self.out_target[0] != self.out_current[0] { if self.out_target[0] != self.out_current[0] {

View File

@ -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))]
morgan marked this conversation as resolved Outdated
Outdated
Review

The only unsafe line is the next one, no?

The only unsafe line is the next one, no?

LAST_VIRTUAL_LED_STATUS is a static mut. So it need an unsafe too

```LAST_VIRTUAL_LED_STATUS``` is a static mut. So it need an unsafe too
Outdated
Review

I don't think this should be gated on has_drtio.

I don't think this should be gated on has_drtio.
fn wait_for_rtio_led_change() -> nb::Result<(), Void> {
unsafe {
morgan marked this conversation as resolved Outdated

Could save a CSR call by getting the value before the if

Could save a CSR call by getting the value before the ``if``
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;
}
Outdated
Review

I don't think this entire block_async is any useful. It also duplicates logic and breaks abstraction layers. Anyway just remove it.

I don't think this entire block_async is any useful. It also duplicates logic and breaks abstraction layers. Anyway just remove it.

The block_async prevent "io_expanders_service" from locking up the whole cpu. As it blocks the loop from running over and over again.

The block_async prevent "io_expanders_service" from locking up the whole cpu. As it blocks the loop from running over and over again.
if READ_RTIO_LED != SEEN_RTIO_LED {
Ok(())
} else {
Err(nb::Error::WouldBlock)
morgan marked this conversation as resolved Outdated
Outdated
Review

async_ in the name sounds redundant since this is declared ``async fn

``async_`` in the name sounds redundant since this is declared ``async fn
}
}
Outdated
Review

It's confusing to create IO expander objects, which also should be singletons and do more than controlling LEDs, into a function whose name indicates it is about LEDs.

It's confusing to create IO expander objects, which also should be singletons and do more than controlling LEDs, into a function whose name indicates it is about LEDs.
Outdated
Review

As I told you before, those I/O expander objects should be created only once in the entire firmware.

Additionally, you create them once with i2c::I2C_BUS and once with &mut *i2c_ptr, which is inconsistent.

As I told you before, those I/O expander objects should be created only once in the entire firmware. Additionally, you create them once with ``i2c::I2C_BUS`` and once with ``&mut *i2c_ptr``, which is inconsistent.
}
#[cfg(all(feature = "target_kasli_soc", has_drtio))]
morgan marked this conversation as resolved Outdated
Outdated
Review

Just inline it.

 block_async!(|| {
   let current = unsafe { pl::csr::virtual_leds::status_read() };
   ...
Just inline it. ``` block_async!(|| { let current = unsafe { pl::csr::virtual_leds::status_read() }; ... ```
async fn async_rtio_led() {
let _ = block_async!(wait_for_rtio_led_change()).await;
morgan marked this conversation as resolved Outdated

Debug leftover? Remove pls

Debug leftover? Remove pls
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]; static mut LOG_BUFFER: [u8; 1 << 17] = [0; 1 << 17];
#[no_mangle] #[no_mangle]

View File

@ -664,6 +664,12 @@ pub extern "C" fn main_core0() -> i32 {
for mut rep in repeaters.iter_mut() { for mut rep in repeaters.iter_mut() {
rep.service(&routing_table, rank, &mut timer); 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); 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() { for mut rep in repeaters.iter_mut() {
rep.service(&routing_table, rank, &mut timer); 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); hardware_tick(&mut hardware_tick_ts, &mut timer);
if drtiosat_tsc_loaded() { if drtiosat_tsc_loaded() {
info!("TSC loaded from uplink"); info!("TSC loaded from uplink");