Skip to content

Commit a48fa97

Browse files
Add reset and thermtrip handling to cosmo-seq (#2160)
Fixes oxidecomputer/stlouis#806 --------- Co-authored-by: Nathanael Huffman <[email protected]>
1 parent 3211ce0 commit a48fa97

File tree

4 files changed

+103
-54
lines changed

4 files changed

+103
-54
lines changed

build/fpga-regmap/src/lib.rs

Lines changed: 33 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -433,9 +433,9 @@ pub fn build_peripheral(
433433
};
434434
assert_eq!(*regwidth, 32, "only 32-bit registers are supported");
435435
let mut struct_fns = vec![];
436-
let mut debug_values = vec![];
437-
let mut debug_names = vec![];
438-
let mut debug_types = vec![];
436+
let mut view_values = vec![];
437+
let mut view_names = vec![];
438+
let mut view_types = vec![];
439439
let mut encode_types = vec![];
440440
for c in children {
441441
let Node::Field {
@@ -459,6 +459,7 @@ pub fn build_peripheral(
459459
if lsb == msb {
460460
if sw_access.is_write() {
461461
struct_fns.push(quote! {
462+
#[inline]
462463
#[doc = #desc]
463464
pub fn #setter(&self, t: bool) {
464465
let mut d = self.get_raw();
@@ -473,17 +474,18 @@ pub fn build_peripheral(
473474
}
474475
if sw_access.is_read() {
475476
struct_fns.push(quote! {
477+
#[inline]
476478
#[doc = #desc]
477479
pub fn #getter(&self) -> bool {
478480
let d = self.get_raw();
479481
(d & (1 << #msb)) != 0
480482
}
481483
});
482-
debug_values.push(quote! {
484+
view_values.push(quote! {
483485
let #getter = (d & (1 << #msb)) != 0;
484486
});
485-
debug_names.push(quote! { #getter });
486-
debug_types.push(quote! { pub #getter: bool });
487+
view_names.push(quote! { #getter });
488+
view_types.push(quote! { pub #getter: bool });
487489
}
488490
} else if let Some(encode) = encode {
489491
let ty: syn::Ident =
@@ -534,6 +536,7 @@ pub fn build_peripheral(
534536
});
535537
if sw_access.is_write() {
536538
struct_fns.push(quote! {
539+
#[inline]
537540
#[doc = #desc]
538541
pub fn #setter(&self, t: #ty) {
539542
let mut d = self.get_raw();
@@ -545,19 +548,20 @@ pub fn build_peripheral(
545548
}
546549
if sw_access.is_read() {
547550
struct_fns.push(quote! {
551+
#[inline]
548552
#[doc = #desc]
549553
pub fn #getter(&self) -> Result<#ty, #raw_ty> {
550554
let d = self.get_raw();
551555
let t = ((d >> #lsb) & #mask) as #raw_ty;
552556
#ty::try_from(t)
553557
}
554558
});
555-
debug_values.push(quote! {
559+
view_values.push(quote! {
556560
let t = ((d >> #lsb) & #mask) as #raw_ty;
557561
let #getter = #ty::try_from(t);
558562
});
559-
debug_names.push(quote! { #getter });
560-
debug_types
563+
view_names.push(quote! { #getter });
564+
view_types
561565
.push(quote! { pub #getter: Result<#ty, #raw_ty> });
562566
}
563567
} else {
@@ -573,6 +577,7 @@ pub fn build_peripheral(
573577
let ty: syn::Ident = syn::parse_str(ty).unwrap();
574578
if sw_access.is_write() {
575579
struct_fns.push(quote! {
580+
#[inline]
576581
#[doc = #desc]
577582
pub fn #setter(&self, t: #ty) {
578583
let mut d = self.get_raw();
@@ -584,17 +589,18 @@ pub fn build_peripheral(
584589
}
585590
if sw_access.is_read() {
586591
struct_fns.push(quote! {
592+
#[inline]
587593
#[doc = #desc]
588594
pub fn #getter(&self) -> #ty {
589595
let d = self.get_raw();
590596
((d >> #lsb) & #mask) as #ty
591597
}
592598
});
593-
debug_values.push(quote! {
599+
view_values.push(quote! {
594600
let #getter = ((d >> #lsb) & #mask) as #ty;
595601
});
596-
debug_names.push(quote! { #getter });
597-
debug_types.push(quote! { pub #getter: #ty });
602+
view_names.push(quote! { #getter });
603+
view_types.push(quote! { pub #getter: #ty });
598604
}
599605
}
600606
}
@@ -603,8 +609,8 @@ pub fn build_peripheral(
603609
let struct_name: syn::Ident = syn::parse_str(&inst_name).unwrap();
604610
let handle_name: syn::Ident =
605611
syn::parse_str(&format!("{}Handle", inst_name)).unwrap();
606-
let debug_name: syn::Ident =
607-
syn::parse_str(&format!("{}Debug", inst_name)).unwrap();
612+
let view_name: syn::Ident =
613+
syn::parse_str(&format!("{}View", inst_name)).unwrap();
608614
let reg_addr = base_addr
609615
+ u32::try_from(*periph_offset).unwrap()
610616
+ u32::try_from(*addr_offset).unwrap();
@@ -626,12 +632,17 @@ pub fn build_peripheral(
626632
Self::ADDR.write_volatile(v)
627633
}
628634
}
635+
#[inline]
629636
pub fn modify<F: Fn(&mut #handle_name)>(&self, f: F) {
630637
let mut v =
631638
#handle_name(core::cell::Cell::new(self.get_raw()));
632639
f(&mut v);
633640
self.set_raw(v.0.get());
634641
}
642+
#[inline]
643+
pub fn view(&self) -> #view_name {
644+
#view_name::from(self)
645+
}
635646

636647
#(#struct_fns)*
637648
}
@@ -650,17 +661,18 @@ pub fn build_peripheral(
650661

651662
#[derive(Copy, Clone, Eq, PartialEq)]
652663
#[allow(dead_code, clippy::useless_conversion, clippy::unnecessary_cast)]
653-
pub struct #debug_name {
654-
#(#debug_types),*
664+
pub struct #view_name {
665+
#(#view_types),*
655666
}
656667
#[allow(dead_code, clippy::useless_conversion, clippy::unnecessary_cast)]
657-
impl<'a> From<&'a #struct_name> for #debug_name {
658-
fn from(s: &'a #struct_name) -> #debug_name {
668+
impl<'a> From<&'a #struct_name> for #view_name {
669+
#[inline]
670+
fn from(s: &'a #struct_name) -> #view_name {
659671
#[allow(unused_variables)]
660672
let d = s.get_raw();
661-
#(#debug_values)*
662-
#debug_name {
663-
#(#debug_names),*
673+
#(#view_values)*
674+
#view_name {
675+
#(#view_names),*
664676
}
665677
}
666678
}

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

Lines changed: 68 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -47,17 +47,17 @@ enum Trace {
4747
Programmed,
4848

4949
Startup {
50-
early_power_rdbks: fmc_periph::EarlyPowerRdbksDebug,
50+
early_power_rdbks: fmc_periph::EarlyPowerRdbksView,
5151
},
5252
RegStateValues {
53-
seq_api_status: fmc_periph::SeqApiStatusDebug,
54-
seq_raw_status: fmc_periph::SeqRawStatusDebug,
55-
nic_api_status: fmc_periph::NicApiStatusDebug,
56-
nic_raw_status: fmc_periph::NicRawStatusDebug,
53+
seq_api_status: fmc_periph::SeqApiStatusView,
54+
seq_raw_status: fmc_periph::SeqRawStatusView,
55+
nic_api_status: fmc_periph::NicApiStatusView,
56+
nic_raw_status: fmc_periph::NicRawStatusView,
5757
},
5858
RegPgValues {
59-
rail_pgs: fmc_periph::RailPgsDebug,
60-
rail_pgs_max_hold: fmc_periph::RailPgsMaxHoldDebug,
59+
rail_pgs: fmc_periph::RailPgsView,
60+
rail_pgs_max_hold: fmc_periph::RailPgsMaxHoldView,
6161
},
6262
SetState {
6363
prev: Option<PowerState>,
@@ -80,6 +80,11 @@ enum Trace {
8080
sp5r3: bool,
8181
sp5r4: bool,
8282
},
83+
ResetCounts {
84+
rstn: u8,
85+
pwrokn: u8,
86+
},
87+
Thermtrip,
8388
}
8489
counted_ringbuf!(Trace, 128, Trace::None);
8590

@@ -421,9 +426,21 @@ impl ServerImpl {
421426
use fmc_periph::A0Sm;
422427
match (self.get_state_impl(), state) {
423428
(PowerState::A2, PowerState::A0) => {
429+
// Reset edge counters in the sequencer
430+
self.seq.amd_reset_fedges.set_counts(0);
431+
self.seq.amd_pwrok_fedges.set_counts(0);
432+
433+
// Reset edge interrupts flags
434+
self.seq.ifr.modify(|h| {
435+
h.set_amd_pwrok_fedge(false);
436+
h.set_amd_rstn_fedge(false);
437+
});
438+
439+
// Tell the sequencer to go to A0
424440
self.seq.power_ctrl.modify(|m| m.set_a0_en(true));
425-
let mut okay = false;
441+
426442
// Wait 2 seconds for power-up
443+
let mut okay = false;
427444
for _ in 0..200 {
428445
let state = self.log_state_registers();
429446
match state.seq {
@@ -502,7 +519,8 @@ impl ServerImpl {
502519
}
503520
(PowerState::A0, PowerState::A2)
504521
| (PowerState::A0PlusHP, PowerState::A2)
505-
| (PowerState::A0Thermtrip, PowerState::A2) => {
522+
| (PowerState::A0Thermtrip, PowerState::A2)
523+
| (PowerState::A0Reset, PowerState::A2) => {
506524
self.seq.power_ctrl.modify(|m| m.set_a0_en(false));
507525
let mut okay = false;
508526
for _ in 0..200 {
@@ -540,10 +558,15 @@ impl ServerImpl {
540558
_ => return Err(CpuSeqError::IllegalTransition),
541559
}
542560

561+
self.set_state_internal(state);
562+
Ok(Transition::Changed)
563+
}
564+
565+
/// Updates our internal `state` and the global state in `jefe`
566+
fn set_state_internal(&mut self, state: PowerState) {
543567
self.state = state;
544568
self.jefe.set_state(state as u32);
545569
self.poke_timer();
546-
Ok(Transition::Changed)
547570
}
548571

549572
/// Returns the current timer interval, in milliseconds
@@ -628,13 +651,24 @@ impl NotificationHandler for ServerImpl {
628651
let state = self.log_state_registers();
629652
use fmc_periph::{A0Sm, NicSm};
630653

631-
// Detect unexpected power-off
632-
match (self.state, state.seq) {
633-
(PowerState::A0 | PowerState::A0PlusHP, Ok(A0Sm::Done)) => (),
634-
(PowerState::A0 | PowerState::A0PlusHP, seq_state) => {
654+
// Detect when the NIC comes online
655+
// TODO: should we handle the NIC powering down while the main CPU
656+
// power remains up?
657+
if self.state == PowerState::A0 && state.nic == Ok(NicSm::Done) {
658+
self.set_state_impl(
659+
PowerState::A0PlusHP,
660+
StateChangeReason::InitialPowerOn,
661+
)
662+
.unwrap(); // this should be infallible
663+
}
664+
665+
// If Hubris thinks the system is up, do some basic checks
666+
if matches!(self.state, PowerState::A0 | PowerState::A0PlusHP) {
667+
// Detect the FPGA powering off without us
668+
if state.seq != Ok(A0Sm::Done) {
635669
ringbuf_entry!(Trace::UnexpectedPowerOff {
636670
our_state: self.state,
637-
seq_state,
671+
seq_state: state.seq,
638672
});
639673
self.log_pg_registers();
640674

@@ -645,23 +679,26 @@ impl NotificationHandler for ServerImpl {
645679
{
646680
ringbuf_entry!(Trace::PowerDownError(e))
647681
}
682+
} else {
683+
// Check that the FPGA has not logged any reset conditions from
684+
// the CPU.
685+
let ifr = self.seq.ifr.view();
686+
if ifr.thermtrip {
687+
self.seq.ifr.modify(|h| h.set_thermtrip(false));
688+
ringbuf_entry!(Trace::Thermtrip);
689+
self.set_state_internal(PowerState::A0Thermtrip)
690+
// this is a terminal state (for now)
691+
} else if ifr.amd_pwrok_fedge || ifr.amd_rstn_fedge {
692+
let rstn = self.seq.amd_reset_fedges.counts();
693+
let pwrokn = self.seq.amd_pwrok_fedges.counts();
694+
ringbuf_entry!(Trace::ResetCounts { rstn, pwrokn });
695+
// counters are cleared in the A2 -> A0 transition
696+
self.set_state_internal(PowerState::A0Reset);
697+
// host_sp_comms will be notified of this change and will
698+
// call back into this task to reboot the system (going to
699+
// A2 then back into A0)
700+
}
648701
}
649-
// TODO are there other states that we should check here?
650-
_ => (),
651-
}
652-
653-
// Detect when the NIC comes online
654-
match (self.state, state.nic) {
655-
(PowerState::A0, Ok(NicSm::Done)) => {
656-
self.set_state_impl(
657-
PowerState::A0PlusHP,
658-
StateChangeReason::InitialPowerOn,
659-
)
660-
.unwrap(); // this should be infallible
661-
}
662-
// TODO: should we handle the NIC powering down while the main CPU
663-
// power remains up?
664-
_ => (),
665702
}
666703

667704
self.poke_timer();
Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
FPGA images and collateral are generated from:
2-
[this sha](https://github.com/oxidecomputer/quartz/commit/0d094c19ea381fdf4aa8f0b0fc7e5f2a33a37506)
3-
[release](https://api.github.com/repos/oxidecomputer/quartz/releases/234047771)
2+
[this sha](https://github.com/oxidecomputer/quartz/commit/32f52f4b3f7fe5b5efbce5a56ceea43e4f5aa70c)
3+
[release](https://api.github.com/repos/oxidecomputer/quartz/releases/234584432)
12.6 KB
Binary file not shown.

0 commit comments

Comments
 (0)