Skip to main content

regio_macro/
lib.rs

1// Copyright (c) 2026 Joshua Seaton
2//
3// Use of this source code is governed by a MIT-style
4// license that can be found in the LICENSE file or at
5// https://opensource.org/licenses/MIT
6
7mod riscv;
8
9use proc_macro::TokenStream;
10use proc_macro2::TokenStream as TokenStream2;
11use quote::quote;
12use syn::parse::{Error, Parse, ParseStream, Result};
13use syn::{DeriveInput, Expr, Ident, Token, parse_macro_input};
14
15#[proc_macro_attribute]
16pub fn offset(attr: TokenStream, item: TokenStream) -> TokenStream {
17    let input = parse_macro_input!(item as DeriveInput);
18    let ty = &input.ident;
19    let OffsetAttrs { offset, access } = parse_macro_input!(attr as OffsetAttrs);
20    let marker_impls = access.marker_impls(ty);
21    quote! {
22        #input
23
24        impl ::regio::Register for #ty {
25            type Base = <Self as ::core::ops::Deref>::Target;
26            type Addr = ::regio::Offset;
27        }
28
29        impl ::regio::FixedAddr for #ty {
30            const ADDR: ::regio::Offset = ::regio::Offset(#offset);
31        }
32
33        #marker_impls
34    }
35    .into()
36}
37
38#[proc_macro_attribute]
39pub fn riscv_csr(attr: TokenStream, item: TokenStream) -> TokenStream {
40    let attrs = parse_macro_input!(attr as riscv::CsrAttrs);
41    let input = parse_macro_input!(item as DeriveInput);
42    attrs.expand(input).into()
43}
44
45struct OffsetAttrs {
46    offset: Expr,
47    access: AccessMode,
48}
49
50impl Parse for OffsetAttrs {
51    fn parse(input: ParseStream) -> Result<Self> {
52        let offset: Expr = input.parse()?;
53        let access: AccessMode = if input.is_empty() {
54            AccessMode::ReadWrite
55        } else {
56            let _: Token![,] = input.parse()?;
57            input.parse()?
58        };
59        Ok(Self { offset, access })
60    }
61}
62
63enum AccessMode {
64    ReadOnly,
65    ReadWrite,
66    WriteOnly,
67}
68impl Parse for AccessMode {
69    fn parse(input: ParseStream) -> Result<Self> {
70        let ident: Ident = input.parse()?;
71        if ident == "ro" {
72            Ok(AccessMode::ReadOnly)
73        } else if ident == "rw" {
74            Ok(AccessMode::ReadWrite)
75        } else if ident == "wo" {
76            Ok(AccessMode::WriteOnly)
77        } else {
78            Err(Error::new_spanned(
79                ident,
80                "access mode must be one of `ro`, `rw`, or `wo`, or \
81                unspecified altogether for a default of `rw`",
82            ))
83        }
84    }
85}
86
87impl AccessMode {
88    pub(crate) fn marker_impls(&self, ty: &Ident) -> TokenStream2 {
89        match self {
90            AccessMode::ReadOnly => quote! {
91                impl ::regio::Readable for #ty {}
92            },
93            AccessMode::ReadWrite => quote! {
94                impl ::regio::Readable for #ty {}
95                impl ::regio::Writable for #ty {}
96            },
97            AccessMode::WriteOnly => quote! {
98                impl ::regio::Writable for #ty {}
99            },
100        }
101    }
102}