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