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 !input.is_empty() {
158 let _: Token![,] = input.parse()?;
159
160 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}