aclint/
lib.rs

1//! Rust support for RISC-V ACLINT (Advanced Core Local Interruptor) peripheral.
2//!
3//! RISC-V ACLINT is defined in <https://github.com/riscv/riscv-aclint>.
4#![no_std]
5#![feature(naked_functions)]
6#![deny(warnings)]
7
8use core::{arch::naked_asm, cell::UnsafeCell};
9
10/// MTIME register.
11#[repr(transparent)]
12pub struct MTIME(UnsafeCell<u64>);
13
14/// One MTIMECMP register.
15#[repr(transparent)]
16pub struct MTIMECMP(UnsafeCell<u64>);
17
18/// One MSIP register.
19#[repr(transparent)]
20pub struct MSIP(UnsafeCell<u32>);
21
22/// One SETSSIP register.
23#[repr(transparent)]
24pub struct SETSSIP(UnsafeCell<u32>);
25
26/// Machine-level Timer Device (MTIMER).
27#[repr(transparent)]
28pub struct MTIMER([MTIMECMP; 4095]);
29
30/// Machine-level Software Interrupt Device (MSWI).
31#[repr(transparent)]
32pub struct MSWI([MSIP; 4095]);
33
34/// Supervisor-level Software Interrupt Device (SSWI).
35#[repr(transparent)]
36pub struct SSWI([SETSSIP; 4095]);
37
38/// Sifive CLINT device.
39#[repr(C)]
40pub struct SifiveClint {
41    mswi: MSWI,
42    reserve: u32,
43    mtimer: MTIMER,
44    mtime: MTIME,
45}
46
47impl SifiveClint {
48    const MTIMER_OFFSET: usize = size_of::<MSWI>() + size_of::<u32>();
49    const MTIME_OFFSET: usize = Self::MTIMER_OFFSET + size_of::<MTIMER>();
50
51    #[inline]
52    pub fn read_mtime(&self) -> u64 {
53        unsafe { self.mtime.0.get().read_volatile() }
54    }
55
56    #[inline]
57    pub fn write_mtime(&self, val: u64) {
58        unsafe { self.mtime.0.get().write_volatile(val) }
59    }
60
61    #[inline]
62    pub fn read_mtimecmp(&self, hart_idx: usize) -> u64 {
63        unsafe { self.mtimer.0[hart_idx].0.get().read_volatile() }
64    }
65
66    #[inline]
67    pub fn write_mtimecmp(&self, hart_idx: usize, val: u64) {
68        unsafe { self.mtimer.0[hart_idx].0.get().write_volatile(val) }
69    }
70
71    #[inline]
72    pub fn read_msip(&self, hart_idx: usize) -> bool {
73        unsafe { self.mswi.0[hart_idx].0.get().read_volatile() != 0 }
74    }
75
76    #[inline]
77    pub fn set_msip(&self, hart_idx: usize) {
78        unsafe { self.mswi.0[hart_idx].0.get().write_volatile(1) }
79    }
80
81    #[inline]
82    pub fn clear_msip(&self, hart_idx: usize) {
83        unsafe { self.mswi.0[hart_idx].0.get().write_volatile(0) }
84    }
85}
86
87impl SifiveClint {
88    #[naked]
89    pub extern "C" fn read_mtime_naked(&self) -> u64 {
90        unsafe {
91            naked_asm!(
92                "   addi sp, sp, -8
93                    sd   a1, (sp)
94
95                    li   a1, {offset}
96                    add  a0, a0, a1
97
98                    ld   a1, (sp)
99                    addi sp, sp,  8
100
101                    ld   a0, (a0)
102                    ret
103                ",
104                offset = const Self::MTIME_OFFSET,
105            )
106        }
107    }
108
109    #[naked]
110    pub extern "C" fn write_mtime_naked(&self, val: u64) -> u64 {
111        unsafe {
112            naked_asm!(
113                "   addi sp, sp, -8
114                    sd   a1, (sp)
115
116                    li   a1, {offset}
117                    add  a0, a0, a1
118
119                    ld   a1, (sp)
120                    addi sp, sp,  8
121
122                    sd   a1, (a0)
123                    ret
124                ",
125                offset = const Self::MTIME_OFFSET,
126            )
127        }
128    }
129
130    #[naked]
131    pub extern "C" fn read_mtimecmp_naked(&self, hart_idx: usize) -> u64 {
132        unsafe {
133            naked_asm!(
134                "   slli a1, a1, 3
135                    add  a0, a0, a1
136
137                    li   a1, {offset}
138                    add  a0, a0, a1
139
140                    ld   a0, (a0)
141                    ret
142                ",
143                offset = const Self::MTIMER_OFFSET,
144            )
145        }
146    }
147
148    #[naked]
149    pub extern "C" fn write_mtimecmp_naked(&self, hart_idx: usize, val: u64) {
150        unsafe {
151            naked_asm!(
152                "   slli a1, a1, 3
153                    add  a0, a0, a1
154
155                    li   a1, {offset}
156                    add  a0, a0, a1
157
158                    sd   a2, (a0)
159                    ret
160                ",
161                offset = const Self::MTIMER_OFFSET,
162            )
163        }
164    }
165
166    #[naked]
167    pub extern "C" fn read_msip_naked(&self, hart_idx: usize) -> bool {
168        unsafe {
169            naked_asm!(
170                "   slli a1, a1, 2
171                    add  a0, a0, a1
172                    lw   a0, (a0)
173                    ret
174                ",
175            )
176        }
177    }
178
179    #[naked]
180    pub extern "C" fn set_msip_naked(&self, hart_idx: usize) {
181        unsafe {
182            naked_asm!(
183                "   slli a1, a1, 2
184                    add  a0, a0, a1
185                    addi a1, zero, 1
186                    sw   a1, (a0)
187                    ret
188                ",
189            )
190        }
191    }
192
193    #[naked]
194    pub extern "C" fn clear_msip_naked(&self, hart_idx: usize) {
195        unsafe {
196            naked_asm!(
197                "   slli a1, a1, 2
198                    add  a0, a0, a1
199                    sw   zero, (a0)
200                    ret
201                ",
202            )
203        }
204    }
205}
206
207#[cfg(test)]
208mod tests {
209    use super::*;
210    #[test]
211    fn test() {
212        assert_eq!(size_of::<MSWI>(), 0x3ffc);
213        assert_eq!(size_of::<SSWI>(), 0x3ffc);
214        assert_eq!(size_of::<MTIMER>(), 0x7ff8);
215        assert_eq!(size_of::<SifiveClint>(), 0xc000);
216    }
217}