rust-embedded / rust-embedded/cortex-m

New static mutable storage for the entry function

Open
#616 3 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Dominant language
Rust
Stars
1k
Forks
202
Avg merge
6d 2h
Merged PRs (30d)
2

Description

We carefully ensure the #[entry] function cannot be called other than after a reset (and after the start-up code has initialised everything).

Currently we use this knowledge to 'safely' replace static mut FOO: T = T::new() with code that uses an unsafe block to create &mut T reference to the static, which is passed in as an argument.

That is:

#[cortex_m_rt::entry]
fn main() -> ! {
    static mut FOO: u32 = 123;

    loop {}
}

becomes:

#[doc(hidden)]
#[export_name = "main"]
pub unsafe extern "C" fn __cortex_m_rt_main_trampoline() {
    #[allow(static_mut_refs)]
    __cortex_m_rt_main({
        static mut FOO: u32 = 123;
        unsafe { &mut FOO }
    })
}
fn __cortex_m_rt_main(#[allow(non_snake_case)] FOO: &'static mut u32) -> ! {
    loop {}
}

It is widely accepted that this kind of sleight of hand is not good. However, RTIC shows us a syntax that could work:

#[task(local = [state: u32 = 0])]
async fn foo(c: foo::Context) {
    let old_state: u32 = *c.local.state;
    *c.local.state += 1;
}

So, what if we wrote a new macro, called entry_with_context:

#[cortex_m_rt::entry_with_context(context = [state: u32 = 0])]
fn main(c: main::Context) -> ! {
    let old_state: u32 = *c.state;
    *c.state += 1;

    loop {}
}

The advantage here is that is clear some 'magic' is happening to create a static resource and pass it to the main function.

It would expand to something like:

mod main {
    pub(crate) struct Context {
        state: &mut u32
    }
}

#[doc(hidden)]
#[export_name = "main"]
pub unsafe extern "C" fn __cortex_m_rt_main_trampoline() {
    static STATE: RacyCell<u32> = RacyCell::new(123);
    __cortex_m_rt_main(main::Context {
        state: unsafe { STATE.get_mut_ref() },
    })
}

fn main(c: main::Context) -> ! {
    let old_state: u32 = *c.state;
    *c.state += 1;

    loop {}
}

We don't even need to hide the actual fn main() function anymore, because you cannot call it without creating the context object.

Contributor guide

No contributing guide indexed for this repository

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start by reviewing the existing #[entry] macro behavior and the proposed entry_with_context syntax, then compare the RTIC local-resource example described in the issue. Trace how the current static mut expansion reaches the entry function and define the desired context API and expansion; done means the design is agreed and its safety and compatibility implications are documented.

Written by the indexing model from the issue text.

Assessment

Tech stack
rust
Domain
embedded-iot
Issue type
Feature
Difficulty
5/5
Estimated time
Over a week
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
35/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.