Skip to main content

culvert/register/
mod.rs

1//! Typed SCDC register map: bitfield structs and typed values.
2
3use display_types::HdmiForumFrl;
4
5// Re-export for use in the public API.
6pub use display_types::HdmiForumFrl as FrlRate;
7
8/// Configuration written to `TMDS_Config` (0x20).
9#[derive(Debug, Clone, Copy, PartialEq, Eq)]
10pub struct TmdsConfig {
11    /// Enable TMDS scrambling.
12    pub scrambling_enable: bool,
13    /// TMDS bit clock ratio: `false` = divide by 10, `true` = divide by 40.
14    pub high_tmds_clock_ratio: bool,
15}
16
17/// Decoded content of `Scrambler_Status` (0x21).
18#[non_exhaustive]
19#[derive(Debug, Clone, Copy, PartialEq, Eq)]
20pub struct ScramblerStatus {
21    /// The sink confirms that TMDS scrambling is active.
22    pub scrambling_active: bool,
23}
24
25impl ScramblerStatus {
26    /// Constructs a `ScramblerStatus`.
27    pub fn new(scrambling_active: bool) -> Self {
28        Self { scrambling_active }
29    }
30}
31
32/// FFE (Feed-Forward Equalization) levels written into `Config_1` bits\[7:4\]: the highest
33/// TxFFE level index the source supports for the requested rate.
34///
35/// The allowed maximum depends on the FRL rate: 3 up to 12 Gbps, 7 for faster rates (see
36/// [`FfeLevels::max_for`]). [`Scdc::write_frl_config`](crate::Scdc::write_frl_config)
37/// rejects a configuration that exceeds it.
38#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
39pub struct FfeLevels(u8);
40
41impl FfeLevels {
42    /// Constructs `FfeLevels`; `None` above 7, the highest level any FRL rate allows.
43    pub const fn new(levels: u8) -> Option<Self> {
44        if levels <= 7 {
45            Some(Self(levels))
46        } else {
47            None
48        }
49    }
50
51    /// The highest FFE level allowed at `rate`: 3 for rates up to 12 Gbps, 7 above.
52    ///
53    /// `HdmiForumFrl` values are in rate order (12 Gbps is 6; the 16, 20 and 24 Gbps rates
54    /// that allow 7 follow it), so rates added to `HdmiForumFrl` later get 7 without a
55    /// change here.
56    pub fn max_for(rate: HdmiForumFrl) -> Self {
57        // 3 for rates up to 12 Gbps, 3 + 4 = 7 for faster ones.
58        let faster_than_12g = (rate as u8 > HdmiForumFrl::Rate12Gbps4Lanes as u8) as u8;
59        Self(3 + 4 * faster_than_12g)
60    }
61
62    /// Returns the level index.
63    pub const fn value(self) -> u8 {
64        self.0
65    }
66}
67
68/// Configuration written to `Config_1` (0x31). Build one with [`new`](Self::new).
69#[non_exhaustive]
70#[derive(Debug, Clone, Copy, PartialEq, Eq)]
71pub struct FrlConfig {
72    /// FRL rate to request. Use [`HdmiForumFrl::NotSupported`] to clear FRL mode.
73    pub frl_rate: HdmiForumFrl,
74    /// FFE levels to advertise to the sink.
75    pub ffe_levels: FfeLevels,
76}
77
78impl FrlConfig {
79    /// Constructs `FrlConfig` from the rate and the FFE levels.
80    pub const fn new(frl_rate: HdmiForumFrl, ffe_levels: FfeLevels) -> Self {
81        Self {
82            frl_rate,
83            ffe_levels,
84        }
85    }
86}
87
88/// Content written to `Config_0` (0x30).
89///
90/// [`Scdc::write_config_0`](crate::Scdc::write_config_0) writes the whole register, so
91/// both fields are always set.
92#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
93pub struct Config0 {
94    /// Bit 0: the sink may raise read requests.
95    pub rr_enable: bool,
96    /// Bit 1: the sink should not request link retraining.
97    pub flt_no_retrain: bool,
98}
99
100/// Decoded content of `Source_Test_Configuration` (0x35).
101///
102/// Written by the sink, typically during compliance testing, to instruct the source.
103/// Read it when [`UpdateFlags::source_test_update`] is set. Bits 0 and 4 are reserved.
104/// The default has no bit set.
105#[non_exhaustive]
106#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
107pub struct SourceTestConfig {
108    /// Bit 1: use pre-shoot only for TxFFE.
109    pub txffe_pre_shoot_only: bool,
110    /// Bit 2: use de-emphasis only for TxFFE.
111    pub txffe_de_emphasis_only: bool,
112    /// Bit 3: use no TxFFE.
113    pub txffe_no_ffe: bool,
114    /// Bit 5: the source should not time out link training.
115    pub flt_no_timeout: bool,
116    /// Bit 6: the source should use the maximum FRL rate with DSC. With `frl_max` also
117    /// set, the Amlogic (citing the specification) and NVIDIA drivers act on neither; the
118    /// AMD driver trains at the maximum rate.
119    pub dsc_frl_max: bool,
120    /// Bit 7: the source should use the maximum FRL rate. With `dsc_frl_max` also set, the
121    /// Amlogic (citing the specification) and NVIDIA drivers act on neither; the AMD
122    /// driver trains at the maximum rate.
123    pub frl_max: bool,
124}
125
126impl SourceTestConfig {
127    /// Constructs `SourceTestConfig` from its flags, in bit order.
128    pub const fn new(
129        txffe_pre_shoot_only: bool,
130        txffe_de_emphasis_only: bool,
131        txffe_no_ffe: bool,
132        flt_no_timeout: bool,
133        dsc_frl_max: bool,
134        frl_max: bool,
135    ) -> Self {
136        Self {
137            txffe_pre_shoot_only,
138            txffe_de_emphasis_only,
139            txffe_no_ffe,
140            flt_no_timeout,
141            dsc_frl_max,
142            frl_max,
143        }
144    }
145}
146
147/// Link Training Pattern requested by the sink for one lane: a 4-bit field in
148/// `Status_Flags_1` (0x41, lanes 0–1) or `Status_Flags_2` (0x42, lanes 2–3).
149///
150/// Every 4-bit value decodes: the values the HDMI 2.1 specification leaves undefined
151/// (0x9–0xD) are [`Reserved`](Self::Reserved), with the raw value. Whether one matters
152/// depends on the lanes in use, which the link training layer knows and culvert does not.
153#[non_exhaustive]
154#[derive(Debug, Clone, Copy, PartialEq, Eq)]
155pub enum LtpReq {
156    /// 0x0: no pattern requested; the lane is trained.
157    None,
158    /// 0x1: all-ones pattern.
159    AllOnes,
160    /// 0x2: all-zeros pattern.
161    AllZeros,
162    /// 0x3: Nyquist clock pattern.
163    NyquistClock,
164    /// 0x4: DDE (Data Dependent Equalization) compliance pattern. The Xilinx drivers name
165    /// it after their side (RxDDE in the receiver driver, TxDDE in the transmitter's);
166    /// the name here, like hdmi-hal's `LtpPattern::DdeCompliance`, is the pattern's.
167    DdeCompliance,
168    /// 0x5: LFSR 0.
169    Lfsr0,
170    /// 0x6: LFSR 1.
171    Lfsr1,
172    /// 0x7: LFSR 2.
173    Lfsr2,
174    /// 0x8: LFSR 3.
175    Lfsr3,
176    /// 0xE: the sink requests a change of FFE level.
177    FfeChange,
178    /// 0xF: the sink requests a lower FRL rate.
179    RateChange,
180    /// 0x9–0xD: a value the specification leaves undefined.
181    ///
182    /// Build requests from a raw value with [`from_value`](Self::from_value), which only
183    /// produces `Reserved` for 0x9–0xD. A `Reserved` built by hand with any other value
184    /// is not a request a sink can send: `Reserved(0x5)` has the value of `Lfsr0` but
185    /// does not compare equal to it.
186    Reserved(u8),
187}
188
189impl LtpReq {
190    /// The request with the 4-bit `value`, or `None` above 0xF. Every value has exactly
191    /// one request: 0x9–0xD are [`Reserved`](Self::Reserved).
192    pub const fn from_value(value: u8) -> Option<Self> {
193        Some(match value {
194            0x0 => Self::None,
195            0x1 => Self::AllOnes,
196            0x2 => Self::AllZeros,
197            0x3 => Self::NyquistClock,
198            0x4 => Self::DdeCompliance,
199            0x5 => Self::Lfsr0,
200            0x6 => Self::Lfsr1,
201            0x7 => Self::Lfsr2,
202            0x8 => Self::Lfsr3,
203            0x9..=0xD => Self::Reserved(value),
204            0xE => Self::FfeChange,
205            0xF => Self::RateChange,
206            _ => return None,
207        })
208    }
209
210    /// Decodes a 4-bit LTP request field (the low nibble of `nibble`).
211    pub(crate) const fn from_nibble(nibble: u8) -> Self {
212        // Every 4-bit value has a request.
213        Self::from_value(nibble & 0x0F).unwrap()
214    }
215
216    /// The request's 4-bit value (for a `Reserved` built by hand, the value it holds).
217    pub const fn value(self) -> u8 {
218        match self {
219            Self::None => 0x0,
220            Self::AllOnes => 0x1,
221            Self::AllZeros => 0x2,
222            Self::NyquistClock => 0x3,
223            Self::DdeCompliance => 0x4,
224            Self::Lfsr0 => 0x5,
225            Self::Lfsr1 => 0x6,
226            Self::Lfsr2 => 0x7,
227            Self::Lfsr3 => 0x8,
228            Self::FfeChange => 0xE,
229            Self::RateChange => 0xF,
230            Self::Reserved(value) => value,
231        }
232    }
233}
234
235/// Per-lane link training pattern requests from `Status_Flags_1` (0x41) and
236/// `Status_Flags_2` (0x42).
237#[non_exhaustive]
238#[derive(Debug, Clone, Copy, PartialEq, Eq)]
239pub struct LtpRequests {
240    /// Lane 0 (`Status_Flags_1` bits 3:0).
241    pub lane0: LtpReq,
242    /// Lane 1 (`Status_Flags_1` bits 7:4).
243    pub lane1: LtpReq,
244    /// Lane 2 (`Status_Flags_2` bits 3:0).
245    pub lane2: LtpReq,
246    /// Lane 3 (`Status_Flags_2` bits 7:4). Not in use in 3-lane FRL, where it holds
247    /// whatever the sink leaves there (not necessarily `LtpReq::None`); the link training
248    /// layer ignores it.
249    pub lane3: LtpReq,
250}
251
252impl LtpRequests {
253    /// Constructs `LtpRequests` from the per-lane requests.
254    pub const fn new(lane0: LtpReq, lane1: LtpReq, lane2: LtpReq, lane3: LtpReq) -> Self {
255        Self {
256            lane0,
257            lane1,
258            lane2,
259            lane3,
260        }
261    }
262
263    /// Returns `true` when none of the four lanes requests a pattern. In 3-lane FRL lane 3
264    /// is not in use and may hold anything, so there only lanes 0–2 decide whether
265    /// training passed.
266    pub fn all_trained(&self) -> bool {
267        [self.lane0, self.lane1, self.lane2, self.lane3]
268            .iter()
269            .all(|r| *r == LtpReq::None)
270    }
271}
272
273/// Decoded content of `Status_Flags_0` (0x40).
274///
275/// The per-lane link training pattern requests live in `Status_Flags_1`/`_2` and are
276/// read separately with [`Scdc::read_ltp_requests`](crate::Scdc::read_ltp_requests);
277/// `FRL_Start` is an `Update_0` flag ([`UpdateFlags::frl_start`]).
278#[non_exhaustive]
279#[derive(Debug, Clone, Copy, PartialEq, Eq)]
280pub struct StatusFlags {
281    /// Bit 0: the sink detects a clock.
282    pub clock_detected: bool,
283    /// Bit 1: channel 0 locked.
284    pub ch0_locked: bool,
285    /// Bit 2: channel 1 locked.
286    pub ch1_locked: bool,
287    /// Bit 3: channel 2 locked.
288    pub ch2_locked: bool,
289    /// Bit 4: lane 3 locked (FRL 4-lane only).
290    pub ln3_locked: bool,
291    /// Bit 6: the sink is ready for link training.
292    pub flt_ready: bool,
293    /// Bit 7: the sink failed to decode the DSC stream.
294    pub dsc_decode_fail: bool,
295}
296
297impl StatusFlags {
298    /// Constructs `StatusFlags` from the `Status_Flags_0` flags, in bit order.
299    pub fn new(
300        clock_detected: bool,
301        ch0_locked: bool,
302        ch1_locked: bool,
303        ch2_locked: bool,
304        ln3_locked: bool,
305        flt_ready: bool,
306        dsc_decode_fail: bool,
307    ) -> Self {
308        Self {
309            clock_detected,
310            ch0_locked,
311            ch1_locked,
312            ch2_locked,
313            ln3_locked,
314            flt_ready,
315            dsc_decode_fail,
316        }
317    }
318}
319
320/// Decoded content of `Update_0` (0x10).
321///
322/// Flags are set by the sink to notify the source of state changes. The source reads them
323/// with [`Scdc::read_update_flags`](crate::Scdc::read_update_flags) and clears the ones it
324/// has handled with [`Scdc::clear_update_flags`](crate::Scdc::clear_update_flags)
325/// (write-1-to-clear), which takes a [`ClearableUpdateFlags`]: every flag but `RR_Test`.
326/// [`clearable`](Self::clearable) gives the flags just read in that form. `Update_1`
327/// (0x11) defines no fields and is not accessed. The default has no flag set.
328#[non_exhaustive]
329#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
330pub struct UpdateFlags {
331    /// Bit 0: general status has changed.
332    pub status_update: bool,
333    /// Bit 1: CED counters have been updated; re-read the `ERR_DET` registers.
334    pub ced_update: bool,
335    /// Bit 2: read request test, owned by the sink. The source does not clear it, so
336    /// [`ClearableUpdateFlags`] has no field for it.
337    pub rr_test: bool,
338    /// Bit 3: the sink has written `Source_Test_Configuration` (0x35).
339    pub source_test_update: bool,
340    /// Bit 4: link training passed; the source may start FRL transmission.
341    pub frl_start: bool,
342    /// Bit 5: the per-lane link training pattern requests have changed.
343    pub flt_update: bool,
344    /// Bit 6: the Reed-Solomon correction count has been updated.
345    pub rsed_update: bool,
346}
347
348impl UpdateFlags {
349    /// Constructs `UpdateFlags` from the `Update_0` flags, in bit order.
350    pub const fn new(
351        status_update: bool,
352        ced_update: bool,
353        rr_test: bool,
354        source_test_update: bool,
355        frl_start: bool,
356        flt_update: bool,
357        rsed_update: bool,
358    ) -> Self {
359        Self {
360            status_update,
361            ced_update,
362            rr_test,
363            source_test_update,
364            frl_start,
365            flt_update,
366            rsed_update,
367        }
368    }
369
370    /// These flags as a clear: every flag but `RR_Test`, which the source does not clear.
371    /// `scdc.clear_update_flags(flags.clearable())` acknowledges every flag just read.
372    pub const fn clearable(self) -> ClearableUpdateFlags {
373        ClearableUpdateFlags {
374            status_update: self.status_update,
375            ced_update: self.ced_update,
376            source_test_update: self.source_test_update,
377            frl_start: self.frl_start,
378            flt_update: self.flt_update,
379            rsed_update: self.rsed_update,
380        }
381    }
382}
383
384/// The `Update_0` flags a source may clear: every flag but `RR_Test` (bit 2).
385///
386/// Taken by [`Scdc::clear_update_flags`](crate::Scdc::clear_update_flags). Build it from
387/// flags just read with [`UpdateFlags::clearable`], or with [`new`](Self::new); the
388/// default clears nothing.
389///
390/// `RR_Test` (Read Request Test) has no field: culvert treats it as the one update flag the
391/// source must not clear. Only one reference states that rule (the Intel `xe` HDMI 2.1
392/// series: "Read Request Test is the only update flag the source cannot clear", rejecting
393/// it with `-EINVAL`), and another clears the bit regardless (Amlogic's HDMI 2.1
394/// transmitter). culvert follows the explicit rule because leaving a sink-owned test flag
395/// alone cannot break a sink's read-request test, while clearing it might. With a separate
396/// type, a clear that would include it cannot be written. The decision is provisional: see
397/// "RR_Test is not cleared" in `doc/architecture.md` for what would change it.
398#[non_exhaustive]
399#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
400pub struct ClearableUpdateFlags {
401    /// Bit 0: general status has changed.
402    pub status_update: bool,
403    /// Bit 1: CED counters have been updated.
404    pub ced_update: bool,
405    /// Bit 3: the sink has written `Source_Test_Configuration` (0x35).
406    pub source_test_update: bool,
407    /// Bit 4: link training passed.
408    pub frl_start: bool,
409    /// Bit 5: the per-lane link training pattern requests have changed.
410    pub flt_update: bool,
411    /// Bit 6: the Reed-Solomon correction count has been updated.
412    pub rsed_update: bool,
413}
414
415impl ClearableUpdateFlags {
416    /// Constructs `ClearableUpdateFlags` from the flags to clear, in bit order.
417    pub const fn new(
418        status_update: bool,
419        ced_update: bool,
420        source_test_update: bool,
421        frl_start: bool,
422        flt_update: bool,
423        rsed_update: bool,
424    ) -> Self {
425        Self {
426            status_update,
427            ced_update,
428            source_test_update,
429            frl_start,
430            flt_update,
431            rsed_update,
432        }
433    }
434}
435
436/// A 15-bit character error count decoded from an ERR_DET register pair.
437///
438/// The high byte's bit 7 is the validity flag consumed by [`CedCounters`];
439/// the counter occupies bits\[14:0\]. Values are always ≤ `0x7FFF`.
440#[derive(Debug, Clone, Copy, PartialEq, Eq)]
441pub struct CedCount(u16);
442
443impl CedCount {
444    /// Constructs a `CedCount`, masking to 15 bits.
445    pub fn new(raw: u16) -> Self {
446        Self(raw & 0x7FFF)
447    }
448
449    /// Returns the character error count.
450    pub fn value(self) -> u16 {
451        self.0
452    }
453}
454
455/// A 15-bit Reed-Solomon correction count decoded from `RS_Correction_L/H` (0x59/0x5A).
456///
457/// The high byte's bit 7 is the validity flag consumed by
458/// [`Scdc::read_rs_correction`](crate::Scdc::read_rs_correction); the counter occupies
459/// bits\[14:0\]. Values are always ≤ `0x7FFF`.
460#[derive(Debug, Clone, Copy, PartialEq, Eq)]
461pub struct RsCorrectionCount(u16);
462
463impl RsCorrectionCount {
464    /// Constructs an `RsCorrectionCount`, masking to 15 bits.
465    pub fn new(raw: u16) -> Self {
466        Self(raw & 0x7FFF)
467    }
468
469    /// Returns the Reed-Solomon correction count.
470    pub fn value(self) -> u16 {
471        self.0
472    }
473}
474
475/// Per-lane character error counts decoded from `ERR_DET` registers (0x50–0x55 for
476/// lanes 0–2, 0x57–0x58 for lane 3).
477///
478/// A lane's counter is `None` when its validity bit is not set. `lane3` is only
479/// populated in 4-lane FRL mode.
480#[non_exhaustive]
481#[derive(Debug, Clone, Copy, PartialEq, Eq)]
482pub struct CedCounters {
483    /// Character error count for lane 0, or `None` if the validity bit is not set.
484    pub lane0: Option<CedCount>,
485    /// Character error count for lane 1, or `None` if the validity bit is not set.
486    pub lane1: Option<CedCount>,
487    /// Character error count for lane 2, or `None` if the validity bit is not set.
488    pub lane2: Option<CedCount>,
489    /// Character error count for lane 3, or `None` if the validity bit is not set.
490    /// Always `None` in TMDS mode or 3-lane FRL mode.
491    pub lane3: Option<CedCount>,
492}
493
494impl CedCounters {
495    /// Constructs a `CedCounters`.
496    pub const fn new(
497        lane0: Option<CedCount>,
498        lane1: Option<CedCount>,
499        lane2: Option<CedCount>,
500        lane3: Option<CedCount>,
501    ) -> Self {
502        Self {
503            lane0,
504            lane1,
505            lane2,
506            lane3,
507        }
508    }
509}
510
511#[cfg(test)]
512mod tests {
513    use super::*;
514
515    #[test]
516    fn ced_count_masks_validity_bit() {
517        // The high byte's bit 7 (validity) must not bleed into the value.
518        assert_eq!(CedCount::new(0xFFFF).value(), 0x7FFF);
519        assert_eq!(CedCount::new(0x8000).value(), 0x0000);
520    }
521
522    #[test]
523    fn ced_count_preserves_15_bit_value() {
524        assert_eq!(CedCount::new(0x0000).value(), 0x0000);
525        assert_eq!(CedCount::new(0x0001).value(), 0x0001);
526        assert_eq!(CedCount::new(0x7FFF).value(), 0x7FFF);
527    }
528
529    #[test]
530    fn status_flags_new_field_order() {
531        // Each parameter maps to the named field for its Status_Flags_0 bit, in bit order.
532        let all = |f: StatusFlags| {
533            [
534                f.clock_detected,
535                f.ch0_locked,
536                f.ch1_locked,
537                f.ch2_locked,
538                f.ln3_locked,
539                f.flt_ready,
540                f.dsc_decode_fail,
541            ]
542        };
543        for i in 0..7 {
544            let mut args = [false; 7];
545            args[i] = true;
546            let f = StatusFlags::new(
547                args[0], args[1], args[2], args[3], args[4], args[5], args[6],
548            );
549            assert_eq!(all(f), args, "parameter {i}");
550        }
551    }
552
553    #[test]
554    fn ltp_req_from_value_gives_one_request_per_value() {
555        for value in 0u8..=0xF {
556            let request = LtpReq::from_value(value).unwrap();
557            assert_eq!(request.value(), value, "value {value:#x}");
558            assert_eq!(
559                matches!(request, LtpReq::Reserved(_)),
560                (0x9..=0xD).contains(&value),
561                "value {value:#x}"
562            );
563        }
564        assert_eq!(LtpReq::from_value(0x10), None);
565        assert_eq!(LtpReq::from_value(0xFF), None);
566    }
567
568    #[test]
569    fn ltp_req_from_nibble_decodes_every_value() {
570        for n in 0u8..=0xF {
571            let req = LtpReq::from_nibble(n);
572            assert_eq!(req.value(), n, "nibble {n:#x}");
573            assert_eq!(
574                matches!(req, LtpReq::Reserved(_)),
575                (0x9..=0xD).contains(&n),
576                "nibble {n:#x}"
577            );
578        }
579        // Only the low nibble is a request.
580        assert_eq!(LtpReq::from_nibble(0xF3), LtpReq::NyquistClock);
581    }
582
583    #[test]
584    fn ltp_requests_all_trained() {
585        assert!(
586            LtpRequests::new(LtpReq::None, LtpReq::None, LtpReq::None, LtpReq::None).all_trained()
587        );
588        assert!(
589            !LtpRequests::new(LtpReq::None, LtpReq::None, LtpReq::None, LtpReq::Lfsr0)
590                .all_trained()
591        );
592    }
593
594    #[test]
595    fn ced_counters_new_field_order() {
596        let a = CedCount::new(1);
597        let b = CedCount::new(2);
598        let c = CedCount::new(3);
599        let d = CedCount::new(4);
600        let counters = CedCounters::new(Some(a), Some(b), Some(c), Some(d));
601        assert_eq!(counters.lane0.unwrap().value(), 1);
602        assert_eq!(counters.lane1.unwrap().value(), 2);
603        assert_eq!(counters.lane2.unwrap().value(), 3);
604        assert_eq!(counters.lane3.unwrap().value(), 4);
605    }
606
607    #[test]
608    fn ced_counters_new_lane3_none() {
609        let counters = CedCounters::new(
610            Some(CedCount::new(0)),
611            Some(CedCount::new(0)),
612            Some(CedCount::new(0)),
613            None,
614        );
615        assert!(counters.lane3.is_none());
616    }
617
618    #[test]
619    fn scrambler_status_new() {
620        assert!(ScramblerStatus::new(true).scrambling_active);
621        assert!(!ScramblerStatus::new(false).scrambling_active);
622    }
623
624    #[test]
625    fn update_flags_new_field_order() {
626        // Each parameter maps to the named field for its Update_0 bit, in bit order.
627        let all = |f: UpdateFlags| {
628            [
629                f.status_update,
630                f.ced_update,
631                f.rr_test,
632                f.source_test_update,
633                f.frl_start,
634                f.flt_update,
635                f.rsed_update,
636            ]
637        };
638        for bit in 0..7 {
639            let mut args = [false; 7];
640            args[bit] = true;
641            let f = UpdateFlags::new(
642                args[0], args[1], args[2], args[3], args[4], args[5], args[6],
643            );
644            assert_eq!(all(f), args, "parameter {bit}");
645        }
646    }
647
648    #[test]
649    fn clearable_update_flags_new_is_in_bit_order() {
650        let all = |f: ClearableUpdateFlags| {
651            [
652                f.status_update,
653                f.ced_update,
654                f.source_test_update,
655                f.frl_start,
656                f.flt_update,
657                f.rsed_update,
658            ]
659        };
660        for bit in 0..6 {
661            let mut args = [false; 6];
662            args[bit] = true;
663            let f = ClearableUpdateFlags::new(args[0], args[1], args[2], args[3], args[4], args[5]);
664            assert_eq!(all(f), args, "parameter {bit}");
665        }
666    }
667
668    #[test]
669    fn clearable_keeps_every_flag_but_rr_test() {
670        let read = UpdateFlags::new(true, true, true, true, true, true, true);
671        assert_eq!(
672            read.clearable(),
673            ClearableUpdateFlags::new(true, true, true, true, true, true)
674        );
675        let rr_test_only = UpdateFlags::new(false, false, true, false, false, false, false);
676        assert_eq!(rr_test_only.clearable(), ClearableUpdateFlags::default());
677    }
678}