Skip to content

Commit ddfb878

Browse files
committed
bmr491: mitigation for voltage droop
This adds logic to apply the BMR491 input filter defect mitigation described in #2321. Note that the routine must still be called from somewhere to be useful!
1 parent 0c89f09 commit ddfb878

9 files changed

Lines changed: 556 additions & 40 deletions

File tree

Cargo.lock

Lines changed: 7 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

drv/cosmo-seq-server/Cargo.toml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,9 @@ gnarle = { path = "../../lib/gnarle" }
2020
ringbuf = { path = "../../lib/ringbuf" }
2121
userlib = { path = "../../sys/userlib", features = ["panic-messages"] }
2222
task-jefe-api = { path = "../../task/jefe-api" }
23-
task-packrat-api = { path = "../../task/packrat-api", features = ["serde"] }
23+
task-packrat-api = { path = "../../task/packrat-api", features = ["serde", "microcbor"] }
24+
microcbor = { path = "../../lib/microcbor" }
25+
fixedstr = { path = "../../lib/fixedstr", features = ["microcbor"] }
2426
static-cell = { path = "../../lib/static-cell" }
2527

2628
cfg-if = { workspace = true }

drv/cosmo-seq-server/src/main.rs

Lines changed: 108 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ use drv_packrat_vpd_loader::{read_vpd_and_load_packrat, Packrat};
1515
use drv_spartan7_loader_api::Spartan7Loader;
1616
use drv_spi_api::{SpiDevice, SpiServer};
1717
use drv_stm32xx_sys_api::{self as sys_api, Sys};
18+
use fixedstr::FixedStr;
1819
use idol_runtime::{NotificationHandler, RequestError};
1920
use task_jefe_api::Jefe;
2021
use userlib::{
@@ -102,6 +103,9 @@ enum Trace {
102103
},
103104
UnexpectedInterrupt,
104105
CPUPresent(bool),
106+
EreportSent(usize),
107+
EreportLost(usize, task_packrat_api::EreportWriteError),
108+
EreportTooBig,
105109
}
106110
counted_ringbuf!(Trace, 128, Trace::None);
107111

@@ -160,13 +164,70 @@ struct StateMachineStates {
160164
nic: Result<fmc_sequencer::NicSm, u8>,
161165
}
162166

