1#![allow(clippy::manual_range_contains)]
3#![allow(clippy::unusual_byte_groupings)]
4#![allow(clippy::upper_case_acronyms)]
5
6use pio_core::{
7 InSource, Instruction, InstructionOperands, IrqIndexMode, JmpCondition, MovDestination,
8 MovOperation, MovRxIndex, MovSource, OutDestination, ProgramWithDefines, SetDestination,
9 WaitSource,
10};
11
12use std::collections::HashMap;
13
14mod parser {
15 #![allow(clippy::all)]
16 #![allow(unused)]
17 include!(concat!(env!("OUT_DIR"), "/pio.rs"));
18}
19
20#[derive(Debug)]
21pub(crate) enum Value<'input> {
22 I32(i32),
23 Symbol(&'input str),
24 Add(Box<Value<'input>>, Box<Value<'input>>),
25 Sub(Box<Value<'input>>, Box<Value<'input>>),
26 Mul(Box<Value<'input>>, Box<Value<'input>>),
27 Div(Box<Value<'input>>, Box<Value<'input>>),
28 Neg(Box<Value<'input>>),
29 Rev(Box<Value<'input>>),
30}
31
32impl Value<'_> {
33 fn reify(&self, state: &ProgramState) -> i32 {
34 match self {
35 Value::I32(v) => *v,
36 Value::Symbol(s) => state.resolve(s),
37 Value::Add(a, b) => a.reify(state) + b.reify(state),
38 Value::Sub(a, b) => a.reify(state) - b.reify(state),
39 Value::Mul(a, b) => a.reify(state) * b.reify(state),
40 Value::Div(a, b) => a.reify(state) / b.reify(state),
41 Value::Neg(a) => -a.reify(state),
42 Value::Rev(a) => a.reify(state).reverse_bits(),
43 }
44 }
45}
46
47#[derive(Debug)]
48pub(crate) enum Line<'input> {
49 Directive(ParsedDirective<'input>),
50 Instruction(ParsedInstruction<'input>),
51 Label { public: bool, name: &'input str },
52}
53
54#[derive(Debug)]
55pub(crate) enum ParsedDirective<'input> {
56 Define {
57 public: bool,
58 name: &'input str,
59 value: Value<'input>,
60 },
61 Origin(Value<'input>),
62 SideSet {
63 value: Value<'input>,
64 opt: bool,
65 pindirs: bool,
66 },
67 WrapTarget,
68 Wrap,
69 #[allow(unused)]
70 LangOpt(&'input str),
71}
72
73#[derive(Debug)]
74pub(crate) struct ParsedInstruction<'input> {
75 operands: ParsedOperands<'input>,
76 side_set: Option<Value<'input>>,
77 delay: Value<'input>,
78}
79
80impl ParsedInstruction<'_> {
81 fn reify(&self, state: &ProgramState) -> Instruction {
82 Instruction {
83 operands: self.operands.reify(state),
84 side_set: self.side_set.as_ref().map(|s| s.reify(state) as u8),
85 delay: self.delay.reify(state) as u8,
86 }
87 }
88}
89
90#[derive(Clone, Copy, Debug)]
91pub(crate) enum ParsedMovDestination {
92 PINS,
93 X,
94 Y,
95 PINDIRS,
96 EXEC,
97 PC,
98 ISR,
99 OSR,
100 RXFIFOY,
101 RXFIFO0,
102 RXFIFO1,
103 RXFIFO2,
104 RXFIFO3,
105}
106
107#[derive(Debug)]
108enum MovDestInternal {
109 Mov(MovDestination),
110 Fifo(MovRxIndex),
111}
112
113impl From<ParsedMovDestination> for MovDestInternal {
114 fn from(value: ParsedMovDestination) -> Self {
115 match value {
116 ParsedMovDestination::PINS => MovDestInternal::Mov(MovDestination::PINS),
117 ParsedMovDestination::X => MovDestInternal::Mov(MovDestination::X),
118 ParsedMovDestination::Y => MovDestInternal::Mov(MovDestination::Y),
119 ParsedMovDestination::PINDIRS => MovDestInternal::Mov(MovDestination::PINDIRS),
120 ParsedMovDestination::EXEC => MovDestInternal::Mov(MovDestination::EXEC),
121 ParsedMovDestination::PC => MovDestInternal::Mov(MovDestination::PC),
122 ParsedMovDestination::ISR => MovDestInternal::Mov(MovDestination::ISR),
123 ParsedMovDestination::OSR => MovDestInternal::Mov(MovDestination::OSR),
124 ParsedMovDestination::RXFIFOY => MovDestInternal::Fifo(MovRxIndex::RXFIFOY),
125 ParsedMovDestination::RXFIFO0 => MovDestInternal::Fifo(MovRxIndex::RXFIFO0),
126 ParsedMovDestination::RXFIFO1 => MovDestInternal::Fifo(MovRxIndex::RXFIFO1),
127 ParsedMovDestination::RXFIFO2 => MovDestInternal::Fifo(MovRxIndex::RXFIFO2),
128 ParsedMovDestination::RXFIFO3 => MovDestInternal::Fifo(MovRxIndex::RXFIFO3),
129 }
130 }
131}
132
133#[derive(Clone, Copy, Debug)]
134pub(crate) enum ParsedMovSource {
135 PINS,
136 X,
137 Y,
138 NULL,
139 STATUS,
140 ISR,
141 OSR,
142 RXFIFOY,
143 RXFIFO0,
144 RXFIFO1,
145 RXFIFO2,
146 RXFIFO3,
147}
148
149#[derive(Debug)]
150enum MovSrcInternal {
151 Mov(MovSource),
152 Fifo(MovRxIndex),
153}
154
155impl From<ParsedMovSource> for MovSrcInternal {
156 fn from(value: ParsedMovSource) -> Self {
157 match value {
158 ParsedMovSource::PINS => MovSrcInternal::Mov(MovSource::PINS),
159 ParsedMovSource::X => MovSrcInternal::Mov(MovSource::X),
160 ParsedMovSource::Y => MovSrcInternal::Mov(MovSource::Y),
161 ParsedMovSource::NULL => MovSrcInternal::Mov(MovSource::NULL),
162 ParsedMovSource::STATUS => MovSrcInternal::Mov(MovSource::STATUS),
163 ParsedMovSource::ISR => MovSrcInternal::Mov(MovSource::ISR),
164 ParsedMovSource::OSR => MovSrcInternal::Mov(MovSource::OSR),
165 ParsedMovSource::RXFIFOY => MovSrcInternal::Fifo(MovRxIndex::RXFIFOY),
166 ParsedMovSource::RXFIFO0 => MovSrcInternal::Fifo(MovRxIndex::RXFIFO0),
167 ParsedMovSource::RXFIFO1 => MovSrcInternal::Fifo(MovRxIndex::RXFIFO1),
168 ParsedMovSource::RXFIFO2 => MovSrcInternal::Fifo(MovRxIndex::RXFIFO2),
169 ParsedMovSource::RXFIFO3 => MovSrcInternal::Fifo(MovRxIndex::RXFIFO3),
170 }
171 }
172}
173
174#[derive(Debug)]
175pub(crate) enum ParsedOperands<'input> {
176 JMP {
177 condition: JmpCondition,
178 address: Value<'input>,
179 },
180 WAIT {
181 polarity: Value<'input>,
182 source: WaitSource,
183 index: Value<'input>,
184 relative: bool,
185 },
186 IN {
187 source: InSource,
188 bit_count: Value<'input>,
189 },
190 OUT {
191 destination: OutDestination,
192 bit_count: Value<'input>,
193 },
194 PUSH {
195 if_full: bool,
196 block: bool,
197 },
198 PULL {
199 if_empty: bool,
200 block: bool,
201 },
202 MOV {
203 destination: ParsedMovDestination,
204 op: MovOperation,
205 source: ParsedMovSource,
206 },
207 IRQ {
208 clear: bool,
209 wait: bool,
210 index: Value<'input>,
211 index_mode: IrqIndexMode,
212 },
213 SET {
214 destination: SetDestination,
215 data: Value<'input>,
216 },
217}
218
219impl ParsedOperands<'_> {
220 fn reify(&self, state: &ProgramState) -> InstructionOperands {
221 match self {
222 ParsedOperands::JMP { condition, address } => InstructionOperands::JMP {
223 condition: *condition,
224 address: address.reify(state) as u8,
225 },
226 ParsedOperands::WAIT {
227 polarity,
228 source,
229 index,
230 relative,
231 } => InstructionOperands::WAIT {
232 polarity: polarity.reify(state) as u8,
233 source: *source,
234 index: index.reify(state) as u8,
235 relative: *relative,
236 },
237 ParsedOperands::IN { source, bit_count } => InstructionOperands::IN {
238 source: *source,
239 bit_count: bit_count.reify(state) as u8,
240 },
241 ParsedOperands::OUT {
242 destination,
243 bit_count,
244 } => InstructionOperands::OUT {
245 destination: *destination,
246 bit_count: bit_count.reify(state) as u8,
247 },
248 ParsedOperands::PUSH { if_full, block } => InstructionOperands::PUSH {
249 if_full: *if_full,
250 block: *block,
251 },
252 ParsedOperands::PULL { if_empty, block } => InstructionOperands::PULL {
253 if_empty: *if_empty,
254 block: *block,
255 },
256 ParsedOperands::MOV {
257 destination,
258 op,
259 source,
260 } => {
261 let source_internal = (*source).into();
262 let dest_internal = (*destination).into();
263 match (source_internal, dest_internal) {
264 (MovSrcInternal::Mov(MovSource::ISR), MovDestInternal::Fifo(fifo_index)) => {
265 InstructionOperands::MOVTORX { fifo_index }
266 }
267 (
268 MovSrcInternal::Fifo(fifo_index),
269 MovDestInternal::Mov(MovDestination::OSR),
270 ) => InstructionOperands::MOVFROMRX { fifo_index },
271 (MovSrcInternal::Mov(s), MovDestInternal::Mov(d)) => InstructionOperands::MOV {
272 destination: d,
273 op: *op,
274 source: s,
275 },
276 (d, s) => panic!("Illegal Mov src/dest combination: {:?} {:?}", d, s),
277 }
278 }
279 ParsedOperands::IRQ {
280 clear,
281 wait,
282 index,
283 index_mode,
284 } => InstructionOperands::IRQ {
285 clear: *clear,
286 wait: *wait,
287 index: index.reify(state) as u8,
288 index_mode: *index_mode,
289 },
290 ParsedOperands::SET { destination, data } => InstructionOperands::SET {
291 destination: *destination,
292 data: {
293 let arg = data.reify(state);
294 if arg < 0 || arg > 0x1f {
295 panic!("SET argument out of range: {}", arg);
296 }
297 arg as u8
298 },
299 },
300 }
301 }
302}
303
304#[derive(Debug, Default)]
305struct FileState {
306 defines: HashMap<String, (bool, i32)>,
307}
308
309#[derive(Debug)]
310struct ProgramState<'a> {
311 file_state: &'a mut FileState,
312 defines: HashMap<String, (bool, i32)>,
313}
314
315impl<'a> ProgramState<'a> {
316 fn new(file_state: &'a mut FileState) -> Self {
317 ProgramState {
318 file_state,
319 defines: HashMap::new(),
320 }
321 }
322
323 fn resolve(&self, name: &str) -> i32 {
324 self.defines
325 .get(name)
326 .or_else(|| self.file_state.defines.get(name))
327 .unwrap_or_else(|| panic!("Unknown label {}", name))
328 .1
329 }
330
331 fn public_defines(&self) -> HashMap<String, i32> {
332 let mut p = HashMap::new();
333 for (name, (public, value)) in &self.file_state.defines {
334 if *public {
335 p.insert(name.to_string(), *value);
336 }
337 }
338 for (name, (public, value)) in &self.defines {
339 if *public {
340 p.insert(name.to_string(), *value);
341 }
342 }
343 p
344 }
345}
346
347pub type ParseError<'input> = lalrpop_util::ParseError<usize, parser::Token<'input>, &'static str>;
348
349pub struct Parser<const PROGRAM_SIZE: usize>;
350
351impl<const PROGRAM_SIZE: usize> Parser<PROGRAM_SIZE> {
352 pub fn parse_file(
355 source: &str,
356 ) -> Result<HashMap<String, ProgramWithDefines<HashMap<String, i32>, PROGRAM_SIZE>>, ParseError>
357 {
358 match parser::FileParser::new().parse(source) {
359 Ok(f) => {
360 let mut state = FileState::default();
361
362 let fake_prog_state = ProgramState::new(&mut state);
364 for d in f.0 {
365 if let ParsedDirective::Define {
366 public,
367 name,
368 value,
369 } = d.0
370 {
371 fake_prog_state
372 .file_state
373 .defines
374 .insert(name.to_string(), (public, value.reify(&fake_prog_state)));
375 }
376 }
377
378 Ok(f.1
379 .iter()
380 .map(|p| {
381 let program_name = p.0.to_string();
382 (program_name, Parser::process(&p.1, &mut state))
383 })
384 .collect())
385 }
386 Err(e) => Err(e),
387 }
388 }
389
390 pub fn parse_program(
392 source: &str,
393 ) -> Result<ProgramWithDefines<HashMap<String, i32>, PROGRAM_SIZE>, ParseError> {
394 match parser::ProgramParser::new().parse(source) {
395 Ok(p) => Ok(Parser::process(&p, &mut FileState::default())),
396 Err(e) => Err(e),
397 }
398 }
399
400 fn process(
401 p: &[Line],
402 file_state: &mut FileState,
403 ) -> ProgramWithDefines<HashMap<String, i32>, PROGRAM_SIZE> {
404 let mut state = ProgramState::new(file_state);
405
406 let mut side_set_size = 0;
411 let mut side_set_opt = false;
412 let mut side_set_pindirs = false;
413 let mut origin = None;
414 let mut wrap_target = None;
415 let mut wrap = None;
416 let mut instr_index = 0;
417 for line in p {
418 match line {
419 Line::Instruction(..) => {
420 instr_index += 1;
421 }
422 Line::Label { public, name } => {
423 state
424 .defines
425 .insert(name.to_string(), (*public, instr_index as i32));
426 }
427 Line::Directive(d) => match d {
428 ParsedDirective::Define {
429 public,
430 name,
431 value,
432 } => {
433 state
434 .defines
435 .insert(name.to_string(), (*public, value.reify(&state)));
436 }
437 ParsedDirective::Origin(value) => {
438 origin = Some(value.reify(&state) as u8);
439 }
440 ParsedDirective::SideSet {
441 value,
442 opt,
443 pindirs,
444 } => {
445 assert!(instr_index == 0);
446 side_set_size = value.reify(&state) as u8;
447 side_set_opt = *opt;
448 side_set_pindirs = *pindirs;
449 }
450 ParsedDirective::WrapTarget => {
451 assert!(wrap_target.is_none());
452 wrap_target = Some(instr_index);
453 }
454 ParsedDirective::Wrap => {
455 assert!(wrap.is_none());
456 wrap = Some(instr_index - 1);
457 }
458 _ => {}
459 },
460 }
461 }
462
463 let mut a = pio_core::Assembler::new_with_side_set(pio_core::SideSet::new(
464 side_set_opt,
465 side_set_size,
466 side_set_pindirs,
467 ));
468
469 for line in p {
472 if let Line::Instruction(i) = line {
473 a.instructions.push(i.reify(&state));
474 }
475 }
476
477 let program = a.assemble_program().set_origin(origin);
478
479 let program = match (wrap, wrap_target) {
480 (Some(wrap_source), Some(wrap_target)) => program.set_wrap(pio_core::Wrap {
481 source: wrap_source,
482 target: wrap_target,
483 }),
484 (None, None) => program,
485 _ => panic!(
486 "must define either both or neither of wrap and wrap_target, but not only one of them"
487 ),
488 };
489
490 ProgramWithDefines {
491 program,
492 public_defines: state.public_defines(),
493 }
494 }
495}
496
497#[test]
498fn test() {
499 let p = Parser::<32>::parse_program(
500 "
501 label:
502 pull
503 out pins, 1
504 jmp label
505 ",
506 )
507 .unwrap();
508
509 assert_eq!(
510 &p.program.code[..],
511 &[
512 0b100_00000_101_00000, 0b011_00000_000_00001, 0b000_00000_000_00000, ]
517 );
518 assert_eq!(p.program.origin, None);
519 assert_eq!(
520 p.program.wrap,
521 pio_core::Wrap {
522 source: 2,
523 target: 0,
524 }
525 );
526}
527
528#[test]
529fn test_rp2350() {
530 let p = Parser::<32>::parse_program(
531 "
532 label:
533 mov osr, rxfifo[0]
534 mov rxfifo[1], isr
535 mov pins, isr
536 mov osr, x
537 jmp label
538 ",
539 )
540 .unwrap();
541
542 assert_eq!(
543 &p.program.code[..],
544 &[
545 0b100_00000_1001_1_000, 0b100_00000_0001_1_001, 0b101_00000_000_00_110, 0b101_00000_111_00_001, 0b000_00000_000_00000, ]
552 );
553 assert_eq!(p.program.origin, None);
554 assert_eq!(
555 p.program.wrap,
556 pio_core::Wrap {
557 source: 4,
558 target: 0,
559 }
560 );
561}
562
563#[test]
564fn test_side_set() {
565 let p = Parser::<32>::parse_program(
566 "
567 .side_set 1 opt
568 .origin 5
569
570 label:
571 pull
572 .wrap_target
573 out pins, 1
574 .wrap
575 jmp label side 1
576 ",
577 )
578 .unwrap();
579
580 assert_eq!(
581 &p.program.code[..],
582 &[
583 0b100_00000_101_00000, 0b011_00000_000_00001, 0b000_11000_000_00000, ]
588 );
589 assert_eq!(p.program.origin, Some(5));
590 assert_eq!(
591 p.program.wrap,
592 pio_core::Wrap {
593 source: 1,
594 target: 1,
595 }
596 );
597}
598
599#[test]
600#[should_panic(expected = "Unknown label some_unknown_label")]
601fn test_unknown_label() {
602 let _ = Parser::<32>::parse_program(
603 "
604 jmp some_unknown_label
605 ",
606 )
607 .unwrap();
608}