use super::icepick::{DefaultProtocol, Icepick};
use crate::MemoryMappedRegister;
use crate::architecture::arm::armv7ar::{
clear_hw_breakpoint, core_halted, get_hw_breakpoint, read_word_32, request_halt, run,
set_hw_breakpoint, wait_for_core_halted, write_word_32,
};
use crate::architecture::arm::core::armv7ar_debug_regs::Dbgdscr;
use crate::architecture::arm::dp::{DebugPortError, DpAddress};
use crate::architecture::arm::memory::ArmMemoryInterface;
use crate::architecture::arm::sequences::{ArmDebugSequence, ArmDebugSequenceError};
use crate::architecture::arm::{ArmError, DapProbe, Pins};
use crate::probe::WireProtocol;
use std::sync::Arc;
use std::sync::atomic::{AtomicBool, AtomicU32, Ordering};
use std::time::{Duration, Instant};
const TMS570_TAP_INDEX: u8 = 0;
const ECC_RAM_CLEAR_TIMEOUT: Duration = Duration::from_millis(100);
const RESET_TIMEOUT: Duration = Duration::from_millis(100);
const MINITGCR: u32 = 0xffff_ff5c;
const MSINENA: u32 = 0xffff_ff60;
const MSTCGSTAT: u32 = 0xffff_ff68;
const SYSECR: u32 = 0xffff_ffe0;
const SYSECR_RESET: u32 = 1 << 15;
const SYSESR: u32 = 0xffff_ffe4;
const SYSESR_RESET_MASK: u32 = 0x0000_1747;
const SYSESR_RESET_VALUE: u32 = 0x0000_0000;
struct TemporaryCore<'a> {
memory: &'a mut dyn ArmMemoryInterface,
base_address: u64,
}
impl<'a> TemporaryCore<'a> {
pub fn new(memory: &'a mut dyn ArmMemoryInterface, base_address: u64) -> Self {
TemporaryCore {
memory,
base_address,
}
}
pub fn ensure_itren(&mut self) -> Result<(), ArmError> {
let address = Dbgdscr::get_mmio_address_from_base(self.base_address)?;
let mut dbgdscr = Dbgdscr(self.memory.read_word_32(address)?);
if !dbgdscr.itren() {
dbgdscr.set_itren(true);
self.memory.write_word_32(address, dbgdscr.into())?;
}
Ok(())
}
pub fn write_word_32(&mut self, address: u32, data: u32) -> Result<(), ArmError> {
self.halted_access(|core| {
core.ensure_itren()?;
write_word_32(core.memory, core.base_address, address, data)
})
}
pub fn read_word_32(&mut self, address: u32) -> Result<u32, ArmError> {
self.halted_access(|core| {
core.ensure_itren()?;
read_word_32(core.memory, core.base_address, address)
})
}
pub fn wait_for_core_halted(&mut self, timeout: Duration) -> Result<(), ArmError> {
wait_for_core_halted(self.memory, self.base_address, timeout)
}
pub fn core_halted(&mut self) -> Result<bool, ArmError> {
core_halted(self.memory, self.base_address)
}
pub fn request_halt(&mut self) -> Result<(), ArmError> {
request_halt(self.memory, self.base_address)
}
pub fn run(&mut self) -> Result<(), ArmError> {
run(self.memory, self.base_address)
}
pub fn halted_access<R>(
&mut self,
op: impl FnOnce(&mut Self) -> Result<R, ArmError>,
) -> Result<R, ArmError> {
let was_halted = self.core_halted()?;
if !was_halted {
self.request_halt()?;
self.wait_for_core_halted(Duration::from_millis(200))?;
}
let result = op(self);
if !was_halted {
self.run()?;
}
result
}
pub(crate) fn clear_hw_breakpoint(&mut self, bp_unit_index: usize) -> Result<(), ArmError> {
clear_hw_breakpoint(self.memory, self.base_address, bp_unit_index)
}
pub(crate) fn set_hw_breakpoint(
&mut self,
bp_unit_index: usize,
addr: u32,
) -> Result<(), ArmError> {
set_hw_breakpoint(self.memory, self.base_address, bp_unit_index, addr)
}
pub(crate) fn get_hw_breakpoint(
&mut self,
bp_unit_index: usize,
) -> Result<Option<u32>, ArmError> {
get_hw_breakpoint(self.memory, self.base_address, bp_unit_index)
}
}
#[derive(Debug)]
pub struct TMS570 {
existing_breakpoint: AtomicU32,
breakpoint_active: AtomicBool,
reset_catch: AtomicBool,
}
impl TMS570 {
pub fn create(_name: String) -> Arc<Self> {
Arc::new(Self {
existing_breakpoint: AtomicU32::new(0),
breakpoint_active: AtomicBool::new(false),
reset_catch: AtomicBool::new(false),
})
}
}
fn ensure_ntrst(interface: &mut dyn DapProbe, nrst: bool) -> Result<(), ArmError> {
let mut pin_mask = Pins(0);
pin_mask.set_ntrst(true);
pin_mask.set_nreset(nrst);
let mut pin_value = Pins(0);
pin_value.set_ntrst(true);
pin_value.set_nreset(true);
let _ = interface.swj_pins(pin_value.0.into(), pin_mask.0.into(), 0)?;
Ok(())
}
fn clear_ecc_memory(core: &mut TemporaryCore) -> Result<(), ArmError> {
core.write_word_32(MINITGCR, 0xa)?;
core.write_word_32(MSINENA, 1)?;
let start = Instant::now();
loop {
let mstcgstat = core.read_word_32(MSTCGSTAT)?;
if mstcgstat & (1 << 8) != 0 {
break;
}
if start.elapsed() >= ECC_RAM_CLEAR_TIMEOUT {
tracing::error!(
"Memory didn't clear after {} ms",
ECC_RAM_CLEAR_TIMEOUT.as_millis()
);
return Err(ArmError::Timeout);
}
std::thread::sleep(Duration::from_millis(1));
}
tracing::trace!("Memory cleared after {} ms", start.elapsed().as_millis());
core.write_word_32(MINITGCR, 0x5)?;
Ok(())
}
impl ArmDebugSequence for TMS570 {
fn reset_hardware_assert(&self, interface: &mut dyn DapProbe) -> Result<(), ArmError> {
ensure_ntrst(interface, false)?;
ensure_ntrst(interface, true)?;
let mut icepick = Icepick::new(interface, DefaultProtocol::Jtag)?;
icepick.select_tap(TMS570_TAP_INDEX, "TMS570")?;
Err(ArmError::DebugPort(DebugPortError::Unsupported(
"Hardware reset is not supported by TMS570".to_owned(),
)))
}
fn reset_catch_set(
&self,
memory: &mut dyn ArmMemoryInterface,
_core_type: probe_rs_target::CoreType,
debug_base: Option<u64>,
) -> Result<(), ArmError> {
let base_address = debug_base.ok_or(ArmError::NoArmTarget)?;
TemporaryCore::new(memory, base_address).halted_access(|core| {
let existing = core.get_hw_breakpoint(0)?;
if let Some(existing) = existing {
self.existing_breakpoint.store(existing, Ordering::Release);
}
self.breakpoint_active
.store(existing.is_some(), Ordering::Relaxed);
core.set_hw_breakpoint(0, 0)?;
self.reset_catch.store(true, Ordering::Release);
Ok(())
})
}
fn reset_catch_clear(
&self,
memory: &mut dyn ArmMemoryInterface,
_core_type: probe_rs_target::CoreType,
debug_base: Option<u64>,
) -> Result<(), ArmError> {
self.reset_catch.store(false, Ordering::Release);
let base_address = debug_base.ok_or(ArmError::NoArmTarget)?;
let mut core = TemporaryCore::new(memory, base_address);
core.halted_access(|core| {
if self.breakpoint_active.swap(false, Ordering::Release) {
core.set_hw_breakpoint(0, self.existing_breakpoint.load(Ordering::Relaxed))?;
} else {
core.clear_hw_breakpoint(0)?;
}
clear_ecc_memory(core)?;
Ok(())
})
}
fn reset_system(
&self,
interface: &mut dyn ArmMemoryInterface,
_core_type: probe_rs_target::CoreType,
debug_base: Option<u64>,
) -> Result<(), ArmError> {
let base_address = debug_base.ok_or(ArmError::NoArmTarget)?;
let mut core = TemporaryCore::new(interface, base_address);
core.request_halt()?;
core.wait_for_core_halted(RESET_TIMEOUT)?;
if self.reset_catch.load(Ordering::Relaxed) {
core.write_word_32(SYSECR, SYSECR_RESET)?;
let start = Instant::now();
loop {
if start.elapsed() > RESET_TIMEOUT {
return Err(ArmError::Timeout);
}
let sysesr = core.read_word_32(SYSESR)?;
if sysesr & SYSESR_RESET_MASK == SYSESR_RESET_VALUE {
break;
}
}
} else {
if let Err(e) = core.write_word_32(SYSECR, SYSECR_RESET) {
tracing::info!("Caught error when triggering reset: {e}");
}
}
Ok(())
}
fn debug_port_setup(
&self,
interface: &mut dyn DapProbe,
_dp: DpAddress,
) -> Result<(), ArmError> {
ensure_ntrst(interface, true)?;
match interface.active_protocol() {
Some(WireProtocol::Jtag) => {
let mut icepick = Icepick::new(interface, DefaultProtocol::Jtag)?;
icepick.select_tap(TMS570_TAP_INDEX, "TMS570")?;
interface.configure_jtag(true)?;
}
Some(WireProtocol::Swd) => {
return Err(ArmDebugSequenceError::SequenceSpecific(
"The tms570 family doesn't support SWD".into(),
)
.into());
}
_ => {
return Err(ArmDebugSequenceError::SequenceSpecific(
"Cannot detect current protocol".into(),
)
.into());
}
}
Ok(())
}
}