autd3_driver/firmware/
version.rs

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