1use bitfield_struct::bitfield;
5use chipset_device::ChipsetDevice;
6use chipset_device::io::IoError;
7use chipset_device::io::IoResult;
8use chipset_device::pio::PortIoIntercept;
9use chipset_device::poll_device::PollDevice;
10use inspect::Inspect;
11use inspect::InspectMut;
12use open_enum::open_enum;
13use std::ops::RangeInclusive;
14use std::task::Context;
15use std::task::Poll;
16use std::time::Duration;
17use thiserror::Error;
18use vmcore::device_state::ChangeDeviceState;
19use vmcore::line_interrupt::LineInterrupt;
20use vmcore::vmtime::VmTime;
21use vmcore::vmtime::VmTimeAccess;
22
23#[rustfmt::skip]
24#[derive(Inspect)]
25#[bitfield(u8)]
26struct ControlWord {
27 #[bits(1)] bcd: bool,
28 #[inspect(with = "|x| Mode::from(*x)")]
29 #[bits(3)] mode: u8,
30 #[inspect(with = "|x| RwMode(*x)")]
31 #[bits(2)] rw: u8,
32 #[inspect(skip)] #[bits(2)] select: u8,
34}
35
36#[rustfmt::skip]
37#[bitfield(u8)]
38struct StatusWord {
39 #[bits(1)] bcd: bool,
40 #[bits(3)] mode: u8,
41 #[bits(2)] rw: u8,
42 #[bits(1)] null: bool,
43 #[bits(1)] out: bool,
44}
45
46#[bitfield(u8)]
47struct ReadBackCommand {
48 reserved: bool,
49 counter0: bool,
50 counter1: bool,
51 counter2: bool,
52 status: bool,
53 count: bool,
54 #[bits(2)]
55 one: u8,
56}
57
58const PIT_TIMER_RANGE_START: u16 = 0x40;
59const PIT_TIMER_RANGE_END: u16 = 0x42;
60const PIT_CONTROL_REGISTER: u16 = 0x43;
61const PIT_PORT61_REGISTER: u16 = 0x61;
62
63#[derive(Debug, Inspect)]
64struct Timer {
65 enabled_at_reset: bool,
67
68 interrupt: Option<LineInterrupt>,
70
71 #[inspect(flatten)]
73 state: TimerState,
74}
75
76#[derive(Copy, Clone, Debug, Inspect)]
77struct TimerState {
78 ce: u16, cr: u16, ol: Option<u16>, sl: Option<u8>, state: CountState,
83 control: ControlWord,
84 out: bool, gate: bool, null: bool, read_high: bool, cr_low: Option<u8>,
89}
90
91#[derive(Copy, Clone, Debug, Inspect, PartialEq, Eq)]
92enum CountState {
93 Inactive,
94 WaitingForGate,
95 Reloading,
96 Active,
97 Counting,
98}
99
100#[derive(Debug, Copy, Clone, Inspect, PartialEq, Eq)]
101enum Mode {
102 TerminalCount = 0,
103 HardwareOneShot = 1,
104 RateGenerator = 2,
105 SquareWave = 3,
106 SoftwareStrobe = 4,
107 HardwareStrobe = 5,
108}
109
110impl From<u8> for Mode {
111 fn from(v: u8) -> Self {
112 match v {
113 0 => Mode::TerminalCount,
114 1 => Mode::HardwareOneShot,
115 2 | 6 => Mode::RateGenerator,
116 3 | 7 => Mode::SquareWave,
117 4 => Mode::SoftwareStrobe,
118 5 => Mode::HardwareStrobe,
119 _ => unreachable!(),
120 }
121 }
122}
123
124impl Mode {
125 fn gate_stops_count(&self) -> bool {
127 match self {
128 Mode::TerminalCount | Mode::RateGenerator | Mode::SquareWave | Mode::SoftwareStrobe => {
129 true
130 }
131 Mode::HardwareOneShot | Mode::HardwareStrobe => false,
132 }
133 }
134}
135
136open_enum! {
137 #[derive(Inspect)]
138 #[inspect(debug)]
139 enum RwMode: u8 {
140 LOW = 1,
141 HIGH = 2,
142 LOW_HIGH = 3,
143 }
144}
145
146const fn from_bcd(n: u16) -> u16 {
147 (n & 0xf) + ((n & 0xf0) >> 4) * 10 + ((n & 0xf00) >> 8) * 100 + ((n & 0xf000) >> 12) * 1000
148}
149
150const fn to_bcd(n: u16) -> u16 {
151 (n % 10) + (((n / 10) % 10) << 4) + (((n / 100) % 10) << 8) + (((n / 1000) % 10) << 12)
152}
153
154fn counter_sub(ce: u16, n: u64, bcd: bool) -> u64 {
159 if bcd {
160 let ce = from_bcd(ce);
161 let n = (n % 10000) as u16;
162 (if ce >= n { ce - n } else { 10000 - (n - ce) }) as u64
163 } else {
164 ce.wrapping_sub(n as u16) as u64
165 }
166}
167
168const NANOS_PER_TICK: u64 = 838;
170
171impl Timer {
172 fn new(enabled_at_reset: bool, interrupt: Option<LineInterrupt>) -> Self {
173 Self {
174 enabled_at_reset,
175 interrupt,
176 state: TimerState::new(enabled_at_reset),
177 }
178 }
179
180 fn reset(&mut self) {
181 self.state = TimerState::new(self.enabled_at_reset);
182 self.sync_interrupt();
183 }
184
185 fn sync_interrupt(&mut self) {
186 if let Some(interrupt) = &self.interrupt {
187 interrupt.set_level(self.state.out);
188 }
189 }
190
191 fn set_out(&mut self, state: bool) {
192 if self.state.out != state {
193 self.state.out = state;
194 self.sync_interrupt();
195 }
196 }
197
198 fn load_ce(&mut self) {
199 self.state.ce = self.state.cr;
200 self.state.null = false;
201 }
202
203 fn set_ce(&mut self, ce: u64) {
206 if self.state.control.bcd() {
207 self.state.ce = to_bcd((ce % 10000) as u16);
208 } else {
209 self.state.ce = ce as u16;
210 }
211 }
212
213 fn evaluate(&mut self, mut ticks: u64) {
214 let mode = self.state.op_mode();
215 let bcd = self.state.control.bcd();
216 while ticks > 0 {
217 match self.state.state {
218 CountState::Inactive | CountState::WaitingForGate => break,
219 CountState::Reloading => {
220 ticks -= 1;
221 self.load_ce();
222 self.state.state = CountState::Active;
223 }
224 CountState::Active => {
225 if !self.state.gate && mode.gate_stops_count() {
226 break;
227 }
228 let per = match mode {
230 Mode::TerminalCount
231 | Mode::HardwareOneShot
232 | Mode::RateGenerator
233 | Mode::SoftwareStrobe
234 | Mode::HardwareStrobe => 1,
235 Mode::SquareWave => {
236 if self.state.ce & 1 != 0 {
239 self.state.ce &= !1;
240 if self.state.out {
241 ticks -= 1;
242 continue;
243 }
244 }
245 2
246 }
247 };
248 if self.state.ce as u64 == per {
249 self.state.ce = 0;
251 ticks -= 1;
252 match mode {
253 Mode::TerminalCount | Mode::HardwareOneShot => {
254 self.set_out(true);
255 self.state.state = CountState::Counting;
256 }
257 Mode::RateGenerator => {
258 self.set_out(true);
259 self.load_ce();
260 }
261 Mode::SquareWave => {
262 self.set_out(!self.state.out);
263 self.load_ce();
264 }
265 Mode::SoftwareStrobe | Mode::HardwareStrobe => {
266 self.set_out(false);
267 self.state.state = CountState::Counting;
268 }
269 }
270 } else {
271 if ticks >= counter_sub(self.state.ce, per, bcd) / per {
272 ticks -= counter_sub(self.state.ce, per, bcd) / per;
274 self.state.ce = per as u16;
275 if mode == Mode::RateGenerator {
276 self.set_out(false);
277 }
278 } else {
279 self.set_ce(counter_sub(self.state.ce, ticks * per, bcd));
280 ticks = 0;
281 }
282 }
283 }
284 CountState::Counting => {
285 if !self.state.gate && mode.gate_stops_count() {
286 break;
287 }
288 self.set_ce(counter_sub(self.state.ce, ticks, bcd));
289 ticks = 0;
290 self.set_out(true);
291 }
292 }
293 }
294 }
295
296 fn set_control(&mut self, control: ControlWord) {
297 if control.rw() == 0 {
298 self.state.latch_counter();
299 return;
300 }
301
302 self.state.control = control.with_select(0);
303 self.state.ce = 0;
304 self.state.cr = 0;
305 self.state.cr_low = None;
306 self.state.read_high = false;
307 self.state.state = CountState::Inactive;
308 self.state.null = true;
309 self.set_out(match self.state.op_mode() {
310 Mode::TerminalCount => false,
311 Mode::HardwareOneShot => true,
312 Mode::RateGenerator | Mode::SquareWave => true,
313 Mode::SoftwareStrobe => true,
314 Mode::HardwareStrobe => true,
315 });
316 }
317
318 fn write(&mut self, n: u8) {
319 let n = n as u16;
320 match RwMode(self.state.control.rw()) {
321 RwMode::LOW => self.state.cr = n,
322 RwMode::HIGH => self.state.cr = n << 8,
323 RwMode::LOW_HIGH => {
324 if let Some(low) = self.state.cr_low {
325 self.state.cr = (n << 8) | (low as u16);
326 } else {
327 self.state.cr_low = Some(n as u8);
328 return;
330 }
331 }
332 _ => unreachable!(),
333 }
334 self.state.null = true;
335 match self.state.op_mode() {
336 Mode::TerminalCount => {
337 self.state.state = CountState::Reloading;
338 self.set_out(false);
339 }
340 Mode::HardwareOneShot => {
341 self.state.state = CountState::WaitingForGate;
342 }
343 Mode::RateGenerator | Mode::SquareWave => {
344 if self.state.state != CountState::Active {
345 self.state.state = CountState::Reloading;
346 }
347 }
348 Mode::SoftwareStrobe => {
349 self.state.state = CountState::Reloading;
350 }
351 Mode::HardwareStrobe => {
352 self.state.state = CountState::WaitingForGate;
353 }
354 }
355 }
356
357 fn read(&mut self) -> u8 {
358 if let Some(sl) = self.state.sl.take() {
359 return sl;
360 }
361 let value = self.state.ol.unwrap_or(self.state.ce);
362 let value = match RwMode(self.state.control.rw()) {
363 RwMode::LOW => value as u8,
364 RwMode::HIGH => (value >> 8) as u8,
365 RwMode::LOW_HIGH => {
366 self.state.read_high = !self.state.read_high;
367 if self.state.read_high {
368 value as u8
369 } else {
370 (value >> 8) as u8
371 }
372 }
373 _ => unreachable!(),
374 };
375 if !self.state.read_high {
376 self.state.ol = None;
377 }
378 value
379 }
380
381 fn set_gate(&mut self, gate: bool) {
382 match self.state.op_mode() {
383 Mode::TerminalCount => {}
384 Mode::HardwareOneShot => {
385 if !self.state.gate && gate && self.state.state == CountState::WaitingForGate {
386 self.state.state = CountState::Reloading;
387 self.set_out(false);
388 }
389 }
390 Mode::RateGenerator | Mode::SquareWave => {
391 if gate && !self.state.gate {
392 if self.state.state == CountState::Active {
393 self.state.state = CountState::Reloading;
394 }
395 } else if !gate {
396 self.set_out(true);
397 }
398 }
399 Mode::SoftwareStrobe => {}
400 Mode::HardwareStrobe => {
401 if !self.state.gate && gate && self.state.state == CountState::WaitingForGate {
402 self.state.state = CountState::Reloading;
403 }
404 }
405 }
406 self.state.gate = gate;
407 }
408}
409
410impl TimerState {
411 fn new(enabled: bool) -> Self {
412 Self {
413 ce: 0,
414 cr: 0,
415 ol: None,
416 sl: None,
417 control: ControlWord::new().with_rw(1),
418 state: CountState::Inactive,
419 out: false,
420 null: true,
421 gate: enabled,
422 read_high: false,
423 cr_low: None,
424 }
425 }
426
427 fn op_mode(&self) -> Mode {
428 self.control.mode().into()
429 }
430
431 fn next_wakeup(&self) -> Option<u64> {
433 let mode = self.op_mode();
434 let bcd = self.control.bcd();
435 match self.state {
436 CountState::Inactive => None,
437 CountState::WaitingForGate => None,
438 CountState::Reloading | CountState::Active => {
439 if !self.gate && mode.gate_stops_count() {
440 return None;
441 }
442 let (ce, extra) = if self.state == CountState::Reloading {
444 (self.cr, 1)
445 } else {
446 (self.ce, 0)
447 };
448 let v = {
449 match mode {
450 Mode::TerminalCount
451 | Mode::HardwareOneShot
452 | Mode::SoftwareStrobe
453 | Mode::HardwareStrobe => {
454 counter_sub(ce, 1, bcd) + 1
456 }
457 Mode::RateGenerator => {
458 if ce == 1 {
459 1
461 } else {
462 counter_sub(ce, 1, bcd)
464 }
465 }
466 Mode::SquareWave => {
467 (counter_sub(ce, 2, bcd) + 2) / 2 + (self.out && ce & 1 != 0) as u64
469 }
470 }
471 };
472 Some(v + extra)
473 }
474 CountState::Counting => {
475 if self.out || (!self.gate && mode.gate_stops_count()) {
476 None
477 } else {
478 Some(1)
479 }
480 }
481 }
482 }
483
484 fn latch_status(&mut self) {
485 if self.sl.is_none() {
486 self.sl = Some(
487 StatusWord(self.control.0)
488 .with_null(self.null)
489 .with_out(self.out)
490 .into(),
491 );
492 }
493 }
494
495 fn latch_counter(&mut self) {
496 if self.ol.is_none() {
497 self.ol = Some(self.ce);
498 }
499 }
500}
501
502#[derive(InspectMut)]
503pub struct PitDevice {
504 vmtime: VmTimeAccess,
506
507 #[inspect(iter_by_index)]
509 timers: [Timer; { PIT_TIMER_RANGE_END - PIT_TIMER_RANGE_START + 1 } as usize],
510
511 dram_refresh: bool, last: VmTime,
516}
517
518impl PitDevice {
519 pub fn new(interrupt: LineInterrupt, vmtime: VmTimeAccess) -> Self {
520 PitDevice {
521 timers: [
524 Timer::new(true, Some(interrupt)),
525 Timer::new(true, None),
526 Timer::new(false, None),
527 ],
528 last: vmtime.now(),
529 vmtime,
530 dram_refresh: false,
531 }
532 }
533
534 fn evaluate(&mut self, now: VmTime) {
535 let delta = now.checked_sub(self.last).unwrap_or(Duration::ZERO);
540 let ticks = delta.as_nanos() as u64 / NANOS_PER_TICK;
541 self.last = self
542 .last
543 .wrapping_add(Duration::from_nanos(ticks * NANOS_PER_TICK));
544 self.timers[0].evaluate(ticks);
545 self.timers[1].evaluate(ticks);
546 self.timers[2].evaluate(ticks);
547 }
548
549 fn arm_wakeup(&mut self) {
550 if let Some(next) = self.timers[0].state.next_wakeup() {
553 let next = next.max(20);
555 self.vmtime.set_timeout_if_before(
556 self.last
557 .wrapping_add(Duration::from_nanos(next * NANOS_PER_TICK)),
558 );
559 }
560 }
561}
562
563impl ChangeDeviceState for PitDevice {
564 fn start(&mut self) {}
565
566 async fn stop(&mut self) {}
567
568 async fn reset(&mut self) {
569 for timer in &mut self.timers {
570 timer.reset();
571 }
572 self.last = self.vmtime.now();
573 }
574}
575
576impl ChipsetDevice for PitDevice {
577 fn supports_poll_device(&mut self) -> Option<&mut dyn PollDevice> {
578 Some(self)
579 }
580
581 fn supports_pio(&mut self) -> Option<&mut dyn PortIoIntercept> {
582 Some(self)
583 }
584}
585
586impl PollDevice for PitDevice {
587 fn poll_device(&mut self, cx: &mut Context<'_>) {
588 if let Poll::Ready(now) = self.vmtime.poll_timeout(cx) {
589 self.evaluate(now);
590 assert!(self.vmtime.poll_timeout(cx).is_pending());
594 self.arm_wakeup();
595 }
596 }
597}
598
599impl PortIoIntercept for PitDevice {
600 fn io_read(&mut self, io_port: u16, data: &mut [u8]) -> IoResult {
601 if data.len() != 1 {
602 return IoResult::Err(IoError::InvalidAccessSize);
603 }
604
605 self.evaluate(self.vmtime.now());
606 match io_port {
607 PIT_TIMER_RANGE_START..=PIT_TIMER_RANGE_END => {
608 let offset = io_port - PIT_TIMER_RANGE_START;
609 data[0] = self.timers[offset as usize].read();
610 }
611 PIT_CONTROL_REGISTER => {
612 tracelimit::warn_ratelimited!("reading from write-only command register!");
613 data[0] = !0;
614 }
615 PIT_PORT61_REGISTER => {
616 data[0] = ((self.timers[2].state.out as u8) << 5)
617 | ((self.dram_refresh as u8) << 4)
618 | self.timers[2].state.gate as u8;
619 self.dram_refresh = !self.dram_refresh;
623 }
624 _ => return IoResult::Err(IoError::InvalidRegister),
625 }
626
627 self.arm_wakeup();
628 IoResult::Ok
629 }
630
631 fn io_write(&mut self, io_port: u16, data: &[u8]) -> IoResult {
632 let &[b] = data else {
633 return IoResult::Err(IoError::InvalidAccessSize);
634 };
635
636 self.evaluate(self.vmtime.now());
637
638 match io_port {
639 PIT_TIMER_RANGE_START..=PIT_TIMER_RANGE_END => {
640 let offset = io_port - PIT_TIMER_RANGE_START;
641 self.timers[offset as usize].write(b);
642 }
643 PIT_CONTROL_REGISTER => {
644 let control = ControlWord(b);
645 match control.select() {
646 i @ 0..=2 => {
647 tracing::trace!(timer = i, ?control, "control write");
648 self.timers[i as usize].set_control(control);
649 }
650 3 => {
651 let command = ReadBackCommand(b);
652 tracing::trace!(?command, "read back");
653 for (i, select) in
654 [command.counter0(), command.counter1(), command.counter2()]
655 .into_iter()
656 .enumerate()
657 {
658 if select {
659 if command.status() {
660 self.timers[i].state.latch_status();
661 }
662 if command.count() {
663 self.timers[i].state.latch_counter();
664 }
665 }
666 }
667 }
668 _ => unreachable!(),
669 }
670 }
671 PIT_PORT61_REGISTER => {
672 self.timers[2].set_gate((b & 1) != 0);
673 }
674 _ => return IoResult::Err(IoError::InvalidRegister),
675 }
676
677 self.arm_wakeup();
678 IoResult::Ok
679 }
680
681 fn get_static_regions(&mut self) -> &[(&str, RangeInclusive<u16>)] {
682 &[
683 ("main", PIT_TIMER_RANGE_START..=PIT_CONTROL_REGISTER),
684 ("port61", PIT_PORT61_REGISTER..=PIT_PORT61_REGISTER),
685 ]
686 }
687}
688
689mod save_restore {
690 use super::*;
691 use vmcore::save_restore::RestoreError;
692 use vmcore::save_restore::SaveError;
693 use vmcore::save_restore::SaveRestore;
694
695 mod state {
696 use mesh::payload::Protobuf;
697 use vmcore::save_restore::SavedStateRoot;
698 use vmcore::vmtime::VmTime;
699
700 #[derive(Protobuf)]
701 #[mesh(package = "chipset.pit")]
702 pub enum SavedCountState {
703 #[mesh(1)]
704 Inactive,
705 #[mesh(2)]
706 WaitingForGate,
707 #[mesh(3)]
708 Reloading,
709 #[mesh(4)]
710 Active,
711 #[mesh(5)]
712 Counting,
713 }
714
715 #[derive(Protobuf)]
716 #[mesh(package = "chipset.pit")]
717 pub struct SavedTimerState {
718 #[mesh(1)]
719 pub ce: u16,
720 #[mesh(2)]
721 pub cr: u16,
722 #[mesh(3)]
723 pub ol: Option<u16>,
724 #[mesh(4)]
725 pub sl: Option<u8>,
726 #[mesh(5)]
727 pub state: SavedCountState,
728 #[mesh(6)]
729 pub control: u8,
730 #[mesh(7)]
731 pub out: bool,
732 #[mesh(8)]
733 pub gate: bool,
734 #[mesh(9)]
735 pub null: bool,
736 #[mesh(10)]
737 pub read_high: bool,
738 #[mesh(11)]
739 pub cr_low: Option<u8>,
740 }
741
742 #[derive(Protobuf, SavedStateRoot)]
743 #[mesh(package = "chipset.pit")]
744 pub struct SavedState {
745 #[mesh(1)]
746 pub timers: [SavedTimerState; 3],
747 #[mesh(2)]
748 pub last: VmTime,
749 }
750 }
751
752 #[derive(Debug, Error)]
753 enum PitDeviceRestoreError {
754 #[error("last tick time is after current time")]
755 InvalidLastTick,
756 }
757
758 impl SaveRestore for PitDevice {
759 type SavedState = state::SavedState;
760
761 fn save(&mut self) -> Result<Self::SavedState, SaveError> {
762 let Self {
763 vmtime: _,
764 timers,
765 dram_refresh: _,
766 last,
767 } = self;
768
769 Ok(state::SavedState {
770 timers: [&timers[0].state, &timers[1].state, &timers[2].state].map(|timer| {
771 let &TimerState {
772 ce,
773 cr,
774 ol,
775 sl,
776 state,
777 control,
778 out,
779 gate,
780 null,
781 read_high,
782 cr_low,
783 } = timer;
784
785 state::SavedTimerState {
786 ce,
787 cr,
788 ol,
789 sl,
790 state: match state {
791 CountState::Inactive => state::SavedCountState::Inactive,
792 CountState::WaitingForGate => state::SavedCountState::WaitingForGate,
793 CountState::Reloading => state::SavedCountState::Reloading,
794 CountState::Active => state::SavedCountState::Active,
795 CountState::Counting => state::SavedCountState::Counting,
796 },
797 control: control.into(),
798 out,
799 gate,
800 null,
801 read_high,
802 cr_low,
803 }
804 }),
805
806 last: *last,
807 })
808 }
809
810 fn restore(&mut self, state: Self::SavedState) -> Result<(), RestoreError> {
811 let state::SavedState { timers, last } = state;
812
813 for (timer, state) in self.timers.iter_mut().zip(timers) {
814 let state::SavedTimerState {
815 ce,
816 cr,
817 ol,
818 sl,
819 state,
820 control,
821 out,
822 gate,
823 null,
824 read_high,
825 cr_low,
826 } = state;
827
828 timer.state = TimerState {
829 ce,
830 cr,
831 ol,
832 sl,
833 state: match state {
834 state::SavedCountState::Inactive => CountState::Inactive,
835 state::SavedCountState::WaitingForGate => CountState::WaitingForGate,
836 state::SavedCountState::Reloading => CountState::Reloading,
837 state::SavedCountState::Active => CountState::Active,
838 state::SavedCountState::Counting => CountState::Counting,
839 },
840 out,
841 control: ControlWord::from(control), gate,
843 null,
844 read_high,
845 cr_low,
846 };
847
848 timer.sync_interrupt();
849 }
850
851 self.last = last;
852 if last.is_after(self.vmtime.now()) {
853 return Err(RestoreError::InvalidSavedState(
854 PitDeviceRestoreError::InvalidLastTick.into(),
855 ));
856 }
857
858 Ok(())
859 }
860 }
861}
862
863#[cfg(test)]
864mod tests {
865 use super::ControlWord;
866 use super::Mode;
867 use super::RwMode;
868 use super::Timer;
869 use super::to_bcd;
870 use crate::pit::from_bcd;
871
872 #[test]
873 fn test_bcd_comp() {
874 for i in 0..=9999 {
875 assert_eq!(from_bcd(to_bcd(i)), i, "{i} {}", to_bcd(i));
876 }
877 }
878
879 fn set_timer(timer: &mut Timer, mode: Mode, mut cr: u16, bcd: bool) {
880 timer.set_control(
881 ControlWord::new()
882 .with_mode(mode as u8)
883 .with_rw(RwMode::LOW_HIGH.0)
884 .with_bcd(bcd),
885 );
886 if bcd {
887 cr = to_bcd(cr);
888 }
889 timer.write(cr as u8);
890 timer.write((cr >> 8) as u8);
891 }
892
893 fn check_invert(timer: &mut Timer, initial_out: bool, expected_next: u64) {
894 let mode = Mode::from(timer.state.control.mode());
895 assert_eq!(timer.state.out, initial_out, "{mode:?}");
896 let n = timer.state.next_wakeup().unwrap();
897 assert_eq!(n, expected_next, "{mode:?}");
898 for i in 0..n - 1 {
899 timer.evaluate(1);
900 assert_eq!(
901 i + timer.state.next_wakeup().unwrap() + 1,
902 n,
903 "{mode:?}, {i}"
904 );
905 assert_eq!(timer.state.out, initial_out, "{mode:?}, {i}");
906 }
907 timer.evaluate(1);
908 assert_eq!(timer.state.out, !initial_out, "{mode:?}, {n}");
909 }
910
911 fn check_done(timer: &mut Timer) {
912 assert!(timer.state.next_wakeup().is_none());
913 let out = timer.state.out;
914 for _ in 0..65536 {
915 timer.evaluate(1);
916 assert_eq!(timer.state.out, out);
917 }
918 }
919
920 fn test_output(bcd: bool) {
921 let mut timer = Timer::new(true, None);
922 let max = if bcd { 10000 } else { 0x10000 };
923
924 set_timer(&mut timer, Mode::TerminalCount, 0, bcd);
925 check_invert(&mut timer, false, max + 1);
926 check_done(&mut timer);
927
928 set_timer(&mut timer, Mode::HardwareOneShot, 0, bcd);
929 check_done(&mut timer);
930 timer.set_gate(false);
931 timer.set_gate(true);
932 check_invert(&mut timer, false, max + 1);
933 check_done(&mut timer);
934
935 set_timer(&mut timer, Mode::RateGenerator, 0, bcd);
936 check_invert(&mut timer, true, max);
937 check_invert(&mut timer, false, 1);
938 check_invert(&mut timer, true, max - 1);
939
940 set_timer(&mut timer, Mode::SquareWave, 0, bcd);
941 check_invert(&mut timer, true, max / 2 + 1);
942 check_invert(&mut timer, false, max / 2);
943 check_invert(&mut timer, true, max / 2);
944
945 set_timer(&mut timer, Mode::SquareWave, 1001, bcd);
946 check_invert(&mut timer, true, 502);
947 check_invert(&mut timer, false, 500);
948 check_invert(&mut timer, true, 501);
949
950 set_timer(&mut timer, Mode::SoftwareStrobe, 0, bcd);
951 check_invert(&mut timer, true, max + 1);
952 check_invert(&mut timer, false, 1);
953 check_done(&mut timer);
954
955 set_timer(&mut timer, Mode::HardwareStrobe, 0, bcd);
956 check_done(&mut timer);
957 timer.set_gate(false);
958 timer.set_gate(true);
959 check_invert(&mut timer, true, max + 1);
960 check_invert(&mut timer, false, 1);
961 check_done(&mut timer);
962 }
963
964 #[test]
965 fn test_binary() {
966 test_output(false);
967 }
968
969 #[test]
970 fn test_bcd() {
971 test_output(true);
972 }
973}