use crate::architecture::arm::{ArmError, DapError, DapProbe};
use crate::probe::{DebugProbeError, JtagAccess, JtagSequence, WireProtocol};
use bitvec::field::BitField;
use bitvec::vec::BitVec;
use probe_rs_target::ScanChainElement;
#[derive(Debug, PartialEq)]
pub enum DefaultProtocol {
CJtag,
Jtag,
}
#[derive(Debug)]
pub struct Icepick<'a> {
probe: &'a mut dyn JtagAccess,
}
const IR_ROUTER: u32 = 0x02;
const IR_IDCODE: u32 = 0x04;
const IR_CONNECT: u32 = 0x07;
const IR_BYPASS: u32 = 0x3F;
const IR_LEN_IN_BITS: u8 = 6;
const SD_TAP_DEFAULT: u32 = (1 << 20) | (1 << 8) | (1 << 6) | (1 << 3);
const SYSCTRL_DEFAULT: u32 = 0x80;
#[repr(u32)]
#[derive(Clone, Copy, Debug)]
enum IcepickRoutingRegister {
Sysctrl = 1,
SdTap(u8),
}
#[derive(PartialEq)]
enum JtagOperation {
ShiftDr = 0x03,
ShiftIr = 0x04,
}
impl From<IcepickRoutingRegister> for u32 {
fn from(value: IcepickRoutingRegister) -> Self {
match value {
IcepickRoutingRegister::Sysctrl => 1u32,
IcepickRoutingRegister::SdTap(tap) => 0b010_0000 | tap as u32,
}
}
}
impl<'a> Icepick<'a> {
pub fn new(
interface: &'a mut dyn DapProbe,
protocol: DefaultProtocol,
) -> Result<Self, ArmError> {
let probe = interface.try_as_jtag_probe().ok_or_else(|| {
tracing::error!("Couldn't get probe as JtagAccess");
ArmError::Dap(DapError::Protocol(WireProtocol::Jtag))
})?;
let mut this = Icepick { probe };
this.probe.tap_reset().map_err(ArmError::Probe)?;
if protocol == DefaultProtocol::CJtag {
this.ctag_to_jtag()?;
}
let tap_count = this
.scan_jtag()
.inspect_err(|e| tracing::error!("Unable to scan JTAG: {e}"))?;
if tap_count == 0 {
tracing::error!("No TAP devices found!");
return Err(ArmError::Probe(DebugProbeError::TargetNotFound));
}
this.probe
.set_scan_chain(&[ScanChainElement {
name: Some("ICEPICK".to_owned()),
ir_len: Some(IR_LEN_IN_BITS),
}])
.inspect_err(|e| tracing::error!("Couldn't set scan chain: {e}"))?;
tracing::info!("Selecting target 0");
this.probe
.select_target(0)
.inspect_err(|e| tracing::error!("Unable to select target 0: {e}"))?;
this.probe
.write_register(IR_CONNECT, &[0x89], 8)
.inspect_err(|e| tracing::error!("Couldn't write IR_CONNECT: {e}"))?;
this.icepick_router(IcepickRoutingRegister::Sysctrl, SYSCTRL_DEFAULT)?;
Ok(this)
}
fn scan_jtag(&mut self) -> Result<u8, ArmError> {
let mut tap_count = 0;
tracing::trace!("Scan of JTAG bus:");
for tms in [
true, false, false, ] {
self.raw_jtag_cycle(tms, false)?;
}
for index in 0..255 {
let mut data = BitVec::new();
for _ in 0..32 {
data.push(false);
}
let idcode = self.probe.shift_raw_sequence(JtagSequence {
tdo_capture: true,
tms: false,
data,
})?;
let idcode = idcode.load_be::<u32>();
tracing::trace!(" TAP index {index}: 0x{idcode:08x}");
if idcode == 0 {
break;
}
tap_count += 1;
}
for tms in [
true, true, false, ] {
self.raw_jtag_cycle(tms, false)?;
}
Ok(tap_count)
}
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.probe
.write_register(IR_ROUTER, &dr.to_le_bytes(), 32)?;
let result = self
.probe
.write_register(IR_ROUTER, &0u32.to_le_bytes(), 32)?;
tracing::trace!(
"Value of {register:02x?}: 0x{:08x}",
result.load_le::<u32>()
);
Ok(())
}
pub(crate) fn select_tap(&mut self, secondary_tap: u8, tap_name: &str) -> Result<(), ArmError> {
tracing::trace!("Selecting secondary tap {secondary_tap}");
self.icepick_router(IcepickRoutingRegister::SdTap(secondary_tap), SD_TAP_DEFAULT)?;
self.probe.set_idle_cycles(3)?;
self.probe.read_register(IR_BYPASS, 1)?;
self.probe.set_idle_cycles(0)?;
self.probe
.set_expected_scan_chain(&[
ScanChainElement {
name: Some(tap_name.to_owned()),
ir_len: Some(4),
},
ScanChainElement {
name: Some("ICEPICK".to_owned()),
ir_len: Some(IR_LEN_IN_BITS),
},
])
.inspect_err(|e| tracing::error!("Couldn't set scan chain: {e}"))?;
tracing::trace!("Should be active now");
self.scan_jtag()?;
Ok(())
}
fn raw_jtag_cycle(&mut self, tms: bool, tdi: bool) -> Result<(), ArmError> {
let mut data = BitVec::new();
data.push(tdi);
self.probe.shift_raw_sequence(JtagSequence {
tdo_capture: false,
tms,
data,
})?;
Ok(())
}
fn zero_bit_scan(&mut self) -> Result<(), ArmError> {
for tms in [
true, false, true, false, true, true, false, ] {
self.raw_jtag_cycle(tms, true)?;
}
Ok(())
}
fn shift_reg(&mut self, cycles: u8, reg: u64, action: JtagOperation) -> Result<(), ArmError> {
self.raw_jtag_cycle(true, true)?;
if action == JtagOperation::ShiftIr {
self.raw_jtag_cycle(true, true)?;
}
for tms in [
false, true, false, true, false, ] {
self.raw_jtag_cycle(tms, true)?;
}
for i in 0..cycles {
let tms = i == cycles - 1;
let reg_masked = (reg & (0x01 << u64::from(i))) != 0;
self.raw_jtag_cycle(tms, reg_masked)?;
}
self.raw_jtag_cycle(true, true)?;
self.raw_jtag_cycle(false, true)?;
Ok(())
}
fn shift_ir(&mut self, ir: u64) -> Result<(), ArmError> {
self.shift_reg(IR_LEN_IN_BITS, ir, JtagOperation::ShiftIr)?;
Ok(())
}
fn shift_dr(&mut self, cycles: u8, reg: u64) -> Result<(), ArmError> {
self.shift_reg(cycles, reg, JtagOperation::ShiftDr)?;
Ok(())
}
pub(crate) fn ctag_to_jtag(&mut self) -> Result<(), ArmError> {
self.shift_ir(IR_BYPASS.into())?;
self.zero_bit_scan()?;
self.zero_bit_scan()?;
self.shift_dr(1, 0xff)?;
self.shift_dr(2, 0xff)?;
self.shift_dr(9, 0xff)?;
self.shift_ir(IR_BYPASS.into())?;
self.shift_ir(IR_IDCODE.into())?;
Ok(())
}
}