probe_rs/architecture/arm/core/armv7m.rs
1//! Register types and the core interface for armv7-M
2
3use super::{
4 CortexMState, Dfsr,
5 cortex_m::Mvfr0,
6 registers::cortex_m::{
7 CORTEX_M_CORE_REGISTERS, CORTEX_M_WITH_FP_CORE_REGISTERS, FP, PC, RA, SP,
8 },
9};
10use crate::{
11 BreakpointCause, CoreRegister, CoreType, InstructionSet, MemoryInterface,
12 architecture::arm::{
13 ArmError, core::registers::cortex_m::XPSR, memory::ArmMemoryInterface,
14 sequences::ArmDebugSequence,
15 },
16 core::{
17 Architecture, CoreInformation, CoreInterface, CoreRegisters, CoreStatus, HaltReason,
18 MemoryMappedRegister, RegisterId, RegisterValue, VectorCatchCondition,
19 },
20 error::Error,
21 memory::{CoreMemoryInterface, valid_32bit_address},
22};
23use bitfield::bitfield;
24use std::{
25 mem::size_of,
26 sync::Arc,
27 time::{Duration, Instant},
28};
29
30bitfield! {
31 /// Debug Halting Control and Status Register, DHCSR (see armv7-M Architecture Reference Manual C1.6.2)
32 ///
33 /// To write this register successfully, you need to set the debug key via [`Dhcsr::enable_write`] first!
34 #[derive(Copy, Clone)]
35 pub struct Dhcsr(u32);
36 impl Debug;
37 /// Indicates whether the processor has been reset since the last read of DHCSR:
38 /// `0`: No reset since last DHCSR read.\
39 /// `1`: At least one reset since last DHCSR read.
40 ///
41 /// This is a sticky bit, that clears to `0` on a read of DHCSR.
42 pub s_reset_st, _: 25;
43 /// When not in Debug state, indicates whether the processor has completed
44 /// the execution of an instruction since the last read of DHCSR:
45 ///
46 /// `0`: No instruction has completed since last DHCSR read.\
47 /// `1`: At least one instructions has completed since last DHCSR read.
48 ///
49 /// This is a sticky bit, that clears to `0` on a read of DHCSR.
50 ///
51 /// This bit is UNKNOWN:
52 ///
53 /// - after a Local reset, but is set to `1` as soon as the processor completes
54 /// execution of an instruction.
55 /// - when S_LOCKUP is set to 1.
56 /// - when S_HALT is set to 1.
57 ///
58 /// When the processor is not in Debug state, a debugger can check this bit to
59 /// determine if the processor is stalled on a load, store or fetch access.
60 pub s_retire_st, _: 24;
61 /// Indicates whether the processor is locked up because of an unrecoverable
62 /// exception:
63 ///
64 /// `0`: Not locked up.\
65 /// `1`: Locked up.
66 ///
67 /// See Unrecoverable exception cases on page B1-206 for more information.
68 ///
69 /// This bit can only read as `1` when accessed by a remote debugger using the
70 /// DAP. The value of `1` indicates that the processor is running but locked up.
71 ///
72 /// The bit clears to `0` when the processor enters Debug state.
73 pub s_lockup, _: 19;
74 /// Indicates whether the processor is sleeping:
75 ///
76 /// `0`: Not sleeping.\
77 /// `1`: Sleeping.
78 ///
79 /// The debugger must set the DHCSR.C_HALT bit to `1` to gain control, or
80 /// wait for an interrupt or other wakeup event to wakeup the system
81 pub s_sleep, _: 18;
82 /// Indicates whether the processor is in Debug state:
83 ///
84 /// `0`: Not in Debug state.\
85 /// `1`: In Debug state.
86 pub s_halt, _: 17;
87 /// A handshake flag for transfers through the DCRDR:
88 ///
89 /// - Writing to DCRSR clears the bit to 0.
90 /// - Completion of the DCRDR transfer then sets the bit to 1.
91 ///
92 /// For more information about DCRDR transfers see Debug Core Register
93 /// Data Register, DCRDR on page C1-292.
94 ///
95 /// `0`: There has been a write to the DCRDR, but the transfer is not complete.\
96 /// `1`: The transfer to or from the DCRDR is complete.
97 ///
98 /// This bit is only valid when the processor is in Debug state, otherwise the
99 /// bit is UNKNOWN.
100 pub s_regrdy, _: 16;
101 /// Allow imprecise entry to Debug state. The actions on writing to this bit are:
102 ///
103 /// `0`: No action.\
104 /// `1`: Allow imprecise entry to Debug state, for example by forcing any stalled load
105 /// or store instruction to complete.
106 ///
107 /// Setting this bit to `1` allows a debugger to request imprecise entry to Debug state.
108 ///
109 /// The effect of setting this bit to `1` is UNPREDICTABLE unless the DHCSR write also sets
110 /// C_DEBUGEN and C_HALT to 1. This means that if the processor is not already in Debug
111 /// state it enters Debug state when the stalled instruction completes.
112 ///
113 /// Writing `1` to this bit makes the state of the memory system UNPREDICTABLE. Therefore, if a
114 /// debugger writes `1` to this bit it must reset the processor before leaving Debug state.
115 ///
116 /// **Note**
117 ///
118 /// - A debugger can write to the DHCSR to clear this bit to 0. However, this does not
119 /// remove the UNPREDICTABLE state of the memory system caused by setting C_SNAPSTALL to 1.
120 /// - The architecture does not guarantee that setting this bit to `1` will force entry to Debug state.
121 /// - Arm strongly recommends that a value of `1` is never written to C_SNAPSTALL when
122 /// the processor is in Debug state.
123 ///
124 /// A power-on reset sets this bit to `0`.
125 pub c_snapstall, set_c_snapstall: 5;
126 /// When debug is enabled, the debugger can write to this bit to mask
127 /// PendSV, SysTick and external configurable interrupts:
128 ///
129 /// `0`: Do not mask.\
130 /// `1`: Mask PendSV, SysTick and external configurable interrupts.
131 ///
132 /// The effect of any attempt to change the value of this bit is UNPREDICTABLE
133 /// unless both:
134 ///
135 /// - before the write to DHCSR, the value of the C_HALT bit is `1`.
136 /// - the write to the DHCSR that changes the C_MASKINTS bit also
137 /// writes `1` to the C_HALT bit.
138 ///
139 /// This means that a single write to DHCSR cannot set the C_HALT to `0` and
140 /// change the value of the C_MASKINTS bit.
141 ///
142 /// The bit does not affect NMI. When DHCSR.C_DEBUGEN is set to `0`, the
143 /// value of this bit is UNKNOWN.
144 ///
145 /// For more information about the use of this bit see Table C1-9 on
146 /// page C1-282.
147 ///
148 /// This bit is UNKNOWN after a power-on reset.
149 pub c_maskints, set_c_maskints: 3;
150 /// Processor step bit. The effects of writes to this bit are:
151 ///
152 /// `0`: Single-stepping disabled.\
153 /// `1`: Single-stepping enabled.
154 ///
155 /// For more information about the use of this bit see Table C1-9 on
156 /// page C1-282.
157 ///
158 /// This bit is UNKNOWN after a power-on reset.
159 pub c_step, set_c_step: 2;
160 /// Processor halt bit. The effects of writes to this bit are:
161 ///
162 /// `0`: Request a halted processor to run.\
163 /// `1`: Request a running processor to halt.
164 ///
165 /// Table C1-9 on page C1-282 shows the effect of writes to this bit when the
166 /// processor is in Debug state.
167 ///
168 /// This bit is `0` after a System reset
169 pub c_halt, set_c_halt: 1;
170 /// Halting debug enable bit:
171 ///
172 /// `0`: Halting debug disabled.\
173 /// `1`: Halting debug enabled.
174 ///
175 /// If a debugger writes to DHCSR to change the value of this bit from `0` to
176 /// `1`, it must also write `0` to the C_MASKINTS bit, otherwise behavior is UNPREDICTABLE.
177 ///
178 /// This bit can only be written from the DAP. Access to the DHCSR from
179 /// software running on the processor is IMPLEMENTATION DEFINED.
180 ///
181 /// However, writes to this bit from software running on the processor are ignored.
182 ///
183 /// This bit is `0` after a power-on reset.
184 pub c_debugen, set_c_debugen: 0;
185}
186
187impl Dhcsr {
188 /// This function sets the bit to enable writes to this register.
189 pub fn enable_write(&mut self) {
190 self.0 &= !(0xffff << 16);
191 self.0 |= 0xa05f << 16;
192 }
193}
194
195impl From<u32> for Dhcsr {
196 fn from(value: u32) -> Self {
197 Self(value)
198 }
199}
200
201impl From<Dhcsr> for u32 {
202 fn from(value: Dhcsr) -> Self {
203 value.0
204 }
205}
206
207impl MemoryMappedRegister<u32> for Dhcsr {
208 const ADDRESS_OFFSET: u64 = 0xE000_EDF0;
209 const NAME: &'static str = "DHCSR";
210}
211
212/// Debug Core Register Data Register, DCRDR (see armv7-M Architecture Reference Manual C1.6.3)
213#[derive(Debug, Copy, Clone)]
214pub struct Dcrdr(u32);
215
216impl From<u32> for Dcrdr {
217 fn from(value: u32) -> Self {
218 Self(value)
219 }
220}
221
222impl From<Dcrdr> for u32 {
223 fn from(value: Dcrdr) -> Self {
224 value.0
225 }
226}
227
228impl MemoryMappedRegister<u32> for Dcrdr {
229 const ADDRESS_OFFSET: u64 = 0xE000_EDF8;
230 const NAME: &'static str = "DCRDR";
231}
232
233bitfield! {
234 /// Application Interrupt and Reset Control Register, AIRCR (see armv7-M Architecture Reference Manual B3.2.6)
235 ///
236 /// [`Aircr::vectkey`] must be called before this register can effectively be written!
237 #[derive(Copy, Clone)]
238 pub struct Aircr(u32);
239 impl Debug;
240 /// Vector Key. The value 0x05FA must be written to this register, otherwise
241 /// the register write is UNPREDICTABLE.
242 get_vectkeystat, set_vectkey: 31,16;
243 /// Indicates the memory system data endianness:
244 ///
245 /// `0`: little endian.\
246 /// `1`: big endian.
247 ///
248 /// See Endian support on page A3-44 for more information.
249 pub endianness, set_endianness: 15;
250 /// Priority grouping, indicates the binary point position.
251 ///
252 /// For information about the use of this field see Priority grouping on page B1-527.
253 ///
254 /// This field resets to `0b000`.
255 pub prigroup, set_prigroup: 10,8;
256 /// System Reset Request:
257 ///
258 /// `0`: do not request a reset.\
259 /// `1`: request reset.
260 ///
261 /// Writing `1` to this bit asserts a signal to request a reset by the external
262 /// system. The system components that are reset by this request are
263 /// IMPLEMENTATION DEFINED. A Local reset is required as part of a system
264 /// reset request.
265 ///
266 /// A Local reset clears this bit to `0`.
267 ///
268 /// See Reset management on page B1-208 for more information
269 pub sysresetreq, set_sysresetreq: 2;
270 /// Clears all active state information for fixed and configurable exceptions:
271 ///
272 /// `0`: do not clear state information.\
273 /// `1`: clear state information.
274 ///
275 /// The effect of writing a `1` to this bit if the processor is not halted in Debug
276 /// state is UNPREDICTABLE.
277 pub vectclractive, set_vectclractive: 1;
278 /// Writing `1` to this bit causes a local system reset, see Reset management on page B1-559 for
279 /// more information. This bit self-clears.
280 ///
281 /// The effect of writing a `1` to this bit if the processor is not halted in Debug state is UNPREDICTABLE.
282 ///
283 /// When the processor is halted in Debug state, if a write to the register writes a `1` to both
284 /// VECTRESET and SYSRESETREQ, the behavior is UNPREDICTABLE.
285 ///
286 /// This bit is write only.
287 pub vectreset, set_vectreset: 0;
288}
289
290impl From<u32> for Aircr {
291 fn from(value: u32) -> Self {
292 Self(value)
293 }
294}
295
296impl From<Aircr> for u32 {
297 fn from(value: Aircr) -> Self {
298 value.0
299 }
300}
301
302impl Aircr {
303 /// Must be called before writing the register.
304 pub fn vectkey(&mut self) {
305 self.set_vectkey(0x05FA);
306 }
307
308 /// Verifies that the vector key is correct (see [`Aircr::vectkey`])
309 pub fn vectkeystat(&self) -> bool {
310 self.get_vectkeystat() == 0xFA05
311 }
312}
313
314impl MemoryMappedRegister<u32> for Aircr {
315 const ADDRESS_OFFSET: u64 = 0xE000_ED0C;
316 const NAME: &'static str = "AIRCR";
317}
318
319bitfield! {
320 /// Debug Exception and Monitor Control Register, DEMCR (see armv7-M Architecture Reference Manual C1.6.5)
321 #[derive(Copy, Clone)]
322 pub struct Demcr(u32);
323 impl Debug;
324 /// Global enable for DWT and ITM features
325 pub trcena, set_trcena: 24;
326 /// DebugMonitor semaphore bit
327 pub mon_req, set_mon_req: 19;
328 /// Step the processor?
329 pub mon_step, set_mon_step: 18;
330 /// Sets or clears the pending state of the DebugMonitor exception
331 pub mon_pend, set_mon_pend: 17;
332 /// Enable the DebugMonitor exception
333 pub mon_en, set_mon_en: 16;
334 /// Enable halting debug trap on a HardFault exception
335 pub vc_harderr, set_vc_harderr: 10;
336 /// Enable halting debug trap on a fault occurring during exception entry
337 /// or exception return
338 pub vc_interr, set_vc_interr: 9;
339 /// Enable halting debug trap on a BusFault exception
340 pub vc_buserr, set_vc_buserr: 8;
341 /// Enable halting debug trap on a UsageFault exception caused by a state
342 /// information error, for example an Undefined Instruction exception
343 pub vc_staterr, set_vc_staterr: 7;
344 /// Enable halting debug trap on a UsageFault exception caused by a
345 /// checking error, for example an alignment check error
346 pub vc_chkerr, set_vc_chkerr: 6;
347 /// Enable halting debug trap on a UsageFault caused by an access to a
348 /// Coprocessor
349 pub vc_nocperr, set_vc_nocperr: 5;
350 /// Enable halting debug trap on a MemManage exception.
351 pub vc_mmerr, set_vc_mmerr: 4;
352 /// Enable Reset Vector Catch
353 pub vc_corereset, set_vc_corereset: 0;
354}
355
356impl From<u32> for Demcr {
357 fn from(value: u32) -> Self {
358 Self(value)
359 }
360}
361
362impl From<Demcr> for u32 {
363 fn from(value: Demcr) -> Self {
364 value.0
365 }
366}
367
368impl MemoryMappedRegister<u32> for Demcr {
369 const ADDRESS_OFFSET: u64 = 0xe000_edfc;
370 const NAME: &'static str = "DEMCR";
371}
372
373bitfield! {
374 /// Flash Patch Control Register, FP_CTRL (see armv7-M Architecture Reference Manual C1.11.3)
375 #[derive(Copy,Clone)]
376 pub struct FpCtrl(u32);
377 impl Debug;
378 /// Flash Patch breakpoint architecture revision:
379 ///
380 /// `0b0000` Flash Patch breakpoint version 1.\
381 /// `0b0001` Flash Patch breakpoint version 2. Supports breakpoints on any location in the 4GB address range.
382 pub rev, _: 31, 28;
383 num_code_1, _: 14, 12;
384 /// The number of literal address comparators supported, starting from NUM_CODE upwards.
385 /// UNK/SBZP if Flash Patch is not implemented. Flash Patch is not implemented if `FP_REMAP[29]` is `0`.
386 ///
387 /// If this field is zero, the implementation does not support literal comparators.
388 pub num_lit, _: 11, 8;
389 num_code_0, _: 7, 4;
390 /// On any write to FP_CTRL, this bit must be `1`. A write to the register with this bit set to zero
391 /// is ignored. The Flash Patch Breakpoint unit ignores the write unless this bit is `1`.
392 pub _, set_key: 1;
393 /// Enable bit for the FPB:
394 ///
395 /// `0`: Flash Patch breakpoint disabled.\
396 /// `1`: Flash Patch breakpoint enabled.
397 ///
398 /// A power-on reset clears this bit to `0`.
399 pub enable, set_enable: 0;
400}
401
402impl FpCtrl {
403 /// The number of instruction address comparators.
404 /// If NUM_CODE is zero, the implementation does not support any instruction address comparators.
405 pub fn num_code(&self) -> u32 {
406 (self.num_code_1() << 4) | self.num_code_0()
407 }
408}
409
410impl MemoryMappedRegister<u32> for FpCtrl {
411 const ADDRESS_OFFSET: u64 = 0xE000_2000;
412 const NAME: &'static str = "FP_CTRL";
413}
414
415impl From<u32> for FpCtrl {
416 fn from(value: u32) -> Self {
417 FpCtrl(value)
418 }
419}
420
421impl From<FpCtrl> for u32 {
422 fn from(value: FpCtrl) -> Self {
423 value.0
424 }
425}
426
427bitfield! {
428 /// Flash Patch Comparator register, FP_COMPn (see armv7-M Architecture Reference Manual C1.11.5)
429 #[derive(Copy,Clone)]
430 pub struct FpRev1CompX(u32);
431 impl Debug;
432 /// For an instruction address comparator:
433 ///
434 /// Defines the behavior when the COMP address is matched:
435 ///
436 /// `00` Remap to remap address, see Flash Patch Remap register,
437 /// FP_REMAP on page C1-758.
438 ///
439 /// When the comparators are enabled in the FP_CTRL register, if the
440 /// implementation does not support remapping, the effect of an
441 /// instruction address match with an enabled comparator with
442 /// REPLACE programmed to 0b00 is UNPREDICTABLE.
443 ///
444 /// `01`: Breakpoint on instruction at `'000':COMP:'00'`.\
445 /// `10`: Breakpoint on instruction at `'000':COMP:'10'`.\
446 /// `11`: Breakpoint on both instructions at `'000':COMP:'00'` and `'000':COMP:'10'`.
447 ///
448 /// The reset value of this field is UNKNOWN.
449 ///
450 /// For a literal address comparator:
451 ///
452 /// Field is UNK/SBZP
453 pub replace, set_replace: 31, 30;
454 /// Bits `[28:2]` of the address to compare with addresses from the Code memory region,
455 /// see The system address map on page B3-592. Bits `[31:29]` of the address for comparison are zero.
456 ///
457 /// For a literal address or instruction address remap, bits `[1:0]` of the comparison are also zero.
458 ///
459 /// For an instruction address breakpoint, bits `[1:0]` of the comparison are encoded by the REPLACE field.
460 ///
461 /// If a match occurs:
462 ///
463 /// - For an instruction address comparator, the REPLACE field defines the required action.
464 /// - For a literal address comparator, the FPB remaps the access, see Flash Patch Remap register, FP_REMAP on page C1-758.
465 ///
466 /// The reset value of this field is UNKNOWN.
467 pub comp, set_comp: 28, 2;
468 /// Enable bit for this comparator:
469 ///
470 /// `0`: Comparator disabled.\
471 /// `1`: Comparator enabled.
472 ///
473 /// A power-on reset clears this bit to `0`.
474 pub enable, set_enable: 0;
475}
476
477impl MemoryMappedRegister<u32> for FpRev1CompX {
478 const ADDRESS_OFFSET: u64 = 0xE000_2008;
479 const NAME: &'static str = "FP_CTRL";
480}
481
482impl From<u32> for FpRev1CompX {
483 fn from(value: u32) -> Self {
484 FpRev1CompX(value)
485 }
486}
487
488impl From<FpRev1CompX> for u32 {
489 fn from(value: FpRev1CompX) -> Self {
490 value.0
491 }
492}
493
494impl FpRev1CompX {
495 /// Get the correct comparator value stored at the given address
496 /// This will adjust the `FpRev1CompX.comp() result based on the `FpRev1CompX.replace()` specification
497 /// NOTE: Does not support a `replace value of '11'
498 fn get_breakpoint_comparator(register_value: u32) -> Result<u32, Error> {
499 let fp1_val = FpRev1CompX::from(register_value);
500 if fp1_val.replace() == 0b01 {
501 Ok(fp1_val.comp() << 2)
502 } else if fp1_val.replace() == 0b10 {
503 Ok((fp1_val.comp() << 2) | 0x2)
504 } else {
505 Err(Error::Arm(ArmError::Other(format!(
506 "Unsupported breakpoint comparator value {:#08x} for HW breakpoint. Breakpoint must be on half-word boundaries",
507 fp1_val.0
508 ))))
509 }
510 }
511 /// Get the correct register configuration which enables
512 /// a hardware breakpoint at the given address.
513 /// NOTE: Does not support a `replace` value of '11'
514 pub(crate) fn breakpoint_configuration(address: u32) -> Result<Self, ArmError> {
515 let mut reg = FpRev1CompX::from(0);
516
517 // The highest 3 bits of the address have to be zero, otherwise the breakpoint cannot
518 // be set at the address.
519 if address >= 0x2000_0000 {
520 return Err(ArmError::UnsupportedBreakpointAddress(address));
521 }
522
523 let comp_val = (address & 0x1f_ff_ff_fc) >> 2;
524
525 // the replace value decides if the upper or lower half
526 // word is matched for the break point
527 let replace_val = if (address & 0x3) == 0 {
528 0b01 // lower half word
529 } else {
530 0b10 // upper half word
531 };
532
533 reg.set_replace(replace_val);
534 reg.set_comp(comp_val);
535 reg.set_enable(true);
536
537 Ok(reg)
538 }
539}
540
541bitfield! {
542 /// The FP_COMPn register bit assignments for FPB Version 2 where the Flash Patch is not implemented (see [`FpRev1CompX`]).
543 #[derive(Copy,Clone)]
544 pub struct FpRev2CompX(u32);
545 impl Debug;
546 /// BPADDR, `bits[31:1]` Breakpoint address. Specifies `bits[31:1]` of the breakpoint instruction address.
547 ///
548 /// If `BE == 0`, this field is Reserved, UNK/SBZP.
549 ///
550 /// The reset value of this field is UNKNOWN.
551 pub bpaddr, set_bpaddr: 31, 1;
552 /// Enable bit for breakpoint:
553 ///
554 /// `0`: Breakpoint disabled.\
555 /// `1`: Breakpoint enabled.
556 ///
557 /// The reset value of this bit is UNKNOWN.
558 pub enable, set_enable: 0;
559}
560
561impl MemoryMappedRegister<u32> for FpRev2CompX {
562 const ADDRESS_OFFSET: u64 = 0xE000_2008;
563 const NAME: &'static str = "FP_CTRL";
564}
565
566impl From<u32> for FpRev2CompX {
567 fn from(value: u32) -> Self {
568 FpRev2CompX(value)
569 }
570}
571
572impl From<FpRev2CompX> for u32 {
573 fn from(value: FpRev2CompX) -> Self {
574 value.0
575 }
576}
577
578impl FpRev2CompX {
579 /// Get the correct register configuration which enables
580 /// a hardware breakpoint at the given address.
581 pub(crate) fn breakpoint_configuration(address: u32) -> Self {
582 let mut reg = FpRev2CompX::from(0);
583
584 reg.set_bpaddr(address >> 1);
585 reg.set_enable(true);
586
587 reg
588 }
589}
590
591/// The state of a core that can be used to persist core state across calls to multiple different cores.
592pub struct Armv7m<'probe> {
593 memory: Box<dyn ArmMemoryInterface + 'probe>,
594
595 state: &'probe mut CortexMState,
596
597 sequence: Arc<dyn ArmDebugSequence>,
598}
599
600impl<'probe> Armv7m<'probe> {
601 pub(crate) fn new(
602 mut memory: Box<dyn ArmMemoryInterface + 'probe>,
603 state: &'probe mut CortexMState,
604 sequence: Arc<dyn ArmDebugSequence>,
605 ) -> Result<Self, Error> {
606 if !state.initialized() {
607 // determine current state
608 let dhcsr = Dhcsr(memory.read_word_32(Dhcsr::get_mmio_address())?);
609
610 let core_state = if dhcsr.s_sleep() {
611 CoreStatus::Sleeping
612 } else if dhcsr.s_halt() {
613 let dfsr = Dfsr(memory.read_word_32(Dfsr::get_mmio_address())?);
614
615 let reason = dfsr.halt_reason();
616
617 tracing::debug!("Core was halted when connecting, reason: {:?}", reason);
618
619 CoreStatus::Halted(reason)
620 } else {
621 CoreStatus::Running
622 };
623
624 // Clear DFSR register. The bits in the register are sticky,
625 // so we clear them here to ensure that that none are set.
626 let dfsr_clear = Dfsr::clear_all();
627
628 memory.write_word_32(Dfsr::get_mmio_address(), dfsr_clear.into())?;
629
630 state.current_state = core_state;
631 state.fp_present = Mvfr0(memory.read_word_32(Mvfr0::get_mmio_address())?).fp_present();
632
633 state.initialize();
634 }
635
636 Ok(Self {
637 memory,
638 state,
639 sequence,
640 })
641 }
642
643 fn set_core_status(&mut self, new_status: CoreStatus) {
644 super::update_core_status(&mut self.memory, &mut self.state.current_state, new_status);
645 }
646
647 fn wait_for_status(
648 &mut self,
649 timeout: Duration,
650 predicate: impl Fn(CoreStatus) -> bool,
651 ) -> Result<(), Error> {
652 let start = Instant::now();
653
654 while !predicate(self.status()?) {
655 if start.elapsed() >= timeout {
656 return Err(Error::Arm(ArmError::Timeout));
657 }
658 // Wait a bit before polling again.
659 std::thread::sleep(Duration::from_millis(1));
660 }
661
662 Ok(())
663 }
664}
665
666impl CoreInterface for Armv7m<'_> {
667 fn wait_for_core_halted(&mut self, timeout: Duration) -> Result<(), Error> {
668 // Wait until halted state is active again.
669 self.wait_for_status(timeout, |s| s.is_halted())
670 }
671
672 fn core_halted(&mut self) -> Result<bool, Error> {
673 Ok(self.status()?.is_halted())
674 }
675
676 fn status(&mut self) -> Result<CoreStatus, Error> {
677 let dhcsr = Dhcsr(self.memory.read_word_32(Dhcsr::get_mmio_address())?);
678
679 if dhcsr.s_lockup() {
680 tracing::debug!(
681 "The core is in locked up status as a result of an unrecoverable exception"
682 );
683
684 self.set_core_status(CoreStatus::LockedUp);
685
686 return Ok(CoreStatus::LockedUp);
687 }
688
689 if dhcsr.s_sleep() {
690 // Check if we assumed the core to be halted
691 if self.state.current_state.is_halted() {
692 tracing::warn!("Expected core to be halted, but core is running");
693 }
694
695 self.set_core_status(CoreStatus::Sleeping);
696
697 return Ok(CoreStatus::Sleeping);
698 }
699
700 if dhcsr.s_halt() {
701 let dfsr = Dfsr(self.memory.read_word_32(Dfsr::get_mmio_address())?);
702
703 let mut reason = dfsr.halt_reason();
704
705 // Clear bits from Dfsr register
706 self.memory
707 .write_word_32(Dfsr::get_mmio_address(), Dfsr::clear_all().into())?;
708
709 // If the core was halted before, we cannot read the halt reason from the chip,
710 // because we clear it directly after reading.
711 if self.state.current_state.is_halted() {
712 // There shouldn't be any bits set, otherwise it means
713 // that the reason for the halt has changed. No bits set
714 // means that we have an unkown HaltReason.
715 if reason == HaltReason::Unknown {
716 tracing::debug!("Cached halt reason: {:?}", self.state.current_state);
717 return Ok(self.state.current_state);
718 }
719
720 tracing::debug!(
721 "Reason for halt has changed, old reason was {:?}, new reason is {:?}",
722 &self.state.current_state,
723 &reason
724 );
725 }
726
727 // Set the status so any semihosting operations will know we're halted
728 self.set_core_status(CoreStatus::Halted(reason));
729
730 if let HaltReason::Breakpoint(_) = reason {
731 self.state.semihosting_command = super::cortex_m::check_for_semihosting(
732 self.state.semihosting_command.take(),
733 self,
734 )?;
735 if let Some(command) = self.state.semihosting_command {
736 reason = HaltReason::Breakpoint(BreakpointCause::Semihosting(command));
737 }
738
739 // Set it again if it's changed
740 self.set_core_status(CoreStatus::Halted(reason));
741 }
742
743 return Ok(CoreStatus::Halted(reason));
744 }
745
746 // Core is neither halted nor sleeping, so we assume it is running.
747 if self.state.current_state.is_halted() {
748 tracing::warn!("Core is running, but we expected it to be halted");
749 }
750
751 self.set_core_status(CoreStatus::Running);
752
753 Ok(CoreStatus::Running)
754 }
755
756 fn halt(&mut self, timeout: Duration) -> Result<CoreInformation, Error> {
757 // TODO: Generic halt support
758
759 let mut value = Dhcsr(0);
760 value.set_c_halt(true);
761 value.set_c_debugen(true);
762 value.enable_write();
763
764 self.memory
765 .write_word_32(Dhcsr::get_mmio_address(), value.into())?;
766
767 self.wait_for_core_halted(timeout)?;
768
769 // try to read the program counter
770 let pc_value = self.read_core_reg(self.program_counter().into())?;
771
772 // get pc
773 Ok(CoreInformation {
774 pc: pc_value.try_into()?,
775 })
776 }
777
778 fn run(&mut self) -> Result<(), Error> {
779 // Before we run, we always perform a single instruction step, to account for possible breakpoints that might get us stuck on the current instruction.
780 self.step()?;
781
782 let mut dhcsr = Dhcsr(self.memory.read_word_32(Dhcsr::get_mmio_address())?);
783
784 // First disable the DHCSR->C_MASKINTS.
785 if dhcsr.c_maskints() {
786 dhcsr.set_c_maskints(false);
787 dhcsr.enable_write();
788 self.memory
789 .write_word_32(Dhcsr::get_mmio_address(), dhcsr.into())?;
790 self.memory.flush()?;
791 }
792
793 // Exit halt state ..
794 dhcsr.set_c_step(false);
795 dhcsr.set_c_halt(false);
796 dhcsr.enable_write();
797 self.memory
798 .write_word_32(Dhcsr::get_mmio_address(), dhcsr.into())?;
799 self.memory.flush()?;
800
801 // We assume that the core is running now
802 self.set_core_status(CoreStatus::Running);
803
804 Ok(())
805 }
806
807 fn reset(&mut self) -> Result<(), Error> {
808 self.state.semihosting_command = None;
809
810 self.sequence
811 .reset_system(&mut *self.memory, crate::CoreType::Armv7m, None)?;
812 // Invalidate cached core status
813 self.set_core_status(CoreStatus::Unknown);
814 Ok(())
815 }
816
817 fn reset_and_halt(&mut self, _timeout: Duration) -> Result<CoreInformation, Error> {
818 // Set the vc_corereset bit in the DEMCR register.
819 // This will halt the core after reset.
820 self.reset_catch_set()?;
821
822 self.sequence
823 .reset_system(&mut *self.memory, crate::CoreType::Armv7m, None)?;
824
825 // Invalidate cached core status
826 self.set_core_status(CoreStatus::Unknown);
827
828 // Some processors may not enter the halt state immediately after clearing the reset state.
829 // Particularly: on PSOC 6, vector catch takes effect after the core's boot ROM finishes
830 // executing, when jumping to the reset vector of the user application.
831 match self.wait_for_core_halted(Duration::from_millis(100)) {
832 Ok(()) => (),
833 Err(Error::Arm(ArmError::Timeout)) if self.status()? == CoreStatus::Sleeping => {
834 // On PSOC 6, if no application is loaded in flash, or if this core is waiting for
835 // another core to boot it, the boot ROM sleeps and vector catch is not triggered.
836 tracing::warn!(
837 "reset_and_halt timed out and core is sleeping; assuming core is quiescent"
838 );
839 self.halt(Duration::from_millis(100))?;
840 }
841 Err(e) => return Err(e),
842 }
843
844 const XPSR_THUMB: u32 = 1 << 24;
845
846 let xpsr_value: u32 = self.read_core_reg(XPSR.id())?.try_into()?;
847 if xpsr_value & XPSR_THUMB == 0 {
848 self.write_core_reg(XPSR.id(), (xpsr_value | XPSR_THUMB).into())?;
849 }
850
851 self.reset_catch_clear()?;
852
853 // try to read the program counter
854 let pc_value = self.read_core_reg(self.program_counter().into())?;
855
856 // get pc
857 Ok(CoreInformation {
858 pc: pc_value.try_into()?,
859 })
860 }
861
862 fn step(&mut self) -> Result<CoreInformation, Error> {
863 // First check if we stopped on a breakpoint, because this requires special handling before we can continue.
864 let breakpoint_at_pc = if matches!(
865 self.state.current_state,
866 CoreStatus::Halted(HaltReason::Breakpoint(_))
867 ) {
868 let pc_before_step = self.read_core_reg(self.program_counter().into())?;
869 self.enable_breakpoints(false)?;
870 Some(pc_before_step)
871 } else {
872 None
873 };
874
875 let mut dhcsr = Dhcsr(self.memory.read_word_32(Dhcsr::get_mmio_address())?);
876
877 // Follow the rules of the ... ARMv7-M Architecture reference, C1.6 Debug System Registers - DHCSR, with respect to setting maskints
878 if !dhcsr.c_debugen() {
879 tracing::warn!("Attempting to STEP while DHCSR->C_DEBUGEN is false");
880 }
881 if !dhcsr.c_maskints() {
882 dhcsr.set_c_maskints(true); // This must be reset to false when we run() again.
883 dhcsr.enable_write();
884 self.memory
885 .write_word_32(Dhcsr::get_mmio_address(), dhcsr.into())?;
886 self.memory.flush()?;
887 }
888
889 // Leave halted state.
890 // Step one instruction.
891 dhcsr.set_c_step(true);
892 dhcsr.set_c_halt(false);
893 dhcsr.enable_write();
894 self.memory
895 .write_word_32(Dhcsr::get_mmio_address(), dhcsr.into())?;
896 self.memory.flush()?;
897
898 // The single-step might put the core in lockup state. Lockup isn't considered "halted"
899 // so we can't use `wait_for_core_halted` here.
900 // So we wait for halted OR lockup, and if we entered lockup we halt.
901 self.wait_for_status(Duration::from_millis(100), |s| {
902 matches!(s, CoreStatus::Halted(_) | CoreStatus::LockedUp)
903 })?;
904 if self.status()? == CoreStatus::LockedUp {
905 self.halt(Duration::from_millis(100))?;
906 }
907
908 // Try to read the new program counter.
909 let mut pc_after_step = self.read_core_reg(self.program_counter().into())?;
910
911 // Re-enable breakpoints before we continue.
912 if let Some(pc_before_step) = breakpoint_at_pc {
913 // If we were stopped on a software breakpoint, then we need to manually advance the PC, or else we will be stuck here forever.
914 if pc_before_step == pc_after_step
915 && !self
916 .hw_breakpoints()?
917 .contains(&pc_before_step.try_into().ok())
918 {
919 tracing::debug!(
920 "Encountered a breakpoint instruction @ {}. We need to manually advance the program counter to the next instruction.",
921 pc_after_step
922 );
923 // Advance the program counter by the architecture specific byte size of the BKPT instruction.
924 pc_after_step.increment_address(2)?;
925 self.write_core_reg(self.program_counter().into(), pc_after_step)?;
926 }
927 self.enable_breakpoints(true)?;
928 }
929
930 self.state.semihosting_command = None;
931
932 Ok(CoreInformation {
933 pc: pc_after_step.try_into()?,
934 })
935 }
936
937 fn read_core_reg(&mut self, address: RegisterId) -> Result<RegisterValue, Error> {
938 if self.state.current_state.is_halted() {
939 let val = super::cortex_m::read_core_reg(&mut *self.memory, address)?;
940 Ok(val.into())
941 } else {
942 Err(Error::Arm(ArmError::CoreNotHalted))
943 }
944 }
945
946 fn write_core_reg(&mut self, address: RegisterId, value: RegisterValue) -> Result<(), Error> {
947 if self.state.current_state.is_halted() {
948 super::cortex_m::write_core_reg(&mut *self.memory, address, value.try_into()?)?;
949 Ok(())
950 } else {
951 Err(Error::Arm(ArmError::CoreNotHalted))
952 }
953 }
954
955 fn available_breakpoint_units(&mut self) -> Result<u32, Error> {
956 let raw_val = self.memory.read_word_32(FpCtrl::get_mmio_address())?;
957
958 let reg = FpCtrl::from(raw_val);
959
960 if reg.rev() == 0 || reg.rev() == 1 {
961 Ok(reg.num_code())
962 } else {
963 tracing::warn!(
964 "This chip uses FPBU revision {}, which is not yet supported. HW breakpoints are not available.",
965 reg.rev()
966 );
967 Err(Error::Arm(ArmError::Other(format!(
968 "This chip uses FPBU revision {}, which is not yet supported. HW breakpoints are not available.",
969 reg.rev()
970 ))))
971 }
972 }
973
974 /// See docs on the [`CoreInterface::hw_breakpoints`] trait.
975 fn hw_breakpoints(&mut self) -> Result<Vec<Option<u64>>, Error> {
976 let mut breakpoints = vec![];
977 let num_hw_breakpoints = self.available_breakpoint_units()? as usize;
978 { 0..num_hw_breakpoints }.try_for_each(|bp_unit_index| {
979 let raw_val = self.memory.read_word_32(FpCtrl::get_mmio_address())?;
980 let ctrl_reg = FpCtrl::from(raw_val);
981 // FpRev1 and FpRev2 needs different decoding of the register value, but the location where we read from is the same ...
982 let reg_addr = FpRev1CompX::get_mmio_address() + (bp_unit_index * size_of::<u32>()) as u64;
983 // The raw breakpoint address as read from memory.
984 let register_value = self.memory.read_word_32(reg_addr)?;
985 // The breakpoint address after it has been adjusted for FpRev 1 or 2.
986 let breakpoint:u32;
987 if register_value & 0b1 == 0b1 {
988 // We only care about `enabled` breakpoints.
989 if ctrl_reg.rev() == 0 {
990 breakpoint = FpRev1CompX::get_breakpoint_comparator(register_value)?;
991 } else if ctrl_reg.rev() == 1 {
992 breakpoint = FpRev2CompX::from(register_value).bpaddr() << 1;
993 } else {
994 tracing::warn!("This chip uses FPBU revision {}, which is not yet supported. HW breakpoints are not available.", ctrl_reg.rev());
995 return Err(Error::Other(format!("This chip uses FPBU revision {}, which is not yet supported. HW breakpoints are not available.", ctrl_reg.rev())));
996 }
997 breakpoints.push(Some(breakpoint as u64));
998 } else {
999 breakpoints.push(None);
1000 }
1001 Ok(())
1002 })?;
1003 Ok(breakpoints)
1004 }
1005
1006 fn enable_breakpoints(&mut self, state: bool) -> Result<(), Error> {
1007 let mut val = FpCtrl::from(0);
1008 val.set_key(true);
1009 val.set_enable(state);
1010
1011 self.memory
1012 .write_word_32(FpCtrl::get_mmio_address(), val.into())?;
1013 self.memory.flush()?;
1014
1015 self.state.hw_breakpoints_enabled = state;
1016
1017 Ok(())
1018 }
1019
1020 fn set_hw_breakpoint(&mut self, bp_unit_index: usize, addr: u64) -> Result<(), Error> {
1021 let addr = valid_32bit_address(addr)?;
1022
1023 // First make sure they are asking for a breakpoint on a half-word boundary.
1024 if (addr & 0x1) > 0 {
1025 return Err(Error::Other(format!(
1026 "The requested breakpoint address 0x{addr:08x} is not on a half-word boundary"
1027 )));
1028 }
1029
1030 let raw_val = self.memory.read_word_32(FpCtrl::get_mmio_address())?;
1031 let ctrl_reg = FpCtrl::from(raw_val);
1032
1033 let val: u32;
1034 if ctrl_reg.rev() == 0 {
1035 val = FpRev1CompX::breakpoint_configuration(addr)?.into();
1036 } else if ctrl_reg.rev() == 1 {
1037 val = FpRev2CompX::breakpoint_configuration(addr).into();
1038 } else {
1039 tracing::warn!(
1040 "This chip uses FPBU revision {}, which is not yet supported. HW breakpoints are not available.",
1041 ctrl_reg.rev()
1042 );
1043 return Err(Error::Other(format!(
1044 "This chip uses FPBU revision {}, which is not yet supported. HW breakpoints are not available.",
1045 ctrl_reg.rev()
1046 )));
1047 }
1048
1049 // This is fine as FpRev1CompX and Rev2CompX are just two different
1050 // interpretations of the same memory region as Rev2 can handle bigger
1051 // address spaces than Rev1.
1052 let reg_addr = FpRev1CompX::get_mmio_address() + (bp_unit_index * size_of::<u32>()) as u64;
1053
1054 self.memory.write_word_32(reg_addr, val)?;
1055
1056 Ok(())
1057 }
1058
1059 fn clear_hw_breakpoint(&mut self, bp_unit_index: usize) -> Result<(), Error> {
1060 let mut val = FpRev1CompX::from(0);
1061 val.set_enable(false);
1062
1063 let reg_addr = FpRev1CompX::get_mmio_address() + (bp_unit_index * size_of::<u32>()) as u64;
1064
1065 self.memory.write_word_32(reg_addr, val.into())?;
1066
1067 Ok(())
1068 }
1069
1070 fn registers(&self) -> &'static CoreRegisters {
1071 if self.state.fp_present {
1072 &CORTEX_M_WITH_FP_CORE_REGISTERS
1073 } else {
1074 &CORTEX_M_CORE_REGISTERS
1075 }
1076 }
1077
1078 fn program_counter(&self) -> &'static CoreRegister {
1079 &PC
1080 }
1081
1082 fn frame_pointer(&self) -> &'static CoreRegister {
1083 &FP
1084 }
1085
1086 fn stack_pointer(&self) -> &'static CoreRegister {
1087 &SP
1088 }
1089
1090 fn return_address(&self) -> &'static CoreRegister {
1091 &RA
1092 }
1093
1094 fn hw_breakpoints_enabled(&self) -> bool {
1095 self.state.hw_breakpoints_enabled
1096 }
1097
1098 fn architecture(&self) -> Architecture {
1099 Architecture::Arm
1100 }
1101
1102 fn core_type(&self) -> CoreType {
1103 CoreType::Armv7m
1104 }
1105
1106 fn instruction_set(&mut self) -> Result<InstructionSet, Error> {
1107 Ok(InstructionSet::Thumb2)
1108 }
1109
1110 fn fpu_support(&mut self) -> Result<bool, Error> {
1111 Ok(self.state.fp_present)
1112 }
1113
1114 fn floating_point_register_count(&mut self) -> Result<usize, Error> {
1115 Ok(32)
1116 }
1117
1118 #[tracing::instrument(skip(self))]
1119 fn reset_catch_set(&mut self) -> Result<(), Error> {
1120 self.sequence
1121 .reset_catch_set(&mut *self.memory, CoreType::Armv7m, None)?;
1122
1123 Ok(())
1124 }
1125
1126 #[tracing::instrument(skip(self))]
1127 fn reset_catch_clear(&mut self) -> Result<(), Error> {
1128 self.sequence
1129 .reset_catch_clear(&mut *self.memory, CoreType::Armv7m, None)?;
1130
1131 Ok(())
1132 }
1133
1134 #[tracing::instrument(skip(self))]
1135 fn debug_core_stop(&mut self) -> Result<(), Error> {
1136 self.sequence
1137 .debug_core_stop(&mut *self.memory, CoreType::Armv7m)?;
1138 Ok(())
1139 }
1140
1141 #[tracing::instrument(skip(self))]
1142 fn enable_vector_catch(&mut self, condition: VectorCatchCondition) -> Result<(), Error> {
1143 let mut dhcsr = Dhcsr(self.memory.read_word_32(Dhcsr::get_mmio_address())?);
1144 dhcsr.set_c_debugen(true);
1145 self.memory
1146 .write_word_32(Dhcsr::get_mmio_address(), dhcsr.into())?;
1147
1148 let mut demcr = Demcr(self.memory.read_word_32(Demcr::get_mmio_address())?);
1149 match condition {
1150 VectorCatchCondition::HardFault => demcr.set_vc_harderr(true),
1151 VectorCatchCondition::CoreReset => demcr.set_vc_corereset(true),
1152 VectorCatchCondition::SecureFault => {
1153 return Err(Error::Arm(ArmError::ArchitectureRequired(&["ARMv8"])));
1154 }
1155 VectorCatchCondition::All => {
1156 demcr.set_vc_harderr(true);
1157 demcr.set_vc_corereset(true);
1158 }
1159 };
1160
1161 self.memory
1162 .write_word_32(Demcr::get_mmio_address(), demcr.into())?;
1163 Ok(())
1164 }
1165
1166 fn disable_vector_catch(&mut self, condition: VectorCatchCondition) -> Result<(), Error> {
1167 let mut demcr = Demcr(self.memory.read_word_32(Demcr::get_mmio_address())?);
1168 match condition {
1169 VectorCatchCondition::HardFault => demcr.set_vc_harderr(false),
1170 VectorCatchCondition::CoreReset => demcr.set_vc_corereset(false),
1171 VectorCatchCondition::SecureFault => {
1172 return Err(Error::Arm(ArmError::ArchitectureRequired(&["ARMv8"])));
1173 }
1174 VectorCatchCondition::All => {
1175 demcr.set_vc_harderr(false);
1176 demcr.set_vc_corereset(false);
1177 }
1178 };
1179
1180 self.memory
1181 .write_word_32(Demcr::get_mmio_address(), demcr.into())?;
1182 Ok(())
1183 }
1184}
1185
1186impl CoreMemoryInterface for Armv7m<'_> {
1187 type ErrorType = ArmError;
1188
1189 fn memory(&self) -> &dyn MemoryInterface<Self::ErrorType> {
1190 self.memory.as_ref()
1191 }
1192 fn memory_mut(&mut self) -> &mut dyn MemoryInterface<Self::ErrorType> {
1193 self.memory.as_mut()
1194 }
1195}
1196
1197#[test]
1198fn breakpoint_register_value() {
1199 // Check that the register configuration for the FPBU is
1200 // calculated correctly.
1201 //
1202 // See ARMv7 Architecture Reference Manual, Section C1.11.5
1203 let address: u32 = 0x0800_09A4;
1204
1205 let reg = FpRev1CompX::breakpoint_configuration(address).unwrap();
1206 let reg_val: u32 = reg.into();
1207
1208 assert_eq!(0x4800_09A5, reg_val);
1209}
1210
1211#[test]
1212fn unsupported_breakpoint_address() {
1213 // Revision 1 of the FPBU only supports breakpoints for address < 0x2000_0000.
1214 let address: u32 = 0x2000_0000;
1215
1216 FpRev1CompX::breakpoint_configuration(address).unwrap_err();
1217}