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}