Assimilate lp-hal-procmacros into esp-hal-procmacros (#845)

* Assimilate `lp-hal-procmacros` into `esp-hal-procmacros`

* Update `CHANGELOG.md`
This commit is contained in:
Jesse Braham 2023-10-12 06:58:37 -07:00 committed by GitHub
parent f81dc50748
commit c6af8ba88b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 460 additions and 462 deletions

View File

@ -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

View File

@ -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 }

View File

@ -1,10 +1,6 @@
[package]
name = "esp-hal-procmacros"
version = "0.6.1"
authors = [
"Jesse Braham <jesse@beta7.io>",
"Björn Quentin <bjoern.quentin@mobile-j.de>",
]
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 = []

View File

@ -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<String>,
pub elements: Vec<(String, usize)>,
}
impl Parse for MakeGpioEnumDispatchMacro {
fn parse(input: ParseStream) -> Result<Self> {
let name = input.parse::<Ident>()?.to_string();
let filter = input
.parse::<Group>()?
.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::<Group>()?.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,
})
}
}

View File

@ -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<Attribute>) -> (Vec<Attribute>, Vec<Attribute>) {
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)
}

View File

@ -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<FoundCrate, proc_macro_crate::Error>,
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_macro_crate::FoundCrate, proc_macro_crate::Error>,
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<Attribute>) -> (Vec<Attribute>, Vec<Attribute>) {
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<String>,
elements: Vec<(String, usize)>,
}
impl Parse for MakeGpioEnumDispatchMacro {
fn parse(input: ParseStream) -> syn::parse::Result<Self> {
let name = input.parse::<syn::Ident>()?.to_string();
let filter = input
.parse::<proc_macro2::Group>()?
.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::<proc_macro2::Group>()?.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<u8> = 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<Ident> = 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()
}

View File

@ -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(&quoted);
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 = (&quote! { #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(&quote! { #c }.to_string());
}
_ => (),
}
}
res.push_str(">");
}
}
res
}

View File

@ -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"

View File

@ -12,4 +12,4 @@ pub use embedded_hal::{
},
prelude::*,
};
pub use lp_hal_procmacros::entry;
pub use procmacros::entry;

View File

@ -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 = []

View File

@ -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<u8> = 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<Ident> = 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(&quoted);
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(&quote! { #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 = (&quote! { #c }.to_string()).parse().unwrap();
}
_ => (),
}
}
}
}
}
_ => (),
}
res
}

View File

@ -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" ]

View File

@ -12,4 +12,4 @@ pub use embedded_hal::{
},
prelude::*,
};
pub use lp_hal_procmacros::entry;
pub use procmacros::entry;