simavr_section_macro/
lib.rs

1// This file is part of simavr-section, a Rust port of the *simavr*
2// header `avr_mcu_section.h`.
3//
4// Copyright 2021 Andrew Dona-Couch
5//
6// simavr-section is free software: you can redistribute it and/or modify
7// it under the terms of the GNU General Public License as published by
8// the Free Software Foundation, either version 3 of the License, or
9// (at your option) any later version.
10//
11// simavr-section is distributed in the hope that it will be useful,
12// but WITHOUT ANY WARRANTY; without even the implied warranty of
13// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14// GNU General Public License for more details.
15//
16// You should have received a copy of the GNU General Public License
17// along with this program.  If not, see <http://www.gnu.org/licenses/>.
18
19extern crate proc_macro;
20
21use proc_macro::TokenStream;
22use quote::quote;
23use syn::parse::{self, Parse, ParseStream};
24use syn::{parse_macro_input, Expr, Ident, LitByteStr, Token};
25
26struct StringInput {
27    tag: Ident,
28    string: LitByteStr,
29}
30
31impl Parse for StringInput {
32    fn parse(input: ParseStream) -> parse::Result<StringInput> {
33        let tag = input.parse()?;
34        input.parse::<Token![,]>()?;
35        let string = input.parse()?;
36        Ok(StringInput { tag, string })
37    }
38}
39
40/// Add an AVR metadata string value to the binary.
41///
42/// The first argument should be the tag name as defined by `simavr`, and
43/// the second argument is the byte string value.
44///
45/// # Example
46///
47/// ```
48/// # use simavr_section::avr_mcu_string;
49/// avr_mcu_string!(AVR_MMCU_TAG_VCD_FILENAME, b"tracefile.vcd");
50/// ```
51#[proc_macro]
52pub fn avr_mcu_string(input: TokenStream) -> TokenStream {
53    let StringInput { tag, string } = parse_macro_input!(input as StringInput);
54
55    let value = string
56        .value()
57        .into_iter()
58        .chain(std::iter::repeat(0))
59        .take(64)
60        .collect::<Vec<_>>();
61
62    let name = Ident::new(&format!("_{}", tag), tag.span());
63
64    let array = quote! {
65        [
66            #(
67                #value
68            ),*
69        ]
70    };
71
72    quote!(
73        #[used]
74        #[no_mangle]
75        #[link_section = ".mmcu"]
76        pub static #name: ::simavr_section::bindings::avr_mmcu_string_t = ::simavr_section::bindings::avr_mmcu_string_t {
77            tag: ::simavr_section::bindings::#tag as _,
78            len: ::core::mem::size_of::<::simavr_section::bindings::avr_mmcu_string_t>() as u8 - 2,
79            string: #array,
80        };
81    ).into()
82}
83
84struct LongInput {
85    tag: Ident,
86    val: Expr,
87}
88
89impl Parse for LongInput {
90    fn parse(input: ParseStream) -> parse::Result<LongInput> {
91        let tag = input.parse()?;
92        input.parse::<Token![,]>()?;
93        let val = input.parse()?;
94        Ok(LongInput { tag, val })
95    }
96}
97
98/// Add an AVR metadata long (`u32`) value to the binary.
99///
100/// The first argument should be the tag name as defined by `simavr`, and
101/// the second argument is the `u32` value.
102///
103/// # Example
104///
105/// ```
106/// # use simavr_section::avr_mcu_long;
107/// avr_mcu_long!(AVR_MMCU_TAG_VCC, 5000);
108/// ```
109#[proc_macro]
110pub fn avr_mcu_long(input: TokenStream) -> TokenStream {
111    let LongInput { tag, val } = parse_macro_input!(input as LongInput);
112
113    let name = Ident::new(&format!("_{}{}", tag, tag.span().start().line), tag.span());
114
115    quote!(
116        #[used]
117        #[no_mangle]
118        #[link_section = ".mmcu"]
119        pub static #name: ::simavr_section::bindings::avr_mmcu_long_t = ::simavr_section::bindings::avr_mmcu_long_t {
120            tag: ::simavr_section::bindings::#tag as _,
121            len: ::core::mem::size_of::<::simavr_section::bindings::avr_mmcu_long_t>() as u8 - 2,
122            val: #val,
123        };
124    ).into()
125}
126
127struct TraceInput {
128    tag: Ident,
129    mask: Expr,
130    what: Expr,
131    name: LitByteStr,
132}
133
134impl Parse for TraceInput {
135    fn parse(input: ParseStream) -> parse::Result<TraceInput> {
136        let tag = input.parse()?;
137        input.parse::<Token![,]>()?;
138        let mask = input.parse()?;
139        input.parse::<Token![,]>()?;
140        let what = input.parse()?;
141        input.parse::<Token![,]>()?;
142        let name = input.parse()?;
143        Ok(TraceInput {
144            tag,
145            mask,
146            what,
147            name,
148        })
149    }
150}
151
152/// Add an AVR metadata VCD trace value to the binary.
153///
154/// The first argument should be the tag name as defined by `simavr`, and
155/// the subsequent arguments are specific to the tag.
156///
157/// # Example
158///
159/// ```
160/// # use simavr_section::avr_mcu_vcd_trace;
161/// avr_mcu_vcd_trace!(AVR_MMCU_TAG_VCD_PORTPIN, b'B', 5, b"PINB5");
162/// ```
163#[proc_macro]
164pub fn avr_mcu_vcd_trace(input: TokenStream) -> TokenStream {
165    let TraceInput {
166        tag,
167        mask,
168        what,
169        name,
170    } = parse_macro_input!(input as TraceInput);
171
172    let id = Ident::new(&format!("_{}{}", tag, tag.span().start().line), tag.span());
173
174    let value = name
175        .value()
176        .into_iter()
177        .chain(std::iter::repeat(0))
178        .take(32)
179        .collect::<Vec<_>>();
180    let array = quote! {
181        [
182            #(
183                #value
184            ),*
185        ]
186    };
187
188    quote!(
189        #[used]
190        #[no_mangle]
191        #[link_section = ".mmcu"]
192        pub static #id: ::simavr_section::bindings::UnsafeVcdTrace = ::simavr_section::bindings::UnsafeVcdTrace(::simavr_section::bindings::avr_mmcu_vcd_trace_t {
193            tag: ::simavr_section::bindings::#tag as _,
194            len: ::core::mem::size_of::<::simavr_section::bindings::avr_mmcu_vcd_trace_t>() as u8 - 2,
195            mask: #mask,
196            what: #what as *mut ::simavr_section::bindings::libc::c_void,
197            name: #array,
198        });
199    ).into()
200}