1use derive_more::{Debug, Display};
2use itertools::Itertools;
3
4#[derive(Debug, Clone, Copy, PartialEq, Eq, Display)]
6pub struct Major(pub u8);
7
8#[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#[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#[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#[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 #[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}