use std::{
sync::{
Arc,
atomic::{AtomicBool, Ordering},
},
thread,
time::{Duration, Instant},
};
use crate::{
architecture::arm::{
ArmError,
armv7m::{Demcr, FpCtrl, FpRev2CompX},
core::{
armv7m::{Aircr, Dhcsr},
registers::cortex_m::PC,
},
memory::ArmMemoryInterface,
sequences::{self, ArmDebugSequence, ArmDebugSequenceError},
},
core::MemoryMappedRegister,
};
#[derive(Debug)]
pub struct MIMXRT10xx {
simulate_reset_catch: AtomicBool,
}
impl MIMXRT10xx {
pub fn create() -> Arc<dyn ArmDebugSequence> {
Arc::new(Self {
simulate_reset_catch: AtomicBool::new(false),
})
}
fn halt(&self, probe: &mut dyn ArmMemoryInterface, halt: bool) -> Result<(), ArmError> {
let mut dhcsr = Dhcsr(probe.read_word_32(Dhcsr::get_mmio_address())?);
dhcsr.set_c_halt(halt);
dhcsr.set_c_debugen(true);
dhcsr.enable_write();
probe.write_word_32(Dhcsr::get_mmio_address(), dhcsr.into())?;
probe.flush()?;
self.wait_for_halt(probe, halt)?;
Ok(())
}
fn use_boot_fuses_for_flexram(
&self,
probe: &mut dyn ArmMemoryInterface,
) -> Result<(), ArmError> {
const IOMUXC_GPR_GPR16: u64 = 0x400A_C040;
const FLEXRAM_BANK_CFG_SEL_MASK: u32 = 1 << 2;
let mut gpr16 = probe.read_word_32(IOMUXC_GPR_GPR16)?;
gpr16 &= !FLEXRAM_BANK_CFG_SEL_MASK;
probe.write_word_32(IOMUXC_GPR_GPR16, gpr16)?;
probe.flush()?;
Ok(())
}
fn wait_for_halt(
&self,
probe: &mut dyn ArmMemoryInterface,
halt: bool,
) -> Result<(), ArmError> {
let start = Instant::now();
let action = if halt { "halt" } else { "unhalt" };
while Dhcsr(probe.read_word_32(Dhcsr::get_mmio_address())?).s_halt() != halt {
if start.elapsed() > Duration::from_millis(100) {
tracing::debug!("Exceeded timeout while waiting for core to {action}");
return Err(ArmError::Timeout);
}
thread::sleep(Duration::from_millis(1));
}
Ok(())
}
}
impl ArmDebugSequence for MIMXRT10xx {
fn reset_catch_set(
&self,
_: &mut dyn ArmMemoryInterface,
_: probe_rs_target::CoreType,
_: Option<u64>,
) -> Result<(), ArmError> {
self.simulate_reset_catch.store(true, Ordering::Relaxed);
Ok(())
}
fn reset_catch_clear(
&self,
_: &mut dyn ArmMemoryInterface,
_: probe_rs_target::CoreType,
_: Option<u64>,
) -> Result<(), ArmError> {
self.simulate_reset_catch.store(false, Ordering::Relaxed);
Ok(())
}
fn reset_system(
&self,
interface: &mut dyn ArmMemoryInterface,
_: crate::CoreType,
_: Option<u64>,
) -> Result<(), ArmError> {
tracing::debug!("Halting MCU before changing FlexRAM layout");
self.halt(interface, true)?;
tracing::debug!("Setting FlexRAM layout");
self.use_boot_fuses_for_flexram(interface)?;
tracing::debug!("Enabling DWT to set a watchpoint");
let mut demcr = Demcr(interface.read_word_32(Demcr::get_mmio_address())?);
let trcena = demcr.trcena();
demcr.set_trcena(true);
interface.write_word_32(Demcr::get_mmio_address(), demcr.0)?;
tracing::debug!("Installing watchpoint to catch boot ROM SRC_SBMR1 read");
const DWT_COMP0: u64 = 0xE000_1020;
const DWT_MASK0: u64 = 0xE000_1024;
const DWT_FUNCTION0: u64 = 0xE000_1028;
const DWT_FUNCTION_DATAVSIZE_WORD: u32 = 0b10 << 10;
const DWT_FUNCTION_DEBUG_DATA_RW: u32 = 0b0111;
const SRC_SBMR1: u32 = 0x400F_8004;
interface.write_word_32(DWT_COMP0, SRC_SBMR1)?;
interface.write_word_32(DWT_MASK0, 0)?;
interface.write_word_32(
DWT_FUNCTION0,
DWT_FUNCTION_DATAVSIZE_WORD | DWT_FUNCTION_DEBUG_DATA_RW,
)?;
interface.flush()?;
tracing::debug!("Performing the standard Cortex-M system reset");
sequences::cortex_m_reset_system(interface)?;
tracing::debug!("Waiting for watchpoint to hit");
self.wait_for_halt(interface, true)?;
tracing::debug!("Cleaning up watchpoints");
interface.write_word_32(DWT_COMP0, 0)?;
interface.write_word_32(DWT_FUNCTION0, 0)?;
let mut demcr = Demcr(interface.read_word_32(Demcr::get_mmio_address())?);
demcr.set_trcena(trcena);
interface.write_word_32(Demcr::get_mmio_address(), demcr.0)?;
interface.flush()?;
if !self.simulate_reset_catch.load(Ordering::Relaxed) {
self.halt(interface, false)?;
}
Ok(())
}
}
#[deprecated(note = "Prefer MIMXRT11xx, which supports 1170 and 1160 targets")]
pub type MIMXRT117x = MIMXRT11xx;
#[derive(Debug)]
pub struct MIMXRT11xx {
simulate_reset_catch: AtomicBool,
}
impl MIMXRT11xx {
const SRC: u64 = 0x40C0_4000;
const SRC_SRMR: u64 = Self::SRC + 4;
fn new() -> Self {
Self {
simulate_reset_catch: AtomicBool::new(false),
}
}
pub fn create() -> Arc<dyn ArmDebugSequence> {
Arc::new(Self::new())
}
fn clear_src_srmr_mask(&self, probe: &mut dyn ArmMemoryInterface) -> Result<(), ArmError> {
let mut srmr = probe.read_word_32(Self::SRC_SRMR)?;
tracing::debug!("SRC_SRMR: {srmr:#010X}. Clearing the M7REQ_RESET_MODE mask...");
srmr &= !(0b11 << 12);
probe.write_word_32(Self::SRC_SRMR, srmr)?;
probe.flush()?;
Ok(())
}
fn halt(&self, probe: &mut dyn ArmMemoryInterface, halt: bool) -> Result<(), ArmError> {
let mut dhcsr = Dhcsr(probe.read_word_32(Dhcsr::get_mmio_address())?);
dhcsr.set_c_halt(halt);
dhcsr.set_c_debugen(true);
dhcsr.enable_write();
probe.write_word_32(Dhcsr::get_mmio_address(), dhcsr.into())?;
probe.flush()?;
let start = Instant::now();
let action = if halt { "halt" } else { "unhalt" };
while Dhcsr(probe.read_word_32(Dhcsr::get_mmio_address())?).s_halt() != halt {
if start.elapsed() > Duration::from_millis(100) {
tracing::debug!("Exceeded timeout while waiting for the core to {action}");
return Err(ArmError::Timeout);
}
thread::sleep(Duration::from_millis(1));
}
Ok(())
}
fn wait_for_enable(
&self,
probe: &mut dyn ArmMemoryInterface,
timeout: Duration,
) -> Result<(), ArmError> {
let start = Instant::now();
let mut errors = 0usize;
let mut disables = 0usize;
loop {
match probe.generic_status() {
Ok(csw) if csw.DeviceEn => {
tracing::debug!(
"Device enabled after {}ms with {errors} errors and {disables} invalid statuses",
start.elapsed().as_millis()
);
return Ok(());
}
Ok(_) => disables += 1,
Err(_) => errors += 1,
}
if start.elapsed() > timeout {
tracing::debug!(
"Exceeded {}ms timeout while waiting for enable with {errors} errors and {disables} invalid statuses",
timeout.as_millis()
);
return Err(ArmError::Timeout);
}
thread::sleep(Duration::from_millis(1));
}
}
fn read_core_reg(
&self,
probe: &mut dyn ArmMemoryInterface,
reg: crate::core::registers::CoreRegister,
) -> Result<u32, ArmError> {
crate::architecture::arm::core::cortex_m::read_core_reg(probe, reg.into())
}
fn write_core_reg(
&self,
probe: &mut dyn ArmMemoryInterface,
reg: crate::core::registers::CoreRegister,
value: u32,
) -> Result<(), ArmError> {
crate::architecture::arm::core::cortex_m::write_core_reg(probe, reg.into(), value)?;
probe.flush()?;
Ok(())
}
fn check_pc(
&self,
probe: &mut dyn ArmMemoryInterface,
expected: u32,
) -> Result<(), ArmDebugSequenceError> {
let pc = self
.read_core_reg(probe, PC)
.map_err(|err| ArmDebugSequenceError::SequenceSpecific(err.into()))?;
if pc != expected {
let err = format!(
"The Cortex M7 should be at address {expected:#010X} but it's at {pc:#010X}"
);
return Err(ArmDebugSequenceError::SequenceSpecific(err.into()));
}
Ok(())
}
const BOOT_ROM_SPIN_ADDRESS: u32 = 0x00223104;
fn find_flexspi_image_reset_handler(
&self,
probe: &mut dyn ArmMemoryInterface,
) -> Result<Option<u32>, ArmError> {
const FLEXSPI1: u64 = 0x30000000;
const IVT: u64 = FLEXSPI1 + 0x1000;
tracing::debug!("Assuming that your CM7's program is in FlexSPI1 at {FLEXSPI1:#010X}");
let ivt_header = probe.read_word_32(IVT)?;
tracing::debug!("IVT Header: {ivt_header:#010X}");
if ivt_header & 0xFF != 0xD1 {
tracing::debug!("IVT tag is incorrect! Expected 0xD1 in {ivt_header:#010X}");
return Ok(None);
}
if (ivt_header >> 8) & 0xFFFF != 0x2000 {
tracing::debug!("IVT length is incorrect! {ivt_header:#010X}");
return Ok(None);
}
let ivt_version = ivt_header >> 24;
if !(0x40..=0x45).contains(&ivt_version) {
tracing::debug!("IVT version is invalid! {ivt_header:#010X}");
return Ok(None);
}
let reset_handler = if ivt_version == 0x40 {
let vector_table = probe.read_word_32(IVT + 4)?;
tracing::debug!("Vector table address: {vector_table:#010X}");
probe.read_word_32(u64::from(vector_table) + 4u64)?
} else {
probe.read_word_32(IVT + 4)?
};
tracing::debug!("Reset handler: {reset_handler:#010X}");
if reset_handler & 1 == 0 {
tracing::debug!(
"Is your reset handler actually a function address? Where's its thumb bit?"
);
return Ok(None);
}
Ok(Some(reset_handler))
}
fn use_boot_fuses_for_flexram(
&self,
probe: &mut dyn ArmMemoryInterface,
) -> Result<(), ArmError> {
const IOMUXC_GPR_GPR16: u64 = 0x400E_4040;
const FLEXRAM_BANK_CFG_SEL_MASK: u32 = 1 << 2;
let mut gpr16 = probe.read_word_32(IOMUXC_GPR_GPR16)?;
gpr16 &= !FLEXRAM_BANK_CFG_SEL_MASK;
probe.write_word_32(IOMUXC_GPR_GPR16, gpr16)?;
probe.flush()?;
Ok(())
}
}
impl ArmDebugSequence for MIMXRT11xx {
fn reset_catch_set(
&self,
_: &mut dyn ArmMemoryInterface,
_: probe_rs_target::CoreType,
_: Option<u64>,
) -> Result<(), ArmError> {
self.simulate_reset_catch.store(true, Ordering::Relaxed);
Ok(())
}
fn reset_catch_clear(
&self,
_: &mut dyn ArmMemoryInterface,
_: probe_rs_target::CoreType,
_: Option<u64>,
) -> Result<(), ArmError> {
self.simulate_reset_catch.store(false, Ordering::Relaxed);
Ok(())
}
fn reset_system(
&self,
probe: &mut dyn ArmMemoryInterface,
core_type: probe_rs_target::CoreType,
debug_base: Option<u64>,
) -> Result<(), ArmError> {
self.halt(probe, true)?;
self.use_boot_fuses_for_flexram(probe)?;
let debug_cache = DebugCache::from_target(probe)?;
self.clear_src_srmr_mask(probe)?;
let mut aircr = Aircr(0);
aircr.vectkey();
aircr.set_sysresetreq(true);
probe
.write_word_32(Aircr::get_mmio_address(), aircr.into())
.ok();
probe.flush().ok();
let ap = probe.fully_qualified_address();
let interface = probe.get_arm_debug_interface()?;
interface.reinitialize()?;
assert!(debug_base.is_none());
self.debug_core_start(interface, &ap, core_type, None, None)?;
self.wait_for_enable(probe, Duration::from_millis(300))?;
self.halt(probe, true)?;
self.check_pc(probe, Self::BOOT_ROM_SPIN_ADDRESS)?;
if let Some(pc) = self.find_flexspi_image_reset_handler(probe)? {
self.write_core_reg(probe, PC, pc)?
} else {
tracing::warn!(
"Could not find a valid reset handler in FlexSPI! Keeping the CM7 in the boot ROM."
);
}
debug_cache.restore(probe)?;
if !self.simulate_reset_catch.load(Ordering::Relaxed) {
self.halt(probe, false)?;
}
Ok(())
}
}
struct DebugCache {
fp_ctrl: FpCtrl,
fp_comps: Vec<FpRev2CompX>,
}
impl DebugCache {
fn from_target(probe: &mut dyn ArmMemoryInterface) -> Result<Self, ArmError> {
let fp_ctrl = FpCtrl(probe.read_word_32(FpCtrl::get_mmio_address())?);
Ok(Self {
fp_ctrl,
fp_comps: (0..fp_ctrl.num_code())
.map(|base_address| -> Result<FpRev2CompX, ArmError> {
let address = FpRev2CompX::get_mmio_address_from_base(base_address as u64 * 4)?;
let fp_comp = probe.read_word_32(address)?;
Ok(FpRev2CompX(fp_comp))
})
.collect::<Result<Vec<_>, _>>()?,
})
}
fn restore(mut self, probe: &mut dyn ArmMemoryInterface) -> Result<(), ArmError> {
self.fp_ctrl.set_key(true);
probe.write_word_32(FpCtrl::get_mmio_address(), self.fp_ctrl.into())?;
for (base, fp_comp) in self.fp_comps.into_iter().enumerate() {
probe.write_word_32(
FpRev2CompX::get_mmio_address_from_base(base as u64 * 4)?,
fp_comp.into(),
)?;
}
Ok(())
}
}