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    assert!(false, "All CSRs are readable");
69});
70impl_csr_encoding!(WoUnsafe, {
71    assert!(false, "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
86/// An I/O backend for riscv64 CSRs.
87pub struct CsrIo<Encoding: ControlAndStatusRegisterEncoding>(PhantomData<Encoding>);
88
89impl<Encoding: ControlAndStatusRegisterEncoding> CsrIo<Encoding> {
90    pub const fn new() -> Self {
91        // Associated constants are evaluated lazily, so force an evaluation
92        // now.
93        let _ = Encoding::VALID;
94        Self(PhantomData)
95    }
96}
97
98impl<Encoding: ControlAndStatusRegisterEncoding> IoHandle for CsrIo<Encoding> {
99    type Base = u64;
100}
101
102#[cfg(target_arch = "riscv64")]
103mod riscv64_only {
104    use core::arch::asm;
105
106    use super::*;
107    use crate::{AtomicIoHandle, ReadHandle, Readable, Writable, WriteHandle};
108
109    impl<Encoding: ControlAndStatusRegisterEncoding> ReadHandle for CsrIo<Encoding>
110    where
111        Encoding::Access: Readable,
112    {
113        #[inline]
114        unsafe fn read_raw(&self) -> u64 {
115            let value: u64;
116            unsafe {
117                asm!(
118                    "csrr {value}, {csr}",
119                    value = out(reg) value,
120                    csr = const Encoding::VALUE,
121                    options(nomem, nostack, preserves_flags),
122                )
123            }
124            value
125        }
126    }
127
128    impl<Encoding: ControlAndStatusRegisterEncoding> WriteHandle for CsrIo<Encoding>
129    where
130        Encoding::Access: Writable,
131    {
132        #[inline]
133        unsafe fn write_raw(&self, value: u64) {
134            unsafe {
135                asm!(
136                    "csrw {csr}, {value}",
137                    value = in(reg) value,
138                    csr = const Encoding::VALUE,
139                    // TODO(https://fxbug.dev/525077555): Revisit using nomem here.
140                    options(nostack, preserves_flags),
141                )
142            }
143        }
144    }
145
146    impl<Encoding: ControlAndStatusRegisterEncoding> AtomicIoHandle for CsrIo<Encoding>
147    where
148        Encoding::Access: Readable + Writable,
149    {
150        #[inline]
151        unsafe fn atomic_swap_raw(&self, value: u64) -> u64 {
152            let previous: u64;
153            unsafe {
154                asm!(
155                    "csrrw {previous}, {csr}, {value}",
156                    previous = out(reg) previous,
157                    value = in(reg) value,
158                    csr = const Encoding::VALUE,
159                    // TODO(https://fxbug.dev/525077555): Revisit using nomem here.
160                    options(nostack, preserves_flags),
161                )
162            };
163            previous
164        }
165
166        #[inline]
167        unsafe fn atomic_set_bits_raw(&self, bits: u64) -> u64 {
168            let previous: u64;
169            unsafe {
170                asm!(
171                    "csrrs {previous}, {csr}, {bits}",
172                    previous = out(reg) previous,
173                    bits = in(reg) bits,
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_clear_bits_raw(&self, bits: u64) -> u64 {
184            let previous: u64;
185            unsafe {
186                asm!(
187                    "csrrc {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}
199
200#[cfg(all(test, target_arch = "riscv64"))]
201mod tests {
202    use super::*;
203
204    #[test]
205    fn csrs() {
206        const TIME: Csr<encoding::time, u64> = Csr::new();
207
208        println!("Current time: {:#x}", TIME.read());
209    }
210}