use bitfield::bitfield;
use std::{
sync::Arc,
thread,
time::{Duration, Instant},
};
use crate::{
architecture::arm::{
ArmDebugInterface, ArmError, DapAccess, FullyQualifiedApAddress, Pins,
ap::{AccessPortError, AccessPortType, ApRegister, GenericAp, IDR},
core::armv8m::{Aircr, Demcr, Dhcsr},
dp::{Abort, Ctrl, DPIDR, DpAccess, DpAddress, DpRegister, SelectV1},
memory::ArmMemoryInterface,
sequences::ArmDebugSequence,
},
core::MemoryMappedRegister,
};
pub mod ol23d0;
fn debug_port_start(
interface: &mut dyn DapAccess,
dp: DpAddress,
select: SelectV1,
) -> 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();
loop {
let ctrl = interface.read_dp_register::<Ctrl>(dp)?;
if ctrl.csyspwrupack() && ctrl.cdbgpwrupack() {
break;
}
if start.elapsed() >= Duration::from_secs(1) {
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 dyn DapAccess,
dp: DpAddress,
) -> Result<(), ArmError> {
tracing::info!("debug_port_start");
let _powered_down = self::debug_port_start(interface, dp, SelectV1(0))?;
Ok(())
}
fn reset_catch_set(
&self,
interface: &mut dyn ArmMemoryInterface,
_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();
loop {
let value = interface.read_word_32(0x40034FE0)?;
if (value & 0x4) == 0x4 {
break;
}
if start.elapsed() >= Duration::from_millis(100) {
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 ArmMemoryInterface,
_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 ArmMemoryInterface,
_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();
loop {
if let Ok(v) = interface.read_word_32(Dhcsr::get_mmio_address()) {
let dhcsr = Dhcsr(v);
if !dhcsr.s_reset_st() {
break;
}
}
if start.elapsed() >= Duration::from_millis(500) {
return wait_for_stop_after_reset(interface);
}
}
Ok(())
}
}
fn wait_for_stop_after_reset(memory: &mut dyn ArmMemoryInterface) -> Result<(), ArmError> {
tracing::info!("Wait for stop after reset");
thread::sleep(Duration::from_millis(10));
if memory.generic_status()?.DeviceEn {
let dp = memory.fully_qualified_address().dp();
enable_debug_mailbox(memory.get_arm_debug_interface()?, dp)?;
}
let start = Instant::now();
tracing::debug!("Polling for reset");
loop {
if let Ok(v) = memory.read_word_32(Dhcsr::get_mmio_address()) {
let dhcsr = Dhcsr(v);
if !dhcsr.s_reset_st() {
break;
}
}
if start.elapsed() >= Duration::from_millis(500) {
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 dyn DapAccess, dp: DpAddress) -> Result<(), ArmError> {
tracing::info!("LPC55xx connect script start");
let ap = FullyQualifiedApAddress::v1_with_dp(dp, 2);
let status: IDR = interface
.read_raw_ap_register(GenericAp::new(ap.clone()).ap_address(), IDR::ADDRESS)?
.try_into()?;
tracing::info!("APIDR: {:?}", status);
tracing::info!("APIDR: 0x{:08X}", u32::from(status));
let status: u32 = interface.read_raw_dp_register(dp, DPIDR::ADDRESS)?;
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 {
family: MIMXRTFamily,
}
#[derive(PartialEq, Debug)]
pub enum MIMXRTFamily {
MIMXRT5,
MIMXRT6,
}
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(family: MIMXRTFamily) -> Arc<dyn ArmDebugSequence> {
Arc::new(Self { family })
}
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,
memory: &mut dyn ArmMemoryInterface,
) -> Result<(), ArmError> {
tracing::trace!("waiting for MIMXRT5xxS halt after reset");
thread::sleep(Duration::from_millis(100));
let ap = memory.fully_qualified_address();
let dp = ap.dp();
let start = Instant::now();
while !memory.generic_status()?.DeviceEn && start.elapsed() < Duration::from_millis(300) {
}
let enabled_mailbox =
self.enable_debug_mailbox(memory.get_arm_debug_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();
memory.write_word_32(Dhcsr::get_mmio_address(), dhcsr.into())?;
memory.flush()?;
if enabled_mailbox {
if !memory.generic_status()?.DeviceEn {
tracing::warn!(
"MIMXRT5xxS is still not ready to debug, even after using DebugMailbox to activate session"
);
}
}
memory.write_word_32(Self::DWT_COMP0, 0x0)?;
memory.write_word_32(Self::DWT_FUNCTION0, 0x0)?;
memory.flush()?;
tracing::trace!("cleared data watchpoint for MIMXRT5xxS reset");
let probed = memory.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 ArmMemoryInterface) -> Result<(), ArmError> {
if self.family == MIMXRTFamily::MIMXRT5 {
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)?; interface.flush()?;
thread::sleep(Duration::from_micros(100));
interface.write_word_32(0x40102010, 0)?; interface.flush()?;
thread::sleep(Duration::from_micros(100));
} else {
tracing::trace!("MIMXRT685-EVK FlexSPI flash reset (pulse PIO2_12)");
interface.write_word_32(0x40021044, 1 << 2)?; interface.write_word_32(0x40020074, 1 << 2)?; interface.write_word_32(0x40004130, 0x130)?; interface.write_word_32(0x40102008, 1 << 12)?; interface.write_word_32(0x40102288, 1 << 12)?; interface.flush()?;
thread::sleep(Duration::from_micros(100));
interface.write_word_32(0x40102208, 1 << 12)?; interface.flush()?;
thread::sleep(Duration::from_micros(100));
}
Ok(())
}
fn csw_debug_ready(
&self,
interface: &mut dyn DapAccess,
ap: &FullyQualifiedApAddress,
) -> Result<bool, ArmError> {
let csw = interface.read_raw_ap_register(ap, 0x00)?;
Ok(csw & 0x40 != 0)
}
fn enable_debug_mailbox(
&self,
interface: &mut dyn DapAccess,
dp: DpAddress,
mem_ap: &FullyQualifiedApAddress,
) -> 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 = &FullyQualifiedApAddress::v1_with_dp(dp, 2);
interface.write_raw_ap_register(ap_addr, 0x0, 0x00000021)?;
thread::sleep(Duration::from_millis(30));
interface.read_raw_ap_register(ap_addr, 0x0)?;
interface.write_raw_ap_register(ap_addr, 0x4, 0x00000007)?;
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 dyn DapAccess,
dp: DpAddress,
) -> Result<(), ArmError> {
let mut abort = Abort::default();
abort.set_wderrclr(true);
abort.set_orunerrclr(true);
abort.set_stkcmpclr(true);
abort.set_stkerrclr(true);
tracing::trace!("MIMXRT5xxS debug port start");
interface.write_dp_register(dp, abort)?;
let dpidr: DPIDR = interface.read_dp_register(dp)?;
interface.write_dp_register(dp, SelectV1(0))?;
let mut ctrl: Ctrl = interface.read_dp_register(dp)?;
let powered_down = !ctrl.csyspwrupack() || !ctrl.cdbgpwrupack();
if powered_down {
tracing::trace!("MIMXRT5xxS is powered down, so requesting power-up");
ctrl.set_csyspwrupreq(true);
ctrl.set_cdbgpwrupreq(true);
interface.write_dp_register(dp, ctrl)?;
let start = Instant::now();
loop {
ctrl = interface.read_dp_register(dp)?;
if ctrl.csyspwrupack() && ctrl.cdbgpwrupack() {
break;
}
if start.elapsed() >= Duration::from_secs(1) {
return Err(ArmError::Timeout);
}
}
} else {
tracing::trace!("MIMXRT5xxS debug port is already powered");
}
if !dpidr.min() {
ctrl.set_mask_lane(0xF);
interface.write_dp_register(dp, ctrl)?;
interface.write_dp_register(dp, abort)?;
let ap = FullyQualifiedApAddress::v1_with_dp(dp, 0);
self.enable_debug_mailbox(interface, dp, &ap)?;
}
tracing::trace!("MIMXRT5xxS debug port start was successful");
Ok(())
}
fn reset_system(
&self,
probe: &mut dyn ArmMemoryInterface,
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 ArmDebugInterface,
_default_ap: &FullyQualifiedApAddress,
) -> Result<(), ArmError> {
tracing::trace!("MIMXRT5xxS reset hardware deassert");
let n_reset = Pins(0x80).0 as u32;
let can_read_pins = memory.swj_pins(n_reset, 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(())
}
}
#[derive(Debug)]
pub struct MIMXRT118x(());
bitfield! {
#[derive(Copy, Clone)]
pub struct Srmask(u32);
impl Debug;
pub _, set_cm33_reset_mask : 8;
}
impl MIMXRT118x {
fn new() -> Self {
Self(())
}
pub fn create() -> Arc<dyn ArmDebugSequence> {
Arc::new(Self::new())
}
fn clear_cm33_reset_mask(
&self,
interface: &mut dyn ArmMemoryInterface,
) -> Result<(), ArmError> {
const SRC_SRMASK: u64 = 0x54460018;
let mut srmask = Srmask(interface.read_word_32(SRC_SRMASK)?);
srmask.set_cm33_reset_mask(false);
tracing::trace!("Clearing the SRC.SRMASK.CM33_RESET_MASK mask...");
interface.write_word_32(SRC_SRMASK, srmask.0)?;
interface.flush()
}
}
impl ArmDebugSequence for MIMXRT118x {
fn reset_system(
&self,
interface: &mut dyn ArmMemoryInterface,
_core_type: crate::CoreType,
_: Option<u64>,
) -> Result<(), ArmError> {
tracing::trace!("MIMXRT118x reset system");
self.clear_cm33_reset_mask(interface)?;
let mut aircr = Aircr(0);
aircr.vectkey();
aircr.set_sysresetreq(true);
interface.write_word_32(Aircr::get_mmio_address(), aircr.into())?;
interface.flush()?;
tracing::trace!("Reset requested..");
thread::sleep(Duration::from_millis(50));
let start = Instant::now();
while start.elapsed() < Duration::from_millis(500) {
let dhcsr = match interface.read_word_32(Dhcsr::get_mmio_address()) {
Ok(val) => Dhcsr(val),
Err(ArmError::AccessPort {
source: AccessPortError::RegisterRead { .. },
..
}) => continue,
Err(err) => return Err(err),
};
if dhcsr.s_reset_st() {
tracing::trace!("System reset was successful");
return Ok(());
}
}
tracing::error!("System reset timed out");
Err(ArmError::Timeout)
}
fn allowed_access_ports(&self) -> Vec<u8> {
vec![0, 1, 3, 4, 6]
}
}