pub mod atsame5x;
pub mod efm32xg2;
pub mod infineon;
mod nrf;
pub mod nrf52;
pub mod nrf53;
pub mod nrf91;
pub mod nxp_armv7m;
pub mod nxp_armv8m;
pub mod stm32_armv6;
pub mod stm32_armv7;
pub mod stm32h7;
use std::{
error::Error,
sync::Arc,
thread,
time::{Duration, Instant},
};
use probe_rs_target::CoreType;
use crate::{architecture::arm::ArmProbeInterface, DebugProbeError, MemoryMappedRegister};
use super::{
ap::{AccessPortError, MemoryAp},
armv6m::Demcr,
communication_interface::{DapProbe, Initialized},
component::{TraceFunnel, TraceSink},
core::cortex_m::Dhcsr,
dp::{Abort, Ctrl, DebugPortError, DpAccess, Select, DPIDR},
memory::{
adi_v5_memory_interface::ArmProbe,
romtable::{CoresightComponent, PeripheralType},
},
ArmCommunicationInterface, ArmError, DpAddress, Pins, PortType, Register,
};
#[derive(thiserror::Error, Debug)]
pub enum ArmDebugSequenceError {
#[error("Core access requries debug_base to be specified, but it is not")]
DebugBaseNotSpecified,
#[error("Core access requries cti_base to be specified, but it is not")]
CtiBaseNotSpecified,
#[error("An error occurred in a debug sequnce: {0}")]
SequenceSpecific(#[from] Box<dyn Error + Send + Sync + 'static>),
}
impl ArmDebugSequenceError {
fn custom(message: impl Into<Box<dyn Error + Send + Sync + 'static>>) -> Self {
ArmDebugSequenceError::SequenceSpecific(message.into())
}
}
pub struct DefaultArmSequence(pub(crate) ());
impl DefaultArmSequence {
pub fn create() -> Arc<dyn ArmDebugSequence> {
Arc::new(Self(()))
}
}
impl ArmDebugSequence for DefaultArmSequence {}
fn armv7a_reset_catch_set(
core: &mut dyn ArmProbe,
debug_base: Option<u64>,
) -> Result<(), ArmError> {
use crate::architecture::arm::core::armv7a_debug_regs::Dbgprcr;
let debug_base =
debug_base.ok_or_else(|| ArmError::from(ArmDebugSequenceError::DebugBaseNotSpecified))?;
let address = Dbgprcr::get_mmio_address_from_base(debug_base)?;
let mut dbgprcr = Dbgprcr(core.read_word_32(address)?);
dbgprcr.set_hcwr(true);
core.write_word_32(address, dbgprcr.into())?;
Ok(())
}
fn armv7a_reset_catch_clear(
core: &mut dyn ArmProbe,
debug_base: Option<u64>,
) -> Result<(), ArmError> {
use crate::architecture::arm::core::armv7a_debug_regs::Dbgprcr;
let debug_base =
debug_base.ok_or_else(|| ArmError::from(ArmDebugSequenceError::DebugBaseNotSpecified))?;
let address = Dbgprcr::get_mmio_address_from_base(debug_base)?;
let mut dbgprcr = Dbgprcr(core.read_word_32(address)?);
dbgprcr.set_hcwr(false);
core.write_word_32(address, dbgprcr.into())?;
Ok(())
}
fn armv7a_reset_system(
interface: &mut dyn ArmProbe,
debug_base: Option<u64>,
) -> Result<(), ArmError> {
use crate::architecture::arm::core::armv7a_debug_regs::{Dbgprcr, Dbgprsr};
let debug_base =
debug_base.ok_or_else(|| ArmError::from(ArmDebugSequenceError::DebugBaseNotSpecified))?;
let address = Dbgprcr::get_mmio_address_from_base(debug_base)?;
let mut dbgprcr = Dbgprcr(interface.read_word_32(address)?);
dbgprcr.set_cwrr(true);
interface.write_word_32(address, dbgprcr.into())?;
let address = Dbgprsr::get_mmio_address_from_base(debug_base)?;
loop {
let dbgprsr = Dbgprsr(interface.read_word_32(address)?);
if dbgprsr.sr() {
break;
}
}
Ok(())
}
fn armv7a_core_start(core: &mut dyn ArmProbe, debug_base: Option<u64>) -> Result<(), ArmError> {
use crate::architecture::arm::core::armv7a_debug_regs::{Dbgdsccr, Dbgdscr, Dbgdsmcr, Dbglar};
let debug_base =
debug_base.ok_or_else(|| ArmError::from(ArmDebugSequenceError::DebugBaseNotSpecified))?;
tracing::debug!(
"Starting debug for ARMv7-A core with registers at {:#X}",
debug_base
);
let address = Dbglar::get_mmio_address_from_base(debug_base)?;
core.write_word_32(address, Dbglar(0).into())?;
let address = Dbgdsccr::get_mmio_address_from_base(debug_base)?;
core.write_word_32(address, Dbgdsccr(0).into())?;
let address = Dbgdsmcr::get_mmio_address_from_base(debug_base)?;
core.write_word_32(address, Dbgdsmcr(0).into())?;
let address = Dbgdscr::get_mmio_address_from_base(debug_base)?;
let mut dbgdscr = Dbgdscr(core.read_word_32(address)?);
if dbgdscr.hdbgen() {
tracing::debug!("Core is already in debug mode, no need to enable it again");
return Ok(());
}
dbgdscr.set_hdbgen(true);
core.write_word_32(address, dbgdscr.into())?;
Ok(())
}
fn armv8a_reset_catch_set(
core: &mut dyn ArmProbe,
debug_base: Option<u64>,
) -> Result<(), ArmError> {
use crate::architecture::arm::core::armv8a_debug_regs::Edecr;
let debug_base =
debug_base.ok_or_else(|| ArmError::from(ArmDebugSequenceError::DebugBaseNotSpecified))?;
let address = Edecr::get_mmio_address_from_base(debug_base)?;
let mut edecr = Edecr(core.read_word_32(address)?);
edecr.set_rce(true);
core.write_word_32(address, edecr.into())?;
Ok(())
}
fn armv8a_reset_catch_clear(
core: &mut dyn ArmProbe,
debug_base: Option<u64>,
) -> Result<(), ArmError> {
use crate::architecture::arm::core::armv8a_debug_regs::Edecr;
let debug_base =
debug_base.ok_or_else(|| ArmError::from(ArmDebugSequenceError::DebugBaseNotSpecified))?;
let address = Edecr::get_mmio_address_from_base(debug_base)?;
let mut edecr = Edecr(core.read_word_32(address)?);
edecr.set_rce(false);
core.write_word_32(address, edecr.into())?;
Ok(())
}
fn armv8a_reset_system(
interface: &mut dyn ArmProbe,
debug_base: Option<u64>,
) -> Result<(), ArmError> {
use crate::architecture::arm::core::armv8a_debug_regs::{Edprcr, Edprsr};
let debug_base =
debug_base.ok_or_else(|| ArmError::from(ArmDebugSequenceError::DebugBaseNotSpecified))?;
let address = Edprcr::get_mmio_address_from_base(debug_base)?;
let mut edprcr = Edprcr(interface.read_word_32(address)?);
edprcr.set_cwrr(true);
interface.write_word_32(address, edprcr.into())?;
let address = Edprsr::get_mmio_address_from_base(debug_base)?;
loop {
let edprsr = Edprsr(interface.read_word_32(address)?);
if edprsr.sr() {
break;
}
}
Ok(())
}
fn armv8a_core_start(
core: &mut dyn ArmProbe,
debug_base: Option<u64>,
cti_base: Option<u64>,
) -> Result<(), ArmError> {
use crate::architecture::arm::core::armv8a_debug_regs::{
CtiControl, CtiGate, CtiOuten, Edlar, Edscr,
};
let debug_base =
debug_base.ok_or_else(|| ArmError::from(ArmDebugSequenceError::DebugBaseNotSpecified))?;
let cti_base =
cti_base.ok_or_else(|| ArmError::from(ArmDebugSequenceError::CtiBaseNotSpecified))?;
tracing::debug!(
"Starting debug for ARMv8-A core with registers at {:#X}",
debug_base
);
let address = Edlar::get_mmio_address_from_base(debug_base)?;
core.write_word_32(address, Edlar(0).into())?;
let mut cticontrol = CtiControl(0);
cticontrol.set_glben(true);
let address = CtiControl::get_mmio_address_from_base(cti_base)?;
core.write_word_32(address, cticontrol.into())?;
let address = CtiGate::get_mmio_address_from_base(cti_base)?;
core.write_word_32(address, 0)?;
let mut ctiouten = CtiOuten(0);
ctiouten.set_outen(0, 1);
let address = CtiOuten::get_mmio_address_from_base(cti_base)?;
core.write_word_32(address, ctiouten.into())?;
let mut ctiouten = CtiOuten(0);
ctiouten.set_outen(1, 1);
let address = CtiOuten::get_mmio_address_from_base(cti_base)? + 4;
core.write_word_32(address, ctiouten.into())?;
let address = Edscr::get_mmio_address_from_base(debug_base)?;
let mut edscr = Edscr(core.read_word_32(address)?);
if edscr.hde() {
tracing::debug!("Core is already in debug mode, no need to enable it again");
return Ok(());
}
edscr.set_hde(true);
core.write_word_32(address, edscr.into())?;
Ok(())
}
fn cortex_m_core_start(core: &mut dyn ArmProbe) -> Result<(), ArmError> {
use crate::architecture::arm::core::armv7m::Dhcsr;
let current_dhcsr = Dhcsr(core.read_word_32(Dhcsr::get_mmio_address())?);
if current_dhcsr.c_debugen() {
tracing::debug!("Core is already in debug mode, no need to enable it again");
return Ok(());
}
let mut dhcsr = Dhcsr(0);
dhcsr.set_c_debugen(true);
dhcsr.enable_write();
core.write_word_32(Dhcsr::get_mmio_address(), dhcsr.into())?;
Ok(())
}
fn cortex_m_reset_catch_clear(core: &mut dyn ArmProbe) -> Result<(), ArmError> {
use crate::architecture::arm::core::armv7m::Demcr;
let mut demcr = Demcr(core.read_word_32(Demcr::get_mmio_address())?);
demcr.set_vc_corereset(false);
core.write_word_32(Demcr::get_mmio_address(), demcr.into())?;
Ok(())
}
fn cortex_m_reset_catch_set(core: &mut dyn ArmProbe) -> Result<(), ArmError> {
use crate::architecture::arm::core::armv7m::{Demcr, Dhcsr};
let mut demcr = Demcr(core.read_word_32(Demcr::get_mmio_address())?);
demcr.set_vc_corereset(true);
core.write_word_32(Demcr::get_mmio_address(), demcr.into())?;
let _ = core.read_word_32(Dhcsr::get_mmio_address())?;
Ok(())
}
fn cortex_m_reset_system(interface: &mut dyn ArmProbe) -> Result<(), ArmError> {
use crate::architecture::arm::core::armv7m::{Aircr, Dhcsr};
let mut aircr = Aircr(0);
aircr.vectkey();
aircr.set_sysresetreq(true);
interface.write_word_32(Aircr::get_mmio_address(), aircr.into())?;
let start = Instant::now();
while start.elapsed() < Duration::from_micros(50_0000) {
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() {
return Ok(());
}
}
Err(ArmError::Timeout)
}
pub trait ArmDebugSequence: Send + Sync {
#[doc(alias = "ResetHardwareAssert")]
fn reset_hardware_assert(&self, interface: &mut dyn DapProbe) -> Result<(), ArmError> {
let mut n_reset = Pins(0);
n_reset.set_nreset(true);
let _ = interface.swj_pins(0, n_reset.0 as u32, 0)?;
Ok(())
}
#[doc(alias = "ResetHardwareDeassert")]
fn reset_hardware_deassert(&self, memory: &mut dyn ArmProbe) -> Result<(), ArmError> {
let mut n_reset = Pins(0);
n_reset.set_nreset(true);
let n_reset = n_reset.0 as u32;
let can_read_pins = memory.swj_pins(n_reset, n_reset, 0)? != 0xffff_ffff;
if can_read_pins {
let start = Instant::now();
while start.elapsed() < Duration::from_secs(1) {
if Pins(memory.swj_pins(n_reset, n_reset, 0)? as u8).nreset() {
return Ok(());
}
thread::sleep(Duration::from_millis(100));
}
Err(ArmError::Timeout)
} else {
thread::sleep(Duration::from_millis(100));
Ok(())
}
}
#[doc(alias = "DebugPortSetup")]
fn debug_port_setup(&self, interface: &mut dyn DapProbe) -> Result<(), ArmError> {
interface.swj_sequence(51, 0x0007_FFFF_FFFF_FFFF)?;
match interface.active_protocol() {
Some(crate::WireProtocol::Jtag) => {
interface.swj_sequence(16, 0xE73C)?;
interface.swj_sequence(6, 0x3F)?;
interface.jtag_sequence(1, false, 0x01)?;
interface.configure_jtag()?;
}
Some(crate::WireProtocol::Swd) => {
interface.swj_sequence(16, 0xE79E)?;
interface.swj_sequence(51, 0x0007_FFFF_FFFF_FFFF)?;
interface.swj_sequence(3, 0x00)?;
}
_ => {
return Err(ArmDebugSequenceError::SequenceSpecific(
"Cannot detect current protocol".into(),
)
.into());
}
}
let _ = interface.raw_read_register(PortType::DebugPort, DPIDR::ADDRESS);
Ok(())
}
#[doc(alias = "DebugPortStart")]
fn debug_port_start(
&self,
interface: &mut ArmCommunicationInterface<Initialized>,
dp: DpAddress,
) -> Result<(), ArmError> {
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)?;
interface.write_dp_register(dp, Select(0))?;
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_micros(100_0000) {
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 ctrl_reg: Ctrl = interface.read_dp_register(dp)?;
if !(ctrl_reg.csyspwrupack() && ctrl_reg.cdbgpwrupack()) {
tracing::error!("Debug power request failed");
return Err(DebugPortError::TargetPowerUpFailed.into());
}
}
Ok(())
}
#[doc(alias = "DebugCoreStart")]
fn debug_core_start(
&self,
interface: &mut dyn ArmProbeInterface,
core_ap: MemoryAp,
core_type: CoreType,
debug_base: Option<u64>,
cti_base: Option<u64>,
) -> Result<(), ArmError> {
let mut core = interface.memory_interface(core_ap)?;
match core_type {
CoreType::Armv7a => armv7a_core_start(&mut *core, debug_base),
CoreType::Armv8a => armv8a_core_start(&mut *core, debug_base, cti_base),
CoreType::Armv6m | CoreType::Armv7m | CoreType::Armv7em | CoreType::Armv8m => {
cortex_m_core_start(&mut *core)
}
_ => panic!("Logic inconsistency bug - non ARM core type passed {core_type:?}"),
}
}
#[doc(alias = "ResetCatchSet")]
fn reset_catch_set(
&self,
core: &mut dyn ArmProbe,
core_type: CoreType,
debug_base: Option<u64>,
) -> Result<(), ArmError> {
match core_type {
CoreType::Armv7a => armv7a_reset_catch_set(core, debug_base),
CoreType::Armv8a => armv8a_reset_catch_set(core, debug_base),
CoreType::Armv6m | CoreType::Armv7m | CoreType::Armv7em | CoreType::Armv8m => {
cortex_m_reset_catch_set(core)
}
_ => panic!("Logic inconsistency bug - non ARM core type passed {core_type:?}"),
}
}
#[doc(alias = "ResetCatchClear")]
fn reset_catch_clear(
&self,
core: &mut dyn ArmProbe,
core_type: CoreType,
debug_base: Option<u64>,
) -> Result<(), ArmError> {
match core_type {
CoreType::Armv7a => armv7a_reset_catch_clear(core, debug_base),
CoreType::Armv8a => armv8a_reset_catch_clear(core, debug_base),
CoreType::Armv6m | CoreType::Armv7m | CoreType::Armv7em | CoreType::Armv8m => {
cortex_m_reset_catch_clear(core)
}
_ => panic!("Logic inconsistency bug - non ARM core type passed {core_type:?}"),
}
}
fn trace_start(
&self,
interface: &mut dyn ArmProbeInterface,
components: &[CoresightComponent],
_sink: &TraceSink,
) -> Result<(), ArmError> {
for trace_funnel in components
.iter()
.filter_map(|comp| comp.find_component(PeripheralType::TraceFunnel))
{
let mut funnel = TraceFunnel::new(interface, trace_funnel);
funnel.unlock()?;
funnel.enable_port(0xFF)?;
}
Ok(())
}
#[doc(alias = "ResetSystem")]
fn reset_system(
&self,
interface: &mut dyn ArmProbe,
core_type: CoreType,
debug_base: Option<u64>,
) -> Result<(), ArmError> {
match core_type {
CoreType::Armv7a => armv7a_reset_system(interface, debug_base),
CoreType::Armv8a => armv8a_reset_system(interface, debug_base),
CoreType::Armv6m | CoreType::Armv7m | CoreType::Armv7em | CoreType::Armv8m => {
cortex_m_reset_system(interface)
}
_ => panic!("Logic inconsistency bug - non ARM core type passed {core_type:?}"),
}
}
#[doc(alias = "DebugDeviceUnlock")]
fn debug_device_unlock(
&self,
_interface: &mut dyn ArmProbeInterface,
_default_ap: MemoryAp,
_permissions: &crate::Permissions,
) -> Result<(), ArmError> {
tracing::debug!("debug_device_unlock - empty by default");
Ok(())
}
#[doc(alias = "RecoverSupportStart")]
fn recover_support_start(&self, _interface: &mut dyn ArmProbe) -> Result<(), ArmError> {
Ok(())
}
#[doc(alias = "DebugCoreStop")]
fn debug_core_stop(
&self,
interface: &mut dyn ArmProbe,
core_type: CoreType,
) -> Result<(), ArmError> {
if core_type.is_cortex_m() {
let mut dhcsr = Dhcsr(0);
dhcsr.enable_write();
interface.write_word_32(Dhcsr::get_mmio_address(), dhcsr.0)?;
interface.write_word_32(Demcr::get_mmio_address(), 0x0)?;
}
Ok(())
}
fn debug_erase_sequence(&self) -> Option<Arc<dyn DebugEraseSequence>> {
None
}
}
pub trait DebugEraseSequence: Send + Sync {
fn erase_all(&self, _interface: &mut dyn ArmProbeInterface) -> Result<(), ArmError> {
Err(
DebugProbeError::NotImplemented("Debug erase sequence is not available on this device")
.into(),
)
}
}