Skip to main content

oxigdal_embedded/target/
riscv.rs

1//! RISC-V specific optimizations and support
2//!
3//! Provides implementations for RISC-V processors (RV32/RV64)
4
5use super::{TargetArch, TargetCapabilities};
6use core::sync::atomic::{Ordering, fence};
7
8/// RISC-V target implementation
9pub struct RiscVTarget;
10
11impl TargetArch for RiscVTarget {
12    fn name(&self) -> &'static str {
13        if cfg!(target_arch = "riscv64") {
14            "RISC-V 64"
15        } else {
16            "RISC-V 32"
17        }
18    }
19
20    fn pointer_size(&self) -> usize {
21        core::mem::size_of::<usize>()
22    }
23
24    fn native_alignment(&self) -> usize {
25        core::mem::size_of::<usize>() // Natural alignment
26    }
27
28    fn supports_unaligned_access(&self) -> bool {
29        // RISC-V generally does not support unaligned access in base ISA
30        // Some implementations may support it via emulation
31        false
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 RISC-V target capabilities
44pub fn get_capabilities() -> TargetCapabilities {
45    TargetCapabilities {
46        has_fpu: cfg!(target_feature = "f") || cfg!(target_feature = "d"),
47        has_simd: cfg!(target_feature = "v"), // Vector extension
48        has_aes: cfg!(target_feature = "zkne") || cfg!(target_feature = "zknd"), // Crypto extension
49        has_crc: cfg!(target_feature = "zbkc"), // Bit manipulation crypto
50        cache_line_size: 64,                  // Common cache line size
51        num_cores: 1,
52    }
53}
54
55/// RISC-V memory barrier
56#[inline]
57pub fn memory_barrier() {
58    fence(Ordering::SeqCst);
59
60    #[cfg(any(target_arch = "riscv32", target_arch = "riscv64"))]
61    {
62        unsafe {
63            // Full memory fence
64            core::arch::asm!("fence rw, rw", options(nostack, nomem));
65        }
66    }
67}
68
69/// Get cycle count from RISC-V time CSR
70#[inline]
71pub fn cycle_count() -> Option<u64> {
72    #[cfg(target_arch = "riscv32")]
73    {
74        let low: u32;
75        let high1: u32;
76        let high2: u32;
77
78        unsafe {
79            // Read time CSR (handles 64-bit value on 32-bit platform)
80            loop {
81                core::arch::asm!(
82                    "rdtimeh {high}",
83                    "rdtime {low}",
84                    "rdtimeh {high2}",
85                    high = out(reg) high1,
86                    low = out(reg) low,
87                    high2 = out(reg) high2,
88                    options(nostack, nomem, preserves_flags)
89                );
90
91                // Ensure high word didn't change during read
92                if high1 == high2 {
93                    break;
94                }
95            }
96        }
97
98        Some(((high1 as u64) << 32) | (low as u64))
99    }
100
101    #[cfg(target_arch = "riscv64")]
102    {
103        let count: u64;
104        unsafe {
105            core::arch::asm!(
106                "rdtime {}",
107                out(reg) count,
108                options(nostack, nomem, preserves_flags)
109            );
110        }
111        Some(count)
112    }
113
114    #[cfg(not(any(target_arch = "riscv32", target_arch = "riscv64")))]
115    {
116        None
117    }
118}
119
120/// RISC-V atomic operations
121pub mod atomic {
122    use crate::error::{EmbeddedError, Result};
123
124    /// Atomic compare-and-swap
125    ///
126    /// # Safety
127    ///
128    /// ptr must be valid and properly aligned
129    #[cfg(any(target_arch = "riscv32", target_arch = "riscv64"))]
130    pub unsafe fn compare_and_swap(ptr: *mut usize, old: usize, new: usize) -> Result<usize> {
131        let result: usize;
132
133        #[cfg(target_arch = "riscv32")]
134        core::arch::asm!(
135            "lr.w {tmp}, ({ptr})",
136            "bne {tmp}, {old}, 1f",
137            "sc.w {result}, {new}, ({ptr})",
138            "j 2f",
139            "1:",
140            "li {result}, 1",
141            "2:",
142            ptr = in(reg) ptr,
143            old = in(reg) old,
144            new = in(reg) new,
145            tmp = out(reg) _,
146            result = out(reg) result,
147            options(nostack)
148        );
149
150        #[cfg(target_arch = "riscv64")]
151        core::arch::asm!(
152            "lr.d {tmp}, ({ptr})",
153            "bne {tmp}, {old}, 1f",
154            "sc.d {result}, {new}, ({ptr})",
155            "j 2f",
156            "1:",
157            "li {result}, 1",
158            "2:",
159            ptr = in(reg) ptr,
160            old = in(reg) old,
161            new = in(reg) new,
162            tmp = out(reg) _,
163            result = out(reg) result,
164            options(nostack)
165        );
166
167        if result == 0 {
168            Ok(new)
169        } else {
170            Err(EmbeddedError::ResourceBusy)
171        }
172    }
173
174    /// Compare and swap (stub for non-RISC-V targets)
175    ///
176    /// # Safety
177    ///
178    /// The pointer must be valid and properly aligned
179    #[cfg(not(any(target_arch = "riscv32", target_arch = "riscv64")))]
180    pub unsafe fn compare_and_swap(_ptr: *mut usize, _old: usize, _new: usize) -> Result<usize> {
181        Err(EmbeddedError::UnsupportedOperation)
182    }
183}
184
185/// RISC-V cache operations
186pub mod cache {
187    use crate::error::Result;
188
189    /// Flush instruction cache
190    ///
191    /// # Safety
192    ///
193    /// Must be called after modifying executable code
194    #[cfg(any(target_arch = "riscv32", target_arch = "riscv64"))]
195    pub unsafe fn flush_icache() -> Result<()> {
196        // FENCE.I instruction
197        core::arch::asm!("fence.i", options(nostack, nomem));
198        Ok(())
199    }
200
201    /// Flush instruction cache (stub for non-RISC-V targets)
202    ///
203    /// # Safety
204    ///
205    /// Must be called after modifying executable code
206    #[cfg(not(any(target_arch = "riscv32", target_arch = "riscv64")))]
207    pub unsafe fn flush_icache() -> Result<()> {
208        Ok(())
209    }
210
211    /// Data cache fence
212    #[cfg(any(target_arch = "riscv32", target_arch = "riscv64"))]
213    pub fn dcache_fence() {
214        unsafe {
215            core::arch::asm!("fence rw, rw", options(nostack, nomem));
216        }
217    }
218
219    /// Data cache fence (stub for non-RISC-V targets)
220    #[cfg(not(any(target_arch = "riscv32", target_arch = "riscv64")))]
221    pub fn dcache_fence() {}
222}
223
224/// RISC-V power management
225pub mod power {
226    use crate::error::Result;
227
228    /// Wait for interrupt (low power mode)
229    #[cfg(any(target_arch = "riscv32", target_arch = "riscv64"))]
230    pub fn wait_for_interrupt() -> Result<()> {
231        unsafe {
232            // WFI instruction
233            core::arch::asm!("wfi", options(nostack, nomem));
234        }
235        Ok(())
236    }
237
238    /// Wait for interrupt (stub for non-RISC-V targets)
239    #[cfg(not(any(target_arch = "riscv32", target_arch = "riscv64")))]
240    pub fn wait_for_interrupt() -> Result<()> {
241        Ok(())
242    }
243}
244
245/// RISC-V Vector extension support
246#[cfg(target_feature = "v")]
247pub mod vector {
248    /// Check if vector extension is available
249    pub fn is_available() -> bool {
250        // Check if vector length is non-zero
251        true
252    }
253
254    /// Get vector length in bytes
255    #[cfg(any(target_arch = "riscv32", target_arch = "riscv64"))]
256    pub fn vector_length() -> usize {
257        // This would need to query VLENB CSR
258        // For now, return common value
259        128 / 8 // 128-bit vectors = 16 bytes
260    }
261
262    #[cfg(not(any(target_arch = "riscv32", target_arch = "riscv64")))]
263    pub fn vector_length() -> usize {
264        0
265    }
266}
267
268#[cfg(test)]
269mod tests {
270    use super::*;
271
272    #[test]
273    fn test_riscv_target() {
274        let target = RiscVTarget;
275        assert!(target.name().contains("RISC-V"));
276        assert!(target.pointer_size() > 0);
277        assert!(target.native_alignment() > 0);
278    }
279
280    #[test]
281    fn test_capabilities() {
282        let caps = get_capabilities();
283        assert!(caps.cache_line_size > 0);
284    }
285
286    #[test]
287    fn test_memory_barrier() {
288        // Should not panic
289        memory_barrier();
290    }
291}