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