rp235x_hal/vector_table.rs
1//! Interrupt vector table utilities
2//!
3//! Provide functionality to switch to another vector table using the
4//! Vector Table Offset Register (VTOR) of the Cortex-33
5//! Also provides types and utilities for copying a vector table into RAM
6
7/// Entry for a Vector in the Interrupt Vector Table.
8///
9/// Each entry in the Vector table is a union with usize to allow it to be 0 initialized via const initializer
10///
11/// Implementation borrowed from https://docs.rs/cortex-m-rt/0.7.1/cortex_m_rt/index.html#__interrupts
12#[derive(Clone, Copy)]
13union Vector {
14 handler: extern "C" fn(),
15 reserved: usize,
16}
17
18/// Data type for a properly aligned interrupt vector table
19///
20/// The VTOR register can only point to a 128 byte offsets - see
21/// [Cortex-33 Devices Generic User Guide](https://developer.arm.com/documentation/100235/0004/the-cortex-m33-peripherals/system-control-block/vector-table-offset-register?lang=en) -
22/// so that is our required alignment.
23/// The vector table length depends on the number of interrupts the system supports.
24/// The first 16 words are defined in the ARM Cortex-M spec.
25/// The Cortex-M33 cores on RP235x have 52 interrupts, of which only 47 are wired to external interrupt
26/// signals - but the last 6 can be used for software interrupts so leave room for them
27#[repr(C, align(128))]
28pub struct VectorTable {
29 /// SP + Reset vector + 14 exceptions + 52 interrupts = 68 entries (272 bytes) in an rp235x core's VectorTable
30 table: [Vector; 68],
31}
32
33impl Default for VectorTable {
34 fn default() -> Self {
35 Self::new()
36 }
37}
38
39impl VectorTable {
40 /// Create a new vector table. All entries will point to 0 - you must call init()
41 /// on this to copy the current vector table before setting it as active
42 pub const fn new() -> VectorTable {
43 VectorTable {
44 table: [Vector { reserved: 0 }; 68],
45 }
46 }
47
48 /// Initialise our vector table by copying the current table on top of it
49 #[allow(unknown_lints)]
50 #[allow(clippy::needless_pass_by_ref_mut)]
51 pub fn init(&mut self, ppb: &mut crate::pac::PPB) {
52 let mut vector_table = ppb.vtor().read().bits() as *const usize;
53 for entry in self.table.iter_mut() {
54 // Safety:
55 //
56 // This value must be valid because it's in the current vector table.
57 *entry = Vector {
58 reserved: unsafe { vector_table.read() },
59 };
60 // Safety:
61 //
62 // We are iterating through our copy of the vector table, which we
63 // know is the same size as the real vector table.
64 unsafe {
65 vector_table = vector_table.add(1);
66 }
67 }
68 }
69
70 /// Dynamically register a function as being an interrupt handler
71 pub fn register_handler(&mut self, interrupt_idx: usize, interrupt_fn: extern "C" fn()) {
72 self.table[16 + interrupt_idx].handler = interrupt_fn;
73 }
74
75 /// Set the stack pointer address in a VectorTable. This will be used on Reset
76 ///
77 /// # Safety
78 /// There is no checking whether this is a valid stack pointer address
79 pub unsafe fn set_sp(&mut self, stack_pointer_address: usize) {
80 self.table[0].reserved = stack_pointer_address;
81 }
82
83 /// Set the entry-point address in a VectorTable. This will be used on Reset
84 ///
85 /// # Safety
86 /// There is no checking whether this is a valid entry point
87 pub unsafe fn set_entry(&mut self, entry_address: usize) {
88 self.table[1].reserved = entry_address;
89 }
90
91 /// Switch the current core to use this Interrupt Vector Table
92 ///
93 /// # Safety
94 /// Until the vector table has valid entries, activating it will cause an unhandled hardfault!
95 /// You must call init() first.
96 #[allow(unknown_lints)]
97 #[allow(clippy::needless_pass_by_ref_mut)]
98 pub unsafe fn activate(&mut self, ppb: &mut crate::pac::PPB) {
99 ppb.vtor()
100 .write(|w| w.bits(&mut self.table as *mut _ as *mut u32 as u32));
101 }
102}