From c6af8ba88bccb36b7933d02bf75d212c99af4316 Mon Sep 17 00:00:00 2001 From: Jesse Braham Date: Thu, 12 Oct 2023 06:58:37 -0700 Subject: [PATCH] Assimilate `lp-hal-procmacros` into `esp-hal-procmacros` (#845) * Assimilate `lp-hal-procmacros` into `esp-hal-procmacros` * Update `CHANGELOG.md` --- CHANGELOG.md | 4 +- esp-hal-common/Cargo.toml | 2 +- esp-hal-procmacros/Cargo.toml | 51 +-- esp-hal-procmacros/src/enum_dispatch.rs | 56 +++ esp-hal-procmacros/src/interrupt.rs | 62 ++++ esp-hal-procmacros/src/lib.rs | 449 ++++++++++++------------ esp-hal-procmacros/src/lp_core.rs | 71 ++++ esp32c6-lp-hal/Cargo.toml | 2 +- esp32c6-lp-hal/src/prelude.rs | 2 +- lp-hal-procmacros/Cargo.toml | 36 -- lp-hal-procmacros/src/lib.rs | 175 --------- ulp-riscv-hal/Cargo.toml | 10 +- ulp-riscv-hal/src/prelude.rs | 2 +- 13 files changed, 460 insertions(+), 462 deletions(-) create mode 100644 esp-hal-procmacros/src/enum_dispatch.rs create mode 100644 esp-hal-procmacros/src/interrupt.rs create mode 100644 esp-hal-procmacros/src/lp_core.rs delete mode 100644 lp-hal-procmacros/Cargo.toml delete mode 100644 lp-hal-procmacros/src/lib.rs diff --git a/CHANGELOG.md b/CHANGELOG.md index 6270e6ddd..1711a3072 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -25,9 +25,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - SYSTIMER ETM functionality (#828) - Adding async support for RSA peripheral(doesn't work properly for `esp32` chip - issue will be created)(#790) - Added sleep support for ESP32-C3 with timer and GPIO wakeups (#795) -- Support for ULP-RISCV including Delay and GPIO (#840) +- Support for ULP-RISCV including Delay and GPIO (#840, #845) - Add bare-bones SPI slave support, DMA only (#580, #843) -- Embassy `#[main]` convenience macro +- Embassy `#[main]` convenience macro (#841) ### Changed diff --git a/esp-hal-common/Cargo.toml b/esp-hal-common/Cargo.toml index fa0a95820..ae22eb40f 100644 --- a/esp-hal-common/Cargo.toml +++ b/esp-hal-common/Cargo.toml @@ -28,7 +28,7 @@ fugit = "0.3.7" log = { version = "0.4.20", optional = true } nb = "1.1.0" paste = "1.0.14" -procmacros = { version = "0.6.1", package = "esp-hal-procmacros", path = "../esp-hal-procmacros" } +procmacros = { version = "0.6.1", features = ["enum-dispatch", "ram"], package = "esp-hal-procmacros", path = "../esp-hal-procmacros" } strum = { version = "0.25.0", default-features = false, features = ["derive"] } void = { version = "1.0.2", default-features = false } usb-device = { version = "0.2.9", optional = true } diff --git a/esp-hal-procmacros/Cargo.toml b/esp-hal-procmacros/Cargo.toml index 1319dbf32..8f7fab7a1 100644 --- a/esp-hal-procmacros/Cargo.toml +++ b/esp-hal-procmacros/Cargo.toml @@ -1,10 +1,6 @@ [package] -name = "esp-hal-procmacros" -version = "0.6.1" -authors = [ - "Jesse Braham ", - "Björn Quentin ", -] +name = "esp-hal-procmacros" +version = "0.6.1" edition = "2021" rust-version = "1.67.0" description = "Procedural macros for ESP-HAL" @@ -12,32 +8,37 @@ repository = "https://github.com/esp-rs/esp-hal" license = "MIT OR Apache-2.0" [package.metadata.docs.rs] -features = ["esp32c3", "interrupt"] +features = ["esp32c3", "interrupt", "ram"] [lib] proc-macro = true [dependencies] darling = "0.20.3" -proc-macro-crate = "1.3.1" -proc-macro-error = "1.0.4" -proc-macro2 = "1.0.66" -quote = "1.0.33" -syn = {version = "2.0.31", features = ["extra-traits", "full"]} -object = {version = "0.32.1", optional = true} litrs = "0.4.0" -# toml_edit is a dependency of proc-macro-crate. Unfortunately they raised their MSRV on 0.19.15 so we pin the version for now -toml_edit = "=0.19.14" +object = { version = "0.32.1", optional = true } +proc-macro-crate = "2.0.0" +proc-macro-error = "1.0.4" +proc-macro2 = "1.0.69" +quote = "1.0.33" +syn = { version = "2.0.38", features = ["extra-traits", "full"] } [features] -esp32 = [] -esp32c2 = [] -esp32c3 = [] -esp32c6 = ["dep:object"] -esp32h2 = [] -esp32s2 = ["dep:object"] -esp32s3 = ["dep:object"] +# Select a target device: +esp32 = [] +esp32c2 = [] +esp32c3 = [] +esp32c6 = ["dep:object"] +esp32c6-lp = [] +esp32h2 = [] +esp32s2 = ["dep:object"] +esp32s2-ulp = [] +esp32s3 = ["dep:object"] +esp32s3-ulp = [] -interrupt = [] -rtc_slow = [] -embassy = [] +# Gated features: +embassy = [] +enum-dispatch = [] +interrupt = [] +ram = [] +rtc_slow = [] diff --git a/esp-hal-procmacros/src/enum_dispatch.rs b/esp-hal-procmacros/src/enum_dispatch.rs new file mode 100644 index 000000000..94875f358 --- /dev/null +++ b/esp-hal-procmacros/src/enum_dispatch.rs @@ -0,0 +1,56 @@ +use proc_macro2::{Group, TokenTree}; +use syn::{ + parse::{Parse, ParseStream, Result}, + Ident, +}; + +#[derive(Debug)] +pub(crate) struct MakeGpioEnumDispatchMacro { + pub name: String, + pub filter: Vec, + pub elements: Vec<(String, usize)>, +} + +impl Parse for MakeGpioEnumDispatchMacro { + fn parse(input: ParseStream) -> Result { + let name = input.parse::()?.to_string(); + let filter = input + .parse::()? + .stream() + .into_iter() + .map(|v| match v { + TokenTree::Group(_) => String::new(), + TokenTree::Ident(ident) => ident.to_string(), + TokenTree::Punct(_) => String::new(), + TokenTree::Literal(_) => String::new(), + }) + .filter(|p| !p.is_empty()) + .collect(); + + let mut elements = vec![]; + + let mut stream = input.parse::()?.stream().into_iter(); + let mut element_name = String::new(); + loop { + match stream.next() { + Some(v) => match v { + TokenTree::Ident(ident) => { + element_name = ident.to_string(); + } + TokenTree::Literal(lit) => { + let index = lit.to_string().parse().unwrap(); + elements.push((element_name.clone(), index)); + } + _ => (), + }, + None => break, + } + } + + Ok(MakeGpioEnumDispatchMacro { + name, + filter, + elements, + }) + } +} diff --git a/esp-hal-procmacros/src/interrupt.rs b/esp-hal-procmacros/src/interrupt.rs new file mode 100644 index 000000000..0b4f47fba --- /dev/null +++ b/esp-hal-procmacros/src/interrupt.rs @@ -0,0 +1,62 @@ +use proc_macro::{self, TokenStream}; +use syn::{parse::Error, spanned::Spanned, AttrStyle, Attribute}; + +pub(crate) enum WhiteListCaller { + Interrupt, +} + +pub(crate) fn check_attr_whitelist( + attrs: &[Attribute], + caller: WhiteListCaller, +) -> Result<(), TokenStream> { + let whitelist = &[ + "doc", + "link_section", + "cfg", + "allow", + "warn", + "deny", + "forbid", + "cold", + "ram", + "inline", + ]; + + 'o: for attr in attrs { + for val in whitelist { + if eq(&attr, &val) { + continue 'o; + } + } + + let err_str = match caller { + WhiteListCaller::Interrupt => { + "this attribute is not allowed on an interrupt handler controlled by esp-hal" + } + }; + + return Err(Error::new(attr.span(), &err_str).to_compile_error().into()); + } + + Ok(()) +} + +pub(crate) fn extract_cfgs(attrs: Vec) -> (Vec, Vec) { + let mut cfgs = vec![]; + let mut not_cfgs = vec![]; + + for attr in attrs { + if eq(&attr, "cfg") { + cfgs.push(attr); + } else { + not_cfgs.push(attr); + } + } + + (cfgs, not_cfgs) +} + +/// Returns `true` if `attr.path` matches `name` +fn eq(attr: &Attribute, name: &str) -> bool { + attr.style == AttrStyle::Outer && attr.path().is_ident(name) +} diff --git a/esp-hal-procmacros/src/lib.rs b/esp-hal-procmacros/src/lib.rs index 10427a80b..3b6cf14bb 100644 --- a/esp-hal-procmacros/src/lib.rs +++ b/esp-hal-procmacros/src/lib.rs @@ -47,34 +47,92 @@ #![doc(html_logo_url = "https://avatars.githubusercontent.com/u/46717278")] -use darling::{ast::NestedMeta, FromMeta}; -use proc_macro::{self, Span, TokenStream}; -use proc_macro2::Ident; -use proc_macro_error::{abort, proc_macro_error}; +#[cfg(feature = "ram")] +use darling::{ast::NestedMeta, Error as DarlingError, FromMeta}; +use proc_macro::TokenStream; +use proc_macro_crate::FoundCrate; +use proc_macro_error::proc_macro_error; use quote::quote; -#[cfg(feature = "interrupt")] -use syn::{ - parse, - spanned::Spanned, - AttrStyle, - Attribute, - ItemFn, - Meta::Path, - ReturnType, - Type, - Visibility, -}; -use syn::{ - parse::{Parse, ParseStream}, - parse_macro_input, -}; +use syn::parse_macro_input; #[cfg(all( feature = "embassy", any(feature = "esp32", feature = "esp32s2", feature = "esp32s3") ))] mod embassy_xtensa; +#[cfg(feature = "enum-dispatch")] +mod enum_dispatch; +#[cfg(feature = "interrupt")] +mod interrupt; +#[cfg(any( + feature = "esp32c6-lp", + feature = "esp32s2-ulp", + feature = "esp32s3-ulp" +))] +mod lp_core; +#[cfg(any( + feature = "esp32c6", + feature = "esp32s2", + feature = "esp32s3", + feature = "interrupt" +))] +fn get_hal_crate() -> ( + Result, + proc_macro2::Ident, +) { + use proc_macro::Span; + use proc_macro2::Ident; + use proc_macro_crate::crate_name; + + // Package name: + #[cfg(feature = "esp32")] + let hal_crate = crate_name("esp32-hal"); + #[cfg(feature = "esp32c2")] + let hal_crate = crate_name("esp32c2-hal"); + #[cfg(feature = "esp32c3")] + let hal_crate = crate_name("esp32c3-hal"); + #[cfg(feature = "esp32c6")] + let hal_crate = crate_name("esp32c6-hal"); + #[cfg(feature = "esp32c6-lp")] + let hal_crate = crate_name("esp32c6-lp-hal"); + #[cfg(feature = "esp32h2")] + let hal_crate = crate_name("esp32h2-hal"); + #[cfg(feature = "esp32s2")] + let hal_crate = crate_name("esp32s2-hal"); + #[cfg(feature = "esp32s2-ulp")] + let hal_crate = crate_name("ulp-riscv-hal"); + #[cfg(feature = "esp32s3")] + let hal_crate = crate_name("esp32s3-hal"); + #[cfg(feature = "esp32s3-ulp")] + let hal_crate = crate_name("ulp-riscv-hal"); + + // Crate name: + #[cfg(feature = "esp32")] + let hal_crate_name = Ident::new("esp32_hal", Span::call_site().into()); + #[cfg(feature = "esp32c2")] + let hal_crate_name = Ident::new("esp32c2_hal", Span::call_site().into()); + #[cfg(feature = "esp32c3")] + let hal_crate_name = Ident::new("esp32c3_hal", Span::call_site().into()); + #[cfg(feature = "esp32c6")] + let hal_crate_name = Ident::new("esp32c6_hal", Span::call_site().into()); + #[cfg(feature = "esp32c6-lp")] + let hal_crate_name = Ident::new("esp32c6_lp_hal", Span::call_site().into()); + #[cfg(feature = "esp32h2")] + let hal_crate_name = Ident::new("esp32h2_hal", Span::call_site().into()); + #[cfg(feature = "esp32s2")] + let hal_crate_name = Ident::new("esp32s2_hal", Span::call_site().into()); + #[cfg(feature = "esp32s2-ulp")] + let hal_crate_name = Ident::new("ulp_riscv_hal", Span::call_site().into()); + #[cfg(feature = "esp32s3")] + let hal_crate_name = Ident::new("esp32s3_hal", Span::call_site().into()); + #[cfg(feature = "esp32s3-ulp")] + let hal_crate_name = Ident::new("ulp_riscv_hal", Span::call_site().into()); + + (hal_crate, hal_crate_name) +} + +#[cfg(feature = "ram")] #[derive(Debug, Default, FromMeta)] #[darling(default)] struct RamArgs { @@ -84,45 +142,6 @@ struct RamArgs { zeroed: bool, } -fn get_hal_crate() -> ( - Result, - proc_macro2::Ident, -) { - use proc_macro_crate::crate_name; - - #[cfg(feature = "esp32")] - let hal_crate = crate_name("esp32-hal"); - #[cfg(feature = "esp32s2")] - let hal_crate = crate_name("esp32s2-hal"); - #[cfg(feature = "esp32s3")] - let hal_crate = crate_name("esp32s3-hal"); - #[cfg(feature = "esp32c2")] - let hal_crate = crate_name("esp32c2-hal"); - #[cfg(feature = "esp32c3")] - let hal_crate = crate_name("esp32c3-hal"); - #[cfg(feature = "esp32c6")] - let hal_crate = crate_name("esp32c6-hal"); - #[cfg(feature = "esp32h2")] - let hal_crate = crate_name("esp32h2-hal"); - - #[cfg(feature = "esp32")] - let hal_crate_name = Ident::new("esp32_hal", Span::call_site().into()); - #[cfg(feature = "esp32s2")] - let hal_crate_name = Ident::new("esp32s2_hal", Span::call_site().into()); - #[cfg(feature = "esp32s3")] - let hal_crate_name = Ident::new("esp32s3_hal", Span::call_site().into()); - #[cfg(feature = "esp32c2")] - let hal_crate_name = Ident::new("esp32c2_hal", Span::call_site().into()); - #[cfg(feature = "esp32c3")] - let hal_crate_name = Ident::new("esp32c3_hal", Span::call_site().into()); - #[cfg(feature = "esp32c6")] - let hal_crate_name = Ident::new("esp32c6_hal", Span::call_site().into()); - #[cfg(feature = "esp32h2")] - let hal_crate_name = Ident::new("esp32h2_hal", Span::call_site().into()); - - (hal_crate, hal_crate_name) -} - /// This attribute allows placing statics and functions into ram. /// /// Options that can be specified are rtc_slow or rtc_fast to use the @@ -132,13 +151,17 @@ fn get_hal_crate() -> ( /// (e.g. to persist it across resets or deep sleep mode for the RTC RAM) /// /// Not all targets support RTC slow ram. +#[cfg(feature = "ram")] #[proc_macro_attribute] #[proc_macro_error] pub fn ram(args: TokenStream, input: TokenStream) -> TokenStream { + use proc_macro::Span; + use proc_macro_error::abort; + let attr_args = match NestedMeta::parse_meta_list(args.into()) { Ok(v) => v, Err(e) => { - return TokenStream::from(darling::Error::from(e).write_errors()); + return TokenStream::from(DarlingError::from(e).write_errors()); } }; @@ -200,6 +223,7 @@ pub fn ram(args: TokenStream, input: TokenStream) -> TokenStream { #section #item }; + output.into() } @@ -231,7 +255,22 @@ pub fn ram(args: TokenStream, input: TokenStream) -> TokenStream { #[cfg(feature = "interrupt")] #[proc_macro_attribute] pub fn interrupt(args: TokenStream, input: TokenStream) -> TokenStream { - use proc_macro_crate::FoundCrate; + use std::iter; + + use proc_macro::Span; + use proc_macro2::Ident; + use proc_macro_error::abort; + use syn::{ + parse::Error as ParseError, + spanned::Spanned, + ItemFn, + Meta, + ReturnType, + Type, + Visibility, + }; + + use self::interrupt::{check_attr_whitelist, extract_cfgs, WhiteListCaller}; let mut f: ItemFn = syn::parse(input).expect("`#[interrupt]` must be applied to a function"); @@ -254,7 +293,7 @@ pub fn interrupt(args: TokenStream, input: TokenStream) -> TokenStream { if attr_args.len() == 1 { match &attr_args[0] { - NestedMeta::Meta(Path(x)) => { + NestedMeta::Meta(Meta::Path(x)) => { ident_s = x.get_ident().unwrap(); } _ => { @@ -292,7 +331,7 @@ pub fn interrupt(args: TokenStream, input: TokenStream) -> TokenStream { && f.sig.inputs.len() <= 1; if !valid_signature { - return parse::Error::new( + return ParseError::new( f.span(), "`#[interrupt]` handlers must have signature `[unsafe] fn([&mut Context]) [-> !]`", ) @@ -320,7 +359,7 @@ pub fn interrupt(args: TokenStream, input: TokenStream) -> TokenStream { } }; - f.block.stmts.extend(std::iter::once( + f.block.stmts.extend(iter::once( syn::parse2(quote! {{ // Check that this interrupt actually exists #interrupt_in_hal_crate; @@ -371,133 +410,22 @@ pub fn interrupt(args: TokenStream, input: TokenStream) -> TokenStream { .into() } -#[cfg(feature = "interrupt")] -enum WhiteListCaller { - Interrupt, -} - -#[cfg(feature = "interrupt")] -fn check_attr_whitelist(attrs: &[Attribute], caller: WhiteListCaller) -> Result<(), TokenStream> { - let whitelist = &[ - "doc", - "link_section", - "cfg", - "allow", - "warn", - "deny", - "forbid", - "cold", - "ram", - "inline", - ]; - - 'o: for attr in attrs { - for val in whitelist { - if eq(&attr, &val) { - continue 'o; - } - } - - let err_str = match caller { - WhiteListCaller::Interrupt => { - "this attribute is not allowed on an interrupt handler controlled by esp-hal" - } - }; - - return Err(parse::Error::new(attr.span(), &err_str) - .to_compile_error() - .into()); - } - - Ok(()) -} - -/// Returns `true` if `attr.path` matches `name` -#[cfg(feature = "interrupt")] -fn eq(attr: &Attribute, name: &str) -> bool { - attr.style == AttrStyle::Outer && attr.path().is_ident(name) -} - -#[cfg(feature = "interrupt")] -fn extract_cfgs(attrs: Vec) -> (Vec, Vec) { - let mut cfgs = vec![]; - let mut not_cfgs = vec![]; - - for attr in attrs { - if eq(&attr, "cfg") { - cfgs.push(attr); - } else { - not_cfgs.push(attr); - } - } - - (cfgs, not_cfgs) -} - -#[derive(Debug)] -struct MakeGpioEnumDispatchMacro { - name: String, - filter: Vec, - elements: Vec<(String, usize)>, -} - -impl Parse for MakeGpioEnumDispatchMacro { - fn parse(input: ParseStream) -> syn::parse::Result { - let name = input.parse::()?.to_string(); - let filter = input - .parse::()? - .stream() - .into_iter() - .map(|v| match v { - proc_macro2::TokenTree::Group(_) => String::new(), - proc_macro2::TokenTree::Ident(ident) => ident.to_string(), - proc_macro2::TokenTree::Punct(_) => String::new(), - proc_macro2::TokenTree::Literal(_) => String::new(), - }) - .filter(|p| !p.is_empty()) - .collect(); - - let mut stream = input.parse::()?.stream().into_iter(); - - let mut elements = vec![]; - - let mut element_name = String::new(); - loop { - match stream.next() { - Some(v) => match v { - proc_macro2::TokenTree::Ident(ident) => { - element_name = ident.to_string(); - } - proc_macro2::TokenTree::Literal(lit) => { - let index = lit.to_string().parse().unwrap(); - elements.push((element_name.clone(), index)); - } - _ => (), - }, - None => break, - } - } - - Ok(MakeGpioEnumDispatchMacro { - name, - filter, - elements, - }) - } -} - /// Create an enum for erased GPIO pins, using the enum-dispatch pattern /// /// Only used internally +#[cfg(feature = "enum-dispatch")] #[proc_macro] pub fn make_gpio_enum_dispatch_macro(input: TokenStream) -> TokenStream { + use quote::format_ident; + + use self::enum_dispatch::MakeGpioEnumDispatchMacro; + let input = parse_macro_input!(input as MakeGpioEnumDispatchMacro); let mut arms = Vec::new(); - for (gpio_type, num) in input.elements { - let enum_name = quote::format_ident!("ErasedPin"); - let variant_name = quote::format_ident!("Gpio{}", num); + let enum_name = format_ident!("ErasedPin"); + let variant_name = format_ident!("Gpio{}", num); if input.filter.contains(&gpio_type) { let arm = { @@ -515,7 +443,7 @@ pub fn make_gpio_enum_dispatch_macro(input: TokenStream) -> TokenStream { } } - let macro_name = quote::format_ident!("{}", input.name); + let macro_name = format_ident!("{}", input.name); quote! { #[doc(hidden)] @@ -533,7 +461,6 @@ pub fn make_gpio_enum_dispatch_macro(input: TokenStream) -> TokenStream { .into() } -#[cfg(any(feature = "esp32c6", feature = "esp32s2", feature = "esp32s3"))] /// Load code to be run on the LP/ULP core. /// /// ## Example @@ -541,26 +468,16 @@ pub fn make_gpio_enum_dispatch_macro(input: TokenStream) -> TokenStream { /// let lp_core_code = load_lp_code!("path.elf"); /// lp_core_code.run(&mut lp_core, lp_core::LpCoreWakeupSource::HpCpu, lp_pin); /// ```` +#[cfg(any(feature = "esp32c6", feature = "esp32s2", feature = "esp32s3"))] #[proc_macro] pub fn load_lp_code(input: TokenStream) -> TokenStream { + use std::{fs, path}; + use object::{Object, ObjectSection, ObjectSymbol}; - use proc_macro_crate::{crate_name, FoundCrate}; - #[cfg(not(feature = "interrupt"))] + use proc_macro::Span; use syn::{parse, Ident}; - #[cfg(feature = "esp32c6")] - let hal_crate = crate_name("esp32c6-hal"); - #[cfg(feature = "esp32s2")] - let hal_crate = crate_name("esp32s2-hal"); - #[cfg(feature = "esp32s3")] - let hal_crate = crate_name("esp32s3-hal"); - - #[cfg(feature = "esp32c6")] - let hal_crate_name = Ident::new("esp32c6_hal", Span::call_site().into()); - #[cfg(feature = "esp32s2")] - let hal_crate_name = Ident::new("esp32s2_hal", Span::call_site().into()); - #[cfg(feature = "esp32s3")] - let hal_crate_name = Ident::new("esp32s3_hal", Span::call_site().into()); + let (hal_crate, hal_crate_name) = get_hal_crate(); let hal_crate = match hal_crate { Ok(FoundCrate::Itself) => { @@ -599,13 +516,13 @@ pub fn load_lp_code(input: TokenStream) -> TokenStream { }; let elf_file = arg.value(); - if !std::path::Path::new(elf_file).exists() { + if !path::Path::new(elf_file).exists() { return parse::Error::new(Span::call_site().into(), "File not found") .to_compile_error() .into(); } - let bin_data = std::fs::read(elf_file).unwrap(); + let bin_data = fs::read(elf_file).unwrap(); let obj_file = object::File::parse(&*bin_data).unwrap(); let sections = obj_file.sections(); @@ -718,11 +635,105 @@ pub fn load_lp_code(input: TokenStream) -> TokenStream { .into() } -// just delegates to embassy's macro for RISC-V -#[cfg(all( - feature = "embassy", - not(any(feature = "esp32", feature = "esp32s2", feature = "esp32s3")) +#[cfg(any( + feature = "esp32c6-lp", + feature = "esp32s2-ulp", + feature = "esp32s3-ulp" ))] +#[proc_macro_error] +#[proc_macro_attribute] +pub fn entry(args: TokenStream, input: TokenStream) -> TokenStream { + use proc_macro2::{Ident, Span}; + use proc_macro_crate::crate_name; + use quote::{format_ident, quote}; + use syn::{parse, parse_macro_input, spanned::Spanned, FnArg, ItemFn}; + + use self::lp_core::{extract_pin, get_simplename, make_magic_symbol_name}; + + #[cfg(feature = "esp32c6-lp")] + let found_crate = + crate_name("esp32c6-lp-hal").expect("esp32c6_lp_hal is present in `Cargo.toml`"); + #[cfg(any(feature = "esp32s2-ulp", feature = "esp32s3-ulp"))] + let found_crate = + crate_name("ulp-riscv-hal").expect("ulp-riscv-hal is present in `Cargo.toml`"); + + let hal_crate = match found_crate { + #[cfg(feature = "esp32c6-lp")] + FoundCrate::Itself => quote!(esp32c6_lp_hal), + #[cfg(any(feature = "esp32s2-ulp", feature = "esp32s3-ulp"))] + FoundCrate::Itself => quote!(ulp_riscv_hal), + FoundCrate::Name(name) => { + let ident = Ident::new(&name, Span::call_site()); + quote!( #ident::Something ) + } + }; + + if !args.is_empty() { + return parse::Error::new(Span::call_site(), "This attribute accepts no arguments") + .to_compile_error() + .into(); + } + + let f = parse_macro_input!(input as ItemFn); + + let mut argument_types = Vec::new(); + + let mut used_pins: Vec = Vec::new(); + for arg in &f.sig.inputs { + match arg { + FnArg::Receiver(_) => { + return parse::Error::new(arg.span(), "invalid argument") + .to_compile_error() + .into(); + } + FnArg::Typed(t) => { + if get_simplename(&t.ty) != "GpioPin" { + return parse::Error::new(arg.span(), "invalid argument to main") + .to_compile_error() + .into(); + } + let pin = extract_pin(&t.ty); + if used_pins.contains(&pin) { + return parse::Error::new(arg.span(), "duplicate pin") + .to_compile_error() + .into(); + } + used_pins.push(pin); + + argument_types.push(t); + } + } + } + + let magic_symbol_name = make_magic_symbol_name(&argument_types); + + let param_names: Vec = argument_types + .into_iter() + .enumerate() + .map(|(num, _)| format_ident!("param{}", num)) + .collect(); + + quote!( + #[allow(non_snake_case)] + #[export_name = "main"] + pub fn __risc_v_rt__main() -> ! { + #[export_name = #magic_symbol_name] + static ULP_MAGIC: [u32; 0] = [0u32; 0]; + + unsafe { ULP_MAGIC.as_ptr().read_volatile(); } + + use #hal_crate as the_hal; + #( + let mut #param_names = unsafe { the_hal::gpio::conjour().unwrap() }; + )* + + main(#(#param_names),*); + } + #f + ) + .into() +} + /// Creates a new `executor` instance and declares an application entry point /// spawning the corresponding function body as an async task. /// @@ -744,9 +755,13 @@ pub fn load_lp_code(input: TokenStream) -> TokenStream { /// // Function body /// } /// ``` +#[cfg(all( + feature = "embassy", + not(any(feature = "esp32", feature = "esp32s2", feature = "esp32s3")) +))] #[proc_macro_attribute] pub fn main(_args: TokenStream, input: TokenStream) -> TokenStream { - let f = parse_macro_input!(input as ItemFn); + let f = parse_macro_input!(input as syn::ItemFn); let asyncness = f.sig.asyncness; let args = f.sig.inputs; @@ -761,10 +776,6 @@ pub fn main(_args: TokenStream, input: TokenStream) -> TokenStream { .into() } -#[cfg(all( - feature = "embassy", - any(feature = "esp32", feature = "esp32s2", feature = "esp32s3") -))] /// Creates a new `executor` instance and declares an application entry point /// spawning the corresponding function body as an async task. /// @@ -786,11 +797,19 @@ pub fn main(_args: TokenStream, input: TokenStream) -> TokenStream { /// // Function body /// } /// ``` +#[cfg(all( + feature = "embassy", + any(feature = "esp32", feature = "esp32s2", feature = "esp32s3") +))] #[proc_macro_attribute] pub fn main(args: TokenStream, item: TokenStream) -> TokenStream { - let args = syn::parse_macro_input!(args as embassy_xtensa::Args); - let f = syn::parse_macro_input!(item as syn::ItemFn); - embassy_xtensa::main::run(&args.meta, f, embassy_xtensa::main::main()) - .unwrap_or_else(|x| x) - .into() + use self::embassy_xtensa::{ + main::{main, run}, + Args, + }; + + let args = parse_macro_input!(args as Args); + let f = parse_macro_input!(item as syn::ItemFn); + + run(&args.meta, f, main()).unwrap_or_else(|x| x).into() } diff --git a/esp-hal-procmacros/src/lp_core.rs b/esp-hal-procmacros/src/lp_core.rs new file mode 100644 index 000000000..45fee2f76 --- /dev/null +++ b/esp-hal-procmacros/src/lp_core.rs @@ -0,0 +1,71 @@ +use quote::quote; +use syn::{GenericArgument, PatType, PathArguments, Type}; + +pub(crate) fn make_magic_symbol_name(args: &Vec<&PatType>) -> String { + let mut res = String::from("__ULP_MAGIC_"); + for &a in args { + let t = &a.ty; + let quoted = to_string(&t); + res.push_str("ed); + res.push_str("$"); + } + + res +} + +pub(crate) fn get_simplename(t: &Type) -> String { + String::from(match t { + Type::Path(p) => String::from(&p.path.segments.last().unwrap().ident.to_string()), + _ => String::new(), + }) +} + +pub(crate) fn extract_pin(ty: &Type) -> u8 { + let mut res = 255u8; + if let Type::Path(p) = ty { + let segment = p.path.segments.last().unwrap(); + if let PathArguments::AngleBracketed(g) = &segment.arguments { + for arg in &g.args { + match arg { + GenericArgument::Type(t) => { + res = extract_pin(t); + } + GenericArgument::Const(c) => { + res = ("e! { #c }.to_string()).parse().unwrap(); + } + _ => (), + } + } + } + } + + res +} + +// This is a specialized implementation - won't fit other use-cases +fn to_string(ty: &Type) -> String { + let mut res = String::new(); + if let Type::Path(p) = ty { + let segment = p.path.segments.last().unwrap(); + res.push_str(&segment.ident.to_string()); + + if let PathArguments::AngleBracketed(g) = &segment.arguments { + res.push_str("<"); + for arg in &g.args { + match arg { + GenericArgument::Type(t) => { + res.push_str(&to_string(t)); + } + GenericArgument::Const(c) => { + res.push_str(","); + res.push_str("e! { #c }.to_string()); + } + _ => (), + } + } + res.push_str(">"); + } + } + + res +} diff --git a/esp32c6-lp-hal/Cargo.toml b/esp32c6-lp-hal/Cargo.toml index 795d3c956..f61e84ac9 100644 --- a/esp32c6-lp-hal/Cargo.toml +++ b/esp32c6-lp-hal/Cargo.toml @@ -24,7 +24,7 @@ categories = [ critical-section = { version = "1.1.2", features = ["restore-state-u8"] } embedded-hal = { version = "0.2.7", features = ["unproven"] } esp32c6-lp = { git = "https://github.com/esp-rs/esp-pacs", rev = "a9cad5e", features = ["critical-section"] } -lp-hal-procmacros = { path = "../lp-hal-procmacros", features = ["esp32c6"] } +procmacros = { package = "esp-hal-procmacros", path = "../esp-hal-procmacros", features = ["esp32c6-lp"] } riscv = "0.10.1" paste = "1.0.14" diff --git a/esp32c6-lp-hal/src/prelude.rs b/esp32c6-lp-hal/src/prelude.rs index 41f9afdb8..883977c99 100644 --- a/esp32c6-lp-hal/src/prelude.rs +++ b/esp32c6-lp-hal/src/prelude.rs @@ -12,4 +12,4 @@ pub use embedded_hal::{ }, prelude::*, }; -pub use lp_hal_procmacros::entry; +pub use procmacros::entry; diff --git a/lp-hal-procmacros/Cargo.toml b/lp-hal-procmacros/Cargo.toml deleted file mode 100644 index 87569effa..000000000 --- a/lp-hal-procmacros/Cargo.toml +++ /dev/null @@ -1,36 +0,0 @@ -[package] -name = "lp-hal-procmacros" -version = "0.1.0" -edition = "2021" -rust-version = "1.67.0" -description = "Procedural macros for the LP/ULP coprocessors" -repository = "https://github.com/esp-rs/esp-hal" -license = "MIT OR Apache-2.0" - -keywords = [ - "embedded", - "embedded-hal", - "esp", - "esp32c6", - "esp32s2", - "esp32s3", - "no-std", -] -categories = ["embedded", "hardware-support", "no-std"] - -[lib] -proc-macro = true - -[dependencies] -litrs = "0.4.0" -proc-macro-crate = "1.3.1" -proc-macro-error = "1.0.4" -proc-macro2 = "1.0.63" -quote = "1.0.28" -syn = { version = "2.0.22", features = ["extra-traits", "full"] } -toml_edit = "0.20.0" - -[features] -esp32c6 = [] -esp32s2 = [] -esp32s3 = [] diff --git a/lp-hal-procmacros/src/lib.rs b/lp-hal-procmacros/src/lib.rs deleted file mode 100644 index 5b7dcca3f..000000000 --- a/lp-hal-procmacros/src/lib.rs +++ /dev/null @@ -1,175 +0,0 @@ -use proc_macro::TokenStream; -use proc_macro2::{Ident, Span}; -use proc_macro_crate::{crate_name, FoundCrate}; -use proc_macro_error::proc_macro_error; -use quote::{format_ident, quote}; -use syn::{parse, parse_macro_input, spanned::Spanned, FnArg, ItemFn, PatType}; - -#[proc_macro_error] -#[proc_macro_attribute] -pub fn entry(args: TokenStream, input: TokenStream) -> TokenStream { - #[cfg(feature = "esp32c6")] - let found_crate = - crate_name("esp32c6-lp-hal").expect("esp32c6_lp_hal is present in `Cargo.toml`"); - #[cfg(any(feature = "esp32s2", feature = "esp32s3"))] - let found_crate = crate_name("ulp-riscv-hal").expect("ulp-riscv-ha is present in `Cargo.toml`"); - - let hal_crate = match found_crate { - #[cfg(feature = "esp32c6")] - FoundCrate::Itself => quote!(esp32c6_lp_hal), - #[cfg(any(feature = "esp32s2", feature = "esp32s3"))] - FoundCrate::Itself => quote!(ulp_riscv_hal), - FoundCrate::Name(name) => { - let ident = Ident::new(&name, Span::call_site()); - quote!( #ident::Something ) - } - }; - - if !args.is_empty() { - return parse::Error::new(Span::call_site(), "This attribute accepts no arguments") - .to_compile_error() - .into(); - } - - let f = parse_macro_input!(input as ItemFn); - - let mut argument_types = Vec::new(); - - let mut used_pins: Vec = Vec::new(); - for arg in &f.sig.inputs { - match arg { - FnArg::Receiver(_) => { - return parse::Error::new(arg.span(), "invalid argument") - .to_compile_error() - .into(); - } - FnArg::Typed(t) => { - if get_simplename(&t.ty) != "GpioPin" { - return parse::Error::new(arg.span(), "invalid argument to main") - .to_compile_error() - .into(); - } - let pin = extract_pin(&t.ty); - if used_pins.contains(&pin) { - return parse::Error::new(arg.span(), "duplicate pin") - .to_compile_error() - .into(); - } - used_pins.push(pin); - - argument_types.push(t); - } - } - } - - let magic_symbol_name = make_magic_symbol_name(&argument_types); - - let param_names: Vec = argument_types - .into_iter() - .enumerate() - .map(|(num, _)| format_ident!("param{}", num)) - .collect(); - - quote!( - #[allow(non_snake_case)] - #[export_name = "main"] - pub fn __risc_v_rt__main() -> ! { - #[export_name = #magic_symbol_name] - static ULP_MAGIC: [u32; 0] = [0u32; 0]; - - unsafe { ULP_MAGIC.as_ptr().read_volatile(); } - - use #hal_crate as the_hal; - #( - let mut #param_names = unsafe { the_hal::gpio::conjour().unwrap() }; - )* - - main(#(#param_names),*); - } - #f - ) - .into() -} - -fn make_magic_symbol_name(args: &Vec<&PatType>) -> String { - let mut res = String::from("__ULP_MAGIC_"); - - for &a in args { - let t = &a.ty; - let quoted = to_string(&t); - res.push_str("ed); - res.push_str("$"); - } - res -} - -// this is a specialized implementation - won't fit other use-cases -fn to_string(t: &syn::Type) -> String { - let mut res = String::new(); - - match t { - syn::Type::Path(p) => { - let segment = p.path.segments.last().unwrap(); - res.push_str(&segment.ident.to_string()); - match &segment.arguments { - syn::PathArguments::None => (), - syn::PathArguments::Parenthesized(_) => (), - syn::PathArguments::AngleBracketed(g) => { - res.push_str("<"); - for arg in &g.args { - match arg { - syn::GenericArgument::Type(t) => { - res.push_str(&to_string(t)); - } - syn::GenericArgument::Const(c) => { - res.push_str(","); - res.push_str("e! { #c }.to_string()); - } - _ => (), - } - } - res.push_str(">"); - } - } - } - _ => (), - } - res -} - -fn get_simplename(t: &syn::Type) -> String { - String::from(match t { - syn::Type::Path(p) => String::from(&p.path.segments.last().unwrap().ident.to_string()), - _ => String::new(), - }) -} - -fn extract_pin(t: &syn::Type) -> u8 { - let mut res = 255u8; - - match t { - syn::Type::Path(p) => { - let segment = p.path.segments.last().unwrap(); - match &segment.arguments { - syn::PathArguments::None => (), - syn::PathArguments::Parenthesized(_) => (), - syn::PathArguments::AngleBracketed(g) => { - for arg in &g.args { - match arg { - syn::GenericArgument::Type(t) => { - res = extract_pin(t); - } - syn::GenericArgument::Const(c) => { - res = ("e! { #c }.to_string()).parse().unwrap(); - } - _ => (), - } - } - } - } - } - _ => (), - } - - res -} diff --git a/ulp-riscv-hal/Cargo.toml b/ulp-riscv-hal/Cargo.toml index d0886c433..7df3fd347 100644 --- a/ulp-riscv-hal/Cargo.toml +++ b/ulp-riscv-hal/Cargo.toml @@ -23,10 +23,10 @@ categories = [ [dependencies] embedded-hal = { version = "0.2.7", features = ["unproven"] } -lp-hal-procmacros = { path = "../lp-hal-procmacros" } +procmacros = { package = "esp-hal-procmacros", path = "../esp-hal-procmacros" } paste = "1.0.14" -esp32s2-ulp = { git = "https://github.com/esp-rs/esp-pacs", package = "esp32s2-ulp", optional = true } -esp32s3-ulp = { git = "https://github.com/esp-rs/esp-pacs", package = "esp32s3-ulp", optional = true } +esp32s2-ulp = { git = "https://github.com/esp-rs/esp-pacs", package = "esp32s2-ulp", optional = true } +esp32s3-ulp = { git = "https://github.com/esp-rs/esp-pacs", package = "esp32s3-ulp", optional = true } [dev-dependencies] @@ -35,5 +35,5 @@ panic-halt = "0.2.0" [features] default = [] debug = [] -esp32s2 = [ "dep:esp32s2-ulp", "lp-hal-procmacros/esp32s2" ] -esp32s3 = [ "dep:esp32s3-ulp", "lp-hal-procmacros/esp32s3" ] +esp32s2 = [ "dep:esp32s2-ulp", "procmacros/esp32s2-ulp" ] +esp32s3 = [ "dep:esp32s3-ulp", "procmacros/esp32s3-ulp" ] diff --git a/ulp-riscv-hal/src/prelude.rs b/ulp-riscv-hal/src/prelude.rs index 41f9afdb8..883977c99 100644 --- a/ulp-riscv-hal/src/prelude.rs +++ b/ulp-riscv-hal/src/prelude.rs @@ -12,4 +12,4 @@ pub use embedded_hal::{ }, prelude::*, }; -pub use lp_hal_procmacros::entry; +pub use procmacros::entry;