use crate::architecture::arm::ArmError;
use crate::architecture::arm::communication_interface::DapProbe;
#[derive(Debug)]
pub struct Icepick<'a> {
interface: &'a mut dyn DapProbe,
jtag_state: JtagState,
}
const IR_ROUTER: u64 = 0x02;
const IR_CONNECT: u64 = 0x07;
const IR_BYPASS: u64 = 0x3F;
const IR_LEN_IN_BITS: u8 = 6;
const SD_TAP_DEFAULT: u32 = (1 << 20) | (1 << 8) | (1 << 3);
const SD_TAP_WAIT_IN_RESET: u32 = 1 << 14;
const SD_TAP_RELEASE_FROM_WIR: u32 = 1 << 17;
const SYSCTRL_DEFAULT: u32 = 0x80;
const SYSCTRL_RESET: u32 = 1;
#[derive(PartialEq, Debug)]
enum JtagState {
RunTestIdle = 0x1,
SelectDrScan = 0x2,
}
#[derive(PartialEq)]
enum JtagOperation {
ShiftDr = 0x03,
ShiftIr = 0x04,
}
#[repr(u32)]
enum IcepickRoutingRegister {
Sysctrl = 1,
SdTap(u8),
}
impl From<IcepickRoutingRegister> for u32 {
fn from(value: IcepickRoutingRegister) -> Self {
match value {
IcepickRoutingRegister::Sysctrl => 1u32,
IcepickRoutingRegister::SdTap(tap) => 0b010_0000 | tap as u32,
}
}
}
fn set_n_bits(x: u32) -> u64 {
u64::checked_shl(1, x).unwrap_or(0).wrapping_sub(1)
}
impl<'a> Icepick<'a> {
pub fn new(interface: &'a mut dyn DapProbe) -> Result<Self, ArmError> {
for _ in 0..5 {
interface.jtag_sequence(1, true, 0)?;
}
interface.jtag_sequence(1, false, 0)?;
Ok(Icepick {
interface,
jtag_state: JtagState::RunTestIdle,
})
}
pub fn initialized(interface: &'a mut dyn DapProbe) -> Result<Self, ArmError> {
Ok(Icepick {
interface,
jtag_state: JtagState::RunTestIdle,
})
}
pub fn catch_reset(&mut self, secondary_tap: u8) -> Result<(), ArmError> {
self.icepick_router(
IcepickRoutingRegister::SdTap(secondary_tap),
SD_TAP_DEFAULT | SD_TAP_WAIT_IN_RESET,
)
}
pub fn release_from_reset(&mut self, secondary_tap: u8) -> Result<(), ArmError> {
self.icepick_router(
IcepickRoutingRegister::SdTap(secondary_tap),
SD_TAP_DEFAULT | SD_TAP_RELEASE_FROM_WIR,
)
}
fn zero_bit_scan(&mut self) -> Result<(), ArmError> {
self.interface.jtag_sequence(1, true, 0x01)?;
self.interface.jtag_sequence(1, false, 0x01)?;
self.interface.jtag_sequence(1, true, 0x01)?;
self.interface.jtag_sequence(1, false, 0x01)?;
self.interface.jtag_sequence(1, true, 0x01)?;
self.interface.jtag_sequence(1, true, 0x01)?;
self.interface.jtag_sequence(1, false, 0x01)?;
Ok(())
}
fn shift_reg(
&mut self,
cycles: u8,
reg: u64,
action: JtagOperation,
end_state: JtagState,
) -> Result<(), ArmError> {
if self.jtag_state == JtagState::RunTestIdle {
self.interface.swj_sequence(1, 1)?;
}
if action == JtagOperation::ShiftIr {
self.interface.swj_sequence(1, 1)?;
}
self.interface.swj_sequence(5, 0b01010)?;
for i in 0..cycles {
let tms = i == cycles - 1;
let reg_masked = (reg & (0x01 << u64::from(i))) != 0;
self.interface
.jtag_sequence(1, tms, u64::from(reg_masked))?;
}
self.interface.swj_sequence(
2,
if end_state == JtagState::SelectDrScan {
0b11
} else {
0b01
},
)?;
self.jtag_state = end_state;
Ok(())
}
fn shift_ir(&mut self, ir: u64, end_state: JtagState) -> Result<(), ArmError> {
self.shift_reg(IR_LEN_IN_BITS, ir, JtagOperation::ShiftIr, end_state)?;
Ok(())
}
fn shift_dr(&mut self, cycles: u8, reg: u64, end_state: JtagState) -> Result<(), ArmError> {
self.shift_reg(cycles, reg, JtagOperation::ShiftDr, end_state)?;
Ok(())
}
fn icepick_router(
&mut self,
register: IcepickRoutingRegister,
payload: u32,
) -> Result<(), ArmError> {
let rw = 1;
let dr = (rw << 31) | (u32::from(register) << 24) | (payload & 0xFFFFFF);
self.shift_ir(IR_ROUTER, JtagState::SelectDrScan)?;
self.shift_dr(32, dr as u64, JtagState::SelectDrScan)?;
Ok(())
}
pub(crate) fn select_tap(&mut self, secondary_tap: u8) -> Result<(), ArmError> {
tracing::trace!("Selecting seconary tap {secondary_tap}");
self.shift_ir(IR_CONNECT, JtagState::SelectDrScan)?;
self.shift_dr(8, 0x89, JtagState::SelectDrScan)?;
self.icepick_router(IcepickRoutingRegister::Sysctrl, SYSCTRL_DEFAULT)?;
self.icepick_router(IcepickRoutingRegister::SdTap(secondary_tap), SD_TAP_DEFAULT)?;
self.shift_ir(IR_BYPASS, JtagState::RunTestIdle)?;
self.interface.jtag_sequence(10, false, set_n_bits(10))?;
Ok(())
}
pub(crate) fn sysreset(&mut self) -> Result<(), ArmError> {
self.icepick_router(
IcepickRoutingRegister::Sysctrl,
SYSCTRL_DEFAULT | SYSCTRL_RESET,
)
}
pub(crate) fn ctag_to_jtag(&mut self) -> Result<(), ArmError> {
self.shift_ir(IR_BYPASS, JtagState::RunTestIdle)?;
self.zero_bit_scan()?;
self.zero_bit_scan()?;
self.shift_dr(1, 0x01, JtagState::RunTestIdle)?;
self.shift_dr(2, set_n_bits(2), JtagState::RunTestIdle)?;
self.shift_dr(9, set_n_bits(9), JtagState::RunTestIdle)?;
self.shift_ir(IR_BYPASS, JtagState::RunTestIdle)?;
Ok(())
}
pub(crate) fn bypass(&mut self) -> Result<(), ArmError> {
self.shift_ir(IR_BYPASS, JtagState::RunTestIdle)
}
}