autd3_driver/firmware/fpga/
fpga_state.rs1use crate::firmware::fpga::Segment;
2
3use autd3_core::link::RxMessage;
4use getset::CopyGetters;
5
6const THERMAL_ASSERT_BIT: u8 = 1 << 0;
7const CURRENT_MOD_SEGMENT_BIT: u8 = 1 << 1;
8const CURRENT_STM_SEGMENT_BIT: u8 = 1 << 2;
9const CURRENT_GAIN_SEGMENT_BIT: u8 = 1 << 2;
10const IS_GAIN_MODE_BIT: u8 = 1 << 3;
11const READS_FPGA_STATE_ENABLED: u8 = 1 << 7;
12
13#[repr(C)]
15#[derive(Debug, Clone, Copy, PartialEq, Eq, CopyGetters)]
16pub struct FPGAState {
17 #[doc(hidden)]
18 #[getset(get_copy = "pub")]
19 pub(crate) state: u8,
20}
21
22impl FPGAState {
23 #[must_use]
25 pub const fn is_thermal_assert(&self) -> bool {
26 (self.state & THERMAL_ASSERT_BIT) != 0
27 }
28
29 #[must_use]
31 pub const fn current_mod_segment(&self) -> Segment {
32 match self.state & CURRENT_MOD_SEGMENT_BIT {
33 0 => Segment::S0,
34 _ => Segment::S1,
35 }
36 }
37
38 #[must_use]
40 pub const fn current_stm_segment(&self) -> Option<Segment> {
41 if !self.is_stm_mode() {
42 return None;
43 }
44 match self.state & CURRENT_STM_SEGMENT_BIT {
45 0 => Some(Segment::S0),
46 _ => Some(Segment::S1),
47 }
48 }
49
50 #[must_use]
52 pub const fn current_gain_segment(&self) -> Option<Segment> {
53 if !self.is_gain_mode() {
54 return None;
55 }
56 match self.state & CURRENT_GAIN_SEGMENT_BIT {
57 0 => Some(Segment::S0),
58 _ => Some(Segment::S1),
59 }
60 }
61
62 #[must_use]
64 pub const fn is_gain_mode(&self) -> bool {
65 (self.state & IS_GAIN_MODE_BIT) != 0
66 }
67
68 #[must_use]
70 pub const fn is_stm_mode(&self) -> bool {
71 !self.is_gain_mode()
72 }
73
74 #[doc(hidden)]
75 #[must_use]
76 pub fn from_rx(msg: &RxMessage) -> Option<Self> {
77 if msg.data() & READS_FPGA_STATE_ENABLED != 0 {
78 Some(FPGAState { state: msg.data() })
79 } else {
80 None
81 }
82 }
83}
84
85#[cfg(test)]
86mod tests {
87 use std::mem::size_of;
88
89 use super::*;
90
91 #[test]
92 fn size() {
93 assert_eq!(1, size_of::<FPGAState>());
94 assert_eq!(0, std::mem::offset_of!(FPGAState, state));
95 }
96
97 #[rstest::rstest]
98 #[test]
99 #[case(false, 0b0)]
100 #[case(true, 0b1)]
101 fn is_thermal_assert(#[case] expected: bool, #[case] state: u8) {
102 assert_eq!(expected, FPGAState { state }.is_thermal_assert());
103 }
104
105 #[rstest::rstest]
106 #[test]
107 #[case(Segment::S0, 0b00)]
108 #[case(Segment::S1, 0b10)]
109 fn current_mod_segment(#[case] expected: Segment, #[case] state: u8) {
110 assert_eq!(expected, FPGAState { state }.current_mod_segment());
111 }
112
113 #[rstest::rstest]
114 #[test]
115 #[case(false, 0b0000)]
116 #[case(true, 0b1000)]
117 fn is_gain_mode(#[case] expected: bool, #[case] state: u8) {
118 assert_eq!(expected, FPGAState { state }.is_gain_mode());
119 }
120
121 #[rstest::rstest]
122 #[test]
123 #[case(false, 0b1000)]
124 #[case(true, 0b0000)]
125 fn is_stm_mode(#[case] expected: bool, #[case] state: u8) {
126 assert_eq!(expected, FPGAState { state }.is_stm_mode());
127 }
128
129 #[rstest::rstest]
130 #[test]
131 #[case(None, 0b1000)]
132 #[case(Some(Segment::S0), 0b0000)]
133 #[case(Some(Segment::S1), 0b0100)]
134 fn current_stm_segment(#[case] expected: Option<Segment>, #[case] state: u8) {
135 assert_eq!(expected, FPGAState { state }.current_stm_segment());
136 }
137
138 #[rstest::rstest]
139 #[test]
140 #[case(None, 0b0000)]
141 #[case(Some(Segment::S0), 0b1000)]
142 #[case(Some(Segment::S1), 0b1100)]
143 fn current_gain_segment(#[case] expected: Option<Segment>, #[case] state: u8) {
144 assert_eq!(expected, FPGAState { state }.current_gain_segment());
145 }
146}