Skip to content

Commit

Permalink
embassy #[main] convenience for RISC-V / Xtensa (#841)
Browse files Browse the repository at this point in the history
  • Loading branch information
bjoernQ authored Oct 11, 2023
1 parent 0aa0232 commit a0ebdf0
Show file tree
Hide file tree
Showing 70 changed files with 1,723 additions and 2,224 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Added sleep support for ESP32-C3 with timer and GPIO wakeups (#795)
- Support for ULP-RISCV including Delay and GPIO (#840)
- Add bare-bones SPI slave support, DMA only (#580)
- Embassy `#[main]` convenience macro

### Changed

Expand Down
2 changes: 1 addition & 1 deletion esp-hal-common/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,7 @@ ufmt = ["ufmt-write"]
async = ["embedded-hal-async", "eh1", "embassy-sync", "embassy-futures", "embedded-io-async"]

# Embassy support
embassy = ["embassy-time"]
embassy = ["embassy-time","procmacros/embassy"]

embassy-executor-interrupt = ["embassy", "embassy-executor"]
embassy-executor-thread = ["embassy", "embassy-executor"]
Expand Down
1 change: 1 addition & 0 deletions esp-hal-procmacros/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -40,3 +40,4 @@ esp32s3 = ["dep:object"]

interrupt = []
rtc_slow = []
embassy = []
191 changes: 191 additions & 0 deletions esp-hal-procmacros/src/embassy_xtensa.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,191 @@
use darling::ast::NestedMeta;
use syn::{
parse::{Parse, ParseBuffer},
punctuated::Punctuated,
Token,
};

pub(crate) struct Args {
pub(crate) meta: Vec<NestedMeta>,
}

impl Parse for Args {
fn parse(input: &ParseBuffer) -> syn::Result<Self> {
let meta = Punctuated::<NestedMeta, Token![,]>::parse_terminated(input)?;
Ok(Args {
meta: meta.into_iter().collect(),
})
}
}

pub(crate) mod main {
use std::{cell::RefCell, fmt::Display, thread};

use darling::{export::NestedMeta, FromMeta};
use proc_macro2::{Ident, Span, TokenStream};
use proc_macro_crate::FoundCrate;
use quote::{quote, ToTokens};
use syn::{ReturnType, Type};

#[derive(Debug, FromMeta)]
struct Args {}

pub fn run(
args: &[NestedMeta],
f: syn::ItemFn,
main: TokenStream,
) -> Result<TokenStream, TokenStream> {
#[allow(unused_variables)]
let args = Args::from_list(args).map_err(|e| e.write_errors())?;

let fargs = f.sig.inputs.clone();

let ctxt = Ctxt::new();

if f.sig.asyncness.is_none() {
ctxt.error_spanned_by(&f.sig, "main function must be async");
}
if !f.sig.generics.params.is_empty() {
ctxt.error_spanned_by(&f.sig, "main function must not be generic");
}
if !f.sig.generics.where_clause.is_none() {
ctxt.error_spanned_by(&f.sig, "main function must not have `where` clauses");
}
if !f.sig.abi.is_none() {
ctxt.error_spanned_by(&f.sig, "main function must not have an ABI qualifier");
}
if !f.sig.variadic.is_none() {
ctxt.error_spanned_by(&f.sig, "main function must not be variadic");
}
match &f.sig.output {
ReturnType::Default => {}
ReturnType::Type(_, ty) => match &**ty {
Type::Tuple(tuple) if tuple.elems.is_empty() => {}
Type::Never(_) => {}
_ => ctxt.error_spanned_by(
&f.sig,
"main function must either not return a value, return `()` or return `!`",
),
},
}

if fargs.len() != 1 {
ctxt.error_spanned_by(&f.sig, "main function must have 1 argument: the spawner.");
}

ctxt.check()?;

let f_body = f.block;
let out = &f.sig.output;

let result = quote! {
#[::embassy_executor::task()]
async fn __embassy_main(#fargs) #out {
#f_body
}

unsafe fn __make_static<T>(t: &mut T) -> &'static mut T {
::core::mem::transmute(t)
}

#main
};

Ok(result)
}

/// A type to collect errors together and format them.
///
/// Dropping this object will cause a panic. It must be consumed using
/// `check`.
///
/// References can be shared since this type uses run-time exclusive mut
/// checking.
#[derive(Default)]
pub struct Ctxt {
// The contents will be set to `None` during checking. This is so that checking can be
// enforced.
errors: RefCell<Option<Vec<syn::Error>>>,
}

impl Ctxt {
/// Create a new context object.
///
/// This object contains no errors, but will still trigger a panic if it
/// is not `check`ed.
pub fn new() -> Self {
Ctxt {
errors: RefCell::new(Some(Vec::new())),
}
}

/// Add an error to the context object with a tokenenizable object.
///
/// The object is used for spanning in error messages.
pub fn error_spanned_by<A: ToTokens, T: Display>(&self, obj: A, msg: T) {
self.errors
.borrow_mut()
.as_mut()
.unwrap()
// Curb monomorphization from generating too many identical methods.
.push(syn::Error::new_spanned(obj.into_token_stream(), msg));
}

/// Add one of Syn's parse errors.
#[allow(unused)]
pub fn syn_error(&self, err: syn::Error) {
self.errors.borrow_mut().as_mut().unwrap().push(err);
}

/// Consume this object, producing a formatted error string if there are
/// errors.
pub fn check(self) -> Result<(), TokenStream> {
let errors = self.errors.borrow_mut().take().unwrap();
match errors.len() {
0 => Ok(()),
_ => Err(to_compile_errors(errors)),
}
}
}

fn to_compile_errors(errors: Vec<syn::Error>) -> proc_macro2::TokenStream {
let compile_errors = errors.iter().map(syn::Error::to_compile_error);
quote!(#(#compile_errors)*)
}

impl Drop for Ctxt {
fn drop(&mut self) {
if !thread::panicking() && self.errors.borrow().is_some() {
panic!("forgot to check for errors");
}
}
}

pub fn main() -> TokenStream {
let (hal_crate, hal_crate_name) = crate::get_hal_crate();

let executor = match hal_crate {
Ok(FoundCrate::Itself) => {
quote!( #hal_crate_name::embassy::executor::Executor )
}
Ok(FoundCrate::Name(ref name)) => {
let ident = Ident::new(&name, Span::call_site().into());
quote!( #ident::embassy::executor::Executor )
}
Err(_) => {
quote!(crate::embassy::executor::Executor)
}
};

quote! {
#[entry]
fn main() -> ! {
let mut executor = #executor::new();
let executor = unsafe { __make_static(&mut executor) };
executor.run(|spawner| {
spawner.must_spawn(__embassy_main(spawner));
})
}
}
}
}
Loading

0 comments on commit a0ebdf0

Please sign in to comment.