mirror of
https://github.com/esp-rs/esp-hal.git
synced 2025-10-02 14:44:42 +00:00
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:
parent
f81dc50748
commit
c6af8ba88b
@ -25,9 +25,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||||||
- SYSTIMER ETM functionality (#828)
|
- SYSTIMER ETM functionality (#828)
|
||||||
- Adding async support for RSA peripheral(doesn't work properly for `esp32` chip - issue will be created)(#790)
|
- 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)
|
- 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)
|
- Add bare-bones SPI slave support, DMA only (#580, #843)
|
||||||
- Embassy `#[main]` convenience macro
|
- Embassy `#[main]` convenience macro (#841)
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
|
|
||||||
|
@ -28,7 +28,7 @@ fugit = "0.3.7"
|
|||||||
log = { version = "0.4.20", optional = true }
|
log = { version = "0.4.20", optional = true }
|
||||||
nb = "1.1.0"
|
nb = "1.1.0"
|
||||||
paste = "1.0.14"
|
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"] }
|
strum = { version = "0.25.0", default-features = false, features = ["derive"] }
|
||||||
void = { version = "1.0.2", default-features = false }
|
void = { version = "1.0.2", default-features = false }
|
||||||
usb-device = { version = "0.2.9", optional = true }
|
usb-device = { version = "0.2.9", optional = true }
|
||||||
|
@ -1,10 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "esp-hal-procmacros"
|
name = "esp-hal-procmacros"
|
||||||
version = "0.6.1"
|
version = "0.6.1"
|
||||||
authors = [
|
|
||||||
"Jesse Braham <jesse@beta7.io>",
|
|
||||||
"Björn Quentin <bjoern.quentin@mobile-j.de>",
|
|
||||||
]
|
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
rust-version = "1.67.0"
|
rust-version = "1.67.0"
|
||||||
description = "Procedural macros for ESP-HAL"
|
description = "Procedural macros for ESP-HAL"
|
||||||
@ -12,32 +8,37 @@ repository = "https://github.com/esp-rs/esp-hal"
|
|||||||
license = "MIT OR Apache-2.0"
|
license = "MIT OR Apache-2.0"
|
||||||
|
|
||||||
[package.metadata.docs.rs]
|
[package.metadata.docs.rs]
|
||||||
features = ["esp32c3", "interrupt"]
|
features = ["esp32c3", "interrupt", "ram"]
|
||||||
|
|
||||||
[lib]
|
[lib]
|
||||||
proc-macro = true
|
proc-macro = true
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
darling = "0.20.3"
|
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"
|
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
|
object = { version = "0.32.1", optional = true }
|
||||||
toml_edit = "=0.19.14"
|
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]
|
[features]
|
||||||
|
# Select a target device:
|
||||||
esp32 = []
|
esp32 = []
|
||||||
esp32c2 = []
|
esp32c2 = []
|
||||||
esp32c3 = []
|
esp32c3 = []
|
||||||
esp32c6 = ["dep:object"]
|
esp32c6 = ["dep:object"]
|
||||||
|
esp32c6-lp = []
|
||||||
esp32h2 = []
|
esp32h2 = []
|
||||||
esp32s2 = ["dep:object"]
|
esp32s2 = ["dep:object"]
|
||||||
|
esp32s2-ulp = []
|
||||||
esp32s3 = ["dep:object"]
|
esp32s3 = ["dep:object"]
|
||||||
|
esp32s3-ulp = []
|
||||||
|
|
||||||
interrupt = []
|
# Gated features:
|
||||||
rtc_slow = []
|
|
||||||
embassy = []
|
embassy = []
|
||||||
|
enum-dispatch = []
|
||||||
|
interrupt = []
|
||||||
|
ram = []
|
||||||
|
rtc_slow = []
|
||||||
|
56
esp-hal-procmacros/src/enum_dispatch.rs
Normal file
56
esp-hal-procmacros/src/enum_dispatch.rs
Normal 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,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
62
esp-hal-procmacros/src/interrupt.rs
Normal file
62
esp-hal-procmacros/src/interrupt.rs
Normal 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)
|
||||||
|
}
|
@ -47,34 +47,92 @@
|
|||||||
|
|
||||||
#![doc(html_logo_url = "https://avatars.githubusercontent.com/u/46717278")]
|
#![doc(html_logo_url = "https://avatars.githubusercontent.com/u/46717278")]
|
||||||
|
|
||||||
use darling::{ast::NestedMeta, FromMeta};
|
#[cfg(feature = "ram")]
|
||||||
use proc_macro::{self, Span, TokenStream};
|
use darling::{ast::NestedMeta, Error as DarlingError, FromMeta};
|
||||||
use proc_macro2::Ident;
|
use proc_macro::TokenStream;
|
||||||
use proc_macro_error::{abort, proc_macro_error};
|
use proc_macro_crate::FoundCrate;
|
||||||
|
use proc_macro_error::proc_macro_error;
|
||||||
use quote::quote;
|
use quote::quote;
|
||||||
#[cfg(feature = "interrupt")]
|
use syn::parse_macro_input;
|
||||||
use syn::{
|
|
||||||
parse,
|
|
||||||
spanned::Spanned,
|
|
||||||
AttrStyle,
|
|
||||||
Attribute,
|
|
||||||
ItemFn,
|
|
||||||
Meta::Path,
|
|
||||||
ReturnType,
|
|
||||||
Type,
|
|
||||||
Visibility,
|
|
||||||
};
|
|
||||||
use syn::{
|
|
||||||
parse::{Parse, ParseStream},
|
|
||||||
parse_macro_input,
|
|
||||||
};
|
|
||||||
|
|
||||||
#[cfg(all(
|
#[cfg(all(
|
||||||
feature = "embassy",
|
feature = "embassy",
|
||||||
any(feature = "esp32", feature = "esp32s2", feature = "esp32s3")
|
any(feature = "esp32", feature = "esp32s2", feature = "esp32s3")
|
||||||
))]
|
))]
|
||||||
mod embassy_xtensa;
|
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)]
|
#[derive(Debug, Default, FromMeta)]
|
||||||
#[darling(default)]
|
#[darling(default)]
|
||||||
struct RamArgs {
|
struct RamArgs {
|
||||||
@ -84,45 +142,6 @@ struct RamArgs {
|
|||||||
zeroed: bool,
|
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.
|
/// This attribute allows placing statics and functions into ram.
|
||||||
///
|
///
|
||||||
/// Options that can be specified are rtc_slow or rtc_fast to use the
|
/// 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)
|
/// (e.g. to persist it across resets or deep sleep mode for the RTC RAM)
|
||||||
///
|
///
|
||||||
/// Not all targets support RTC slow ram.
|
/// Not all targets support RTC slow ram.
|
||||||
|
#[cfg(feature = "ram")]
|
||||||
#[proc_macro_attribute]
|
#[proc_macro_attribute]
|
||||||
#[proc_macro_error]
|
#[proc_macro_error]
|
||||||
pub fn ram(args: TokenStream, input: TokenStream) -> TokenStream {
|
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()) {
|
let attr_args = match NestedMeta::parse_meta_list(args.into()) {
|
||||||
Ok(v) => v,
|
Ok(v) => v,
|
||||||
Err(e) => {
|
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
|
#section
|
||||||
#item
|
#item
|
||||||
};
|
};
|
||||||
|
|
||||||
output.into()
|
output.into()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -231,7 +255,22 @@ pub fn ram(args: TokenStream, input: TokenStream) -> TokenStream {
|
|||||||
#[cfg(feature = "interrupt")]
|
#[cfg(feature = "interrupt")]
|
||||||
#[proc_macro_attribute]
|
#[proc_macro_attribute]
|
||||||
pub fn interrupt(args: TokenStream, input: TokenStream) -> TokenStream {
|
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");
|
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 {
|
if attr_args.len() == 1 {
|
||||||
match &attr_args[0] {
|
match &attr_args[0] {
|
||||||
NestedMeta::Meta(Path(x)) => {
|
NestedMeta::Meta(Meta::Path(x)) => {
|
||||||
ident_s = x.get_ident().unwrap();
|
ident_s = x.get_ident().unwrap();
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
@ -292,7 +331,7 @@ pub fn interrupt(args: TokenStream, input: TokenStream) -> TokenStream {
|
|||||||
&& f.sig.inputs.len() <= 1;
|
&& f.sig.inputs.len() <= 1;
|
||||||
|
|
||||||
if !valid_signature {
|
if !valid_signature {
|
||||||
return parse::Error::new(
|
return ParseError::new(
|
||||||
f.span(),
|
f.span(),
|
||||||
"`#[interrupt]` handlers must have signature `[unsafe] fn([&mut Context]) [-> !]`",
|
"`#[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! {{
|
syn::parse2(quote! {{
|
||||||
// Check that this interrupt actually exists
|
// Check that this interrupt actually exists
|
||||||
#interrupt_in_hal_crate;
|
#interrupt_in_hal_crate;
|
||||||
@ -371,133 +410,22 @@ pub fn interrupt(args: TokenStream, input: TokenStream) -> TokenStream {
|
|||||||
.into()
|
.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
|
/// Create an enum for erased GPIO pins, using the enum-dispatch pattern
|
||||||
///
|
///
|
||||||
/// Only used internally
|
/// Only used internally
|
||||||
|
#[cfg(feature = "enum-dispatch")]
|
||||||
#[proc_macro]
|
#[proc_macro]
|
||||||
pub fn make_gpio_enum_dispatch_macro(input: TokenStream) -> TokenStream {
|
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 input = parse_macro_input!(input as MakeGpioEnumDispatchMacro);
|
||||||
|
|
||||||
let mut arms = Vec::new();
|
let mut arms = Vec::new();
|
||||||
|
|
||||||
for (gpio_type, num) in input.elements {
|
for (gpio_type, num) in input.elements {
|
||||||
let enum_name = quote::format_ident!("ErasedPin");
|
let enum_name = format_ident!("ErasedPin");
|
||||||
let variant_name = quote::format_ident!("Gpio{}", num);
|
let variant_name = format_ident!("Gpio{}", num);
|
||||||
|
|
||||||
if input.filter.contains(&gpio_type) {
|
if input.filter.contains(&gpio_type) {
|
||||||
let arm = {
|
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! {
|
quote! {
|
||||||
#[doc(hidden)]
|
#[doc(hidden)]
|
||||||
@ -533,7 +461,6 @@ pub fn make_gpio_enum_dispatch_macro(input: TokenStream) -> TokenStream {
|
|||||||
.into()
|
.into()
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(any(feature = "esp32c6", feature = "esp32s2", feature = "esp32s3"))]
|
|
||||||
/// Load code to be run on the LP/ULP core.
|
/// Load code to be run on the LP/ULP core.
|
||||||
///
|
///
|
||||||
/// ## Example
|
/// ## Example
|
||||||
@ -541,26 +468,16 @@ pub fn make_gpio_enum_dispatch_macro(input: TokenStream) -> TokenStream {
|
|||||||
/// let lp_core_code = load_lp_code!("path.elf");
|
/// let lp_core_code = load_lp_code!("path.elf");
|
||||||
/// lp_core_code.run(&mut lp_core, lp_core::LpCoreWakeupSource::HpCpu, lp_pin);
|
/// lp_core_code.run(&mut lp_core, lp_core::LpCoreWakeupSource::HpCpu, lp_pin);
|
||||||
/// ````
|
/// ````
|
||||||
|
#[cfg(any(feature = "esp32c6", feature = "esp32s2", feature = "esp32s3"))]
|
||||||
#[proc_macro]
|
#[proc_macro]
|
||||||
pub fn load_lp_code(input: TokenStream) -> TokenStream {
|
pub fn load_lp_code(input: TokenStream) -> TokenStream {
|
||||||
|
use std::{fs, path};
|
||||||
|
|
||||||
use object::{Object, ObjectSection, ObjectSymbol};
|
use object::{Object, ObjectSection, ObjectSymbol};
|
||||||
use proc_macro_crate::{crate_name, FoundCrate};
|
use proc_macro::Span;
|
||||||
#[cfg(not(feature = "interrupt"))]
|
|
||||||
use syn::{parse, Ident};
|
use syn::{parse, Ident};
|
||||||
|
|
||||||
#[cfg(feature = "esp32c6")]
|
let (hal_crate, hal_crate_name) = get_hal_crate();
|
||||||
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 = match hal_crate {
|
let hal_crate = match hal_crate {
|
||||||
Ok(FoundCrate::Itself) => {
|
Ok(FoundCrate::Itself) => {
|
||||||
@ -599,13 +516,13 @@ pub fn load_lp_code(input: TokenStream) -> TokenStream {
|
|||||||
};
|
};
|
||||||
let elf_file = arg.value();
|
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")
|
return parse::Error::new(Span::call_site().into(), "File not found")
|
||||||
.to_compile_error()
|
.to_compile_error()
|
||||||
.into();
|
.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 obj_file = object::File::parse(&*bin_data).unwrap();
|
||||||
let sections = obj_file.sections();
|
let sections = obj_file.sections();
|
||||||
|
|
||||||
@ -718,11 +635,105 @@ pub fn load_lp_code(input: TokenStream) -> TokenStream {
|
|||||||
.into()
|
.into()
|
||||||
}
|
}
|
||||||
|
|
||||||
// just delegates to embassy's macro for RISC-V
|
#[cfg(any(
|
||||||
#[cfg(all(
|
feature = "esp32c6-lp",
|
||||||
feature = "embassy",
|
feature = "esp32s2-ulp",
|
||||||
not(any(feature = "esp32", feature = "esp32s2", feature = "esp32s3"))
|
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
|
/// Creates a new `executor` instance and declares an application entry point
|
||||||
/// spawning the corresponding function body as an async task.
|
/// spawning the corresponding function body as an async task.
|
||||||
///
|
///
|
||||||
@ -744,9 +755,13 @@ pub fn load_lp_code(input: TokenStream) -> TokenStream {
|
|||||||
/// // Function body
|
/// // Function body
|
||||||
/// }
|
/// }
|
||||||
/// ```
|
/// ```
|
||||||
|
#[cfg(all(
|
||||||
|
feature = "embassy",
|
||||||
|
not(any(feature = "esp32", feature = "esp32s2", feature = "esp32s3"))
|
||||||
|
))]
|
||||||
#[proc_macro_attribute]
|
#[proc_macro_attribute]
|
||||||
pub fn main(_args: TokenStream, input: TokenStream) -> TokenStream {
|
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 asyncness = f.sig.asyncness;
|
||||||
let args = f.sig.inputs;
|
let args = f.sig.inputs;
|
||||||
@ -761,10 +776,6 @@ pub fn main(_args: TokenStream, input: TokenStream) -> TokenStream {
|
|||||||
.into()
|
.into()
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(all(
|
|
||||||
feature = "embassy",
|
|
||||||
any(feature = "esp32", feature = "esp32s2", feature = "esp32s3")
|
|
||||||
))]
|
|
||||||
/// Creates a new `executor` instance and declares an application entry point
|
/// Creates a new `executor` instance and declares an application entry point
|
||||||
/// spawning the corresponding function body as an async task.
|
/// spawning the corresponding function body as an async task.
|
||||||
///
|
///
|
||||||
@ -786,11 +797,19 @@ pub fn main(_args: TokenStream, input: TokenStream) -> TokenStream {
|
|||||||
/// // Function body
|
/// // Function body
|
||||||
/// }
|
/// }
|
||||||
/// ```
|
/// ```
|
||||||
|
#[cfg(all(
|
||||||
|
feature = "embassy",
|
||||||
|
any(feature = "esp32", feature = "esp32s2", feature = "esp32s3")
|
||||||
|
))]
|
||||||
#[proc_macro_attribute]
|
#[proc_macro_attribute]
|
||||||
pub fn main(args: TokenStream, item: TokenStream) -> TokenStream {
|
pub fn main(args: TokenStream, item: TokenStream) -> TokenStream {
|
||||||
let args = syn::parse_macro_input!(args as embassy_xtensa::Args);
|
use self::embassy_xtensa::{
|
||||||
let f = syn::parse_macro_input!(item as syn::ItemFn);
|
main::{main, run},
|
||||||
embassy_xtensa::main::run(&args.meta, f, embassy_xtensa::main::main())
|
Args,
|
||||||
.unwrap_or_else(|x| x)
|
};
|
||||||
.into()
|
|
||||||
|
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()
|
||||||
}
|
}
|
||||||
|
71
esp-hal-procmacros/src/lp_core.rs
Normal file
71
esp-hal-procmacros/src/lp_core.rs
Normal 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("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
|
||||||
|
}
|
@ -24,7 +24,7 @@ categories = [
|
|||||||
critical-section = { version = "1.1.2", features = ["restore-state-u8"] }
|
critical-section = { version = "1.1.2", features = ["restore-state-u8"] }
|
||||||
embedded-hal = { version = "0.2.7", features = ["unproven"] }
|
embedded-hal = { version = "0.2.7", features = ["unproven"] }
|
||||||
esp32c6-lp = { git = "https://github.com/esp-rs/esp-pacs", rev = "a9cad5e", features = ["critical-section"] }
|
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"
|
riscv = "0.10.1"
|
||||||
paste = "1.0.14"
|
paste = "1.0.14"
|
||||||
|
|
||||||
|
@ -12,4 +12,4 @@ pub use embedded_hal::{
|
|||||||
},
|
},
|
||||||
prelude::*,
|
prelude::*,
|
||||||
};
|
};
|
||||||
pub use lp_hal_procmacros::entry;
|
pub use procmacros::entry;
|
||||||
|
@ -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 = []
|
|
@ -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("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
|
|
||||||
}
|
|
@ -23,7 +23,7 @@ categories = [
|
|||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
embedded-hal = { version = "0.2.7", features = ["unproven"] }
|
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"
|
paste = "1.0.14"
|
||||||
esp32s2-ulp = { git = "https://github.com/esp-rs/esp-pacs", package = "esp32s2-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 }
|
esp32s3-ulp = { git = "https://github.com/esp-rs/esp-pacs", package = "esp32s3-ulp", optional = true }
|
||||||
@ -35,5 +35,5 @@ panic-halt = "0.2.0"
|
|||||||
[features]
|
[features]
|
||||||
default = []
|
default = []
|
||||||
debug = []
|
debug = []
|
||||||
esp32s2 = [ "dep:esp32s2-ulp", "lp-hal-procmacros/esp32s2" ]
|
esp32s2 = [ "dep:esp32s2-ulp", "procmacros/esp32s2-ulp" ]
|
||||||
esp32s3 = [ "dep:esp32s3-ulp", "lp-hal-procmacros/esp32s3" ]
|
esp32s3 = [ "dep:esp32s3-ulp", "procmacros/esp32s3-ulp" ]
|
||||||
|
@ -12,4 +12,4 @@ pub use embedded_hal::{
|
|||||||
},
|
},
|
||||||
prelude::*,
|
prelude::*,
|
||||||
};
|
};
|
||||||
pub use lp_hal_procmacros::entry;
|
pub use procmacros::entry;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user