playdate_symbolize/
ef.rs

1#![allow(dead_code)]
2#![allow(non_snake_case)]
3
4use std::collections::HashMap;
5use std::fmt::Binary;
6use std::fmt::Display;
7use std::fmt::Debug;
8
9use crate::fmt::addr::Addr;
10
11
12/// SCB+
13#[derive(Debug, Clone)]
14pub struct ExceptionFrame {
15	// General-purpose registers:
16	// Low regs:
17	r0: u32,
18	r1: u32,
19	r2: u32,
20	r3: u32,
21	// High regs:
22	r12: u32,
23	/// Link Register (r14)
24	lr: Addr<u32>,
25	/// Program Counter (r15)
26	/// Current program address.
27	pc: Addr<u32>,
28
29	// Special registers:
30	/// Program status register.
31	///
32	/// The Program Status Register combines:
33	/// - Application Program Status Register ([`APSR`]).
34	/// - Interrupt Program Status Register ([`IPSR`]).
35	/// - Execution Program Status Register ([`EPSR`]).
36	psr: PSR,
37
38	/// Configurable Fault Status
39	///
40	/// combines [`ufsr`][Self::ufsr] + [`bfsr`][Self::bfsr] + [`mmfsr`][Self::mmfsr].
41	cfsr: CFSR,
42
43	/// HardFault Status
44	hfsr: HSFR,
45
46	/// MemManage Fault Address
47	///
48	/// The BFAR address is associated with a precise data access BusFault.
49	/// _This field is valid only when [`MMFSR::MMARVALID`] is set._
50	mmfar: Addr<u32>,
51
52	/// BusFault Address.
53	/// Data address for a precise BusFault.
54	///
55	/// Contains the address of a location that produced a BusFault.
56	/// The [`BFSR`] shows the reason for the fault.
57	/// _This field is valid only when [`BFSR::BFARVALID`] is set._
58	bfar: Addr<u32>,
59
60	/// `bootinfo.rcccsr` value
61	rcccsr: u32,
62}
63
64
65pub const SCB_CPACR_FPU_MASK: u32 = 0b11_11 << 20;
66pub const SCB_CPACR_FPU_ENABLE: u32 = 0b01_01 << 20;
67pub const SCB_CPACR_FPU_USER: u32 = 0b10_10 << 20;
68
69impl ExceptionFrame {
70	pub fn ufsr(&self) -> UFSR { self.cfsr.ufsr() }
71	pub fn bfsr(&self) -> BFSR { self.cfsr.bfsr() }
72	pub fn mmfsr(&self) -> MMFSR { self.cfsr.mmfsr() }
73}
74
75
76impl ExceptionFrame {
77	pub fn new_from(values: &HashMap<String, Addr<u32>>) -> Result<Self, &'static str> {
78		Ok(Self { r0: values.get("r0").ok_or("no r0")?.value(),
79		          r1: values.get("r1").ok_or("no r1")?.value(),
80		          r2: values.get("r2").ok_or("no r2")?.value(),
81		          r3: values.get("r3").ok_or("no r3")?.value(),
82		          r12: values.get("r12").ok_or("no r12")?.value(),
83		          lr: values.get("lr").ok_or("no lr")?.to_owned(),
84		          pc: values.get("pc").ok_or("no pc")?.to_owned(),
85		          psr: values.get("psr").ok_or("no psr")?.value().into(),
86		          cfsr: values.get("cfsr").ok_or("no cfsr")?.value().into(),
87		          hfsr: values.get("hfsr").ok_or("no hfsr")?.value().into(),
88		          mmfar: values.get("mmfar").ok_or("no mmfar")?.to_owned(),
89		          bfar: values.get("bfar").ok_or("no bfar")?.to_owned(),
90		          rcccsr: values.get("rcccsr").ok_or("no rcccsr")?.value().into() })
91	}
92}
93
94
95macro_rules! bit {
96	($mask:literal, $name:ident.$field:ident, $doc:literal) => {
97		impl $name {
98			bit! {$mask, $field, $doc}
99		}
100	};
101	($mask:literal, $name:ident) => {
102		pub fn $name(&self) -> bool { self.0 & $mask != 0 }
103
104		paste::paste! {
105			pub const [<DOC_ $name>]: &'static str = stringify!($name);
106		}
107	};
108	($mask:literal, $name:ident, $doc:literal) => {
109		#[doc = $doc]
110		pub fn $name(&self) -> bool { self.0 & $mask != 0 }
111
112		paste::paste! {
113			pub const [<DOC_ $name>]: &'static str = $doc;
114		}
115	};
116
117	($name:ident, $mask:literal) => {
118		bit!($mask, $name)
119	};
120	($name:ident, $mask:literal, $doc:literal) => {
121		bit!($mask, $name, $doc)
122	};
123}
124
125
126macro_rules! impl_fmt {
127		($name:ident) => {
128			impl Display for $name {
129				fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { Display::fmt(&self.0, f) }
130			}
131			impl Binary for $name {
132				fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { Binary::fmt(&self.0, f) }
133			}
134		};
135
136		($name:ident, $($next:ident),+) => {
137			impl_fmt!($name);
138         impl_fmt!($($next),+);
139		}
140	}
141
142
143macro_rules! impl_from {
144	($name:ident<u8>) => {
145		impl_from! {impl $name<u8>}
146		impl_from! {impl $name<i8>}
147	};
148	($name:ident<u16>) => {
149		impl_from! {impl $name<u16>}
150		impl_from! {impl $name<i16>}
151		impl_from! {$name<u8>}
152	};
153	($name:ident<u32>) => {
154		impl_from! {impl $name<u32>}
155		impl_from! {impl $name<i32>}
156		impl_from! {$name<u16>}
157	};
158	($name:ident<u64>) => {
159		impl_from! {impl $name<u64>}
160		impl_from! {impl $name<i64>}
161		impl_from! {$name<u32>}
162	};
163	($name:ident) => {
164		impl_from! {$name<u32>}
165	};
166
167	(impl $name:ident<$t:ty>) => {
168		impl From<$t> for $name {
169			fn from(value: $t) -> Self { Self(value as _) }
170		}
171	};
172}
173
174macro_rules! impl_try_from {
175	($name:ident<u8>) => {
176		impl_try_from! {impl $name<u8, u16>}
177		impl_try_from! {impl $name<u8, u32>}
178		impl_try_from! {impl $name<u8, u64>}
179	};
180	($name:ident<u16>) => {
181		impl_try_from! {impl $name<u8, u32>}
182		impl_try_from! {impl $name<u8, u64>}
183	};
184	($name:ident<u32>) => {
185		impl_try_from! {impl $name<u8, u64>}
186	};
187	($name:ident<u64>) => {};
188	($name:ident) => {
189		impl_try_from! {$name<u32>}
190	};
191
192	(impl $name:ident<$t:ty, $th:ty>) => {
193		impl TryFrom<$th> for $name
194			where $t: TryFrom<$th>,
195			      Self: From<$t>
196		{
197			type Error = <$t as TryFrom<$th>>::Error;
198			fn try_from(value: $th) -> Result<Self, Self::Error> {
199				let value: $t = value.try_into()?;
200				Ok(Self::from(value))
201			}
202		}
203	};
204}
205
206macro_rules! impl_to {
207	($name:ident<u8>) => {
208		impl_to! {impl $name<u8>}
209		impl_to! {impl $name<i8>}
210		impl_to! {$name<u16>}
211	};
212	($name:ident<u16>) => {
213		impl_to! {impl $name<u16>}
214		impl_to! {impl $name<i16>}
215		impl_to! {$name<u32>}
216	};
217	($name:ident<u32>) => {
218		impl_to! {impl $name<u32>}
219		impl_to! {impl $name<i32>}
220		impl_to! {$name<u64>}
221	};
222	($name:ident<u64>) => {
223		impl_to! {impl $name<u64>}
224		impl_to! {impl $name<i64>}
225	};
226	($name:ident) => {
227		impl_to! {$name<u32>}
228	};
229
230	(impl $name:ident<$t:ty>) => {
231		impl From<$name> for $t {
232			fn from(value: $name) -> Self { value.0 as _ }
233		}
234	};
235}
236
237macro_rules! impl_convert {
238	($name:ident<u8>) => {
239		impl_to! {$name<u8>}
240		impl_from! {$name<u8>}
241		impl_try_from! {$name<u8>}
242	};
243	($name:ident<u16>) => {
244		impl_to! {$name<u16>}
245		impl_from! {$name<u16>}
246		impl_try_from! {$name<u16>}
247	};
248	($name:ident<u32>) => {
249		impl_to! {$name<u32>}
250		impl_from! {$name<u32>}
251		impl_try_from! {$name<u32>}
252	};
253	($name:ident<u64>) => {
254		impl_to! {$name<u64>}
255		impl_from! {$name<u64>}
256		impl_try_from! {$name<u64>}
257	};
258	($name:ident) => {
259		impl_convert! {$name<u32>}
260	};
261}
262
263macro_rules! impl_reg {
264		($name:ident) => {
265			impl_reg!{$name<u32>}
266		};
267		($name:ident<$t:ty>) => {
268			impl_fmt!{$name}
269		};
270
271		($name:ident$(<$t:ty>)?, $($next:ident$(<$tn:ty>)?),+) => {
272			impl_reg!($name$(<$t>)?);
273         impl_reg!($($next$(<$tn>)?),+);
274		}
275	}
276
277impl_reg! {PSR, IPSR<u16>, APSR<u16>, EPSR, CFSR, UFSR<u16>, BFSR<u8>, MMFSR<u8>, HSFR}
278impl_convert! { PSR }
279impl_convert! { IPSR<u16> }
280impl_convert! { APSR<u16> }
281impl_convert! { EPSR }
282impl_convert! { CFSR }
283impl_convert! { UFSR<u16> }
284impl_convert! { BFSR<u8> }
285impl_convert! { MMFSR<u8> }
286impl_convert! { HSFR }
287
288// _PSR entry in the crash log also includes the EPSR as well._
289// ESPR - Execution Program Status Register
290//
291/// Program status register.
292///
293/// The Program Status Register (PSR) combines:
294/// - Application Program Status Register (APSR).
295/// - Interrupt Program Status Register (IPSR).
296/// - Execution Program Status Register (EPSR).
297#[derive(Clone, Copy, PartialEq, Eq)]
298pub struct PSR(u32);
299
300impl PSR {
301	pub fn apsr(&self) -> APSR { APSR(((self.0 & 0b11111000_00000111_00000000_00000000) >> 16) as u16) }
302	pub fn ipsr(&self) -> IPSR { IPSR((self.0 & 0b00000000_00000000_00000001_11111111) as u16) }
303	pub fn epsr(&self) -> EPSR { EPSR(self.0 & 0b00000111_00000000_11111100_00000000) }
304}
305
306impl Debug for PSR {
307	fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
308		f.debug_struct("PSR")
309		 .field("APSR", &self.apsr())
310		 .field("IPSR", &self.ipsr())
311		 .field("EPSR", &self.epsr())
312		 .finish()
313	}
314}
315
316
317/// Application Program Status Register.
318#[derive(Clone, Copy, PartialEq, Eq)]
319pub struct APSR(u16);
320bit! {0b1000_0000_00000000, APSR.N, r#"Negative"#}
321bit! {0b0100_0000_00000000, APSR.Z, r#"Zero"#}
322bit! {0b0010_0000_00000000, APSR.C, r#"Carry or borrow"#}
323bit! {0b0001_0000_00000000, APSR.V, r#"Overflow"#}
324bit! {0b0000_1000_00000000, APSR.Q, r#"DSP overflow and saturation"#}
325bit! {0b0000_0000_00000111, APSR.GE, r#"Greater than or Equals"#}
326impl Debug for APSR {
327	fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
328		f.debug_struct("APSR")
329		 .field("N", &self.N())
330		 .field("Z", &self.Z())
331		 .field("C", &self.C())
332		 .field("V", &self.V())
333		 .field("Q", &self.Q())
334		 .field("GE", &self.GE())
335		 .finish()
336	}
337}
338impl RegTags for APSR {
339	fn is_empty(&self) -> bool { self.0 == 0 }
340
341	fn tags(&self) -> impl IntoIterator<Item = (&str, &str)> {
342		if !self.is_empty() {
343			[
344			 self.N().then_some(("N", Self::DOC_N)),
345			 self.Z().then_some(("Z", Self::DOC_Z)),
346			 self.C().then_some(("C", Self::DOC_C)),
347			 self.V().then_some(("V", Self::DOC_V)),
348			 self.Q().then_some(("Q", Self::DOC_Q)),
349			 self.GE().then_some(("GE", Self::DOC_GE)),
350			].into_iter()
351			.flatten()
352		} else {
353			<[_; 6]>::default().into_iter().flatten()
354		}
355	}
356}
357
358/// Interrupt Program Status Register.
359#[derive(Clone, Copy, PartialEq, Eq)]
360pub struct IPSR(u16);
361bit! {0b0000_0000_00000001, IPSR.TM, r#"Thread mode"#}
362bit! {0b0000_0000_00000100, IPSR.NMI, r#"NMI"#}
363bit! {0b0000_0000_00001000, IPSR.HF, r#"HardFault"#}
364bit! {0b0000_0000_00010000, IPSR.MM, r#"MemManage"#}
365bit! {0b0000_0000_00100000, IPSR.BF, r#"BusFault"#}
366bit! {0b0000_0000_01000000, IPSR.UF, r#"UsageFault"#}
367impl Debug for IPSR {
368	fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
369		f.debug_struct("IPSR")
370		 .field("TM", &self.TM())
371		 .field("NMI", &self.NMI())
372		 .field("HF", &self.HF())
373		 .field("MM", &self.MM())
374		 .field("BF", &self.BF())
375		 .field("UF", &self.UF())
376		 .finish()
377	}
378}
379impl RegTags for IPSR {
380	fn is_empty(&self) -> bool { self.0 == 0 }
381
382	fn tags(&self) -> impl IntoIterator<Item = (&str, &str)> {
383		if !self.is_empty() {
384			[
385			 self.TM().then_some(("TM", Self::DOC_TM)),
386			 self.NMI().then_some(("NMI", Self::DOC_NMI)),
387			 self.HF().then_some(("HF", Self::DOC_HF)),
388			 self.MM().then_some(("MM", Self::DOC_MM)),
389			 self.BF().then_some(("BF", Self::DOC_BF)),
390			 self.UF().then_some(("UF", Self::DOC_UF)),
391			].into_iter()
392			.flatten()
393		} else {
394			<[_; 6]>::default().into_iter().flatten()
395		}
396	}
397}
398
399/// Execution Program Status Register.
400#[derive(Clone, Copy, PartialEq, Eq)]
401pub struct EPSR(u32);
402bit! {0b00000110_00000000_00001100_00000000, EPSR.IS_NOT_ICI, r#"Interruptible-continuable instruction bits."#}
403bit! {0b00000000_00000000_11110000_00000000, EPSR.ICI, r#"Interruptible-continuable instruction bits."#}
404bit! {0b00000110_00000000_11111100_00000000, EPSR.IT, r#"If-Then block. Indicates the execution state bits of the IT instruction"#}
405bit! {0b00000001_00000000_00000000_00000000, EPSR.T, r#"Thumb state"#}
406impl Debug for EPSR {
407	fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
408		f.debug_struct("EPSR")
409		 .field("IS_NOT_ICI", &self.IS_NOT_ICI())
410		 .field("ICI", &self.ICI())
411		 .field("IT", &self.IT())
412		 .field("T", &self.T())
413		 .finish()
414	}
415}
416impl RegTags for EPSR {
417	fn is_empty(&self) -> bool { self.0 == 0 }
418
419	fn tags(&self) -> impl IntoIterator<Item = (&str, &str)> {
420		if !self.is_empty() {
421			[
422			 self.IS_NOT_ICI().then_some(("IS_NOT_ICI", Self::DOC_IS_NOT_ICI)),
423			 self.ICI().then_some(("ICI", Self::DOC_ICI)),
424			 self.IT().then_some(("IT", Self::DOC_IT)),
425			 self.T().then_some(("T", Self::DOC_T)),
426			].into_iter()
427			.flatten()
428		} else {
429			<[_; 4]>::default().into_iter().flatten()
430		}
431	}
432}
433
434
435/// HardFault Status Register.
436///
437/// The Hard Fault status register indicates an incorrect usage of a CPU instruction.
438#[derive(Clone, Copy, PartialEq, Eq)]
439pub struct HSFR(u32);
440impl HSFR {
441	// When this bit is set, the PC value stacked for the exception return points to the instruction
442	// that was preempted by the exception. This error is always a Hard Fault.
443	bit! {0b00000000_00000000_00000000_00000010, VECTTBL, r#"Bus Fault on vector table read."#}
444	bit! {0b01000000_00000000_00000000_00000000, FORCED, r#"Forced Hard Fault."#}
445	bit! {0b10000000_00000000_00000000_00000000, DEBUGEVT, r#"Reserved for Debug use."#}
446}
447impl Debug for HSFR {
448	fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
449		f.debug_struct("HSFR")
450		 .field("VECTTBL", &self.VECTTBL())
451		 .field("FORCED", &self.FORCED())
452		 .field("DEBUGEVT", &self.DEBUGEVT())
453		 .finish()
454	}
455}
456impl RegTags for HSFR {
457	fn is_empty(&self) -> bool { self.0 == 0 }
458
459	fn tags(&self) -> impl IntoIterator<Item = (&str, &str)> {
460		if !self.is_empty() {
461			[
462			 self.VECTTBL().then_some(("VECTTBL", Self::DOC_VECTTBL)),
463			 self.FORCED().then_some(("FORCED", Self::DOC_FORCED)),
464			 self.DEBUGEVT().then_some(("DEBUGEVT", Self::DOC_DEBUGEVT)),
465			].into_iter()
466			.flatten()
467		} else {
468			<[_; 3]>::default().into_iter().flatten()
469		}
470	}
471}
472
473
474/// Configurable Fault Status
475///
476/// combines [`ufsr`][Self::ufsr] + [`bfsr`][Self::bfsr] + [`mmfsr`][Self::mmfsr].
477#[derive(Clone, Copy, PartialEq, Eq)]
478pub struct CFSR(u32);
479
480impl CFSR {
481	pub fn ufsr(&self) -> UFSR { UFSR(((self.0 & 0b11111111_11111111_00000000_00000000) >> 16) as u16) }
482	pub fn bfsr(&self) -> BFSR { BFSR(((self.0 & 0b00000000_00000000_11111111_00000000) >> 8) as u8) }
483	pub fn mmfsr(&self) -> MMFSR { MMFSR(self.0 as u8) }
484}
485
486impl Debug for CFSR {
487	fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
488		f.debug_struct("CFSR")
489		 .field("UFSR", &self.ufsr())
490		 .field("BFSR", &self.bfsr())
491		 .field("MMFSR", &self.mmfsr())
492		 .finish()
493	}
494}
495
496
497/// The UsageFault Status Register contains the status for some instruction execution faults, and for data access.
498#[derive(Clone, Copy, PartialEq, Eq)]
499pub struct UFSR(u16);
500impl UFSR {
501	bit! {0b0000_0000_0000_0001, UNDEFINSTR, r#"Undefined instruction."#}
502	bit! {0b0000_0000_0000_0010, INVSTATE, r#"Invalid state."#}
503	bit! {0b0000_0000_0000_0100, INVPC, r#"Invalid `PC` load UsageFault, caused by an invalid `EXC_RETURN` value."#}
504	bit! {0b0000_0000_0000_1000, NOCP, r#"No coprocessor."#}
505	bit! {0b0000_0001_0000_0000, UNALIGNED, r#"Unaligned access UsageFault."#}
506	bit! {0b0010_0000, DIVBYZERO, r#"Divide by zero UsageFault."#}
507}
508
509
510impl RegTags for UFSR {
511	fn is_empty(&self) -> bool { self.0 == 0 }
512
513	fn tags(&self) -> impl IntoIterator<Item = (&str, &str)> {
514		if self.is_empty() {
515			return [None, None, None, None, None, None].into_iter().flatten();
516		}
517
518		[
519		 self.UNDEFINSTR().then_some(("UNDEFINSTR", Self::DOC_UNDEFINSTR)),
520		 self.INVSTATE().then_some(("INVSTATE", Self::DOC_INVSTATE)),
521		 self.INVPC().then_some(("INVPC", Self::DOC_INVPC)),
522		 self.NOCP().then_some(("NOCP", Self::DOC_NOCP)),
523		 self.UNALIGNED().then_some(("UNALIGNED", Self::DOC_UNALIGNED)),
524		 self.DIVBYZERO().then_some(("DIVBYZERO", Self::DOC_DIVBYZERO)),
525		].into_iter()
526		.flatten()
527	}
528}
529
530impl Debug for UFSR {
531	fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
532		f.debug_struct("UFSR")
533		 .field("UNDEFINSTR", &self.UNDEFINSTR())
534		 .field("INVSTATE", &self.INVSTATE())
535		 .field("INVPC", &self.INVPC())
536		 .field("NOCP", &self.NOCP())
537		 .field("UNALIGNED", &self.UNALIGNED())
538		 .field("DIVBYZERO", &self.DIVBYZERO())
539		 .finish()
540	}
541}
542
543
544/// The BusFault Status Register shows the status of bus errors resulting from instruction fetches and data accesses
545/// and indicates memory access faults detected during a bus operation.
546#[derive(Clone, Copy, PartialEq, Eq)]
547pub struct BFSR(u8);
548impl BFSR {
549	bit! {0b0000_0001, IBUSERR, r#"Instruction bus error. Records whether a BusFault on an instruction prefetch has occurred."#}
550	bit! {0b0000_0010, PRECISERR, r#"Precise data bus error."#}
551	bit! {0b0000_0100, IMPRECISERR, r#"Imprecise data bus error."#}
552	bit! {0b0000_1000, UNSTKERR, r#"BusFault on unstacking for a return from exception."#}
553	bit! {0b0001_0000, STKERR, r#"BusFault on stacking for exception entry."#}
554	bit! {0b0010_0000, LSPERR, r#"BusFault during floating point lazy state preservation (only when FPU present)."#}
555	bit! {0b1000_0000, BFARVALID, r#"BusFault Address Register valid flag. BFAR holds a valid fault address."#}
556}
557impl Debug for BFSR {
558	fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
559		f.debug_struct("BFSR")
560		 .field("IBUSERR", &self.IBUSERR())
561		 .field("PRECISERR", &self.PRECISERR())
562		 .field("IMPRECISERR", &self.IMPRECISERR())
563		 .field("UNSTKERR", &self.UNSTKERR())
564		 .field("STKERR", &self.STKERR())
565		 .field("LSPERR", &self.LSPERR())
566		 .field("BFARVALID", &self.BFARVALID())
567		 .finish()
568	}
569}
570impl RegTags for BFSR {
571	fn is_empty(&self) -> bool { self.0 == 0 }
572
573	fn tags(&self) -> impl IntoIterator<Item = (&str, &str)> {
574		if !self.is_empty() {
575			[
576			 self.IBUSERR().then_some(("IBUSERR", Self::DOC_IBUSERR)),
577			 self.PRECISERR().then_some(("PRECISERR", Self::DOC_PRECISERR)),
578			 self.IMPRECISERR()
579			     .then_some(("IMPRECISERR", Self::DOC_IMPRECISERR)),
580			 self.UNSTKERR().then_some(("UNSTKERR", Self::DOC_UNSTKERR)),
581			 self.STKERR().then_some(("STKERR", Self::DOC_STKERR)),
582			 self.LSPERR().then_some(("LSPERR", Self::DOC_LSPERR)),
583			].into_iter()
584			.flatten()
585		} else {
586			<[_; 6]>::default().into_iter().flatten()
587		}
588	}
589}
590
591
592/// The MemManage fault status register
593/// indicates a memory access violation detected by the Memory Protection Unit (MPU).
594#[derive(Clone, Copy, PartialEq, Eq)]
595pub struct MMFSR(u8);
596impl Debug for MMFSR {
597	fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
598		f.debug_struct("MMFSR")
599		 .field("IACCVIOL", &self.IACCVIOL())
600		 .field("DACCVIOL", &self.DACCVIOL())
601		 .field("MUNSTKERR", &self.MUNSTKERR())
602		 .field("MSTKERR", &self.MSTKERR())
603		 .field("MLSPERR", &self.MLSPERR())
604		 .field("MMARVALID", &self.MMARVALID())
605		 .finish()
606	}
607}
608impl MMFSR {
609	// Instruction access violation flag.
610	// The processor attempted an instruction fetch from a location that does not permit execution
611	//
612	// The PC value stacked for the exception return points to the faulting instruction. The processor
613	// has not written a fault address to the `MMFAR`. This fault condition occurs on any attempt of
614	// instruction fetches to an XN (eXecute Never) region, even when the MPU is disabled or not
615	// present. Potential reasons:
616	// - Branch to regions that are not defined in the MPU or defined as non-executable.
617	// - Invalid return due to corrupted stack content.
618	// - Incorrect entry in the exception vector table.
619	bit! {0b0000_0001, IACCVIOL, r#"Instruction access violation.
620		The processor attempted an instruction fetch from a location that does not permit execution.
621		Faulting instruction: see `PC`.
622		"#}
623
624	// Data access violation flag.
625	// The processor attempted a load or store at a location that does not permit the operation.
626	//
627	// The PC value stacked for the exception return points to the faulting instruction.
628	// The processor has loaded the `MMFAR` with the address of the attempted access.
629	bit! {0b0000_0010, DACCVIOL, r#"Data access violation.
630		The processor attempted a load or store at a location that does not permit the operation.
631		Faulting instruction: see `PC`.
632		Address of the attempted access: see `MMFAR`.
633		"#}
634
635	// MemManage fault on unstacking for a return from exception.
636	//
637	// Unstacking for an exception return has caused one or more access violations.
638	//
639	// This fault is chained to the handler which means that the original return stack is still present.
640	// The processor has not adjusted the SP from the failing return, and has not performed a new
641	// save. The processor has not written a fault address to the `MMFAR`. Potential reasons:
642	// - Stack pointer is corrupted
643	// - MPU region for the stack changed during execution of the exception handler.
644	bit! {0b0000_1000, MUNSTKERR, r#"MemManage fault on unstacking for a return from exception.
645		Fault address - see MMFAR.
646		Potential reasons:
647		- Stack pointer is corrupted
648		- MPU region for the stack changed during execution of the exception handler
649		"#}
650
651
652	bit! {0b0001_0000, MSTKERR, r#"
653			MemManage fault on stacking for exception entry.
654
655			The SP is still adjusted but the values in the context area on the stack might be incorrect. The
656			processor has not written a fault address to the MMFAR. Potential reasons:
657			- Stack pointer is corrupted or not initialized
658			- Stack is reaching a region not defined by the MPU as read/write memory.
659			"#}
660
661	bit! {0b0010_0000, MLSPERR, r#"MemManage fault during floating point lazy state preservation (only Cortex-M4 with FPU)."#}
662	bit! {0b1000_0000, MMARVALID, r#"MemManage Fault Address Register (MMFAR) valid flag. SCB->MMFAR holds a valid fault address."#}
663}
664
665impl RegTags for MMFSR {
666	fn is_empty(&self) -> bool { self.0 == 0 }
667
668	fn tags(&self) -> impl IntoIterator<Item = (&str, &str)> {
669		if self.is_empty() {
670			return [None, None, None, None, None].into_iter().flatten();
671		}
672
673		[
674		 self.IACCVIOL().then_some(("IACCVIOL", Self::DOC_IACCVIOL)),
675		 self.DACCVIOL().then_some(("DACCVIOL", Self::DOC_DACCVIOL)),
676		 self.MUNSTKERR().then_some(("MUNSTKERR", Self::DOC_MUNSTKERR)),
677		 self.MSTKERR().then_some(("MSTKERR", Self::DOC_MSTKERR)),
678		 self.MLSPERR().then_some(("MLSPERR", Self::DOC_MLSPERR)),
679		].into_iter()
680		.flatten()
681	}
682}
683
684
685pub trait RegTags {
686	/// Has no tags
687	fn is_empty(&self) -> bool;
688
689	/// Returns `true` tags as (name + description)
690	fn tags(&self) -> impl IntoIterator<Item = (&str, &str)>;
691}
692
693
694pub enum FaultKind {
695	// HFSR:
696	/// Bus error on a vector read error HardFault
697	VECTTBL,
698	/// Fault that is escalated to a hard fault
699	FORCED,
700	/// Fault on breakpoint escalation
701	DEBUGEVT,
702
703	// MMFSR:
704	/// Fault on instruction access MemManage
705	IACCVIOL,
706	/// Fault on direct data access
707	DACCVIOL,
708	/// Context stacking, because of an MPU access violation
709	MSTKERR,
710	/// Context unstacking, because of an MPU access violation
711	MUNSTKERR,
712	/// During lazy floating-point state preservation
713	MLSPERR,
714
715	// BFSR:
716	/// During exception stacking BusFault
717	STKERR,
718	/// During exception unstacking
719	UNSTKERR,
720	/// During instruction prefetching, precise
721	IBUSERR,
722	/// During lazy floating-point state preservation
723	LSPERR,
724	/// Precise data access error, precise
725	PRECISERR,
726	/// Imprecise data access error, imprecise
727	IMPRECISERR,
728
729	// UFSR:
730	/// Undefined instruction UsageFault
731	UNDEFINSTR,
732	/// Attempt to enter an invalid instruction set state
733	INVSTATE,
734	/// Failed integrity check on exception return
735	INVPC,
736	/// Attempt to access a non-existing coprocessor
737	NOCPC,
738	/// Illegal unaligned load or store
739	UNALIGNED,
740	/// Stack overflow
741	STKOF,
742	/// Divide By 0
743	DIVBYZERO,
744}
745
746
747#[derive(Clone, Copy, Debug, PartialEq, Eq)]
748pub enum FpuAccessMode {
749	/// FPU is not accessible
750	Disabled,
751	/// FPU is accessible in Privileged and User mode
752	Enabled,
753	/// FPU is accessible in Privileged mode only
754	Privileged,
755}
756
757
758/// Active exception number
759#[derive(Clone, Copy, Debug, Eq, PartialEq, PartialOrd, Hash)]
760pub enum VectActive {
761	/// Thread mode
762	ThreadMode,
763
764	/// Processor core exception (internal interrupts)
765	Exception(Exception),
766
767	/// Device specific exception (external interrupts)
768	Interrupt {
769		/// Interrupt number. This number is always within half open range `[0, 512)` (9 bit)
770		irqn: u16,
771	},
772}
773
774impl VectActive {
775	/// Converts a vector number into `VectActive`
776	#[inline]
777	pub fn from(vect_active: u16) -> Option<Self> {
778		Some(match vect_active {
779			0 => VectActive::ThreadMode,
780			2 => VectActive::Exception(Exception::NonMaskableInt),
781			3 => VectActive::Exception(Exception::HardFault),
782			4 => VectActive::Exception(Exception::MemoryManagement),
783			5 => VectActive::Exception(Exception::BusFault),
784			6 => VectActive::Exception(Exception::UsageFault),
785			7 => VectActive::Exception(Exception::SecureFault),
786			11 => VectActive::Exception(Exception::SVCall),
787			12 => VectActive::Exception(Exception::DebugMonitor),
788			14 => VectActive::Exception(Exception::PendSV),
789			15 => VectActive::Exception(Exception::SysTick),
790			irqn if (16..512).contains(&irqn) => VectActive::Interrupt { irqn: irqn - 16 },
791			_ => return None,
792		})
793	}
794}
795
796
797/// Processor core exceptions (internal interrupts)
798#[derive(Clone, Copy, Debug, Eq, PartialEq, PartialOrd, Hash)]
799pub enum Exception {
800	/// Non maskable interrupt
801	NonMaskableInt,
802
803	/// Hard fault interrupt
804	HardFault,
805
806	/// Memory management interrupt (not present on Cortex-M0 variants)
807	MemoryManagement,
808
809	/// Bus fault interrupt (not present on Cortex-M0 variants)
810	BusFault,
811
812	/// Usage fault interrupt (not present on Cortex-M0 variants)
813	UsageFault,
814
815	/// Secure fault interrupt (only on ARMv8-M)
816	SecureFault,
817
818	/// SV call interrupt
819	SVCall,
820
821	/// Debug monitor interrupt (not present on Cortex-M0 variants)
822	DebugMonitor,
823
824	/// Pend SV interrupt
825	PendSV,
826
827	/// System Tick interrupt
828	SysTick,
829}
830
831
832#[cfg(test)]
833mod tests {
834	use super::*;
835
836
837	#[test]
838	fn test_mmfsr_daccviol() {
839		let reg = ExceptionFrame { r0: 0x00000004,
840		                           r1: 0x00000008,
841		                           r2: 0x00000008,
842		                           r3: 0x00000008,
843		                           r12: 0x00000008,
844		                           lr: 0x6000d2e7.into(),
845		                           pc: 0x6000d5e8.into(),
846		                           psr: 0x010f0000.into(),
847		                           cfsr: 0x00000082.into(),
848		                           hfsr: 0x00000000.into(),
849		                           mmfar: 0x00000004.into(),
850		                           bfar: 0x00000004.into(),
851		                           rcccsr: 0x00000000 };
852		let mmfsr = reg.mmfsr();
853		assert!(mmfsr.DACCVIOL());
854		assert!(mmfsr.MMARVALID());
855	}
856
857	#[test]
858	fn test_mmfsr_daccviol__() {
859		let reg = ExceptionFrame { r0: 0x00000004,
860		                           r1: 0x00000008,
861		                           r2: 0x00000008,
862		                           r3: 0x00000008,
863		                           r12: 0x00000008,
864		                           lr: 0x6000d2e7.into(),
865		                           pc: 0x6000d5e8.into(),
866		                           psr: 0x010f0000.into(),
867		                           cfsr: 0x00000082.into(),
868		                           hfsr: 0x00000000.into(),
869		                           mmfar: 0x00000004.into(),
870		                           bfar: 0x00000004.into(),
871		                           rcccsr: 0x00000000 };
872		let mmfsr = reg.mmfsr();
873		assert!(mmfsr.DACCVIOL());
874		assert!(mmfsr.MMARVALID());
875	}
876}