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}