Skip to main content

regio/riscv64/
mod.rs

1// Copyright 2026 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5pub mod encoding;
6
7use core::marker::PhantomData;
8
9use crate::{Accessible, IoHandle, LayoutOver, Register, Ro, RwSafe, RwUnsafe, WoSafe, WoUnsafe};
10
11/// Models a riscv64 CSR with a given layout.
12///
13/// It takes a CSR encoding as a generic parameter, all of which have been
14/// stamped out in the `regio::riscv64::encoding` submodule with names equal to
15/// their official mnemonics.
16///
17/// Example usage:
18/// ```rust
19/// use regio::riscv64::{Csr, encoding};
20///
21/// const TIME: Csr<encoding::time, u64> = Csr::new();
22///
23/// println!("TIME: {:#x}", TIME.read());
24/// ```
25pub type Csr<Encoding, Layout> =
26    Register<Layout, <Encoding as ControlAndStatusRegisterEncoding>::Access, CsrIo<Encoding>>;
27
28/// Marker for a riscv64 CSR, expressing its encoding and access permissions.
29pub trait ControlAndStatusRegisterEncoding {
30    /// The register's 12-bit encoding value.
31    const VALUE: u16;
32
33    /// The register's access permissions.
34    type Access: Accessible;
35
36    /// Whether the encoding is valid.
37    const VALID: ();
38}
39
40/// Tag for a riscv64 CSR, expressing its encoding and access permissions.
41pub struct CsrEncoding<const ENCODING: u16, Access: Accessible>(PhantomData<Access>);
42
43macro_rules! impl_csr_encoding {
44    ($access:ty, $validate:block) => {
45        impl<const ENCODING: u16> ControlAndStatusRegisterEncoding
46            for CsrEncoding<ENCODING, $access>
47        {
48            const VALUE: u16 = ENCODING;
49            type Access = $access;
50
51            const VALID: () = {
52                assert!(Self::VALUE <= 0xfff, "CSR encoding must be 12-bit");
53                $validate
54            };
55        }
56    };
57}
58
59impl_csr_encoding!(Ro, {
60    assert!(
61        (ENCODING >> 10) & 0b11_u16 == 0b11_u16,
62        "Associated access type is read-only but the encoding is not"
63    );
64});
65impl_csr_encoding!(RwSafe, {});
66impl_csr_encoding!(RwUnsafe, {});
67impl_csr_encoding!(WoSafe, {
68    panic!("All CSRs are readable");
69});
70impl_csr_encoding!(WoUnsafe, {
71    panic!("All CSRs are readable");
72});
73
74impl<Encoding, Layout> Csr<Encoding, Layout>
75where
76    Encoding: ControlAndStatusRegisterEncoding,
77    Layout: LayoutOver<u64>,
78{
79    /// Constructs a new CSR instance.
80    pub const fn new() -> Self {
81        // Safety: There is nothing unsound about CsrIo construction.
82        unsafe { Self::from_io(CsrIo::new()) }
83    }
84}
85
86impl<Encoding, Layout> Default for Csr<Encoding, Layout>
87where
88    Encoding: ControlAndStatusRegisterEncoding,
89    Layout: LayoutOver<u64>,
90{
91    fn default() -> Self {
92        Self::new()
93    }
94}
95
96/// An I/O backend for riscv64 CSRs.
97pub struct CsrIo<Encoding: ControlAndStatusRegisterEncoding>(PhantomData<Encoding>);
98
99impl<Encoding: ControlAndStatusRegisterEncoding> CsrIo<Encoding> {
100    pub const fn new() -> Self {
101        // Associated constants are evaluated lazily, so force an evaluation
102        // now.
103        const { Encoding::VALID };
104        Self(PhantomData)
105    }
106}
107
108impl<Encoding: ControlAndStatusRegisterEncoding> Default for CsrIo<Encoding> {
109    fn default() -> Self {
110        Self::new()
111    }
112}
113
114impl<Encoding: ControlAndStatusRegisterEncoding> IoHandle for CsrIo<Encoding> {
115    type Base = u64;
116}
117
118#[cfg(target_arch = "riscv64")]
119mod riscv64_only {
120    use core::arch::asm;
121
122    use super::*;
123    use crate::{AtomicIoHandle, ReadHandle, Readable, Writable, WriteHandle};
124
125    impl<Encoding: ControlAndStatusRegisterEncoding> ReadHandle for CsrIo<Encoding>
126    where
127        Encoding::Access: Readable,
128    {
129        #[inline]
130        unsafe fn read_raw(&self) -> u64 {
131            let value: u64;
132            unsafe {
133                asm!(
134                    "csrr {value}, {csr}",
135                    value = out(reg) value,
136                    csr = const Encoding::VALUE,
137                    options(nomem, nostack, preserves_flags),
138                )
139            }
140            value
141        }
142    }
143
144    impl<Encoding: ControlAndStatusRegisterEncoding> WriteHandle for CsrIo<Encoding>
145    where
146        Encoding::Access: Writable,
147    {
148        #[inline]
149        unsafe fn write_raw(&self, value: u64) {
150            unsafe {
151                asm!(
152                    "csrw {csr}, {value}",
153                    value = in(reg) value,
154                    csr = const Encoding::VALUE,
155                    // TODO(https://fxbug.dev/525077555): Revisit using nomem here.
156                    options(nostack, preserves_flags),
157                )
158            }
159        }
160    }
161
162    impl<Encoding: ControlAndStatusRegisterEncoding> AtomicIoHandle for CsrIo<Encoding>
163    where
164        Encoding::Access: Readable + Writable,
165    {
166        #[inline]
167        unsafe fn atomic_swap_raw(&self, value: u64) -> u64 {
168            let previous: u64;
169            unsafe {
170                asm!(
171                    "csrrw {previous}, {csr}, {value}",
172                    previous = out(reg) previous,
173                    value = in(reg) value,
174                    csr = const Encoding::VALUE,
175                    // TODO(https://fxbug.dev/525077555): Revisit using nomem here.
176                    options(nostack, preserves_flags),
177                )
178            };
179            previous
180        }
181
182        #[inline]
183        unsafe fn atomic_set_bits_raw(&self, bits: u64) -> u64 {
184            let previous: u64;
185            unsafe {
186                asm!(
187                    "csrrs {previous}, {csr}, {bits}",
188                    previous = out(reg) previous,
189                    bits = in(reg) bits,
190                    csr = const Encoding::VALUE,
191                    // TODO(https://fxbug.dev/525077555): Revisit using nomem here.
192                    options(nostack, preserves_flags),
193                )
194            };
195            previous
196        }
197
198        #[inline]
199        unsafe fn atomic_clear_bits_raw(&self, bits: u64) -> u64 {
200            let previous: u64;
201            unsafe {
202                asm!(
203                    "csrrc {previous}, {csr}, {bits}",
204                    previous = out(reg) previous,
205                    bits = in(reg) bits,
206                    csr = const Encoding::VALUE,
207                    // TODO(https://fxbug.dev/525077555): Revisit using nomem here.
208                    options(nostack, preserves_flags),
209                )
210            };
211            previous
212        }
213    }
214}
215
216#[cfg(all(test, target_arch = "riscv64"))]
217mod tests {
218    use super::*;
219
220    #[test]
221    fn csrs() {
222        const TIME: Csr<encoding::time, u64> = Csr::new();
223
224        println!("Current time: {:#x}", TIME.read());
225    }
226}