pub struct DecodedInsn {
pub op: PioOp,
pub delay: u8,
pub sideset: Option<u8>,
}
pub enum PioOp {
Jmp {
condition: u8,
address: u8,
},
Wait {
polarity: bool,
source: u8,
index: u8,
},
In {
source: u8,
bit_count: u8,
},
Out {
destination: u8,
bit_count: u8,
},
Push {
if_full: bool,
block: bool,
},
Pull {
if_empty: bool,
block: bool,
},
Mov {
destination: u8,
op: u8,
source: u8,
},
Irq {
clear: bool,
wait: bool,
index: u8,
},
Set {
destination: u8,
data: u8,
},
}
pub fn decode(insn: u16, pinctrl: u32, execctrl: u32) -> DecodedInsn {
let opcode = (insn >> 13) & 0x7;
let delay_sideset = ((insn >> 8) & 0x1F) as u8;
let operand = (insn & 0xFF) as u8;
let sideset_count = (((pinctrl >> 29) & 7) as u8).min(5);
let delay_bits = 5 - sideset_count;
let side_en = (execctrl >> 30) & 1 != 0;
let (sideset, delay) = if sideset_count == 0 {
(None, delay_sideset)
} else {
let delay_mask = (1u8 << delay_bits) - 1;
let delay = delay_sideset & delay_mask;
let ss_raw = delay_sideset >> delay_bits;
let sideset = if side_en {
let enable = (ss_raw >> (sideset_count - 1)) & 1 != 0;
if enable {
let ss_val_bits = sideset_count - 1;
let ss_val = ss_raw & ((1u8 << ss_val_bits) - 1);
Some(ss_val)
} else {
None
}
} else {
Some(ss_raw)
};
(sideset, delay)
};
let op = match opcode {
0 => PioOp::Jmp {
condition: (operand >> 5) & 0x7,
address: operand & 0x1F,
},
1 => PioOp::Wait {
polarity: (operand >> 7) & 1 != 0,
source: (operand >> 5) & 0x3,
index: operand & 0x1F,
},
2 => {
let bit_count = operand & 0x1F;
PioOp::In {
source: (operand >> 5) & 0x7,
bit_count: if bit_count == 0 { 32 } else { bit_count },
}
}
3 => {
let bit_count = operand & 0x1F;
PioOp::Out {
destination: (operand >> 5) & 0x7,
bit_count: if bit_count == 0 { 32 } else { bit_count },
}
}
4 => {
let direction = (operand >> 7) & 1 != 0;
let if_x = (operand >> 6) & 1 != 0;
let block = (operand >> 5) & 1 != 0;
if direction {
PioOp::Pull {
if_empty: if_x,
block,
}
} else {
PioOp::Push {
if_full: if_x,
block,
}
}
}
5 => PioOp::Mov {
destination: (operand >> 5) & 0x7,
op: (operand >> 3) & 0x3,
source: operand & 0x7,
},
6 => PioOp::Irq {
clear: (operand >> 6) & 1 != 0,
wait: (operand >> 5) & 1 != 0,
index: operand & 0x1F,
},
_ => PioOp::Set {
destination: (operand >> 5) & 0x7,
data: operand & 0x1F,
},
};
DecodedInsn { op, delay, sideset }
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn side_en_enable_bit_set_yields_sideset() {
let pinctrl = 2u32 << 29;
let execctrl = 1u32 << 30;
let d = decode(0xFD23, pinctrl, execctrl);
assert_eq!(d.delay, 5, "delay is the bottom 3 bits");
assert_eq!(d.sideset, Some(1), "side-set value bit = 1");
}
#[test]
fn side_en_enable_bit_clear_yields_none() {
let pinctrl = 2u32 << 29;
let execctrl = 1u32 << 30; let d = decode(0xEA20, pinctrl, execctrl);
assert_eq!(d.delay, 2);
assert!(d.sideset.is_none(), "enable bit clear must yield None");
}
#[test]
fn in_with_bc_zero_decodes_as_32() {
let d = decode(0x4020, 0x1400_0000, 0x0001_F000);
match d.op {
PioOp::In { source, bit_count } => {
assert_eq!(source, 1);
assert_eq!(bit_count, 32, "bc field=0 decodes as 32");
}
_ => panic!("expected IN"),
}
}
#[test]
fn out_with_bc_zero_decodes_as_32() {
let d = decode(0x6000, 0x1400_0000, 0x0001_F000);
match d.op {
PioOp::Out {
destination,
bit_count,
} => {
assert_eq!(destination, 0);
assert_eq!(bit_count, 32);
}
_ => panic!("expected OUT"),
}
}
#[test]
fn pull_decodes_from_direction_bit() {
let d = decode(0x80A0, 0x1400_0000, 0x0001_F000);
match d.op {
PioOp::Pull { if_empty, block } => {
assert!(!if_empty);
assert!(block);
}
_ => panic!("expected PULL"),
}
}
#[test]
fn mov_and_irq_opcodes_decode() {
let d = decode(0xA049, 0x1400_0000, 0x0001_F000);
match d.op {
PioOp::Mov {
destination,
op,
source,
} => {
assert_eq!(destination, 2);
assert_eq!(op, 1);
assert_eq!(source, 1);
}
_ => panic!("expected MOV"),
}
let d = decode(0xC065, 0x1400_0000, 0x0001_F000);
match d.op {
PioOp::Irq { clear, wait, index } => {
assert!(clear);
assert!(wait);
assert_eq!(index, 5);
}
_ => panic!("expected IRQ"),
}
}
}