1use anyhow::{Result, bail, ensure};
6use std::collections::HashMap;
7use std::iter::Peekable;
8use std::ops::Mul;
9use std::time::Duration;
10
11use crate::io::gpio::{BitbangEntry, DacBangEntry};
12
13#[derive(Debug, Eq, PartialEq)]
14enum Token<'a> {
15 Numeric(&'a str),
16 Alphabetic(&'a str),
17 Quoted(&'a str),
18
19 Await,
20 Linear,
21
22 LParen,
23 RParen,
24}
25
26fn get_token<'a>(
27 input: &'a str,
28 iter: &mut Peekable<std::str::CharIndices<'a>>,
29) -> Result<Option<Token<'a>>> {
30 let (token_start, first_char) = loop {
32 match iter.peek() {
33 Some((_, ch)) if ch.is_whitespace() => {
34 iter.next();
35 }
36 Some((idx, ch)) => break (*idx, *ch),
37 None => return Ok(None),
38 }
39 };
40 iter.next();
41 if first_char.is_numeric() || first_char == '.' {
42 let token_end = loop {
43 match iter.peek() {
44 Some((_, ch)) if ch.is_numeric() || *ch == '.' => {
45 iter.next();
46 }
47 Some((idx, _)) => break *idx,
48 None => break input.len(),
49 }
50 };
51 Ok(Some(Token::Numeric(&input[token_start..token_end])))
52 } else if first_char.is_alphabetic() {
53 let token_end = loop {
54 match iter.peek() {
55 Some((_, ch)) if ch.is_alphabetic() => {
56 iter.next();
57 }
58 Some((idx, _)) => break *idx,
59 None => break input.len(),
60 }
61 };
62 match &input[token_start..token_end] {
63 "await" => Ok(Some(Token::Await)),
64 "linear" => Ok(Some(Token::Linear)),
65 other => Ok(Some(Token::Alphabetic(other))),
66 }
67 } else if first_char == '\'' {
68 let token_end = loop {
69 match iter.next() {
70 Some((_, '\'')) => {
71 if let Some((idx, _)) = iter.peek() {
72 break *idx;
73 } else {
74 break input.len();
75 }
76 }
77 Some(_) => (),
78 None => bail!("Unterminated string"),
79 }
80 };
81 Ok(Some(Token::Quoted(&input[token_start..token_end])))
82 } else if first_char == '\"' {
83 let token_end = loop {
84 match iter.next() {
85 Some((_, '\"')) => {
86 if let Some((idx, _)) = iter.peek() {
87 break *idx;
88 } else {
89 break input.len();
90 }
91 }
92 Some(_) => (),
93 None => bail!("Unterminated string"),
94 }
95 };
96 Ok(Some(Token::Quoted(&input[token_start..token_end])))
97 } else if first_char == '(' {
98 Ok(Some(Token::LParen))
99 } else if first_char == ')' {
100 Ok(Some(Token::RParen))
101 } else {
102 bail!("Unexpected character `{}`", first_char);
103 }
104}
105
106fn get_all_tokens(input: &str) -> Result<Vec<Token<'_>>> {
107 let mut char_indices = input.char_indices().peekable();
108 let mut all_tokens = Vec::new();
109 loop {
110 let Some(token) = get_token(input, &mut char_indices)? else {
111 return Ok(all_tokens);
112 };
113 all_tokens.push(token);
114 }
115}
116
117pub fn parse_clock_frequency(input: &str) -> Result<Duration> {
120 let all_tokens = get_all_tokens(input)?;
121 let tokens: &[Token] = &all_tokens;
122 match tokens {
123 [Token::Numeric(num), Token::Alphabetic(time_unit)] => Ok(match *time_unit {
124 "ns" => Duration::from_nanos(num.parse().unwrap()),
125 "us" => Duration::from_secs_f64(num.parse::<f64>().unwrap() / 1000000.0),
126 "ms" => Duration::from_secs_f64(num.parse::<f64>().unwrap() / 1000.0),
127 "s" => Duration::from_secs_f64(num.parse().unwrap()),
128 "Hz" => Duration::from_secs(1).div_f64(num.parse().unwrap()),
129 "kHz" => Duration::from_secs(1).div_f64(num.parse::<f64>().unwrap() * 1000.0),
130 "MHz" => Duration::from_secs(1).div_f64(num.parse::<f64>().unwrap() * 1000000.0),
131 _ => bail!("Unknown unit: {}", time_unit),
132 }),
133 _ => bail!("Parse error"),
134 }
135}
136
137pub fn parse_delay(num: &str, time_unit: &str, clock: Duration) -> Result<u32> {
138 if time_unit == "ticks" {
139 let Ok(ticks) = num.parse::<u32>() else {
140 bail!("Delay must use integer number of ticks, try increasing clock frequency");
141 };
142 ensure!(
143 ticks > 0,
144 "Zero length delay requested: {} {}",
145 num,
146 time_unit
147 );
148 return Ok(ticks);
149 }
150
151 let duration = match time_unit {
154 "us" => num.parse::<f64>().unwrap() / 1000000.0,
155 "ms" => num.parse::<f64>().unwrap() / 1000.0,
156 "s" => num.parse::<f64>().unwrap(),
157 unit => bail!("Unrecognized time unit: '{}'", unit),
158 };
159 ensure!(
160 duration > 0.0,
161 "Zero length delay requested: {} {}",
162 num,
163 time_unit
164 );
165 let duration_in_ticks = duration / clock.as_secs_f64();
166 ensure!(
167 duration_in_ticks <= u32::MAX as f64,
168 "Requested delay exceeds range, try lower clock frequency",
169 );
170 let closest_ticks = duration_in_ticks.round() as u32;
171 let actual_duration = clock.mul(closest_ticks);
172 let ratio = actual_duration.as_secs_f64() / duration;
173 ensure!(
174 (0.99..=1.01).contains(&ratio),
175 "Requested delay cannot be approximated to within 1%, try increasing clock frequency",
176 );
177 Ok(closest_ticks)
178}
179
180#[allow(clippy::type_complexity)]
185pub fn parse_sequence<'a, 'wr, 'rd>(
186 input: &'a str,
187 num_pins: usize,
188 clock: Duration,
189 accumulator_rd: &'rd mut Vec<u8>,
190 accumulator_wr: &'wr mut Vec<u8>,
191) -> Result<(Box<[BitbangEntry<'rd, 'wr>]>, HashMap<&'a str, usize>)> {
192 ensure!(
193 num_pins > 0,
194 "Must specify at least one GPIO pin for bitbanging"
195 );
196 let all_tokens = get_all_tokens(input)?;
197
198 let mut token_map: HashMap<&'a str, usize> = HashMap::new();
199
200 let mut needed_bytes = 0usize;
202 let mut last_token_was_capture = false;
203 let mut tokens: &[Token] = &all_tokens;
204 loop {
205 match tokens {
206 [Token::Await, Token::LParen, rest @ ..] => {
207 ensure!(
208 !last_token_was_capture,
209 "Capturing GPIO samples only supported immediately preceeding output, not `await`. (Consider repeating the previous output values before the `await`.)",
210 );
211 tokens = rest;
212 loop {
213 match tokens {
214 [Token::Numeric(_), rest @ ..] => {
215 tokens = rest;
216 }
217 [Token::Alphabetic(_), rest @ ..] => {
218 tokens = rest;
219 }
220 [Token::RParen, rest @ ..] => {
221 tokens = rest;
222 break;
223 }
224 _ => {
225 bail!("Mismatched parenthesis");
226 }
227 }
228 }
229 last_token_was_capture = false;
230 }
231 [Token::Numeric(_), Token::Alphabetic(_), rest @ ..] => {
232 ensure!(
233 !last_token_was_capture,
234 "Capturing GPIO samples only supported immediately preceeding output, not with delay. (Consider repeating the previous output values before the delay.)",
235 );
236 tokens = rest;
237 last_token_was_capture = false;
238 }
239 [Token::Numeric(bits), rest @ ..] => {
240 ensure!(
241 bits.len() % num_pins == 0,
242 "Unexpected number of bits {}, should be multiple of the number of pins {}",
243 bits.len(),
244 num_pins,
245 );
246 needed_bytes += bits.len() / num_pins;
247 tokens = rest;
248 last_token_was_capture = false;
249 }
250 [Token::Quoted(identifier), rest @ ..] => {
251 token_map.insert(&identifier[1..identifier.len() - 1], needed_bytes);
252 tokens = rest;
253 last_token_was_capture = true;
254 }
255 [] => break,
256 _ => bail!("Parse error"),
257 }
258 }
259 accumulator_wr.clear();
260 accumulator_wr.resize(needed_bytes, 0u8);
261 accumulator_rd.clear();
262 accumulator_rd.resize(needed_bytes, 0u8);
263 let mut slice_wr: &'wr mut [u8] = accumulator_wr;
264 let mut slice_rd: &'rd mut [u8] = accumulator_rd;
265
266 let mut result = Vec::new();
268 let mut tokens: &[Token] = &all_tokens;
269 loop {
270 match tokens {
271 [Token::Await, Token::LParen, rest @ ..] => {
272 tokens = rest;
273 let mut pattern = String::new();
274 loop {
275 match tokens {
276 [Token::Numeric(p), rest @ ..] => {
277 pattern = pattern + p;
278 tokens = rest;
279 }
280 [Token::Alphabetic(p), rest @ ..] => {
281 pattern = pattern + p;
282 tokens = rest;
283 }
284 [Token::RParen, rest @ ..] => {
285 tokens = rest;
286 break;
287 }
288 _ => {
289 bail!("Mismatched parenthesis");
290 }
291 }
292 }
293 let pattern_str = pattern.as_bytes();
294 let mut mask = 0u8;
295 let mut pattern = 0u8;
296 for (i, ch) in pattern_str.iter().enumerate() {
297 match pattern_str[i] {
298 b'0' => {
299 mask |= 1 << i;
300 }
301 b'1' => {
302 pattern |= 1 << i;
303 mask |= 1 << i;
304 }
305 b'x' | b'X' => (),
306 _ => bail!("Unexpected character in await pattern: `{}`", ch),
307 }
308 }
309 ensure!(mask != 0, "Pattern consisting of only x'es not allowed");
310 result.push(BitbangEntry::Await { mask, pattern });
311 }
312 [Token::Numeric(num), Token::Alphabetic(time_unit), rest @ ..] => {
313 result.push(BitbangEntry::Delay(parse_delay(num, time_unit, clock)?));
314 tokens = rest;
315 }
316 [Token::Numeric(bits), rest @ ..] => {
317 let samples = bits.len() / num_pins;
318 let (left_wr, right_wr) = slice_wr.split_at_mut(samples);
319 let (left_rd, right_rd) = slice_rd.split_at_mut(samples);
320
321 for (sample_no, item) in left_wr.iter_mut().enumerate() {
322 for pin_no in 0..num_pins {
323 match bits.as_bytes()[sample_no * num_pins + pin_no] {
324 b'0' => (),
325 b'1' => *item |= 1 << pin_no,
326 _ => bail!("Non-binary digit encountered in '{}'", bits),
327 }
328 }
329 }
330
331 result.push(BitbangEntry::Both(left_wr, left_rd));
332 slice_wr = right_wr;
333 slice_rd = right_rd;
334 tokens = rest;
335 }
336 [Token::Quoted(_), rest @ ..] => {
337 tokens = rest;
338 }
339 [] => break,
340 _ => bail!("Parse error"),
341 }
342 }
343
344 Ok((result.into(), token_map))
345}
346
347pub fn parse_dac_sequence<'a, 'wr>(
352 input: &'a str,
353 num_pins: usize,
354 clock: Duration,
355 accumulator: &'wr mut Vec<f32>,
356) -> Result<Box<[DacBangEntry<'wr>]>> {
357 ensure!(
358 num_pins > 0,
359 "Must specify at least one analog pin for dac-banging"
360 );
361 let all_tokens = get_all_tokens(input)?;
362
363 let mut needed_entries = 0usize;
365 let mut run_start = 0usize;
366 let mut run_lengths: Vec<usize> = Vec::new();
367 let mut tokens: &[Token] = &all_tokens;
368 loop {
369 match tokens {
370 [Token::Numeric(_), Token::Alphabetic(_), rest @ ..]
371 | [
372 Token::Linear,
373 Token::LParen,
374 Token::Numeric(_),
375 Token::Alphabetic(_),
376 Token::RParen,
377 rest @ ..,
378 ] => {
379 if needed_entries > run_start {
380 let run_length = needed_entries - run_start;
381 ensure!(
382 run_length.is_multiple_of(num_pins),
383 "Unexpected number of samples {}, should be multiple of the number of pins {}",
384 run_length,
385 num_pins,
386 );
387 run_lengths.push(run_length);
388 }
389 run_start = needed_entries;
390 tokens = rest;
391 }
392 [Token::Numeric(_), rest @ ..] => {
393 needed_entries += 1;
394 tokens = rest;
395 }
396 [] => break,
397 _ => bail!("Parse error"),
398 }
399 }
400 if needed_entries > run_start {
401 let run_length = needed_entries - run_start;
402 ensure!(
403 run_length.is_multiple_of(num_pins),
404 "Unexpected number of samples {}, should be multiple of the number of pins {}",
405 run_length,
406 num_pins,
407 );
408 run_lengths.push(run_length);
409 }
410 accumulator.clear();
411 accumulator.resize(needed_entries, 0.0);
412 let mut slice_wr: &'wr mut [f32] = accumulator;
413 let mut run_lengths: &[usize] = &run_lengths;
414
415 let mut result = Vec::new();
417 let mut tokens: &[Token] = &all_tokens;
418 loop {
419 match tokens {
420 [Token::Numeric(num), Token::Alphabetic(time_unit), rest @ ..] => {
421 result.push(DacBangEntry::Delay(parse_delay(num, time_unit, clock)?));
422 tokens = rest;
423 }
424 [
425 Token::Linear,
426 Token::LParen,
427 Token::Numeric(num),
428 Token::Alphabetic(time_unit),
429 Token::RParen,
430 rest @ ..,
431 ] => {
432 result.push(DacBangEntry::Linear(parse_delay(num, time_unit, clock)?));
433 tokens = rest;
434 }
435 [Token::Numeric(_), ..] => {
436 let samples = run_lengths[0];
437 let (left_wr, right_wr) = slice_wr.split_at_mut(samples);
438
439 for i in 0..samples {
440 match tokens[i] {
441 Token::Numeric(voltage) => {
442 left_wr[i] = voltage.parse::<f32>()?;
443 }
444 _ => bail!("Parse error"),
445 }
446 }
447
448 result.push(DacBangEntry::Write(left_wr));
449 slice_wr = right_wr;
450 tokens = &tokens[samples..];
451 run_lengths = &run_lengths[1..];
452 }
453 [] => break,
454 _ => bail!("Parse error"),
455 }
456 }
457
458 Ok(result.into())
459}
460
461#[cfg(test)]
462mod tests {
463 use super::*;
464
465 #[test]
466 fn test_get_all_tokens() {
467 assert_eq!(
468 get_all_tokens("01 14ms 11").unwrap(),
470 [
471 Token::Numeric("01"),
472 Token::Numeric("14"),
473 Token::Alphabetic("ms"),
474 Token::Numeric("11"),
475 ],
476 );
477 assert_eq!(
478 get_all_tokens("\t01\r14\nms\x0B11\x0C").unwrap(),
480 [
481 Token::Numeric("01"),
482 Token::Numeric("14"),
483 Token::Alphabetic("ms"),
484 Token::Numeric("11"),
485 ],
486 );
487 assert_eq!(
488 get_all_tokens(" 0101010101010101010101010101010101010101010111 ").unwrap(),
490 [Token::Numeric(
491 "0101010101010101010101010101010101010101010111"
492 ),],
493 );
494 assert_eq!(
495 get_all_tokens("'s6' 17ms10's7'").unwrap(),
497 [
498 Token::Quoted("'s6'"),
499 Token::Numeric("17"),
500 Token::Alphabetic("ms"),
501 Token::Numeric("10"),
502 Token::Quoted("'s7'"),
503 ],
504 );
505 assert_eq!(
506 get_all_tokens("01 25ms await(x1) 100us 11").unwrap(),
508 [
509 Token::Numeric("01"),
510 Token::Numeric("25"),
511 Token::Alphabetic("ms"),
512 Token::Await,
513 Token::LParen,
514 Token::Alphabetic("x"),
515 Token::Numeric("1"),
516 Token::RParen,
517 Token::Numeric("100"),
518 Token::Alphabetic("us"),
519 Token::Numeric("11"),
520 ],
521 );
522 assert_eq!(
523 get_all_tokens("1.0 linear(1ms) 0.0").unwrap(),
525 [
526 Token::Numeric("1.0"),
527 Token::Linear,
528 Token::LParen,
529 Token::Numeric("1"),
530 Token::Alphabetic("ms"),
531 Token::RParen,
532 Token::Numeric("0.0"),
533 ],
534 );
535 }
536}