avr_device/
asm.rs

1//! Assembly instructions
2
3#[cfg(target_arch = "avr")]
4use core::arch::asm;
5
6/// No Operation - Burn one CPU cycle
7///
8/// This emits exactly one NOP instruction and therefore burns at least one CPU cycle at runtime.
9///
10/// This function is an optimization fence.
11/// That means memory accesses will not be re-ordered by the compiler across this function call.
12#[inline(always)]
13pub fn nop() {
14    cfg_if::cfg_if! {
15        if #[cfg(target_arch = "avr")] {
16            unsafe {
17                asm!(
18                    "nop",
19                    options(preserves_flags)
20                )
21            }
22        } else {
23            unimplemented!()
24        }
25    }
26}
27
28/// No Operation - Burn two CPU cycles
29///
30/// This emits a minimum amount of instructions to burn at least two CPU cycles at runtime.
31///
32/// This function is an optimization fence.
33/// That means memory accesses will not be re-ordered by the compiler across this function call.
34#[inline(always)]
35pub fn nop2() {
36    cfg_if::cfg_if! {
37        if #[cfg(target_arch = "avr")] {
38            unsafe {
39                asm!(
40                    "rjmp 1f",
41                    "1:",
42                    options(preserves_flags)
43                )
44            }
45        } else {
46            unimplemented!()
47        }
48    }
49}
50
51/// No Operation - Burn three CPU cycles
52///
53/// This emits a minimum amount of instructions to burn at least three CPU cycles at runtime.
54///
55/// This function is an optimization fence.
56/// That means memory accesses will not be re-ordered by the compiler across this function call.
57#[inline(always)]
58pub fn nop3() {
59    cfg_if::cfg_if! {
60        if #[cfg(target_arch = "avr")] {
61            unsafe {
62                asm!(
63                    "rjmp 1f",
64                    "1: nop",
65                    options(preserves_flags)
66                )
67            }
68        } else {
69            unimplemented!()
70        }
71    }
72}
73
74/// No Operation - Burn four CPU cycles
75///
76/// This emits a minimum amount of instructions to burn at least four CPU cycles at runtime.
77///
78/// This function is an optimization fence.
79/// That means memory accesses will not be re-ordered by the compiler across this function call.
80#[inline(always)]
81pub fn nop4() {
82    cfg_if::cfg_if! {
83        if #[cfg(target_arch = "avr")] {
84            unsafe {
85                asm!(
86                    "rjmp 1f",
87                    "1: rjmp 2f",
88                    "2:",
89                    options(preserves_flags)
90                )
91            }
92        } else {
93            unimplemented!()
94        }
95    }
96}
97
98/// No Operation - Burn five CPU cycles
99///
100/// This emits a minimum amount of instructions to burn at least five CPU cycles at runtime.
101///
102/// This function is an optimization fence.
103/// That means memory accesses will not be re-ordered by the compiler across this function call.
104#[inline(always)]
105pub fn nop5() {
106    cfg_if::cfg_if! {
107        if #[cfg(target_arch = "avr")] {
108            unsafe {
109                asm!(
110                    "rjmp 1f",
111                    "1: rjmp 2f",
112                    "2: nop",
113                    options(preserves_flags)
114                )
115            }
116        } else {
117            unimplemented!()
118        }
119    }
120}
121
122/// Sleep / Wait For Interrupt
123///
124/// Emit a SLEEP instruction.
125///
126/// This function is an optimization fence.
127/// That means memory accesses will not be re-ordered by the compiler across this function call.
128#[inline(always)]
129pub fn sleep() {
130    cfg_if::cfg_if! {
131        if #[cfg(target_arch = "avr")] {
132            unsafe {
133                asm!(
134                    "sleep",
135                    options(preserves_flags)
136                )
137            }
138        } else {
139            unimplemented!()
140        }
141    }
142}
143
144/// Watchdog Reset
145///
146/// Emit a WDR instruction.
147///
148/// This function is an optimization fence.
149/// That means memory accesses will not be re-ordered by the compiler across this function call.
150#[inline(always)]
151pub fn wdr() {
152    cfg_if::cfg_if! {
153        if #[cfg(target_arch = "avr")] {
154            unsafe {
155                asm!(
156                    "wdr",
157                    options(preserves_flags)
158                )
159            }
160        } else {
161            unimplemented!()
162        }
163    }
164}
165
166/// Get the stack size in bytes, for debbuging.
167#[cfg(feature = "rt")]
168#[inline(always)]
169pub fn get_stack_size() -> usize {
170    extern "C" {
171        static __DATA_REGION_ORIGIN__: usize;
172        static __DATA_REGION_LENGTH__: usize;
173    }
174    
175    use core::ptr::addr_of;
176
177    cfg_if::cfg_if! {
178        if #[cfg(target_arch = "avr")] {
179            unsafe {
180                let data_region_end = addr_of!(__DATA_REGION_ORIGIN__) as usize + addr_of!(__DATA_REGION_LENGTH__) as usize;
181                let sp: usize;
182                if data_region_end > u8::MAX as usize {
183                    // Here the stack pointer is 2 bytes.
184
185                    let spl: u8;
186                    let sph: u8;
187                    core::arch::asm!(
188                        "in {}, 0x3d",
189                        "in {}, 0x3e",
190                        out(reg) spl,
191                        out(reg) sph,
192                        options(nostack, nomem, pure),
193                    );
194                    sp = usize::from_le_bytes([spl, sph]);
195                } else {
196                    // Here the stack pointer is 1 byte.
197
198                    let spl: u8;
199                    core::arch::asm!(
200                        "in {}, 0x3d",
201                        out(reg) spl,
202                        options(nostack, nomem, pure),
203                    );
204                    sp = spl as usize;
205                }
206                data_region_end - sp
207            }
208        } else {
209            unimplemented!()
210        }
211    }
212}
213
214/// Blocks the program for at least `cycles` CPU cycles.
215///
216/// This is intended for very simple delays in low-level drivers, but it
217/// has some caveats:
218///
219/// - The delay may be significantly longer if an interrupt is serviced at the
220///   same time, since the delay loop will not be executing during the interrupt.
221///   If you need precise timing, use a hardware timer peripheral instead.
222///
223/// - The real-time delay depends on the CPU clock frequency. If you want to
224///   conveniently specify a delay value in real-time units like microseconds,
225///   then use the `delay` module in the HAL crate for your platform.
226///
227/// This function is an optimization fence.
228/// That means memory accesses will not be re-ordered by the compiler across this function call.
229#[inline(always)]
230pub fn delay_cycles(cycles: u32) {
231    cfg_if::cfg_if! {
232        if #[cfg(target_arch = "avr")] {
233            let mut cycles_bytes = cycles.to_le_bytes();
234            // Each loop iteration takes 6 cycles when the branch is taken,
235            // and 5 cycles when the branch is not taken.
236            // So, this loop is guaranteed to run for at least `cycles - 1`
237            // cycles, and there will be approximately 4 cycles before the
238            // loop to initialize the counting registers.
239            unsafe {
240                asm!(
241                    "1:",
242                    "subi {r0}, 6",
243                    "sbci {r1}, 0",
244                    "sbci {r2}, 0",
245                    "sbci {r3}, 0",
246                    "brcc 1b",
247
248                    r0 = inout(reg_upper) cycles_bytes[0],
249                    r1 = inout(reg_upper) cycles_bytes[1],
250                    r2 = inout(reg_upper) cycles_bytes[2],
251                    r3 = inout(reg_upper) cycles_bytes[3],
252                )
253            }
254        } else {
255            let _ = cycles;
256            unimplemented!()
257        }
258    }
259}