diff --git a/Cargo.lock b/Cargo.lock index 1bc0afbcf4..eacc291587 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1212,6 +1212,57 @@ dependencies = [ "zerocopy-derive 0.8.25", ] +[[package]] +name = "drv-front-io-api" +version = "0.1.0" +dependencies = [ + "build-fpga-regmap", + "build-util", + "counters", + "derive-idol-err", + "drv-auxflash-api", + "drv-fpga-api", + "drv-i2c-api", + "drv-i2c-devices", + "hubpack", + "idol", + "idol-runtime", + "num-traits", + "serde", + "transceiver-messages", + "userlib", + "vsc85xx", + "zerocopy 0.8.25", + "zerocopy-derive 0.8.25", +] + +[[package]] +name = "drv-front-io-server" +version = "0.1.0" +dependencies = [ + "build-i2c", + "build-util", + "drv-auxflash-api", + "drv-fpga-api", + "drv-fpga-user-api", + "drv-front-io-api", + "drv-i2c-api", + "drv-i2c-devices", + "drv-sidecar-mainboard-controller", + "drv-transceivers-api", + "enum-map", + "hubpack", + "idol", + "idol-runtime", + "multitimer", + "num-traits", + "ringbuf", + "serde", + "userlib", + "zerocopy 0.8.25", + "zerocopy-derive 0.8.25", +] + [[package]] name = "drv-gimlet-hf-server" version = "0.1.0" @@ -1254,6 +1305,7 @@ dependencies = [ "drv-stm32h7-spi", "drv-stm32xx-sys-api", "gnarle", + "hubpack", "idol", "idol-runtime", "num-derive 0.4.2", @@ -1771,11 +1823,11 @@ dependencies = [ "cortex-m", "drv-fpga-api", "drv-fpga-user-api", + "drv-front-io-api", "drv-i2c-api", "drv-i2c-devices", "drv-medusa-seq-api", "drv-packrat-vpd-loader", - "drv-sidecar-front-io", "drv-sidecar-mainboard-controller", "drv-stm32xx-sys-api", "hubpack", @@ -2040,31 +2092,6 @@ dependencies = [ "zerocopy-derive 0.8.25", ] -[[package]] -name = "drv-sidecar-front-io" -version = "0.1.0" -dependencies = [ - "build-fpga-regmap", - "build-util", - "cfg-if", - "drv-auxflash-api", - "drv-fpga-api", - "drv-i2c-api", - "drv-i2c-devices", - "drv-transceivers-api", - "gnarle", - "idol", - "num-derive 0.4.2", - "num-traits", - "ringbuf", - "transceiver-messages", - "userlib", - "vsc7448-pac", - "vsc85xx", - "zerocopy 0.8.25", - "zerocopy-derive 0.8.25", -] - [[package]] name = "drv-sidecar-mainboard-controller" version = "0.1.0" @@ -2106,6 +2133,7 @@ dependencies = [ "derive-idol-err", "drv-fpga-api", "drv-fpga-user-api", + "drv-front-io-api", "drv-sidecar-mainboard-controller", "hubpack", "idol", @@ -2128,10 +2156,10 @@ dependencies = [ "cortex-m", "drv-fpga-api", "drv-fpga-user-api", + "drv-front-io-api", "drv-i2c-api", "drv-i2c-devices", "drv-packrat-vpd-loader", - "drv-sidecar-front-io", "drv-sidecar-mainboard-controller", "drv-sidecar-seq-api", "drv-stm32xx-sys-api", @@ -2625,6 +2653,7 @@ dependencies = [ "counters", "derive-idol-err", "drv-fpga-api", + "drv-front-io-api", "idol", "idol-runtime", "num-traits", @@ -2639,21 +2668,17 @@ dependencies = [ name = "drv-transceivers-server" version = "0.1.0" dependencies = [ - "build-i2c", "build-util", "cfg-if", "counters", "drv-fpga-api", - "drv-i2c-api", - "drv-i2c-devices", - "drv-sidecar-front-io", + "drv-front-io-api", "drv-sidecar-seq-api", "drv-transceivers-api", "enum-map", "hubpack", "idol", "idol-runtime", - "multitimer", "num-traits", "ringbuf", "serde", @@ -3341,7 +3366,7 @@ checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" [[package]] name = "idol" version = "0.5.0" -source = "git+https://github.com/oxidecomputer/idolatry.git#6c54f3b87c58f329b07d5bf736624cf591b7c8c5" +source = "git+https://github.com/oxidecomputer/idolatry.git#7df9c0d9eefed605e5d108f33e42c1e27e1ee11d" dependencies = [ "indexmap 1.9.1", "once_cell", @@ -5216,6 +5241,7 @@ dependencies = [ "drv-caboose", "drv-caboose-pos", "drv-cpu-seq-api", + "drv-front-io-api", "drv-hf-api", "drv-ignition-api", "drv-lpc55-update-api", @@ -5225,7 +5251,6 @@ dependencies = [ "drv-sprot-api", "drv-stm32h7-update-api", "drv-stm32h7-usart", - "drv-transceivers-api", "drv-update-api", "drv-user-leds-api", "dump-agent-api", @@ -5554,9 +5579,9 @@ dependencies = [ "build-util", "cfg-if", "counters", + "drv-front-io-api", "drv-medusa-seq-api", "drv-monorail-api", - "drv-sidecar-front-io", "drv-sidecar-mainboard-controller", "drv-sidecar-seq-api", "drv-spi-api", @@ -5869,6 +5894,7 @@ dependencies = [ "cortex-m", "counters", "drv-cpu-seq-api", + "drv-front-io-api", "drv-i2c-api", "drv-i2c-devices", "drv-onewire", diff --git a/app/medusa/base.toml b/app/medusa/base.toml index f78efbe5bb..93b81aee96 100644 --- a/app/medusa/base.toml +++ b/app/medusa/base.toml @@ -263,7 +263,7 @@ memory-size = 33_554_432 # 256 Mib / 32 MiB slot-count = 16 # 2 MiB slots [[auxflash.blobs]] -file = "drv/sidecar-front-io/sidecar_qsfp_x32_controller_rev_b_c.bit" +file = "drv/front-io-api/sidecar_qsfp_x32_controller_rev_b_c.bit" compress = true tag = "QSFP" diff --git a/app/medusa/src/main.rs b/app/medusa/src/main.rs index ca6260f187..4781c891b3 100644 --- a/app/medusa/src/main.rs +++ b/app/medusa/src/main.rs @@ -21,9 +21,6 @@ fn main() -> ! { const CYCLES_PER_MS: u32 = 400_000; - #[cfg(feature = "traptrace")] - kern::profiling::configure_events_table(tracing::table()); - unsafe { kern::startup::start_kernel(CYCLES_PER_MS) } } diff --git a/app/minibar/app.toml b/app/minibar/app.toml index 57b46fdc47..21f62c3bd3 100644 --- a/app/minibar/app.toml +++ b/app/minibar/app.toml @@ -119,7 +119,6 @@ notifications = ["timer"] [tasks.power] name = "task-power" -features = ["sidecar"] priority = 6 max-sizes = {flash = 32768, ram = 8192 } stacksize = 3800 diff --git a/app/sidecar/base.toml b/app/sidecar/base.toml index c399811049..c4bdc0a12a 100644 --- a/app/sidecar/base.toml +++ b/app/sidecar/base.toml @@ -87,7 +87,7 @@ task-slots = ["sys"] [tasks.net] name = "task-net" stacksize = 10000 -priority = 5 +priority = 6 features = ["mgmt", "h753", "sidecar", "vlan", "vpd-mac", "use-spi-core", "spi3"] max-sizes = {flash = 131072, ram = 131072, sram1_mac = 16384} sections = {eth_bulk = "sram1_mac"} @@ -103,7 +103,7 @@ task-slots = ["sys", "packrat", { seq = "sequencer" }, "jefe"] [tasks.control_plane_agent] name = "task-control-plane-agent" -priority = 7 +priority = 8 # This is a big number -- do we need to tune this? stacksize = 12000 start = true @@ -121,9 +121,9 @@ task-slots = [ "sequencer", "sprot", "sys", - "transceivers", "update_server", "validate", + "front_io", "vpd", ] features = ["sidecar", "vlan", "auxflash", "vpd"] @@ -146,7 +146,7 @@ interrupts = {"spi4.irq" = "spi-irq"} [tasks.udpecho] name = "task-udpecho" -priority = 6 +priority = 7 max-sizes = {flash = 16384, ram = 8192} stacksize = 4096 start = true @@ -156,7 +156,7 @@ notifications = ["socket"] [tasks.udpbroadcast] name = "task-udpbroadcast" -priority = 6 +priority = 7 max-sizes = {flash = 16384, ram = 8192} stacksize = 4096 start = true @@ -171,7 +171,7 @@ max-sizes = {flash = 262144, ram = 16384} features = ["mgmt", "sidecar", "vlan", "use-spi-core", "h753", "spi2"] stacksize = 4096 start = true -task-slots = ["ecp5_front_io", "sys", { seq = "sequencer" }] +task-slots = ["front_io", "sys", { seq = "sequencer" }] uses = ["spi2"] notifications = ["spi-irq", "wake-timer"] interrupts = {"spi2.irq" = "spi-irq"} @@ -240,20 +240,33 @@ interrupts = {"spi1.irq" = "spi-irq"} [tasks.transceivers] name = "drv-transceivers-server" features = ["vlan", "thermal-control"] -priority = 6 +priority = 7 max-sizes = {flash = 65536, ram = 16384} stacksize = 4096 start = true task-slots = [ - "i2c_driver", "net", "sensor", "sys", "thermal", - {front_io = "ecp5_front_io"}, + "front_io", {seq = "sequencer"}] notifications = ["socket", "timer"] +[tasks.front_io] +name = "drv-front-io-server" +priority = 4 +max-sizes = {flash = 65536, ram = 16384} +stacksize = 4096 +start = true +task-slots = [ + "sys", + "auxflash", + "i2c_driver", + {mainboard = "ecp5_mainboard"}, + {front_io = "ecp5_front_io"}] +notifications = ["timer"] + [tasks.packrat] name = "task-packrat" priority = 3 @@ -264,7 +277,7 @@ task-slots = [] [tasks.sequencer] name = "drv-sidecar-seq-server" -priority = 4 +priority = 5 stacksize = 4096 start = true task-slots = [ @@ -272,14 +285,14 @@ task-slots = [ "i2c_driver", "auxflash", "packrat", - {mainboard = "ecp5_mainboard"}, - {front_io = "ecp5_front_io"}] + "front_io", + {mainboard = "ecp5_mainboard"}] notifications = ["timer"] [tasks.thermal] name = "task-thermal" features = ["sidecar"] -priority = 5 +priority = 6 max-sizes = {flash = 32768, ram = 16384 } stacksize = 8096 start = true @@ -307,7 +320,7 @@ task-slots = ["i2c_driver"] [tasks.ignition] name = "drv-ignition-server" features = ["sequencer"] -priority = 5 +priority = 6 max-sizes = {flash = 16384, ram = 8192} stacksize = 2048 start = true @@ -324,7 +337,7 @@ stacksize = 800 [tasks.dump_agent] name = "task-dump-agent" -priority = 6 +priority = 7 max-sizes = {flash = 32768, ram = 16384 } start = true task-slots = ["sprot", "jefe", "net"] @@ -335,7 +348,7 @@ features = ["net", "vlan"] [tasks.idle] name = "task-idle" -priority = 8 +priority = 10 max-sizes = {flash = 128, ram = 256} stacksize = 256 start = true @@ -1168,7 +1181,7 @@ memory-size = 33_554_432 # 256 Mib / 32 MiB slot-count = 16 # 2 MiB slots [[auxflash.blobs]] -file = "drv/sidecar-front-io/sidecar_qsfp_x32_controller_rev_b_c.bit" +file = "drv/front-io-api/sidecar_qsfp_x32_controller_rev_b_c.bit" compress = true tag = "QSFP" diff --git a/app/sidecar/dev.toml b/app/sidecar/dev.toml index e1cce16fa9..2381bc0883 100644 --- a/app/sidecar/dev.toml +++ b/app/sidecar/dev.toml @@ -4,7 +4,7 @@ request_reset = ["udprpc"] [tasks.udprpc] name = "task-udprpc" -priority = 6 +priority = 7 max-sizes = {flash = 32768, ram = 8192} stacksize = 4096 start = true diff --git a/build/fpga-regmap/src/lib.rs b/build/fpga-regmap/src/lib.rs index 5406efccd5..45f114a54d 100644 --- a/build/fpga-regmap/src/lib.rs +++ b/build/fpga-regmap/src/lib.rs @@ -202,7 +202,10 @@ fn write_reg_fields( writeln!( output, " -{prefix} #[derive(Copy, Clone, Eq, PartialEq)] +{prefix} use hubpack::SerializedSize; +{prefix} use serde::{{Deserialize, Serialize}}; +{prefix} #[derive(Copy, Clone, Eq, PartialEq, Deserialize, Serialize, SerializedSize)] +{prefix} #[repr(u8)] {prefix} #[allow(dead_code)] {prefix} pub enum {encode_name} {{" ) diff --git a/drv/front-io-api/Cargo.toml b/drv/front-io-api/Cargo.toml new file mode 100644 index 0000000000..c17a7b200c --- /dev/null +++ b/drv/front-io-api/Cargo.toml @@ -0,0 +1,32 @@ +[package] +name = "drv-front-io-api" +version = "0.1.0" +edition = "2021" + +[dependencies] +hubpack.workspace = true +num-traits.workspace = true +idol-runtime.workspace = true +serde.workspace = true +transceiver-messages.workspace = true +zerocopy.workspace = true +zerocopy-derive.workspace = true + +counters = { path = "../../lib/counters", features = ["derive"] } +drv-auxflash-api = { path = "../../drv/auxflash-api" } +drv-fpga-api = { path = "../fpga-api", features = ["auxflash"] } +drv-i2c-api = { path = "../i2c-api" } +drv-i2c-devices = { path = "../i2c-devices" } +derive-idol-err = { path = "../../lib/derive-idol-err" } +userlib = { path = "../../sys/userlib" } +vsc85xx = { path = "../../drv/vsc85xx" } + +[build-dependencies] +build-fpga-regmap = { path = "../../build/fpga-regmap" } +build-util = { path = "../../build/util" } +idol.workspace = true + +[lib] +test = false +doctest = false +bench = false diff --git a/drv/sidecar-front-io/build.rs b/drv/front-io-api/build.rs similarity index 88% rename from drv/sidecar-front-io/build.rs rename to drv/front-io-api/build.rs index ce49969834..2206223b92 100644 --- a/drv/sidecar-front-io/build.rs +++ b/drv/front-io-api/build.rs @@ -1,7 +1,6 @@ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at https://mozilla.org/MPL/2.0/. - use build_fpga_regmap::fpga_regs; use std::{fs, io::Write}; @@ -16,6 +15,9 @@ fn main() -> Result<(), Box> { { panic!("unknown target board"); } + idol::Generator::new() + .with_counters(idol::CounterSettings::default()) + .build_client_stub("../../idl/front-io.idol", "client_stub.rs")?; let out_dir = build_util::out_dir(); let out_file = out_dir.join("sidecar_qsfp_x32_controller_regs.rs"); diff --git a/drv/sidecar-front-io/sidecar_qsfp_x32_controller_regs.adoc b/drv/front-io-api/sidecar_qsfp_x32_controller_regs.adoc similarity index 100% rename from drv/sidecar-front-io/sidecar_qsfp_x32_controller_regs.adoc rename to drv/front-io-api/sidecar_qsfp_x32_controller_regs.adoc diff --git a/drv/sidecar-front-io/sidecar_qsfp_x32_controller_regs.html b/drv/front-io-api/sidecar_qsfp_x32_controller_regs.html similarity index 100% rename from drv/sidecar-front-io/sidecar_qsfp_x32_controller_regs.html rename to drv/front-io-api/sidecar_qsfp_x32_controller_regs.html diff --git a/drv/sidecar-front-io/sidecar_qsfp_x32_controller_regs.json b/drv/front-io-api/sidecar_qsfp_x32_controller_regs.json similarity index 100% rename from drv/sidecar-front-io/sidecar_qsfp_x32_controller_regs.json rename to drv/front-io-api/sidecar_qsfp_x32_controller_regs.json diff --git a/drv/sidecar-front-io/sidecar_qsfp_x32_controller_rev_b_c.bit b/drv/front-io-api/sidecar_qsfp_x32_controller_rev_b_c.bit similarity index 100% rename from drv/sidecar-front-io/sidecar_qsfp_x32_controller_rev_b_c.bit rename to drv/front-io-api/sidecar_qsfp_x32_controller_rev_b_c.bit diff --git a/drv/sidecar-front-io/src/controller.rs b/drv/front-io-api/src/controller.rs similarity index 98% rename from drv/sidecar-front-io/src/controller.rs rename to drv/front-io-api/src/controller.rs index ef7a205568..0c40e651a8 100644 --- a/drv/sidecar-front-io/src/controller.rs +++ b/drv/front-io-api/src/controller.rs @@ -8,7 +8,7 @@ use userlib::UnwrapLite; pub struct FrontIOController { fpga: Fpga, - user_design: FpgaUserDesign, + pub user_design: FpgaUserDesign, } impl FrontIOController { diff --git a/drv/sidecar-front-io/src/leds.rs b/drv/front-io-api/src/leds.rs similarity index 93% rename from drv/sidecar-front-io/src/leds.rs rename to drv/front-io-api/src/leds.rs index c00f84a301..d3624a2318 100644 --- a/drv/sidecar-front-io/src/leds.rs +++ b/drv/front-io-api/src/leds.rs @@ -1,10 +1,10 @@ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at https://mozilla.org/MPL/2.0/. -use super::transceivers::{LogicalPort, LogicalPortMask}; +use crate::transceivers::{LogicalPort, LogicalPortMask, NUM_PORTS}; use drv_i2c_api::I2cDevice; use drv_i2c_devices::pca9956b::{Error, LedErr, Pca9956B}; -use drv_transceivers_api::NUM_PORTS; +use transceiver_messages::message::LedState; /// Leds controllers and brightness state pub struct Leds { @@ -33,6 +33,36 @@ const DEFAULT_LED_CURRENT: u8 = 44; /// Default written into the PCA9956B PWMx registers. const DEFAULT_LED_PWM: u8 = 255; +#[derive(Copy, Clone)] +pub struct LedStates([LedState; NUM_PORTS as usize]); + +impl LedStates { + pub fn set(mut self, mask: LogicalPortMask, state: LedState) { + for port in mask.to_indices() { + self.0[port.0 as usize] = state + } + } + + pub fn get(self, port: LogicalPort) -> LedState { + self.0[port.0 as usize] + } +} + +impl Default for LedStates { + fn default() -> Self { + LedStates([LedState::Off; NUM_PORTS as usize]) + } +} + +impl<'a> IntoIterator for &'a LedStates { + type Item = &'a LedState; + type IntoIter = core::slice::Iter<'a, LedState>; + + fn into_iter(self) -> Self::IntoIter { + self.0.as_slice().iter() + } +} + /// One controller for the LEDs on the left side, one for those on the right #[derive(Copy, Clone, PartialEq, Eq)] pub enum LedController { diff --git a/drv/front-io-api/src/lib.rs b/drv/front-io-api/src/lib.rs new file mode 100644 index 0000000000..bd7d3b4ca0 --- /dev/null +++ b/drv/front-io-api/src/lib.rs @@ -0,0 +1,82 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. + +//! API crate for the Front IO server. + +#![no_std] + +use crate::phy_smi::PhyOscState; +use counters::Count; +use derive_idol_err::IdolError; +use drv_fpga_api::FpgaError; +use hubpack::SerializedSize; +use serde::{Deserialize, Serialize}; +use userlib::*; + +// Re-export for use in the server +pub use transceiver_messages::message::LedState; + +pub mod controller; +pub mod leds; +pub mod phy_smi; +pub mod transceivers; + +#[derive( + Copy, Clone, Debug, FromPrimitive, Eq, PartialEq, IdolError, Count, +)] +pub enum FrontIOError { + FpgaError = 1, + NotPresent, + NotReady, + InvalidPortNumber, + InvalidNumberOfBytes, + InvalidPhysicalToLogicalMap, + InvalidModuleResult, + LedInitFailure, + PowerFault, + + #[idol(server_death)] + ServerRestarted, +} + +impl From for FrontIOError { + fn from(_: FpgaError) -> Self { + Self::FpgaError + } +} + +#[derive( + Copy, + Clone, + Debug, + Count, + Eq, + PartialEq, + Deserialize, + Serialize, + SerializedSize, +)] +pub enum FrontIOStatus { + /// Start state + Init, + /// No board detected + NotPresent, + /// The FPGAs are being configured + FpgaInit, + /// Confirming that the PHY oscillator is behaving + OscInit, + /// Board is present and fully operational + Ready, +} + +include!(concat!( + env!("OUT_DIR"), + "/sidecar_qsfp_x32_controller_regs.rs" +)); + +use crate::transceivers::{ + LogicalPort, LogicalPortMask, ModuleResult, ModuleResultNoFailure, + PortI2CStatus, TransceiverStatus, +}; +include!(concat!(env!("OUT_DIR"), "/client_stub.rs")); diff --git a/drv/sidecar-front-io/src/phy_smi.rs b/drv/front-io-api/src/phy_smi.rs similarity index 94% rename from drv/sidecar-front-io/src/phy_smi.rs rename to drv/front-io-api/src/phy_smi.rs index 1381d440e0..cc3fd80e62 100644 --- a/drv/sidecar-front-io/src/phy_smi.rs +++ b/drv/front-io-api/src/phy_smi.rs @@ -2,21 +2,32 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at https://mozilla.org/MPL/2.0/. -use core::cell::Cell; - use crate::{Addr, Reg}; +use core::cell::Cell; use drv_fpga_api::{FpgaError, FpgaUserDesign, WriteOp}; +use userlib::FromPrimitive; use vsc85xx::{PhyRw, VscError}; use zerocopy::{ byteorder::little_endian, FromBytes, Immutable, IntoBytes, KnownLayout, Unaligned, }; -#[derive(Copy, Clone, Eq, Debug, PartialEq)] +#[derive( + Copy, + Clone, + Debug, + FromPrimitive, + Eq, + PartialEq, + IntoBytes, + Immutable, + KnownLayout, +)] +#[repr(u8)] pub enum PhyOscState { - Unknown, - Bad, - Good, + Unknown = 0, + Bad = 1, + Good = 2, } pub struct PhySmi { @@ -122,7 +133,7 @@ impl PhySmi { } #[inline(never)] - fn read_raw_inner(&self, phy: u8, reg: u8) -> Result { + pub fn read_raw_inner(&self, phy: u8, reg: u8) -> Result { let request = SmiReadRequest { phy, reg, @@ -155,7 +166,7 @@ impl PhySmi { } #[inline(never)] - fn write_raw_inner( + pub fn write_raw_inner( &self, phy: u8, reg: u8, diff --git a/drv/sidecar-front-io/src/transceivers.rs b/drv/front-io-api/src/transceivers.rs similarity index 91% rename from drv/sidecar-front-io/src/transceivers.rs rename to drv/front-io-api/src/transceivers.rs index 61d25c150f..bb062c217f 100644 --- a/drv/sidecar-front-io/src/transceivers.rs +++ b/drv/front-io-api/src/transceivers.rs @@ -2,9 +2,10 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at https://mozilla.org/MPL/2.0/. -use crate::{Addr, Reg}; +use crate::{Addr, FrontIOError, Reg}; use drv_fpga_api::{FpgaError, FpgaUserDesign, ReadOp, WriteOp}; -use drv_transceivers_api::{ModuleStatus, TransceiversError, NUM_PORTS}; +use hubpack::SerializedSize; +use serde::{Deserialize, Serialize}; use transceiver_messages::ModuleId; use userlib::UnwrapLite; use zerocopy::{ @@ -12,6 +13,19 @@ use zerocopy::{ Unaligned, }; +/// The only instantiation of Front IO board that exists is one with 32 QSFP +/// ports. +pub const NUM_PORTS: u8 = 32; + +/// Size in bytes of a page section we will read or write +/// +/// QSFP module's internal memory map is 256 bytes, with the lower 128 being +/// static and then the upper 128 are paged in. The internal address register +/// is only 7 bits, so you can only access half in any single transaction and +/// thus our communication mechanisms have been designed for that. +/// See SFF-8636 and CMIS specifications for details. +pub const PAGE_SIZE_BYTES: usize = 128; + // The transceiver modules are split across two FPGAs on the QSFP Front IO // board, so while we present the modules as a unit, the communication is // actually bifurcated. @@ -21,14 +35,16 @@ pub struct Transceivers { // There are two FPGA controllers, each controlling the FPGA on either the left // or right of the board. -#[derive(Copy, Clone, PartialEq, Eq)] +#[derive( + Copy, Clone, PartialEq, Eq, Deserialize, Serialize, SerializedSize, +)] pub enum FpgaController { Left = 0, Right = 1, } /// Physical port location -#[derive(Copy, Clone, PartialEq)] +#[derive(Copy, Clone, PartialEq, Deserialize, Serialize, SerializedSize)] pub struct PortLocation { pub controller: FpgaController, pub port: PhysicalPort, @@ -41,7 +57,7 @@ impl From for PortLocation { } /// Physical port location within a particular FPGA, as a 0-15 index -#[derive(Copy, Clone, PartialEq)] +#[derive(Copy, Clone, PartialEq, Deserialize, Serialize, SerializedSize)] pub struct PhysicalPort(pub u8); impl PhysicalPort { pub fn as_mask(&self) -> PhysicalPortMask { @@ -55,14 +71,14 @@ impl PhysicalPort { pub fn to_logical_port( &self, fpga: FpgaController, - ) -> Result { + ) -> Result { let loc = PortLocation { controller: fpga, port: *self, }; match PORT_MAP.into_iter().position(|&l| l == loc) { Some(p) => Ok(LogicalPort(p as u8)), - None => Err(TransceiversError::InvalidPhysicalToLogicalMap), + None => Err(FrontIOError::InvalidPhysicalToLogicalMap), } } } @@ -123,7 +139,19 @@ impl FpgaPortMasks { } /// Represents a single logical port (0-31) -#[derive(Copy, Clone, Debug, PartialEq, PartialOrd)] +#[derive( + Copy, + Clone, + Debug, + PartialEq, + PartialOrd, + FromBytes, + IntoBytes, + Deserialize, + Serialize, + SerializedSize, +)] +#[repr(transparent)] pub struct LogicalPort(pub u8); impl LogicalPort { pub fn as_mask(&self) -> LogicalPortMask { @@ -135,7 +163,22 @@ impl LogicalPort { } } /// Represents a set of selected logical ports, i.e. a 32-bit bitmask -#[derive(Copy, Clone, Debug, Default, PartialEq, Eq)] +#[derive( + Copy, + Clone, + Debug, + Default, + PartialEq, + Eq, + FromBytes, + IntoBytes, + Immutable, + KnownLayout, + Deserialize, + Serialize, + SerializedSize, +)] +#[repr(transparent)] pub struct LogicalPortMask(pub u32); impl LogicalPortMask { @@ -492,7 +535,10 @@ const RIGHT_LOGICAL_MASK: LogicalPortMask = LogicalPortMask(0xff00ff00); /// - The module operation succeeded /// - The module operation failed /// - The module could not be interacted with due to an FPGA communication error -#[derive(Copy, Clone, Default, PartialEq)] +#[derive( + Copy, Clone, Default, PartialEq, Deserialize, Serialize, SerializedSize, +)] +#[repr(C)] pub struct ModuleResult { success: LogicalPortMask, failure: LogicalPortMask, @@ -527,12 +573,12 @@ impl ModuleResult { failure: LogicalPortMask, error: LogicalPortMask, failure_types: LogicalPortFailureTypes, - ) -> Result { + ) -> Result { if !(success & failure).is_empty() || !(success & error).is_empty() || !(failure & error).is_empty() { - return Err(TransceiversError::InvalidModuleResult); + return Err(FrontIOError::InvalidModuleResult); } Ok(Self { success, @@ -626,7 +672,20 @@ impl ModuleResult { /// handle a mix of the following cases on a per-module basis: /// - The module operation succeeded /// - The module could not be interacted with due to an FPGA communication error -#[derive(Copy, Clone, Default, PartialEq)] +#[derive( + Copy, + Clone, + Default, + PartialEq, + FromBytes, + IntoBytes, + Immutable, + KnownLayout, + Deserialize, + Serialize, + SerializedSize, +)] +#[repr(C)] pub struct ModuleResultNoFailure { success: LogicalPortMask, error: LogicalPortMask, @@ -637,9 +696,9 @@ impl ModuleResultNoFailure { pub fn new( success: LogicalPortMask, error: LogicalPortMask, - ) -> Result { + ) -> Result { if !(success & error).is_empty() { - return Err(TransceiversError::InvalidModuleResult); + return Err(FrontIOError::InvalidModuleResult); } Ok(Self { success, error }) } @@ -654,6 +713,7 @@ impl ModuleResultNoFailure { } /// A type to provide more ergonomic access to the FPGA generated type +// #[derive(IntoBytes, FromBytes)] pub type FpgaI2CFailure = Reg::QSFP::PORT0_STATUS::ErrorEncoded; /// A type to consolidate per-module failure types. @@ -661,7 +721,8 @@ pub type FpgaI2CFailure = Reg::QSFP::PORT0_STATUS::ErrorEncoded; /// Currently the only types of operations that can be considered failures are /// those that involve the FPGA doing I2C. Thus, that is the only supported type /// right now. -#[derive(Copy, Clone, PartialEq)] +#[derive(Copy, Clone, PartialEq, Deserialize, Serialize, SerializedSize)] +#[repr(C)] pub struct LogicalPortFailureTypes(pub [FpgaI2CFailure; NUM_PORTS as usize]); impl Default for LogicalPortFailureTypes { @@ -678,7 +739,7 @@ impl core::ops::Index for LogicalPortFailureTypes { } } -#[derive(Copy, Clone)] +#[derive(Copy, Clone, Deserialize, Serialize, SerializedSize)] pub struct PortI2CStatus { pub stretching_seen: bool, pub rdata_fifo_empty: bool, @@ -886,12 +947,6 @@ impl Transceivers { ) } - /// Clear a fault for each port per the specified `mask`. - /// - /// The meaning of the - /// returned `ModuleResultNoFailure`: - /// success: we were able to write to the FPGA - /// error: an `FpgaError` occurred pub fn clear_power_fault( &self, mask: LogicalPortMask, @@ -931,36 +986,6 @@ impl Transceivers { ModuleResultNoFailure::new(success, error).unwrap_lite() } - /// Initiate an I2C random read on all ports per the specified `mask`. - /// - /// The maximum value of `num_bytes` is 128.The meaning of the returned - /// `ModuleResultNoFailure`: - /// success: we were able to write to the FPGA - /// error: an `FpgaError` occurred - pub fn setup_i2c_read( - &self, - reg: u8, - num_bytes: u8, - mask: LogicalPortMask, - ) -> ModuleResultNoFailure { - self.setup_i2c_op(true, reg, num_bytes, mask) - } - - /// Initiate an I2C write on all ports per the specified `mask`. - /// - /// The maximum value of `num_bytes` is 128. The meaning of the - /// returned `ModuleResultNoFailure`: - /// success: we were able to write to the FPGA - /// error: an `FpgaError` occurred - pub fn setup_i2c_write( - &self, - reg: u8, - num_bytes: u8, - mask: LogicalPortMask, - ) -> ModuleResultNoFailure { - self.setup_i2c_op(false, reg, num_bytes, mask) - } - /// Initiate an I2C operation on all ports per the specified `mask`. /// /// When `is_read` is true, the operation will be a random-read, not a pure @@ -968,7 +993,7 @@ impl Transceivers { /// The meaning of the returned `ModuleResultNoFailure`: /// success: we were able to write to the FPGA /// error: an `FpgaError` occurred - fn setup_i2c_op( + pub fn setup_i2c_op( &self, is_read: bool, reg: u8, @@ -1128,45 +1153,6 @@ impl Transceivers { ModuleResultNoFailure::new(success, error).unwrap_lite() } - /// Apply reset to the LED controller - /// - /// Per section 7.6 of the datasheet the minimum required pulse width here - /// is 2.5 microseconds. Given the SPI interface runs at 3MHz, the - /// transaction to clear the reset would take ~10 microseconds on its own, - /// so there is no additional delay here. - pub fn assert_led_controllers_reset(&mut self) -> Result<(), FpgaError> { - for fpga in &self.fpgas { - fpga.write(WriteOp::BitSet, Addr::LED_CTRL, Reg::LED_CTRL::RESET)?; - } - Ok(()) - } - - /// Remove reset from the LED controller - /// - /// Per section 7.6 of the datasheet the device has a maximum wait time of - /// 1.5 milliseconds after the release of reset to normal operation, so - /// there is a 2 millisecond wait here. - pub fn deassert_led_controllers_reset(&mut self) -> Result<(), FpgaError> { - for fpga in &self.fpgas { - fpga.write( - WriteOp::BitClear, - Addr::LED_CTRL, - Reg::LED_CTRL::RESET, - )?; - } - userlib::hl::sleep_for(2); - Ok(()) - } - - /// Releases the LED controller from reset and enables the output - pub fn enable_led_controllers(&mut self) -> Result<(), FpgaError> { - self.deassert_led_controllers_reset()?; - for fpga in &self.fpgas { - fpga.write(WriteOp::BitSet, Addr::LED_CTRL, Reg::LED_CTRL::OE)?; - } - Ok(()) - } - /// Waits for all of the I2C busy bits to go low /// /// Returns a set of masks indicating which channels (among the ones active @@ -1270,3 +1256,31 @@ pub struct TransceiversI2CRequest { mask: little_endian::U16, op: u8, } + +/// Each field is a bitmask of the 32 transceivers in big endian order, which +/// results in Port 31 being bit 31, and so forth. +#[derive( + Copy, Clone, Default, FromBytes, IntoBytes, Immutable, KnownLayout, +)] +#[repr(C)] +pub struct ModuleStatus { + pub power_enable: u32, + pub power_good: u32, + pub power_good_timeout: u32, + pub power_good_fault: u32, + pub resetl: u32, + pub lpmode_txdis: u32, + pub modprsl: u32, + pub intl_rxlosl: u32, +} + +/// Composite struct to package a ModuleStatus and a ModuleResultNoFailure for +/// IPC +#[derive( + Copy, Clone, Default, FromBytes, IntoBytes, Immutable, KnownLayout, +)] +#[repr(C)] +pub struct TransceiverStatus { + pub status: ModuleStatus, + pub result: ModuleResultNoFailure, +} diff --git a/drv/front-io-server/Cargo.toml b/drv/front-io-server/Cargo.toml new file mode 100644 index 0000000000..529f2a45ca --- /dev/null +++ b/drv/front-io-server/Cargo.toml @@ -0,0 +1,39 @@ +[package] +name = "drv-front-io-server" +version = "0.1.0" +edition = "2021" +authors = ["Arjen Roodselaar ", "Aaron Hartwig "] + +[dependencies] +enum-map.workspace = true +hubpack.workspace = true +idol-runtime.workspace = true +num-traits.workspace = true +serde.workspace = true +zerocopy.workspace = true +zerocopy-derive.workspace = true + +drv-auxflash-api = { path = "../../drv/auxflash-api" } +drv-front-io-api = { path = "../front-io-api" } +drv-fpga-api = { path = "../fpga-api" } +drv-fpga-user-api = { path = "../fpga-user-api" } +drv-i2c-api = { path = "../i2c-api" } +drv-i2c-devices = { path = "../i2c-devices" } +drv-sidecar-mainboard-controller = { path = "../../drv/sidecar-mainboard-controller" } +drv-transceivers-api = { path = "../../drv/transceivers-api" } +multitimer = { path = "../../lib/multitimer" } +ringbuf = { path = "../../lib/ringbuf" } +userlib = { path = "../../sys/userlib", features = ["panic-messages"] } + +[build-dependencies] +build-util = { path = "../../build/util" } +build-i2c = { path = "../../build/i2c" } +idol = { workspace = true } + +# This section is here to discourage RLS/rust-analyzer from doing test builds, +# since test builds don't work for cross compilation. +[[bin]] +name = "drv-front-io-server" +test = false +doctest = false +bench = false diff --git a/drv/sidecar-front-io/README.md b/drv/front-io-server/README.md similarity index 100% rename from drv/sidecar-front-io/README.md rename to drv/front-io-server/README.md diff --git a/drv/front-io-server/build.rs b/drv/front-io-server/build.rs new file mode 100644 index 0000000000..332a1031fe --- /dev/null +++ b/drv/front-io-server/build.rs @@ -0,0 +1,31 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. + +fn main() -> Result<(), Box> { + build_util::expose_target_board(); + build_util::build_notifications()?; + + let disposition = build_i2c::Disposition::Devices; + if let Err(e) = build_i2c::codegen(disposition) { + println!("code generation failed: {}", e); + std::process::exit(1); + } + + let board = build_util::env_var("HUBRIS_BOARD")?; + if board != "sidecar-b" && board != "sidecar-c" && board != "sidecar-d" { + panic!("unknown target board"); + } + + idol::Generator::new() + .with_counters( + idol::CounterSettings::default().with_server_counters(false), + ) + .build_server_support( + "../../idl/front-io.idol", + "server_stub.rs", + idol::server::ServerStyle::InOrder, + )?; + + Ok(()) +} diff --git a/drv/front-io-server/src/bsp.rs b/drv/front-io-server/src/bsp.rs new file mode 100644 index 0000000000..2c97e3a10e --- /dev/null +++ b/drv/front-io-server/src/bsp.rs @@ -0,0 +1,6 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. + +// This file should never be included; if it is, the build configuration is bad +compile_error!("no BSP for the given target board"); diff --git a/drv/front-io-server/src/bsp/sidecar_bcd.rs b/drv/front-io-server/src/bsp/sidecar_bcd.rs new file mode 100644 index 0000000000..7ab37ba827 --- /dev/null +++ b/drv/front-io-server/src/bsp/sidecar_bcd.rs @@ -0,0 +1,56 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. + +use drv_fpga_api::{FpgaError, FpgaUserDesign}; +use drv_fpga_user_api::power_rail::{PowerRailStatus, RawPowerRailState}; +use drv_front_io_api::FrontIOError; +use drv_sidecar_mainboard_controller::{Addr, MainboardController, Reg}; +use userlib::task_slot; + +task_slot!(MAINBOARD, mainboard); + +pub struct Bsp { + fpga: FpgaUserDesign, +} + +impl Bsp { + pub fn new() -> Result { + Ok(Self { + fpga: FpgaUserDesign::new( + MAINBOARD.get_task_id(), + MainboardController::DEVICE_INDEX, + ), + }) + } + + #[inline] + fn raw_state(&self) -> Result { + self.fpga.read(Addr::FRONT_IO_STATE) + } + + #[inline] + pub fn status(&self) -> Result { + PowerRailStatus::try_from(self.raw_state()?) + } + + pub fn power_good(&self) -> Result { + match self.status().map_err(FrontIOError::from)? { + PowerRailStatus::Enabled => Ok(true), + PowerRailStatus::Disabled | PowerRailStatus::RampingUp => Ok(false), + PowerRailStatus::GoodTimeout | PowerRailStatus::Aborted => { + Err(FrontIOError::PowerFault) + } + } + } + + pub fn set_power_enable(&self, enable: bool) -> Result<(), FrontIOError> { + self.fpga + .write( + enable.into(), + Addr::FRONT_IO_STATE, + Reg::FRONT_IO_STATE::ENABLE, + ) + .map_err(FrontIOError::from) + } +} diff --git a/drv/front-io-server/src/main.rs b/drv/front-io-server/src/main.rs new file mode 100644 index 0000000000..f1f00b28c8 --- /dev/null +++ b/drv/front-io-server/src/main.rs @@ -0,0 +1,998 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. + +//! Server for managing the Front IO board + +#![no_std] +#![no_main] + +#[cfg_attr( + any( + target_board = "sidecar-b", + target_board = "sidecar-c", + target_board = "sidecar-d" + ), + path = "bsp/sidecar_bcd.rs" +)] +mod bsp; + +use crate::bsp::Bsp; +use core::convert::Infallible; +use drv_fpga_api::{DeviceState, FpgaError, WriteOp}; +use drv_front_io_api::{ + controller::FrontIOController, + leds::{FullErrorSummary, LedStates, Leds}, + phy_smi::{PhyOscState, PhySmi}, + transceivers::{ + LogicalPort, LogicalPortMask, ModuleResult, ModuleResultNoFailure, + PortI2CStatus, TransceiverStatus, Transceivers, NUM_PORTS, + PAGE_SIZE_BYTES, + }, + Addr, FrontIOError, FrontIOStatus, LedState, Reg, +}; +use drv_i2c_devices::{at24csw080::At24Csw080, pca9956b, Validate}; +use enum_map::Enum; +use idol_runtime::{ + ClientError, Leased, NotificationHandler, RequestError, R, W, +}; +use multitimer::{Multitimer, Repeat}; +use ringbuf::*; +use userlib::*; + +task_slot!(I2C, i2c_driver); +task_slot!(FRONT_IO, front_io); +task_slot!(AUXFLASH, auxflash); + +include!(concat!(env!("OUT_DIR"), "/i2c_config.rs")); + +#[derive(Copy, Clone, PartialEq)] +enum Trace { + None, + BspInit, + BspInitComplete, + BspInitFailed, + FpgaInitError(FpgaError), + PhyOscGood, + PhyOscBad, + LEDInitComplete, + LEDInitError(pca9956b::Error), + LEDUpdateError(pca9956b::Error), + LEDReadError(pca9956b::Error), + LEDErrorSummary(FullErrorSummary), + LEDUninitialized, + SeqStatus(FrontIOStatus), + FpgaBitstreamError(u32), + LoadingFrontIOControllerBitstream { + fpga_id: usize, + }, + SkipLoadingFrontIOControllerBitstream { + fpga_id: usize, + }, + FrontIOControllerIdent { + fpga_id: usize, + ident: u32, + }, + FrontIOControllerChecksum { + fpga_id: usize, + checksum: [u8; 4], + expected: [u8; 4], + }, + SystemLedState(LedState), +} +ringbuf!(Trace, 32, Trace::None); + +struct ServerImpl { + /// A BSP to help deliver core functionality whose implementation varies from board to board + bsp: Bsp, + + /// Handle for the auxflash task + auxflash_task: userlib::TaskId, + + /// Handles for each FPGA + controllers: [FrontIOController; 2], + + /// Interface for the LED controllers + leds: Leds, + + /// VSC8562 SMI Interface + phy_smi: PhySmi, + + /// Interface for the trnasceivers + transceivers: Transceivers, + + /// Status of the Front IO board + board_status: FrontIOStatus, + + /// State to allow blinking LEDs to be in phase + led_blink_on: bool, + /// State around LED management + led_error: FullErrorSummary, + leds_initialized: bool, + led_states: LedStates, + system_led_state: LedState, +} + +/// Controls how often we update the LED controllers (in milliseconds). +const I2C_INTERVAL: u64 = 100; + +/// Blink LEDs at a 50% duty cycle (in milliseconds) +const BLINK_INTERVAL: u64 = 500; + +/// How often we should attempt the next sequencing step (in milliseconds) +const SEQ_INTERVAL: u64 = 100; + +impl ServerImpl { + // Encapsulates the logic of verifying checksums and loading FPGA images as necessary with the + // goal of only reloading FPGAs when there are new images in order to preserve state. + fn fpga_init(&mut self) -> Result { + let mut controllers_ready = true; + + for (i, controller) in self.controllers.iter_mut().enumerate() { + let state = controller.await_fpga_ready(25)?; + let mut ident; + let mut ident_valid = false; + let mut checksum; + let mut checksum_valid = false; + + if state == DeviceState::RunningUserDesign { + (ident, ident_valid) = controller.ident_valid()?; + ringbuf_entry!(Trace::FrontIOControllerIdent { + fpga_id: i, + ident + }); + + (checksum, checksum_valid) = controller.checksum_valid()?; + ringbuf_entry!(Trace::FrontIOControllerChecksum { + fpga_id: i, + checksum, + expected: FrontIOController::short_checksum(), + }); + + if !ident_valid || !checksum_valid { + // Attempt to correct the invalid IDENT by reloading the + // bitstream. + controller.fpga_reset()?; + } + } + + if ident_valid && checksum_valid { + ringbuf_entry!(Trace::SkipLoadingFrontIOControllerBitstream { + fpga_id: i + }); + } else { + ringbuf_entry!(Trace::LoadingFrontIOControllerBitstream { + fpga_id: i + }); + + if let Err(e) = controller.load_bitstream(self.auxflash_task) { + ringbuf_entry!(Trace::FpgaBitstreamError(u32::from(e))); + return Err(e); + } + + (ident, ident_valid) = controller.ident_valid()?; + ringbuf_entry!(Trace::FrontIOControllerIdent { + fpga_id: i, + ident + }); + + controller.write_checksum()?; + (checksum, checksum_valid) = controller.checksum_valid()?; + ringbuf_entry!(Trace::FrontIOControllerChecksum { + fpga_id: i, + checksum, + expected: FrontIOController::short_checksum(), + }); + } + + controllers_ready &= ident_valid & checksum_valid; + } + + Ok(controllers_ready) + } + + // Helper function to query if both FPGAs are ready + fn fpga_ready(&self) -> bool { + self.controllers.iter().all(|c| c.ready().unwrap_or(false)) + } + + // `assert` = true will reset the LED controllers, flase will release reset + fn leds_set_reset(&self, assert: bool) -> Result<(), FrontIOError> { + let op = match assert { + true => WriteOp::BitSet, + false => WriteOp::BitClear, + }; + + for c in &self.controllers { + c.user_design + .write(op, Addr::LED_CTRL, Reg::LED_CTRL::RESET) + .map_err(FrontIOError::from)?; + } + + Ok(()) + } + + // Update internal state for the system LED + fn set_system_led_state(&mut self, state: LedState) { + self.system_led_state = state; + ringbuf_entry!(Trace::SystemLedState(state)); + } + + // Next state logic for the LEDs + fn update_leds(&mut self) { + // handle port LEDs + let mut next_state = LogicalPortMask(0); + for (i, state) in self.led_states.into_iter().enumerate() { + let i = LogicalPort(i as u8); + match state { + LedState::On => next_state.set(i), + LedState::Blink => { + if self.led_blink_on { + next_state.set(i) + } + } + LedState::Off => (), + } + } + if let Err(e) = self.leds.update_led_state(next_state) { + ringbuf_entry!(Trace::LEDUpdateError(e)); + } + + // handle system LED + let system_led_on = match self.system_led_state { + LedState::On => true, + LedState::Blink => self.led_blink_on, + LedState::Off => false, + }; + if let Err(e) = self.leds.update_system_led_state(system_led_on) { + ringbuf_entry!(Trace::LEDUpdateError(e)); + } + } + + // Loop for the front_io I2C bus + fn handle_i2c_loop(&mut self) { + if self.leds_initialized { + self.update_leds(); + let errors = match self.leds.error_summary() { + Ok(errs) => errs, + Err(e) => { + ringbuf_entry!(Trace::LEDReadError(e)); + Default::default() + } + }; + if errors != self.led_error { + self.led_error = errors; + ringbuf_entry!(Trace::LEDErrorSummary(errors)); + } + } else { + ringbuf_entry!(Trace::LEDUninitialized); + } + } +} + +impl idl::InOrderFrontIOImpl for ServerImpl { + /// Enable or disable the front IO power per `enable` + fn set_power_enable( + &mut self, + _: &RecvMessage, + enable: bool, + ) -> Result<(), RequestError> { + self.bsp + .set_power_enable(enable) + .map_err(RequestError::from) + } + + /// Returns if front IO power is good + fn power_good( + &mut self, + _: &RecvMessage, + ) -> Result> { + self.bsp.power_good().map_err(RequestError::from) + } + + /// Blow away server state, resulting in a resequencing + fn board_reset( + &mut self, + _: &RecvMessage, + ) -> Result<(), RequestError> { + *self = ServerImpl::default(); + Ok(()) + } + + /// Returns the current status of the front IO board + fn board_status( + &mut self, + _: &RecvMessage, + ) -> Result> { + Ok(self.board_status) + } + + /// Returns true if a front IO board was determined to be present + fn board_present( + &mut self, + _: &RecvMessage, + ) -> Result> { + Ok(matches!( + self.board_status, + FrontIOStatus::FpgaInit + | FrontIOStatus::OscInit + | FrontIOStatus::Ready + )) + } + + /// Returns true if the front IO FPGAs have been initialized + fn board_fpgas_initialized( + &mut self, + _: &RecvMessage, + ) -> Result> { + Ok(matches!( + self.board_status, + FrontIOStatus::OscInit | FrontIOStatus::Ready + )) + } + + /// Returns if the front IO board has completely sequenced and is ready + fn board_ready( + &mut self, + _: &RecvMessage, + ) -> Result> { + Ok(self.board_status == FrontIOStatus::Ready) + } + + /// Returns the state of the PHY's oscilllator + fn phy_osc_state( + &mut self, + _: &RecvMessage, + ) -> Result> { + self.phy_smi + .osc_state() + .map_err(FrontIOError::from) + .map_err(RequestError::from) + } + + /// Returns if the PHY has been powered up and is ready + fn phy_ready( + &mut self, + _: &RecvMessage, + ) -> Result> { + self.phy_smi + .powered_up_and_ready() + .map_err(FrontIOError::from) + .map_err(RequestError::from) + } + + /// Set the internal state of the PHY's oscillator + fn phy_set_osc_state( + &mut self, + _: &RecvMessage, + good: bool, + ) -> Result<(), RequestError> { + match self + .phy_smi + .osc_state() + .map_err(FrontIOError::from) + .map_err(RequestError::from)? + { + // The state of the oscillator has not yet been examined or was + // marked bad in the previous run. Update as appropriate. + PhyOscState::Unknown | PhyOscState::Bad => { + ringbuf_entry!(if good { + Trace::PhyOscGood + } else { + Trace::PhyOscBad + }); + + self.phy_smi + .set_osc_good(good) + .map_err(FrontIOError::from) + .map_err(RequestError::from) + } + // The oscillator is already marked good and this state only changes + // if it (and by extension the whole front IO board) is power + // cycled. In that case the value of this register in the FPGA is + // automatically reset when the bitstream is loaded and the other + // arm of this match would be taken. + // + // So ignore this call if the oscillator has been found good since the last power + // cycle of the front IO board. + PhyOscState::Good => Ok(()), + } + } + + /// Apply power to the PHY + fn phy_enable_power( + &mut self, + _: &RecvMessage, + ) -> Result<(), RequestError> { + self.phy_smi + .set_phy_power_enabled(true) + .map_err(FrontIOError::from) + .map_err(RequestError::from) + } + + /// Remove power from the PHY + fn phy_disable_power( + &mut self, + _: &RecvMessage, + ) -> Result<(), RequestError> { + self.phy_smi + .set_phy_power_enabled(false) + .map_err(FrontIOError::from) + .map_err(RequestError::from) + } + + /// Set the coma_mode pin per `asserted` + fn phy_set_coma_mode( + &mut self, + _: &RecvMessage, + asserted: bool, + ) -> Result<(), RequestError> { + self.phy_smi + .set_coma_mode(asserted) + .map_err(FrontIOError::from) + .map_err(RequestError::from) + } + + /// Perform a read from the PHY + fn phy_read( + &mut self, + _: &RecvMessage, + phy: u8, + reg: u8, + ) -> Result> { + self.phy_smi + .read_raw_inner(phy, reg) + .map_err(FrontIOError::from) + .map_err(RequestError::from) + } + + /// Perform a write to the PHY + fn phy_write( + &mut self, + _: &RecvMessage, + phy: u8, + reg: u8, + value: u16, + ) -> Result<(), RequestError> { + self.phy_smi + .write_raw_inner(phy, reg, value) + .map_err(FrontIOError::from) + .map_err(RequestError::from) + } + + /// Apply reset to the LED controller + /// + /// Per section 7.6 of the datasheet the minimum required pulse width here + /// is 2.5 microseconds. Given the SPI interface runs at 3MHz, the + /// transaction to clear the reset would take ~10 microseconds on its own, + /// so there is no additional delay here. + fn leds_assert_reset( + &mut self, + _: &RecvMessage, + ) -> Result<(), RequestError> { + self.leds_set_reset(true).map_err(RequestError::from) + } + + /// Remove reset from the LED controller + /// + /// Per section 7.6 of the datasheet the device has a maximum wait time of + /// 1.5 milliseconds after the release of reset to normal operation, so + /// there is a 2 millisecond wait here. + fn leds_deassert_reset( + &mut self, + _: &RecvMessage, + ) -> Result<(), RequestError> { + self.leds_set_reset(false).map_err(RequestError::from)?; + userlib::hl::sleep_for(2); + Ok(()) + } + + /// Releases the LED controller from reset and enables the output + fn leds_enable( + &mut self, + _: &RecvMessage, + ) -> Result<(), RequestError> { + self.leds_set_reset(false).map_err(RequestError::from)?; + for c in &self.controllers { + c.user_design + .write(WriteOp::BitSet, Addr::LED_CTRL, Reg::LED_CTRL::OE) + .map_err(FrontIOError::from) + .map_err(RequestError::from)?; + } + + // Once we've initialized the LED driver we do not need to do so again + if !self.leds_initialized { + match self.leds.initialize_current() { + Ok(_) => { + self.set_system_led_state(LedState::On); + self.leds_initialized = true; + ringbuf_entry!(Trace::LEDInitComplete); + } + Err(e) => { + ringbuf_entry!(Trace::LEDInitError(e)); + return Err(RequestError::from( + FrontIOError::LedInitFailure, + )); + } + } + } + Ok(()) + } + + /// Asserts the LED controller reset and disables the output + fn leds_disable( + &mut self, + _: &RecvMessage, + ) -> Result<(), RequestError> { + self.leds_set_reset(true).map_err(RequestError::from)?; + for c in &self.controllers { + c.user_design + .write(WriteOp::BitClear, Addr::LED_CTRL, Reg::LED_CTRL::OE) + .map_err(FrontIOError::from) + .map_err(RequestError::from)?; + } + Ok(()) + } + + /// Update the internal port LED state of each bit in `mask` to `state` + fn led_set_state( + &mut self, + _: &RecvMessage, + mask: LogicalPortMask, + state: LedState, + ) -> Result<(), RequestError> { + self.led_states.set(mask, state); + Ok(()) + } + + /// Return the LED state of each port + fn led_get_state( + &mut self, + _: &RecvMessage, + port: LogicalPort, + ) -> Result> { + Ok(self.led_states.get(port)) + } + + /// Return the LED state of the system LED + fn led_get_system_state( + &mut self, + _: &RecvMessage, + ) -> Result> { + Ok(self.system_led_state) + } + + /// Turn the system LED on + fn led_set_system_on( + &mut self, + _msg: &userlib::RecvMessage, + ) -> Result<(), idol_runtime::RequestError> { + self.set_system_led_state(LedState::On); + Ok(()) + } + + /// Turn the system LED off + fn led_set_system_off( + &mut self, + _msg: &userlib::RecvMessage, + ) -> Result<(), idol_runtime::RequestError> { + self.set_system_led_state(LedState::Off); + Ok(()) + } + + /// Blink the system LED + fn led_set_system_blink( + &mut self, + _msg: &userlib::RecvMessage, + ) -> Result<(), idol_runtime::RequestError> { + self.set_system_led_state(LedState::Blink); + Ok(()) + } + + /// Get the current status of all modules + /// + /// This operation is considered infallible because the error cases are + /// handled by the transceivers crate, which then passes back a + /// TransceiverStatus to be consumed or ignored by the caller. + fn transceivers_status( + &mut self, + _: &RecvMessage, + ) -> Result> { + let (status, result) = self.transceivers.get_module_status(); + + Ok(TransceiverStatus { status, result }) + } + + /// Enable power for modules in `mask` + /// + /// This operation is considered infallible because the error cases are + /// handled by the transceivers crate, which then passes back a + /// ModuleResultNoFailure to be consumed or ignored by the caller. The + /// meaning of the returned `ModuleResultNoFailure`: + /// success: we were able to write to the FPGA + /// error: an `FpgaError` occurred + fn transceivers_enable_power( + &mut self, + _: &RecvMessage, + mask: LogicalPortMask, + ) -> Result> { + Ok(self.transceivers.enable_power(mask)) + } + + /// Disable power for modules in `mask` + /// + /// This operation is considered infallible because the error cases are + /// handled by the transceivers crate, which then passes back a + /// ModuleResultNoFailure to be consumed or ignored by the caller. The + /// meaning of the returned `ModuleResultNoFailure`: + /// success: we were able to write to the FPGA + /// error: an `FpgaError` occurred + fn transceivers_disable_power( + &mut self, + _: &RecvMessage, + mask: LogicalPortMask, + ) -> Result> { + Ok(self.transceivers.disable_power(mask)) + } + + /// Clear a fault for each port per the specified `mask`. + /// + /// The meaning of the + /// returned `ModuleResultNoFailure`: + /// success: we were able to write to the FPGA + /// error: an `FpgaError` occurred + fn transceivers_clear_power_fault( + &mut self, + _: &RecvMessage, + mask: LogicalPortMask, + ) -> Result> { + Ok(self.transceivers.clear_power_fault(mask)) + } + + /// Assert reset for modules in `mask` + /// + /// This operation is considered infallible because the error cases are + /// handled by the transceivers crate, which then passes back a + /// ModuleResultNoFailure to be consumed or ignored by the caller. The + /// meaning of the returned `ModuleResultNoFailure`: + /// success: we were able to write to the FPGA + /// error: an `FpgaError` occurred + fn transceivers_assert_reset( + &mut self, + _: &RecvMessage, + mask: LogicalPortMask, + ) -> Result> { + Ok(self.transceivers.assert_reset(mask)) + } + + /// Deassert reset for modules in `mask` + /// + /// This operation is considered infallible because the error cases are + /// handled by the transceivers crate, which then passes back a + /// ModuleResultNoFailure to be consumed or ignored by the caller. The + /// meaning of the returned `ModuleResultNoFailure`: + /// success: we were able to write to the FPGA + /// error: an `FpgaError` occurred + fn transceivers_deassert_reset( + &mut self, + _: &RecvMessage, + mask: LogicalPortMask, + ) -> Result> { + Ok(self.transceivers.deassert_reset(mask)) + } + + /// Assert LpMode for modules in `mask` + /// + /// This operation is considered infallible because the error cases are + /// handled by the transceivers crate, which then passes back a + /// ModuleResultNoFailure to be consumed or ignored by the caller. The + /// meaning of the returned `ModuleResultNoFailure`: + /// success: we were able to write to the FPGA + /// error: an `FpgaError` occurred + fn transceivers_assert_lpmode( + &mut self, + _: &RecvMessage, + mask: LogicalPortMask, + ) -> Result> { + Ok(self.transceivers.assert_lpmode(mask)) + } + + /// Deassert LpMode for modules in `mask` + /// + /// This operation is considered infallible because the error cases are + /// handled by the transceivers crate, which then passes back a + /// ModuleResultNoFailure to be consumed or ignored by the caller. The + /// meaning of the returned `ModuleResultNoFailure`: + /// success: we were able to write to the FPGA + /// error: an `FpgaError` occurred + fn transceivers_deassert_lpmode( + &mut self, + _: &RecvMessage, + mask: LogicalPortMask, + ) -> Result> { + Ok(self.transceivers.deassert_lpmode(mask)) + } + + /// Initiate an I2C random read on all ports per the specified `mask`. + /// + /// The maximum value of `num_bytes` is 128. This operation is considered + /// infallible because the error cases are handled by the transceivers + /// crate, which then passes back a ModuleResultNoFailure to be consumed or + /// ignored by the caller. The meaning of the returned + /// `ModuleResultNoFailure`: + /// success: we were able to write to the FPGA + /// error: an `FpgaError` occurred + fn transceivers_setup_i2c_read( + &mut self, + _: &RecvMessage, + reg: u8, + num_bytes: u8, + mask: LogicalPortMask, + ) -> Result> { + if usize::from(num_bytes) > PAGE_SIZE_BYTES { + return Err(FrontIOError::InvalidNumberOfBytes.into()); + } + Ok(self.transceivers.setup_i2c_op(true, reg, num_bytes, mask)) + } + + /// Initiate an I2C write on all ports per the specified `mask`. + /// + /// The maximum value of `num_bytes` is 128. This operation is considered + /// infallible because the error cases are handled by the transceivers + /// crate, which then passes back a ModuleResultNoFailure to be consumed or + /// ignored by the caller. The meaning of the returned + /// `ModuleResultNoFailure`: + /// success: we were able to write to the FPGA + /// error: an `FpgaError` occurred + fn transceivers_setup_i2c_write( + &mut self, + _msg: &userlib::RecvMessage, + reg: u8, + num_bytes: u8, + mask: LogicalPortMask, + ) -> Result> { + if usize::from(num_bytes) > PAGE_SIZE_BYTES { + return Err(FrontIOError::InvalidNumberOfBytes.into()); + } + Ok(self.transceivers.setup_i2c_op(false, reg, num_bytes, mask)) + } + + /// Write `data` into the I2C write buffer for each port specified by `mask` + /// + /// The maximum value of `num_bytes` is 128. This operation is considered + /// infallible because the error cases are handled by the transceivers + /// crate, which then passes back a ModuleResultNoFailure to be consumed or + /// ignored by the caller. The meaning of the returned + /// `ModuleResultNoFailure`: + /// success: we were able to write to the FPGA + /// error: an `FpgaError` occurred + fn transceivers_set_i2c_write_buffer( + &mut self, + _: &RecvMessage, + mask: LogicalPortMask, + data: Leased, + ) -> Result> { + if data.len() > PAGE_SIZE_BYTES { + return Err(FrontIOError::InvalidNumberOfBytes.into()); + } + + let mut buf = [0u8; PAGE_SIZE_BYTES]; + data.read_range(0..data.len(), &mut buf[..data.len()]) + .map_err(|_| RequestError::Fail(ClientError::WentAway))?; + + Ok(self.transceivers.set_i2c_write_buffer(&buf, mask)) + } + + /// Get both the status byte and the read data buffer for the specified `port` + fn transceivers_get_i2c_status_and_read_buffer( + &mut self, + _: &RecvMessage, + port: LogicalPort, + dest: Leased, + ) -> Result> { + if port.0 >= NUM_PORTS { + return Err(FrontIOError::InvalidPortNumber.into()); + } + + if dest.len() > PAGE_SIZE_BYTES { + return Err(FrontIOError::InvalidNumberOfBytes.into()); + } + + // PAGE_SIZE_BYTES + 1 since we have a status byte alongside data + let mut buf = [0u8; PAGE_SIZE_BYTES + 1]; + + let status = self + .transceivers + .get_i2c_status_and_read_buffer(port, &mut buf[..dest.len()]) + .map_err(FrontIOError::from)?; + + dest.write_range(0..dest.len(), &buf[..dest.len()]) + .map_err(|_| RequestError::Fail(ClientError::WentAway))?; + Ok(status) + } + + fn transceivers_wait_and_check_i2c( + &mut self, + _: &RecvMessage, + mask: LogicalPortMask, + ) -> Result> { + Ok(self.transceivers.wait_and_check_i2c(mask)) + } +} + +impl NotificationHandler for ServerImpl { + fn current_notification_mask(&self) -> u32 { + notifications::TIMER_MASK + } + + fn handle_notification(&mut self, _bits: u32) {} +} + +impl Default for ServerImpl { + fn default() -> Self { + ringbuf_entry!(Trace::BspInit); + let bsp = match Bsp::new() { + Ok(bsp) => { + ringbuf_entry!(Trace::BspInitComplete); + bsp + } + Err(_) => { + ringbuf_entry!(Trace::BspInitFailed); + panic!(); + } + }; + let i2c_task = I2C.get_task_id(); + let fpga_task = FRONT_IO.get_task_id(); + let auxflash_task = AUXFLASH.get_task_id(); + + ServerImpl { + bsp, + auxflash_task, + controllers: [ + FrontIOController::new(fpga_task, 0), + FrontIOController::new(fpga_task, 1), + ], + leds: Leds::new( + &i2c_config::devices::pca9956b_front_leds_left(i2c_task), + &i2c_config::devices::pca9956b_front_leds_right(i2c_task), + ), + phy_smi: PhySmi::new(fpga_task), + transceivers: Transceivers::new(fpga_task), + board_status: FrontIOStatus::Init, + led_blink_on: false, + led_error: Default::default(), + leds_initialized: false, + led_states: LedStates::default(), + system_led_state: LedState::Off, + } + } +} + +#[export_name = "main"] +fn main() -> ! { + let mut buffer = [0; idl::INCOMING_SIZE]; + let mut server = ServerImpl::default(); + + #[derive(Copy, Clone, Enum)] + #[allow(clippy::upper_case_acronyms)] + enum Timers { + I2C, + Blink, + Seq, + } + let mut multitimer = Multitimer::::new(notifications::TIMER_BIT); + let now = sys_get_timer().now; + multitimer.set_timer( + Timers::I2C, + now, + Some(Repeat::AfterDeadline(I2C_INTERVAL)), + ); + multitimer.set_timer( + Timers::Blink, + now, + Some(Repeat::AfterDeadline(BLINK_INTERVAL)), + ); + multitimer.set_timer( + Timers::Seq, + now, + Some(Repeat::AfterDeadline(SEQ_INTERVAL)), + ); + + // This will put our timer in the past, and should immediately kick us. + let deadline = sys_get_timer().now; + sys_set_timer(Some(deadline), notifications::TIMER_MASK); + + loop { + multitimer.poll_now(); + for t in multitimer.iter_fired() { + match t { + Timers::I2C => { + // There's no point to try to talk to the I2C bus if a board + // is not present. + if server.board_status != FrontIOStatus::NotPresent { + server.handle_i2c_loop(); + } + } + Timers::Blink => { + server.led_blink_on = !server.led_blink_on; + } + Timers::Seq => { + // Sequencing of the Front IO board + match server.board_status { + // The best way we have to detect the presence of a + // Front IO board is our ability to talk to its FRUID + // device. + FrontIOStatus::Init | FrontIOStatus::NotPresent => { + ringbuf_entry!(Trace::SeqStatus( + server.board_status + )); + let fruid = + i2c_config::devices::at24csw080_front_io( + I2C.get_task_id(), + )[0]; + if At24Csw080::validate(&fruid).unwrap_or(false) { + server.board_status = FrontIOStatus::FpgaInit; + } else { + server.board_status = FrontIOStatus::NotPresent; + } + } + + // Once there is a board present, configure its FPGAs + // and wait for its oscillator to be functional. + FrontIOStatus::FpgaInit => match server.fpga_init() { + Ok(done) => { + ringbuf_entry!(Trace::SeqStatus( + server.board_status + )); + if done && server.fpga_ready() { + server.board_status = + FrontIOStatus::OscInit; + } + } + Err(e) => ringbuf_entry!(Trace::FpgaInitError(e)), + }, + + // Wait for the PHY oscillator to be deemed operational. + // Currently this server does not control the power to + // the Front IO board, so it relies on whatever task + // _does_ have that control to power cycle the board and + // make a judgement about the oscillator. + FrontIOStatus::OscInit => { + ringbuf_entry!(Trace::SeqStatus( + server.board_status + )); + if server + .phy_smi + .osc_state() + .unwrap_or(PhyOscState::Unknown) + == PhyOscState::Good + { + server.board_status = FrontIOStatus::Ready; + ringbuf_entry!(Trace::SeqStatus( + server.board_status + )); + } + } + + // The board is operational, not further action needed + FrontIOStatus::Ready => (), + } + } + } + } + + idol_runtime::dispatch(&mut buffer, &mut server); + } +} + +mod idl { + use super::{ + FrontIOError, FrontIOStatus, LedState, LogicalPort, LogicalPortMask, + ModuleResult, ModuleResultNoFailure, PhyOscState, PortI2CStatus, + TransceiverStatus, + }; + + include!(concat!(env!("OUT_DIR"), "/server_stub.rs")); +} + +include!(concat!(env!("OUT_DIR"), "/notifications.rs")); diff --git a/drv/gimlet-seq-server/Cargo.toml b/drv/gimlet-seq-server/Cargo.toml index 0870aa58dd..78bbea9f44 100644 --- a/drv/gimlet-seq-server/Cargo.toml +++ b/drv/gimlet-seq-server/Cargo.toml @@ -22,10 +22,12 @@ userlib = { path = "../../sys/userlib", features = ["panic-messages"] } cfg-if = { workspace = true } cortex-m = { workspace = true } +hubpack.workspace = true idol-runtime.workspace = true num-traits = { workspace = true } zerocopy = { workspace = true } zerocopy-derive = { workspace = true } +serde.workspace = true num-derive = { workspace = true } static_assertions = { workspace = true } spd = { workspace = true } diff --git a/drv/medusa-seq-server/Cargo.toml b/drv/medusa-seq-server/Cargo.toml index 6f9951f166..ba657d77c6 100644 --- a/drv/medusa-seq-server/Cargo.toml +++ b/drv/medusa-seq-server/Cargo.toml @@ -15,11 +15,11 @@ zerocopy-derive.workspace = true drv-fpga-api = { path = "../fpga-api", features = ["auxflash"] } drv-fpga-user-api = { path = "../fpga-user-api" } +drv-front-io-api = {path = "../front-io-api" } drv-i2c-api = { path = "../i2c-api" } drv-i2c-devices = { path = "../i2c-devices" } drv-medusa-seq-api = { path = "../medusa-seq-api" } drv-packrat-vpd-loader = { path = "../packrat-vpd-loader" } -drv-sidecar-front-io = { path = "../sidecar-front-io", features = ["controller", "phy_smi"] } drv-sidecar-mainboard-controller = { path = "../sidecar-mainboard-controller" } drv-stm32xx-sys-api = { path = "../../drv/stm32xx-sys-api", features = ["family-stm32h7"] } ringbuf = { path = "../../lib/ringbuf" } diff --git a/drv/medusa-seq-server/src/front_io.rs b/drv/medusa-seq-server/src/front_io.rs index 7dd35d7f7c..a36ce5cf3a 100644 --- a/drv/medusa-seq-server/src/front_io.rs +++ b/drv/medusa-seq-server/src/front_io.rs @@ -4,9 +4,8 @@ use crate::*; use drv_fpga_api::{DeviceState, FpgaError}; +use drv_front_io_api::{controller::FrontIOController, phy_smi::PhySmi}; use drv_i2c_devices::{at24csw080::At24Csw080, Validate}; -use drv_sidecar_front_io::controller::FrontIOController; -use drv_sidecar_front_io::phy_smi::PhySmi; #[allow(dead_code)] pub(crate) struct FrontIOBoard { diff --git a/drv/medusa-seq-server/src/main.rs b/drv/medusa-seq-server/src/main.rs index dac64e3423..c371f03fb0 100644 --- a/drv/medusa-seq-server/src/main.rs +++ b/drv/medusa-seq-server/src/main.rs @@ -10,8 +10,8 @@ use crate::front_io::FrontIOBoard; use crate::power_control::PowerControl; use core::convert::Infallible; +use drv_front_io_api::phy_smi::PhyOscState; use drv_medusa_seq_api::{MedusaError, RailName}; -use drv_sidecar_front_io::phy_smi::PhyOscState; use idol_runtime::{NotificationHandler, RequestError}; use ringbuf::{ringbuf, ringbuf_entry}; use userlib::*; diff --git a/drv/sidecar-front-io/Cargo.toml b/drv/sidecar-front-io/Cargo.toml deleted file mode 100644 index 62bbb3792a..0000000000 --- a/drv/sidecar-front-io/Cargo.toml +++ /dev/null @@ -1,43 +0,0 @@ -[package] -name = "drv-sidecar-front-io" -version = "0.1.0" -edition = "2021" - -[dependencies] -cfg-if = { workspace = true } -num-derive = { workspace = true } -num-traits = { workspace = true } -transceiver-messages = { workspace = true } -vsc7448-pac = { workspace = true, optional = true } -zerocopy = { workspace = true } -zerocopy-derive = { workspace = true } - -drv-auxflash-api = { path = "../../drv/auxflash-api" } -drv-fpga-api = { path = "../../drv/fpga-api", features = ["auxflash"] } -drv-i2c-api = { path = "../i2c-api" } -drv-i2c-devices = { path = "../i2c-devices" } -drv-transceivers-api = { path = "../../drv/transceivers-api" } -ringbuf = { path = "../../lib/ringbuf" } -userlib = { path = "../../sys/userlib" } -vsc85xx = { path = "../../drv/vsc85xx", optional = true } - -[features] -controller = [] -phy_smi = ["vsc85xx", "vsc7448-pac"] -transceivers = [] -leds = [] -no-ipc-counters = ["idol/no-counters"] - -[build-dependencies] -build-fpga-regmap = { path = "../../build/fpga-regmap" } -build-util = { path = "../../build/util" } -gnarle = { path = "../../lib/gnarle", features=["std"] } -idol = { workspace = true } - -[lib] -test = false -doctest = false -bench = false - -[lints] -workspace = true diff --git a/drv/sidecar-front-io/src/lib.rs b/drv/sidecar-front-io/src/lib.rs deleted file mode 100644 index 2013212dfb..0000000000 --- a/drv/sidecar-front-io/src/lib.rs +++ /dev/null @@ -1,19 +0,0 @@ -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. - -#![no_std] - -include!(concat!( - env!("OUT_DIR"), - "/sidecar_qsfp_x32_controller_regs.rs" -)); - -#[cfg(feature = "controller")] -pub mod controller; -#[cfg(feature = "leds")] -pub mod leds; -#[cfg(feature = "phy_smi")] -pub mod phy_smi; -#[cfg(feature = "transceivers")] -pub mod transceivers; diff --git a/drv/sidecar-mainboard-controller/src/front_io.rs b/drv/sidecar-mainboard-controller/src/front_io.rs deleted file mode 100644 index 40a4c16bba..0000000000 --- a/drv/sidecar-mainboard-controller/src/front_io.rs +++ /dev/null @@ -1,40 +0,0 @@ -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. - -use crate::{Addr, MainboardController, Reg}; -use drv_fpga_api::{FpgaError, FpgaUserDesign}; -pub use drv_fpga_user_api::power_rail::*; - -pub struct HotSwapController { - fpga: FpgaUserDesign, -} - -impl HotSwapController { - pub fn new(task_port: userlib::TaskId) -> Self { - Self { - fpga: FpgaUserDesign::new( - task_port, - MainboardController::DEVICE_INDEX, - ), - } - } - - #[inline] - fn raw_state(&self) -> Result { - self.fpga.read(Addr::FRONT_IO_STATE) - } - - #[inline] - pub fn status(&self) -> Result { - PowerRailStatus::try_from(self.raw_state()?) - } - - pub fn set_enable(&self, enable: bool) -> Result<(), FpgaError> { - self.fpga.write( - enable.into(), - Addr::FRONT_IO_STATE, - Reg::FRONT_IO_STATE::ENABLE, - ) - } -} diff --git a/drv/sidecar-mainboard-controller/src/lib.rs b/drv/sidecar-mainboard-controller/src/lib.rs index d58433e546..8c551f612b 100644 --- a/drv/sidecar-mainboard-controller/src/lib.rs +++ b/drv/sidecar-mainboard-controller/src/lib.rs @@ -9,7 +9,6 @@ use drv_fpga_api::*; include!(concat!(env!("OUT_DIR"), "/sidecar_mainboard_controller.rs")); pub mod fan_modules; -pub mod front_io; pub mod ignition; pub mod tofino2; diff --git a/drv/sidecar-seq-api/Cargo.toml b/drv/sidecar-seq-api/Cargo.toml index 89a8fe942c..0735bb3d5e 100644 --- a/drv/sidecar-seq-api/Cargo.toml +++ b/drv/sidecar-seq-api/Cargo.toml @@ -14,6 +14,7 @@ counters = { path = "../../lib/counters" } derive-idol-err = { path = "../../lib/derive-idol-err" } drv-fpga-api = { path = "../fpga-api" } drv-fpga-user-api = { path = "../fpga-user-api" } +drv-front-io-api = { path = "../front-io-api" } drv-sidecar-mainboard-controller = { path = "../sidecar-mainboard-controller" } userlib = { path = "../../sys/userlib" } diff --git a/drv/sidecar-seq-api/src/lib.rs b/drv/sidecar-seq-api/src/lib.rs index 7d1fa3ac86..a7c68b1e79 100644 --- a/drv/sidecar-seq-api/src/lib.rs +++ b/drv/sidecar-seq-api/src/lib.rs @@ -8,6 +8,7 @@ use derive_idol_err::IdolError; use drv_fpga_api::FpgaError; +use drv_front_io_api::FrontIOError; pub use drv_sidecar_mainboard_controller::{ fan_modules::{FanModuleStatus, NUM_FAN_MODULES}, tofino2::{ @@ -36,6 +37,7 @@ pub enum SeqError { NoFrontIOBoard, FrontIOBoardPowerFault, FrontIOPowerNotGood, + FrontIOError, #[idol(server_death)] ServerRestarted, @@ -47,6 +49,12 @@ impl From for SeqError { } } +impl From for SeqError { + fn from(_: FrontIOError) -> Self { + Self::FrontIOError + } +} + #[derive( Copy, Clone, diff --git a/drv/sidecar-seq-server/Cargo.toml b/drv/sidecar-seq-server/Cargo.toml index 27cf9fffb3..1ccb243b45 100644 --- a/drv/sidecar-seq-server/Cargo.toml +++ b/drv/sidecar-seq-server/Cargo.toml @@ -16,10 +16,10 @@ zerocopy-derive.workspace = true drv-fpga-api = { path = "../fpga-api", features = ["auxflash"] } drv-fpga-user-api = { path = "../fpga-user-api" } +drv-front-io-api = {path = "../front-io-api" } drv-i2c-api = { path = "../i2c-api" } drv-i2c-devices = { path = "../i2c-devices" } drv-packrat-vpd-loader = { path = "../packrat-vpd-loader" } -drv-sidecar-front-io = { path = "../sidecar-front-io", features = ["controller", "phy_smi"] } drv-sidecar-mainboard-controller = { path = "../sidecar-mainboard-controller", features = ["bitstream"] } drv-sidecar-seq-api = { path = "../sidecar-seq-api" } drv-stm32xx-sys-api = { path = "../stm32xx-sys-api", features = ["family-stm32h7"] } diff --git a/drv/sidecar-seq-server/src/front_io.rs b/drv/sidecar-seq-server/src/front_io.rs deleted file mode 100644 index 2aae1f4595..0000000000 --- a/drv/sidecar-seq-server/src/front_io.rs +++ /dev/null @@ -1,110 +0,0 @@ -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. - -use crate::*; -use drv_i2c_devices::{at24csw080::At24Csw080, Validate}; -use drv_sidecar_front_io::controller::FrontIOController; -use drv_sidecar_front_io::phy_smi::PhySmi; - -#[allow(dead_code)] -pub(crate) struct FrontIOBoard { - pub controllers: [FrontIOController; 2], - fpga_task: userlib::TaskId, - auxflash_task: userlib::TaskId, -} - -impl FrontIOBoard { - pub fn new( - fpga_task: userlib::TaskId, - auxflash_task: userlib::TaskId, - ) -> Self { - Self { - controllers: [ - FrontIOController::new(fpga_task, 0), - FrontIOController::new(fpga_task, 1), - ], - fpga_task, - auxflash_task, - } - } - - pub fn phy(&self) -> PhySmi { - PhySmi::new(self.fpga_task) - } - - pub fn present(i2c_task: userlib::TaskId) -> bool { - let fruid = i2c_config::devices::at24csw080_front_io(i2c_task)[0]; - At24Csw080::validate(&fruid).unwrap_or(false) - } - - pub fn initialized(&self) -> bool { - self.controllers.iter().all(|c| c.ready().unwrap_or(false)) - } - - pub fn init(&mut self) -> Result { - let mut controllers_ready = true; - - for (i, controller) in self.controllers.iter_mut().enumerate() { - let state = controller.await_fpga_ready(25)?; - let mut ident; - let mut ident_valid = false; - let mut checksum; - let mut checksum_valid = false; - - if state == DeviceState::RunningUserDesign { - (ident, ident_valid) = controller.ident_valid()?; - ringbuf_entry!(Trace::FrontIOControllerIdent { - fpga_id: i, - ident - }); - - (checksum, checksum_valid) = controller.checksum_valid()?; - ringbuf_entry!(Trace::FrontIOControllerChecksum { - fpga_id: i, - checksum, - expected: FrontIOController::short_checksum(), - }); - - if !ident_valid || !checksum_valid { - // Attempt to correct the invalid IDENT by reloading the - // bitstream. - controller.fpga_reset()?; - } - } - - if ident_valid && checksum_valid { - ringbuf_entry!(Trace::SkipLoadingFrontIOControllerBitstream { - fpga_id: i - }); - } else { - ringbuf_entry!(Trace::LoadingFrontIOControllerBitstream { - fpga_id: i - }); - - if let Err(e) = controller.load_bitstream(self.auxflash_task) { - ringbuf_entry!(Trace::FpgaBitstreamError(u32::from(e))); - return Err(e); - } - - (ident, ident_valid) = controller.ident_valid()?; - ringbuf_entry!(Trace::FrontIOControllerIdent { - fpga_id: i, - ident - }); - - controller.write_checksum()?; - (checksum, checksum_valid) = controller.checksum_valid()?; - ringbuf_entry!(Trace::FrontIOControllerChecksum { - fpga_id: i, - checksum, - expected: FrontIOController::short_checksum(), - }); - } - - controllers_ready &= ident_valid & checksum_valid; - } - - Ok(controllers_ready) - } -} diff --git a/drv/sidecar-seq-server/src/main.rs b/drv/sidecar-seq-server/src/main.rs index 2966e7b220..1c07dbf66f 100644 --- a/drv/sidecar-seq-server/src/main.rs +++ b/drv/sidecar-seq-server/src/main.rs @@ -8,15 +8,16 @@ #![no_main] use crate::clock_generator::ClockGenerator; -use crate::front_io::FrontIOBoard; use crate::tofino::Tofino; use core::convert::Infallible; use drv_fpga_api::{DeviceState, FpgaError, WriteOp}; +use drv_fpga_user_api::power_rail::{PowerRailStatus, RawPowerRailState}; +use drv_front_io_api::{ + phy_smi::PhyOscState, FrontIO, FrontIOError, FrontIOStatus, +}; use drv_i2c_api::{I2cDevice, ResponseCode}; use drv_packrat_vpd_loader::{read_vpd_and_load_packrat, Packrat}; -use drv_sidecar_front_io::phy_smi::PhyOscState; use drv_sidecar_mainboard_controller::fan_modules::*; -use drv_sidecar_mainboard_controller::front_io::*; use drv_sidecar_mainboard_controller::tofino2::*; use drv_sidecar_mainboard_controller::MainboardController; use drv_sidecar_seq_api::{ @@ -39,7 +40,6 @@ task_slot!(SYS, sys); include!(concat!(env!("OUT_DIR"), "/i2c_config.rs")); mod clock_generator; -mod front_io; mod tofino; #[allow(dead_code)] @@ -87,23 +87,6 @@ enum Trace { FrontIOBoardPresent, FrontIOBoardNotPresent, FrontIOBoardPhyPowerEnable(bool), - FrontIOBoardPhyOscGood, - FrontIOBoardPhyOscBad, - LoadingFrontIOControllerBitstream { - fpga_id: usize, - }, - SkipLoadingFrontIOControllerBitstream { - fpga_id: usize, - }, - FrontIOControllerIdent { - fpga_id: usize, - ident: u32, - }, - FrontIOControllerChecksum { - fpga_id: usize, - checksum: [u8; 4], - expected: [u8; 4], - }, FpgaFanModuleFailure(FpgaError), FanModulePowerFault(FanModuleIndex, FanModuleStatus), FanModuleLedUpdate(FanModuleIndex, FanModuleLedState), @@ -135,8 +118,7 @@ struct ServerImpl { mainboard_controller: MainboardController, clock_generator: ClockGenerator, tofino: Tofino, - front_io_hsc: HotSwapController, - front_io_board: Option, + front_io_board: FrontIO, fan_modules: FanModules, // a piece of state to allow blinking LEDs to be in phase led_blink_on: bool, @@ -217,22 +199,22 @@ impl ServerImpl { // Make sure the front IO hot swap controller is enabled and good. The // power rail FSM will reach either the GoodTimeout, Aborted or Enabled // state or experience an FpgaError, so an open loop is safe. - while match self.front_io_hsc.status()? { - PowerRailStatus::GoodTimeout | PowerRailStatus::Aborted => { - return Err(SeqError::FrontIOBoardPowerFault) - } - PowerRailStatus::Disabled => { - self.front_io_hsc.set_enable(true)?; + while match self.front_io_board.power_good() { + Ok(true) => false, // power is good, stop looping + Ok(false) => { + self.front_io_board + .set_power_enable(true) + .map_err(SeqError::from)?; ringbuf_entry!(Trace::FrontIOBoardPowerEnable(true)); - true // Retry HSC status. } - PowerRailStatus::RampingUp => { - true // Retry HSC status. + Err(FrontIOError::PowerFault) => { + return Err(SeqError::FrontIOBoardPowerFault) } - PowerRailStatus::Enabled => false, + Err(e) => return Err(SeqError::from(e)), } { - userlib::hl::sleep_for(25); + // The front IO HSC was observed to take as long as 35 ms to assert power good + userlib::hl::sleep_for(100); } // Check if the power is good via the PG pin @@ -244,88 +226,78 @@ impl ServerImpl { } // Determine if a front IO board is present. - Ok(FrontIOBoard::present(I2C.get_task_id())) - } - - fn front_io_phy_osc_good(&self) -> Result { - if let Some(front_io_board) = self.front_io_board.as_ref() { - Ok(front_io_board.initialized() - && front_io_board - .phy() - .osc_state() - .unwrap_or(PhyOscState::Unknown) - == PhyOscState::Good) - } else { - Err(SeqError::NoFrontIOBoard) - } + Ok(self.front_io_board.board_present()) } fn actually_reset_front_io_phy(&mut self) -> Result<(), SeqError> { - if let Some(front_io_board) = self.front_io_board.as_mut() { - if front_io_board.initialized() { - // The board was initialized prior and this function is called - // by the monorail task because it is initializing the front IO - // PHY. Unfortunately some front IO boards have PHY oscillators - // which do not start reliably when their enable pin is used and - // the only way to resolve this is by power cycling the front IO - // board. But power cycling the board also bounces any QSFP - // transceivers which may be running, so this function attempts - // to determine what the monorail task wants to do. - // - // Whether or not the PHY oscillator was found to be operating - // nominally is recorded in the front IO board controller. Look - // up what this value is to determine if a power reset of the - // front IO board is needed. - match front_io_board.phy().osc_state()? { - PhyOscState::Bad => { - // The PHY was attempted to be initialized but its - // oscillator was deemed not functional. Unfortunately - // the only course of action is to power cycle the - // entire front IO board, so do so now. - self.front_io_hsc.set_enable(false)?; - ringbuf_entry!(Trace::FrontIOBoardPowerEnable(false)); - - // Wait some cool down period to allow caps to bleed off - // etc. - userlib::hl::sleep_for(1000); - } - PhyOscState::Good => { - // The PHY was initialized properly before and its - // oscillator declared operating nominally. Assume this - // has not changed and only a reset the PHY itself is - // desired. - front_io_board.phy().set_phy_power_enabled(false)?; - ringbuf_entry!(Trace::FrontIOBoardPhyPowerEnable( - false - )); - - userlib::hl::sleep_for(10); - } - PhyOscState::Unknown => { - // Do nothing (yet) since the oscillator state is - // unknown. - } - } + // The board was initialized prior and this function is called + // by the monorail task because it is initializing the front IO + // PHY. Unfortunately some front IO boards have PHY oscillators + // which do not start reliably when their enable pin is used and + // the only way to resolve this is by power cycling the front IO + // board. But power cycling the board also bounces any QSFP + // transceivers which may be running, so this function attempts + // to determine what the monorail task wants to do. + // + // Whether or not the PHY oscillator was found to be operating + // nominally is recorded in the front IO board controller. Look + // up what this value is to determine if a power reset of the + // front IO board is needed. + match self + .front_io_board + .phy_osc_state() + .map_err(SeqError::from)? + { + PhyOscState::Bad => { + // The PHY was attempted to be initialized but its + // oscillator was deemed not functional. Unfortunately + // the only course of action is to power cycle the + // entire front IO board, so do so now. + self.front_io_board.set_power_enable(false)?; + // After removing power to the board we must reset its + // server + self.front_io_board.board_reset(); + ringbuf_entry!(Trace::FrontIOBoardPowerEnable(false)); + + // Wait some cool down period to allow caps to bleed off + // etc. + userlib::hl::sleep_for(1000); + } + PhyOscState::Good => { + // The PHY was initialized properly before and its + // oscillator declared operating nominally. Assume this + // has not changed and only a reset the PHY itself is + // desired. + self.front_io_board + .phy_disable_power() + .map_err(SeqError::from)?; + ringbuf_entry!(Trace::FrontIOBoardPhyPowerEnable(false)); + + userlib::hl::sleep_for(10); + } + PhyOscState::Unknown => { + // Do nothing (yet) since the oscillator state is + // unknown. } } // Run preinit to check HSC status. - self.front_io_board_preinit()?; - - if let Some(front_io_board) = self.front_io_board.as_mut() { + if self.front_io_board_preinit()? { // At this point the front IO board has either not yet been // initalized or may have been power cycled and should be // initialized. - if !front_io_board.initialized() { - front_io_board.init()?; + while !self.front_io_board.board_fpgas_initialized() { + userlib::hl::sleep_for(20); } // The PHY is still powered down. Request the sequencer to power up // and wait for it to be ready. - front_io_board.phy().set_phy_power_enabled(true)?; + self.front_io_board + .phy_enable_power() + .map_err(SeqError::from)?; ringbuf_entry!(Trace::FrontIOBoardPhyPowerEnable(true)); - while !front_io_board.phy().powered_up_and_ready()? { + while !self.front_io_board.phy_ready().map_err(SeqError::from)? { userlib::hl::sleep_for(20); } @@ -341,12 +313,11 @@ impl ServerImpl { // interface initialization the chassis either should not have a front IO // board or the oscillator should have been found operating nominally before // transitioning to A0. - fn ready_for_tofino_power_up(&self) -> Result { - match self.front_io_phy_osc_good() { - Ok(osc_good) => Ok(osc_good), - Err(SeqError::NoFrontIOBoard) => Ok(true), - Err(e) => Err(e), - } + fn ready_for_tofino_power_up(&self) -> bool { + matches!( + self.front_io_board.board_status(), + FrontIOStatus::NotPresent | FrontIOStatus::Ready + ) } } @@ -504,20 +475,6 @@ impl idl::InOrderSequencerImpl for ServerImpl { Ok(self.clock_generator.config_loaded) } - fn front_io_board_present( - &mut self, - _: &RecvMessage, - ) -> Result> { - Ok(self.front_io_board.is_some()) - } - - fn front_io_board_ready( - &mut self, - _: &RecvMessage, - ) -> Result> { - self.front_io_phy_osc_good().map_err(RequestError::from) - } - fn reset_front_io_phy( &mut self, _: &RecvMessage, @@ -526,49 +483,6 @@ impl idl::InOrderSequencerImpl for ServerImpl { .map_err(RequestError::from) } - fn set_front_io_phy_osc_state( - &mut self, - _: &RecvMessage, - good: bool, - ) -> Result<(), RequestError> { - let front_io_board = self - .front_io_board - .as_ref() - .ok_or(SeqError::NoFrontIOBoard)?; - - match front_io_board - .phy() - .osc_state() - .map_err(SeqError::from) - .map_err(RequestError::from)? - { - // The state of the oscillator has not yet been examined or was - // marked bad in the previous run. Update as appropriate. - PhyOscState::Unknown | PhyOscState::Bad => { - ringbuf_entry!(if good { - Trace::FrontIOBoardPhyOscGood - } else { - Trace::FrontIOBoardPhyOscBad - }); - - front_io_board - .phy() - .set_osc_good(good) - .map_err(SeqError::from) - .map_err(RequestError::from) - } - // The oscillator is already marked good and this state only changes - // if it (and by extension the whole front IO board) is power - // cycled. In that case the value of this register in the FPGA is - // automatically reset when the bitstream is loaded and the other - // arm of this match would be taken. - // - // So ignore this call if the oscillator has been found good since the last power - // cycle of the front IO board. - PhyOscState::Good => Ok(()), - } - } - fn tofino_debug_port_state( &mut self, _: &RecvMessage, @@ -782,8 +696,7 @@ impl NotificationHandler for ServerImpl { // plane. See the comment of `ready_for_tofino_power_up` for more // context. if !self.tofino.ready_for_power_up { - self.tofino.ready_for_power_up = - self.ready_for_tofino_power_up().unwrap_or(false); + self.tofino.ready_for_power_up = self.ready_for_tofino_power_up(); } if let Err(e) = self.tofino.handle_tick() { @@ -825,8 +738,8 @@ fn main() -> ! { MainboardController::new(MAINBOARD.get_task_id()); let clock_generator = ClockGenerator::new(i2c_task); let tofino = Tofino::new(i2c_task); - let front_io_hsc = HotSwapController::new(MAINBOARD.get_task_id()); let fan_modules = FanModules::new(MAINBOARD.get_task_id()); + let front_io_board = FrontIO::from(FRONT_IO.get_task_id()); let sys = sys_api::Sys::from(SYS.get_task_id()); sys.gpio_configure_input(POWER_GOOD, sys_api::Pull::None); @@ -835,8 +748,7 @@ fn main() -> ! { mainboard_controller, clock_generator, tofino, - front_io_hsc, - front_io_board: None, + front_io_board, fan_modules, led_blink_on: false, sys, @@ -974,20 +886,7 @@ fn main() -> ! { match server.front_io_board_preinit() { Ok(true) => { ringbuf_entry!(Trace::FrontIOBoardPresent); - - let mut front_io_board = FrontIOBoard::new( - FRONT_IO.get_task_id(), - AUXFLASH.get_task_id(), - ); - - front_io_board.init().unwrap_lite(); - // TODO (arjen): check/load VPD data into packrat. - - // So far the front IO board looks functional. Assign it to the - // server, implicitly marking it present for the lifetime of this - // task. - server.front_io_board = Some(front_io_board); } Ok(false) => ringbuf_entry!(Trace::FrontIOBoardNotPresent), Err(SeqError::FrontIOBoardPowerFault) => { diff --git a/drv/sidecar-seq-server/src/tofino.rs b/drv/sidecar-seq-server/src/tofino.rs index 9eb3910e43..32f3d80e7b 100644 --- a/drv/sidecar-seq-server/src/tofino.rs +++ b/drv/sidecar-seq-server/src/tofino.rs @@ -3,6 +3,7 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. use crate::*; +use drv_fpga_user_api::power_rail::PowerRailStatus; use drv_i2c_devices::raa229618::Raa229618; pub(crate) struct Tofino { diff --git a/drv/transceivers-api/Cargo.toml b/drv/transceivers-api/Cargo.toml index ecfeb3606e..cbcb42155e 100644 --- a/drv/transceivers-api/Cargo.toml +++ b/drv/transceivers-api/Cargo.toml @@ -13,6 +13,7 @@ zerocopy-derive = { workspace = true } counters = { path = "../../lib/counters" } derive-idol-err = { path = "../../lib/derive-idol-err" } drv-fpga-api = { path = "../fpga-api" } +drv-front-io-api = { path = "../front-io-api" } task-sensor-api = { path = "../../task/sensor-api" } userlib = { path = "../../sys/userlib" } diff --git a/drv/transceivers-api/build.rs b/drv/transceivers-api/build.rs index 5e1c25465e..27630d2398 100644 --- a/drv/transceivers-api/build.rs +++ b/drv/transceivers-api/build.rs @@ -3,11 +3,6 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. fn main() -> Result<(), Box> { - idol::client::build_client_stub( - "../../idl/transceivers.idol", - "client_stub.rs", - )?; - let disposition = build_i2c::Disposition::Sensors; if let Err(e) = build_i2c::codegen(disposition) { println!("code generation failed: {}", e); diff --git a/drv/transceivers-api/src/lib.rs b/drv/transceivers-api/src/lib.rs index 4d3854275d..babf0208ab 100644 --- a/drv/transceivers-api/src/lib.rs +++ b/drv/transceivers-api/src/lib.rs @@ -8,8 +8,9 @@ use derive_idol_err::IdolError; use drv_fpga_api::FpgaError; +use drv_front_io_api::transceivers::NUM_PORTS; use task_sensor_api::{config::other_sensors, SensorId}; -use userlib::{sys_send, FromPrimitive}; +use userlib::FromPrimitive; use zerocopy::{FromBytes, Immutable, IntoBytes, KnownLayout}; #[derive( @@ -17,12 +18,8 @@ use zerocopy::{FromBytes, Immutable, IntoBytes, KnownLayout}; )] pub enum TransceiversError { FpgaError = 1, - InvalidPortNumber, - InvalidNumberOfBytes, InvalidPowerState, - InvalidModuleResult, LedI2cError, - InvalidPhysicalToLogicalMap, #[idol(server_death)] ServerRestarted, @@ -60,10 +57,6 @@ pub struct ModuleStatus { /// See SFF-8636 and CMIS specifications for details. pub const PAGE_SIZE_BYTES: usize = 128; -/// The only instantiation of Front IO board that exists is one with 32 QSFP -/// ports. -pub const NUM_PORTS: u8 = 32; - //////////////////////////////////////////////////////////////////////////////// pub const TRANSCEIVER_TEMPERATURE_SENSORS: [SensorId; NUM_PORTS as usize] = [ @@ -100,6 +93,3 @@ pub const TRANSCEIVER_TEMPERATURE_SENSORS: [SensorId; NUM_PORTS as usize] = [ other_sensors::QSFP_XCVR30_TEMPERATURE_SENSOR, other_sensors::QSFP_XCVR31_TEMPERATURE_SENSOR, ]; -//////////////////////////////////////////////////////////////////////////////// - -include!(concat!(env!("OUT_DIR"), "/client_stub.rs")); diff --git a/drv/transceivers-server/Cargo.toml b/drv/transceivers-server/Cargo.toml index cde7bd4938..6eedab343d 100644 --- a/drv/transceivers-server/Cargo.toml +++ b/drv/transceivers-server/Cargo.toml @@ -7,12 +7,9 @@ edition = "2021" [dependencies] counters = { path = "../../lib/counters" } drv-fpga-api = { path = "../fpga-api" } -drv-i2c-api = { path = "../i2c-api" } -drv-i2c-devices = { path = "../i2c-devices" } -drv-sidecar-front-io = { path = "../sidecar-front-io", features = ["transceivers", "leds"] } +drv-front-io-api = { path = "../front-io-api" } drv-sidecar-seq-api = { path = "../sidecar-seq-api" } drv-transceivers-api = { path = "../transceivers-api" } -multitimer = { path = "../../lib/multitimer" } ringbuf = { path = "../../lib/ringbuf" } static-cell = { path = "../../lib/static-cell" } task-net-api = { path = "../../task/net-api" } @@ -39,7 +36,6 @@ no-ipc-counters = ["idol/no-counters"] [build-dependencies] build-util = { path = "../../build/util" } -build-i2c = { path = "../../build/i2c" } idol = { workspace = true } diff --git a/drv/transceivers-server/build.rs b/drv/transceivers-server/build.rs index 9242e7fd85..8ec03a4b09 100644 --- a/drv/transceivers-server/build.rs +++ b/drv/transceivers-server/build.rs @@ -6,22 +6,11 @@ fn main() -> Result<(), Box> { build_util::expose_target_board(); build_util::build_notifications()?; - let disposition = build_i2c::Disposition::Devices; - - if let Err(e) = build_i2c::codegen(disposition) { - println!("code generation failed: {}", e); - std::process::exit(1); - } - - idol::Generator::new() - .with_counters( - idol::CounterSettings::default().with_server_counters(false), - ) - .build_server_support( - "../../idl/transceivers.idol", - "server_stub.rs", - idol::server::ServerStyle::InOrder, - )?; + idol::Generator::new().build_server_support( + "../../idl/transceivers.idol", + "server_stub.rs", + idol::server::ServerStyle::InOrder, + )?; Ok(()) } diff --git a/drv/transceivers-server/src/main.rs b/drv/transceivers-server/src/main.rs index 7b01b83e37..e4ee826093 100644 --- a/drv/transceivers-server/src/main.rs +++ b/drv/transceivers-server/src/main.rs @@ -7,62 +7,44 @@ use counters::Count; use idol_runtime::{NotificationHandler, RequestError}; -use multitimer::{Multitimer, Repeat}; use ringbuf::*; use static_cell::ClaimOnceCell; -use userlib::{sys_get_timer, task_slot, units::Celsius}; +use userlib::{set_timer_relative, task_slot, units::Celsius, RecvMessage}; use drv_fpga_api::FpgaError; -use drv_i2c_devices::pca9956b::Error; -use drv_sidecar_front_io::{ - leds::{FullErrorSummary, Leds}, +use drv_front_io_api::{ transceivers::{ - FpgaI2CFailure, LogicalPort, LogicalPortMask, Transceivers, + FpgaI2CFailure, LogicalPort, LogicalPortMask, ModuleStatus, NUM_PORTS, }, - Reg, + FrontIO, FrontIOError, FrontIOStatus, Reg, }; -use drv_sidecar_seq_api::{SeqError, Sequencer, TofinoSeqState}; use drv_transceivers_api::{ - ModuleStatus, TransceiversError, NUM_PORTS, TRANSCEIVER_TEMPERATURE_SENSORS, + TransceiversError, TRANSCEIVER_TEMPERATURE_SENSORS, }; -use enum_map::Enum; use task_sensor_api::{NoData, Sensor}; #[allow(unused_imports)] use task_thermal_api::{Thermal, ThermalError, ThermalProperties}; -use transceiver_messages::{ - message::LedState, mgmt::ManagementInterface, MAX_PACKET_SIZE, -}; +use transceiver_messages::{mgmt::ManagementInterface, MAX_PACKET_SIZE}; use zerocopy::{FromBytes, FromZeros, IntoBytes}; mod udp; // UDP API is implemented in a separate file -task_slot!(I2C, i2c_driver); task_slot!(FRONT_IO, front_io); -task_slot!(SEQ, seq); task_slot!(NET, net); task_slot!(SENSOR, sensor); #[cfg(feature = "thermal-control")] task_slot!(THERMAL, thermal); -include!(concat!(env!("OUT_DIR"), "/i2c_config.rs")); - #[allow(dead_code)] #[derive(Copy, Clone, PartialEq, Eq, Count)] enum Trace { #[count(skip)] None, - FrontIOBoardReady(#[count(children)] bool), - FrontIOSeqErr(SeqError), + FrontIOStatus(#[count(children)] FrontIOStatus), LEDInit, - LEDInitComplete, - LEDInitError(Error), - LEDErrorSummary(FullErrorSummary), - LEDUninitialized, - LEDEnableError(FpgaError), - LEDReadError(Error), - LEDUpdateError(Error), + LEDEnableError(FrontIOError), ModulePresenceUpdate(LogicalPortMask), TransceiversError(#[count(children)] TransceiversError), GotInterface(u8, ManagementInterface), @@ -78,7 +60,6 @@ enum Trace { DisablingPorts(LogicalPortMask), DisableFailed(usize, LogicalPortMask), ClearDisabledPorts(LogicalPortMask), - SeqError(SeqError), } counted_ringbuf!(Trace, 16, Trace::None); @@ -99,31 +80,14 @@ const MAX_CONSECUTIVE_ERRORS: u8 = 3; //////////////////////////////////////////////////////////////////////////////// -#[derive(Copy, Clone)] -struct LedStates([LedState; NUM_PORTS as usize]); - -#[derive(Copy, Clone, PartialEq)] -enum FrontIOStatus { - NotReady, - NotPresent, - Ready, -} - struct ServerImpl { - transceivers: Transceivers, - leds: Leds, + /// The FrontIO server is the interface to the transceivers and LED drivers + front_io: FrontIO, net: task_net_api::Net, modules_present: LogicalPortMask, /// The Front IO board is not guaranteed to be present and ready - front_io_board_present: FrontIOStatus, - - /// State around LED management - led_error: FullErrorSummary, - leds_initialized: bool, - led_states: LedStates, - blink_on: bool, - system_led_state: LedState, + front_io_status: FrontIOStatus, /// Modules that are physically present but disabled by Hubris disabled: LogicalPortMask, @@ -155,79 +119,7 @@ struct ThermalModel { /// /// For transceivers that are present and include a thermal model, we measure /// their temperature and send it to the `thermal` task. -const SPI_INTERVAL: u64 = 500; - -/// Controls how often we update the LED controllers (in milliseconds). -const I2C_INTERVAL: u64 = 100; - -/// Blink LEDs at a 50% duty cycle (in milliseconds) -const BLINK_INTERVAL: u64 = 500; - -impl ServerImpl { - fn led_init(&mut self) { - match self.leds.initialize_current() { - Ok(_) => { - self.set_system_led_state(LedState::On); - self.leds_initialized = true; - ringbuf_entry!(Trace::LEDInitComplete); - } - Err(e) => ringbuf_entry!(Trace::LEDInitError(e)), - }; - } - - fn set_led_state(&mut self, mask: LogicalPortMask, state: LedState) { - for index in mask.to_indices() { - self.led_states.0[index.0 as usize] = state; - } - } - - fn get_led_state(&self, port: LogicalPort) -> LedState { - self.led_states.0[port.0 as usize] - } - - fn set_system_led_state(&mut self, state: LedState) { - self.system_led_state = state; - } - - #[allow(dead_code)] - fn get_system_led_state(&self) -> LedState { - self.system_led_state - } - - fn update_leds(&mut self, seq_state: TofinoSeqState) { - let mut next_state = LogicalPortMask(0); - - // We only turn transceiver LEDs on when Sidecar is in A0, since that is when there can be - // meaningful link activity happening. When outside of A0, we default the LEDs to off. - if seq_state == TofinoSeqState::A0 { - for (i, state) in self.led_states.0.into_iter().enumerate() { - let i = LogicalPort(i as u8); - match state { - LedState::On => next_state.set(i), - LedState::Blink => { - if self.blink_on { - next_state.set(i) - } - } - LedState::Off => (), - } - } - } - - if let Err(e) = self.leds.update_led_state(next_state) { - ringbuf_entry!(Trace::LEDUpdateError(e)); - } - // handle system LED - let system_led_on = match self.system_led_state { - LedState::On => true, - LedState::Blink => self.blink_on, - LedState::Off => false, - }; - if let Err(e) = self.leds.update_system_led_state(system_led_on) { - ringbuf_entry!(Trace::LEDUpdateError(e)); - } - } -} +const SPI_INTERVAL: u32 = 500; impl ServerImpl { /// Returns the temperature from a CMIS transceiver. @@ -259,7 +151,15 @@ impl ServerImpl { port: LogicalPort, reg: u8, ) -> Result { - let result = self.transceivers.setup_i2c_read(reg, 2, port.as_mask()); + let result = match self.front_io.transceivers_setup_i2c_read( + reg, + 2, + port.as_mask(), + ) { + Ok(r) => r, + Err(e) => return Err(FpgaError::ImplError(e as u8)), + }; + if !result.error().is_empty() { return Err(FpgaError::CommsError); } @@ -272,8 +172,12 @@ impl ServerImpl { let mut out = Temperature::new_zeroed(); let status = self - .transceivers - .get_i2c_status_and_read_buffer(port, out.as_mut_bytes())?; + .front_io + .transceivers_get_i2c_status_and_read_buffer( + port, + out.as_mut_bytes(), + ) + .map_err(|e| FpgaError::ImplError(e as u8))?; if status.error == FpgaI2CFailure::NoError { // "Internally measured free side device temperatures are @@ -291,7 +195,15 @@ impl ServerImpl { &mut self, port: LogicalPort, ) -> Result { - let result = self.transceivers.setup_i2c_read(0, 1, port.as_mask()); + let result = match self.front_io.transceivers_setup_i2c_read( + 0, + 1, + port.as_mask(), + ) { + Ok(r) => r, + Err(e) => return Err(FpgaError::ImplError(e as u8)), + }; + if !result.error().is_empty() { return Err(FpgaError::CommsError); } @@ -300,8 +212,9 @@ impl ServerImpl { // Wait for the I2C transaction to complete let status = self - .transceivers - .get_i2c_status_and_read_buffer(port, &mut out)?; + .front_io + .transceivers_get_i2c_status_and_read_buffer(port, &mut out) + .map_err(|e| FpgaError::ImplError(e as u8))?; if status.error == FpgaI2CFailure::NoError { match out[0] { @@ -500,14 +413,14 @@ impl ServerImpl { fn disable_ports(&mut self, mask: LogicalPortMask) { ringbuf_entry!(Trace::DisablingPorts(mask)); for (step, f) in [ - Transceivers::assert_reset, - Transceivers::deassert_lpmode, - Transceivers::disable_power, + FrontIO::transceivers_assert_reset, + FrontIO::transceivers_deassert_lpmode, + FrontIO::transceivers_disable_power, ] .iter() .enumerate() { - let err = f(&mut self.transceivers, mask).error(); + let err = f(&mut self.front_io, mask).error(); if !err.is_empty() { ringbuf_entry!(Trace::DisableFailed(step, err)); } @@ -518,30 +431,11 @@ impl ServerImpl { // the `sensors` and `thermal` tasks. } - fn handle_i2c_loop(&mut self, seq_state: TofinoSeqState) { - if self.leds_initialized { - self.update_leds(seq_state); - let errors = match self.leds.error_summary() { - Ok(errs) => errs, - Err(e) => { - ringbuf_entry!(Trace::LEDReadError(e)); - Default::default() - } - }; - if errors != self.led_error { - self.led_error = errors; - ringbuf_entry!(Trace::LEDErrorSummary(errors)); - } - } else { - ringbuf_entry!(Trace::LEDUninitialized); - } - } - fn handle_spi_loop(&mut self) { // Query module presence as this drives other state - let (status, _) = self.transceivers.get_module_status(); + let xcvr_status = self.front_io.transceivers_status(); - let modules_present = LogicalPortMask(!status.modprsl); + let modules_present = LogicalPortMask(!xcvr_status.status.modprsl); if modules_present != self.modules_present { // check to see if any disabled ports had their modules removed and // allow their power to be turned on when a module is reinserted @@ -549,7 +443,8 @@ impl ServerImpl { self.modules_present & !modules_present & self.disabled; if !disabled_ports_removed.is_empty() { self.disabled &= !disabled_ports_removed; - self.transceivers.enable_power(disabled_ports_removed); + self.front_io + .transceivers_enable_power(disabled_ports_removed); ringbuf_entry!(Trace::ClearDisabledPorts( disabled_ports_removed )); @@ -559,47 +454,17 @@ impl ServerImpl { ringbuf_entry!(Trace::ModulePresenceUpdate(modules_present)); } - self.update_thermal_loop(status); + self.update_thermal_loop(xcvr_status.status); } } //////////////////////////////////////////////////////////////////////////////// impl idl::InOrderTransceiversImpl for ServerImpl { - fn get_module_status( - &mut self, - _msg: &userlib::RecvMessage, - ) -> Result> - { - let (mod_status, result) = self.transceivers.get_module_status(); - if result.error().is_empty() { - Ok(mod_status) - } else { - Err(RequestError::from(TransceiversError::FpgaError)) - } - } - - fn set_system_led_on( - &mut self, - _msg: &userlib::RecvMessage, - ) -> Result<(), idol_runtime::RequestError> { - self.set_system_led_state(LedState::On); - Ok(()) - } - - fn set_system_led_off( - &mut self, - _msg: &userlib::RecvMessage, - ) -> Result<(), idol_runtime::RequestError> { - self.set_system_led_state(LedState::Off); - Ok(()) - } - - fn set_system_led_blink( + fn ping( &mut self, - _msg: &userlib::RecvMessage, - ) -> Result<(), idol_runtime::RequestError> { - self.set_system_led_state(LedState::Blink); + _msg: &RecvMessage, + ) -> Result<(), RequestError> { Ok(()) } } @@ -610,8 +475,11 @@ impl NotificationHandler for ServerImpl { } fn handle_notification(&mut self, _bits: u32) { - // Nothing to do here; notifications are just to wake up this task, and - // all of the actual work is handled in the main loop + if self.front_io_status == FrontIOStatus::Ready { + self.handle_spi_loop(); + } + + set_timer_relative(SPI_INTERVAL, notifications::TIMER_MASK); } } @@ -620,13 +488,7 @@ fn main() -> ! { // This is a temporary workaround that makes sure the FPGAs are up // before we start doing things with them. A more sophisticated // notification system will be put in place. - let seq = Sequencer::from(SEQ.get_task_id()); - - let transceivers = Transceivers::new(FRONT_IO.get_task_id()); - let leds = Leds::new( - &i2c_config::devices::pca9956b_front_leds_left(I2C.get_task_id()), - &i2c_config::devices::pca9956b_front_leds_right(I2C.get_task_id()), - ); + let front_io = FrontIO::from(FRONT_IO.get_task_id()); let net = task_net_api::Net::from(NET.get_task_id()); let sensor_api = Sensor::from(SENSOR.get_task_id()); @@ -643,16 +505,10 @@ fn main() -> ! { let thermal_api = Thermal::from(THERMAL.get_task_id()); let mut server = ServerImpl { - transceivers, - leds, + front_io, net, modules_present: LogicalPortMask(0), - front_io_board_present: FrontIOStatus::NotReady, - led_error: Default::default(), - leds_initialized: false, - led_states: LedStates([LedState::Off; NUM_PORTS as usize]), - blink_on: false, - system_led_state: LedState::Off, + front_io_status: FrontIOStatus::NotPresent, disabled: LogicalPortMask(0), consecutive_errors: [0; NUM_PORTS as usize], #[cfg(feature = "thermal-control")] @@ -661,109 +517,36 @@ fn main() -> ! { thermal_models: [None; NUM_PORTS as usize], }; - // There are two timers, one for each communication bus: - #[derive(Copy, Clone, Enum)] - #[allow(clippy::upper_case_acronyms)] - enum Timers { - I2C, - SPI, - Blink, - } - let mut multitimer = Multitimer::::new(notifications::TIMER_BIT); - // Immediately fire each timer, then begin to service regularly - let now = sys_get_timer().now; - multitimer.set_timer( - Timers::I2C, - now, - Some(Repeat::AfterDeadline(I2C_INTERVAL)), - ); - multitimer.set_timer( - Timers::SPI, - now, - Some(Repeat::AfterDeadline(SPI_INTERVAL)), - ); - multitimer.set_timer( - Timers::Blink, - now, - Some(Repeat::AfterDeadline(BLINK_INTERVAL)), - ); - + // + // This will put our timer in the past, and should immediately kick us. + // + set_timer_relative(0, notifications::TIMER_MASK); let mut buffer = [0; idl::INCOMING_SIZE]; + loop { - if server.front_io_board_present == FrontIOStatus::NotReady { - server.front_io_board_present = match seq.front_io_board_ready() { - Ok(true) => { - ringbuf_entry!(Trace::FrontIOBoardReady(true)); - FrontIOStatus::Ready - } - Err(SeqError::NoFrontIOBoard) => { - ringbuf_entry!(Trace::FrontIOSeqErr( - SeqError::NoFrontIOBoard - )); - FrontIOStatus::NotPresent - } - _ => { - ringbuf_entry!(Trace::FrontIOBoardReady(false)); - FrontIOStatus::NotReady - } + // We do this check within the main loop because we still want to + // service any requests from `net` even if a front IO board is not ready + // If a board is ready, attempt to initialize its LED drivers + if server.front_io_status == FrontIOStatus::Ready { + if let Err(e) = server.front_io.leds_enable() { + ringbuf_entry!(Trace::LEDEnableError(e)); }; - - // If a board is present, attempt to initialize its - // LED drivers - if server.front_io_board_present == FrontIOStatus::Ready { - ringbuf_entry!(Trace::LEDInit); - match server.transceivers.enable_led_controllers() { - Ok(_) => server.led_init(), - Err(e) => { - ringbuf_entry!(Trace::LEDEnableError(e)) - } - }; - } - } - - multitimer.poll_now(); - for t in multitimer.iter_fired() { - match t { - Timers::I2C => { - // Check what power state we are in since that can impact LED state which is - // part of the I2C loop. - let seq_state = - seq.tofino_seq_state().unwrap_or_else(|e| { - // The failure path here is that we cannot get the state from the FPGA. - // If we cannot communicate with the FPGA then something has likely went - // rather wrong, and we are probably not in A0. For handling the error - // we will assume to be in the Init state, since that is what the main - // sequencer does as well. - ringbuf_entry!(Trace::SeqError(e)); - TofinoSeqState::Init - }); - - // Handle the Front IO status checking as part of this - // loop because the frequency is what we had before and - // the server itself has no knowledge of the sequencer. - server.handle_i2c_loop(seq_state); - } - Timers::SPI => { - if server.front_io_board_present == FrontIOStatus::Ready { - server.handle_spi_loop(); - } - } - Timers::Blink => { - server.blink_on = !server.blink_on; - } - } + } else { + userlib::hl::sleep_for(5); + server.front_io_status = server.front_io.board_status(); + ringbuf_entry!(Trace::FrontIOStatus(server.front_io_status)); } + // monitor messages from the host server .check_net(tx_data_buf.as_mut_slice(), rx_data_buf.as_mut_slice()); + idol_runtime::dispatch(&mut buffer, &mut server); } } -mod idl { - use super::{ModuleStatus, TransceiversError}; +include!(concat!(env!("OUT_DIR"), "/notifications.rs")); +mod idl { include!(concat!(env!("OUT_DIR"), "/server_stub.rs")); } - -include!(concat!(env!("OUT_DIR"), "/notifications.rs")); diff --git a/drv/transceivers-server/src/udp.rs b/drv/transceivers-server/src/udp.rs index 22cf149d79..fa94a4df12 100644 --- a/drv/transceivers-server/src/udp.rs +++ b/drv/transceivers-server/src/udp.rs @@ -17,9 +17,12 @@ use ringbuf::*; use userlib::UnwrapLite; use crate::{FrontIOStatus, ServerImpl}; -use drv_sidecar_front_io::transceivers::{ - FpgaI2CFailure, LogicalPort, LogicalPortFailureTypes, LogicalPortMask, - ModuleResult, ModuleResultNoFailure, ModuleResultSlim, +use drv_front_io_api::{ + transceivers::{ + FpgaI2CFailure, LogicalPort, LogicalPortFailureTypes, LogicalPortMask, + ModuleResult, ModuleResultNoFailure, ModuleResultSlim, + }, + FrontIOError, }; use task_net_api::*; use transceiver_messages::{ @@ -65,6 +68,7 @@ enum Trace { PageSelectI2CFailures(LogicalPort, FpgaI2CFailure), ReadI2CFailures(LogicalPort, FpgaI2CFailure), WriteI2CFailures(LogicalPort, FpgaI2CFailure), + FrontIOError(FrontIOError), } counted_ringbuf!(Trace, 32, Trace::None); @@ -310,9 +314,7 @@ impl ServerImpl { HostRequest::Status(modules) => { ringbuf_entry!(Trace::Status(modules)); let mask = LogicalPortMask::from(modules); - let (data_len, result) = if self.front_io_board_present - == FrontIOStatus::Ready - { + let (data_len, result) = if self.front_io.board_ready() { self.get_status(mask, out) } else { ( @@ -339,9 +341,7 @@ impl ServerImpl { HostRequest::ExtendedStatus(modules) => { ringbuf_entry!(Trace::ExtendedStatus(modules)); let mask = LogicalPortMask::from(modules); - let (data_len, result) = if self.front_io_board_present - == FrontIOStatus::Ready - { + let (data_len, result) = if self.front_io.board_ready() { self.get_extended_status(mask, out) } else { ( @@ -389,23 +389,22 @@ impl ServerImpl { ); } - let (data_len, result) = - if self.front_io_board_present == FrontIOStatus::Ready { - let r = self.read(read, mask & !self.disabled, out); - let len = r.success().count() * read.len() as usize; - (len, r) - } else { - ( - 0, - ModuleResult::new( - LogicalPortMask(0), - LogicalPortMask(0), - mask, - LogicalPortFailureTypes::default(), - ) - .unwrap_lite(), + let (data_len, result) = if self.front_io.board_ready() { + let r = self.read(read, mask & !self.disabled, out); + let len = r.success().count() * read.len() as usize; + (len, r) + } else { + ( + 0, + ModuleResult::new( + LogicalPortMask(0), + LogicalPortMask(0), + mask, + LogicalPortFailureTypes::default(), ) - }; + .unwrap_lite(), + ) + }; ringbuf_entry!(Trace::OperationResult(result.to_slim())); let success = ModuleId::from(result.success()); @@ -439,18 +438,17 @@ impl ServerImpl { } let mask = LogicalPortMask::from(modules); - let result = - if self.front_io_board_present == FrontIOStatus::Ready { - self.write(write, mask & !self.disabled, data) - } else { - ModuleResult::new( - LogicalPortMask(0), - LogicalPortMask(0), - mask, - LogicalPortFailureTypes::default(), - ) - .unwrap_lite() - }; + let result = if self.front_io.board_ready() { + self.write(write, mask & !self.disabled, data) + } else { + ModuleResult::new( + LogicalPortMask(0), + LogicalPortMask(0), + mask, + LogicalPortFailureTypes::default(), + ) + .unwrap_lite() + }; ringbuf_entry!(Trace::OperationResult(result.to_slim())); let success = ModuleId::from(result.success()); @@ -472,13 +470,12 @@ impl ServerImpl { ringbuf_entry!(Trace::AssertReset(modules)); let mask = LogicalPortMask::from(modules) & !self.disabled; - let result = - if self.front_io_board_present == FrontIOStatus::Ready { - self.transceivers.assert_reset(mask) - } else { - ModuleResultNoFailure::new(LogicalPortMask(0), mask) - .unwrap_lite() - }; + let result = if self.front_io.board_ready() { + self.front_io.transceivers_assert_reset(mask) + } else { + ModuleResultNoFailure::new(LogicalPortMask(0), mask) + .unwrap_lite() + }; ringbuf_entry!(Trace::OperationNoFailResult(result)); let success = ModuleId::from(result.success()); @@ -497,13 +494,12 @@ impl ServerImpl { ringbuf_entry!(Trace::DeassertReset(modules)); let mask = LogicalPortMask::from(modules) & !self.disabled; - let result = - if self.front_io_board_present == FrontIOStatus::Ready { - self.transceivers.deassert_reset(mask) - } else { - ModuleResultNoFailure::new(LogicalPortMask(0), mask) - .unwrap_lite() - }; + let result = if self.front_io.board_ready() { + self.front_io.transceivers_deassert_reset(mask) + } else { + ModuleResultNoFailure::new(LogicalPortMask(0), mask) + .unwrap_lite() + }; ringbuf_entry!(Trace::OperationNoFailResult(result)); let success = ModuleId::from(result.success()); @@ -522,13 +518,12 @@ impl ServerImpl { ringbuf_entry!(Trace::AssertLpMode(modules)); let mask = LogicalPortMask::from(modules) & !self.disabled; - let result = - if self.front_io_board_present == FrontIOStatus::Ready { - self.transceivers.assert_lpmode(mask) - } else { - ModuleResultNoFailure::new(LogicalPortMask(0), mask) - .unwrap_lite() - }; + let result = if self.front_io.board_ready() { + self.front_io.transceivers_assert_lpmode(mask) + } else { + ModuleResultNoFailure::new(LogicalPortMask(0), mask) + .unwrap_lite() + }; ringbuf_entry!(Trace::OperationNoFailResult(result)); let success = ModuleId::from(result.success()); @@ -547,13 +542,12 @@ impl ServerImpl { ringbuf_entry!(Trace::DeassertLpMode(modules)); let mask = LogicalPortMask::from(modules) & !self.disabled; - let result = - if self.front_io_board_present == FrontIOStatus::Ready { - self.transceivers.deassert_lpmode(mask) - } else { - ModuleResultNoFailure::new(LogicalPortMask(0), mask) - .unwrap_lite() - }; + let result = if self.front_io.board_ready() { + self.front_io.transceivers_deassert_lpmode(mask) + } else { + ModuleResultNoFailure::new(LogicalPortMask(0), mask) + .unwrap_lite() + }; ringbuf_entry!(Trace::OperationNoFailResult(result)); let success = ModuleId::from(result.success()); @@ -572,13 +566,12 @@ impl ServerImpl { ringbuf_entry!(Trace::EnablePower(modules)); let mask = LogicalPortMask::from(modules) & !self.disabled; - let result = - if self.front_io_board_present == FrontIOStatus::Ready { - self.transceivers.enable_power(mask) - } else { - ModuleResultNoFailure::new(LogicalPortMask(0), mask) - .unwrap_lite() - }; + let result = if self.front_io.board_ready() { + self.front_io.transceivers_enable_power(mask) + } else { + ModuleResultNoFailure::new(LogicalPortMask(0), mask) + .unwrap_lite() + }; ringbuf_entry!(Trace::OperationNoFailResult(result)); let success = ModuleId::from(result.success()); @@ -597,13 +590,12 @@ impl ServerImpl { ringbuf_entry!(Trace::DisablePower(modules)); let mask = LogicalPortMask::from(modules) & !self.disabled; - let result = - if self.front_io_board_present == FrontIOStatus::Ready { - self.transceivers.disable_power(mask) - } else { - ModuleResultNoFailure::new(LogicalPortMask(0), mask) - .unwrap_lite() - }; + let result = if self.front_io.board_ready() { + self.front_io.transceivers_disable_power(mask) + } else { + ModuleResultNoFailure::new(LogicalPortMask(0), mask) + .unwrap_lite() + }; ringbuf_entry!(Trace::OperationNoFailResult(result)); let success = ModuleId::from(result.success()); @@ -648,13 +640,12 @@ impl ServerImpl { ringbuf_entry!(Trace::ClearPowerFault(modules)); let mask = LogicalPortMask::from(modules) & !self.disabled; - let result = - if self.front_io_board_present == FrontIOStatus::Ready { - self.transceivers.clear_power_fault(mask) - } else { - ModuleResultNoFailure::new(LogicalPortMask(0), mask) - .unwrap_lite() - }; + let result = if self.front_io.board_ready() { + self.front_io.transceivers_clear_power_fault(mask) + } else { + ModuleResultNoFailure::new(LogicalPortMask(0), mask) + .unwrap_lite() + }; ringbuf_entry!(Trace::OperationNoFailResult(result)); let success = ModuleId::from(result.success()); @@ -672,9 +663,7 @@ impl ServerImpl { HostRequest::LedState(modules) => { ringbuf_entry!(Trace::LedState(modules)); let mask = LogicalPortMask::from(modules); - let (data_len, result) = if self.front_io_board_present - == FrontIOStatus::Ready - { + let (data_len, result) = if self.front_io.board_ready() { self.get_led_state_response(mask, out) } else { ( @@ -702,15 +691,14 @@ impl ServerImpl { ringbuf_entry!(Trace::SetLedState(modules, state)); let mask = LogicalPortMask::from(modules); - let result = - if self.front_io_board_present == FrontIOStatus::Ready { - self.set_led_state(mask, state); - ModuleResultNoFailure::new(mask, LogicalPortMask(0)) - .unwrap_lite() - } else { - ModuleResultNoFailure::new(LogicalPortMask(0), mask) - .unwrap_lite() - }; + let result = if self.front_io.board_ready() { + self.front_io.led_set_state(mask, state); + ModuleResultNoFailure::new(mask, LogicalPortMask(0)) + .unwrap_lite() + } else { + ModuleResultNoFailure::new(LogicalPortMask(0), mask) + .unwrap_lite() + }; // This operation just sets internal SP state, so it is always // successful. However, invalid modules may been specified by @@ -886,12 +874,14 @@ impl ServerImpl { // shared logic between the various functions which handle errors fn resolve_error_type(&self) -> HwError { - match self.front_io_board_present { + match self.front_io.board_status() { // Front IO is present and ready, so the only other error // path currently is if we handle an FpgaError. FrontIOStatus::Ready => HwError::FpgaError, FrontIOStatus::NotPresent => HwError::NoFrontIo, - FrontIOStatus::NotReady => HwError::FrontIoNotReady, + FrontIOStatus::Init + | FrontIOStatus::FpgaInit + | FrontIOStatus::OscInit => HwError::FrontIoNotReady, } } @@ -964,7 +954,9 @@ impl ServerImpl { ) -> (usize, ModuleResultNoFailure) { // This will get the status of every module, so we will have to only // select the data which was requested. - let (mod_status, full_result) = self.transceivers.get_module_status(); + let xcvr_status = self.front_io.transceivers_status(); + let mod_status = xcvr_status.status; + let full_result = xcvr_status.result; // adjust the result success mask to be only our requested modules let desired_result = ModuleResultNoFailure::new( full_result.success() & modules, @@ -1019,7 +1011,9 @@ impl ServerImpl { ) -> (usize, ModuleResultNoFailure) { // This will get the status of every module, so we will have to only // select the data which was requested. - let (mod_status, full_result) = self.transceivers.get_module_status(); + let xcvr_status = self.front_io.transceivers_status(); + let mod_status = xcvr_status.status; + let full_result = xcvr_status.result; // adjust the result success mask to be only our requested modules let desired_result = ModuleResultNoFailure::new( full_result.success() & modules, @@ -1089,12 +1083,29 @@ impl ServerImpl { // We can always write the lower page; upper pages require modifying // registers in the transceiver to select it. if let Some(page) = page.page() { - self.transceivers.set_i2c_write_buffer(&[page], mask); - result = result.chain(self.transceivers.setup_i2c_write( + result = match self + .front_io + .transceivers_set_i2c_write_buffer(mask, &[page]) + { + Ok(r) => result.chain(r), + Err(e) => { + ringbuf_entry!(Trace::FrontIOError(e)); + result + } + }; + + result = match self.front_io.transceivers_setup_i2c_write( PAGE_SELECT, 1, mask, - )); + ) { + Ok(r) => result.chain(r), + Err(e) => { + ringbuf_entry!(Trace::FrontIOError(e)); + result + } + }; + result = result.chain(self.wait_and_check_i2c(result.success())); } else { // If the request is to the lower page it is always successful @@ -1108,12 +1119,29 @@ impl ServerImpl { } if let Some(bank) = page.bank() { - self.transceivers.set_i2c_write_buffer(&[bank], mask); - result = result.chain(self.transceivers.setup_i2c_write( + result = match self + .front_io + .transceivers_set_i2c_write_buffer(mask, &[bank]) + { + Ok(r) => result.chain(r), + Err(e) => { + ringbuf_entry!(Trace::FrontIOError(e)); + result + } + }; + + result = match self.front_io.transceivers_setup_i2c_write( BANK_SELECT, 1, result.success(), - )); + ) { + Ok(r) => result.chain(r), + Err(e) => { + ringbuf_entry!(Trace::FrontIOError(e)); + result + } + }; + result = result.chain(self.wait_and_check_i2c(result.success())); } @@ -1132,7 +1160,7 @@ impl ServerImpl { // failure: The I2C operation failed. // error: The SP could not communicate with the FPGA. fn wait_and_check_i2c(&mut self, mask: LogicalPortMask) -> ModuleResult { - self.transceivers.wait_and_check_i2c(mask) + self.front_io.transceivers_wait_and_check_i2c(mask) } fn read( @@ -1145,11 +1173,17 @@ impl ServerImpl { let mut result = self.select_page(*mem.page(), modules); // Ask the FPGA to start the read - result = result.chain(self.transceivers.setup_i2c_read( + result = match self.front_io.transceivers_setup_i2c_read( mem.offset(), mem.len(), result.success(), - )); + ) { + Ok(r) => result.chain(r), + Err(e) => { + ringbuf_entry!(Trace::FrontIOError(e)); + result + } + }; let mut success = LogicalPortMask(0); let mut failure = LogicalPortMask(0); @@ -1160,14 +1194,15 @@ impl ServerImpl { for port in result.success().to_indices() { let mut buf = [0u8; 128]; - let port_loc = port.get_physical_location(); // If we have not encountered any errors, keep pulling full // status + buffer payloads. let status = match self - .transceivers - .get_i2c_status_and_read_buffer(port_loc, &mut buf[0..buf_len]) - { + .front_io + .transceivers_get_i2c_status_and_read_buffer( + port, + &mut buf[0..buf_len], + ) { Ok(status) => status, Err(_) => { error.set(port); @@ -1214,15 +1249,29 @@ impl ServerImpl { let mut result = self.select_page(*mem.page(), modules); // Copy data into the FPGA write buffer - self.transceivers - .set_i2c_write_buffer(&data[..mem.len() as usize], modules); + result = match self.front_io.transceivers_set_i2c_write_buffer( + modules, + &data[..mem.len() as usize], + ) { + Ok(r) => result.chain(r), + Err(e) => { + ringbuf_entry!(Trace::FrontIOError(e)); + result + } + }; // Trigger a multicast write to all transceivers in the mask - result = result.chain(self.transceivers.setup_i2c_write( + result = match self.front_io.transceivers_setup_i2c_write( mem.offset(), mem.len(), result.success(), - )); + ) { + Ok(r) => result.chain(r), + Err(e) => { + ringbuf_entry!(Trace::FrontIOError(e)); + result + } + }; result = result.chain(self.wait_and_check_i2c(result.success())); for port in result.failure().to_indices() { @@ -1240,7 +1289,9 @@ impl ServerImpl { out: &mut [u8], ) -> (usize, ModuleResultNoFailure) { let mut led_state_len = 0; - for led_state in modules.to_indices().map(|m| self.get_led_state(m)) { + for led_state in + modules.to_indices().map(|m| self.front_io.led_get_state(m)) + { // LedState will serialize to a u8, so we aren't concerned about // buffer overflow here let led_state_size = diff --git a/idl/front-io.idol b/idl/front-io.idol new file mode 100644 index 0000000000..9abaf65407 --- /dev/null +++ b/idl/front-io.idol @@ -0,0 +1,332 @@ +// Front IO API + +Interface( + name: "FrontIO", + ops: { + "set_power_enable": ( + args: { + "enable": "bool", + }, + reply: Result( + ok: "()", + err: CLike("FrontIOError"), + ), + ), + + "power_good": ( + args: {}, + reply: Result( + ok: "bool", + err: CLike("FrontIOError"), + ), + ), + + "board_reset": ( + args: {}, + reply: Simple("()"), + idempotent: true, + ), + + "board_status": ( + args: {}, + reply: Simple("FrontIOStatus"), + idempotent: true, + encoding: Hubpack, + ), + + "board_present": ( + args: {}, + reply: Simple("bool"), + idempotent: true, + ), + + "board_fpgas_initialized": ( + args: {}, + reply: Simple("bool"), + idempotent: true, + ), + + "board_ready": ( + args: {}, + reply: Simple("bool"), + idempotent: true, + ), + + "phy_osc_state": ( + args: {}, + reply: Result( + ok: ( + type: "PhyOscState", + recv: FromPrimitive("u8"), + ), + err: CLike("FrontIOError"), + ), + ), + + "phy_ready": ( + args: {}, + reply: Result( + ok: "bool", + err: CLike("FrontIOError"), + ), + ), + + "phy_set_osc_state": ( + args: { + "good": "bool", + }, + reply: Result( + ok: "()", + err: CLike("FrontIOError"), + ), + ), + + "phy_enable_power": ( + args: {}, + reply: Result( + ok: "()", + err: CLike("FrontIOError"), + ), + ), + + "phy_disable_power": ( + args: {}, + reply: Result( + ok: "()", + err: CLike("FrontIOError"), + ), + ), + + "phy_set_coma_mode": ( + args: { + "asserted": "bool", + }, + reply: Result( + ok: "()", + err: CLike("FrontIOError"), + ), + ), + + "phy_read": ( + args: { + "phy": "u8", + "reg": "u8", + }, + reply: Result( + ok: "u16", + err: CLike("FrontIOError"), + ), + ), + + "phy_write": ( + args: { + "phy": "u8", + "reg": "u8", + "value": "u16", + }, + reply: Result( + ok: "()", + err: CLike("FrontIOError"), + ), + ), + + "leds_assert_reset": ( + args: {}, + reply: Result( + ok: "()", + err: CLike("FrontIOError"), + ), + ), + + "leds_deassert_reset": ( + args: {}, + reply: Result( + ok: "()", + err: CLike("FrontIOError"), + ), + ), + + "leds_enable": ( + args: {}, + reply: Result( + ok: "()", + err: CLike("FrontIOError"), + ), + ), + + "leds_disable": ( + args: {}, + reply: Result( + ok: "()", + err: CLike("FrontIOError"), + ), + ), + + "led_set_state": ( + args: { + "mask": "LogicalPortMask", + "state": "LedState", + }, + reply: Simple("()"), + idempotent: true, + encoding: Hubpack, + ), + + "led_get_state": ( + args: { + "mask": "LogicalPort", + }, + reply: Simple("LedState"), + idempotent: true, + encoding: Hubpack, + ), + + "led_set_system_on": ( + doc: "Turn on the System LED.", + reply: Simple("()"), + idempotent: true, + ), + + "led_set_system_off": ( + doc: "Turn off the System LED.", + reply: Simple("()"), + idempotent: true, + ), + + "led_set_system_blink": ( + doc: "Blink the System LED.", + reply: Simple("()"), + idempotent: true, + ), + + "led_get_system_state": ( + args: {}, + reply: Simple("LedState"), + idempotent: true, + encoding: Hubpack, + ), + + "transceivers_status": ( + args: {}, + reply: Simple("TransceiverStatus"), + idempotent: true, + ), + + "transceivers_enable_power": ( + args: { + "mask": "LogicalPortMask", + }, + reply: Simple("ModuleResultNoFailure"), + idempotent: true, + ), + + "transceivers_disable_power": ( + args: { + "mask": "LogicalPortMask", + }, + reply: Simple("ModuleResultNoFailure"), + idempotent: true, + ), + + "transceivers_clear_power_fault": ( + args: { + "mask": "LogicalPortMask", + }, + reply: Simple("ModuleResultNoFailure"), + idempotent: true, + ), + + "transceivers_assert_reset": ( + args: { + "mask": "LogicalPortMask", + }, + reply: Simple("ModuleResultNoFailure"), + idempotent: true, + ), + + "transceivers_deassert_reset": ( + args: { + "mask": "LogicalPortMask", + }, + reply: Simple("ModuleResultNoFailure"), + idempotent: true, + ), + + "transceivers_assert_lpmode": ( + args: { + "mask": "LogicalPortMask", + }, + reply: Simple("ModuleResultNoFailure"), + idempotent: true, + ), + + "transceivers_deassert_lpmode": ( + args: { + "mask": "LogicalPortMask", + }, + reply: Simple("ModuleResultNoFailure"), + idempotent: true, + ), + + "transceivers_setup_i2c_read": ( + args: { + "reg": "u8", + "num_bytes": "u8", + "mask": "LogicalPortMask", + }, + reply: Result( + ok: "ModuleResultNoFailure", + err: CLike("FrontIOError"), + ), + ), + + "transceivers_setup_i2c_write": ( + args: { + "reg": "u8", + "num_bytes": "u8", + "mask": "LogicalPortMask", + }, + reply: Result( + ok: "ModuleResultNoFailure", + err: CLike("FrontIOError"), + ), + ), + + "transceivers_get_i2c_status_and_read_buffer": ( + args: { + "port": "LogicalPort", + }, + leases: { + "dest": (type: "[u8]", write: true), + }, + reply: Result( + ok: "PortI2CStatus", + err: CLike("FrontIOError"), + ), + encoding: Hubpack, + ), + + "transceivers_set_i2c_write_buffer": ( + args: { + "mask": "LogicalPortMask", + }, + leases: { + "data": (type: "[u8]", read: true), + }, + reply: Result( + ok: "ModuleResultNoFailure", + err: CLike("FrontIOError"), + ), + encoding: Hubpack, + ), + + "transceivers_wait_and_check_i2c": ( + args: { + "mask": "LogicalPortMask", + }, + reply: Simple("ModuleResult"), + idempotent: true, + encoding: Hubpack, + ), + }, +) diff --git a/idl/sidecar-seq.idol b/idl/sidecar-seq.idol index 2eeaed53d2..88cf73de3c 100644 --- a/idl/sidecar-seq.idol +++ b/idl/sidecar-seq.idol @@ -151,18 +151,6 @@ Interface( ), ), - "front_io_board_present": ( - args: {}, - reply: Simple("bool"), - idempotent: true, - ), - "front_io_board_ready": ( - args: {}, - reply: Result( - ok: "bool", - err: CLike("SeqError"), - ), - ), "reset_front_io_phy": ( args: {}, reply: Result( @@ -170,15 +158,6 @@ Interface( err: CLike("SeqError"), ), ), - "set_front_io_phy_osc_state": ( - args: { - "good": "bool", - }, - reply: Result( - ok: "()", - err: CLike("SeqError"), - ), - ), "tofino_debug_port_state": ( doc: "Return the state of the Tofino debug port", diff --git a/idl/transceivers.idol b/idl/transceivers.idol index 0497e5aad0..530bfbb229 100644 --- a/idl/transceivers.idol +++ b/idl/transceivers.idol @@ -1,38 +1,16 @@ -// Transceivers API - +// Transceivers task API +// +// Right now, this doesn't actually do anything, but it lets us use the idol +// runtime for notifications / dispatch. Interface( name: "Transceivers", ops: { - "get_module_status": ( - doc: "Collect the status of each modules control and status signals", - reply: Result( - ok: "ModuleStatus", - err: CLike("TransceiversError"), - ), - ), - - "set_system_led_on": ( - doc: "Turn on the System LED.", - reply: Result( - ok: "()", - err: CLike("TransceiversError"), - ), - ), - - "set_system_led_off": ( - doc: "Turn off the System LED.", - reply: Result( - ok: "()", - err: CLike("TransceiversError"), - ), - ), - - "set_system_led_blink": ( - doc: "Blink the System LED.", - reply: Result( - ok: "()", - err: CLike("TransceiversError"), - ), + "ping": ( + doc: "blocks until the server is running", + args: {}, + reply: Simple("()"), + idempotent: true, ), - } + }, ) + diff --git a/task/control-plane-agent/Cargo.toml b/task/control-plane-agent/Cargo.toml index 519193ceac..948c85c1bd 100644 --- a/task/control-plane-agent/Cargo.toml +++ b/task/control-plane-agent/Cargo.toml @@ -25,6 +25,7 @@ drv-caboose = { path = "../../drv/caboose" } drv-caboose-pos = { path = "../../drv/caboose-pos" } drv-hf-api = { path = "../../drv/hf-api", optional = true } drv-cpu-seq-api = { path = "../../drv/cpu-seq-api", optional = true } +drv-front-io-api = { path = "../../drv/front-io-api", optional = true } drv-ignition-api = { path = "../../drv/ignition-api", optional = true } drv-lpc55-update-api = { path = "../../drv/lpc55-update-api" } drv-monorail-api = { path = "../../drv/monorail-api", optional = true } @@ -32,7 +33,6 @@ drv-sidecar-seq-api = { path = "../../drv/sidecar-seq-api", optional = true } drv-sprot-api = { path = "../../drv/sprot-api" } drv-stm32h7-update-api = { path = "../../drv/stm32h7-update-api" } drv-stm32h7-usart = { path = "../../drv/stm32h7-usart", features = ["h753"], optional = true } -drv-transceivers-api = { path = "../../drv/transceivers-api", optional = true } drv-update-api = { path = "../../drv/update-api" } drv-user-leds-api = { path = "../../drv/user-leds-api", optional = true } drv-rng-api = { path = "../../drv/rng-api", optional = true } @@ -64,7 +64,7 @@ no-ipc-counters = ["idol/no-counters"] compute-sled = ["drv-hf-api", "drv-cpu-seq-api", "drv-stm32h7-usart", "drv-user-leds-api"] gimlet = ["compute-sled"] cosmo = ["compute-sled"] -sidecar = ["drv-sidecar-seq-api", "drv-monorail-api", "drv-ignition-api", "drv-transceivers-api", "p256", "sha2", "drv-rng-api"] +sidecar = ["drv-sidecar-seq-api", "drv-monorail-api", "drv-ignition-api", "drv-front-io-api", "p256", "sha2", "drv-rng-api"] psc = ["drv-user-leds-api"] vpd = ["task-vpd-api"] vlan = ["task-net-api/vlan"] diff --git a/task/control-plane-agent/src/mgs_sidecar.rs b/task/control-plane-agent/src/mgs_sidecar.rs index 4a5e14ed64..1aa74716d3 100644 --- a/task/control-plane-agent/src/mgs_sidecar.rs +++ b/task/control-plane-agent/src/mgs_sidecar.rs @@ -6,10 +6,10 @@ use crate::{ mgs_common::MgsCommon, update::rot::RotUpdate, update::sp::SpUpdate, update::ComponentUpdater, usize_max, CriticalEvent, Log, MgsMessage, }; +use drv_front_io_api::FrontIO; use drv_ignition_api::IgnitionError; use drv_monorail_api::{Monorail, MonorailError}; use drv_sidecar_seq_api::Sequencer; -use drv_transceivers_api::Transceivers; use enum_map::EnumMap; use gateway_messages::sp_impl::{ BoundsChecked, DeviceDescription, Sender, SpHandler, @@ -44,8 +44,8 @@ use ignition_handler::IgnitionController; userlib::task_slot!(SIDECAR_SEQ, sequencer); userlib::task_slot!(MONORAIL, monorail); -userlib::task_slot!(TRANSCEIVERS, transceivers); userlib::task_slot!(RNG, rng_driver); +userlib::task_slot!(FRONT_IO, front_io); #[allow(dead_code)] // Not all cases are used by all variants #[derive(Clone, Copy, PartialEq, ringbuf::Count)] @@ -131,7 +131,7 @@ pub(crate) struct MgsHandler { common: MgsCommon, sequencer: Sequencer, monorail: Monorail, - transceivers: Transceivers, + front_io: FrontIO, ignition: IgnitionController, last_challenge: Option<(UnlockChallenge, u64)>, @@ -147,7 +147,7 @@ impl MgsHandler { common: MgsCommon::claim_static_resources(base_mac_address), sequencer: Sequencer::from(SIDECAR_SEQ.get_task_id()), monorail: Monorail::from(MONORAIL.get_task_id()), - transceivers: Transceivers::from(TRANSCEIVERS.get_task_id()), + front_io: FrontIO::from(FRONT_IO.get_task_id()), ignition: IgnitionController::new(), last_challenge: None, @@ -654,16 +654,15 @@ impl SpHandler for MgsHandler { use gateway_messages::LedComponentAction; match action { LedComponentAction::TurnOn => { - self.transceivers.set_system_led_on() + self.front_io.led_set_system_on(); } LedComponentAction::Blink => { - self.transceivers.set_system_led_blink() + self.front_io.led_set_system_blink(); } LedComponentAction::TurnOff => { - self.transceivers.set_system_led_off() + self.front_io.led_set_system_off(); } } - .unwrap(); Ok(ComponentActionResponse::Ack) } (SpComponent::MONORAIL, ComponentAction::Monorail(action)) => { diff --git a/task/monorail-server/Cargo.toml b/task/monorail-server/Cargo.toml index bcf5a74c1e..5d2033e85f 100644 --- a/task/monorail-server/Cargo.toml +++ b/task/monorail-server/Cargo.toml @@ -5,9 +5,9 @@ edition = "2021" [dependencies] drv-medusa-seq-api = { path = "../../drv/medusa-seq-api", optional = true } -drv-monorail-api = { path = "../../drv/monorail-api" } -drv-sidecar-mainboard-controller = { path = "../../drv/sidecar-mainboard-controller" } -drv-sidecar-front-io = { path = "../../drv/sidecar-front-io", features = ["phy_smi"], optional = true } +drv-front-io-api = { path = "../../drv/front-io-api", optional = true } +drv-monorail-api = { path = "../../drv/monorail-api" } +drv-sidecar-mainboard-controller = { path = "../../drv/sidecar-mainboard-controller" } drv-sidecar-seq-api = { path = "../../drv/sidecar-seq-api", optional = true } drv-spi-api = { path = "../../drv/spi-api" } drv-stm32h7-spi-server-core = { path = "../../drv/stm32h7-spi-server-core", optional = true } @@ -31,9 +31,9 @@ zerocopy-derive.workspace = true [features] leds = ["drv-user-leds-api"] -medusa = ["drv-medusa-seq-api", "drv-sidecar-front-io"] +medusa = ["drv-medusa-seq-api", "drv-front-io-api"] mgmt = ["task-net-api"] -sidecar = ["drv-sidecar-seq-api", "drv-sidecar-front-io"] +sidecar = ["drv-sidecar-seq-api", "drv-front-io-api"] minibar = [] vlan = ["task-net-api?/vlan"] no-ipc-counters = ["idol/no-counters"] diff --git a/task/monorail-server/src/bsp/medusa_a.rs b/task/monorail-server/src/bsp/medusa_a.rs index fd59ed28a6..e183ea99c6 100644 --- a/task/monorail-server/src/bsp/medusa_a.rs +++ b/task/monorail-server/src/bsp/medusa_a.rs @@ -2,9 +2,9 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at https://mozilla.org/MPL/2.0/. +use drv_front_io_api::phy_smi::PhySmi; use drv_medusa_seq_api::Sequencer; use drv_monorail_api::MonorailError; -use drv_sidecar_front_io::phy_smi::PhySmi; use idol_runtime::{ClientError, RequestError}; use ringbuf::*; use userlib::{task_slot, UnwrapLite}; diff --git a/task/monorail-server/src/bsp/sidecar_bcd.rs b/task/monorail-server/src/bsp/sidecar_bcd.rs index 8089bf6042..1b92428e9f 100644 --- a/task/monorail-server/src/bsp/sidecar_bcd.rs +++ b/task/monorail-server/src/bsp/sidecar_bcd.rs @@ -2,8 +2,8 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at https://mozilla.org/MPL/2.0/. +use drv_front_io_api::FrontIO; use drv_monorail_api::MonorailError; -use drv_sidecar_front_io::phy_smi::PhySmi; use drv_sidecar_seq_api::Sequencer; use idol_runtime::RequestError; use ringbuf::*; @@ -15,7 +15,7 @@ use vsc7448_pac::{DEVCPU_GCB, HSIO, VAUI0, VAUI1}; use vsc85xx::{vsc8504::Vsc8504, vsc8562::Vsc8562Phy, PhyRw}; task_slot!(SEQ, seq); -task_slot!(FRONT_IO, ecp5_front_io); +task_slot!(FRONT_IO, front_io); /// Interval at which `Bsp::wake()` is called by the main loop pub const WAKE_INTERVAL: Option = Some(500); @@ -59,6 +59,9 @@ pub struct Bsp<'a, R> { /// PHY for the on-board PHY ("PHY4") vsc8504: Vsc8504, + /// handle for the front-io task + front_io: FrontIO, + /// RPC handle for the front IO board's PHY, which is a VSC8562. This is /// used for PHY control via a Rube Goldberg machine of /// Hubris RPC -> SPI -> FPGA -> MDIO -> PHY @@ -167,20 +170,21 @@ pub fn preinit() { impl<'a, R: Vsc7448Rw> Bsp<'a, R> { /// Constructs and initializes a new BSP handle pub fn new(vsc7448: &'a Vsc7448<'a, R>) -> Result { - let seq = Sequencer::from(SEQ.get_task_id()); - let has_front_io = seq.front_io_board_present(); + let front_io = FrontIO::from(FRONT_IO.get_task_id()); + let has_front_io = front_io.board_present(); let mut out = Bsp { vsc7448, vsc8504: Vsc8504::empty(), + front_io_speed: [Speed::Speed1G; 2], + link_down_at: None, + front_io, vsc8562: if has_front_io { Some(PhySmi::new(FRONT_IO.get_task_id())) } else { None }, - front_io_speed: [Speed::Speed1G; 2], - link_down_at: None, vlan_mode: VLanMode::Locked, - seq, + seq: Sequencer::from(SEQ.get_task_id()), }; out.reinit()?; @@ -261,11 +265,11 @@ impl<'a, R: Vsc7448Rw> Bsp<'a, R> { osc_good = self.is_front_io_link_good()?; - // Notify the sequencer about the state of the oscillator. If the + // Notify the front IO about the state of the oscillator. If the // oscillator is good any future resets of the PHY do not require a // full power cycle of the front IO board. - self.seq - .set_front_io_phy_osc_state(osc_good) + self.front_io + .phy_set_osc_state(osc_good) .map_err(|e| VscError::ProxyError(e.into()))?; if !osc_good { @@ -428,8 +432,8 @@ impl<'a, R: Vsc7448Rw> Bsp<'a, R> { let mut v = Vsc8562Phy { phy: &mut phy }; v.init_qsgmii()?; } - phy_rw - .set_coma_mode(false) + self.front_io + .phy_set_coma_mode(false) .map_err(|e| VscError::ProxyError(e.into()))?; } @@ -638,3 +642,31 @@ impl<'a, R: Vsc7448Rw> PhyRw for GenericPhyRw<'a, R> { } } } + +pub struct PhySmi { + server: FrontIO, +} + +impl PhySmi { + pub fn new(front_io_task: userlib::TaskId) -> Self { + Self { + server: FrontIO::from(front_io_task), + } + } +} + +impl PhyRw for PhySmi { + #[inline(always)] + fn read_raw(&self, phy: u8, reg: u8) -> Result { + self.server + .phy_read(phy, reg) + .map_err(|e| VscError::ProxyError(e.into())) + } + + #[inline(always)] + fn write_raw(&self, phy: u8, reg: u8, value: u16) -> Result<(), VscError> { + self.server + .phy_write(phy, reg, value) + .map_err(|e| VscError::ProxyError(e.into())) + } +} diff --git a/task/thermal/Cargo.toml b/task/thermal/Cargo.toml index 4e50031336..fa72fa3a95 100644 --- a/task/thermal/Cargo.toml +++ b/task/thermal/Cargo.toml @@ -14,6 +14,7 @@ zerocopy.workspace = true zerocopy-derive.workspace = true drv-cpu-seq-api = { path = "../../drv/cpu-seq-api", optional = true } +drv-front-io-api = { path = "../../drv/front-io-api", optional = true } drv-sidecar-seq-api = { path = "../../drv/sidecar-seq-api", optional = true } drv-transceivers-api = { path = "../../drv/transceivers-api", optional = true } userlib = { path = "../../sys/userlib", features = ["panic-messages"] } @@ -38,7 +39,7 @@ build-util = { path = "../../build/util" } [features] gimlet = ["drv-cpu-seq-api", "h753"] cosmo = ["drv-cpu-seq-api", "h753"] -sidecar = ["drv-sidecar-seq-api", "drv-transceivers-api", "h753"] +sidecar = ["drv-sidecar-seq-api", "drv-front-io-api", "drv-transceivers-api", "h753"] medusa = ["h753", "drv-transceivers-api"] grapefruit = ["h753"] minibar = ["h753"] diff --git a/task/thermal/src/bsp/sidecar_bcd.rs b/task/thermal/src/bsp/sidecar_bcd.rs index 68c4b8688c..6c209e0622 100644 --- a/task/thermal/src/bsp/sidecar_bcd.rs +++ b/task/thermal/src/bsp/sidecar_bcd.rs @@ -34,7 +34,7 @@ pub const NUM_TEMPERATURE_INPUTS: usize = // External temperature inputs, which are provided to the task over IPC // In practice, these are our transceivers. pub const NUM_DYNAMIC_TEMPERATURE_INPUTS: usize = - drv_transceivers_api::NUM_PORTS as usize; + drv_front_io_api::transceivers::NUM_PORTS as usize; // Number of individual fans pub const NUM_FANS: usize = sensors::NUM_MAX31790_SPEED_SENSORS;