167+
const EREPORT_BUF_LEN: usize = {
168+
let n = microcbor::max_cbor_len_for!(
169+
task_packrat_api::Ereport<EreportClass, EreportKind>,
170+
);
171+
// someday, we will have const max.
172+
if n < 256 {
173+
256
174+
} else {
175+
n
176+
}
177+
};
178+
163179
#[export_name = "main"]
164180
fn main() -> ! {
165181
// Populate packrat with our mac address and identity.
166182
let packrat = Packrat::from(PACKRAT.get_task_id());
167183
read_vpd_and_load_packrat(&packrat, I2C.get_task_id());
168184

169-
match init(packrat) {
185+
let ereport_buf = {
186+
use static_cell::ClaimOnceCell;
187+
static EREPORT_BUF: ClaimOnceCell<[u8; EREPORT_BUF_LEN]> =
188+
ClaimOnceCell::new([0; EREPORT_BUF_LEN]);
189+
EREPORT_BUF.claim()
190+
};
191+
192+
//
193+
// Apply the configuration mitigation on the BMR491, if required. This
194+
// is an external device access and may fail. We'll attempt it thrice
195+
// and then allow boot to continue.
196+
//
197+
{
198+
use drv_i2c_devices::bmr491::{Bmr491, ExternalInputVoltageProtection};
199+
200+
let dev = i2c_config::devices::bmr491_u80(I2C.get_task_id());
201+
let driver = Bmr491::new(&dev, 0);
202+
203+
// Cosmo provides external undervoltage protection that kicks in at a
204+
// lower voltage than we'd like to tolerate, so, request additional
205+
// protection from the mitigation code.
206+
let protection = ExternalInputVoltageProtection::CutoffBelow40V;
207+
208+
let (failures, last_cause, succeeded) =
209+
match driver.apply_mitigation_for_rma2402311(protection) {
210+
Ok(r) => (r.failures, r.last_failure, true),
211+
Err(e) => (e.retries, Some(e.last_cause), false),
212+
};
213+
214+
if let Some(last_cause) = last_cause {
215+
// Report the failure even if we eventually succeeded.
216+
try_send_ereport(
217+
&packrat,
218+
&mut ereport_buf[..],
219+
EreportClass::Bmr491MitigationFailure,
220+
EreportKind::Bmr491MitigationFailure {
221+
refdes: FixedStr::from_str(dev.component_id()),
222+
failures,
223+
last_cause,
224+
succeeded,
225+
},
226+
);
227+
}
228+
}
229+
230+
match init(packrat, ereport_buf) {
170231
// Set up everything nicely, time to start serving incoming messages.
171232
Ok(mut server) => {
172233
// Enable the backplane PCIe clock if requested
@@ -208,7 +269,10 @@ fn main() -> ! {
208269
}
209270
}
210271

211-
fn init(packrat: Packrat) -> Result<ServerImpl, SeqError> {
272+
fn init(
273+
packrat: Packrat,
274+
ereport_buf: &'static mut [u8; EREPORT_BUF_LEN],
275+
) -> Result<ServerImpl, SeqError> {
212276
let sys = sys_api::Sys::from(SYS.get_task_id());
213277

214278
// Pull the fault line low while we're loading
@@ -299,7 +363,7 @@ fn init(packrat: Packrat) -> Result<ServerImpl, SeqError> {
299363
// Turn on the chassis LED!
300364
sys.gpio_set(SP_CHASSIS_STATUS_LED);
301365

302-
Ok(ServerImpl::new(loader, packrat))
366+
Ok(ServerImpl::new(loader, packrat, ereport_buf))
303367
}
304368

305369
/// Configures the front FPGA pins and holds it in reset
@@ -385,12 +449,27 @@ struct ServerImpl {
385449
ereport_buf: &'static mut [u8; EREPORT_BUF_LEN],
386450
}
387451

388-
const EREPORT_BUF_LEN: usize = 256;
452+
#[derive(microcbor::Encode)]
453+
pub enum EreportClass {
454+
#[cbor(rename = "hw.pwr.bmr491.mitfail")]
455+
Bmr491MitigationFailure,
456+
}
457+
458+
#[derive(microcbor::EncodeFields)]
459+
pub(crate) enum EreportKind {
460+
Bmr491MitigationFailure {
461+
refdes: FixedStr<{ crate::i2c_config::MAX_COMPONENT_ID_LEN }>,
462+
failures: u32,
463+
last_cause: drv_i2c_devices::bmr491::MitigationFailureKind,
464+
succeeded: bool,
465+
},
466+
}
389467

390468
impl ServerImpl {
391469
fn new(
392470
loader: drv_spartan7_loader_api::Spartan7Loader,
393471
packrat: Packrat,
472+
ereport_buf: &'static mut [u8; EREPORT_BUF_LEN],
394473
) -> Self {
395474
let now = sys_get_timer().now;
396475

@@ -410,13 +489,6 @@ impl ServerImpl {
410489
let jefe = Jefe::from(JEFE.get_task_id());
411490
jefe.set_state(PowerState::A2 as u32);
412491

413-
let ereport_buf = {
414-
use static_cell::ClaimOnceCell;
415-
static EREPORT_BUF: ClaimOnceCell<[u8; EREPORT_BUF_LEN]> =
416-
ClaimOnceCell::new([0; EREPORT_BUF_LEN]);
417-
EREPORT_BUF.claim()
418-
};
419-
420492
ServerImpl {
421493
state: PowerState::A2,
422494
jefe,
@@ -978,6 +1050,31 @@ impl NotificationHandler for ServerImpl {
9781050
}
9791051
}
9801052

1053+
fn try_send_ereport(
1054+
packrat: &task_packrat_api::Packrat,
1055+
ereport_buf: &mut [u8],
1056+
class: EreportClass,
1057+
report: EreportKind,
1058+
) {
1059+
let eresult = packrat.encode_ereport(
1060+
&task_packrat_api::Ereport {
1061+
class,
1062+
version: 0,
1063+
report,
1064+
},
1065+
ereport_buf,
1066+
);
1067+
match eresult {
1068+
Ok(len) => ringbuf_entry!(Trace::EreportSent(len)),
1069+
Err(task_packrat_api::EreportEncodeError::Packrat { len, err }) => {
1070+
ringbuf_entry!(Trace::EreportLost(len, err))
1071+
}
1072+
Err(task_packrat_api::EreportEncodeError::Encoder(_)) => {
1073+
ringbuf_entry!(Trace::EreportTooBig)
1074+
}
1075+
}
1076+
}
1077+
9811078
////////////////////////////////////////////////////////////////////////////////
9821079

9831080
mod idl {

drv/gimlet-seq-server/src/main.rs

Lines changed: 84 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ use idol_runtime::{NotificationHandler, RequestError};
3030
use seq_spi::{Addr, Reg};
3131
use static_assertions::const_assert;
3232
use task_jefe_api::Jefe;
33+
use task_packrat_api as packrat_api;
3334

3435
task_slot!(SYS, sys);
3536
task_slot!(SPI, spi_driver);
@@ -155,6 +156,9 @@ enum Trace {
155156
retries_remaining: u8,
156157
},
157158
StartFailed(#[count(children)] SeqError),
159+
EreportSent(usize),
160+
EreportLost(usize, packrat_api::EreportWriteError),
161+
EreportTooBig,
158162
}
159163

160164
counted_ringbuf!(Trace, 128, Trace::None);
@@ -217,6 +221,8 @@ const EREPORT_BUF_LEN: usize = microcbor::max_cbor_len_for!(
217221
pub enum EreportClass {
218222
#[cbor(rename = "hw.pwr.pmbus.alert")]
219223
PmbusAlert,
224+
#[cbor(rename = "hw.pwr.bmr491.mitfail")]
225+
Bmr491MitigationFailure,
220226
}
221227

222228
#[derive(microcbor::EncodeFields)]
@@ -229,6 +235,12 @@ pub(crate) enum EreportKind {
229235
pwr_good: Option<bool>,
230236
pmbus_status: PmbusStatus,
231237
},
238+
Bmr491MitigationFailure {
239+
refdes: FixedStr<{ crate::i2c_config::MAX_COMPONENT_ID_LEN }>,
240+
failures: u32,
241+
last_cause: drv_i2c_devices::bmr491::MitigationFailureKind,
242+
succeeded: bool,
243+
},
232244
}
233245

234246
#[derive(Copy, Clone, Default, microcbor::Encode)]
@@ -477,6 +489,53 @@ impl<S: SpiServer + Clone> ServerImpl<S> {
477489
hl::sleep_for(1);
478490
}
479491

492+
let ereport_buf = {
493+
use static_cell::ClaimOnceCell;
494+
static EREPORT_BUF: ClaimOnceCell<[u8; EREPORT_BUF_LEN]> =
495+
ClaimOnceCell::new([0; EREPORT_BUF_LEN]);
496+
EREPORT_BUF.claim()
497+
};
498+
let packrat = Packrat::from(PACKRAT.get_task_id());
499+
500+
//
501+
// Apply the configuration mitigation on the BMR491, if required. This
502+
// is an external device access and may fail. We'll attempt it thrice
503+
// and then allow boot to continue.
504+
//
505+
{
506+
use drv_i2c_devices::bmr491::{
507+
Bmr491, ExternalInputVoltageProtection,
508+
};
509+
510+
let dev = i2c_config::devices::bmr491_u431(I2C.get_task_id());
511+
let driver = Bmr491::new(&dev, 0);
512+
513+
// Gimlet provides external undervoltage protection that is better
514+
// than what we'd get from the 491, so we rely on that.
515+
let protection = ExternalInputVoltageProtection::CutoffAt40V;
516+
517+
let (failures, last_cause, succeeded) =
518+
match driver.apply_mitigation_for_rma2402311(protection) {
519+
Ok(r) => (r.failures, r.last_failure, true),
520+
Err(e) => (e.retries, Some(e.last_cause), false),
521+
};
522+
523+
if let Some(last_cause) = last_cause {
524+
// Report the failure even if we eventually succeeded.
525+
try_send_ereport(
526+
&packrat,
527+
&mut ereport_buf[..],
528+
EreportClass::Bmr491MitigationFailure,
529+
EreportKind::Bmr491MitigationFailure {
530+
refdes: FixedStr::from_str(dev.component_id()),
531+
failures,
532+
last_cause,
533+
succeeded,
534+
},
535+
);
536+
}
537+
}
538+
480539
//
481540
// If our clock generator is configured to load from external EEPROM,
482541
// we need to wait for up to 150 ms here (!).
@@ -497,7 +556,6 @@ impl<S: SpiServer + Clone> ServerImpl<S> {
497556
})?;
498557

499558
// Populate packrat with our mac address and identity.
500-
let packrat = Packrat::from(PACKRAT.get_task_id());
501559
read_vpd_and_load_packrat(&packrat, I2C.get_task_id());
502560

503561
jefe.set_state(PowerState::A2 as u32);
@@ -520,13 +578,6 @@ impl<S: SpiServer + Clone> ServerImpl<S> {
520578

521579
let (device, rail) = i2c_config::pmbus::vdd_vcore(I2C.get_task_id());
522580

523-
let ereport_buf = {
524-
use static_cell::ClaimOnceCell;
525-
static EREPORT_BUF: ClaimOnceCell<[u8; EREPORT_BUF_LEN]> =
526-
ClaimOnceCell::new([0; EREPORT_BUF_LEN]);
527-
EREPORT_BUF.claim()
528-
};
529-
530581
let mut server = Self {
531582
state: PowerState::A2,
532583
sys: sys.clone(),
@@ -1549,6 +1600,31 @@ cfg_if::cfg_if! {
15491600
}
15501601
}
15511602

1603+
fn try_send_ereport(
1604+
packrat: &packrat_api::Packrat,
1605+
ereport_buf: &mut [u8],
1606+
class: EreportClass,
1607+
report: EreportKind,
1608+
) {
1609+
let eresult = packrat.encode_ereport(
1610+
&packrat_api::Ereport {
1611+
class,
1612+
version: 0,
1613+
report,
1614+
},
1615+
ereport_buf,
1616+
);
1617+
match eresult {
1618+
Ok(len) => ringbuf_entry!(Trace::EreportSent(len)),
1619+
Err(task_packrat_api::EreportEncodeError::Packrat { len, err }) => {
1620+
ringbuf_entry!(Trace::EreportLost(len, err))
1621+
}
1622+
Err(task_packrat_api::EreportEncodeError::Encoder(_)) => {
1623+
ringbuf_entry!(Trace::EreportTooBig)
1624+
}
1625+
}
1626+
}
1627+
15521628
mod idl {
15531629
use super::StateChangeReason;
15541630

0 commit comments

Comments
 (0)