use super::{TargetArch, TargetCapabilities};
use core::sync::atomic::{Ordering, fence};
pub struct ArmTarget;
impl TargetArch for ArmTarget {
fn name(&self) -> &'static str {
"ARM"
}
fn pointer_size(&self) -> usize {
core::mem::size_of::<usize>()
}
fn native_alignment(&self) -> usize {
4 }
fn supports_unaligned_access(&self) -> bool {
cfg!(any(
target_feature = "v7",
target_feature = "v8",
target_arch = "aarch64"
))
}
fn memory_barrier(&self) {
memory_barrier();
}
fn cycle_count(&self) -> Option<u64> {
cycle_count()
}
}
pub fn get_capabilities() -> TargetCapabilities {
TargetCapabilities {
has_fpu: cfg!(target_feature = "vfp2")
|| cfg!(target_feature = "vfp3")
|| cfg!(target_feature = "vfp4"),
has_simd: cfg!(target_feature = "neon"),
has_aes: cfg!(target_feature = "aes"),
has_crc: cfg!(target_feature = "crc"),
cache_line_size: 64, num_cores: 1, }
}
#[inline]
pub fn memory_barrier() {
fence(Ordering::SeqCst);
#[cfg(target_arch = "arm")]
{
unsafe {
core::arch::asm!("dmb", options(nostack, nomem));
}
}
#[cfg(target_arch = "aarch64")]
{
unsafe {
core::arch::asm!("dmb sy", options(nostack, nomem));
}
}
}
#[inline]
pub fn cycle_count() -> Option<u64> {
#[cfg(target_arch = "arm")]
{
let count: u32;
unsafe {
core::arch::asm!(
"mrc p15, 0, {}, c9, c13, 0",
out(reg) count,
options(nostack, nomem, preserves_flags)
);
}
Some(count as u64)
}
#[cfg(target_arch = "aarch64")]
{
let count: u64;
unsafe {
core::arch::asm!(
"mrs {}, pmccntr_el0",
out(reg) count,
options(nostack, nomem, preserves_flags)
);
}
Some(count)
}
#[cfg(not(any(target_arch = "arm", target_arch = "aarch64")))]
{
None
}
}
pub mod cache {
use crate::error::Result;
#[cfg(any(target_arch = "arm", target_arch = "aarch64"))]
pub unsafe fn clean_dcache(addr: usize, size: usize) -> Result<()> {
let cache_line_size = 64; let start = addr & !(cache_line_size - 1);
let end = (addr + size + cache_line_size - 1) & !(cache_line_size - 1);
let mut current = start;
while current < end {
#[cfg(target_arch = "arm")]
{
unsafe {
core::arch::asm!(
"mcr p15, 0, {}, c7, c10, 1",
in(reg) current,
options(nostack, preserves_flags)
);
}
}
#[cfg(target_arch = "aarch64")]
{
unsafe {
core::arch::asm!(
"dc cvac, {}",
in(reg) current,
options(nostack, preserves_flags)
);
}
}
current = current.wrapping_add(cache_line_size);
}
super::memory_barrier();
Ok(())
}
#[cfg(any(target_arch = "arm", target_arch = "aarch64"))]
pub unsafe fn invalidate_dcache(addr: usize, size: usize) -> Result<()> {
let cache_line_size = 64;
let start = addr & !(cache_line_size - 1);
let end = (addr + size + cache_line_size - 1) & !(cache_line_size - 1);
let mut current = start;
while current < end {
#[cfg(target_arch = "arm")]
{
unsafe {
core::arch::asm!(
"mcr p15, 0, {}, c7, c6, 1",
in(reg) current,
options(nostack, preserves_flags)
);
}
}
#[cfg(target_arch = "aarch64")]
{
unsafe {
core::arch::asm!(
"dc ivac, {}",
in(reg) current,
options(nostack, preserves_flags)
);
}
}
current = current.wrapping_add(cache_line_size);
}
super::memory_barrier();
Ok(())
}
#[cfg(not(any(target_arch = "arm", target_arch = "aarch64")))]
pub unsafe fn clean_dcache(_addr: usize, _size: usize) -> Result<()> {
Ok(())
}
#[cfg(not(any(target_arch = "arm", target_arch = "aarch64")))]
pub unsafe fn invalidate_dcache(_addr: usize, _size: usize) -> Result<()> {
Ok(())
}
}
#[cfg(target_feature = "neon")]
pub mod simd {
#[cfg(target_arch = "arm")]
use core::arch::arm::*;
#[cfg(target_arch = "arm")]
pub unsafe fn memcpy_neon(dst: *mut u8, src: *const u8, len: usize) {
let mut offset = 0;
let chunks = len / 16;
for _ in 0..chunks {
unsafe {
let data = vld1q_u8(src.add(offset));
vst1q_u8(dst.add(offset), data);
}
offset += 16;
}
for i in offset..len {
unsafe {
*dst.add(i) = *src.add(i);
}
}
}
#[cfg(target_arch = "aarch64")]
pub unsafe fn memcpy_neon(dst: *mut u8, src: *const u8, len: usize) {
use core::arch::aarch64::*;
let mut offset = 0;
let chunks = len / 16;
for _ in 0..chunks {
unsafe {
let data = vld1q_u8(src.add(offset));
vst1q_u8(dst.add(offset), data);
}
offset += 16;
}
for i in offset..len {
unsafe {
*dst.add(i) = *src.add(i);
}
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_arm_target() {
let target = ArmTarget;
assert_eq!(target.name(), "ARM");
assert!(target.pointer_size() > 0);
assert!(target.native_alignment() > 0);
}
#[test]
fn test_capabilities() {
let caps = get_capabilities();
assert!(caps.cache_line_size > 0);
}
}