Giter Site home page Giter Site logo

cortex-m's People

Contributors

adamgreig avatar bors[bot] avatar cbiffle avatar david-oconnor avatar diondokter avatar dirbaio avatar disasm avatar homunkulus avatar hug-dev avatar jannic avatar japaric avatar joe1994 avatar jonas-schievink avatar jordens avatar kellerkindt avatar kjetilkjeka avatar korken89 avatar m-ou-se avatar newam avatar ovidiusabou avatar qwerty19106 avatar sekineh avatar stianeklund avatar tdholmes avatar thejpster avatar therealprof avatar tiwalun avatar tmplt avatar whitequark avatar yodaldevoid avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

cortex-m's Issues

Support Secure Mode for Armv8-m (e.g. Secture Interrupt Vector Table)

TrustZone in Armv8-m processors like the Cortex-M23 and Cortex-M33 supports a second 'secure' copy of registers like SysTick and VTOR. It also supports a second interrupt vector table.

I'd like to add support, but it should be behind a feature flag as not all Cortex-M23/33 implementations support Secure Mode.

References:

Unable to run cargo doc over project.

I am using this crate indirectly from the stm32f1 crate when I try to document that library I'm getting an error with cortex-m.

cargo doc -p stm32f1 --open

Documenting cortex-m v0.5.2 error:[::task::Poll]` cannot be resolved, ignoring it...
|
note: lint level defined here
--> /Users/billsb/.cargo/registry/src/github.com-1ecc6299db9ec823/cortex-m-0.5.2/src/lib.rs:42:9
|
42 | #![deny(warnings)]
| ^^^^^^^^
= note: #[deny(intra_doc_link_resolution_failure)] implied by #[deny(warnings)]
= note: the link appears in this line:

        [`Poll::Pending`](::task::Poll). The future will *also* register the
                          ^^^^^^^^^^^^

= help: to escape [ and ] characters, just add '' before them like \[ or \]

error: [::task::Context::waker] cannot be resolved, ignoring it...
|
= note: the link appears in this line:

        [`cx.waker()`](::task::Context::waker)). Once a task has been woken up,
                       ^^^^^^^^^^^^^^^^^^^^^^

= help: to escape [ and ] characters, just add '' before them like \[ or \]

error: Could not document cortex-m.`

Peripheral definitions in cortex-m are not `Send`, while those from `svd2rust` generated crates are

Comparing something like:

