autd3_driver/firmware/
version.rs

1use derive_more::{Debug, Display};
2use itertools::Itertools;
3
4/// Major version number.
5#[derive(Debug, Clone, Copy, PartialEq, Eq, Display)]
6pub struct Major(pub u8);
7
8/// Minor version number.
9#[derive(Debug, Clone, Copy, PartialEq, Eq, Display)]
10pub struct Minor(pub u8);
11
12#[must_use]
13fn version_map(major: Major, minor: Minor) -> String {
14    let major = major.0;
15    let minor = minor.0;
16    match major {
17        0 => "older than v0.4".to_string(),
18        0x01..=0x06 => format!("v0.{}", major + 3),
19        0x0A..=0x15 => format!("v1.{}", major - 0x0A),
20        0x80..=0x89 => format!("v2.{}.{}", major - 0x80, minor),
21        0x8A..=0x8A => format!("v3.{}.{}", major - 0x8A, minor),
22        0x8B..=0x8C => format!("v4.{}.{}", major - 0x8B, minor),
23        0x8D..=0x8E => format!("v5.{}.{}", major - 0x8D, minor),
24        0x8F..=0x90 => format!("v6.{}.{}", major - 0x8F, minor),
25        0x91..=0x91 => format!("v7.{}.{}", major - 0x91, minor),
26        0x92..=0x92 => format!("v8.{}.{}", major - 0x92, minor),
27        0xA0..=0xA1 => format!("v9.{}.{}", major - 0xA0, minor),
28        0xA2..=0xA2 => format!("v10.{}.{}", major - 0xA2, minor),
29        _ => format!("unknown ({major})"),
30    }
31}
32
33/// FPGA firmware version.
34#[derive(Debug, Clone, Copy, PartialEq, Eq)]
35pub struct FPGAVersion {
36    #[doc(hidden)]
37    pub major: Major,
38    #[doc(hidden)]
39    pub minor: Minor,
40    #[doc(hidden)]
41    pub function_bits: u8,
42}
43
44impl FPGAVersion {
45    #[doc(hidden)]
46    pub const DYNAMIC_FREQ_BIT: u8 = 1 << 1;
47    #[doc(hidden)]
48    pub const ENABLED_EMULATOR_BIT: u8 = 1 << 7;
49
50    #[doc(hidden)]
51    #[must_use]
52    pub const fn dynamic_freq_enabled(&self) -> bool {
53        (self.function_bits & Self::DYNAMIC_FREQ_BIT) == Self::DYNAMIC_FREQ_BIT
54    }
55
56    #[doc(hidden)]
57    #[must_use]
58    pub const fn is_emulator(&self) -> bool {
59        (self.function_bits & Self::ENABLED_EMULATOR_BIT) == Self::ENABLED_EMULATOR_BIT
60    }
61}
62
63impl std::fmt::Display for FPGAVersion {
64    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
65        write!(f, "{}", version_map(self.major, self.minor))?;
66        let features = [
67            self.is_emulator().then_some("Emulator"),
68            self.dynamic_freq_enabled().then_some("DynamicFreq"),
69        ]
70        .iter()
71        .filter_map(Option::as_ref)
72        .join(", ");
73        if !features.is_empty() {
74            write!(f, " [{}]", features)?;
75        }
76        Ok(())
77    }
78}
79
80/// CPU firmware version.
81#[derive(Debug, Clone, Copy, PartialEq, Eq, Display)]
82#[display("{}", version_map(self.major, self.minor))]
83pub struct CPUVersion {
84    #[doc(hidden)]
85    pub major: Major,
86    #[doc(hidden)]
87    pub minor: Minor,
88}
89
90/// Firmware version.
91#[derive(Debug, Clone, Copy, PartialEq, Eq, Display)]
92#[display(
93    "{}: CPU = {}, FPGA = {}",
94    idx,
95    self.cpu,
96    self.fpga,
97)]
98#[debug("{}", self)]
99pub struct FirmwareVersion {
100    #[doc(hidden)]
101    pub idx: usize,
102    #[doc(hidden)]
103    pub cpu: CPUVersion,
104    #[doc(hidden)]
105    pub fpga: FPGAVersion,
106}
107
108impl FirmwareVersion {
109    #[doc(hidden)]
110    pub const LATEST_VERSION_NUM_MAJOR: Major = Major(0xA2);
111    #[doc(hidden)]
112    pub const LATEST_VERSION_NUM_MINOR: Minor = Minor(0x01);
113
114    #[doc(hidden)]
115    #[must_use]
116    pub const fn is_emulator(&self) -> bool {
117        self.fpga.is_emulator()
118    }
119
120    /// Gets the latest version.
121    #[must_use]
122    pub fn latest() -> String {
123        version_map(
124            Self::LATEST_VERSION_NUM_MAJOR,
125            Self::LATEST_VERSION_NUM_MINOR,
126        )
127    }
128}
129
130#[cfg(test)]
131mod tests {
132    use super::*;
133
134    #[rstest::rstest]
135    #[test]
136    #[case("older than v0.4", 0)]
137    #[case("v0.4", 1)]
138    #[case("v0.5", 2)]
139    #[case("v0.6", 3)]
140    #[case("v0.7", 4)]
141    #[case("v0.8", 5)]
142    #[case("v0.9", 6)]
143    #[case("unknown (7)", 7)]
144    #[case("unknown (8)", 8)]
145    #[case("unknown (9)", 9)]
146    #[case("v1.0", 10)]
147    #[case("v1.1", 11)]
148    #[case("v1.2", 12)]
149    #[case("v1.3", 13)]
150    #[case("v1.4", 14)]
151    #[case("v1.5", 15)]
152    #[case("v1.6", 16)]
153    #[case("v1.7", 17)]
154    #[case("v1.8", 18)]
155    #[case("v1.9", 19)]
156    #[case("v1.10", 20)]
157    #[case("v1.11", 21)]
158    #[case("v2.0.0", 128)]
159    #[case("v2.1.0", 129)]
160    #[case("v2.2.0", 130)]
161    #[case("v2.3.0", 131)]
162    #[case("v2.4.0", 132)]
163    #[case("v2.5.0", 133)]
164    #[case("v2.6.0", 134)]
165    #[case("v2.7.0", 135)]
166    #[case("v2.8.0", 136)]
167    #[case("v2.9.0", 137)]
168    #[case("v3.0.0", 138)]
169    #[case("v4.0.0", 139)]
170    #[case("v4.1.0", 140)]
171    #[case("v5.0.0", 141)]
172    #[case("v5.1.0", 142)]
173    #[case("v6.0.0", 143)]
174    #[case("v6.1.0", 144)]
175    #[case("v7.0.0", 145)]
176    #[case("v8.0.0", 146)]
177    #[case("v9.0.0", 160)]
178    #[case("v9.1.0", 161)]
179    #[case("v10.0.0", 162)]
180    #[case("unknown (147)", 147)]
181    fn version(#[case] expected: &str, #[case] num: u8) {
182        let info = FirmwareVersion {
183            idx: 0,
184            cpu: CPUVersion {
185                major: Major(num),
186                minor: Minor(0),
187            },
188            fpga: FPGAVersion {
189                major: Major(num),
190                minor: Minor(0),
191                function_bits: 0,
192            },
193        };
194        assert_eq!(expected, info.cpu.to_string());
195        assert_eq!(expected, info.fpga.to_string());
196    }
197
198    #[test]
199    fn latest() {
200        assert_eq!("v10.0.1", FirmwareVersion::latest());
201    }
202
203    #[rstest::rstest]
204    #[case(false, 0)]
205    #[case(true, FPGAVersion::ENABLED_EMULATOR_BIT)]
206    #[test]
207    fn is_emulator(#[case] expected: bool, #[case] function_bits: u8) {
208        assert_eq!(
209            expected,
210            FirmwareVersion {
211                idx: 0,
212                cpu: CPUVersion {
213                    major: Major(0),
214                    minor: Minor(0)
215                },
216                fpga: FPGAVersion {
217                    major: Major(0),
218                    minor: Minor(0),
219                    function_bits
220                }
221            }
222            .is_emulator()
223        );
224    }
225
226    #[rstest::rstest]
227    #[test]
228    #[case(
229        "0: CPU = v0.4, FPGA = v0.5",
230        FirmwareVersion {
231            idx: 0,
232            cpu: CPUVersion {
233                major: Major(1),
234                minor: Minor(3)
235            },
236            fpga: FPGAVersion {
237                major: Major(2),
238                minor: Minor(4),
239                function_bits: 0
240            }
241        }
242    )]
243    #[case(
244        "0: CPU = v0.4, FPGA = v0.5 [Emulator]",
245        FirmwareVersion {
246            idx: 0,
247            cpu: CPUVersion {
248                major: Major(1),
249                minor: Minor(3)
250            },
251            fpga: FPGAVersion {
252                major: Major(2),
253                minor: Minor(4),
254                function_bits: FPGAVersion::ENABLED_EMULATOR_BIT
255            }
256        }
257    )]
258    #[case(
259        "0: CPU = v0.4, FPGA = v0.5 [DynamicFreq]",
260        FirmwareVersion {
261            idx: 0,
262            cpu: CPUVersion {
263                major: Major(1),
264                minor: Minor(3)
265            },
266            fpga: FPGAVersion {
267                major: Major(2),
268                minor: Minor(4),
269                function_bits: FPGAVersion::DYNAMIC_FREQ_BIT
270            }
271        }
272    )]
273    #[case(
274        "0: CPU = v0.4, FPGA = v0.5 [Emulator, DynamicFreq]",
275        FirmwareVersion {
276            idx: 0,
277            cpu: CPUVersion {
278                major: Major(1),
279                minor: Minor(3)
280            },
281            fpga: FPGAVersion {
282                major: Major(2),
283                minor: Minor(4),
284                function_bits: FPGAVersion::ENABLED_EMULATOR_BIT | FPGAVersion::DYNAMIC_FREQ_BIT
285            }
286        }
287    )]
288    fn display(#[case] expected: &str, #[case] info: FirmwareVersion) {
289        assert_eq!(expected, format!("{}", info));
290        assert_eq!(expected, format!("{:?}", info));
291    }
292}