• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2025 The Pigweed Authors
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License"); you may not
4 // use this file except in compliance with the License. You may obtain a copy of
5 // the License at
6 //
7 //     https://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11 // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12 // License for the specific language governing permissions and limitations under
13 // the License.
14 use core::arch::asm;
15 use cortex_m::peripheral::*;
16 use pw_log::info;
17 
18 use super::ArchInterface;
19 
20 mod exceptions;
21 mod regs;
22 mod spinlock;
23 mod threads;
24 mod timer;
25 
26 pub struct Arch {}
27 
28 // Demonstration of zero over head register abstraction.
29 #[inline(never)]
get_num_mpu_regions(mpu: &mut regs::Mpu) -> u830 fn get_num_mpu_regions(mpu: &mut regs::Mpu) -> u8 {
31     mpu._type.read().dregion()
32 }
33 
34 impl ArchInterface for Arch {
35     type ThreadState = threads::ArchThreadState;
36     type BareSpinLock = spinlock::BareSpinLock;
37 
early_init()38     fn early_init() {
39         info!("arch early init");
40         // TODO: set up the cpu here:
41         //  --interrupt vector table--
42         //  irq priority levels
43         //  clear pending interrupts
44         //  FPU initial state
45         //  enable cache (if present)
46         //  enable cycle counter?
47         let mut p = Peripherals::take().unwrap();
48         let mut r = regs::Regs::get();
49         let cpuid = p.CPUID.base.read();
50         info!("CPUID 0x{:x}", cpuid);
51         info!("Num MPU Regions: {}", get_num_mpu_regions(&mut r.mpu));
52 
53         unsafe {
54             // Set the VTOR (assumes it exists)
55             extern "C" {
56                 fn pw_boot_vector_table_addr();
57             }
58             let vector_table = pw_boot_vector_table_addr as *const ();
59             p.SCB.vtor.write(vector_table as u32);
60 
61             // Set the interrupt priority for SVCall and PendSV to the lowest level
62             // so that all of the external IRQs will preempt them.
63             let mut scb = p.SCB;
64             scb.set_priority(scb::SystemHandler::SVCall, 255);
65             scb.set_priority(scb::SystemHandler::PendSV, 255);
66 
67             // Set the systick priority to medium
68             scb.set_priority(scb::SystemHandler::SysTick, 128);
69 
70             // TODO: set all of the NVIC external irqs to medium as well
71 
72             // TODO: configure BASEPRI, FAULTMASK
73         } // unsafe
74 
75         timer::systick_early_init(&mut p.SYST);
76 
77         // TEST: Intentionally trigger a hard fault to make sure the VTOR is working.
78         // use core::arch::asm;
79         // unsafe {
80         //     asm!("bkpt");
81         // }
82 
83         // TEST: Intentionally trigger a pendsv
84         // use cortex_m::interrupt;
85         // SCB::set_pendsv();
86         // unsafe {
87         //     interrupt::enable();
88         // }
89     }
90 
init()91     fn init() {
92         info!("arch init");
93         timer::systick_init();
94     }
95 
enable_interrupts()96     fn enable_interrupts() {
97         unsafe {
98             cortex_m::interrupt::enable();
99         }
100     }
disable_interrupts()101     fn disable_interrupts() {
102         cortex_m::interrupt::disable();
103     }
interrupts_enabled() -> bool104     fn interrupts_enabled() -> bool {
105         // It's a complicated concept in cortex-m:
106         // If PRIMASK is inactive, then interrupts are 100% disabled otherwise
107         // if the current interrupt priority level is not zero (BASEPRI register) interrupts
108         // at that level are not allowed. For now we're treating nonzero as full disabled.
109         let primask = cortex_m::register::primask::read();
110         let basepri = cortex_m::register::basepri::read();
111         primask.is_active() && (basepri == 0)
112     }
113 
idle()114     fn idle() {
115         cortex_m::asm::wfi();
116     }
117 
panic() -> !118     fn panic() -> ! {
119         unsafe {
120             asm!("bkpt");
121         }
122         loop {}
123     }
124 }
125 
ipsr_register_read() -> u32126 fn ipsr_register_read() -> u32 {
127     let ipsr: u32;
128     // Note: cortex-m crate does not implement this register for some reason, so
129     // read and mask manually
130     unsafe {
131         asm!("mrs {ipsr}, ipsr", ipsr = out(reg) ipsr);
132     }
133     ipsr
134 }
135 
136 // Utility function to read whether or not the cpu considers itself in a handler
in_interrupt_handler() -> bool137 fn in_interrupt_handler() -> bool {
138     // IPSR[8:0] is the current exception handler (or 0 if in thread mode)
139     ipsr_register_read() & (0x1ff) != 0
140 }
141 
142 #[allow(dead_code)]
dump_int_pri()143 fn dump_int_pri() {
144     info!(
145         "basepri {} primask {}",
146         cortex_m::register::basepri::read(),
147         cortex_m::register::primask::read().is_active()
148     );
149 }
150