RISC-V support over CLINT (#815)

* Rebase to master

* using interrupt_mod

* bug fixes

* fix other backends

* Add changelog

* forgot about rtic-macros

* backend-specific configuration

* core peripherals optional over macro argument

* pre_init_preprocessing binding

* CI for RISC-V (WIP)

* separation of concerns

* add targets for RISC-V examples

* remove qemu feature

* prepare examples folder

* move examples all together

* move ci out of examples

* minor changes

* add cortex-m

* new xtask: proof of concept

* fix build.yml

* feature typo

* clean rtic examples

* reproduce weird issue

* remove unsafe code in user app

* update dependencies

* allow builds on riscv32imc

* let's fix QEMU

* Update .github/workflows/build.yml

Co-authored-by: Henrik Tjäder <henrik@tjaders.com>

* New build.rs

* removing test features

* adapt ui test to new version of clippy

* add more examples to RISC-V backend

* proper configuration of heapless for riscv32imc

* opt-out examples for riscv32imc

* point to new version of riscv-slic

* adapt new macro bindings

* adapt examples and CI to stable

* fix cortex-m CI

* Review

---------

Co-authored-by: Henrik Tjäder <henrik@tjaders.com>
This commit is contained in:
Román Cárdenas Rodríguez 2024-03-20 21:06:47 +01:00 committed by GitHub
parent 22ac33a826
commit 4060c3def8
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
166 changed files with 2322 additions and 315 deletions

View file

@ -11,6 +11,7 @@ For each category, *Added*, *Changed*, *Fixed* add new entries at the top!
### Added
- Unstable support for RISC-V targets compatible with `riscv-slic`
- RTIC v2 now works on stable.
- Unstable ESP32-C3 support.

View file

@ -36,8 +36,8 @@ cortex-m-basepri = []
riscv-esp32c3 = []
# riscv-clic = []
# riscv-ch32 = []
riscv-slic = []
# backend API test
test-template = []

View file

@ -8,6 +8,7 @@ pub mod bindings;
mod assertions;
mod async_dispatchers;
mod extra_mods;
mod hardware_tasks;
mod idle;
mod init;

View file

@ -2,7 +2,7 @@ use crate::syntax::ast::App;
use crate::{
analyze::Analysis,
codegen::{
bindings::{async_entry, handler_config, interrupt_entry, interrupt_exit},
bindings::{async_entry, handler_config, interrupt_entry, interrupt_exit, interrupt_mod},
util,
},
};
@ -36,10 +36,9 @@ pub fn codegen(app: &App, analysis: &Analysis) -> TokenStream2 {
};
let pend_interrupt = if level > 0 {
let device = &app.args.device;
let enum_ = util::interrupt_ident();
let int_mod = interrupt_mod(app);
quote!(rtic::export::pend(#device::#enum_::#dispatcher_name);)
quote!(rtic::export::pend(#int_mod::#dispatcher_name);)
} else {
// For 0 priority tasks we don't need to pend anything
quote!()

View file

@ -2,7 +2,8 @@
feature = "cortex-m-source-masking",
feature = "cortex-m-basepri",
feature = "test-template",
feature = "riscv-esp32c3"
feature = "riscv-esp32c3",
feature = "riscv-slic",
)))]
compile_error!("No backend selected");
@ -22,4 +23,10 @@ mod template;
pub use esp32c3::*;
#[cfg(feature = "riscv-esp32c3")]
mod esp32c3;
mod esp32c3;
#[cfg(feature = "riscv-slic")]
pub use riscv_slic::*;
#[cfg(feature = "riscv-slic")]
mod riscv_slic;

View file

@ -35,6 +35,12 @@ pub fn interrupt_ident() -> Ident {
Ident::new("interrupt", span)
}
pub fn interrupt_mod(app: &App) -> TokenStream2 {
let device = &app.args.device;
let interrupt = interrupt_ident();
quote!(#device::#interrupt)
}
pub fn check_stack_overflow_before_init(
_app: &App,
_analysis: &CodegenAnalysis,
@ -199,12 +205,16 @@ mod basepri {
}
}
pub fn pre_init_preprocessing(_app: &mut App, _analysis: &SyntaxAnalysis) -> parse::Result<()> {
Ok(())
}
pub fn pre_init_checks(app: &App, _: &SyntaxAnalysis) -> Vec<TokenStream2> {
let mut stmts = vec![];
// check that all dispatchers exists in the `Interrupt` enumeration regardless of whether
// they are used or not
let interrupt = util::interrupt_ident();
let interrupt = interrupt_ident();
let rt_err = util::rt_err_ident();
for name in app.args.dispatchers.keys() {
@ -217,7 +227,7 @@ pub fn pre_init_checks(app: &App, _: &SyntaxAnalysis) -> Vec<TokenStream2> {
pub fn pre_init_enable_interrupts(app: &App, analysis: &CodegenAnalysis) -> Vec<TokenStream2> {
let mut stmts = vec![];
let interrupt = util::interrupt_ident();
let interrupt = interrupt_ident();
let rt_err = util::rt_err_ident();
let device = &app.args.device;
let nvic_prio_bits = quote!(#device::NVIC_PRIO_BITS);
@ -381,3 +391,7 @@ pub fn handler_config(
) -> Vec<TokenStream2> {
vec![]
}
pub fn extra_modules(_app: &App, _analysis: &SyntaxAnalysis) -> Vec<TokenStream2> {
vec![]
}

View file

@ -55,10 +55,20 @@ mod esp32c3 {
Ident::new("Interrupt", span)
}
pub fn interrupt_mod(app: &App) -> TokenStream2 {
let device = &app.args.device;
let interrupt = interrupt_ident();
quote!(#device::#interrupt)
}
pub fn extra_assertions(_: &App, _: &SyntaxAnalysis) -> Vec<TokenStream2> {
vec![]
}
pub fn pre_init_preprocessing(_app: &mut App, _analysis: &SyntaxAnalysis) -> parse::Result<()> {
Ok(())
}
pub fn pre_init_checks(app: &App, _: &SyntaxAnalysis) -> Vec<TokenStream2> {
let mut stmts = vec![];
// check that all dispatchers exists in the `Interrupt` enumeration regardless of whether
@ -232,3 +242,7 @@ mod esp32c3 {
stmts
}
}
pub fn extra_modules(_app: &App, _analysis: &SyntaxAnalysis) -> Vec<TokenStream2> {
vec![]
}

View file

@ -0,0 +1,255 @@
use crate::{
analyze::Analysis as CodegenAnalysis,
syntax::{
analyze::Analysis as SyntaxAnalysis,
ast::{App, Dispatcher},
},
};
use proc_macro2::{Span, TokenStream as TokenStream2};
use quote::quote;
use std::{collections::HashSet, vec};
use syn::{parse, Attribute, Ident};
/// Utility function to get the SLIC interrupt module.
pub fn interrupt_ident() -> Ident {
let span = Span::call_site();
Ident::new("Interrupt", span)
}
pub fn interrupt_mod(_app: &App) -> TokenStream2 {
let interrupt = interrupt_ident();
quote!(slic::#interrupt)
}
/// This macro implements the [`rtic::Mutex`] trait for shared resources using the SLIC.
#[allow(clippy::too_many_arguments)]
pub fn impl_mutex(
_app: &App,
_analysis: &CodegenAnalysis,
cfgs: &[Attribute],
resources_prefix: bool,
name: &Ident,
ty: &TokenStream2,
ceiling: u8,
ptr: &TokenStream2,
) -> TokenStream2 {
let path = if resources_prefix {
quote!(shared_resources::#name)
} else {
quote!(#name)
};
quote!(
#(#cfgs)*
impl<'a> rtic::Mutex for #path<'a> {
type T = #ty;
#[inline(always)]
fn lock<RTIC_INTERNAL_R>(&mut self, f: impl FnOnce(&mut #ty) -> RTIC_INTERNAL_R) -> RTIC_INTERNAL_R {
const CEILING: u8 = #ceiling;
unsafe {
rtic::export::lock(#ptr, CEILING, f)
}
}
}
)
}
/// This macro is used to define additional compile-time assertions in case the platform needs it.
/// The Cortex-M implementations do not use it. Thus, we think we do not need it either.
pub fn extra_assertions(_app: &App, _analysis: &SyntaxAnalysis) -> Vec<TokenStream2> {
vec![]
}
pub fn pre_init_preprocessing(app: &mut App, _analysis: &SyntaxAnalysis) -> parse::Result<()> {
app.args.core = false; // RISC-V SLIC is not compatible with using core peripherals
if !app.args.dispatchers.is_empty() {
return Err(parse::Error::new(
Span::call_site(),
"this backend does not support explicit interrupt dispatchers; remove the `dispatchers` argument from `#[app]`",
));
}
// Compute the number of handlers we need to dispatch the software tasks
let soft_priorities = app
.software_tasks
.iter()
.map(|(_, task)| task.args.priority)
.filter(|prio| *prio > 0)
.collect::<HashSet<_>>();
for i in 0..soft_priorities.len() {
let dispatcher_ident = Ident::new(&format!("__RTICDispatcher{}", i), Span::call_site());
app.args
.dispatchers
.insert(dispatcher_ident, Dispatcher { attrs: vec![] });
}
Ok(())
}
/// This macro is used to check at run-time that all the interruption dispatchers exist.
pub fn pre_init_checks(app: &App, _analysis: &SyntaxAnalysis) -> Vec<TokenStream2> {
let mut stmts: Vec<TokenStream2> = vec![];
let int_mod = interrupt_mod(app);
// check that all dispatchers exists in the `slic::Interrupt` enumeration
for name in app.args.dispatchers.keys() {
stmts.push(quote!(let _ = #int_mod::#name;));
}
stmts
}
/// This macro must perform all the required operations to activate the
/// interrupt sources with their corresponding priority level.
pub fn pre_init_enable_interrupts(app: &App, analysis: &CodegenAnalysis) -> Vec<TokenStream2> {
let mut stmts = vec![];
// First, we reset and disable all the interrupt controllers
stmts.push(quote!(rtic::export::clear_interrupts();));
// Then, we set the corresponding priorities
let interrupt_ids = analysis.interrupts.iter().map(|(p, (id, _))| (p, id));
for (&p, name) in interrupt_ids.chain(
app.hardware_tasks
.values()
.map(|task| (&task.args.priority, &task.args.binds)),
) {
stmts.push(quote!(
rtic::export::set_priority(slic::Interrupt::#name, #p);
));
}
// Finally, we activate the interrupts
stmts.push(quote!(rtic::export::set_interrupts();));
stmts
}
/// Any additional checks that depend on the system architecture.
pub fn architecture_specific_analysis(app: &App, _analysis: &SyntaxAnalysis) -> parse::Result<()> {
// Check that there are enough external interrupts to dispatch the software tasks and the timer queue handler
let mut first = None;
let priorities = app
.software_tasks
.iter()
.map(|(name, task)| {
first = Some(name);
task.args.priority
})
.filter(|prio| *prio > 0)
.collect::<HashSet<_>>();
let need = priorities.len();
let given = app.args.dispatchers.len();
if need > given {
let s = {
format!(
"not enough interrupts to dispatch \
all software tasks (need: {need}; given: {given})"
)
};
return Err(parse::Error::new(first.unwrap().span(), s));
}
if app.args.backend.is_none() {
return Err(parse::Error::new(
Span::call_site(),
"SLIC requires backend-specific configuration",
));
}
Ok(())
}
/// Macro to add statements to be executed at the beginning of all the interrupt handlers.
pub fn interrupt_entry(_app: &App, _analysis: &CodegenAnalysis) -> Vec<TokenStream2> {
vec![]
}
/// Macro to add statements to be executed at the end of all the interrupt handlers.
pub fn interrupt_exit(_app: &App, _analysis: &CodegenAnalysis) -> Vec<TokenStream2> {
vec![]
}
pub fn check_stack_overflow_before_init(
_app: &App,
_analysis: &CodegenAnalysis,
) -> Vec<TokenStream2> {
vec![quote!(
// Check for stack overflow using symbols from `risc-v-rt`.
extern "C" {
pub static _stack_start: u32;
pub static _ebss: u32;
}
let stack_start = &_stack_start as *const _ as u32;
let ebss = &_ebss as *const _ as u32;
if stack_start > ebss {
// No flip-link usage, check the SP for overflow.
if rtic::export::read_sp() <= ebss {
panic!("Stack overflow after allocating executors");
}
}
)]
}
pub fn async_entry(
_app: &App,
_analysis: &CodegenAnalysis,
_dispatcher_name: Ident,
) -> Vec<TokenStream2> {
vec![]
}
/// Macro to define a maximum priority level for async tasks.
pub fn async_prio_limit(_app: &App, analysis: &CodegenAnalysis) -> Vec<TokenStream2> {
let max = if let Some(max) = analysis.max_async_prio {
quote!(#max)
} else {
quote!(u8::MAX) // No limit
};
vec![quote!(
/// Holds the maximum priority level for use by async HAL drivers.
#[no_mangle]
static RTIC_ASYNC_MAX_LOGICAL_PRIO: u8 = #max;
)]
}
pub fn handler_config(
_app: &App,
_analysis: &CodegenAnalysis,
_dispatcher_name: Ident,
) -> Vec<TokenStream2> {
vec![]
}
/// The SLIC requires us to call to the [`riscv_rtic::codegen`] macro to generate
/// the appropriate SLIC structure, interrupt enumerations, etc.
pub fn extra_modules(app: &App, _analysis: &SyntaxAnalysis) -> Vec<TokenStream2> {
let mut stmts = vec![];
let hw_slice: Vec<_> = app
.hardware_tasks
.values()
.map(|task| &task.args.binds)
.collect();
let sw_slice: Vec<_> = app.args.dispatchers.keys().collect();
let swi_slice: Vec<_> = hw_slice.iter().chain(sw_slice.iter()).collect();
let device = &app.args.device;
stmts.push(quote!(
use rtic::export::riscv_slic;
));
let hart_id = &app.args.backend.as_ref().unwrap().hart_id;
stmts.push(quote!(rtic::export::codegen!(pac = #device, swi = [#(#swi_slice,)*], backend = [hart_id = #hart_id]);));
stmts
}

View file

@ -6,40 +6,55 @@ use proc_macro2::TokenStream as TokenStream2;
use quote::quote;
use syn::{parse, Attribute, Ident};
pub fn interrupt_ident() -> Ident {
let span = Span::call_site();
Ident::new("interrupt", span)
}
pub fn interrupt_mod(app: &App) -> TokenStream2 {
let device = &app.args.device;
let interrupt = interrupt_ident();
quote!(#device::#interrupt)
}
pub fn impl_mutex(
_app: &App,
_analysis: &CodegenAnalysis,
_cfgs: &[Attribute],
_resources_prefix: bool,
_name: &Ident,
_ty: &TokenStream2,
_ceiling: u8,
_ptr: &TokenStream2,
app: &App,
analysis: &CodegenAnalysis,
cfgs: &[Attribute],
resources_prefix: bool,
name: &Ident,
ty: &TokenStream2,
ceiling: u8,
ptr: &TokenStream2,
) -> TokenStream2 {
quote!()
}
pub fn extra_assertions(_app: &App, _analysis: &SyntaxAnalysis) -> Vec<TokenStream2> {
pub fn extra_assertions(app: &App, analysis: &SyntaxAnalysis) -> Vec<TokenStream2> {
vec![]
}
pub fn pre_init_checks(_app: &App, _analysis: &SyntaxAnalysis) -> Vec<TokenStream2> {
vec![]
}
pub fn pre_init_enable_interrupts(_app: &App, _analysis: &CodegenAnalysis) -> Vec<TokenStream2> {
vec![]
}
pub fn architecture_specific_analysis(_app: &App, _analysis: &SyntaxAnalysis) -> parse::Result<()> {
pub fn pre_init_preprocessing(app: &mut App, analysis: &SyntaxAnalysis) -> parse::Result<()> {
Ok(())
}
pub fn interrupt_entry(_app: &App, _analysis: &CodegenAnalysis) -> Vec<TokenStream2> {
pub fn pre_init_checks(app: &App, analysis: &SyntaxAnalysis) -> Vec<TokenStream2> {
vec![]
}
pub fn interrupt_exit(_app: &App, _analysis: &CodegenAnalysis) -> Vec<TokenStream2> {
pub fn pre_init_enable_interrupts(app: &App, analysis: &CodegenAnalysis) -> Vec<TokenStream2> {
vec![]
}
pub fn architecture_specific_analysis(app: &App, analysis: &SyntaxAnalysis) -> parse::Result<()> {
Ok(())
}
pub fn interrupt_entry(app: &App, analysis: &CodegenAnalysis) -> Vec<TokenStream2> {
vec![]
}
pub fn interrupt_exit(app: &App, analysis: &CodegenAnalysis) -> Vec<TokenStream2> {
vec![]
}
@ -51,20 +66,25 @@ pub fn check_stack_overflow_before_init(
}
pub fn async_entry(
_app: &App,
_analysis: &CodegenAnalysis,
_dispatcher_name: Ident,
) -> Vec<TokenStream2> {
vec![]
}
pub fn async_prio_limit(app: &App, _analysis: &CodegenAnalysis) -> Vec<TokenStream2> {
vec![]
}
pub fn handler_config(
_app: &App,
_analysis: &CodegenAnalysis,
app: &App,
analysis: &CodegenAnalysis,
dispatcher_name: Ident,
) -> Vec<TokenStream2> {
vec![]
}
pub fn async_prio_limit(app: &App, analysis: &CodegenAnalysis) -> Vec<TokenStream2> {
vec![]
}
pub fn handler_config(
app: &App,
analysis: &CodegenAnalysis,
dispatcher_name: Ident,
) -> Vec<TokenStream2> {
vec![]
}
pub fn extra_modules(app: &App, analysis: &SyntaxAnalysis) -> Vec<TokenStream2> {
vec![]
}

View file

@ -0,0 +1,9 @@
use super::bindings::extra_modules;
use crate::analyze::Analysis;
use crate::syntax::ast::App;
use proc_macro2::TokenStream as TokenStream2;
/// Generates code that runs before `#[init]`
pub fn codegen(app: &App, analysis: &Analysis) -> Vec<TokenStream2> {
extra_modules(app, analysis)
}

View file

@ -1,4 +1,3 @@
use super::{assertions, post_init, pre_init};
use crate::{
analyze::Analysis,
codegen::{bindings, util},
@ -7,8 +6,12 @@ use crate::{
use proc_macro2::TokenStream as TokenStream2;
use quote::quote;
use super::{assertions, extra_mods, post_init, pre_init};
/// Generates code for `fn main`
pub fn codegen(app: &App, analysis: &Analysis) -> TokenStream2 {
let extra_mods_stmts = extra_mods::codegen(app, analysis);
let assertion_stmts = assertions::codegen(app, analysis);
let pre_init_stmts = pre_init::codegen(app, analysis);
@ -40,9 +43,18 @@ pub fn codegen(app: &App, analysis: &Analysis) -> TokenStream2 {
let main = util::suffixed("main");
let init_name = &app.init.name;
let init_args = if app.args.core {
quote!(core.into(), executors_size)
} else {
quote!(executors_size)
};
let msp_check = bindings::check_stack_overflow_before_init(app, analysis);
quote!(
#(#extra_mods_stmts)*
#[doc(hidden)]
#[no_mangle]
unsafe extern "C" fn #main() -> ! {
@ -63,7 +75,7 @@ pub fn codegen(app: &App, analysis: &Analysis) -> TokenStream2 {
// Wrap late_init_stmts in a function to ensure that stack space is reclaimed.
__rtic_init_resources(||{
let (shared_resources, local_resources) = #init_name(#init_name::Context::new(core.into(), executors_size));
let (shared_resources, local_resources) = #init_name(#init_name::Context::new(#init_args));
#(#post_init_stmts)*
});

View file

@ -1,5 +1,5 @@
use crate::syntax::{ast::App, Context};
use crate::{analyze::Analysis, codegen::util};
use crate::{analyze::Analysis, codegen::bindings::interrupt_mod, codegen::util};
use proc_macro2::TokenStream as TokenStream2;
use quote::quote;
@ -16,16 +16,20 @@ pub fn codegen(ctxt: Context, app: &App, analysis: &Analysis) -> TokenStream2 {
match ctxt {
Context::Init => {
fields.push(quote!(
/// Core peripherals
pub core: rtic::export::Peripherals
));
fields.push(quote!(
/// The space used to allocate async executors in bytes.
pub executors_size: usize
));
if app.args.core {
fields.push(quote!(
/// Core peripherals
pub core: rtic::export::Peripherals
));
values.push(quote!(core: core));
}
if app.args.peripherals {
let device = &app.args.device;
@ -43,8 +47,6 @@ pub fn codegen(ctxt: Context, app: &App, analysis: &Analysis) -> TokenStream2 {
));
values.push(quote!(cs: rtic::export::CriticalSection::new()));
values.push(quote!(core));
values.push(quote!(executors_size));
}
@ -98,7 +100,11 @@ pub fn codegen(ctxt: Context, app: &App, analysis: &Analysis) -> TokenStream2 {
};
let core = if ctxt.is_init() {
Some(quote!(core: rtic::export::Peripherals, executors_size: usize))
if app.args.core {
Some(quote!(core: rtic::export::Peripherals, executors_size: usize))
} else {
Some(quote!(executors_size: usize))
}
} else {
None
};
@ -144,10 +150,9 @@ pub fn codegen(ctxt: Context, app: &App, analysis: &Analysis) -> TokenStream2 {
task_cfgs = cfgs.clone();
let pend_interrupt = if priority > 0 {
let device = &app.args.device;
let enum_ = util::interrupt_ident();
let int_mod = interrupt_mod(app);
let interrupt = &analysis.interrupts.get(&priority).expect("UREACHABLE").0;
quote!(rtic::export::pend(#device::#enum_::#interrupt);)
quote!(rtic::export::pend(#int_mod::#interrupt);)
} else {
quote!()
};

View file

@ -11,10 +11,12 @@ pub fn codegen(app: &App, analysis: &Analysis) -> Vec<TokenStream2> {
// Disable interrupts -- `init` must run with interrupts disabled
stmts.push(quote!(rtic::export::interrupt::disable();));
stmts.push(quote!(
// To set the variable in cortex_m so the peripherals cannot be taken multiple times
let mut core: rtic::export::Peripherals = rtic::export::Peripherals::steal().into();
));
if app.args.core {
stmts.push(quote!(
// To set the variable in cortex_m so the peripherals cannot be taken multiple times
let mut core: rtic::export::Peripherals = rtic::export::Peripherals::steal().into();
));
}
stmts.append(&mut pre_init_checks(app, analysis));

View file

@ -3,8 +3,6 @@ use core::sync::atomic::{AtomicUsize, Ordering};
use proc_macro2::{Span, TokenStream as TokenStream2};
use quote::quote;
use syn::{Ident, PatType};
//hook the target specific interrupt_ident function
pub use super::bindings::interrupt_ident;
const RTIC_INTERNAL: &str = "__rtic_internal";

View file

@ -14,13 +14,14 @@ macro_rules! with_backend {
feature = "cortex-m-source-masking",
feature = "cortex-m-basepri",
feature = "test-template",
feature = "riscv-esp32c3"
feature = "riscv-esp32c3",
feature = "riscv-slic",
))]
$($tokens)*
};
}
with_backend! { mod: [analyze, check, codegen, syntax] }
with_backend! { mod: [analyze, check, codegen, preprocess, syntax] }
with_backend! { use std::{fs, env, path::Path}; }
with_backend! { use proc_macro::TokenStream; }
@ -47,11 +48,18 @@ with_backend! {
/// Should never panic, cargo feeds a path which is later converted to a string
#[proc_macro_attribute]
pub fn app(_args: TokenStream, _input: TokenStream) -> TokenStream {
let (app, analysis) = match syntax::parse(_args, _input) {
let (mut app, analysis) = match syntax::parse(_args, _input) {
Err(e) => return e.to_compile_error().into(),
Ok(x) => x,
};
// Modify app based on backend before continuing
if let Err(e) = preprocess::app(&mut app, &analysis) {
return e.to_compile_error().into();
}
let app = app;
// App is not mutable after this point
if let Err(e) = check::app(&app, &analysis) {
return e.to_compile_error().into();
}
@ -109,6 +117,7 @@ with_backend! {
feature = "cortex-m-source-masking",
feature = "cortex-m-basepri",
feature = "test-template",
feature = "riscv-esp32c3"
feature = "riscv-esp32c3",
feature = "riscv-slic",
)))]
compile_error!("Cannot compile. No backend feature selected.");

View file

@ -0,0 +1,7 @@
use crate::codegen::bindings::pre_init_preprocessing;
use crate::syntax::{analyze::Analysis, ast::App};
use syn::parse;
pub fn app(app: &mut App, analysis: &Analysis) -> parse::Result<()> {
pre_init_preprocessing(app, analysis)
}

View file

@ -12,6 +12,7 @@ use crate::syntax::ast::App;
mod accessors;
pub mod analyze;
pub mod ast;
mod backend;
mod check;
mod parse;

View file

@ -2,7 +2,7 @@
use syn::{Attribute, Expr, Ident, Item, ItemUse, Pat, PatType, Path, Stmt, Type};
use crate::syntax::Map;
use crate::syntax::{backend::BackendArgs, Map};
/// The `#[app]` attribute
#[derive(Debug)]
@ -60,11 +60,17 @@ pub struct AppArgs {
/// Device
pub device: Path,
/// Peripherals
/// Core peripherals
pub core: bool,
/// Device peripherals
pub peripherals: bool,
/// Interrupts used to dispatch software tasks
pub dispatchers: Dispatchers,
/// Backend-specific arguments
pub backend: Option<BackendArgs>,
}
/// The `init`-ialization function

View file

@ -0,0 +1,32 @@
#[cfg(not(any(
feature = "cortex-m-source-masking",
feature = "cortex-m-basepri",
feature = "test-template",
feature = "riscv-esp32c3",
feature = "riscv-slic",
)))]
compile_error!("No backend selected");
#[cfg(any(feature = "cortex-m-source-masking", feature = "cortex-m-basepri"))]
pub use cortex::*;
#[cfg(feature = "test-template")]
pub use template::*;
#[cfg(feature = "riscv-esp32c3")]
pub use esp32c3::*;
#[cfg(feature = "riscv-slic")]
pub use riscv_slic::*;
#[cfg(any(feature = "cortex-m-source-masking", feature = "cortex-m-basepri"))]
mod cortex;
#[cfg(feature = "test-template")]
mod template;
#[cfg(feature = "riscv-esp32c3")]
mod esp32c3;
#[cfg(feature = "riscv-slic")]
mod riscv_slic;

View file

@ -0,0 +1,16 @@
use syn::{
parse::{Parse, ParseStream},
Error, Result,
};
#[derive(Debug)]
pub struct BackendArgs();
impl Parse for BackendArgs {
fn parse(input: ParseStream) -> Result<Self> {
Err(Error::new(
input.span(),
"cortex backend does not accept any arguments",
))
}
}

View file

@ -0,0 +1,16 @@
use syn::{
parse::{Parse, ParseStream},
Error, Result,
};
#[derive(Debug)]
pub struct BackendArgs();
impl Parse for BackendArgs {
fn parse(input: ParseStream) -> Result<Self> {
Err(Error::new(
input.span(),
"esp32c3 backend does not accept any arguments",
))
}
}

View file

@ -0,0 +1,16 @@
use syn::{
parse::{Parse, ParseStream},
Ident, Result,
};
#[derive(Debug)]
pub struct BackendArgs {
pub hart_id: Ident,
}
impl Parse for BackendArgs {
fn parse(input: ParseStream) -> Result<Self> {
let hart_id = input.parse()?;
Ok(BackendArgs { hart_id })
}
}

View file

@ -0,0 +1,15 @@
use syn::{
parse::{Parse, ParseStream},
Result,
};
#[derive(Debug)]
pub struct BackendArgs {
// Define your backend-specific input here
}
impl Parse for BackendArgs {
fn parse(input: ParseStream) -> Result<Self> {
todo!("define how to parse your backend-specific arguments")
}
}

View file

@ -13,6 +13,7 @@ use crate::syntax::{
App, AppArgs, Dispatcher, Dispatchers, HardwareTask, Idle, IdleArgs, Init, InitArgs,
LocalResource, SharedResource, SoftwareTask,
},
backend::BackendArgs,
parse::{self as syntax_parse, util},
Either, Map, Set,
};
@ -24,8 +25,10 @@ impl AppArgs {
(|input: ParseStream<'_>| -> parse::Result<Self> {
let mut custom = Set::new();
let mut device = None;
let mut core = true;
let mut peripherals = true;
let mut dispatchers = Dispatchers::new();
let mut backend = None;
loop {
if input.is_empty() {
@ -59,6 +62,17 @@ impl AppArgs {
}
}
"core" => {
if let Ok(p) = input.parse::<LitBool>() {
core = p.value;
} else {
return Err(parse::Error::new(
ident.span(),
"unexpected argument value; this should be a boolean",
));
}
}
"peripherals" => {
if let Ok(p) = input.parse::<LitBool>() {
peripherals = p.value;
@ -113,6 +127,18 @@ impl AppArgs {
));
}
}
"backend" => {
if let Ok(p) = input.parse::<BackendArgs>() {
backend = Some(p);
} else {
return Err(parse::Error::new(
ident.span(),
"unable to parse backend configuration",
));
}
}
_ => {
return Err(parse::Error::new(ident.span(), "unexpected argument"));
}
@ -134,8 +160,10 @@ impl AppArgs {
Ok(AppArgs {
device,
core,
peripherals,
dispatchers,
backend,
})
})
.parse2(tokens)