use std::{
sync::Arc,
thread,
time::{Duration, Instant},
};
use crate::{
architecture::arm::{
ap::{AccessPort, ApAccess, GenericAp, MemoryAp, CSW, IDR},
communication_interface::{FlushableArmAccess, Initialized},
core::armv8m::{Aircr, Demcr, Dhcsr},
dp::{Abort, Ctrl, DpAccess, Select, DPIDR},
memory::adi_v5_memory_interface::ArmProbe,
sequences::ArmDebugSequence,
ApAddress, ArmCommunicationInterface, ArmError, DapAccess, DpAddress, Pins,
},
core::MemoryMappedRegister,
};
fn debug_port_start(
interface: &mut ArmCommunicationInterface<Initialized>,
dp: DpAddress,
select: Select,
) -> Result<bool, ArmError> {
interface.write_dp_register(dp, select)?;
let ctrl = interface.read_dp_register::<Ctrl>(dp)?;
let powered_down = !(ctrl.csyspwrupack() && ctrl.cdbgpwrupack());
if powered_down {
let mut ctrl = Ctrl(0);
ctrl.set_cdbgpwrupreq(true);
ctrl.set_csyspwrupreq(true);
interface.write_dp_register(dp, ctrl)?;
let start = Instant::now();
let mut timeout = true;
while start.elapsed() < Duration::from_secs(1) {
let ctrl = interface.read_dp_register::<Ctrl>(dp)?;
if ctrl.csyspwrupack() && ctrl.cdbgpwrupack() {
timeout = false;
break;
}
}
if timeout {
return Err(ArmError::Timeout);
}
let mut ctrl = Ctrl(0);
ctrl.set_cdbgpwrupreq(true);
ctrl.set_csyspwrupreq(true);
ctrl.set_mask_lane(0b1111);
interface.write_dp_register(dp, ctrl)?;
let mut abort = Abort(0);
abort.set_orunerrclr(true);
abort.set_wderrclr(true);
abort.set_stkerrclr(true);
abort.set_stkcmpclr(true);
interface.write_dp_register(dp, abort)?;
}
Ok(powered_down)
}
#[derive(Debug)]
pub struct LPC55Sxx(());
impl LPC55Sxx {
pub fn create() -> Arc<dyn ArmDebugSequence> {
Arc::new(Self(()))
}
}
impl ArmDebugSequence for LPC55Sxx {
fn debug_port_start(
&self,
interface: &mut ArmCommunicationInterface<Initialized>,
dp: DpAddress,
) -> Result<(), ArmError> {
tracing::info!("debug_port_start");
let _powered_down = self::debug_port_start(interface, dp, Select(0))?;
Ok(())
}
fn reset_catch_set(
&self,
interface: &mut dyn ArmProbe,
_core_type: crate::CoreType,
_debug_base: Option<u64>,
) -> Result<(), ArmError> {
let mut reset_vector: u32 = 0xffff_ffff;
let mut reset_vector_addr = 0x0000_0004;
let mut demcr = Demcr(interface.read_word_32(Demcr::get_mmio_address())?);
demcr.set_vc_corereset(false);
interface.write_word_32(Demcr::get_mmio_address(), demcr.into())?;
interface.write_word_32(0x40034010, 0x00000000)?; interface.write_word_32(0x40034014, 0x00000000)?; interface.write_word_32(0x40034080, 0x00000000)?; interface.write_word_32(0x40034084, 0x00000000)?; interface.write_word_32(0x40034088, 0x00000000)?; interface.write_word_32(0x4003408C, 0x00000000)?; interface.write_word_32(0x40034090, 0x00000000)?; interface.write_word_32(0x40034094, 0x00000000)?; interface.write_word_32(0x40034098, 0x00000000)?; interface.write_word_32(0x4003409C, 0x00000000)?; interface.write_word_32(0x40034FE8, 0x0000000F)?; interface.write_word_32(0x40034000, 0x00000003)?; interface.flush()?;
let start = Instant::now();
let mut timeout = true;
while start.elapsed() < Duration::from_millis(100) {
let value = interface.read_word_32(0x40034FE0)?;
if (value & 0x4) == 0x4 {
timeout = false;
break;
}
}
if timeout {
tracing::warn!("Failed: Wait for flash word read to finish");
return Err(ArmError::Timeout);
}
if (interface.read_word_32(0x4003_4fe0)? & 0xB) == 0 {
tracing::info!("No Error reading Flash Word with Reset Vector");
if (interface.read_word_32(0x400a_cffc)? & 0xC != 0x8)
|| (interface.read_word_32(0x400a_cff8)? & 0xC != 0x8)
{
reset_vector_addr = 0x10000004;
}
reset_vector = interface.read_word_32(reset_vector_addr)?;
}
if reset_vector != 0xffff_ffff {
tracing::info!("Breakpoint on user application reset vector: {reset_vector:#010x}");
interface.write_word_32(0xE000_2008, reset_vector | 1)?;
interface.write_word_32(0xE000_2000, 3)?;
}
if reset_vector == 0xffff_ffff {
tracing::info!("Enable reset vector catch");
let mut demcr = Demcr(interface.read_word_32(Demcr::get_mmio_address())?);
demcr.set_vc_corereset(true);
interface.write_word_32(Demcr::get_mmio_address(), demcr.into())?;
}
let _ = interface.read_word_32(Dhcsr::get_mmio_address())?;
tracing::debug!("reset_catch_set -- done");
Ok(())
}
fn reset_catch_clear(
&self,
interface: &mut dyn ArmProbe,
_core_type: crate::CoreType,
_debug_base: Option<u64>,
) -> Result<(), ArmError> {
interface.write_word_32(0xE000_2008, 0x0)?;
interface.write_word_32(0xE000_2000, 0x2)?;
let mut demcr = Demcr(interface.read_word_32(Demcr::get_mmio_address())?);
demcr.set_vc_corereset(false);
interface.write_word_32(Demcr::get_mmio_address(), demcr.into())
}
fn reset_system(
&self,
interface: &mut dyn ArmProbe,
_core_type: crate::CoreType,
_debug_base: Option<u64>,
) -> Result<(), ArmError> {
let mut aircr = Aircr(0);
aircr.vectkey();
aircr.set_sysresetreq(true);
let mut result = interface.write_word_32(Aircr::get_mmio_address(), aircr.into());
if result.is_ok() {
result = interface.flush();
}
if let Err(e) = result {
tracing::warn!("Error requesting reset: {:?}", e);
}
tracing::info!("Waiting after reset");
thread::sleep(Duration::from_millis(10));
let start = Instant::now();
let mut timeout = true;
while start.elapsed() < Duration::from_millis(500) {
if let Ok(v) = interface.read_word_32(Dhcsr::get_mmio_address()) {
let dhcsr = Dhcsr(v);
if !dhcsr.s_reset_st() {
timeout = false;
break;
}
}
}
if timeout {
wait_for_stop_after_reset(interface)
} else {
Ok(())
}
}
}
fn wait_for_stop_after_reset(memory: &mut dyn ArmProbe) -> Result<(), ArmError> {
tracing::info!("Wait for stop after reset");
thread::sleep(Duration::from_millis(10));
let dp = memory.ap().ap_address().dp;
let ap = memory.ap();
let interface = memory.get_arm_communication_interface()?;
let ap0_csw: CSW = interface.read_ap_register(ap)?;
let ap0_disabled = ap0_csw.DeviceEn == 0;
if ap0_disabled {
enable_debug_mailbox(interface, dp)?;
}
let mut timeout = true;
let start = Instant::now();
tracing::info!("Polling for reset");
while start.elapsed() < Duration::from_millis(500) {
if let Ok(v) = memory.read_word_32(Dhcsr::get_mmio_address()) {
let dhcsr = Dhcsr(v);
if !dhcsr.s_reset_st() {
timeout = false;
break;
}
}
}
if timeout {
return Err(ArmError::Timeout);
}
let dhcsr = Dhcsr(memory.read_word_32(Dhcsr::get_mmio_address())?);
if !dhcsr.s_halt() {
let mut dhcsr = Dhcsr(0);
dhcsr.enable_write();
dhcsr.set_c_halt(true);
dhcsr.set_c_debugen(true);
tracing::debug!("Force halt until finding a proper catch.");
memory.write_word_32(Dhcsr::get_mmio_address(), dhcsr.into())?;
}
Ok(())
}
fn enable_debug_mailbox(
interface: &mut ArmCommunicationInterface<Initialized>,
dp: DpAddress,
) -> Result<(), ArmError> {
tracing::info!("LPC55xx connect script start");
let ap = ApAddress { dp, ap: 2 };
let status: IDR = interface.read_ap_register(GenericAp::new(ap))?;
tracing::info!("APIDR: {:?}", status);
tracing::info!("APIDR: 0x{:08X}", u32::from(status));
let status: u32 = interface.read_dp_register::<DPIDR>(dp)?.into();
tracing::info!("DPIDR: 0x{:08X}", status);
interface.write_raw_ap_register(ap, 0x0, 0x0000_0021)?;
interface.flush()?;
thread::sleep(Duration::from_millis(30));
let _ = interface.read_raw_ap_register(ap, 0)?;
interface.write_raw_ap_register(ap, 0x4, 0x0000_0007)?;
interface.flush()?;
thread::sleep(Duration::from_millis(30));
let _ = interface.read_raw_ap_register(ap, 8)?;
tracing::info!("LPC55xx connect srcipt end");
Ok(())
}
#[derive(Debug)]
pub struct MIMXRT5xxS {}
impl MIMXRT5xxS {
const DWT_COMP0: u64 = 0xE0001020;
const DWT_FUNCTION0: u64 = 0xE0001028;
const SYSTEM_STICK_CALIB_ADDR: u32 = 0x50002034;
const FLEXSPI_NOR_FLASH_HEADER_ADDR: u64 = 0x08000400;
const FLEXSPI_NOR_FLASH_HEADER_MAGIC: u32 = 0x42464346;
pub fn create() -> Arc<dyn ArmDebugSequence> {
Arc::new(Self {})
}
fn check_core_type(&self, core_type: crate::CoreType) -> Result<(), ArmError> {
if core_type != crate::CoreType::Armv8m {
return Err(ArmError::ArchitectureRequired(&["ARMv8"]));
}
Ok(())
}
fn wait_for_stop_after_reset(&self, probe: &mut dyn ArmProbe) -> Result<(), ArmError> {
tracing::trace!("waiting for MIMXRT5xxS halt after reset");
std::thread::sleep(Duration::from_millis(100));
let ap: MemoryAp = probe.ap();
let dp = ap.ap_address().dp;
let start = Instant::now();
while !self.csw_debug_ready(probe.get_arm_communication_interface()?, ap)?
&& start.elapsed() < Duration::from_millis(300)
{
}
let enabled_mailbox =
self.enable_debug_mailbox(probe.get_arm_communication_interface()?, dp, ap)?;
tracing::trace!("halting MIMXRT5xxS Cortex-M33 core");
let mut dhcsr = Dhcsr(0);
dhcsr.set_c_halt(true);
dhcsr.set_c_debugen(true);
dhcsr.enable_write();
probe.write_word_32(Dhcsr::get_mmio_address(), dhcsr.into())?;
probe.flush()?;
if enabled_mailbox {
if !self.csw_debug_ready(probe.get_arm_communication_interface()?, ap)? {
tracing::warn!("MIMXRT5xxS is still not ready to debug, even after using DebugMailbox to activate session");
}
}
probe.write_word_32(Self::DWT_COMP0, 0x0)?;
probe.write_word_32(Self::DWT_FUNCTION0, 0x0)?;
probe.flush()?;
tracing::trace!("cleared data watchpoint for MIMXRT5xxS reset");
let probed = probe.read_word_32(Self::FLEXSPI_NOR_FLASH_HEADER_ADDR)?;
if probed != Self::FLEXSPI_NOR_FLASH_HEADER_MAGIC {
tracing::warn!(
"FlexSPI0 NOR flash config block starts with {:#010x} (valid blocks start with {:#010x})",
probed, Self::FLEXSPI_NOR_FLASH_HEADER_MAGIC,
);
} else {
tracing::trace!(
"FlexSPI0 NOR flash config block starts with {:#010x}, as expected",
probed
);
}
Ok(())
}
fn reset_flash(&self, interface: &mut dyn ArmProbe) -> Result<(), ArmError> {
tracing::trace!("MIMXRT595S-EVK FlexSPI flash reset (pulse PIO4_5)");
interface.write_word_32(0x40001044, 1 << 24)?; interface.write_word_32(0x40000074, 1 << 24)?; interface.write_word_32(0x40004214, 0x130)?; interface.write_word_32(0x40102010, 1 << 5)?; interface.write_word_32(0x40103214, 0)?; std::thread::sleep(Duration::from_millis(100));
interface.write_word_32(0x40102010, 0)?; interface.flush()?;
std::thread::sleep(Duration::from_millis(100));
Ok(())
}
fn csw_debug_ready(
&self,
interface: &mut ArmCommunicationInterface<Initialized>,
ap: MemoryAp,
) -> Result<bool, ArmError> {
let csw = interface.read_raw_ap_register(ap.ap_address(), 0x00)?;
Ok(csw & 0x40 != 0)
}
fn enable_debug_mailbox(
&self,
interface: &mut ArmCommunicationInterface<Initialized>,
dp: DpAddress,
mem_ap: MemoryAp,
) -> Result<bool, ArmError> {
if self.csw_debug_ready(interface, mem_ap)? {
tracing::trace!("don't need to enable MIMXRT5xxS DebugMailbox");
return Ok(false);
}
tracing::debug!("enabling MIMXRT5xxS DebugMailbox");
let ap_addr = ApAddress { dp, ap: 2 };
interface.write_raw_ap_register(ap_addr, 0x0, 0x00000021)?;
std::thread::sleep(Duration::from_millis(30));
interface.read_raw_ap_register(ap_addr, 0x0)?;
interface.write_raw_ap_register(ap_addr, 0x4, 0x00000007)?;
std::thread::sleep(Duration::from_millis(30));
interface.read_raw_ap_register(ap_addr, 0x0)?;
tracing::debug!("entered MIMXRT5xxS debug session");
Ok(true)
}
}
impl ArmDebugSequence for MIMXRT5xxS {
fn debug_port_start(
&self,
interface: &mut ArmCommunicationInterface<Initialized>,
dp: DpAddress,
) -> Result<(), ArmError> {
const SW_DP_ABORT: u8 = 0x0;
const DP_CTRL_STAT: u8 = 0x4;
const DP_SELECT: u8 = 0x8;
tracing::trace!("MIMXRT5xxS debug port start");
interface.write_raw_dp_register(dp, SW_DP_ABORT, 0x0000001E)?;
interface.write_raw_dp_register(dp, DP_SELECT, 0x00000000)?;
let powered_down =
(interface.read_raw_dp_register(dp, DP_CTRL_STAT)? & 0xA0000000) != 0xA0000000;
if powered_down {
tracing::trace!("MIMXRT5xxS is powered down, so requesting power-up");
interface.write_raw_dp_register(dp, DP_CTRL_STAT, 0x50000000)?;
let start = Instant::now();
while (interface.read_raw_dp_register(dp, DP_CTRL_STAT)? & 0xA0000000) != 0xA0000000 {
if start.elapsed() >= Duration::from_secs(1) {
return Err(ArmError::Timeout);
}
}
} else {
tracing::trace!("MIMXRT5xxS debug port is already powered");
}
{
interface.write_raw_dp_register(dp, DP_CTRL_STAT, 0x50000F00)?;
interface.write_raw_dp_register(dp, SW_DP_ABORT, 0x0000001E)?;
let ap = ApAddress { dp, ap: 0 };
let mem_ap = MemoryAp::new(ap);
self.enable_debug_mailbox(interface, dp, mem_ap)?;
}
tracing::trace!("MIMXRT5xxS debug port start was successful");
Ok(())
}
fn reset_system(
&self,
probe: &mut dyn ArmProbe,
core_type: probe_rs_target::CoreType,
_debug_base: Option<u64>,
) -> Result<(), ArmError> {
self.check_core_type(core_type)?;
tracing::trace!("MIMXRT5xxS reset system");
let mut dhcsr = Dhcsr(0);
dhcsr.set_c_halt(true);
dhcsr.set_c_debugen(true);
dhcsr.enable_write();
probe.write_word_32(Dhcsr::get_mmio_address(), dhcsr.into())?;
probe.flush()?;
let mut demcr: Demcr = probe.read_word_32(Demcr::get_mmio_address())?.into();
demcr.set_trcena(true);
demcr.set_vc_corereset(false);
probe.write_word_32(Demcr::get_mmio_address(), demcr.into())?;
probe.flush()?;
self.reset_flash(probe)?;
probe.write_word_32(Self::DWT_COMP0, Self::SYSTEM_STICK_CALIB_ADDR)?;
probe.write_word_32(Self::DWT_FUNCTION0, 0x00000814)?;
probe.flush()?;
tracing::trace!("set data watchpoint for MIMXRT5xxS reset");
let mut aircr = Aircr(0);
aircr.set_sysresetreq(true);
aircr.vectkey();
probe
.write_word_32(Aircr::get_mmio_address(), aircr.into())
.ok();
probe.flush().ok();
tracing::trace!("MIMXRT5xxS reset system was successful; waiting for halt after reset");
self.wait_for_stop_after_reset(probe)
}
fn reset_hardware_deassert(&self, memory: &mut dyn ArmProbe) -> Result<(), ArmError> {
tracing::trace!("MIMXRT5xxS reset hardware deassert");
let n_reset = Pins(0x80).0 as u32;
let can_read_pins = memory.swj_pins(0, n_reset, 0)? != 0xffff_ffff;
thread::sleep(Duration::from_millis(50));
let mut assert_n_reset = || memory.swj_pins(n_reset, n_reset, 0);
if can_read_pins {
let start = Instant::now();
let timeout_occured = || start.elapsed() > Duration::from_secs(1);
while assert_n_reset()? & n_reset == 0 || !timeout_occured() {
}
} else {
assert_n_reset()?;
thread::sleep(Duration::from_millis(100));
}
Ok(())
}
}