Skip to content

Commit 740ffac

Browse files
authored
Merge pull request #58 from hnez/dut-pwr-inversion-only-on
dut_power: implement an error-free grace period after DUT power on
2 parents 6b17dbf + 70b9d04 commit 740ffac

File tree

2 files changed

+124
-44
lines changed

2 files changed

+124
-44
lines changed

src/digital_io/gpio/test.rs

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -15,17 +15,19 @@
1515
// with this program; if not, write to the Free Software Foundation, Inc.,
1616
// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
1717

18+
use std::cell::RefCell;
1819
use std::iter::Iterator;
1920
use std::ops::BitOr;
2021
use std::sync::atomic::{AtomicU8, Ordering};
2122
use std::thread::sleep;
2223
use std::time::Duration;
2324

2425
use anyhow::Result;
25-
use async_std::sync::{Arc, Mutex};
26-
use async_std::task::block_on;
26+
use async_std::sync::Arc;
2727

28-
static LINES: Mutex<Vec<(String, Arc<AtomicU8>)>> = Mutex::new(Vec::new());
28+
std::thread_local! {
29+
static LINES: RefCell<Vec<(String, Arc<AtomicU8>)>> = RefCell::new(Vec::new());
30+
}
2931

3032
pub struct LineHandle {
3133
name: String,
@@ -115,17 +117,15 @@ impl FindDecoy {
115117
}
116118

117119
pub fn find_line(name: &str) -> Option<FindDecoy> {
118-
let val = {
119-
let mut lines = block_on(LINES.lock());
120-
120+
let val = LINES.with_borrow_mut(|lines| {
121121
if let Some((_, v)) = lines.iter().find(|(n, _)| n == name) {
122122
v.clone()
123123
} else {
124124
let v = Arc::new(AtomicU8::new(0));
125125
lines.push((name.to_string(), v.clone()));
126126
v
127127
}
128-
};
128+
});
129129

130130
Some(FindDecoy {
131131
name: name.to_string(),

src/dut_power.rs

Lines changed: 117 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@ use prio::realtime_priority;
6363
const MAX_AGE: Duration = Duration::from_millis(300);
6464
const THREAD_INTERVAL: Duration = Duration::from_millis(100);
6565
const TASK_INTERVAL: Duration = Duration::from_millis(200);
66+
const TURN_ON_ERROR_GRACE_PERIOD: Duration = Duration::from_millis(600);
6667
const MAX_CURRENT: f32 = 5.0;
6768
const MAX_VOLTAGE: f32 = 48.0;
6869
const MIN_VOLTAGE: f32 = -1.0;
@@ -353,6 +354,12 @@ impl DutPwrThread {
353354
}
354355
};
355356

357+
// The grace period contains the number of loop iterations until
358+
// we start handling over/under voltage and overcurrent events.
359+
// This counts down to zero after turning on the output.
360+
// And is kept at TURN_ON_ERROR_GRACE_PERIOD while the output is off.
361+
let mut grace_period = TURN_ON_ERROR_GRACE_PERIOD;
362+
356363
// Run as long as there is a strong reference to `tick`.
357364
// As tick is a private member of the struct this is equivalent
358365
// to running as long as the DutPwrThread was not dropped.
@@ -413,45 +420,61 @@ impl DutPwrThread {
413420
.swap(OutputRequest::Idle as u8, Ordering::Relaxed)
414421
.into();
415422

416-
// Don't even look at the requests if there is an ongoing
417-
// overvoltage condition. Instead turn the output off and
418-
// go back to measuring.
419-
if volt > MAX_VOLTAGE {
420-
turn_off_with_reason(
421-
OutputState::OverVoltage,
422-
&pwr_line,
423-
&discharge_line,
424-
&state,
425-
);
426-
427-
continue;
428-
}
423+
// Checking for MAX_VOLTAGE, MIN_VOLTAGE, MAX_CURRENT error conditions while
424+
// the DUT power switch is off does not make a lot of sense,
425+
// considering the way we measure these values right now (behind the DUT power switch).
426+
// And what would we even do in this case? Turn the output even more off?
427+
// If we see one of these error conditions while the output is off it is
428+
// likely due to our high-impedance measurements and not due to a real error.
429+
// Ignore these kinds of errors while the output is off and for a few
430+
// THREAD_INTERVALs after turning it on.
431+
grace_period = match state.load(Ordering::Relaxed).into() {
432+
OutputState::On => grace_period.saturating_sub(THREAD_INTERVAL),
433+
OutputState::Off
434+
| OutputState::OffFloating
435+
| OutputState::Changing
436+
| OutputState::InvertedPolarity
437+
| OutputState::OverCurrent
438+
| OutputState::OverVoltage
439+
| OutputState::RealtimeViolation => TURN_ON_ERROR_GRACE_PERIOD,
440+
};
429441

430-
// Don't even look at the requests if there is an ongoin
431-
// polarity inversion. Turn off, go back to start, do not
432-
// collect $200.
433-
if volt < MIN_VOLTAGE {
434-
turn_off_with_reason(
435-
OutputState::InvertedPolarity,
436-
&pwr_line,
437-
&discharge_line,
438-
&state,
439-
);
440-
441-
continue;
442-
}
442+
if grace_period == Duration::ZERO {
443+
// At this point the output is on and has been on for
444+
// TURN_ON_ERROR_GRACE_PERIOD, so we start checking for error conditions.
445+
446+
if volt > MAX_VOLTAGE {
447+
turn_off_with_reason(
448+
OutputState::OverVoltage,
449+
&pwr_line,
450+
&discharge_line,
451+
&state,
452+
);
443453

444-
// Don't even look at the requests if there is an ongoin
445-
// overcurrent condition.
446-
if curr > MAX_CURRENT {
447-
turn_off_with_reason(
448-
OutputState::OverCurrent,
449-
&pwr_line,
450-
&discharge_line,
451-
&state,
452-
);
453-
454-
continue;
454+
continue;
455+
}
456+
457+
if volt < MIN_VOLTAGE {
458+
turn_off_with_reason(
459+
OutputState::InvertedPolarity,
460+
&pwr_line,
461+
&discharge_line,
462+
&state,
463+
);
464+
465+
continue;
466+
}
467+
468+
if curr > MAX_CURRENT {
469+
turn_off_with_reason(
470+
OutputState::OverCurrent,
471+
&pwr_line,
472+
&discharge_line,
473+
&state,
474+
);
475+
476+
continue;
477+
}
455478
}
456479

457480
// There is no ongoing fault condition, so we could e.g. turn
@@ -742,4 +765,61 @@ mod tests {
742765
assert_eq!(pwr_line.stub_get(), 1 - PWR_LINE_ASSERTED);
743766
assert_eq!(discharge_line.stub_get(), DISCHARGE_LINE_ASSERTED);
744767
}
768+
769+
#[test]
770+
fn grace_period() {
771+
let mut wtb = WatchedTasksBuilder::new();
772+
let pwr_line = find_line("DUT_PWR_EN").unwrap();
773+
let discharge_line = find_line("DUT_PWR_DISCH").unwrap();
774+
775+
let (adc, dut_pwr) = {
776+
let mut bb = BrokerBuilder::new();
777+
let adc = block_on(Adc::new(&mut bb, &mut wtb)).unwrap();
778+
let led = Topic::anonymous(None);
779+
780+
let dut_pwr = block_on(DutPwrThread::new(
781+
&mut bb,
782+
&mut wtb,
783+
adc.pwr_volt.clone(),
784+
adc.pwr_curr.clone(),
785+
led,
786+
))
787+
.unwrap();
788+
789+
(adc, dut_pwr)
790+
};
791+
792+
// Set acceptable voltage / current
793+
adc.pwr_volt.fast.set(MAX_VOLTAGE * 0.99);
794+
adc.pwr_curr.fast.set(MAX_CURRENT * 0.99);
795+
796+
println!("Turn Off");
797+
dut_pwr.request.set(OutputRequest::Off);
798+
block_on(sleep(Duration::from_millis(500)));
799+
assert_eq!(pwr_line.stub_get(), 1 - PWR_LINE_ASSERTED);
800+
assert_eq!(discharge_line.stub_get(), DISCHARGE_LINE_ASSERTED);
801+
assert_eq!(block_on(dut_pwr.state.get()), OutputState::Off);
802+
803+
println!("Set overvoltage");
804+
adc.pwr_volt.fast.set(MAX_VOLTAGE * 1.01);
805+
block_on(sleep(Duration::from_millis(500)));
806+
807+
println!("Check if output stays off and does not go into error state");
808+
assert_eq!(pwr_line.stub_get(), 1 - PWR_LINE_ASSERTED);
809+
assert_eq!(discharge_line.stub_get(), DISCHARGE_LINE_ASSERTED);
810+
assert_eq!(block_on(dut_pwr.state.get()), OutputState::Off);
811+
812+
println!("Turn On (with overvoltage applied)");
813+
dut_pwr.request.set(OutputRequest::On);
814+
block_on(sleep(Duration::from_millis(400)));
815+
assert_eq!(pwr_line.stub_get(), PWR_LINE_ASSERTED);
816+
assert_eq!(discharge_line.stub_get(), 1 - DISCHARGE_LINE_ASSERTED);
817+
assert_eq!(block_on(dut_pwr.state.get()), OutputState::On);
818+
819+
println!("Go into overvoltage error");
820+
block_on(sleep(Duration::from_millis(500)));
821+
assert_eq!(pwr_line.stub_get(), 1 - PWR_LINE_ASSERTED);
822+
assert_eq!(discharge_line.stub_get(), DISCHARGE_LINE_ASSERTED);
823+
assert_eq!(block_on(dut_pwr.state.get()), OutputState::OverVoltage);
824+
}
745825
}

0 commit comments

Comments
 (0)