regs_macros/
spec.rs

1use proc_macro::TokenStream;
2use proc_macro2::Span;
3use proc_macro_crate::{crate_name, FoundCrate};
4use quote::{format_ident, quote};
5use syn::parse::{Parse, ParseStream, Result};
6use syn::punctuated::Punctuated;
7use syn::{braced, bracketed, Error, Ident, LitInt, Token, Type};
8
9fn get_regs_crate() -> Result<proc_macro2::TokenStream> {
10    match crate_name("regs") {
11        Ok(FoundCrate::Itself) => Ok(quote!(crate)),
12        Ok(FoundCrate::Name(name)) => {
13            let ident = Ident::new(&name, Span::call_site());
14            Ok(quote!(#ident))
15        }
16        Err(_) => Err(Error::new(Span::call_site(), "cannot find crate `regs`")),
17    }
18}
19
20struct Access {
21    r: bool,
22    w: bool,
23}
24
25impl Default for Access {
26    fn default() -> Self {
27        Self { r: true, w: true }
28    }
29}
30
31enum RegBitfieldOptions {
32    Access(Access),
33}
34
35impl Parse for RegBitfieldOptions {
36    fn parse(input: ParseStream) -> Result<Self> {
37        let ident: Ident = input.parse()?;
38        match ident.to_string().as_str() {
39            "ro" => Ok(RegBitfieldOptions::Access(Access { r: true, w: false })),
40            "rw" => Ok(RegBitfieldOptions::Access(Access { r: true, w: true })),
41            "wo" => Ok(RegBitfieldOptions::Access(Access { r: false, w: true })),
42            other => Err(Error::new(
43                ident.span(),
44                &format!("Unexpected bitfield option: {other}"),
45            )),
46        }
47    }
48}
49
50pub struct RegBitfield {
51    ident: Ident,
52    shift: LitInt,
53    width: LitInt,
54    access: Access,
55}
56
57impl Parse for RegBitfield {
58    fn parse(input: ParseStream) -> Result<Self> {
59        let ident: Ident = input.parse()?;
60        let content;
61        bracketed!(content in input);
62
63        let shift: LitInt = content.parse()?;
64        let start: u8 = shift.base10_parse()?;
65
66        let width = if content.peek(Token![..]) {
67            let _: Token![..] = content.parse()?;
68            let end: LitInt = content.parse()?;
69            end.base10_parse::<u8>()? - start + 1
70        } else if content.peek(Token![;]) {
71            let _: Token![;] = content.parse()?;
72            let width: LitInt = content.parse()?;
73            width.base10_parse::<u8>()?
74        } else {
75            1u8
76        };
77
78        let options = if content.peek(Token![,]) {
79            let _: Token![,] = content.parse()?;
80            Some(Punctuated::<RegBitfieldOptions, Token![,]>::parse_separated_nonempty(&content)?)
81        } else {
82            None
83        };
84
85        let mut access: Option<Access> = None;
86        if let Some(options) = options {
87            for opt in options {
88                match opt {
89                    RegBitfieldOptions::Access(a) => {
90                        if access.is_some() {
91                            return Err(Error::new(
92                                input.span(),
93                                "Duplicate bitfield option: access",
94                            ));
95                        }
96
97                        access.replace(a);
98                    }
99                }
100            }
101        }
102
103        let width = LitInt::new(&width.to_string(), Span::call_site());
104
105        Ok(Self {
106            ident,
107            shift,
108            width,
109            access: access.unwrap_or_default(),
110        })
111    }
112}
113
114pub enum RegSpecOption {
115    Bits(Punctuated<RegBitfield, Token![,]>),
116}
117
118impl RegSpecOption {
119    fn parse_bits(input: ParseStream) -> Result<Self> {
120        let bits;
121        braced!(bits in input);
122
123        let fields = Punctuated::<RegBitfield, Token![,]>::parse_terminated(&bits)?;
124        Ok(Self::Bits(fields))
125    }
126}
127
128impl Parse for RegSpecOption {
129    fn parse(input: ParseStream) -> Result<Self> {
130        let ident: Ident = input.parse()?;
131        match ident.to_string().as_str() {
132            "bits" => Self::parse_bits(input),
133            other => Err(Error::new(
134                ident.span(),
135                format!("Unexpected option: {other}"),
136            )),
137        }
138    }
139}
140
141pub struct RegSpec {
142    ident: Ident,
143    width: Type,
144    fields: Vec<RegBitfield>,
145    regs_crate: proc_macro2::TokenStream,
146}
147
148impl Parse for RegSpec {
149    fn parse(input: ParseStream) -> Result<Self> {
150        let ident: Ident = input.parse()?;
151        let _as: Token![as] = input.parse()?;
152        let width: Type = input.parse()?;
153
154        let mut fields: Option<Vec<RegBitfield>> = None;
155
156        // If there is more to parse, at this point we need to encounter a comma.
157        if !input.is_empty() {
158            let _: Token![,] = input.parse()?;
159
160            // If there is nothing more after the comma, we reject the input.
161            if input.is_empty() {
162                return Err(input.error("Unexpected trailing comma."));
163            }
164
165            let items = Punctuated::<RegSpecOption, Token![,]>::parse_separated_nonempty(input)?;
166            for item in items {
167                match item {
168                    RegSpecOption::Bits(punctuated) => {
169                        if fields.is_some() {
170                            return Err(Error::new(Span::call_site(), "Duplicate option: bits"));
171                        }
172
173                        let mut res = Vec::new();
174                        for f in punctuated {
175                            res.push(f);
176                        }
177                        fields.replace(res);
178                    }
179                }
180            }
181        }
182
183        Ok(Self {
184            ident,
185            width,
186            fields: fields.unwrap_or_default(),
187            regs_crate: get_regs_crate()?,
188        })
189    }
190}
191
192impl RegSpec {
193    pub fn into_token_stream(self) -> TokenStream {
194        let regs_crate = self.regs_crate;
195
196        let reg_type = format_ident!("{}_Reg", self.ident);
197        let val_type = format_ident!("{}_Val", self.ident);
198
199        let width = self.width;
200
201        let mut methods = Vec::new();
202        for f in self.fields {
203            let shift = f.shift;
204            let len = f.width;
205
206            if f.access.r {
207                let ident = Ident::new(&f.ident.to_string().to_lowercase(), f.ident.span());
208                let read = quote! {
209                    pub fn #ident(&self) -> #width {
210                        (self.0 >> #shift) & ((1 << #len) - 1)
211                    }
212                };
213
214                methods.push(read);
215            }
216
217            if f.access.w {
218                let ident = &f.ident;
219                let ident = Ident::new(
220                    &format_ident!("with_{ident}").to_string().to_lowercase(),
221                    f.ident.span(),
222                );
223
224                let write = quote! {
225                    pub fn #ident(self, value: #width) -> Self {
226                        Self(self.0 | ((value & ((1 << #len) - 1)) << #shift))
227                    }
228                };
229
230                methods.push(write);
231            }
232        }
233
234        let regspec_def = quote! {
235            #[allow(non_camel_case_types)]
236            pub struct #reg_type(usize);
237
238            impl #reg_type {
239                pub const fn address(&self) -> usize {
240                    self.0
241                }
242
243                pub unsafe fn write(&self, value: #val_type) {
244                    ::#regs_crate::write::<#width>(self.0, value.bits());
245                }
246
247                pub unsafe fn read(&self) -> #val_type {
248                    #val_type(::#regs_crate::read::<#width>(self.0))
249                }
250
251                pub unsafe fn modify<F: FnOnce(#val_type) -> #val_type>(&self, f: F) {
252                    self.write(f(self.read()));
253                }
254            }
255
256            #[allow(non_camel_case_types)]
257            pub struct #val_type(#width);
258
259            impl #val_type {
260                pub fn bits(&self) -> #width {
261                    self.0
262                }
263
264                #(#methods)*
265            }
266        };
267
268        regspec_def.into()
269    }
270}