Skip to main content

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}