ecos_macros/
lib.rs

1extern crate proc_macro;
2
3mod prelude;
4
5use proc_macro::TokenStream;
6use proc_macro2::TokenStream as TokenStream2;
7use quote::{ToTokens, quote};
8use std::collections::HashMap;
9use syn::{
10    ItemFn, Meta, ReturnType, Token, parse::Parser, parse_macro_input, punctuated::Punctuated,
11};
12
13use crate::prelude::generate_prelude_imports;
14
15/// 直接执行版本:
16///
17/// #[rust_main]
18/// fn app() -> ! { ... }
19#[proc_macro_attribute]
20pub fn rust_main(_attr: TokenStream, item: TokenStream) -> TokenStream {
21    let input_fn = parse_macro_input!(item as ItemFn);
22
23    if !input_fn.sig.inputs.is_empty() {
24        panic!("Function marked with #[rust_main] must have no parameters");
25    }
26
27    let docs: Vec<TokenStream2> = input_fn
28        .attrs
29        .iter()
30        .filter(|attr| attr.path().is_ident("doc"))
31        .map(|attr| {
32            let attr_tokens = attr.to_token_stream();
33            quote! { #attr_tokens }
34        })
35        .collect();
36
37    match &input_fn.sig.output {
38        syn::ReturnType::Default => panic!("Function marked with #[rust_main] must return -> !"),
39        syn::ReturnType::Type(_, ty) => {
40            if let syn::Type::Never(_) = &**ty {
41                // OK
42            } else {
43                panic!("Function marked with #[rust_main] must return -> !");
44            }
45        }
46    }
47
48    let fn_block = &input_fn.block;
49
50    let prelude = generate_prelude_imports();
51
52    let dev_debug = if cfg!(feature = "dev") {
53        quote! {
54            loop {
55                if '\n' as u8 == ecos_ssc1::Uart::read_byte_blocking() {
56                    break;
57                }
58            }
59        }
60    } else {
61        quote! {}
62    };
63
64    let init_alloc = if cfg!(feature = "alloc") {
65        quote! {
66            unsafe {
67                ::ecos_ssc1::features::alloc::init();
68            }
69        }
70    } else {
71        quote! {}
72    };
73
74    let expanded = quote! {
75        #prelude
76
77        #(#docs)*
78        #[unsafe(no_mangle)]
79        pub extern "C" fn main() -> ! {
80            #dev_debug
81
82            #init_alloc
83            #fn_block
84        }
85    };
86
87    TokenStream::from(expanded)
88}
89
90/// 默认初始化 UART
91///
92/// 默认选项:
93///     None
94///
95/// 可用选项:
96///     - no_uart
97///     - no_gpio
98///     - tick
99///     - qspi || qspi(clkdiv=0) || qspi(0)
100///     - on == 一键开启all
101///     - off == 一键关闭all == rust_main
102///
103/// #[ecos_main]
104/// #[ecos_main(no_uart)]
105/// #[ecos_main(tick)]
106/// #[ecos_main(no_uart, tick, ...)]
107#[proc_macro_attribute]
108pub fn ecos_main(attr: TokenStream, item: TokenStream) -> TokenStream {
109    let parser = Punctuated::<Meta, Token![,]>::parse_terminated;
110    let attr_args = parser.parse(attr).unwrap_or_default();
111
112    let input_fn = parse_macro_input!(item as ItemFn);
113
114    if !input_fn.sig.inputs.is_empty() {
115        panic!("Function marked with #[ecos_main] must have no parameters");
116    }
117
118    let mut qspi_clkdiv: Option<u32> = None;
119
120    for arg in &attr_args {
121        match arg {
122            Meta::Path(path) => {
123                // 简单标识符,如 qspi
124                if let Some(ident) = path.get_ident() {
125                    if ident == "qspi" {
126                        qspi_clkdiv = Some(0);
127                    }
128                }
129            }
130            Meta::List(list) => {
131                // 带括号的参数,如 qspi(clkdiv=2) 或 qspi(2)
132                if let Some(ident) = list.path.get_ident() {
133                    if ident == "qspi" {
134                        let args = parse_qspi_args(&list.tokens);
135                        qspi_clkdiv = Some(args.clkdiv);
136                    }
137                }
138            }
139            Meta::NameValue(_) => {
140                panic!("ecos_main does not support name=value syntax for qspi");
141            }
142        }
143    }
144
145    let docs: Vec<TokenStream2> = input_fn
146        .attrs
147        .iter()
148        .filter(|attr| attr.path().is_ident("doc"))
149        .map(|attr| {
150            let attr_tokens = attr.to_token_stream();
151            quote! { #attr_tokens }
152        })
153        .collect();
154
155    match &input_fn.sig.output {
156        ReturnType::Default => panic!("Function marked with #[ecos_main] must return -> !"),
157        ReturnType::Type(_, ty) => {
158            if let syn::Type::Never(_) = &**ty {
159                // OK
160            } else {
161                panic!("Function marked with #[ecos_main] must return -> !");
162            }
163        }
164    }
165
166    let fn_block = &input_fn.block;
167
168    let mut pm = PeripheralManager::new();
169
170    // 注册到off的:默认会初始化(default_enabled = true),禁用就得:no_xxx
171    pm.register("uart", true, || {
172        if !cfg!(feature = "log") {
173            quote! {
174                unsafe {
175                    ::ecos_ssc1::bindings::sys_uart_init();
176                }
177            }
178        } else {
179            quote! {}
180        }
181    });
182
183    // 注册到on的:默认不会初始化(default_enabled = false),开启就得:xxx
184    pm.register("tick", false, || {
185        if !cfg!(feature = "log") {
186            quote! {
187                unsafe {
188                    ::ecos_ssc1::bindings::sys_tick_init();
189                }
190            }
191        } else {
192            quote! {}
193        }
194    });
195
196    pm.register("qspi", false, {
197        let clkdiv = qspi_clkdiv;
198        move || {
199            if clkdiv.is_some() {
200                let clkdiv_val = clkdiv.unwrap_or(0);
201                quote! {
202                    unsafe {
203                        ::ecos_ssc1::bindings::qspi_init(::ecos_ssc1::bindings::qspi_config_t {
204                            clkdiv: #clkdiv_val
205                        });
206                    }
207                }
208            } else {
209                quote! {}
210            }
211        }
212    });
213
214    // 因为编译优化的原因,不调用一个函数对应的C就直接跳过了,导致其他函数找不到
215    pm.register("gpio", true, || {
216        quote! { unsafe { ::ecos_ssc1::bindings::gpio_config(
217            // 16位全部输出
218            &::ecos_ssc1::bindings::gpio_config_t {
219                pin_bit_mask: 0xFFFF,
220                mode: ::ecos_ssc1::bindings::gpio_mode_t_GPIO_MODE_OUTPUT,
221            }
222        ); } }
223    });
224
225    // on预设:开启所有注册到on的(默认不会初始化的)
226    pm.add_preset("on", |pm| {
227        // 有 on 标签就开启所有 default_enabled = false 的外设(注册到on的)
228        pm.enable("tick");
229        pm.enable("qspi");
230    });
231
232    // off预设:禁用所有注册到off的(默认会初始化的)
233    pm.add_preset("off", |pm| {
234        // 有 off 标签禁用所有 default_enabled = true 的外设(注册到off的)
235        pm.disable("uart");
236        pm.disable("gpio");
237    });
238
239    // ================ 处理传入的宏选项 ================
240    for arg in attr_args {
241        match arg {
242            Meta::Path(path) => {
243                if let Some(ident) = path.get_ident() {
244                    let ident_str = ident.to_string();
245                    // 跳过之前已处理的 qspi
246                    if ident_str == "qspi" {
247                        continue;
248                    }
249
250                    pm.process_option(&ident_str);
251                }
252            }
253            Meta::List(list) => {
254                // 只处理 qspi(...) 格式,其他列表格式不支持
255                if let Some(ident) = list.path.get_ident() {
256                    if ident != "qspi" {
257                        panic!(
258                            "ecos_main only supports qspi with parameters, other options must be simple identifiers"
259                        );
260                    }
261                } else {
262                    panic!(
263                        "ecos_main only supports simple identifiers as options or qspi(...) syntax"
264                    );
265                }
266            }
267            Meta::NameValue(_) => {
268                panic!("ecos_main does not support name=value syntax (except qspi(clkdiv=value))");
269            }
270        }
271    }
272
273    let init_pm = pm.generate_init_code();
274
275    let dev_debug = if cfg!(feature = "dev") {
276        quote! {
277            loop {
278                if '\n' as u8 == ecos_ssc1::Uart::read_byte_blocking() {
279                    break;
280                }
281            }
282        }
283    } else {
284        quote! {}
285    };
286
287    let init_log = if cfg!(feature = "log") {
288        quote! {
289            unsafe {
290                // 启用log由于要打印时间戳以及初始化uart,附带开启tick ...
291                ::ecos_ssc1::bindings::sys_uart_init();
292                println!("asdsadas");
293                ::ecos_ssc1::bindings::sys_tick_init();
294                ::ecos_ssc1::features::log::init_logger();
295            }
296        }
297    } else {
298        quote! {}
299    };
300
301    let init_alloc = if cfg!(feature = "alloc") {
302        quote! {
303            unsafe {
304                ::ecos_ssc1::features::alloc::init();
305            }
306        }
307    } else {
308        quote! {}
309    };
310
311    let prelude = generate_prelude_imports();
312
313    let expanded = quote! {
314        #prelude
315
316        #(#docs)*
317        #[unsafe(no_mangle)]
318        pub extern "C" fn main() -> ! {
319            #init_pm
320            #init_log
321
322            #dev_debug
323
324            #init_alloc
325            #fn_block
326        }
327    };
328
329    TokenStream::from(expanded)
330}
331
332struct PeripheralManager {
333    peripherals: HashMap<String, PeripheralConfig>,
334    presets: HashMap<String, Box<dyn Fn(&mut PeripheralManager)>>,
335}
336
337impl PeripheralManager {
338    fn new() -> Self {
339        Self {
340            peripherals: HashMap::new(),
341            presets: HashMap::new(),
342        }
343    }
344
345    /// 注册外设
346    /// - name: 外设名称
347    /// - default_enabled:
348    ///   - true: 注册到off,默认会初始化
349    ///   - false: 注册到on,默认不会初始化
350    fn register<F>(&mut self, name: &str, default_enabled: bool, init_fn: F)
351    where
352        F: Fn() -> TokenStream2 + 'static,
353    {
354        self.peripherals.insert(
355            name.to_string(),
356            PeripheralConfig {
357                enabled: default_enabled,
358                init_fn: Box::new(init_fn),
359            },
360        );
361    }
362
363    fn add_preset<F>(&mut self, name: &str, preset_fn: F)
364    where
365        F: Fn(&mut PeripheralManager) + 'static,
366    {
367        self.presets.insert(name.to_string(), Box::new(preset_fn));
368    }
369
370    fn enable(&mut self, name: &str) {
371        if let Some(config) = self.peripherals.get_mut(name) {
372            config.enabled = true;
373        }
374    }
375
376    fn disable(&mut self, name: &str) {
377        if let Some(config) = self.peripherals.get_mut(name) {
378            config.enabled = false;
379        }
380    }
381
382    fn process_option(&mut self, option: &str) {
383        match option {
384            // 预设:on - 开启所有注册到on的(开启所有的默认不会初始化的)
385            "on" => {
386                let f = |pm: &mut PeripheralManager| {
387                    pm.enable("tick");
388                };
389                f(self);
390            }
391            // 预设:off - 禁用所有注册到off的(关闭所有的默认会初始化的)
392            "off" => {
393                let f = |pm: &mut PeripheralManager| {
394                    pm.disable("uart");
395                };
396                f(self);
397            }
398            _ => {
399                // 检查是否是 no_xxx 格式(禁用默认初始化的)
400                if let Some(periph_name) = option.strip_prefix("no_") {
401                    // 禁用注册到off的外设(默认会初始化的)
402                    self.disable(periph_name);
403                } else {
404                    // 否则是开启注册到on的外设(默认不会初始化的)
405                    self.enable(option);
406                }
407            }
408        }
409    }
410
411    fn generate_init_code(&self) -> TokenStream2 {
412        let mut code = TokenStream2::new();
413
414        for (_, config) in &self.peripherals {
415            if config.enabled {
416                let init_code = (config.init_fn)();
417                code.extend(init_code);
418            }
419        }
420
421        code
422    }
423}
424
425struct PeripheralConfig {
426    enabled: bool,
427    init_fn: Box<dyn Fn() -> TokenStream2>,
428}
429
430fn parse_qspi_args(tokens: &TokenStream2) -> QspiArgs {
431    let mut args = QspiArgs { clkdiv: 0 };
432
433    if let Ok(value) = syn::parse::<syn::LitInt>(tokens.clone().into()) {
434        args.clkdiv = value.base10_parse::<u32>().unwrap_or(0);
435        return args;
436    }
437
438    let parser = Punctuated::<syn::Meta, Token![,]>::parse_terminated;
439    if let Ok(meta_list) = parser.parse(tokens.clone().into()) {
440        for meta in meta_list {
441            match meta {
442                syn::Meta::NameValue(nv) => {
443                    if let Some(ident) = nv.path.get_ident() {
444                        if ident == "clkdiv" {
445                            if let syn::Expr::Lit(expr_lit) = &nv.value {
446                                if let syn::Lit::Int(lit_int) = &expr_lit.lit {
447                                    args.clkdiv = lit_int.base10_parse::<u32>().unwrap_or(0);
448                                } else {
449                                    panic!("clkdiv must be an integer literal");
450                                }
451                            } else {
452                                panic!("clkdiv must be a literal");
453                            }
454                        } else {
455                            panic!("qspi only supports clkdiv parameter");
456                        }
457                    }
458                }
459                _ => {
460                    panic!("qspi only supports clkdiv=value syntax");
461                }
462            }
463        }
464        return args;
465    }
466
467    args
468}
469
470struct QspiArgs {
471    clkdiv: u32,
472}