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 /// MPU Control Register, MPU_CTRL (see armv7-M Architecture Reference Manual B3.5.3)
375 #[derive(Copy, Clone)]
376 pub struct MpuCtrl(u32);
377 impl Debug;
378 /// When the ENABLE bit is set to `1`, controls whether privileged software
379 /// access to the default memory map is enabled:
380 ///
381 /// `0`: Disabled. Any privileged access to an address not covered by an
382 /// enabled MPU region generates a fault.\
383 /// `1`: Enabled. Privileged accesses to addresses not covered by an
384 /// enabled MPU region use the default memory map.
385 pub privdefena, set_privdefena: 2;
386 /// Controls whether handlers executing with priority less than 0 access
387 /// memory with the MPU enabled or disabled (HardFault, NMI, and
388 /// FAULTMASK escalated handlers):
389 ///
390 /// `0`: MPU disabled for these handlers.\
391 /// `1`: MPU enabled for these handlers.
392 pub hfnmiena, set_hfnmiena: 1;
393 /// Enables the MPU:
394 ///
395 /// `0`: MPU disabled.\
396 /// `1`: MPU enabled.
397 pub enable, set_enable: 0;
398}
399
400impl From<u32> for MpuCtrl {
401 fn from(value: u32) -> Self {
402 Self(value)
403 }
404}
405
406impl From<MpuCtrl> for u32 {
407 fn from(value: MpuCtrl) -> Self {
408 value.0
409 }
410}
411
412impl MemoryMappedRegister<u32> for MpuCtrl {
413 const ADDRESS_OFFSET: u64 = 0xE000_ED94;
414 const NAME: &'static str = "MPU_CTRL";
415}
416
417bitfield! {
418 /// Flash Patch Control Register, FP_CTRL (see armv7-M Architecture Reference Manual C1.11.3)
419 #[derive(Copy,Clone)]
420 pub struct FpCtrl(u32);
421 impl Debug;
422 /// Flash Patch breakpoint architecture revision:
423 ///
424 /// `0b0000` Flash Patch breakpoint version 1.\
425 /// `0b0001` Flash Patch breakpoint version 2. Supports breakpoints on any location in the 4GB address range.
426 pub rev, _: 31, 28;
427 num_code_1, _: 14, 12;
428 /// The number of literal address comparators supported, starting from NUM_CODE upwards.
429 /// UNK/SBZP if Flash Patch is not implemented. Flash Patch is not implemented if `FP_REMAP[29]` is `0`.
430 ///
431 /// If this field is zero, the implementation does not support literal comparators.
432 pub num_lit, _: 11, 8;
433 num_code_0, _: 7, 4;
434 /// On any write to FP_CTRL, this bit must be `1`. A write to the register with this bit set to zero
435 /// is ignored. The Flash Patch Breakpoint unit ignores the write unless this bit is `1`.
436 pub _, set_key: 1;
437 /// Enable bit for the FPB:
438 ///
439 /// `0`: Flash Patch breakpoint disabled.\
440 /// `1`: Flash Patch breakpoint enabled.
441 ///
442 /// A power-on reset clears this bit to `0`.
443 pub enable, set_enable: 0;
444}
445
446impl FpCtrl {
447 /// The number of instruction address comparators.
448 /// If NUM_CODE is zero, the implementation does not support any instruction address comparators.
449 pub fn num_code(&self) -> u32 {
450 (self.num_code_1() << 4) | self.num_code_0()
451 }
452}
453
454impl MemoryMappedRegister<u32> for FpCtrl {
455 const ADDRESS_OFFSET: u64 = 0xE000_2000;
456 const NAME: &'static str = "FP_CTRL";
457}
458
459impl From<u32> for FpCtrl {
460 fn from(value: u32) -> Self {
461 FpCtrl(value)
462 }
463}
464
465impl From<FpCtrl> for u32 {
466 fn from(value: FpCtrl) -> Self {
467 value.0
468 }
469}
470
471bitfield! {
472 /// Flash Patch Comparator register, FP_COMPn (see armv7-M Architecture Reference Manual C1.11.5)
473 #[derive(Copy,Clone)]
474 pub struct FpRev1CompX(u32);
475 impl Debug;
476 /// For an instruction address comparator:
477 ///
478 /// Defines the behavior when the COMP address is matched:
479 ///
480 /// `00` Remap to remap address, see Flash Patch Remap register,
481 /// FP_REMAP on page C1-758.
482 ///
483 /// When the comparators are enabled in the FP_CTRL register, if the
484 /// implementation does not support remapping, the effect of an
485 /// instruction address match with an enabled comparator with
486 /// REPLACE programmed to 0b00 is UNPREDICTABLE.
487 ///
488 /// `01`: Breakpoint on instruction at `'000':COMP:'00'`.\
489 /// `10`: Breakpoint on instruction at `'000':COMP:'10'`.\
490 /// `11`: Breakpoint on both instructions at `'000':COMP:'00'` and `'000':COMP:'10'`.
491 ///
492 /// The reset value of this field is UNKNOWN.
493 ///
494 /// For a literal address comparator:
495 ///
496 /// Field is UNK/SBZP
497 pub replace, set_replace: 31, 30;
498 /// Bits `[28:2]` of the address to compare with addresses from the Code memory region,
499 /// see The system address map on page B3-592. Bits `[31:29]` of the address for comparison are zero.
500 ///
501 /// For a literal address or instruction address remap, bits `[1:0]` of the comparison are also zero.
502 ///
503 /// For an instruction address breakpoint, bits `[1:0]` of the comparison are encoded by the REPLACE field.
504 ///
505 /// If a match occurs:
506 ///
507 /// - For an instruction address comparator, the REPLACE field defines the required action.
508 /// - For a literal address comparator, the FPB remaps the access, see Flash Patch Remap register, FP_REMAP on page C1-758.
509 ///
510 /// The reset value of this field is UNKNOWN.
511 pub comp, set_comp: 28, 2;
512 /// Enable bit for this comparator:
513 ///
514 /// `0`: Comparator disabled.\
515 /// `1`: Comparator enabled.
516 ///
517 /// A power-on reset clears this bit to `0`.
518 pub enable, set_enable: 0;
519}
520
521impl MemoryMappedRegister<u32> for FpRev1CompX {
522 const ADDRESS_OFFSET: u64 = 0xE000_2008;
523 const NAME: &'static str = "FP_CTRL";
524}
525
526impl From<u32> for FpRev1CompX {
527 fn from(value: u32) -> Self {
528 FpRev1CompX(value)
529 }
530}
531
532impl From<FpRev1CompX> for u32 {
533 fn from(value: FpRev1CompX) -> Self {
534 value.0
535 }
536}
537
538impl FpRev1CompX {
539 /// Get the correct comparator value stored at the given address
540 /// This will adjust the `FpRev1CompX.comp() result based on the `FpRev1CompX.replace()` specification
541 /// NOTE: Does not support a `replace value of '11'
542 fn get_breakpoint_comparator(register_value: u32) -> Result<u32, Error> {
543 let fp1_val = FpRev1CompX::from(register_value);
544 if fp1_val.replace() == 0b01 {
545 Ok(fp1_val.comp() << 2)
546 } else if fp1_val.replace() == 0b10 {
547 Ok((fp1_val.comp() << 2) | 0x2)
548 } else {
549 Err(Error::Arm(ArmError::Other(format!(
550 "Unsupported breakpoint comparator value {:#08x} for HW breakpoint. Breakpoint must be on half-word boundaries",
551 fp1_val.0
552 ))))
553 }
554 }
555 /// Get the correct register configuration which enables
556 /// a hardware breakpoint at the given address.
557 /// NOTE: Does not support a `replace` value of '11'
558 pub(crate) fn breakpoint_configuration(address: u32) -> Result<Self, ArmError> {
559 let mut reg = FpRev1CompX::from(0);
560
561 // The highest 3 bits of the address have to be zero, otherwise the breakpoint cannot
562 // be set at the address.
563 if address >= 0x2000_0000 {
564 return Err(ArmError::UnsupportedBreakpointAddress(address));
565 }
566
567 let comp_val = (address & 0x1f_ff_ff_fc) >> 2;
568
569 // the replace value decides if the upper or lower half
570 // word is matched for the break point
571 let replace_val = if (address & 0x3) == 0 {
572 0b01 // lower half word
573 } else {
574 0b10 // upper half word
575 };
576
577 reg.set_replace(replace_val);
578 reg.set_comp(comp_val);
579 reg.set_enable(true);
580
581 Ok(reg)
582 }
583}
584
585bitfield! {
586 /// The FP_COMPn register bit assignments for FPB Version 2 where the Flash Patch is not implemented (see [`FpRev1CompX`]).
587 #[derive(Copy,Clone)]
588 pub struct FpRev2CompX(u32);
589 impl Debug;
590 /// BPADDR, `bits[31:1]` Breakpoint address. Specifies `bits[31:1]` of the breakpoint instruction address.
591 ///
592 /// If `BE == 0`, this field is Reserved, UNK/SBZP.
593 ///
594 /// The reset value of this field is UNKNOWN.
595 pub bpaddr, set_bpaddr: 31, 1;
596 /// Enable bit for breakpoint:
597 ///
598 /// `0`: Breakpoint disabled.\
599 /// `1`: Breakpoint enabled.
600 ///
601 /// The reset value of this bit is UNKNOWN.
602 pub enable, set_enable: 0;
603}
604
605impl MemoryMappedRegister<u32> for FpRev2CompX {
606 const ADDRESS_OFFSET: u64 = 0xE000_2008;
607 const NAME: &'static str = "FP_CTRL";
608}
609
610impl From<u32> for FpRev2CompX {
611 fn from(value: u32) -> Self {
612 FpRev2CompX(value)
613 }
614}
615
616impl From<FpRev2CompX> for u32 {
617 fn from(value: FpRev2CompX) -> Self {
618 value.0
619 }
620}
621
622impl FpRev2CompX {
623 /// Get the correct register configuration which enables
624 /// a hardware breakpoint at the given address.
625 pub(crate) fn breakpoint_configuration(address: u32) -> Self {
626 let mut reg = FpRev2CompX::from(0);
627
628 reg.set_bpaddr(address >> 1);
629 reg.set_enable(true);
630
631 reg
632 }
633}
634
635/// The state of a core that can be used to persist core state across calls to multiple different cores.
636pub struct Armv7m<'probe> {
637 memory: Box<dyn ArmMemoryInterface + 'probe>,
638
639 state: &'probe mut CortexMState,
640
641 sequence: Arc<dyn ArmDebugSequence>,
642}
643
644impl<'probe> Armv7m<'probe> {
645 pub(crate) fn new(
646 mut memory: Box<dyn ArmMemoryInterface + 'probe>,
647 state: &'probe mut CortexMState,
648 sequence: Arc<dyn ArmDebugSequence>,
649 ) -> Result<Self, Error> {
650 if !state.initialized() {
651 // determine current state
652 let dhcsr = Dhcsr(memory.read_word_32(Dhcsr::get_mmio_address())?);
653
654 let core_state = if dhcsr.s_sleep() {
655 CoreStatus::Sleeping
656 } else if dhcsr.s_halt() {
657 let dfsr = Dfsr(memory.read_word_32(Dfsr::get_mmio_address())?);
658
659 let reason = dfsr.halt_reason();
660
661 tracing::debug!("Core was halted when connecting, reason: {:?}", reason);
662
663 CoreStatus::Halted(reason)
664 } else {
665 CoreStatus::Running
666 };
667
668 // Clear DFSR register. The bits in the register are sticky,
669 // so we clear them here to ensure that that none are set.
670 let dfsr_clear = Dfsr::clear_all();
671
672 memory.write_word_32(Dfsr::get_mmio_address(), dfsr_clear.into())?;
673
674 state.current_state = core_state;
675 state.fp_present = Mvfr0(memory.read_word_32(Mvfr0::get_mmio_address())?).fp_present();
676
677 state.initialize();
678 }
679
680 Ok(Self {
681 memory,
682 state,
683 sequence,
684 })
685 }
686
687 fn set_core_status(&mut self, new_status: CoreStatus) {
688 super::update_core_status(&mut self.memory, &mut self.state.current_state, new_status);
689 }
690
691 fn wait_for_status(
692 &mut self,
693 timeout: Duration,
694 predicate: impl Fn(CoreStatus) -> bool,
695 ) -> Result<(), Error> {
696 let start = Instant::now();
697
698 while !predicate(self.status()?) {
699 if start.elapsed() >= timeout {
700 return Err(Error::Arm(ArmError::Timeout));
701 }
702 // Wait a bit before polling again.
703 std::thread::sleep(Duration::from_millis(1));
704 }
705
706 Ok(())
707 }
708}
709
710impl CoreInterface for Armv7m<'_> {
711 fn wait_for_core_halted(&mut self, timeout: Duration) -> Result<(), Error> {
712 // Wait until halted state is active again.
713 self.wait_for_status(timeout, |s| s.is_halted())
714 }
715
716 fn core_halted(&mut self) -> Result<bool, Error> {
717 Ok(self.status()?.is_halted())
718 }
719
720 fn status(&mut self) -> Result<CoreStatus, Error> {
721 let dhcsr = Dhcsr(self.memory.read_word_32(Dhcsr::get_mmio_address())?);
722
723 if dhcsr.s_lockup() {
724 tracing::debug!(
725 "The core is in locked up status as a result of an unrecoverable exception"
726 );
727
728 self.state.clear_pending_step();
729 self.set_core_status(CoreStatus::LockedUp);
730
731 return Ok(CoreStatus::LockedUp);
732 }
733
734 if dhcsr.s_sleep() {
735 // Check if we assumed the core to be halted
736 if self.state.current_state.is_halted() {
737 tracing::warn!("Expected core to be halted, but core is running");
738 }
739
740 self.set_core_status(CoreStatus::Sleeping);
741
742 return Ok(CoreStatus::Sleeping);
743 }
744
745 if dhcsr.s_halt() {
746 let dfsr = Dfsr(self.memory.read_word_32(Dfsr::get_mmio_address())?);
747
748 let mut reason = dfsr.halt_reason();
749 reason = self.state.resolve_halt_reason(reason);
750
751 // Clear bits from Dfsr register
752 self.memory
753 .write_word_32(Dfsr::get_mmio_address(), Dfsr::clear_all().into())?;
754
755 // If the core was halted before, we cannot read the halt reason from the chip,
756 // because we clear it directly after reading.
757 if self.state.current_state.is_halted() {
758 // There shouldn't be any bits set, otherwise it means
759 // that the reason for the halt has changed. No bits set
760 // means that we have an unknown HaltReason.
761 if reason == HaltReason::Unknown {
762 tracing::debug!("Cached halt reason: {:?}", self.state.current_state);
763 return Ok(self.state.current_state);
764 }
765
766 tracing::debug!(
767 "Reason for halt has changed, old reason was {:?}, new reason is {:?}",
768 &self.state.current_state,
769 &reason
770 );
771 }
772
773 // Set the status so any semihosting operations will know we're halted
774 self.set_core_status(CoreStatus::Halted(reason));
775
776 if let HaltReason::Breakpoint(_) = reason {
777 self.state.semihosting_command = super::cortex_m::check_for_semihosting(
778 self.state.semihosting_command.take(),
779 self,
780 )?;
781 if let Some(command) = self.state.semihosting_command {
782 reason = HaltReason::Breakpoint(BreakpointCause::Semihosting(command));
783 }
784
785 // Set it again if it's changed
786 self.set_core_status(CoreStatus::Halted(reason));
787 }
788
789 return Ok(CoreStatus::Halted(reason));
790 }
791
792 // Core is neither halted nor sleeping, so we assume it is running.
793 if self.state.current_state.is_halted() {
794 tracing::warn!("Core is running, but we expected it to be halted");
795 }
796
797 self.set_core_status(CoreStatus::Running);
798
799 Ok(CoreStatus::Running)
800 }
801
802 fn halt(&mut self, timeout: Duration) -> Result<CoreInformation, Error> {
803 // TODO: Generic halt support
804 self.state.clear_pending_step();
805
806 let mut value = Dhcsr(0);
807 value.set_c_halt(true);
808 value.set_c_debugen(true);
809 value.enable_write();
810
811 self.memory
812 .write_word_32(Dhcsr::get_mmio_address(), value.into())?;
813
814 self.wait_for_core_halted(timeout)?;
815
816 // try to read the program counter
817 let pc_value = self.read_core_reg(self.program_counter().into())?;
818
819 // get pc
820 Ok(CoreInformation {
821 pc: pc_value.try_into()?,
822 })
823 }
824
825 fn run(&mut self) -> Result<(), Error> {
826 // Before we run, we always perform a single instruction step, to account for possible breakpoints that might get us stuck on the current instruction.
827 self.step()?;
828 self.state.clear_pending_step();
829
830 let mut dhcsr = Dhcsr(self.memory.read_word_32(Dhcsr::get_mmio_address())?);
831
832 // First disable the DHCSR->C_MASKINTS.
833 if dhcsr.c_maskints() {
834 dhcsr.set_c_maskints(false);
835 dhcsr.enable_write();
836 self.memory
837 .write_word_32(Dhcsr::get_mmio_address(), dhcsr.into())?;
838 self.memory.flush()?;
839 }
840
841 // Exit halt state ..
842 dhcsr.set_c_step(false);
843 dhcsr.set_c_halt(false);
844 dhcsr.enable_write();
845 self.memory
846 .write_word_32(Dhcsr::get_mmio_address(), dhcsr.into())?;
847 self.memory.flush()?;
848
849 // We assume that the core is running now
850 self.set_core_status(CoreStatus::Running);
851
852 Ok(())
853 }
854
855 fn reset(&mut self) -> Result<(), Error> {
856 self.state.semihosting_command = None;
857 self.state.clear_pending_step();
858
859 self.sequence
860 .reset_system(&mut *self.memory, crate::CoreType::Armv7m, None)?;
861 // Invalidate cached state: chip reset clears FP_CTRL and core status
862 self.set_core_status(CoreStatus::Unknown);
863 self.state.hw_breakpoints_enabled = false;
864 Ok(())
865 }
866
867 fn reset_and_halt(&mut self, _timeout: Duration) -> Result<CoreInformation, Error> {
868 // Set the vc_corereset bit in the DEMCR register.
869 // This will halt the core after reset.
870 self.reset_catch_set()?;
871 self.state.clear_pending_step();
872
873 self.sequence
874 .reset_system(&mut *self.memory, crate::CoreType::Armv7m, None)?;
875
876 // Invalidate cached state: chip reset clears FP_CTRL and core status
877 self.set_core_status(CoreStatus::Unknown);
878 self.state.hw_breakpoints_enabled = false;
879
880 // Some processors may not enter the halt state immediately after clearing the reset state.
881 // Particularly: on PSOC 6, vector catch takes effect after the core's boot ROM finishes
882 // executing, when jumping to the reset vector of the user application.
883 match self.wait_for_core_halted(Duration::from_millis(100)) {
884 Ok(()) => (),
885 Err(Error::Arm(ArmError::Timeout)) if self.status()? == CoreStatus::Sleeping => {
886 // On PSOC 6, if no application is loaded in flash, or if this core is waiting for
887 // another core to boot it, the boot ROM sleeps and vector catch is not triggered.
888 tracing::warn!(
889 "reset_and_halt timed out and core is sleeping; assuming core is quiescent"
890 );
891 self.halt(Duration::from_millis(100))?;
892 }
893 Err(e) => return Err(e),
894 }
895
896 const XPSR_THUMB: u32 = 1 << 24;
897
898 let xpsr_value: u32 = self.read_core_reg(XPSR.id())?.try_into()?;
899 if xpsr_value & XPSR_THUMB == 0 {
900 self.write_core_reg(XPSR.id(), (xpsr_value | XPSR_THUMB).into())?;
901 }
902
903 self.reset_catch_clear()?;
904
905 // try to read the program counter
906 let pc_value = self.read_core_reg(self.program_counter().into())?;
907
908 // get pc
909 Ok(CoreInformation {
910 pc: pc_value.try_into()?,
911 })
912 }
913
914 fn step(&mut self) -> Result<CoreInformation, Error> {
915 // First check if we stopped on a breakpoint, because this requires special handling before we can continue.
916 let breakpoint_at_pc = if matches!(
917 self.state.current_state,
918 CoreStatus::Halted(HaltReason::Breakpoint(_))
919 ) {
920 let pc_before_step = self.read_core_reg(self.program_counter().into())?;
921 self.enable_breakpoints(false)?;
922 Some(pc_before_step)
923 } else {
924 None
925 };
926
927 let mut dhcsr = Dhcsr(self.memory.read_word_32(Dhcsr::get_mmio_address())?);
928
929 // Follow the rules of the ... ARMv7-M Architecture reference, C1.6 Debug System Registers - DHCSR, with respect to setting maskints
930 if !dhcsr.c_debugen() {
931 tracing::warn!("Attempting to STEP while DHCSR->C_DEBUGEN is false");
932 }
933 if !dhcsr.c_maskints() {
934 dhcsr.set_c_maskints(true); // This must be reset to false when we run() again.
935 dhcsr.enable_write();
936 self.memory
937 .write_word_32(Dhcsr::get_mmio_address(), dhcsr.into())?;
938 self.memory.flush()?;
939 }
940
941 // Leave halted state.
942 // Step one instruction.
943 self.state.begin_step();
944 dhcsr.set_c_step(true);
945 dhcsr.set_c_halt(false);
946 dhcsr.enable_write();
947 self.memory
948 .write_word_32(Dhcsr::get_mmio_address(), dhcsr.into())?;
949 self.memory.flush()?;
950
951 // The single-step might put the core in lockup state. Lockup isn't considered "halted"
952 // so we can't use `wait_for_core_halted` here.
953 // So we wait for halted OR lockup, and if we entered lockup we halt.
954 if let Err(err) = self.wait_for_status(Duration::from_millis(100), |s| {
955 matches!(s, CoreStatus::Halted(_) | CoreStatus::LockedUp)
956 }) {
957 self.state.clear_pending_step();
958 return Err(err);
959 }
960 if self.status()? == CoreStatus::LockedUp {
961 self.halt(Duration::from_millis(100))?;
962 }
963
964 // Try to read the new program counter.
965 let mut pc_after_step = self.read_core_reg(self.program_counter().into())?;
966
967 // Re-enable breakpoints before we continue.
968 if let Some(pc_before_step) = breakpoint_at_pc {
969 // If we were stopped on a software breakpoint, then we need to manually advance the PC, or else we will be stuck here forever.
970 if pc_before_step == pc_after_step
971 && !self
972 .hw_breakpoints()?
973 .contains(&pc_before_step.try_into().ok())
974 {
975 tracing::debug!(
976 "Encountered a breakpoint instruction @ {}. We need to manually advance the program counter to the next instruction.",
977 pc_after_step
978 );
979 // Advance the program counter by the architecture specific byte size of the BKPT instruction.
980 pc_after_step.increment_address(2)?;
981 self.write_core_reg(self.program_counter().into(), pc_after_step)?;
982 }
983 self.enable_breakpoints(true)?;
984 }
985
986 self.state.semihosting_command = None;
987
988 Ok(CoreInformation {
989 pc: pc_after_step.try_into()?,
990 })
991 }
992
993 fn read_core_reg(&mut self, address: RegisterId) -> Result<RegisterValue, Error> {
994 if self.state.current_state.is_halted() {
995 let val = super::cortex_m::read_core_reg(&mut *self.memory, address)?;
996 Ok(val.into())
997 } else {
998 Err(Error::Arm(ArmError::CoreNotHalted))
999 }
1000 }
1001
1002 fn write_core_reg(&mut self, address: RegisterId, value: RegisterValue) -> Result<(), Error> {
1003 if self.state.current_state.is_halted() {
1004 super::cortex_m::write_core_reg(&mut *self.memory, address, value.try_into()?)?;
1005 Ok(())
1006 } else {
1007 Err(Error::Arm(ArmError::CoreNotHalted))
1008 }
1009 }
1010
1011 fn available_breakpoint_units(&mut self) -> Result<u32, Error> {
1012 let raw_val = self.memory.read_word_32(FpCtrl::get_mmio_address())?;
1013
1014 let reg = FpCtrl::from(raw_val);
1015
1016 if reg.rev() == 0 || reg.rev() == 1 {
1017 Ok(reg.num_code())
1018 } else {
1019 tracing::warn!(
1020 "This chip uses FPBU revision {}, which is not yet supported. HW breakpoints are not available.",
1021 reg.rev()
1022 );
1023 Err(Error::Arm(ArmError::Other(format!(
1024 "This chip uses FPBU revision {}, which is not yet supported. HW breakpoints are not available.",
1025 reg.rev()
1026 ))))
1027 }
1028 }
1029
1030 /// See docs on the [`CoreInterface::hw_breakpoints`] trait.
1031 fn hw_breakpoints(&mut self) -> Result<Vec<Option<u64>>, Error> {
1032 let mut breakpoints = vec![];
1033 let num_hw_breakpoints = self.available_breakpoint_units()? as usize;
1034 { 0..num_hw_breakpoints }.try_for_each(|bp_unit_index| {
1035 let raw_val = self.memory.read_word_32(FpCtrl::get_mmio_address())?;
1036 let ctrl_reg = FpCtrl::from(raw_val);
1037 // FpRev1 and FpRev2 needs different decoding of the register value, but the location where we read from is the same ...
1038 let reg_addr = FpRev1CompX::get_mmio_address() + (bp_unit_index * size_of::<u32>()) as u64;
1039 // The raw breakpoint address as read from memory.
1040 let register_value = self.memory.read_word_32(reg_addr)?;
1041 // The breakpoint address after it has been adjusted for FpRev 1 or 2.
1042 let breakpoint:u32;
1043 if register_value & 0b1 == 0b1 {
1044 // We only care about `enabled` breakpoints.
1045 if ctrl_reg.rev() == 0 {
1046 breakpoint = FpRev1CompX::get_breakpoint_comparator(register_value)?;
1047 } else if ctrl_reg.rev() == 1 {
1048 breakpoint = FpRev2CompX::from(register_value).bpaddr() << 1;
1049 } else {
1050 tracing::warn!("This chip uses FPBU revision {}, which is not yet supported. HW breakpoints are not available.", ctrl_reg.rev());
1051 return Err(Error::Other(format!("This chip uses FPBU revision {}, which is not yet supported. HW breakpoints are not available.", ctrl_reg.rev())));
1052 }
1053 breakpoints.push(Some(breakpoint as u64));
1054 } else {
1055 breakpoints.push(None);
1056 }
1057 Ok(())
1058 })?;
1059 Ok(breakpoints)
1060 }
1061
1062 fn enable_breakpoints(&mut self, state: bool) -> Result<(), Error> {
1063 let mut val = FpCtrl::from(0);
1064 val.set_key(true);
1065 val.set_enable(state);
1066
1067 self.memory
1068 .write_word_32(FpCtrl::get_mmio_address(), val.into())?;
1069 self.memory.flush()?;
1070
1071 self.state.hw_breakpoints_enabled = state;
1072
1073 Ok(())
1074 }
1075
1076 fn set_hw_breakpoint(&mut self, bp_unit_index: usize, addr: u64) -> Result<(), Error> {
1077 let addr = valid_32bit_address(addr)?;
1078
1079 // First make sure they are asking for a breakpoint on a half-word boundary.
1080 if (addr & 0x1) > 0 {
1081 return Err(Error::Other(format!(
1082 "The requested breakpoint address 0x{addr:08x} is not on a half-word boundary"
1083 )));
1084 }
1085
1086 let raw_val = self.memory.read_word_32(FpCtrl::get_mmio_address())?;
1087 let ctrl_reg = FpCtrl::from(raw_val);
1088
1089 let val: u32;
1090 if ctrl_reg.rev() == 0 {
1091 val = FpRev1CompX::breakpoint_configuration(addr)?.into();
1092 } else if ctrl_reg.rev() == 1 {
1093 val = FpRev2CompX::breakpoint_configuration(addr).into();
1094 } else {
1095 tracing::warn!(
1096 "This chip uses FPBU revision {}, which is not yet supported. HW breakpoints are not available.",
1097 ctrl_reg.rev()
1098 );
1099 return Err(Error::Other(format!(
1100 "This chip uses FPBU revision {}, which is not yet supported. HW breakpoints are not available.",
1101 ctrl_reg.rev()
1102 )));
1103 }
1104
1105 // This is fine as FpRev1CompX and Rev2CompX are just two different
1106 // interpretations of the same memory region as Rev2 can handle bigger
1107 // address spaces than Rev1.
1108 let reg_addr = FpRev1CompX::get_mmio_address() + (bp_unit_index * size_of::<u32>()) as u64;
1109
1110 self.memory.write_word_32(reg_addr, val)?;
1111
1112 Ok(())
1113 }
1114
1115 fn clear_hw_breakpoint(&mut self, bp_unit_index: usize) -> Result<(), Error> {
1116 let mut val = FpRev1CompX::from(0);
1117 val.set_enable(false);
1118
1119 let reg_addr = FpRev1CompX::get_mmio_address() + (bp_unit_index * size_of::<u32>()) as u64;
1120
1121 self.memory.write_word_32(reg_addr, val.into())?;
1122
1123 Ok(())
1124 }
1125
1126 fn registers(&self) -> &'static CoreRegisters {
1127 if self.state.fp_present {
1128 &CORTEX_M_WITH_FP_CORE_REGISTERS
1129 } else {
1130 &CORTEX_M_CORE_REGISTERS
1131 }
1132 }
1133
1134 fn program_counter(&self) -> &'static CoreRegister {
1135 &PC
1136 }
1137
1138 fn frame_pointer(&self) -> &'static CoreRegister {
1139 &FP
1140 }
1141
1142 fn stack_pointer(&self) -> &'static CoreRegister {
1143 &SP
1144 }
1145
1146 fn return_address(&self) -> &'static CoreRegister {
1147 &RA
1148 }
1149
1150 fn hw_breakpoints_enabled(&self) -> bool {
1151 self.state.hw_breakpoints_enabled
1152 }
1153
1154 fn architecture(&self) -> Architecture {
1155 Architecture::Arm
1156 }
1157
1158 fn core_type(&self) -> CoreType {
1159 CoreType::Armv7m
1160 }
1161
1162 fn instruction_set(&mut self) -> Result<InstructionSet, Error> {
1163 Ok(InstructionSet::Thumb2)
1164 }
1165
1166 fn fpu_support(&mut self) -> Result<bool, Error> {
1167 Ok(self.state.fp_present)
1168 }
1169
1170 fn floating_point_register_count(&mut self) -> Result<usize, Error> {
1171 Ok(32)
1172 }
1173
1174 #[tracing::instrument(skip(self))]
1175 fn reset_catch_set(&mut self) -> Result<(), Error> {
1176 self.sequence
1177 .reset_catch_set(&mut *self.memory, CoreType::Armv7m, None)?;
1178
1179 Ok(())
1180 }
1181
1182 #[tracing::instrument(skip(self))]
1183 fn reset_catch_clear(&mut self) -> Result<(), Error> {
1184 self.sequence
1185 .reset_catch_clear(&mut *self.memory, CoreType::Armv7m, None)?;
1186
1187 Ok(())
1188 }
1189
1190 #[tracing::instrument(skip(self))]
1191 fn debug_core_stop(&mut self) -> Result<(), Error> {
1192 self.sequence
1193 .debug_core_stop(&mut *self.memory, CoreType::Armv7m)?;
1194 Ok(())
1195 }
1196
1197 #[tracing::instrument(skip(self))]
1198 fn enable_vector_catch(&mut self, condition: VectorCatchCondition) -> Result<(), Error> {
1199 let mut dhcsr = Dhcsr(self.memory.read_word_32(Dhcsr::get_mmio_address())?);
1200 dhcsr.set_c_debugen(true);
1201 self.memory
1202 .write_word_32(Dhcsr::get_mmio_address(), dhcsr.into())?;
1203
1204 let mut demcr = Demcr(self.memory.read_word_32(Demcr::get_mmio_address())?);
1205 match condition {
1206 VectorCatchCondition::HardFault => demcr.set_vc_harderr(true),
1207 VectorCatchCondition::CoreReset => demcr.set_vc_corereset(true),
1208 VectorCatchCondition::SecureFault => {
1209 return Err(Error::Arm(ArmError::ArchitectureRequired(&["ARMv8"])));
1210 }
1211 VectorCatchCondition::All => {
1212 demcr.set_vc_harderr(true);
1213 demcr.set_vc_corereset(true);
1214 }
1215 VectorCatchCondition::Svc | VectorCatchCondition::Hlt => {
1216 return Err(Error::NotImplemented("vector catch condition Svc/Hlt"));
1217 }
1218 };
1219
1220 self.memory
1221 .write_word_32(Demcr::get_mmio_address(), demcr.into())?;
1222 Ok(())
1223 }
1224
1225 fn disable_vector_catch(&mut self, condition: VectorCatchCondition) -> Result<(), Error> {
1226 let mut demcr = Demcr(self.memory.read_word_32(Demcr::get_mmio_address())?);
1227 match condition {
1228 VectorCatchCondition::HardFault => demcr.set_vc_harderr(false),
1229 VectorCatchCondition::CoreReset => demcr.set_vc_corereset(false),
1230 VectorCatchCondition::SecureFault => {
1231 return Err(Error::Arm(ArmError::ArchitectureRequired(&["ARMv8"])));
1232 }
1233 VectorCatchCondition::All => {
1234 demcr.set_vc_harderr(false);
1235 demcr.set_vc_corereset(false);
1236 }
1237 VectorCatchCondition::Svc | VectorCatchCondition::Hlt => {
1238 return Err(Error::NotImplemented("vector catch condition Svc/Hlt"));
1239 }
1240 };
1241
1242 self.memory
1243 .write_word_32(Demcr::get_mmio_address(), demcr.into())?;
1244 Ok(())
1245 }
1246}
1247
1248impl CoreMemoryInterface for Armv7m<'_> {
1249 type ErrorType = ArmError;
1250
1251 fn memory(&self) -> &dyn MemoryInterface<Self::ErrorType> {
1252 self.memory.as_ref()
1253 }
1254 fn memory_mut(&mut self) -> &mut dyn MemoryInterface<Self::ErrorType> {
1255 self.memory.as_mut()
1256 }
1257}
1258
1259#[test]
1260fn breakpoint_register_value() {
1261 // Check that the register configuration for the FPBU is
1262 // calculated correctly.
1263 //
1264 // See ARMv7 Architecture Reference Manual, Section C1.11.5
1265 let address: u32 = 0x0800_09A4;
1266
1267 let reg = FpRev1CompX::breakpoint_configuration(address).unwrap();
1268 let reg_val: u32 = reg.into();
1269
1270 assert_eq!(0x4800_09A5, reg_val);
1271}
1272
1273#[test]
1274fn unsupported_breakpoint_address() {
1275 // Revision 1 of the FPBU only supports breakpoints for address < 0x2000_0000.
1276 let address: u32 = 0x2000_0000;
1277
1278 FpRev1CompX::breakpoint_configuration(address).unwrap_err();
1279}