#micro-controller #flash #memory

no-std emcell

Reserve flash memory regions to safely keep multiple binary and library binaries (cells) on a microcontroller. The cells can call functions from other cells.

4 releases

0.0.3 Sep 10, 2024
0.0.2 Aug 21, 2024
0.0.1 May 29, 2024
0.0.0 May 28, 2024

#337 in Embedded development

MIT license

19KB
330 lines

emcell

emcell (EMbedded CELL) - is a library, which make it very easy to keep several binaries on a single microcontroller. You just need to create a separate crate with cells definitions and create simple build.rs, specifying which cell is a current crate for.

Cell is an abstract word for a binary or library, which have a specific region of FLASH and RAM memory assigned. You can keep several cells on a single microcontroller, and even define header for each of them to cross-call different functions.

Emcell also allow you to define a special function with signature fn() -> !, which will perform vector table switch. For example, you can define function run() -> ! in your main code, and run it from bootloader cell.

Usage

Example of lib.rs for cells definitions crate:

#![no_std]

#[macro_use]
extern crate emcell_macro;

emcell_configuration! {
    device!{
        initial_stack_ptr: 0x2000_6000,

        ram_range_start: 0x2000_0000,
        ram_range_end: 0x2001_8000, // 96Kb RAM

        flash_range_start: 0x0800_0000,
        flash_range_end: 0x0810_0000, // 1Mb flash
    }

    #[cell(primary)]
    #[ram_region(0x6000, 0x6400)]
    #[flash_region(0x0, 0x4000)]
    pub struct Cell1 {
    }

    #[cell]
    #[ram_region(0x6400, 0xA000)]
    #[flash_region(0x0_4000, 0xF_1000)]
    pub struct Cell2 {
        #[switch_vectors]
        pub run: fn() -> !,
        pub a: u32,
        pub print_some_value: fn(u32),
    }

    #[cell]
    #[ram_region(0xA000, 0x1_0000)]
    #[flash_region(0xF_1000, 0x10_0000)]
    pub struct Cell3 {
        pub b: u32,
        pub run_some_code: fn(),
        pub access_static: fn() -> u32,
    }
}

In this example you define 3 cells: Cell1, Cell2 and Cell3. Each of them have separate crate and can call functions from other crates using safe wrapper, which is created automatically with macro.

Example of main.rs for cell1 crate:

#![no_std]
#![no_main]

#![feature(const_refs_to_static)]
use emcell_macro::{define_primary_header, extern_header_forward};
use cells_defs::{Cell1, Cell2};
use cortex_m::asm::delay;

extern crate panic_halt;

define_primary_header!{
    Cell1 {
    }
}

extern_header_forward!(Cell2Wrapper: Cell2);

#[cortex_m_rt::entry]
unsafe fn main() -> ! {
    gpio_cfgr();
    led_on();

    if let Some(cell2) = Cell2Wrapper::new() {
        cell2.switch_vectors_and_run() // execute run() -> ! for cell2
    }
    else {
        loop {
            delay(1_000_000);
        }
    }
}

build.rs:

fn main() {
    emcell::build_rs::<cells_defs::Cell1>();
}

Cell2Wrapper::new() is created automatically and perform additional checks to ensure, that header for cell2 was not modified (by comparing hash) and is compatible with current crate.

Nightly toolchain

Currently, emcell requires nightly because of const_refs_to_static feature. You can use rustup override set nightly to set nightly for the current directory.

Dependencies

~135KB