1#![doc = include_str!("../README.md")]
2use itertools::Itertools;
3
4#[cfg(feature = "ral")]
5mod macros;
6
7#[derive(Default, Clone, Debug, Eq, PartialEq)]
9pub enum Command {
10 #[default]
12 Nop,
13 Write(Write),
15 Check(Check),
17}
18
19#[derive(Default, Clone, Debug, Eq, PartialEq)]
33pub struct Write {
34 pub width: Width,
36 pub op: WriteOp,
38 pub address: u32,
40 pub value: u32,
41}
42
43#[derive(Default, Clone, Debug, Eq, PartialEq)]
45pub struct Check {
46 pub width: Width,
48 pub cond: CheckCond,
50 pub address: u32,
52 pub mask: u32,
54 pub count: Option<u32>,
60}
61
62#[derive(Default, Debug, Copy, Clone, Eq, PartialEq)]
73#[repr(u8)]
74pub enum Width {
75 B1 = 0b001u8,
77 B2 = 0b010u8,
79 #[default]
81 B4 = 0b100u8,
82}
83
84impl Width {
85 pub const fn from_num_bytes(num_bytes: usize) -> Self {
92 match num_bytes {
93 1 => Self::B1,
94 2 => Self::B2,
95 4 => Self::B4,
96 _ => panic!("invalid width"),
97 }
98 }
99
100 pub const fn from_reg<T>(_: &T) -> Self {
113 Self::from_num_bytes(core::mem::size_of::<T>())
114 }
115}
116
117#[derive(Default, Debug, Copy, Clone, Eq, PartialEq)]
119#[repr(u8)]
120pub enum WriteOp {
121 #[default]
123 Write = 0b00_000u8,
124 Clear = 0b01_000u8,
126 Set = 0b11_000u8,
128}
129
130#[derive(Default, Debug, Copy, Clone, Eq, PartialEq)]
132#[repr(u8)]
133pub enum CheckCond {
134 #[default]
135 AllClear = 0b00_000u8,
137 AnyClear = 0b01_000u8,
139 AllSet = 0b10_000u8,
141 AnySet = 0b11_000u8,
143}
144
145fn dcd_header(byte_len: u16) -> [u8; 4] {
148 let mut header = [0xD2, 0x00, 0x00, 0x41];
149 header[1..=2].copy_from_slice(&byte_len.to_be_bytes()[0..=1]);
150 header
151}
152
153const NOP_HEADER: [u8; 4] = [0xC0, 0x00, 0x04, 0x00];
154
155impl Write {
156 fn byte_len(group_size: usize) -> u16 {
157 let n = 4 + group_size * 8;
158 assert!(n <= u16::MAX as usize);
159 n as u16
160 }
161 fn header(&self, group_size: usize) -> [u8; 4] {
162 let mut header = [0xCC, 0x00, 0x00, self.width as u8 | self.op as u8];
163 header[1..=2].copy_from_slice(&Self::byte_len(group_size).to_be_bytes()[0..=1]);
164 header
165 }
166 fn payload(&self) -> [u8; 8] {
167 let mut payload = [0u8; 8];
168 payload[0..4].copy_from_slice(&self.address.to_be_bytes()[0..4]);
169 payload[4..8].copy_from_slice(&self.value.to_be_bytes()[0..4]);
170 payload
171 }
172}
173
174impl Check {
175 fn byte_len(&self) -> u16 {
176 if self.count.is_some() {
177 16
178 } else {
179 12
180 }
181 }
182 fn header(&self) -> [u8; 4] {
183 let mut header = [0xCF, 0x00, 0x00, self.width as u8 | self.cond as u8];
184 header[1..=2].copy_from_slice(&self.byte_len().to_be_bytes()[0..=1]);
185 header
186 }
187 fn payload(&self) -> [u8; 8] {
188 let mut payload = [0u8; 8];
189 payload[0..4].copy_from_slice(&self.address.to_be_bytes()[0..4]);
190 payload[4..8].copy_from_slice(&self.mask.to_be_bytes()[0..4]);
191 payload
192 }
193 fn payload_with_count(&self) -> [u8; 12] {
194 let mut payload = [0u8; 12];
195 payload[0..4].copy_from_slice(&self.address.to_be_bytes()[0..4]);
196 payload[4..8].copy_from_slice(&self.mask.to_be_bytes()[0..4]);
197 payload[8..12].copy_from_slice(&self.count.unwrap().to_be_bytes()[0..4]);
198 payload
199 }
200}
201
202fn group_key(index: usize, command: &Command) -> (usize, Width, WriteOp) {
203 match command {
204 &Command::Write(Write {
205 width, op, ..
206 }) => (usize::MAX, width, op),
207 _ => (index, Width::default(), WriteOp::default()),
208 }
209}
210
211pub fn serialize(mut w: impl std::io::Write, commands: &[Command]) -> std::io::Result<usize> {
225 if commands.is_empty() {
226 return Ok(0);
227 }
228 let mut byte_len: usize = 4; for (_, mut group) in &commands
231 .into_iter()
232 .enumerate()
233 .group_by(|&(index, command)| group_key(index, command))
234 {
235 let Some((_, head)) = group.next() else { continue; };
236 match head {
237 Command::Nop => {
238 byte_len += 4;
239 }
240 Command::Check(check) => {
241 byte_len += check.byte_len() as usize;
242 }
243 Command::Write(_) => {
244 byte_len += Write::byte_len(group.count() + 1) as usize;
245 }
246 }
247 }
248 if byte_len > u16::MAX as usize {
249 return Err(std::io::Error::new(
250 std::io::ErrorKind::InvalidInput,
251 "DCD byte length too large",
252 ));
253 }
254 w.write_all(&dcd_header(byte_len as u16))?;
255 for (_, mut group) in &commands
256 .into_iter()
257 .enumerate()
258 .group_by(|&(index, command)| group_key(index, command))
259 {
260 let Some((_, head)) = group.next() else { continue; };
261 match head {
262 Command::Nop => {
263 w.write_all(&NOP_HEADER)?;
264 }
265 Command::Check(check) => {
266 w.write_all(&check.header())?;
267 if check.count.is_some() {
268 w.write_all(&check.payload_with_count())?;
269 } else {
270 w.write_all(&check.payload())?;
271 }
272 }
273 Command::Write(write) => {
274 let (counter, rest) = group.tee();
275 w.write_all(&write.header(counter.count() + 1))?;
276 w.write_all(&write.payload())?;
277 for (_, command) in rest {
278 if let Command::Write(write) = command {
279 w.write_all(&write.payload())?;
280 }
281 }
282 }
283 }
284 }
285 Ok(byte_len)
286}
287
288#[cfg(test)]
289mod tests {
290 use super::*;
291
292 #[test]
293 #[rustfmt::skip]
294 fn serialize_simple() {
295 let mut buf = vec![];
296 let byte_len = serialize(
297 &mut buf,
298 &[
299 Command::Nop,
300 Command::Write(Write {
301 width: Width::B4,
302 op: WriteOp::Write,
303 address: 0x01234567,
304 value: 0xdeadbeef,
305 }),
306 Command::Check(Check {
307 width: Width::B2,
308 cond: CheckCond::AnySet,
309 address: 0x89abcdef,
310 mask: 0x55aa55aa,
311 count: Some(16),
312 }),
313 Command::Check(Check {
314 width: Width::B1,
315 cond: CheckCond::AnyClear,
316 address: 0x89abcdef,
317 mask: 0x55aa55aa,
318 count: None,
319 }),
320 ],
321 )
322 .expect("IO failure");
323 assert_eq!(byte_len, 48);
324 assert_eq!(
325 &buf,
326 &[
327 0xD2, 0, 48, 0x41,
329 0xC0, 0x00, 0x04, 0x00,
331 0xCC, 0, 12, 0x04, 0x01, 0x23, 0x45, 0x67, 0xde, 0xad, 0xbe, 0xef,
333 0xCF, 0, 16, 0x1a, 0x89, 0xab, 0xcd, 0xef, 0x55, 0xaa, 0x55, 0xaa, 0, 0, 0, 16,
335 0xCF, 0, 12, 0x09, 0x89, 0xab, 0xcd, 0xef, 0x55, 0xaa, 0x55, 0xaa,
337 ]
338 );
339 }
340
341 #[test]
342 #[rustfmt::skip]
343 fn serialize_merge() {
344 let mut buf = vec![];
345 let byte_len = serialize(
346 &mut buf,
347 &[
348 Command::Write(Write {
350 width: Width::B4,
351 op: WriteOp::Write,
352 address: 0x01234567,
353 value: 0xdeadbeef,
354 }),
355 Command::Write(Write {
356 width: Width::B4,
357 op: WriteOp::Write,
358 address: 0x89abcdef,
359 value: 0x13370000,
360 }),
361 Command::Write(Write {
362 width: Width::B4,
363 op: WriteOp::Write,
364 address: 0x55aa55aa,
365 value: 0xaa55aa55,
366 }),
367 Command::Nop,
368 Command::Write(Write {
370 width: Width::B4,
371 op: WriteOp::Write,
372 address: 0x89abcdef,
373 value: 0x13370000,
374 }),
375 Command::Write(Write {
377 width: Width::B2,
378 op: WriteOp::Write,
379 address: 0x89abcdef,
380 value: 0x13370000,
381 }),
382 Command::Write(Write {
384 width: Width::B4,
385 op: WriteOp::Write,
386 address: 0x55aa55aa,
387 value: 0xaa55aa55,
388 }),
389 ],
390 ).expect("IO failure");
391 assert_eq!(byte_len, 72);
392 assert_eq!(
393 &buf,
394 &[
395 0xD2, 0, 72, 0x41,
397 0xCC, 0, 28, 0x04,
399 0x01, 0x23, 0x45, 0x67, 0xde, 0xad, 0xbe, 0xef,
401 0x89, 0xab, 0xcd, 0xef, 0x13, 0x37, 0x00, 0x00,
403 0x55, 0xaa, 0x55, 0xaa, 0xaa, 0x55, 0xaa, 0x55,
405 0xC0, 0x00, 0x04, 0x00,
407 0xCC, 0, 12, 0x04,
409 0x89, 0xab, 0xcd, 0xef, 0x13, 0x37, 0x00, 0x00,
411 0xCC, 0, 12, 0x02,
413 0x89, 0xab, 0xcd, 0xef, 0x13, 0x37, 0x00, 0x00,
415 0xCC, 0, 12, 0x04,
417 0x55, 0xaa, 0x55, 0xaa, 0xaa, 0x55, 0xaa, 0x55,
419 ]
420 );
421 }
422}