Skip to main content

oxigdal_embedded/target/
arm.rs

1//! ARM-specific optimizations and support
2//!
3//! Provides implementations for ARM Cortex-M and Cortex-A processors
4
5use super::{TargetArch, TargetCapabilities};
6use core::sync::atomic::{Ordering, fence};
7
8/// ARM target implementation
9pub struct ArmTarget;
10
11impl TargetArch for ArmTarget {
12    fn name(&self) -> &'static str {
13        "ARM"
14    }
15
16    fn pointer_size(&self) -> usize {
17        core::mem::size_of::<usize>()
18    }
19
20    fn native_alignment(&self) -> usize {
21        4 // ARM typically prefers 4-byte alignment
22    }
23
24    fn supports_unaligned_access(&self) -> bool {
25        // Cortex-M3/M4/M7 support unaligned access
26        // Cortex-M0/M0+ do not
27        cfg!(any(
28            target_feature = "v7",
29            target_feature = "v8",
30            target_arch = "aarch64"
31        ))
32    }
33
34    fn memory_barrier(&self) {
35        memory_barrier();
36    }
37
38    fn cycle_count(&self) -> Option<u64> {
39        cycle_count()
40    }
41}
42
43/// Get ARM target capabilities
44pub fn get_capabilities() -> TargetCapabilities {
45    TargetCapabilities {
46        has_fpu: cfg!(target_feature = "vfp2")
47            || cfg!(target_feature = "vfp3")
48            || cfg!(target_feature = "vfp4"),
49        has_simd: cfg!(target_feature = "neon"),
50        has_aes: cfg!(target_feature = "aes"),
51        has_crc: cfg!(target_feature = "crc"),
52        cache_line_size: 64, // Common ARM cache line size
53        num_cores: 1,        // Embedded systems typically have 1 core
54    }
55}
56
57/// ARM memory barrier
58#[inline]
59pub fn memory_barrier() {
60    fence(Ordering::SeqCst);
61
62    #[cfg(target_arch = "arm")]
63    {
64        // Data Memory Barrier
65        unsafe {
66            core::arch::asm!("dmb", options(nostack, nomem));
67        }
68    }
69
70    #[cfg(target_arch = "aarch64")]
71    {
72        // Data Memory Barrier
73        unsafe {
74            core::arch::asm!("dmb sy", options(nostack, nomem));
75        }
76    }
77}
78
79/// Get cycle count from ARM performance counter
80#[inline]
81pub fn cycle_count() -> Option<u64> {
82    #[cfg(target_arch = "arm")]
83    {
84        // Read PMCCNTR (Performance Monitors Cycle Count Register)
85        // Note: This requires appropriate permissions and setup
86        let count: u32;
87        unsafe {
88            core::arch::asm!(
89                "mrc p15, 0, {}, c9, c13, 0",
90                out(reg) count,
91                options(nostack, nomem, preserves_flags)
92            );
93        }
94        Some(count as u64)
95    }
96
97    #[cfg(target_arch = "aarch64")]
98    {
99        // Read PMCCNTR_EL0 (Performance Monitors Cycle Count Register)
100        let count: u64;
101        unsafe {
102            core::arch::asm!(
103                "mrs {}, pmccntr_el0",
104                out(reg) count,
105                options(nostack, nomem, preserves_flags)
106            );
107        }
108        Some(count)
109    }
110
111    #[cfg(not(any(target_arch = "arm", target_arch = "aarch64")))]
112    {
113        None
114    }
115}
116
117/// ARM cache operations
118pub mod cache {
119    use crate::error::Result;
120
121    /// Clean data cache by address range
122    ///
123    /// # Safety
124    ///
125    /// The address range must be valid
126    #[cfg(any(target_arch = "arm", target_arch = "aarch64"))]
127    pub unsafe fn clean_dcache(addr: usize, size: usize) -> Result<()> {
128        let cache_line_size = 64; // Common ARM cache line size
129        let start = addr & !(cache_line_size - 1);
130        let end = (addr + size + cache_line_size - 1) & !(cache_line_size - 1);
131
132        let mut current = start;
133        while current < end {
134            #[cfg(target_arch = "arm")]
135            {
136                // SAFETY: Inline assembly for cache cleaning
137                unsafe {
138                    core::arch::asm!(
139                        "mcr p15, 0, {}, c7, c10, 1",
140                        in(reg) current,
141                        options(nostack, preserves_flags)
142                    );
143                }
144            }
145
146            #[cfg(target_arch = "aarch64")]
147            {
148                // SAFETY: Inline assembly for cache cleaning
149                unsafe {
150                    core::arch::asm!(
151                        "dc cvac, {}",
152                        in(reg) current,
153                        options(nostack, preserves_flags)
154                    );
155                }
156            }
157
158            current = current.wrapping_add(cache_line_size);
159        }
160
161        super::memory_barrier();
162        Ok(())
163    }
164
165    /// Invalidate data cache by address range
166    ///
167    /// # Safety
168    ///
169    /// The address range must be valid
170    #[cfg(any(target_arch = "arm", target_arch = "aarch64"))]
171    pub unsafe fn invalidate_dcache(addr: usize, size: usize) -> Result<()> {
172        let cache_line_size = 64;
173        let start = addr & !(cache_line_size - 1);
174        let end = (addr + size + cache_line_size - 1) & !(cache_line_size - 1);
175
176        let mut current = start;
177        while current < end {
178            #[cfg(target_arch = "arm")]
179            {
180                // SAFETY: Inline assembly for cache invalidation
181                unsafe {
182                    core::arch::asm!(
183                        "mcr p15, 0, {}, c7, c6, 1",
184                        in(reg) current,
185                        options(nostack, preserves_flags)
186                    );
187                }
188            }
189
190            #[cfg(target_arch = "aarch64")]
191            {
192                // SAFETY: Inline assembly for cache invalidation
193                unsafe {
194                    core::arch::asm!(
195                        "dc ivac, {}",
196                        in(reg) current,
197                        options(nostack, preserves_flags)
198                    );
199                }
200            }
201
202            current = current.wrapping_add(cache_line_size);
203        }
204
205        super::memory_barrier();
206        Ok(())
207    }
208
209    /// Clean data cache for the specified address range (no-op on non-ARM)
210    #[cfg(not(any(target_arch = "arm", target_arch = "aarch64")))]
211    pub unsafe fn clean_dcache(_addr: usize, _size: usize) -> Result<()> {
212        Ok(())
213    }
214
215    /// Invalidate data cache for the specified address range (no-op on non-ARM)
216    #[cfg(not(any(target_arch = "arm", target_arch = "aarch64")))]
217    pub unsafe fn invalidate_dcache(_addr: usize, _size: usize) -> Result<()> {
218        Ok(())
219    }
220}
221
222/// ARM SIMD operations (NEON)
223#[cfg(target_feature = "neon")]
224pub mod simd {
225    #[cfg(target_arch = "arm")]
226    use core::arch::arm::*;
227
228    /// Copy memory using NEON instructions
229    ///
230    /// # Safety
231    ///
232    /// src and dst must be valid and properly aligned
233    #[cfg(target_arch = "arm")]
234    pub unsafe fn memcpy_neon(dst: *mut u8, src: *const u8, len: usize) {
235        let mut offset = 0;
236        let chunks = len / 16;
237
238        for _ in 0..chunks {
239            // SAFETY: Caller guarantees dst and src are valid and aligned
240            unsafe {
241                let data = vld1q_u8(src.add(offset));
242                vst1q_u8(dst.add(offset), data);
243            }
244            offset += 16;
245        }
246
247        // Handle remaining bytes
248        for i in offset..len {
249            // SAFETY: Caller guarantees dst and src are valid
250            unsafe {
251                *dst.add(i) = *src.add(i);
252            }
253        }
254    }
255
256    /// Copy memory using NEON instructions (AArch64)
257    ///
258    /// # Safety
259    ///
260    /// src and dst must be valid and properly aligned
261    #[cfg(target_arch = "aarch64")]
262    pub unsafe fn memcpy_neon(dst: *mut u8, src: *const u8, len: usize) {
263        use core::arch::aarch64::*;
264
265        let mut offset = 0;
266        let chunks = len / 16;
267
268        for _ in 0..chunks {
269            // SAFETY: Caller guarantees dst and src are valid and aligned
270            unsafe {
271                let data = vld1q_u8(src.add(offset));
272                vst1q_u8(dst.add(offset), data);
273            }
274            offset += 16;
275        }
276
277        // Handle remaining bytes
278        for i in offset..len {
279            // SAFETY: Caller guarantees dst and src are valid
280            unsafe {
281                *dst.add(i) = *src.add(i);
282            }
283        }
284    }
285}
286
287#[cfg(test)]
288mod tests {
289    use super::*;
290
291    #[test]
292    fn test_arm_target() {
293        let target = ArmTarget;
294        assert_eq!(target.name(), "ARM");
295        assert!(target.pointer_size() > 0);
296        assert!(target.native_alignment() > 0);
297    }
298
299    #[test]
300    fn test_capabilities() {
301        let caps = get_capabilities();
302        assert!(caps.cache_line_size > 0);
303    }
304}