Skip to content
Snippets Groups Projects
Select Git revision
  • 904e666497a89b4bfb2bf7b2392b8ff2afe4c99b
  • master default protected
  • exam
  • exper
  • klee
  • simple
  • v0.3.2
  • v0.3.1
  • v0.3.0
  • v0.2.2
  • v0.2.1
  • v0.2.0
  • v0.1.1
  • v0.1.0
14 results

lib.rs

Blame
  • lib.rs 5.95 KiB
    //! Procedural macros of the `cortex-m-rtfm` crate
    //#![deny(warnings)]
    #![feature(proc_macro)]
    #![recursion_limit = "128"]
    
    #[macro_use]
    extern crate error_chain;
    extern crate proc_macro;
    #[macro_use]
    extern crate quote;
    extern crate rtfm_syntax as syntax;
    extern crate syn;
    
    use std::fs::File;
    use std::io::Write;
    use std::path::Path;
    
    use proc_macro::TokenStream;
    use syntax::App;
    use syntax::error::*;
    
    mod analyze;
    mod check;
    mod trans;
    
    /// The `app!` macro, a macro used to specify the tasks and resources of a RTFM application.
    ///
    /// The contents of this macro uses a `key: value` syntax. All the possible keys are shown below:
    ///
    /// ``` text
    /// app! {
    ///     device: ..,
    ///
    ///     resources: { .. },
    ///
    ///     init: { .. },
    ///
    ///     idle: { .. },
    ///
    ///     tasks: { .. },
    /// }
    /// ```
    ///
    /// # `device`
    ///
    /// The value of this key is a Rust path, like `foo::bar::baz`, that must point to a *device crate*,
    /// a crate generated using `svd2rust`.
    ///
    /// # `resources`
    ///
    /// This key is optional. Its value is a list of `static` variables. These variables are the data
    /// that can be safely accessed, modified and shared by tasks.
    ///
    /// ``` text
    /// resources: {
    ///     static A: bool = false;
    ///     static B: i32 = 0;
    ///     static C: [u8; 16] = [0; 16];
    ///     static D: Thing = Thing::new(..);
    ///     static E: Thing;
    /// }
    /// ```
    ///
    /// The initial value of a resource can be omitted. This means that the resource will be runtime
    /// initialized; these runtime initialized resources are also known as *late resources*.
    ///
    /// If this key is omitted its value defaults to an empty list.
    ///
    /// # `init`
    ///
    /// This key is optional. Its value is a set of key values. All the possible keys are shown below:
    ///
    /// ``` text
    /// init: {
    ///     path: ..,
    /// }
    /// ```
    ///
    /// ## `init.path`
    ///
    /// This key is optional. Its value is a Rust path, like `foo::bar::baz`, that points to the
    /// initialization function.
    ///
    /// If the key is omitted its value defaults to `init`.
    ///
    /// ## `init.resources`
    ///
    /// This key is optional. Its value is a set of resources the `init` function *owns*. The resources
    /// in this list must be a subset of the resources listed in the top `resources` key. Note that some
    /// restrictions apply:
    ///
    /// - The resources in this list can't be late resources.
    /// - The resources that appear in this list can't appear in other list like `idle.resources` or
    /// `tasks.$TASK.resources`
    ///
    /// If this key is omitted its value is assumed to be an empty list.
    ///
    /// # `idle`
    ///
    /// This key is optional. Its value is a set of key values. All the possible keys are shown below:
    ///
    /// ``` text
    /// idle: {
    ///     path: ..,
    ///     resources: [..],
    /// }
    /// ```
    ///
    /// ## `idle.path`
    ///
    /// This key is optional. Its value is a Rust path, like `foo::bar::baz`, that points to the idle
    /// loop function.
    ///
    /// If the key is omitted its value defaults to `idle`.
    ///
    /// ## `idle.resources`
    ///
    /// This key is optional. Its value is a list of resources the `idle` loop has access to. The
    /// resources in this list must be a subset of the resources listed in the top `resources` key.
    ///
    /// If omitted its value defaults to an empty list.
    ///
    /// # `tasks`
    ///
    /// This key is optional. Its value is a list of tasks. Each task itself is a set of key value pair.
    /// The full syntax is shown below:
    ///
    /// ``` text
    /// tasks: {
    ///     $TASK: {
    ///         enabled: ..,
    ///         path: ..,
    ///         priority: ..,
    ///         resources: [..],
    ///     },
    /// }
    /// ```
    ///
    /// If this key is omitted its value is assumed to be an empty list.
    ///
    /// ## `tasks.$TASK`
    ///
    /// The key must be either a Cortex-M exception or a device specific interrupt. `PENDSV`, `SVCALL`,
    /// `SYS_TICK` are considered as exceptions. All other names are assumed to be interrupts.
    ///
    /// ## `tasks.$TASK.enabled`
    ///
    /// This key is optional for interrupts and forbidden for exceptions. Its value must be a boolean
    /// and indicates whether the interrupt will be enabled (`true`) or disabled (`false`) after `init`
    /// ends and before `idle` starts.
    ///
    /// If this key is omitted its value defaults to `true`.
    ///
    /// ## `tasks.$TASK.path`
    ///
    /// The value of this key is a Rust path, like `foo::bar::baz`, that points to the handler of this
    /// task.
    ///
    /// ## `tasks.$TASK.priority`
    ///
    /// This key is optional. Its value is an integer with type `u8` that specifies the priority of this
    /// task. The minimum valid priority is 1. The maximum valid priority depends on the number of the
    /// NVIC priority bits the device has; if the device has 4 priority bits the maximum allowed value
    /// would be 16.
    ///
    /// If this key is omitted its value defaults to `1`.
    ///
    /// ## `tasks.$TASK.resources`
    ///
    /// This key is optional. Its value is a list of resources this task has access to. The resources in
    /// this list must be a subset of the resources listed in the top `resources` key.
    ///
    /// If omitted its value defaults to an empty list.
    #[proc_macro]
    pub fn app(ts: TokenStream) -> TokenStream {
        match run(ts) {
            Err(e) => panic!("{}", error_chain::ChainedError::display(&e)),
            Ok(ts) => ts,
        }
    }
    
    fn run(ts: TokenStream) -> Result<TokenStream> {
        let input = format!("{}", ts);
    
        let app = App::parse(&input).chain_err(|| "parsing")?;
        let app = syntax::check::app(app).chain_err(|| "checking the AST")?;
        let app = check::app(app)?;
    
        let ownerships = analyze::app(&app);
        let tokens = trans::app(&app, &ownerships);
    
        if cfg!(feature = "klee_mode") {
            println!("tasks");
            let mut tasks = Vec::new();
            for (id, task) in app.tasks {
                println!("{}", id);
                tasks.push(format!("{} {} {}", id, task.priority, task.interarrival));
            }
    
            let path = Path::new("klee/tasks.txt");
    
            let mut file = File::create(path).unwrap();
            write!(file, "// autogenerated file\n{:?}", tasks).unwrap();
        }
        Ok(format!("{}", tokens)
            .parse()
            .map_err(|_| "BUG: error parsing the generated code")?)
    }