Notably:

  • Send is omitted from cortex_m::ITM
  • mutable access (in the form of ptr() -> *mut RegisterBlock and DerefMut are removed from stm32f103xx::TIM6

I ran into this while trying to pass around ITM in cortex_m_rtfm, which wants Send on resources:

error[E0277]: the trait bound `*const (): core::marker::Send` is not satisfied in `cortex_m::peripheral::ITM`
  --> src/main.rs:16:1
   |
16 | / app! {
17 | |     device: stm32f429,
18 | |
19 | |     resources: {
...  |
29 | |     },
30 | | }
   | |_^ `*const ()` cannot be sent between threads safely
   |
   = help: within `cortex_m::peripheral::ITM`, the trait `core::marker::Send` is not implemented for `*const ()`
   = note: required because it appears within the type `core::marker::PhantomData<*const ()>`
   = note: required because it appears within the type `cortex_m::peripheral::ITM`

I've used a quick wrapper to workaround:

...
use core::ops::{Deref,DerefMut};
pub struct ITM(cortex_m::peripheral::ITM);

impl Deref for ITM {
    type Target = cortex_m::peripheral::ITM;
    fn deref(&self) -> &Self::Target {
        &self.0
    }
}

impl DerefMut for ITM {
    fn deref_mut(&mut self) -> &mut Self::Target {
        &mut self.0
    }
}
unsafe impl Send for ITM {}

...
app! {
...
    resources: {
        static R_ITM: ITM;
        ...
    },

    tasks: {
        TIM7: {
            path: periodic,
            resources: [R_ITM, ...],
        },
    },
}

...
fn init(mut p: init::Peripherals) -> init::LateResources {
    ...

    init::LateResources {
        R_ITM: ITM(p.core.ITM),
        R_TIM7: tim,
    }
}

...

SCB missing VTOR on Cortex-M0+

The SCB definition uses cfg(not(armv6m)) to choose whether or not to expose the VTOR field on non-armv6m platforms. However, Cortex-M0+ has a VTOR field: http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.dui0662b/CIHFDJCA.html

Since Cortex-M0 doesn't have a VTOR field in its SCB block I'm not sure what the correct solution here is. How do you feel about a feature that enables M0+ registers? It's not as elegant as architecture detection through the target.

RFC: Even higher-level cache maintenance operations

#40 landed CMSIS-style DCache cleaning and invalidating by address and size, both usize. This maps well to the actual hardware operations but is inconvenient to use.

We could add some convenience methods that operate on normal Rust objects:

  • clean_dcache_by_slice<T>(slice: &[T]) which passes slice.as_ptr() as usize and core::mem::size_of<T>() * slice.len() to the underlying methods,
  • clean_dcache_by_ref<T>(obj: &T), which passes T as *const _ as usize and core::mem::size_of<T>() to the underlying methods.
  • As above for invalidate_dcache and clean_invalidate_dcache

Big safety issue: all the cache methods operate on cache lines of 32 bytes. If you pass something whose size is not a multiple of 32, then some extra bytes after that object will be cleaned or invalidated. In general this is definitely unsafe. Perhaps these methods could panic on non-32-byte-multiple sizes?

SCB missing FPSCR, FPCAR, FPCCR

I see the CPACR in scb, but I do not see the Floating-point Status and Control Register (FPSCR), Floating-point Context Address Register (FPCAR), or Floating-point Context Control Register (FPCCR). AFAIK this is only an optional feature with Cortex-M4 and Cortex-M7 and should be included along with the CPACR behind the has_fpu cfg, but I could be mistaken. See the User Guide for more info.

cortex_m::asm::delay() doesn't seem to work on thumbv6m

While attempting to create an example for rust-embedded/wg#259 to blink an LED using cortex_m::asm::delay I'm greeted with this interesting error:

#![no_main]
#![no_std]

extern crate cortex_m;
extern crate cortex_m_rt;
extern crate panic_halt;

extern crate stm32f042_hal as hal;

use hal::prelude::*;
use hal::stm32;

use cortex_m::asm::delay;
use cortex_m_rt::entry;

#[entry]
fn main() -> ! {
    let p = stm32::Peripherals::take().unwrap();
    let gpioa = p.GPIOA.split();
    let mut led = gpioa.pa1.into_push_pull_output();

    loop {
        led.set_high();
        delay(8_000_000);

        led.set_low();
        delay(8_000_000);
    }
}
error: linking with `rust-lld` failed: exit code: 1
  |
  = note: "rust-lld" "-flavor" "gnu" "-L" "/Users/egger/.rustup/toolchains/stable-x86_64-apple-darwin/lib/rustlib/thumbv6m-none-eabi/lib" "/Users/egger/OSS/stm32f042-hal/target/thumbv6m-none-eabi/release/examples/blinky-b437c53f5b36f500.blinky.60cthedq-cgu.1.rcgu.o" "-o" "/Users/egger/OSS/stm32f042-hal/target/thumbv6m-none-eabi/release/examples/blinky-b437c53f5b36f500" "--gc-sections" "-L" "/Users/egger/OSS/stm32f042-hal/target/thumbv6m-none-eabi/release/deps" "-L" "/Users/egger/OSS/stm32f042-hal/target/release/deps" "-L" "/Users/egger/OSS/stm32f042-hal/target/thumbv6m-none-eabi/release/build/cortex-m-563ed60a5fde6333/out" "-L" "/Users/egger/OSS/stm32f042-hal/target/thumbv6m-none-eabi/release/build/cortex-m-rt-414ccd4438657f1e/out" "-L" "/Users/egger/OSS/stm32f042-hal/target/thumbv6m-none-eabi/release/build/stm32f0-f3baf49fdf34c49d/out" "-L" "/Users/egger/.rustup/toolchains/stable-x86_64-apple-darwin/lib/rustlib/thumbv6m-none-eabi/lib" "--start-group" "-Bstatic" "/var/folders/8f/ch8vbtq93ldfc4vksdp5cw540000gn/T/rustcjqym5j/libcortex_m_rt-f3ac26fff634eea2.rlib" "/var/folders/8f/ch8vbtq93ldfc4vksdp5cw540000gn/T/rustcjqym5j/libcortex_m-945aa0e87bd515a7.rlib" "--end-group" "/Users/egger/.rustup/toolchains/stable-x86_64-apple-darwin/lib/rustlib/thumbv6m-none-eabi/lib/libcompiler_builtins-962f36023deecec7.rlib" "-Tlink.x" "-Bdynamic"
  = note: rust-lld: error: /var/folders/8f/ch8vbtq93ldfc4vksdp5cw540000gn/T/rustcjqym5j/libcortex_m-945aa0e87bd515a7.rlib(cortex-m.o):(.text.__delay+0x4): unrecognized reloc 103

peripheral: "safer" API like the one svd2rust generates?

right now you can write any u32 in the peripherals' registers. This is bad because you may write 1s to reserved bits that are supposed to always be set to 0.

svd2rust generates a "safer" API that prevents this kind of problem. But it generates the API from SVD files and I don't if there are SVD files for these core peripherals.

Should we manually implement an API like that? Or, perhaps, should we be more conservative and just provide bit masks (as consts -- think const SCB_CPACR_CP10: u32 = 0b11 << 20) to begin with? (the bit masks would save you the trouble of having to look into the documentation for the right bit offset of each bit field but they are not fool proof as you can still write to reserved bits)

cc @thejpster

[RFC] `#[uninit]` for truly uninitialized `static` variables

Summary

The #[uninit] attribute will place static [mut] variables into an .uninit
section, located in RAM, that won't be initialized before main.

Motivation

Today, uninitialized (see MaybeUninit) static variables will be placed in
.bss, which means that'll be zeroed before main. This leads to unnecessary
work as leaving the memory uninitialized was the intended behavior.

Design

The #[uninit] attribute will have the following syntax.

#[uninit]
static mut FOO: u32 = {};

The initial value of uninit variables must be the placeholder {}. This
attribute will expand into:

#[link_section = ".uninit"]
static mut FOO: core::mem::MaybeUninit<u32> = core::mem::MaybeUninit::new();

#[uninit] composes with #[entry] and #[exception]; it can be used on
safe local static mut variables.

#[entry]
fn main() -> ! {
    #[uninit]
    static mut KEY: [u32; 8] = {};

    // ..

    // the name of this MaybeUninit method hasn't been decided but it writes
    // a value into the MaybeUninit and then returns a reference to the written
    // value
    let key: &'static mut [u32; 8] = KEY.insert(/* runtime value */);

    // ..
}

Implementation

Before this can be implemented, MaybeUninit must first land in the core
library. This feature will live behind a "nightly" (Cargo) feature until
MaybeUninit and its const constructor are stabilized.

Alternative syntax

Option A

#[uninit]
static mut FOO: u32 = ();

Option B

#[uninit]
static mut FOO: u32 = ..;

We can't omit the RHS of static mut because then the code won't parse and the
compiler will never reach the macro expansion phase.

Drawbacks

This is not a perfect solution.

For example, a heapless vector contains an uninitialized buffer when it's
constructed using the new method but it also contains a length field that must
be initialized to zero. Applying #[uninit] to such data structure means that
the vector will have to be initialized (assign 0 to its length field) at
runtime, which is not ergonomic. Not using #[uninit] means that the vector
length and its buffer will be zeroed, which is wasteful.

It's not possible to have partial initialization of static variables so this
is as good as it gets.

Doesn't support JSON targets

If you supply a target as a JSON file, the build.rs can't detect the target. This is because target is a full path - in my case starts this starts C:\, which != thumbv.

I think the fix is to take the file_stem() of the path and use that. This should work for both JSON targets and built-in targets (that don't contain .). If the built-in target does use . (e.g. thumbv8-m.base) we'll need to be a bit cleverer.

This is the same as rust-embedded/cortex-m-rt#145.

should we add `std::sync` like primitives based on critical sections?

See the mutex branch for an implementation of a mutex based on critical sections. And this PR for an example that uses that mutex to share state between the main thread and an interrupt.

The advantage of implementing them using critical sections is smaller code size, faster locking and that they don't require the LDREX or STREX instructions which are not available in the Cortex-M0(+).

If you have and can afford atomics then you should probably use the parking_lot crate.

cc @thejpster

Move generic device stuff out of cortex-m

via rust-embedded/svd2rust#117

Peripheral, and maybe some other structs, aren't really Cortex-M specific. svd2rust currently relies on this crate just for that struct, but if it generates, say, some Rust code for a Cortex-A target instead, it still needs Peripheral but nothing else.

First thought that comes to mind would be to move Peripheral (and friends?) out into a separate crate, and have svd2rust and cortex-m rely on that generic crate instead. But, I'm sure there are many ways to do it.

Thoughts?

Enhancing the default handler

I was looking into the default handler implementation and found some (seemingly) low-hanging fruit. I wanted to get your opinion before I made any changes though.

  1. I think the trampoline should test link register to determine if the exception frame is referenced in MSP or PSP. I've used this code in the past.
  2. I think the default_handler! macro should include the trampoline and call the specified function with the exception frame as the argument. This would allow me to define my own handler without having to reinvent the trampoline, but it would break backward compatibility.

BASEPRI and FAULTMASK break the standalone build on armv6m

PS C:\Users\david\Devel\cortex-m> cat .\build.ps1
$env:RUSTFLAGS="-Z linker-flavor=ld.lld -C linker=ld.lld.exe"
$env:CC="clang"
$env:AR="llvm-ar"
cargo build --target=thumbv6m-none-eabi --release
PS C:\Users\david\Devel\cortex-m> .\build.ps1
   Compiling cortex-m v0.5.2 (file:///C:/Users/david/Devel/cortex-m)
error: failed to run custom build command for `cortex-m v0.5.2 (file:///C:/Users/david/Devel/cortex-m)`
process didn't exit successfully: `C:\Users\david\Devel\cortex-m\target\release\build\cortex-m-d6e8f84c8a15b15f\build-script-build` (exit code: 101)
--- stdout
TARGET = Some("thumbv6m-none-eabi")
OPT_LEVEL = Some("3")
TARGET = Some("thumbv6m-none-eabi")
HOST = Some("x86_64-pc-windows-msvc")
TARGET = Some("thumbv6m-none-eabi")
TARGET = Some("thumbv6m-none-eabi")
HOST = Some("x86_64-pc-windows-msvc")
CC_thumbv6m-none-eabi = None
CC_thumbv6m_none_eabi = None
TARGET_CC = None
CC = Some("clang")
TARGET = Some("thumbv6m-none-eabi")
HOST = Some("x86_64-pc-windows-msvc")
CFLAGS_thumbv6m-none-eabi = None
CFLAGS_thumbv6m_none_eabi = None
TARGET_CFLAGS = None
CFLAGS = None
DEBUG = Some("false")
running: "clang" "-O3" "-ffunction-sections" "-fdata-sections" "-fPIC" "--target=thumbv6m-none-eabi" "-Wall" "-Wextra" "-o" "C:\\Users\\david\\Devel\\cortex-m\\target\\thumbv6m-none-eabi\\release\\build\\cortex-m-9e47ce484a32dbc3\\out\\asm\\basepri_r.o" "-c" "asm/basepri_r.s"
cargo:warning=clang.exe: warning: argument unused during compilation: '-ffunction-sections' [-Wunused-command-line-argument]
cargo:warning=clang.exe: warning: argument unused during compilation: '-fdata-sections' [-Wunused-command-line-argument]
cargo:warning=asm/basepri_r.s:3:11: error: invalid operand for instruction
cargo:warning=  mrs r0, BASEPRI
cargo:warning=          ^
exit code: 1

--- stderr
thread 'main' panicked at '

Internal error occurred: Command "clang" "-O3" "-ffunction-sections" "-fdata-sections" "-fPIC" "--target=thumbv6m-none-eabi" "-Wall" "-Wextra" "-o" "C:\\Users\\david\\Devel\\cortex-m\\target\\thumbv6m-none-eabi\\release\\build\\cortex-m-9e47ce484a32dbc3\\out\\asm\\basepri_r.o" "-c" "asm/basepri_r.s" with args "clang" did not execute successfully (status code exit code: 1).

', C:\Users\david\.cargo\registry\src\github.com-1ecc6299db9ec823\cc-1.0.18\src\lib.rs:2181:5
note: Run with `RUST_BACKTRACE=1` for a backtrace.

Allow setting exception return value in exceptions

As far as I could tell the API does not currently allow setting the exception return value (EXC_RETURN) in exception handlers, which would be useful for implementing context switching etc.

Could we add an extra case to the #[exception] macro to also accept something like:

#[exception]
fn SysTick(exc: ExceptionReturn) -> ExceptionReturn

Where:

pub enum ExceptionReturn {
    HandlerMsp,
    HandlerFpuMsp,
    ThreadMsp,
    ....
}

This enum can then be used under the hood to set the correct value for LR/PC upon exception return.

I'd be happy to further experiment with this idea and turn it into a pull request.

[RFC] [breaking-change] don't depend on GCC by default

It's very likely that the thumb targets will soon switch to rust-lld as their default linker (see
rust-embedded/wg#160). If we want to achieve the goal of not depending on GCC, by default, to
build Cortex-M applications then we also have to make core crates like this one not depend on GCC by
default. This RFC is exactly about that.

I propose that in the next minor (breaking) release, v0.6.0, we tweak the meaning of the existing
"asm" Cargo feature and that we add a new Cargo feature named "inline-asm". The relationship between
these two features would be as follows:

  • If "asm" is disabled the cortex_m::asm module will not be present. The crate will not depend
    on GCC and will compile on stable / beta in this case. The state of the "inline-asm" feature will
    be ignored in this scenario.

  • If "asm" is enabled and "inline-asm" is disabled then the cortex_m::asm module will be
    present and it will be implemented using external assembly files. The crate will depend on GCC
    and will compile on stable / beta in this scenario.

  • If "asm" is enabled and "inline-asm" is also enabled then the cortex_m::asm module will be
    present and it will be implemented using inline assembly (i.e. asm!). In this scenario, the
    crate will depend on GCC and will require a nightly compiler to build.

Extensions

When the Cortex-M API in core::arch::arm becomes stabilized we'll expose a subset of the
cortex_m::asm module (see #101) in the scenario where the "asm" feature is disabled. Remember
that when this feature is disabled the crate will not depend on GCC or on a nightly compiler.

Alternatives

  • We could emit a compiler error (see compile_error!) when both "asm" and "inline-asm" are enabled.

Unresolved questions

  • Should the "inline-asm" feature, instead, be named "nightly"?

cc @rust-embedded/cortex-m @hannobraun @crawford

Fails to build: can't find crate for `compiler_builtins`

On OSX:

rustc -V

rustc 1.27.0-nightly (ad610bed8 2018-04-11)

rustup target add thumbv7m-none-eabi

info: downloading component 'rust-std' for 'thumbv7m-none-eabi'
info: installing component 'rust-std' for 'thumbv7m-none-eabi'

cargo build --target thumbv7em-none-eabi

Compiling vcell v0.1.0
Compiling bare-metal v0.1.1
Compiling aligned v0.1.1
Compiling untagged-option v0.1.1
Compiling cortex-m v0.4.3 (file:///[snip]/cortex-m)
error[E0463]: can't find crate for compiler_builtins
error[E0463]: can't find crate for compiler_builtins
error[E0463]: can't find crate for compiler_builtins
error[E0463]: can't find crate for compiler_builtins

error: aborting due to previous error

For more information about this error, try rustc --explain E0463.
error: aborting due to previous error
error: aborting due to previous error
error: aborting due to previous error

For more information about this error, try rustc --explain E0463.
For more information about this error, try rustc --explain E0463.
For more information about this error, try rustc --explain E0463.
error: Could not compile aligned.
warning: build failed, waiting for other jobs to finish...
error: Could not compile untagged-option.
warning: build failed, waiting for other jobs to finish...
error: Could not compile vcell.
warning: build failed, waiting for other jobs to finish...
error: Could not compile bare-metal.

What am doing wrong?

Errors in documentation?

Hi, when running cargo doc, the following errors are produced for this package:

 Documenting cortex-m v0.5.2
error: `[::task::Poll]` cannot be resolved, ignoring it...
   |
note: lint level defined here
  --> /home/emifre/.cargo/registry/src/github.com-1ecc6299db9ec823/cortex-m-0.5.2/src/lib.rs:42:9
   |
42 | #![deny(warnings)]
   |         ^^^^^^^^
   = note: #[deny(intra_doc_link_resolution_failure)] implied by #[deny(warnings)]
   = note: the link appears in this line:
           
            [`Poll::Pending`](::task::Poll). The future will *also* register the
                              ^^^^^^^^^^^^
   = help: to escape `[` and `]` characters, just add '\' before them like `\[` or `\]`

error: `[::task::Context::waker]` cannot be resolved, ignoring it...
   |
   = note: the link appears in this line:
           
            [`cx.waker()`](::task::Context::waker)). Once a task has been woken up,
                           ^^^^^^^^^^^^^^^^^^^^^^
   = help: to escape `[` and `]` characters, just add '\' before them like `\[` or `\]`

error: Could not document `cortex-m`.

I hope this helps to fix it :)

feedback on the SYST API

"Some nits about the SYST implementation: fn clear () does not make a whole lot of sense as clearing actually disables the SysTick timer because the exception is triggered when the count changes from 1 -> 0 which is also what causes the reload. Similarly the manuals suggest you should load the counter with the value 1 (for which there's currently no safe API) to ensure that the timer will not start with some offset in the first turn and also the correct value for the reload is actually - 1."

from rust-embedded/cortex-m-quickstart#15 (comment)

cc @whitequark

RFC: `#[late]` statics

Summary

#[late] static are statics that are initialized at runtime, before the entry
point is called and after RAM initialization happens.

Motivation

Consider the allocator example from the quickstart template:

#[global_allocator]
static ALLOCATOR: CortexMHeap = CortexMHeap::empty();

const HEAP_SIZE: usize = 1024; // in bytes

#[entry]
fn main() -> ! {
    // Initialize the allocator BEFORE you use it
    unsafe { ALLOCATOR.init(cortex_m_rt::heap_start() as usize, HEAP_SIZE) }

    let xs = vec![0, 1, 2];

    // ..
}

Quite a few things can go wrong there: you could forget to initialize the
allocator or you could initialize it too late. In either case you end up with
OOM (abort).

This example can be simplified using a #[late] static:

#[late]
#[global_allocator]
static ALLOCATOR: SyncAllocator = unsafe {
    static mut BYTES: [u8; 1024] = [0; 1024];

    // you are familiar with this transformation
    let bytes: &'static mut [u8] = BYTES;

    // `SyncAllocator` is a newtype over `Mutex<SomeAllocator>`
    SyncAllocator::new(bytes)
}

#[entry]
fn main() -> ! {
    // Growable array allocated on the heap
    let xs = vec![0, 1, 2];

    // ..
}

Design

#[late] statics are not lazy statics. Lazy statics are initialized on
first use, they have runtime cost on each access and memory overhead. #[late]
statics are statics that are initialized before the #[entry] function is
called, accessing them is zero cost and they have no memory overhead.

The above example expands to something like this:

// For details about the `.uninint` section see RFC rust-embedded/cortex-m#398
#[link_section = ".uninit.<random-hash>"]
#[global_allocator]
static ALLOCATOR: Late<SyncAllocator> = {
    #[link_section = ".late.<random-hash>"] // new linker section
    static INITIALIZER: unsafe fn() = initialize;

    unsafe fn initialize() {
        let val = {
            // (proc-macro transformation)
            let BYTES: &'static mut _ = {
                static mut BYTES: [u8; 1024] = [0; 1024];

                &mut BYTES
            };

            // (user code)
            // you are familiar with this transformation
            let bytes: &'static mut [u8] = BYTES;

            // `SyncAllocator` is a newtype over `Mutex<Allocator>`
            SyncAllocator::new(bytes)
        };

        ALLOCATOR.initialize(val);
    }

    Late::uninitialized()
};

The Late newtype will be provided by this crate:

/// IMPLEMENTATION DETAIL. DO NOT USE
#[doc(hidden)]
pub struct Late<T> {
    inner: UnsafeCell<MaybeUninit<T>>,
}

unsafe impl<T> Sync for Late<T> where T: Sync {}

impl<T> Late<T> {
    /// IMPLEMENTATION DETAIL. DO NOT USE
    #[doc(hidden)]
    pub const fn uninitialized() -> Self {
        Late {
            inner: UnsafeCell::new(MaybeUninit::uninitialized()),
        }
    }

    /// IMPLEMENTATION DETAIL. DO NOT USE
    #[doc(hidden)]
    pub unsafe fn initialize(&self, value: T) {
        (*self.inner.get()).set(value)
    }
}

impl<T> Deref for Late<T> {
    type Target = T;

    fn deref(&self) -> &T {
        unsafe { (*self.inner.get()).get_ref() }
    }
}

impl<T> DerefMut for Late<T> {
    fn deref_mut(&mut self) -> &mut T {
        unsafe { (*self.inner.get()).get_mut() }
    }
}

As you can see Deref and DerefMut perform no runtime checks. However,
dereferencing is only valid after Late has been initialize-d.

#[late] statics are initialized before #[entry] / main and after RAM
initialization using r0::run_init_array or its equivalent.

fn Reset() -> ! {
    __pre_init();

    r0::zero_bss(&mut __sbss, &mut __ebss);
    r0::init_data(&mut __sdata, &mut __edata, & __sidata);
    r0::run_init_array(&__slate, &__elate);

    main()
}

#[global_allocator]

To compose with the built-in global_allocator attribute we need to provide
the implementation below in this crate.

unsafe impl<T> GlobalAlloc for Late<T>
where
    T: GlobalAlloc,
{
    unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
        T::alloc(self.deref(), layout)
    }

    unsafe fn dealloc(&self, p: *mut u8, layout: Layout) {
        T::dealloc(self.deref(), p, layout)
    }
}

Why unsafe?

Using normal statics within a #[late] static initializer is sound, but using
any #[late] static within the initializer is not because there are no
guarantees about the execution order of #[late] initializers.

Furthermore for global resources like global_allocator trying to use the
resource before it's initialized is also undefined behavior.

#[late]
#[global_allocator]
static ALLOCATOR: SyncAllocator = unsafe {
    static mut BYTES: [u8; 1024] = [0; 1024];

    let xs = vec![0, 1, 2]; // undefined behavior
    ALLOCATOR.alloc(..); // also undefined behavior

    SyncAllocator::new(BYTES)
}

It's not possible to prevent all these issues at compile time so unsafe must
be used. Arguably, we could prevent the first issue by making the static
unnameable (by assigning it a randomly generated name) but this would greatly
reduce the flexibility of #[late] statics.


Thoughts? Are there use cases other than global allocators?

New cortex-m-rt doesn't work with Cargo "rename-dependency" feature

In 2018 edition instead of extern crate cortex_m_rt as rt one would use Cargo rename-dependency:

[dependencies]
rt = {version = "0.6.2", package='cortex-m-rt'}

cortex_m_rt_macros, however, doesn't respect this and still generates code with extern crate cortex_m_rt.

stdout for the exception handler

Currently if and exception or a non-implemented interrupt triggered the default_handler function called. In case if no debugger connected it appears as freeze. This can be way more friendly by adding output to std-out as the configurable feature:
extern "C" fn handler(_sr: &StackedRegisters) -> ! {
#[cfg(exception_print = "stdout")]
{
println!("EXCEPTION {:?} @ PC=0x{:08x} LR=0x{:08x} XPSR=0x{:08x}", Exception::current(), _sr.pc, _sr.lr, _sr.xpsr);
println!("R0=0x{:08x} R1=0x{:08x} R2=0x{:08x} R3=0x{:08x} R12=0x{:08x}", _sr.r0, _sr.r1, _sr.r2, _sr.r3, _sr.r12);
}
or similar.

Doesn't work with `CARGO_INCREMENTAL`.

It took me a long while to debug why linker script behaves erratically and sometimes compiles right, but other times complaints either about the lack of exception handlers or too many of them if I attempt to add them. Turns out, all it took is to disable CARGO_INCREMENTAL environment variable.

NVIC's IPR registers are broken on ARMv6-M

The IPR registers are modelled as an array of byte-sized registers. I believe this is fine on ARMv7-M, as the ARMv7-M Architecture Reference Manual (link requires registration), section B.3.4.9 states:

The registers are byte, aligned halfword, and word accessible.

However, the situation is different on ARMv6-M. The ARMv6-M Architecture Reference Manual (link requires registration), section B3.4.7 states:

Subject to standard PPB usage constraints, see General rules for PPB register accesses on page B3-260.

Which states (section B3.1.1):

The PPB address space only supports aligned word accesses. Byte and halfword access is UNPREDICTABLE.

This aligns with my own observation, that setting interrupt priority on an ARM Cortex-M0+ via NVIC::set_priority doesn't seem to have any effect.

I think the right way to fix this is to model the ipr field as an array of 32-bit registers, which should be compatible with both architectures. I should be able to put together a pull request by tomorrow, or next week at the latest.

Provide storage section for writable flash memory

This may be getting a bit too complex to call "minimal", but to support applications that require some permanent storage there needs to be a section defined for that. This probably requires adding the flash page size into memory.x, then having a section something like

.storage BLOCK(_page_size) (NOLOAD) :
{
    FILL(0xFF)
    *(.storage .storage.*)
    . = ALIGN(_page_size);
} > FLASH

If possible, aligning in between each .storage.* section would be wonderful to allow having separately erasable sections (although, I don't know any way to do so).

(Note, I have only used flash storage on the nrf51, I assume other cortex-m devices behave similarly, but this may not be general enough).

consider enabling interrupts in reset handler

It's something of a footgun: since the body of main must be protected by cortex_m::interrupt::free whether I want it or not, it's easy to assume that interrupts are enabled outside of that critical section, and forget to add an explicit cortex_m::interrupt::enable(); after it.

It does seem odd when coming from C, but with interrupt safety by default, I can't think of any real downsides...

Should ITM write functions check if a debugger is connected like CMSIS ITM_SendChar ?

I am currently unable to run my application standalone when a debugger is not present. I am suspecting that it is because the TPIU and the ITM port are not enabled by my code. Removing traces fixes my issue.

Would it be a good idea to check if tracing is enabled like ITM_SendChar ?

https://github.com/ARM-software/CMSIS/blob/6891719834ac6ca2a79e8ded00620faffae10ae8/CMSIS/Include/core_cm3.h#L1698

If so, I would like to submit a pull request to short circuit write_all if tracing is not enabled.

Add API for controlling SLEEPDEEP bit

There's no API for controlling the SLEEPDEEP bit in the SCB's SCR register. It would be great to have a nice API for doing that.

I plan to take a look at this, but I won't be able to any time soon. If anyone wants to pick this up, please go ahead!

Update method signatures to better match scoped singletons

Now that scoped singletons are a thing, it makes sense to change some method signatures from taking &self to taking &mut self. An example of this is the NVIC API. See comments by @japaric in #62.

I plan to take a look at this, but I won't be able to any time soon. If anyone wants to pick this up, go ahead!

Privileged mode

There is currently no support for working in privileged mode.

There should at least be

  • a way to call the svc instruction
    • This can be implemented by cortex_m::asm::svc(num: u32)
  • a way to write thread mode privilege level
    • This can be implemented by cortex_m::register::control::write(Control) and perhaps also something in the lines of cortex_m::register::control::modify(f: F) where f: FnOnce(Control) -> Control

The cortex_m_rt crate seem to already implement the possibility for overriding the SVC handler. So with these changed it should be possible to:

  • Execute the work in need of privileged mode from the svc handler
  • Set the thread mode from the SVC handler and then execute the work after returning from the handler.

Generate SVD during building?

Hi,

So I am very impressed by all these arm crates, and after reading the blog entries wanted to try it out, and see if I can get something blinking on my Teensy Board.

I am wondering about the workflow here. In the Rust your ARM microcontroller! blogpost manual SVD -> Rust crate conversion is described. I was wondering if this could be done automatically. Eg. in build.rs one could call some function to generate the necessary files during the build itself. Does such approach make sense?

BTW. Is there a place like IRC/gitter to hang around to lurk at the discussion around this crate?

Critical section token

What do you think about having a struct like zinc's NoInterrupts struct and passing it into the closure for the interrupt::free function? The idea is to make a zero-sized struct that can't be created by users and is only ever created in a critical section. Then, if a function should only be called in a critical section it can take a reference to that struct which will guarantee that it will only be called in a critical section.

It would look something like this

mod interrupts {
    pub fn disable() {}
    pub fn enable() {}

    pub struct CritToken {
        _private: ()
    }

    pub fn free<F>(f: F)
        where F: FnOnce(&CritToken)
    {
        disable();

        f(&CritToken{ _private: () });

        enable();
    }
}

fn dangerous_func(tok: &interrupts::CritToken) {}

fn calling_dangerous_func() {
    interrupts::free(|tok| {
        dangerous_func(tok);
    });
}

Safe API to enable the ITM

The ITM can easily be enabled using OpenOCD but that requires having a debugger connected to the MCU. ITM can be used for logging or as a communication channel in a production application; in that case the MCU must take care of initializing the ITM. Right now enabling the ITM requires unsafe code; this is what it looks like:

    unsafe {
        // enable TPIU and ITM
        cp.DCB.demcr.modify(|r| r | (1 << 24));

        // prescaler
        let swo_freq = 2_000_000;
        cp.TPIU.acpr.write((clocks.sysclk().0 / swo_freq) - 1);

        // SWO NRZ
        cp.TPIU.sppr.write(2);

        cp.TPIU.ffcr.modify(|r| r & !(1 << 1));

        // STM32 specific: enable tracing in the DBGMCU_CR register
        const DBGMCU_CR: *mut u32 = 0xe0042004 as *mut u32;
        let r = ptr::read_volatile(DBGMCU_CR);
        ptr::write_volatile(DBGMCU_CR, r | (1 << 5));

        // unlock the ITM
        cp.ITM.lar.write(0xC5ACCE55);

        cp.ITM.tcr.write(
            (0b000001 << 16) | // TraceBusID
            (1 << 3) | // enable SWO output
            (1 << 0), // enable the ITM
        );

        // enable stimulus port 0
        cp.ITM.ter[0].write(1);
    }

We should have a safe API to enable the ITM. The API could be an ITM::enable(&mut self, prescaler: u32) method to globally enable the ITM and an ITM::{enable,disable}_port(&mut self, i: u8) to enable / disable an individual port.

Signature of bkpt() function

I was just looking at ways to cheaply and clippy happy "terminate" an #[entry] function and was looking at the cortex_m::asm:bkpt() function noticing that it has a signature of pub fn bkpt(). My expectation would have been that this function has a signature of pub fn bkpt() -> ! instead which would qualify it as a terminating statement in #[entry].

Is there a reason for bkpt() not returning never?

as it is, SCB::set_fpu_access_mode() is a footgun

Let's say FPU is disabled at entry in main() as it usually is, and you want to do some FP operations. Presto!

fn main() {
    cortex_m::interrupt::free(|cs| {
        let scb = tm4c129x::SCB.borrow(cs);
        scb.enable_fpu();

        // some FP-using code
    });
}

... or something like that. Well, not so easy. Since the code below enable_fpu changes callee-save FP registers, LLVM will try to stack those at the top of main... using the vpush instruction. Which is not available until FPU is enabled. Oops.

I don't know how to fix this.

Using semihosting with no debugger attached

Currently it's not possible to run code that contains semihosting calls without having a debugger attached, since semihosting relies on the debugger catching bkpt 0xab and without a debugger, this will trigger an exception and halt the mcu in most cases. I read that some semihosting C runtime provides an exception handler that is able to ignore this exception so that the mcu keeps running even without a debugger attached.
Even though semihosting is not meant to be used without a debugger, it might be useful to be able to run without one. It should at least be mentioned in the docs to make developers aware of it. Otherwise they might wonder why their code stops working when they plug out the debugger.
Unfortunately, this crate can't patch the vector table to catch and ignore the exception automatically, but we could try to find a way to make it trivial for the user to install such a handler (like providing the handler and the user just has to add it to the vector table)

Easy way to transfer code from flash to RAM?

I have an interrupt handler which is called very frequently and due to the CPU running at high speed I have to use wait states which seems to slow down processing quite a bit. Is there any simple way to declare that the IRQ handler should be copied to RAM during initialisation instead of being run from flash?

HardFault in Primask::read

I've been working on making an embedded-hal implementation for the stm32f469 and building up to a board specific crate for the stm32f469i discovery board and have got to the point where I've started trying to flash some LEDs but I encountered a hard fault when executing Primask::read

Here is the project I'm programming onto the board link and below is some output from gdb from when I was stepping through. The fault's probably my own so any help would be appreciated!

Breakpoint 1, leds_on::main::hfa770daafd2bee96 () at examples/leds_on.rs:30
30	    let p = stm32f469xx::Peripherals::take().unwrap();
>>> step
stm32f469xx::Peripherals::take::h6b0a9d4241464a77 ()
    at /home/xd009642/.cargo/registry/src/github.com-1ecc6299db9ec823/stm32f469xx-0.2.1/src/lib.rs:473549
473549	        cortex_m::interrupt::free(|_| {
>>> step
cortex_m::interrupt::free::he28aea6b14b68ad8 (f=...)
    at /home/xd009642/.cargo/registry/src/github.com-1ecc6299db9ec823/cortex-m-0.5.2/src/interrupt.rs:64
64	    let primask = ::register::primask::read();
>>> step
cortex_m::register::primask::read::h1846bf7d2763250f ()
    at /home/xd009642/.cargo/registry/src/github.com-1ecc6299db9ec823/cortex-m-0.5.2/src/register/primask.rs:44
44	                    unsafe { __primask() }
>>> step
HardFault () at asm.s:4
4	  mrs r0, MSP
>>> step
5	  bl UserHardFault
>>> step
UserHardFault (ef=0x2004fef0) at <exception macros>:10
10	<exception macros>: No such file or directory.
>>> step
leds_on::safe_state::hd0df1968d7543a7e (_ef=0x2004fef0) at examples/leds_on.rs:26
26	    loop {}
>>> step

EDIT: Done some peeking and poking of the ARM fault status registers and this hard fault is down to an undefined instruction. These appear to be at the end of the panics but it doesn't appear to hit a panic.

Default handlers get mono-morphized needlessly

Since #17, identical copies of exception::default_handler get included in the binary, thanks to the token generic parameter. It's not a lot of code, but it seems a bit unfortunate. Is there any way around this?

Thanks for all the work you do for embedded Rust, btw! <3

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.