cortexm/support.rs
1// Licensed under the Apache License, Version 2.0 or the MIT License.
2// SPDX-License-Identifier: Apache-2.0 OR MIT
3// Copyright Tock Contributors 2022.
4
5//! Helper functions for the Cortex-M architecture.
6
7use crate::scb;
8
9/// NOP instruction
10#[cfg(all(target_arch = "arm", target_os = "none"))]
11#[inline(always)]
12pub fn nop() {
13 use core::arch::asm;
14
15 // # Safety
16 //
17 // - INPUTS: This does not use the existing value of any registers.
18 // - OUTPUTS: This does not write any registers.
19 // - Options set:
20 // - nomem: We do not read or write memory.
21 // - nostack: This does not use the stack.
22 // - preserves_flags: This does not change flags.
23 // - Options not set:
24 // - pure: not required
25 // - readonly: implied by nomem
26 // - noreturn: we do fall-through
27 // - att_syntax: not on arm
28 // - raw: not required
29 unsafe {
30 asm!("nop", options(nomem, nostack, preserves_flags));
31 }
32}
33
34/// WFI instruction
35#[cfg(all(target_arch = "arm", target_os = "none"))]
36#[inline(always)]
37pub unsafe fn wfi() {
38 use core::arch::asm;
39
40 // # Safety
41 //
42 // - INPUTS: This does not use the existing value of any registers.
43 // - OUTPUTS: This does not write any registers.
44 // - Options set:
45 // - nomem: We do not read or write memory.
46 // - nostack: This does not use the stack.
47 // - preserves_flags: This does not change flags.
48 // - Options not set:
49 // - pure: not required
50 // - readonly: implied by nomem
51 // - noreturn: we do fall-through
52 // - att_syntax: not on arm
53 // - raw: not required
54 unsafe {
55 asm!("wfi", options(nomem, nostack, preserves_flags));
56 }
57}
58
59/// Single-core critical section operation
60#[cfg(all(target_arch = "arm", target_os = "none"))]
61pub fn with_interrupts_disabled<F, R>(f: F) -> R
62where
63 F: FnOnce() -> R,
64{
65 use core::arch::asm;
66 // Set PRIMASK to disable interrupts.
67 //
68 // # Safety
69 //
70 // - INPUTS: This does not use the existing value of any registers.
71 // - OUTPUTS: This does not write any registers.
72 // - Options set:
73 // - nomem: We do not read or write memory.
74 // - nostack: This does not use the stack.
75 // - preserves_flags: This does not change flags.
76 // - Options not set:
77 // - pure: not required
78 // - readonly: implied by nomem
79 // - noreturn: we do fall-through
80 // - att_syntax: not on arm
81 // - raw: not required
82 unsafe {
83 asm!("cpsid i", options(nomem, nostack, preserves_flags));
84 }
85
86 let res = f();
87
88 // Unset PRIMASK to re-enable interrupts.
89 //
90 // # Safety
91 //
92 // - INPUTS: This does not use the existing value of any registers.
93 // - OUTPUTS: This does not write any registers.
94 // - Options set:
95 // - nomem: We do not read or write memory.
96 // - nostack: This does not use the stack.
97 // - preserves_flags: This does not change flags.
98 // - Options not set:
99 // - pure: not required
100 // - readonly: implied by nomem
101 // - noreturn: we do fall-through
102 // - att_syntax: not on arm
103 // - raw: not required
104 unsafe {
105 asm!("cpsie i", options(nomem, nostack, preserves_flags));
106 }
107 res
108}
109
110/// NOP instruction (mock)
111// Mock implementations for tests on Travis-CI.
112#[cfg(not(all(target_arch = "arm", target_os = "none")))]
113pub fn nop() {
114 unimplemented!()
115}
116
117/// WFI instruction (mock)
118#[cfg(not(all(target_arch = "arm", target_os = "none")))]
119pub unsafe fn wfi() {
120 unimplemented!()
121}
122
123/// Single-core critical section operation (mock)
124#[cfg(not(all(target_arch = "arm", target_os = "none")))]
125pub fn with_interrupts_disabled<F, R>(_f: F) -> R
126where
127 F: FnOnce() -> R,
128{
129 unimplemented!()
130}
131
132/// Reset the chip.
133pub fn reset() -> ! {
134 unsafe {
135 scb::reset();
136 }
137 loop {
138 // This is required to avoid the empty loop clippy
139 // warning #[warn(clippy::empty_loop)]
140 nop();
141 }
142}
143
144/// Check if we are executing in an interrupt handler or not.
145///
146/// Returns `true` if the CPU is executing in an interrupt handler. Returns
147/// `false` if the chip is executing in thread mode.
148#[cfg(all(target_arch = "arm", target_os = "none"))]
149pub fn is_interrupt_context() -> bool {
150 use core::arch::asm;
151 let mut interrupt_number: u32;
152
153 // # Safety
154 //
155 // - INPUTS: This does not use the existing value of any registers.
156 // - OUTPUTS: This writes `r0` which is specified as an output.
157 // - Options set:
158 // - nomem: We do not read or write memory.
159 // - nostack: This does not use the stack.
160 // - preserves_flags: This does not change flags.
161 // - Options not set:
162 // - pure: not required
163 // - readonly: implied by nomem
164 // - noreturn: we do fall-through
165 // - att_syntax: not on arm
166 // - raw: not required
167 unsafe {
168 // IPSR[8:0] holds the currently active interrupt
169 asm!(
170 "
171 mrs r0, ipsr
172 ",
173 out("r0") interrupt_number,
174 options(nomem, nostack, preserves_flags)
175 );
176 }
177
178 // If IPSR[8:0] is 0 then we are in thread mode. Otherwise an interrupt has
179 // occurred and we are in some interrupt service routine.
180 (interrupt_number & 0x1FF) != 0
181}
182
183#[cfg(not(all(target_arch = "arm", target_os = "none")))]
184pub fn is_interrupt_context() -> bool {
185 unimplemented!()
186}