use std::{
sync::{
atomic::{AtomicBool, Ordering},
Arc,
},
thread,
time::{Duration, Instant},
};
use crate::{
architecture::arm::{
ap::{memory_ap::MemoryApType, AccessPortError, AccessPortType},
armv7m::{FpCtrl, FpRev2CompX},
core::{
armv7m::{Aircr, Dhcsr},
registers::cortex_m::PC,
},
memory::ArmMemoryInterface,
sequences::{ArmDebugSequence, ArmDebugSequenceError},
ArmError,
},
core::MemoryMappedRegister,
};
#[derive(Debug)]
pub struct MIMXRT10xx(());
impl MIMXRT10xx {
pub fn create() -> Arc<dyn ArmDebugSequence> {
Arc::new(Self(()))
}
fn check_core_type(&self, core_type: crate::CoreType) -> Result<(), ArmError> {
const EXPECTED: crate::CoreType = crate::CoreType::Armv7em;
if core_type != EXPECTED {
tracing::warn!(
"MIMXRT10xx core type supplied as {core_type:?}, but the actual core is a {EXPECTED:?}"
);
}
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(())
}
}
impl ArmDebugSequence for MIMXRT10xx {
fn reset_system(
&self,
interface: &mut dyn ArmMemoryInterface,
core_type: crate::CoreType,
_: Option<u64>,
) -> Result<(), ArmError> {
self.check_core_type(core_type)?;
self.use_boot_fuses_for_flexram(interface)?;
let mut aircr = Aircr(0);
aircr.vectkey();
aircr.set_sysresetreq(true);
interface
.write_word_32(Aircr::get_mmio_address(), aircr.into())
.ok();
interface.flush().ok();
thread::sleep(Duration::from_millis(100));
let start = Instant::now();
loop {
let dhcsr = match interface.read_word_32(Dhcsr::get_mmio_address()) {
Ok(val) => Dhcsr(val),
Err(ArmError::AccessPort {
source:
AccessPortError::RegisterRead { .. } | AccessPortError::RegisterWrite { .. },
..
}) => {
continue;
}
Err(err) => return Err(err),
};
if !dhcsr.s_reset_st() {
return Ok(());
}
if start.elapsed() >= Duration::from_millis(500) {
return Err(ArmError::Timeout);
}
}
}
}
#[derive(Debug)]
pub struct MIMXRT117x {
simulate_reset_catch: AtomicBool,
}
impl MIMXRT117x {
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;
let (interface, memory_ap) = probe.try_as_parts()?;
loop {
match memory_ap.generic_status(interface) {
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 MIMXRT1170's 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 MIMXRT117x {
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.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.ap().ap_address().clone();
let interface = probe.get_arm_communication_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(())
}
}