Skip to content

[RFC] Resources initialized at runtime #37

@japaric

Description

@japaric

Motivation

In the current version of RTFM every resource must be assigned an initial value
at compile time and in const / static context. This requirement can sometimes be
too strict given the current limitations of const evaluation (no loops, no
conditionals, etc.).

In other cases one may want to initialize a resource according to the outcome of
the init function. For instance one may want to store the frequency of the
processor, product of the initialization routine, in a variable; in the current
system this requires the programmer to manually keep a copy of the value (as a
literal) in the resources section of the app! macro, which is error prone.

The workaround for these issues is to use an Option for the resource and to
select None as the initial value. Then a value can be assigned to the resource
resource in the init function. The downside of this approach is that it
requires the program to unwrap or match the Option value whenever they
want to access the resource data. And, of course, there's no compile time
guarantee that the programmer won't forget to actually initialize the Option
resource to some value in init; forgetting to do so would cause a panic! at
runtime.

Design

We make the initial value of resources optional in the app! macro:

app! {
    resources: {
        static BUFFER: [u8; 32] = [0; 32];
        static FREQUENCY: u32;
        static FOO: Vec<u8>;
    },
}

When the initial value of any resource is omitted the signature of init will
be changed to force the programmer to assign a value to these resources during
the initialization phase:

fn init(p: init::Peripherals, r: init::Resources) -> init::ResourceValues {
    // .. initialize stuff like the heap allocator ..

    // These are the initial values of the "uninitialized" resources
    init::ResourceValues {
        FREQUENCY: frequency,
        FOO: vec![a, b, c],
    }
}

Apart from this change in init there's no difference between using resources
with different initialization strategies.

What goes on under the hood?

Under the hood the resource without initial values will actually be static
variables with type equal to Option and initialized to None. However, since
we know that the resources will be initialized in init and before any task
(or idle) can run we can expose them to the user without the Option wrapper,
plus we can apply unsafe optimizations like intrinsics::unreachable under the
hood to eliminate branches and avoid unwrapping.

Possible extensions

Optimizing the pre-main initialization of static variables

Since the runtime initialized resources will be represented as static
variables containing an Option they'll still be initialized before init by
the runtime even though those initial values won't be read. This useless
initialization can be optimized away with the help of linker script magic:

// runtime initialized resources
#[link_section = ".uninit"]
static mut FREQUENCY: Option<u32> = None;

#[link_section = ".uninit"]
static mut FOO: Option<Vec<u8>> = None;

// "normal" resources
static mut BUFFER: [u8; 32] = [0; 32];

static variables placed in the .uninit linker section won't be initialized
before init. This may require changes in cortex-m-rt to ensure that static
variables placed in the .uninit section end up having a valid address in the
RAM region though. cf. rust-embedded/cortex-m-rt#32

cc @cr1901

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions