rtic-rs / rtic Goto Github PK
View Code? Open in Web Editor NEWReal-Time Interrupt-driven Concurrency (RTIC) framework for ARM Cortex-M microcontrollers
Home Page: https://rtic.rs
License: Apache License 2.0
Real-Time Interrupt-driven Concurrency (RTIC) framework for ARM Cortex-M microcontrollers
Home Page: https://rtic.rs
License: Apache License 2.0
Hi,
I have been experimenting with a simple RTFM application using embedded-nrf24l01 crate. That crate implements driver for nRF24L01 radio chip using some kind of 'mutate object using self move' semantics:
// create driver in 'standby' state and perform initial configuration
let mut s = NRF24L01::new(ce, cs, spi).unwrap();
s.set_frequency(10).unwrap();
...
// 'move' into Tx state to send packets
let t = s.tx().unwrap()
t.send(&[0x1, 0x2]).unwrap();
// 'move' back into standby state after transmission is acknowledged
let s = t.standby().unwrap()
I had some difficulties with keeping this kind of driver as a resource in RTFM application. So far I could make it work only using RefCell<Option<...>>
aproach:
...
type Standby = ...;
app! {
device: hal::stm32f103xx,
resources: {
static NRF: RefCell<Option<Standby>>;
...
},
...
fn timer_handler(_t: &mut Threshold, mut r: TIM3::Resources) {
let data: [u8; 10] = [0x31; 10];
let s = match r.NRF.replace(None) {
None => panic!("booo"),
Some(s) => {
let mut t = s.tx().unwrap();
t.send(&data).unwrap();
t.standby().unwrap()
}
};
r.NRF.replace(Some(s));
}
Full source is available here. Is there another, more appropriate and elegant way to handle such a driver in RTFM applications ?
Regards,
Sergey
I cannot get anything to link with rustc 1.30.0-nightly (3bc2ca7e4 2018-09-20)
. More specifically the GNU linker fails with:
= note: arm-none-eabi-ld:
BUG(cortex-m-rt): the reset vector is missing
arm-none-eabi-ld:
BUG(cortex-m-rt): the exception vectors are missing
and the rustc lld cannot even find lld, even with llvm-tools-preview-x86_64-unknown-linux-gnu
I would really like try Rust for a project, but it might be better to use C++ instead if I cannot even get my environment setup.
As far as i understand, in existing RTFM approach "tasks" are low-level units (driven by hardware interrupts). That may "conflict" with "normal" architecture design process (from top to bottom), when tasks are business-logic oriented units (because interrupt-driven approach is "from bottom to top").
Everything is great for simple case, when "user actions" and interrupts are equal:
What about more complex cases? Let's consider several samples:
\n
-limited) on UART
Of cause, we can make each RTFM task more and more complex, adding FSM inside and so on. But that still keep us on "from bottom to top" design - difficult and inconvenient for complex things.
Are there some pattern to solve such collision (move from "bottom to top" to "top to bottom" design)? May be virtual tasks/evens or something else...
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.
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.
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 unwrap
ping.
static
variablesSince 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
Currently when you assign a resource to a task the task gains exclusive access
to the resource: it can both claim
and claim_mut
the resource.
This RFC proposes extending the access model to let tasks declare whether they
need exclusive access (&mut-
) or shared access (&-
) to their resources. This
extra information would let the app!
macro optimize access to resources
reducing the number of required critical sections, at least in some scenarios.
We extend, in a backward compatible fashion, the syntax around the resources
array. Today the syntax looks like this: [A, B, C]
; here the task has
exclusive access to the resources A
, B
and C
. The syntax will be extended
to allow specifying shared or exclusive access through the &
and &mut
operators. So, [&A, &mut B, &C]
indicates that the task has shared access to
the resources A
and C
, and exclusive access to the resource B
.
Here's an example:
app! {
resources: {
static COUNTER: u64 = 0;
},
idle: {
resources: [&mut COUNTER],
},
tasks: {
EXTI0: {
enabled: true,
priority: 1,
resources: [&COUNTER],
},
EXTI1: {
enabled: true,
priority: 2,
resources: [&COUNTER],
},
},
}
In this case both tasks, EXTI0
and EXTI1
, get lockless "read access" to
the COUNTER
resource. In today's model exti0
would need a critical section
(claim
) to read data
.
task!(EXTI0, exti0);
fn exti0(_t: &mut Threshold, r: EXTI0::Resources) {
let data: &u64 = r.COUNTER;
}
task!(EXTI1, exti1);
fn exti1(_t: &mut Threshold, r: EXTI1::Resources) {
let data: &u64 = r.COUNTER;
}
Whereas idle
, which has lower priority than both tasks, also gets lockless
"read access" to the resource but requires a critical section (claim_mut
) to
mutate the data.
fn idle(t: &mut Threshold, r: idle::Resources) -> ! {
loop {
{
let data: &u64 = r.COUNTER.deref();
}
r.COUNTER.claim_mut(t, |counter, _t| *counter += 1);
{
let data: &u64 = r.COUNTER.deref();
}
}
}
This new access model imposes a new constraint on the resource data: it must now
implement the Sync
trait. This forbids interior mutability (e.g. Cell
/
RefCell
) which could lead to data races.
If you don't want the Sync
constraint on your resource data you can continue
to use the old syntax with no access (&-
or &mut-
) information.
This is the example shown in the example above. Only the writer needs to lock
the resource to modify it. The writer needs to run at the lowest priority,
though, or this pattern won't reduce the number of critical sections required.
A resource shared by tasks where none needs exclusive access (&mut-
) to it can
be "locklessly" shared (&-
) by all them. IOW, the resource can only be "read"
by the tasks. The only place where one can mutate (&mut-
) the resource is
in init
.
app! {
resources: {
static FREQUENCY: u32 = 0;
},
idle: {
resources: [&FREQUENCY],
}
tasks: {
EXTI0: {
enabled: true,
priority: 1,
resources: [&FREQUENCY],
},
},
}
fn init(_: init::Peripherals, r: init::Resources) {
*r.FREQUENCY = 8_000_000;
}
fn idle(r: init::Resources) {
let f: &u32 = r.FREQUENCY;
}
task!(EXTI0, exti0);
fn exti0(_: &mut Threshold, r: EXTI0::Resources) {
let f: &u32 = r.FREQUENCY;
}
The previous examples assumed that tasks that have shared (&-
) access to a
resource can't modify it. This is not always true; tasks can modify resources
through a shared (&-
) reference under this new system if the mutation is
synchronized.
Atomic types are one of the few types that allow mutation through a shared
reference and implement the Sync
trait. Given these properties you can
already use them safely in static
variables without the help of the RTFM
system. However, combining atomic types with this new access model lets you
restrict which tasks have access to the atomic variables, which may be useful
to prevent logic bugs. With the current, exclusive access only, model you can't
do that because it requires you to lock the resource that contains the atomic
variable when you want to access it from the lower priority tasks.
cc @pftbest this may interest you since you are exploring atomics and lock-less queues.
Hi,
I'm new to rust and the embedded rust eco-system, so forgive me if I've missed a simple initial step which would resolve my issues. I've uploaded a single task RTFM application here:
https://github.com/osterwood/rtfm-app
It should blink an LED on a STM32F042 board I have. It's based on the rtfm-one.rs
example in this repository.
I've run into a compile issue that I don't know how to resolve.
error[E0432]: unresolved import `_initResources`
--> examples/rtfm-one.rs:28:1
|
28 | / app! {
29 | | device: stm32f0::stm32f0x2,
30 | |
31 | | // Here data resources are declared
... |
57 | | }
58 | | }
| |_^ no `_initResources` external crate
error[E0433]: failed to resolve. Could not find `init` in `{{root}}`
--> examples/rtfm-one.rs:28:1
|
28 | / app! {
29 | | device: stm32f0::stm32f0x2,
30 | |
31 | | // Here data resources are declared
... |
57 | | }
58 | | }
| |_^ Could not find `init` in `{{root}}`
error[E0425]: cannot find value `ON` in the crate root
--> examples/rtfm-one.rs:28:1
|
28 | / app! {
29 | | device: stm32f0::stm32f0x2,
30 | |
31 | | // Here data resources are declared
... |
57 | | }
58 | | }
| |_^ not found in the crate root
error: unused import: `stm32f0::stm32f0x2`
--> examples/rtfm-one.rs:25:5
|
25 | use stm32f0::stm32f0x2;
| ^^^^^^^^^^^^^^^^^^
|
note: lint level defined here
--> examples/rtfm-one.rs:3:9
|
3 | #![deny(warnings)]
| ^^^^^^^^
= note: #[deny(unused_imports)] implied by #[deny(warnings)]
error[E0308]: mismatched types
--> examples/rtfm-one.rs:28:1
|
28 | / app! {
29 | | device: stm32f0::stm32f0x2,
30 | |
31 | | // Here data resources are declared
... |
57 | | }
58 | | }
| |_^ expected u32, found u8
help: you can cast an `u8` to `u32`, which will zero-extend the source value
|
28 | app! {
29 | device: stm32f0::stm32f0x2,
30 |
31 | // Here data resources are declared
32 | //
33 | // Data resources are static variables that are safe to share across tasks
...
error: aborting due to 5 previous errors
It seems there are multiple issues with the app!
macro. Most of the examples I've found for RTFM are based on the stm32f103xx and stm32f103_hal crates, not stm32f0. Is there something different about the stm32f0 crate which breaks RTFM?
Thanks in advance for any ideas you have.
I was revisiting the original use case that @pftbest once told me about -- a lock free single
producer single consumer ring buffer -- and that led me to submit #37 and noticed that RTFM still
can't fulfill that use case in 100% safe code. (I hope I'm wrong)
Below is a program that explains the use case:
#![deny(warnings)]
#![feature(proc_macro)]
#![no_std]
extern crate blue_pill;
extern crate cortex_m_rtfm as rtfm;
// https://github.com/japaric/spscrb
extern crate spscrb;
use blue_pill::stm32f103xx::{self, Interrupt};
use rtfm::{app, Threshold};
use spscrb::{Consumer, Producer, RingBuffer};
app! {
device: stm32f103xx,
resources: {
static PRODUCER: Producer<u32, [u32; 32]>;
static CONSUMER: Consumer<u32, [u32; 32]>;
},
tasks: {
EXTI0: {
path: exti0,
resources: [PRODUCER],
priority: 1,
},
EXTI1: {
path: exti1,
resources: [CONSUMER],
priority: 2,
},
},
}
fn init(_p: init::Peripherals) -> init::LateResourceValues {
let rb: &'static mut RingBuffer<u32, [u32; 32]>;
// this part needs to be hidden from the user, or rather the user shouldn't need to do this
rb = {
static mut RB: RingBuffer<u32, [u32; 32]> = RingBuffer::new();
unsafe { &mut RB }
};
// NOTE this method *consumes* a `&'static mut` reference
let (p, c) = rb.spsc();
// let (p2, c2) = rb.spsc();
// ^ this would be an error
init::LateResourceValues { PRODUCER: p, CONSUMER: c }
}
fn idle() -> ! {
rtfm::set_pending(Interrupt::EXTI0);
rtfm::set_pending(Interrupt::EXTI1);
loop {
rtfm::wfi();
}
}
fn exti0(_t: &mut Threshold, r: EXTI0::Resources) {
// lock-free operation
r.PRODUCER.enqueue(0xdead_beef).unwrap();
}
fn exti1(_t: &mut Threshold, r: EXTI1::Resources) {
// lock-free operation
r.CONSUMER.dequeue().unwrap();
}
Basically you have a statically allocated ring buffer which is hidden from the user. To enqueue and
dequeue elements into the buffer you have to use the "producer" and "consumer" end points,
respectively. These end points can be used from different execution contexts, which can run at
different priorities, in a lock free manner. This is memory safe, without locking, because the
producer and the consumer, each, own different cursors into the ring buffer and because the cursors
are word sized (they can be atomically read) -- check the implementation for details.
To construct the producer and consumer there's this spsc
method with signature fn(&'static mut RingBuffer<_, _>) -> ..
. This signature ensures that (a) the ring buffer is statically allocated,
and thus it will never be destroyed, and (b) that once you have called this method the reference to
the ring buffer becomes invalidated (required to avoid mutable aliasing).
Thanks to #43 (thanks @jonas-schievink!) we can defer initialization of resources and initialize the
producer and consumer end points in init
(see example above). However, one problem remains:
there's no way to safely get / create a &'static mut RingBuffer<_, _>
in init
. The only place
where &'static mut -
references are available right now is in idle
but that's too late for
initialization of resources (without wrapping them in Option
).
Here's my proposal for solving this:
Add a roots
field to app!
. This field contains a list of static
variables with initial values.
The syntax of its contents is similar to the contents of resources
.
app! {
roots: {
static RB: RingBuffer<u32, [u32; 32]> = RingBuffer::new();
},
}
The user will get a list of &'static mut-
references to these roots in init
:
// auto-generated: struct Roots { RB: &'static mut RingBuffer<u32, [u32; 32]>, }
fn init(roots: init::Roots, ..) {
let (p, c) = roots.RB.spsc();
// ..
}
This can be implemented by creating a static mut
variable for each root.
// auto-generated
fn main() {
interrupt::free(|_| {
static mut RB: RingBuffer<u32, [u32; 32]> = RingBuffer::new();
init(init::Roots { RB: &mut RB }, ..);
});
}
Alternatively, the roots could be allocated in the lowest / first stack frame (hence "rooting" in
the proposal name), which is known to never be deallocated: (I haven't tested if this works)
// auto-generated
fn main() {
interrupt::free(|_| {
let mut RB: RingBuffer<u32, [u32; 32]> = RingBuffer::new();
let roots = init::Roots { RB: &mut *(&mut RB as *mut _) };
mem::forget(RB); // ???
init(roots, ..);
// ..
});
// idle is a divergent function
idle();
}
Another use case for having &'static mut-
references in init
is being able to set up periodic
DMA transfers in init
. An example of this use case is setting up a circular DMA transfer that
reads ADC or Serial data:
app! {
roots: {
static BUFFER: [[u16; 64]; 2] = [[0; 64]; 2];
},
resources: {
// cf. japaric/embedded-hal#14 and the blue-pill `dma` module
static BUFFER: dma::CircBuffer<[u16; 64]>;
}
}
fn init(roots: init::Roots, ..) -> init::LateResourceValues {
let buffer = adc1.start(dma1, roots.BUFFER);
init::LateResourceValues {
BUFFER: buffer,
}
}
Thoughts? Can these use cases be safely achieved through some other means?
I don't know how much value would be to support all possible exceptions (as tasks), but at least sys_tick
should be supported, I think.
(git clone https://github.com/jamwaffles/esp8266-at && cd esp8266-at && cargo run --example blink
should exhibit this error on a Blue Pill)
Tested on nightly-2018-08-31 and nightly-2018-09-01.
I have the following code which should blink the onboard LED on an STM31F103 "blue pill" board:
#![no_std]
#![no_main]
extern crate cortex_m;
#[macro_use(entry, exception)]
extern crate cortex_m_rt;
extern crate cortex_m_rtfm as rtfm;
extern crate cortex_m_semihosting as semihosting;
extern crate panic_itm;
extern crate stm32f103xx_hal as blue_pill;
use blue_pill::gpio::gpioc::PC13;
use blue_pill::gpio::{Output, PushPull};
use blue_pill::prelude::*;
use blue_pill::stm32f103xx;
use blue_pill::timer::{Event as TimerEvent, Timer};
use core::fmt::Write;
use cortex_m::asm;
use cortex_m_rt::ExceptionFrame;
use rtfm::{app, Threshold};
use semihosting::hio;
app! {
device: stm32f103xx,
resources: {
static LED: PC13<Output<PushPull>>;
static HSTDOUT: hio::HStdout;
},
tasks: {
SYS_TICK: {
path: sys_tick,
resources: [LED, HSTDOUT],
},
},
}
// Wrap `app!()`-generated `main()` to fix return type
fn ent() -> ! {
main();
loop {}
}
entry!(ent);
fn init(p: init::Peripherals) -> init::LateResources {
let mut flash = p.device.FLASH.constrain();
let mut rcc = p.device.RCC.constrain();
let clocks = rcc.cfgr.freeze(&mut flash.acr);
let mut gpioc = p.device.GPIOC.split(&mut rcc.apb2);
Timer::syst(p.core.SYST, 10.hz(), clocks).listen(TimerEvent::Update);
let mut led = gpioc.pc13.into_push_pull_output(&mut gpioc.crh);
led.set_low();
let mut hstdout = hio::hstdout().unwrap();
writeln!(hstdout, "INIT").unwrap();
init::LateResources {
LED: led,
HSTDOUT: hstdout,
}
}
fn idle() -> ! {
loop {
rtfm::wfi();
}
}
fn sys_tick(_t: &mut Threshold, mut r: SYS_TICK::Resources) {
writeln!(r.HSTDOUT, "TICK, LED low: {}", r.LED.is_set_low()).unwrap();
if r.LED.is_set_low() {
r.LED.set_high()
} else {
r.LED.set_low()
}
}
exception!(HardFault, hard_fault);
fn hard_fault(ef: &ExceptionFrame) -> ! {
panic!("{:#?}", ef);
}
exception!(*, default_handler);
fn default_handler(irqn: i16) {
panic!("Unhandled exception (IRQn = {})", irqn);
}
The INIT
print is output to the OpenOCD console, but the debug statement in sys_tick
never gets called (and the LED never changes state). I also have some code that should respond to a DMA USART interrupt and that doesn't work either. I believe it's known that the app!()
macro doesn't work due to the examples in japaric/stm32f103xx-hal being marked as outdated for some time. What can be done to get app!()
working again? I'd be happy to take a stab if someone can give me some pointers ๐
I use RTFM 0.4 from
https://github.com/japaric/stm32f103xx-hal/tree/rtfm-up
But when I try set priority
different from 1 for task that not associated with interrupt, program compiling fails with error:
error: proc macro panicked
--> src/main.rs:344:1
|
344 | / app! {
345 | | device: stm32f103xx,
346 | |
347 | | resources: {
... |
395 | | },
396 | | }
| |_^
|
= help: message: not enough free interrupts
Currently, the examples build against the stm32f103xx crate, which is for a family of Cortex-M3 microcontrollers. Cortex-M3 implements armv7, and due to recent changes in svd2rust such a crate can not be compiled for the thumbv6m-none-eabi
target. While it's generally nonsensical to do that, it prevents us from building the examples in this repo for an armv6 target.
In #43, I've made the tests pass by replacing the linker for the thumbv6m-none-eabi
target with true
- a pretty horrible hack. The proper solution for this would be to switch to a device crate for some Cortex-M0 microcontroller, propably in the stm32f0 family.
NB: It makes even less sense to compile an M0 device crate for armv7, but it currently works.
The original RTFM language had timing semantics baked into it. It was possible
to e.g. schedule task A to run N milliseconds after task B started where A and B
could even be the same task.
This issue will be used to track progress on incrementally reproducing this
functionality in cortex-m-rtfm.
The implementation must only use core peripherals and must not require dynamic
allocations.
recurrency: Have a single task execute periodically e.g. every second.
"Task A must run N milliseconds after task A started"
multiple: Have two tasks with different priorities execute periodically at
different frequencies.
dispatcher: Same as before but the tasks will run at the same priority.
This time both tasks must be dispatched from a single interrupt.
offset: Have a task A execute periodically, and a task B execute after N
milliseconds from the start of task A.
conditional: Same as before but have task B only be scheduled under some
condition: for example every other execution of the task A.
srp: Make sure all tasks have their own resources and local data and that
they adhere to SRP semantics.
The implementation will live in a feature branch for the duration of the
experiment. The implementation should use DWT.CYCCNT as a monotonic timer and
SYS_TICK to generate timeouts interrupts.
Upcoming book contains very good messaging example: https://japaric.github.io/cortex-m-rtfm/user/messages.html. There are some common cases (like block transfers via uart/serial/i2c buses), had to be repeated in many project. It looks attractive to arrange those to crates somehow. Questions are:
In other words, as user, i'd like to have "ready solution to read strings from UART" (that's only an example). And use it in RTFM. What is the optimal way to develop such crates? May be worth add some words about into the book.
PS. i'm not sure. Feel free to close anytime.
I had a bit of trouble with a task named RTC that uses a resource named RTC that is a peripheral named RTC. I am using RTFM 0.3.1.
The workaround I applied was to rename the resource RTC_R and not import the peripheral name directly.
The main problem seems to revolve around the fact that the task and resource are simply prefixed by a single underscore, resulting in a collision.
Conceptually, do you see tasks as living in a different namespace than resources ? Is there any other way similar use cases could be made simpler ?
app! {
device: stm32f103xx,
resources: {
static RTC_R: stm32f103xx::RTC;
static PWR: PWR;
static SCB: SCB;
},
tasks: {
RTC: {
path: isr_rtc,
resources: [RTC_R, PWR, SCB],
},
},
}
I am trying to use the clean architecture for my device - i.e. the main application logic is not dependent on the hardware, but is written with a generic output trait that can be instantiated with different types.
pub trait Output {
fn write(Message);
}
pub struct App<O: Output> {
out: O,
โฆ
}
impl<O: Output> App<O> {
pub fn new(out: O) -> Self {
Self { out, โฆ }
}
pub fn process(&mut self, msg: Message) {
โฆ // heavy lifting
self.out.write(โฆ);
โฆ
}
}
For testing, I can instantiate the Output
with a growing vector where I can inspect the results after processing various example inputs.
In the application, I can instantiate the Output
with a producer that writes into a buffer, getting consumed from the serial port interrupt:
// pseudo code :-)
use my_library::App;
use buffer::{Buffer, BufferConsumer, BufferProducer};
use hal::Uart;
struct SerialApp {
input: BufferConsumer,
app: App<SerialBufferProducer>
}
struct SerialBufferProducer(BufferProducer);
rtfm::app! {
resources: {
static app: SerialApp;
static serial_rx: BufferProducer;
static serial_tx: BufferConsumer;
static uart: Uart;
},
idle: {
resources: [app],
},
tasks: {
USART1: {
path: rxtx,
resources: [uart, serial_rx, serial_tx],
enabled: true,
}
},
}
fn init(mut p: init::Peripherals) -> init::LateResources {
static mut rx_buffer: Buffer = Buffer::empty();
static mut tx_buffer: Buffer = Buffer::empty();
let (rx_consumer, rx_producer) = unsafe { rx_buffer.split() };
let (tx_consumer, tx_producer) = unsafe { tx_buffer.split() };
init::LateResources {
app: SerialApp::new(rx_consumer, SerialBufferProducer(tx_producer)),
serial_rx: rx_producer,
serial_tx: tx_consumer,
uart: Uart::init(p.USART1),
}
}
fn idle(t: &mut Threshold, mut r: idle::Resources) -> ! {
loop {
if let Some(msg) = r.serial_app.input.read() {
r.serial_app.app.process(msg);
}
}
}
pub fn rxtx(_: &mut ::Threshold, mut r: ::USART1::Resources) {
if let Some(byte) = r.uart.received() {
r.serial_rx.write(byte);
}
if r.uart.can_send() {
if let Some(byte) = r.serial_tx.read() {
r.uart.send(byte);
} else {
r.uart.disable_tx_event();
}
}
}
Here's the catch: the SerialBufferProducer
wrapper that implements the app Output
is supposed to enable the uart tx event after writing to the buffer, and it would need to claim the uart
resource for that!
Let's assume that
app
in other tasks as well, so I can't just instantiate it within idle
and use its token and uart resource in the construction of the SerialBufferProducer
.uart
resource or using bit banding - I really need to prevent other uart
-using interruptsI think I need a way to access a uart
resource in the init
function so that I can put it into the SerialBufferProducer
, and then a way to claim it without being passed a token. The clean architecture prevents me from passing the token that is accessible at the call site of process
through to the write
call. What do you think?
The documentation link for version 0.3.2 on https://crates.io/crates/cortex-m-rtfm does not work. It simply returns a page with:
The requested resource does not exist
By changing the 0.3.2
in the link to for example 0.3.1
you get the actual documentation: https://docs.rs/cortex-m-rtfm/0.3.1/cortex_m_rtfm/.
Either a compiler, memory or an instruction one or a mixture of them. To prevent the closure code from being executed outside the modified BASEPRI context. This should become more apparent when dealing with non-volatile resources (i.e. not peripherals)
This keeps track of all the unstable features that are required to use RTFM framework.
asm
compiler_builtins_lib
const_fn
optin_builtin_traits
. The compiler suggest using the NoSend
markerstruct
) but that doesn't seem to exist anymore (?).used
lang_items
. For panic_fmt
.linkage
. Could maybe be made optionalnaked_functions
. Could maybe be made optionalrust-std
component for thethumbv*m
targets that contained the core
and the compiler_builtins
(I though this list would be longer)
I've converted a project I'm tinkering with to v2 but a basic example of a timer interrupt doesn't work in debug mode.
In release mode it works as expected, never hits the default_handler and a counter the timer interrupt is incrementing goes up every 10 seconds as expected.
In debug mode it goes straight to the default handler. The exception is 55, which is the correct interrupt.
I've double checked the "rt" feature flag stuff is getting generated in my device crate and the xargo expand
output contains what looks like an override for the weak symbol defined in the device support crate:
#[allow(non_snake_case)]
#[allow(unsafe_code)]
#[export_name = "TIM7"]
pub unsafe extern "C" fn _TIM7() {
let f: fn(&mut rtfm::Threshold, TIM7::Resources) = tim7_int;
f(
&mut if 1u8 == 1 << stm32f7x6::NVIC_PRIO_BITS {
rtfm::Threshold::new(::core::u8::MAX)
} else {
rtfm::Threshold::new(1u8)
},
TIM7::Resources::new(),
)
}
I'm a bit hazy on the details of how linking/weak symbols/etc works so please let me know if there are any commands i can run on the ELF files to get more info. This seemed relevant:
Release:
arm-none-eabi-objdump -t target/thumbv7em-none-eabihf/release/examples/adc | grep TIM7
08000222 g F .text 0000002a TIM7
Then in gdb:
x 0x8000222
0x8000222 <adc::_TIM7>
info functions
<snip>
File examples/adc.rs:
fn adc::_TIM7();
Debug:
arm-none-eabi-objdump -t target/thumbv7em-none-eabihf/debug/examples/adc | grep TIM7
0800539e g F .text 00000012 _ZN59_$LT$stm32f7x6..TIM7$u20$as$u20$core..ops..deref..Deref$GT$5deref17h4b245b5fe55c3366E
Then in gdb:
x 0x0800539e
0x800539e <stm32f7x6::{{impl}}::deref>: 0x4601b083
info functions
<snip>
File examples/adc.rs:
static fn adc::_initResources::new() -> adc::_initResources;
static fn adc::idle() -> !;
static fn adc::init(stm32f7x6::Peripherals, adc::_initResources);
static fn adc::main();
static fn adc::main::{{closure}}(rtfm_core::Threshold *);
I've updated my arm and rust toolchains and double checked the various xargo and cargo config files for all projects and everything seems to be in line with your blue-pill repo.
Thanks
Hello,
Trying to update to the last version of this crate, I had the following error at compilation :
[clement@clement-desktop-home i2c_magnetic]$ xargo build
Compiling i2c_magnetic v0.1.0 (file:///home/clement/atom-projects/cortex/i2c_magnetic)
error: language item required, but not found: `panic_fmt`
error: aborting due to previous error
error: Could not compile `i2c_magnetic`.
I also updated the f3 crate to the 0.5.1 version.
One of the ways how RTFMv2 became simpler than v1 was the removal of type level
integers along with the various associated tokens. However this simplification
comes at a price: heavy reliance on LLVM for proper optimization.
Right now, there's only one token that the user has to deal with: the preemption
Threshold
token. This token is actually a newtype over u8
that tracks the
preemption threshold of the system through the whole application. This token is
used to unlock Resource
s through the borrow{,_mut}
and claim{,_mut}
methods. All these methods have branches and assertions in them for memory
safety. In a correctly written and properly optimized program the assertions in
borrow{,_mut}
, all but one branch in claim{,_mut}
and all the Threshold
tokens should be optimized away. However this requires that LLVM knows the
exact value of the Threshold
token in every single node of the function
call graph. In complex enough call graphs LLVM "gives up" and generates code to
track the value of Threshold
at runtime; this destroy performance: panicking
branches as well as all the branches in a claim{,_mut}
call are kept in. In
the worst case scenario this can triple the size of the output .text
section.
The only way I can think of to fix the problems outline above is to turn
Threshold
into a type level integer. This way the token is guaranteed to not
exist at runtime; the token would become a zero sized type. This change would
turn the panicking branch in borrow{,_mut}
into a compile error and make it
very easy for LLVM to optimize away the branches in claim{,_mut}
because the
"value" of Threshold
would be "local" to a function rather than require
the close-to global program analysis that v2 requires.
In principle we can move the API to type level integers today (see appendix) by
using the typenum
crate, but it seems that the implementation is blocked by
a rustc bug (cf. rust-lang/rust#43580).
The main downside of implementing this change is that generic code that uses the
Resource
trait becomes much more complicated to write and to read.
This is generic code that deals with two resources, today:
fn foo<A, B>(t: &mut Threshold, a: A, b: B)
where
A: Resource<Data = u8>,
B: Resource<Data = u8>,
{
a.claim(t, |a, t| {
b.claim(t, |b, t| {
// ..
});
});
}
This is how that generic code would look like with the proposed typenum
-based
API:
fn foo<A, B, CA, CB, THRESHOLD>(t: &mut T<THRESHOLD>, a: A, b: B)
where
A: Resource<Data = u8, Ceiling = CA>,
B: Resource<Data = u8, Ceiling = CB>,
CA: Unsigned,
CB: Unsigned,
THRESHOLD: Max<CA> + Unsigned, // needed for the outer claim
Maximum<THRESHOLD, CA>: Max<CB> + Unsigned, // needed for the inner claim
Maximum<Maximum<THRESHOLD, CA>, CB>: Unsigned, // also for the inner claim
{
a.claim(t, |a, t| {
b.claim(t, |b, t| {
// ..
});
});
}
Effectively every claim
can add one bound to the where
clause of the generic
function. The bounds give no extra information to the reader; they are just
there to please the compiler.
I think the only way to avoid this downside would be to build the API on top of
proper type level integers baked into the language (*). The problem is that they are
not implemented and that the API requires a type level cmp::max(A, B)
operator (**) -- it's unknown when / if that operator will be implemented.
EDIT1: (*) because, in theory, in that case no bound should be required. const N: u8
is known to be an integer and type level operations on it (where N1 > N2
) would be baked into the language.
EDIT1: (**) A type level if a > b
operator / clause is also required for borrow
.
The other minor downside of the change is that the signature of task functions
would have to include the exact threshold level. Like this:
// priority = 3
fn exti0(t: &mut T3, r: EXTI0::Resources) { .. }
// priority = 4
fn exti1(t: &mut T4, r: EXTI0::Resources) { .. }
This doesn't seem too bad but changing the priority of a task will require you
to change the type of the threshold token as well.
The whole new typenum
-based API
#![no_std]
extern crate typenum;
use core::marker::PhantomData;
use typenum::{IsGreaterOrEqual, Max, Maximum, True, Unsigned};
/// A resource, a mechanism to safely share data between tasks
pub trait Resource {
/// The data protected by the resource
type Data: Send;
/// The ceiling of the resource
type Ceiling: Unsigned;
/// Borrows the resource data for the span of the current context (which may
/// be a critical section)
///
/// The current preemption threshold must be greater than the resource
/// ceiling for this to work; otherwise this call will cause the program to
/// not compile.
fn borrow<'cs, THRESHOLD>(
&'cs self,
t: &'cs T<THRESHOLD>,
) -> &'cs R<Self::Data, Self::Ceiling>
where
THRESHOLD: IsGreaterOrEqual<Self::Ceiling, Output = True> + Unsigned;
// plus `borrow_mut`
/// Grants access to the resource data for the span of the closure `f`
///
/// The closure may be executed in a critical section, created by raising
/// the preemption threshold, if required
fn claim<RTY, THRESHOLD, F>(&self, t: &mut T<THRESHOLD>, f: F) -> RTY
where
F: FnOnce(
&R<Self::Data, Self::Ceiling>,
&mut Maximum<THRESHOLD, Self::Ceiling>,
) -> RTY,
THRESHOLD: Unsigned + Max<Self::Ceiling>;
// plus `claim_mut`
}
/// An unlocked resource
pub struct R<DATA, CEILING>
where
CEILING: Unsigned,
DATA: Send,
{
data: DATA,
_ceiling: PhantomData<CEILING>,
}
/// Preemption threshold token
pub struct T<THRESHOLD>
where
THRESHOLD: Unsigned,
{
_threshold: PhantomData<THRESHOLD>,
}
cc @cr1901 this would fix the misoptimization you have been seeing in your AT2XT program. Actually implementing this for MSP430 may be possible / easy because effectively there's only two priority levels in that architecture.
here is an example project:
https://gitlab.com/xnor/stm32f0308-disco-rust
If I build it without --release I get
target/thumbv6m-none-eabi/debug /deps/libstm32f030-5466fdead1a18a6d.rlib(stm32f030-5466fdead1a18a6d.0.o): In function `WWDG':
stm32f030.cgu-0.rs:(.text+0x0): relocation truncated to fit: R_ARM_THM_JUMP11 against symbol `DEFAULT_HANDLER' defined in .text.DEFAULT_HANDLER section in /home/alex/projects/modular/threshpan/target/thumbv6m-none-eabi/debug/deps/libcortex_m_rt-881d17200def560b.rlib(cortex_m_rt-881d17200def560b.0.o)
This would make all RTFM applications use less static memory (.bss / .data), reduce pre-initialization runtime and make them smaller (.text).
This would mainly involve replacing the MaybeUninit
defined in rtfm::export
with a re-export of core::mem::MaybeUninit
and then removing all these runtime initializations.
Today, assigning celing to resources is unsafe
because there's no mechanism to stop you from aliasing a peripheral and assign to each instance a different ceiling. Like this:
static GPIOA: Peripheral<Gpioa, C1> = unsafe { Peripheral::new(stm32f30x::GPIOA) };
static GPIOA2: Peripheral<Gpioa, C2> = unsafe { Peripheral::new(stm32f30x::GPIOA) };
I have come up with a macro that would fix the problem:
peripherals!(stm32f30x ,{
GPIOA: Peripheral {
register_block: Gpioa,
ceiling: C1,
}
});
Which should expand to:
#[export_name = "Gpioa"] // or maybe `"stm32f30x.Gpioa"`?
static GPIOA: Peripheral<Gpioa, C1> = unsafe { Peripheral::new(stm32f30x::GPIOA) };
The key here is the export_name
. With this we can prevent aliasing of Peripheral
s even if the declaration are in different modules / crates. For instance, this:
peripherals!(stm32f30x ,{
GPIOA: Peripheral {
register_block: Gpioa,
ceiling: C1,
}
});
mod foo {
peripherals!(stm32f30x ,{
GPIOA: Peripheral {
register_block: Gpioa,
ceiling: C1,
}
});
}
Would be rejected at compile time (not at link time) with the following error:
error: symbol `Gpioa` is already defined
Sadly, this macro is not implementable today because this is not a valid macro expansion:
#[export_name = stringify($RegisterBlock)]
static $PERIPHERAL: ...
AIUI, we need early expansion support to allow stringify!
in that position.
See erratum 837070 in "ARM Processor Cortex-M7 (AT610) and Cortex-M7 with FPU (AT611), Product revision r0, Sofware Developers Errata Notice".
Basically the problem is that msr BASEPRI
won't immediately take effect and the instruction following it won't have the new / desired dynamic priority and thus it can be preempted as if BASEPRI hadn't been modified.
The suggested workaround is to temporarily disable interrupts when msr BASEPRI
is issued, so basically something like this:
cpsid i
msr BASEPRI
cpsie i
We can directly apply this fix in claim since claim only modifies BASEPRI when interrupts are enabled. (If interrupts are disabled, claim is a no-operation)
However, It would be ideal if we only applied this fix when targeting Cortex-M7 devices. And I think we can do that but we'll need some help from svd2rust. SVD files contains these <cpu>
and <revision>
fields that would let us identify chips with the problem. Device crates could expose CPU
and REVISION
constants that claim could check to decide whether to apply the workaround or not.
Thanks @pftbest for reporting this.
cc @perlindgren
I'm observing weird behavior if I have a very trivial idle function (just an infinite loop): "init" function gets called twice.
What I observe is that:
I inserted breakpoint just before the "idle(p0, t0);" invocation and here is what I see in GDB:
0x80007a4 <x2_feed::main+8> bkpt 0x0000 ; Breakpoint I inserted just before the "idle" call
; Hey, where is my infinite loop?
; I think this piece of code is "closure" that calls "init'... That kind of explains it
0x80007a6 <x2_feed::main::{{closure}}> push {r7, lr}
0x80007a8 <x2_feed::main::{{closure}}+2> mov r7, sp
0x80007aa <x2_feed::main::{{closure}}+4> movw r0, #8594 ; 0x2192
(this is probably not RTFM issue per se)
Provide a way to access read-only registers without using unsafe code.
Currently, rtfm
provides methods to access peripherals in a concurrent way.
This means that even when we know a read is safe (and doesn't change the value), rtfm doesn't know because there is no way for us to tell that a register is read-only (and safe).
This example shows one way how to do this in rtfm v0.1.1
(It is rather dumb in the way it is setup, but it was the simplest way I could demonstrate this)
A button is setup on PA1, another button is on PA2 and a led on PA5.
The button on PA1 controls the led on PA5 inside the idle loop. The button on PA2 is setup to trigger an exti interrupt that controls the led on PA5.
extern crate cortex_m;
extern crate cortex_m_rt;
#[macro_use]
extern crate cortex_m_rtfm;
extern crate pac; // Our peripheral access crate
use rtfm::{C1, P0, P1, Resource, T0, T1, TMax};
peripherals! {
GPIOA: Peripheral {
register_block: GPIOA,
ceiling: C1,
},
EXTI: Peripheral {
register_block: EXTI,
ceiling: C1,
},
}
fn init(prio: T0, thres: &TMax) {
// setup exti on PA2 ...
}
fn exti_pa2(mut task: EXTI0, ref prio: P1, ref thres: T1) {
let exti = EXTI.access(prio, thres);
let gpioa = GPIOA.access(prio, thres);
// Clear pending exti and change led PA5
}
fn idle(ref prio: T0, ref thres: T0) -> ! {
let gpioa_ro = pac::GPIOA.get();
loop {
if unsafe { (*gpioa_ro).idr.read().idr1().is_set() } {
thres.raise(&GPIOA, |threshold: &T1| {
let gpioa = GPIOA.access(prio, threshold);
// Change state of led on PA5
}
}
}
}
With information on read-only registers, rtfm should enable us to write something like the following.
registers_ro! {
GPIOA: [IDR,],
}
fn idle(ref prio: T0, ref thres: T0) -> ! {
let gpioa_idr = GPIOA.access_read::<GPIOA_IDR>();
loop {
if gpio_idr.read().idr1().is_set() {
thres.raise(&GPIOA, |threshold: &T1| {
let gpioa = GPIOA.access(prio, threshold);
// Change state of led on PA5
}
}
}
}
Here, the register_ro
macro provides an static object called GPIOA_IDR
that is bare-metal::Register<T, RW> where RW: bare-metal::Read + !bare-metal::Write
.
The function Peripheral::access_read<Register<T, RW> where RW: _
returns T
.
Tasks always start with a threshold level that matches the priority level of the task. It's safe to add a method to the priority token to synthesize a threshold token (at the level) from it:
impl Priority<N> {
pub fn threshold(&self) -> Threshold<N> { .. }
}
then the threshold token could be dropped from tasks' signatures making them simpler:
fn t1(task: Exti0, ref prio: P1) {
let thr = &prio.threshold();
let r1 = R1.access(prio, thr);
}
Should we do this?
threshold
method in a task. Would that feel repetitive?threshold
be named differently? Perhaps base_threshold
?I follow the copper book and your blog posts (great stuff!) to create a proof of concept for some STM boards we had lying around: https://github.com/hmvp/rust_poc
It seemed logical to make to modules within the poc library for board support. I used the peripherals! macro in both resulting in a conflict because both modules declared a GPIO peripheral with the same name.
I solved this by not using the macro:
https://github.com/hmvp/rust_poc/blob/master/src/stm32f3348_discovery/mod.rs
Uncommenting the macro stuff gives the error.
For the task! macro I did not even try to move the common stuff to the bsp modules but I suspect I will get similar issues.
NB: I am quite new to this so I might have missed an obvious way to to things that does not lead to issues..
NB2: the two binaries now are named the same as the board but ideally the nucleo one should be able to run on both boards (if the cpu specific stuff was moved to the bsp module)
I tried latest hello.rs from blue-pill . However got build error "error: language item required, but not found: panic_fmt
". Fixed the app by adding panic handler related code as per "cortex-m-quickstart" examples/panic.rs. However earlier version of cortex-m-rtfm didn't require such modification. Looked into the code of cortext-m-rtfm. I found that panic handler was part of cortex-m-rtfm earlier version. However there is no panic handler in the latest cortex-m-rtfm.
So am my missing any thing? or should panic handler be part of latest cortex-m-rtfm?
In the original RTFM language it was possible to execute functions
asynchronously. This operation would queue the execution of the function with
some arguments. A dispatcher task would then execute the function according to
its priority.
This let you organize your programs as processes where each process was a
sequence of smaller tasks. Each task would communicate with the following task
through message passing reducing the need for shared global state.
This issue will be used to track progress on incrementally reproducing this
functionality in cortex-m-rtfm.
The implementation must only use core peripherals and must not require dynamic
allocations.
single: have a task execute a function f(a, b, c) asynchronously with
priority lower than the task priority. The function will be executed after the
current task ends.
buffered: as before but have the task execute several instances of the
same function.
multiple: have a task execute more than one function asynchronously. Each
function will run at a different priority.
dispatcher: as before but have both functions execute at the same
priority. Both functions must be dispatched from a single interrupt.
srp: Make sure all tasks have their own resources and local data and that
they adhere to SRP semantics.
timing-semantics: incorporate the work from #32 and add timing semantics.
"Execute function f with arguments a, b and c after N milliseconds from the
start of the current task."
The implementation will live in a feature branch for the duration of the
experiment.
There's no BASEPRI on this architecture.
We can start with just supporting cooperative tasks and then support lock
by manually masking the other tasks using NVIC.
Hello there. I'm sure the issue is something I overlooked, but I'm attempting to get the zero-tasks.rs
example working with the blue pill.
Cargo.toml:
[dependencies]
cortex-m-rtfm = "0.3.4"
[dependencies.stm32f103xx]
features = ["rt"]
version = "0.10.0"
main.rs: literally copied from examples/zero-tasks.rs
in repo
Rust & Cargo versions:
rustc 1.30.0-nightly (5c875d938 2018-09-24)
cargo 1.31.0-nightly (de314a8b2 2018-09-21)
cargo build
output:
error: `#[panic_handler]` function required, but not found
error: aborting due to previous error
error: Could not compile `testing`.
Static
wrapperIf you have been using RTFM claim
s you probably have noticed this "pattern":
r.FOO.claim_mut(|foo| {
**foo += 1;
});
Here you need a double dereference because claim
returns a &mut Static<T>
, instead of a plain
mutable reference (&mut T
). Static<T>
is a newtype over T
that Deref
s to T
. You normally
won't notice the Static
wrapper when using methods because of the Deref
implementation, but the
wrapper becomes apparent when you need to assign some new value to a resource.
So, why is Static
being used here? The main reason is that I needed some (zero cost) abstraction
to make DMA transfers memory safe. You can't build a safe DMA API on top of plain (non-static)
references. See below:
impl Serial {
fn read_exact<'a>(&'a mut self, buf: &'a mut [u8]) -> Transfer<'a> { .. }
}
impl<'a> Transfer<'a> {
fn wait(self) {
drop(self)
}
}
impl<'a> Drop for Transfer<'a> {
fn drop(&mut self) {
// waits until the DMA transfer finishes
}
}
let mut on_the_stack = [0; 16];
{
let transfer = serial.read_exact(&mut on_the_stack);
// meanwhile, do some other stuff
// on_the_stack[0] = 1;
//~^ error `on_the_stack`
transfer.wait();
}
// use `on_the_stack`
At first glance, this looks like an OK DMA API. While the DMA transfer is writing to the buffer you
can't access the buffer (on_the_stack
is "frozen" by the outstanding borrow). The Transfer
value
represents the on-going transfer and upon destruction (when drop
ped) it waits for the transfer to
finish. You can use the wait
method to make the wait operation more explicit.
However, this API is not safe because you can safely mem::forget
the Transfer
value to gain
access to the buffer while the transfer is on-going:
let mut on_the_stack = [0; 16];
{
let transfer = serial.read_exact(&mut on_the_stack);
// destructor not run
mem::forget(transfer);
}
// the transfer may not be over at this point
on_the_stack[0] = 1;
assert_eq!(on_the_stack[0], 1);
This doesn't look too dangerous but it's a violation of Rust aliasing model and will result in UB.
In the last line two mutable references to on_the_stack
exist: one is being used in the indexing
operation, and the other is owned by the DMA (external hardware).
It gets much worse though because this mem::forget
hole can be used to corrupt stack memory:
fn foo() {
let mut on_the_stack = [0; 16];
mem::forget(serial.read_exact(&mut on_the_stack));
}
fn bar() {
// do stuff while the DMA transfer is on going
}
foo();
bar();
Here foo
starts a DMA transfer that modifies some stack allocation but then immediately returns,
releasing the stack allocation. Next bar
starts while the DMA is still on going; the problem is
that the DMA transfer will write into the stack potentially overwriting bar
's local variables and
causing undefined behavior.
Static
help?We can prevent the memory corruption by having the API only accept references that point into memory
that will never be de-allocated. And that's what the Static
wrapper represents: &Static<T>
is a
reference into a statically allocated (i.e. stored in a static
variable) value of type T
. With
this change the API would look like this:
impl Serial {
fn read_all<'a>(&'a mut self, buf: &'a mut Static<[u8]>) -> Transfer<'a> { .. }
}
(Note that this change is not enough to prevent the aliasing problem caused by mem::forget
.
Discussing a solution for that issue is out of scope for this RFC though. The RefCell
-like
Buffer
abstraction in the blue-pill crate prevents the mem::forget
aliasing problem showcased
above but it still has other issues like mem::swap
aliasing and that you can e.g. still use
Serial
while the transfer is in progress)
A value can't be safely wrapped in Static
but RTFM does that for you in every claim and that
lets you use the memory safer DMA API from above.
Changing buf
's type to &'static mut
would also have worked but there's no way to safely create
&'static mut
references, or rather there wasn't a way before this RFC.
Being able to safely create &'static mut
references.
Why? &'static mut
references have interesting properties that I think will enable the creation of
novel APIs:
&'static mut T
has move semantics. See below:
fn reborrow<'a, T>(x: &'a mut T) { .. }
fn consume<T>(x: &'static mut T) { .. }
fn foo<T>(x: &'static mut T) {
reborrow(x);
// OK to call again
reborrow(x);
// actually the compiler is doing this in each `reborrow` call
reborrow(&mut *x);
// this is different: this takes ownership of `x`
consume(x);
// now you can't use `x` anymore
//consume(x);
//~^ error `x` has been already moved
//reborrow(x);
//~^ error `x` has been already moved
}
&'static mut T
has 'static
lifetime (gasp!) so, unlike its non-static cousin, it can be stored
in a static
variable and thus we can have a resource that protects a &'static mut T
.
&'static mut T
is pointer sized. If you need to send (transfer ownership) of a buffer from one
task (execution context) to another then it's cheaper to send &'static mut [u8; 256]
than to send
[u8; 256]
even though they are both semantically a move.
So &'static mut T
is a bit like Box<T>
: both have move semantics and are
pointer sized but the former doesn't need dynamic memory allocation (it's statically allocated!) and
we know that T
's destructor will never run ('static lifetime).
We can revise the DMA API to make it truly memory safe:
impl Serial {
fn read_exact(self, buf: &'static mut [u8]) -> Transfer { .. }
}
impl Transfer {
fn wait(self) -> (Serial, &'static mut [u8]) { .. }
}
let buf: &'static mut [u8] = /* created somehow */;
let transfer = serial.read_exact(&mut on_the_stack);
// can't use Serial while the DMA transfer is in progress
// let byte = serial.read();
//~^ error `serial` has been moved
// can't access `buf` while the transfer is in progress
// buf[0] = 1;
//~^ error `buf` has been moved
// meanwhile, do other stuff
// block until the transfer finishes
let (serial, buf) = transfer.wait();
// now you can use `serial` and access the `buf`fer again
Here if you mem::forget
the transfer then you can't never access serial
or the buf
fer again,
which may seem overkill but fulfills the memory safety requirement.
This use case prompted the original RFC (cf. #47). Basically a ring buffer queue can be split into
one producer end point and one consumer end point. Each end point can locklessly enqueue or dequeue
items into / from the same queue -- even if the end points are in different execution contexts (e.g.
threads or interrupts).
The API for this already exists in the heapless
crate but the Consumer
and Producer
end
points have a lifetime parameter that matches the lifetime of the ring buffer queue. To put these
end points in resources the lifetime would have to be 'static
and that requires a &'static mut RingBuffer
, which can't be safely created without this RFC.
init.resources
We add a resources
field to app.init
. The value of this field is a list of resources, previously
declared in app.resources
, that init
will own for the rest of the program. The resources in
this list will appear under the init::Resources
struct as &'static mut
references. Example:
app! {
device: stm32f103xx,
resources: {
static BUFFER: [u8; 16] = [0; 16];
static SHARED: bool = false;
},
init: {
// NEW!
resources: [BUFFER],
},
idle: {
resources: [SHARED],
},
tasks: {
EXTI0: {
path: exti0,
resources: [SHARED],
},
}
}
fn init(p: init::Peripherals, r: init::Resources) {
// static lifetime
let buf: &'static mut [u8; 16] = r.BUFFER;
// non-static lifetime
let shared: &mut bool = r.SHARED;
}
// ..
Some constraints apply to init
owned resources:
These resources must have an initial value; i.e. they can't be "late" resources.
Resources assigned to init
can't appear in idle.resources
or in tasks.$T.resources
.
Basically, the resources are owned by init
so they can't be shared with other tasks, or with
idle
.
These constraints will be enforced by the app!
macro. An error will be thrown before expansion if
any constraint is violated.
Static
wrapperSince this RFC brings proper support for &'static mut
references to the table I think the Static
wrapper is no longer useful -- memory safe DMA APIs can be built without it and that was its main
reason for existing.
This will be implementing by changing all &[mut] Static<T>
to &[mut] T
. This means you will no
longer need to doubly dereference to assign a new value to a resource.
This is a breaking change, but we are breaking things due to #50 so it's not much of a problem.
pre_init
functionA pre_init
function with signature fn() -> T
could be run before init
. The value returned by
this function would be passed as &'static mut T
to init
. Unlike the main proposal this value
would be created at runtime so const eval limitations would not apply; also the value would be
allocated in the stack (in the first frame, which will never be deallocated), not in .bss
/
.data
.
With code it would look like this:
app! {
device: stm32f103xx,
pre_init: pre_init,
}
struct Root {
buffer: [u8; 16],
}
fn pre_init() -> Root {
Root { buffer: [0; 16] }
}
fn init(root: &'static mut Root, p: init::Peripherals, r: init::Resources) { .. }
I think it may make sense to also support this because it potentially lets you use a different
memory region -- think of the case of microcontrollers with two RAM regions the stack could be on
one region and .bss / .data could be on the other. However, if we get better support for multiple
memory regions in cortex-m-rt
and support for placing resources in custom linker sections in
cortex-m-rtfm
then there is no longer a need for this, I think, because then you can place an
init
owned resource in any memory region (in RAM, e.g. .bss1
, or in core-coupled RAM, .bss2
).
I'm not too concerned about the const eval limitation that affects the main proposal because, in my
limited experience, the T
in the &'static mut T
references one creates is usually an array ([T; N]
) or a thin abstraction over uninitialized arrays (e.g. heapless::RingBuffer
).
See #58
Hi @japaric @jonas-schievink. First of all, thanks for this extraordinary work.
What do you think of having toml like syntax in app!
.
app! {
device = blue_pill::stm32f103xx,
[resources]
static ON: bool = false;
[tasks.SYS_TICK]
path = toggle
resources = [ON]
}
vs
app! {
device: blue_pill::stm32f103xx,
resources: {
static ON: bool = false;
},
tasks: {
SYS_TICK: {
path: toggle,
resources: [ON],
},
},
}
I'm don't know if this is possible at all but I feel the top one is much cleaner. What do you think?
As in title
I've copied
https://github.com/japaric/cortex-m-rtfm/blob/master/examples/zero-tasks.rs
into my own repo, and adapted it to use the stm32f4 crate. The resulting code is here:
https://github.com/timbod7/rust-stm32f4-examples/blob/master/minimal-rtfm/src/main.rs
this fails to build with a suprising (to me) error:
Compiling minimal-rtfm v0.0.1 (file:///home/timd/personal/projects/stm-f4/rust-stm32f4-examples/minimal-rtfm)
error: function is never used: `init`
--> src/main.rs:28:1
|
28 | fn init(p: init::Peripherals) {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: function is never used: `idle`
--> src/main.rs:41:1
|
41 | fn idle() -> ! {
| ^^^^^^^^^^^^^^
I would have expected these functions to be referenced from the code generated by the app!
macro.
What am I doing wrong?
#9 broke the tasks! macro but the test suite didn't detect the breakage
on my nightly I get:
cargo build --target thumbv7em-none-eabihf --examples
Compiling cortex-m-rtfm v0.3.2 (file:///Users/eugene.tolmachev/sources/cortex-m-rtfm)
error[E0658]: procedural macros cannot expand to modules (see issue #38356)
--> examples/safe-static-mut-ref.rs:12:1
|
12 | / app! {
13 | | device: stm32f103xx,
14 | |
15 | | resources: {
... |
21 | | },
22 | | }
| |_^
|
= help: add #![feature(proc_macro_gen)] to the crate attributes to enable
Anyone knows which toolchain/dependencies combo does work?
Trying to use rtfm with a blue pill board. I am having trouble with tasks. Adding a task causes a hard fault for debug builds. Release builds work. Debug builds work if opt-level is changed not to be 0.
Normal code (https://gist.github.com/nallar/49a1be1e1b6aa157dff7703b7ed61b19):
app! {
device: device,
tasks: {
CAN1_RX0: {
path: usb_interrupt,
resources: [USB]
}
}
}
Expanded code:
#[allow(unsafe_code)]
fn main() {
let init: fn(device::Peripherals) = init;
rtfm::atomic(unsafe { &mut rtfm::Threshold::new(0) },
|_t|
unsafe {
let _late_resources =
init(device::Peripherals::all());
let nvic = &*device::NVIC.get();
let prio_bits = device::NVIC_PRIO_BITS;
let hw = ((1 << prio_bits) - 1u8) << (8 - prio_bits);
nvic.set_priority(device::Interrupt::CAN1_RX0, hw);
nvic.enable(device::Interrupt::CAN1_RX0);
});
let idle: fn() -> ! = idle;
idle();
}
The hard fault happens on let hw = ((1 << prio_bits) - 1u8) << (8 - prio_bits);
. Removing that line and hardcoding a priority of 1 prevents the hardfault. In particular, (1 << prio_bits) - 1u8 seems to cause it.
Program received signal SIGTRAP, Trace/breakpoint trap.
cortex_m_rt::default_handler::hb779949c33da971d (ef=0x20004df0)
at C:\Users\Luna\.cargo\registry\src\github.com-1ecc6299db9ec823\cortex-m-rt-0.3.8\src\lib.rs:459
459 asm::bkpt();
(gdb) bt full
#0 cortex_m_rt::default_handler::hb779949c33da971d (ef=0x20004df0)
at C:\Users\Luna\.cargo\registry\src\github.com-1ecc6299db9ec823\cortex-m-rt-0.3.8\src\lib.rs:459
No locals.
#1 <signal handler called>
No symbol table info available.
#2 0x0800025a in blue_pill_usb_keyboard::main::_$u7b$$u7b$closure$u7d$$u7d$::h6df592e3e3c4b436 (_t=0x20004f77) at src\main.rs:110
init = 0x8000301 <blue_pill_usb_keyboard::init::h7fbaa15cdf06d57f>
#3 0x0800577e in cortex_m_rtfm::atomic::h04d4c70a3b2d7412 (t=0x20004f87,
f=...)
at C:\Users\Luna\.cargo\registry\src\github.com-1ecc6299db9ec823\cortex-m-rtfm-0.2.2\src/lib.rs:110
No locals.
#4 0x080001ea in blue_pill_usb_keyboard::main::h70c85532f64f8efa ()
at src\main.rs:102
init = 0x8000301 <blue_pill_usb_keyboard::init::h7fbaa15cdf06d57f>
#5 0x08005730 in cortex_m_rt::lang_items::start::hb2ca1dee2fb9b063 (
main=0x80001c3 <blue_pill_usb_keyboard::main::h70c85532f64f8efa>,
_argc=0, _argv=0x0)
at C:\Users\Luna\.cargo\registry\src\github.com-1ecc6299db9ec823\cortex-m-rt-0.3.8\src/lang_items.rs:31
No locals.
#6 0x08001286 in main ()
cortex_m_rt::RESET_VECTOR::haccb3bf5c3f20a4b = 0x8000131 <cortex_m_rt::reset_handler::h4aae2e8c4d94cc8b>
EXCEPTIONS = {{RUST$ENCODED$ENUM$0$None = {
__0 = 0x80022b7 <DEFAULT_HANDLER>}}, {
RUST$ENCODED$ENUM$0$None = {__0 = 0x80022b7 <DEFAULT_HANDLER>}}, {
RUST$ENCODED$ENUM$0$None = {__0 = 0x80022b7 <DEFAULT_HANDLER>}}, {
RUST$ENCODED$ENUM$0$None = {__0 = 0x80022b7 <DEFAULT_HANDLER>}}, {
RUST$ENCODED$ENUM$0$None = {__0 = 0x80022b7 <DEFAULT_HANDLER>}}, {
RUST$ENCODED$ENUM$0$None = {__0 = 0x0}}, {
RUST$ENCODED$ENUM$0$None = {__0 = 0x0}}, {
RUST$ENCODED$ENUM$0$None = {__0 = 0x0}}, {
RUST$ENCODED$ENUM$0$None = {__0 = 0x0}}, {
RUST$ENCODED$ENUM$0$None = {__0 = 0x80022b7 <DEFAULT_HANDLER>}}, {
RUST$ENCODED$ENUM$0$None = {__0 = 0x80022b7 <DEFAULT_HANDLER>}}, {
RUST$ENCODED$ENUM$0$None = {__0 = 0x0}}, {
RUST$ENCODED$ENUM$0$None = {__0 = 0x80022b7 <DEFAULT_HANDLER>}}, {
RUST$ENCODED$ENUM$0$None = {__0 = 0x80022b7 <DEFAULT_HANDLER>}}}
(gdb) x 0xe000ed04
0xe000ed04: 0x00000803
(icsr register, least significant byte = 0x03 = hard fault?)
(gdb) p ef
$3 = (struct ExceptionFrame *) 0x20004df0
(gdb) x/32
0x20004df0: 0xffffffff 0x00000010 0x00000001 0x08006200
0x20004e00: 0x08006200 0x08000235 0x0800025a 0x61000000
0x20004e10: 0x40006400 0x40007400 0xe0042000 0x40004c00
0x20004e20: 0x00000010 0x20004e44 0x20004f80 0x20004f77
0x20004e30: 0x08000301 0x20004f80 0x20004e34 0x20004f77
0x20004e40: 0x00000021 0xe000ed00 0xe000edf0 0xe0001000
0x20004e50: 0xe0002000 0xe000ef30 0xe0000000 0xe000ed90
0x20004e60: 0xe000e100 0xe000ed04 0xe000e010 0xe0040000
(gdb)
The backtrace shows it's faulting on this instruction:
.text:0800025a 50 fa 81 f0 uxtab r0, r0, r1
I don't have enough experience to debug further.
Late resources are initialized at runtime so their memory doesn't need to be initialized before main / init. However, today, late resources are placed in the .data
section so not only they get initialized (using memcpy) but they also use up FLASH memory (because everything in .data
has some initial value stored in FLASH).
To fix this we'll have to create a new section, let's call it .uninit
, in cortex-m-rt and then place the static
variables that will be used with late resources in that section. The change in this repo is minimal; here's the patch (macros/src/trans.rs):
None => quote! {
// Resource initialized in `init`
- static mut #_name: #krate::UntaggedOption<#ty> = #krate::UntaggedOption { none: () };
+ #[link_section = ".uninit"]
+ static mut #_name: #krate::UntaggedOption<#ty> =
+ #krate::UntaggedOption { none: () };
},
But this change requires updating cortex-m-rt, specially if rust-embedded/cortex-m-rt#43 lands. It's probably best to wait until rust-embedded/cortex-m-rt#43 lands.
The latest version of the syn
crate makes it easier to create parsers thanks to its parser
combinator (?) macros. It can also give better error (and warning) messages that have proper span
information. Let's port the app!
macro to it to provide a better user experience.
Here's what needs to be done:
app!
parser in the rtfm-syntax
crate. You'll have to replacertfm_syntax::App
and friends with a new struct that implements the Synom
trait. You can findapp!
macro syntax in the crate documentation of thecortex-m-rtfm-macros
crate.Update the code that converts the AST into a syntax checked AST. Basically update the
rtfm_syntax::check::app
function. You should try to keep the structs in rtfm_syntax::check
unchanged -- unless there's some improvement that requires changing them.
Update cortex-m-rtfm-macros
to use the new version of rtfm-syntax
. The changes should be
minimal.
To get familiar with the parse combinator macros check out:
syn
crate documentation.lazy_static!
examplemat!
macro I recently wrote.Be sure to enable the following features in your dependencies:
[dependencies.syn]
version = "0.12.12"
features = ["full"]
[dependencies.proc-macro2]
features = ["nightly"]
version = "0.2.2"
If you want to give this a stab leave a comment. If no one volunteers I'll probably get to this
before the week ends.
Need to pick up this commit:
rtic-rs/rtic-syntax@9a0b8e1
With this crate on cortex-m-0.4
, it's impossible to use any tooling which also includes cortex-m-0.5
- you get errors like
warning: Linking globals named 'CORE_PERIPHERALS': symbol multiply defined!
error: failed to load bc of "cortex_m.22u1h9r5-cgu.2":
Because (I'm pretty sure) both versions of the cortex-m libraries define those symbols and they clash. An example of this in action is:
https://travis-ci.org/wez/atsamd21-rs/builds/421535430
= note: rust-lld: error: duplicate symbol: CORE_PERIPHERALS
>>> defined at mod.rs:148 (/home/travis/.cargo/registry/src/github.com-1ecc6299db9ec823/cortex-m-0.4.3/src/peripheral/mod.rs:148)
>>> cortex_m-89a330ceffd88411.cortex_m.6bne0cac-cgu.0.rcgu.o:(CORE_PERIPHERALS) in archive /home/travis/build/wez/atsamd21-rs/metro_m0/target/thumbv6m-none-eabi/debug/deps/libcortex_m-89a330ceffd88411.rlib
>>> defined at mod.rs:154 (/home/travis/.cargo/registry/src/github.com-1ecc6299db9ec823/cortex-m-0.5.6/src/peripheral/mod.rs:154)
>>> cortex_m-202b80cd0a33d787.cortex_m.er0kkjw4-cgu.0.rcgu.o:(.bss.CORE_PERIPHERALS+0x0) in archive /home/travis/build/wez/atsamd21-rs/metro_m0/target/thumbv6m-none-eabi/debug/deps/libcortex_m-202b80cd0a33d787.rlib
Currently, when you want to share some data structure that needs to be mutated through a mutable (&mut-
) reference between tasks you have no option but to wrap it in a RefCell
because the Resource.access
method only returns shared (&-
) references. This involves runtime overhead because the RefCell
does runtime checks to ensure that accessing the inner data preserves Rust's borrowing semantics (one &mut-
reference OR several &-
references)
static R1: Resource<RefCell<Thing>, C1> = Resource::new(RefCell::new(Thing::new()));
fn t1(_: Exti0, ref prio: P1, ref thr: T1) {
let r1 = R1.access(prio, thr);
r1.borrow().foo(); // runtime check
r1.borrow_mut().foo_mut(); // runtime check
}
Due to how RTFM works it will be always be the case that a resource that contains a RefCell will have no outstanding borrows when it's first accessed in a task. This means that provided that you don't break borrow semantics within a task then executing the task will never fail a runtime borrow check. The compiler doesn't know this though, and will still perform the borrow checks at runtime even if they are effectively infallible.
To remove the runtime checks the assume
intrinsic can be used to tell the compiler about the "no outstanding borrow on first access
" property:
static R1: Resource<RefCell<Thing>, C1> = Resource::new(RefCell::new(Thing::new()));
fn t1(_: Exti0, ref prio: P1, ref thr: T1) {
let r1 = R1.access(prio, thr);
unsafe { intrinsics::assume(r1.borrow_state() == BorrowState::Unused) }
r1.borrow().foo(); // no runtime check
r1.borrow_mut().foo_mut(); // no runtime check
}
With this information the compiler is able to optimize away the runtime checks. The downside is that this requires unsafe
, borrow_state
is a deprecated API and that this doesn't optimize away the RefCell
reference counter from the Resource<RefCell<_>>
memory representation.
A possible solution is to add an access_mut
method to Resource
that's similar to Local.borrow_mut
and that has the following signature:
// trait bounds not shown for brevity
impl<T, RC> Resource<T, RC> {
// it must hold that: TP <= RC <= PT
fn access_mut(&'static self, priority: &mut TP, threshold: &PT) -> &mut T {
// ..
}
}
The problem here are the lifetime constraints. The borrow &mut T
can't outlive the threshold
token, or it would be possible for it to outlive a Threshold.raise
critical section and that would lead to data races. Additionally, the borrow &mut T
must also freeze the priority
token; this is required to avoid mutable aliasing -- without this it would be possible to get two or more mutable references to the data protected by the Resource
abstraction. Alternatively, this method could request a mutable reference to the task token to prevent aliasing but the priority
token will still be required (as a shared reference) so the signature would require 3 tokens.
Whatever we end up doing these operations must result in compile errors:
{
// reference to inner data escaped the critical section
let bad_r1 = threshold.raise(&R1, |threshold| {
R1.access_mut(..)
});
}
let r1: &mut Foo = R1.access_mut(..);
let r1_alias: &mut Foo = R1.access_mut(..);
This solution suffers from the problem that it's overly restrictive when it comes to borrowing different resources. You can't take a mutable reference to resource A and within the same scope take a shared reference to resource B (where A != B). Local
has the same problem so let's use it to illustrate the problem:
fn t1(mut task: Exti0, ref prio: P1, ref thr: T1) {
static A: Local<(), Exti0> = Local::new(());
static B: Local<(), Exti0> = Local::new(());
{
// This is perfectly legal but the API prevents it
let a = A.borrow(&task);
let b = B.borrow_mut(&mut task);
}
{
// what the API really wants to prevent is this
let a = A.borrow(&task);
let a_alias = A.borrow_mut(&mut task);
}
}
The workaround for this borrow checker problem is to minimize the borrows to free up the task
token ASAP. So something like this:
fn t1(mut task: Exti0, ref prio: P1, ref thr: T1) {
static A: Local<(), Exti0> = Local::new(());
static B: Local<(), Exti0> = Local::new(());
A.borrow(&task).foo();
B.borrow_mut(&mut task).foo_mut();
}
But this very unergonomic and doesn't work if you have a function call that must be called with a shared reference to resource A and a mutable reference to resource B:
fn t1(mut task: Exti0, ref prio: P1, ref thr: T1) {
static A: Local<(), Exti0> = Local::new(());
static B: Local<(), Exti0> = Local::new(());
bar(A.borrow(&task), B.borrow_mut(&mut task));
//~^ error
}
The ideal solution should allow mutably borrowing resources without requiring runtime checks and without preventing borrowing other resources. In my mind such solution would involve some sort of compile time task local borrow checker which I have no idea of how to implement but that would probably require making every single resource have a different type (static A: Resource<(), C1>
and static B: Resource<(), C1>
would need to have different types). I expect the implementation to be rather unergonomic as well, unless we implement it as a compiler plugin of sorts.
https://github.com/japaric/cortex-m-rtfm/blob/master/gen-examples.sh#L7
local examples=(
zero-tasks
one-task
two-tasks
preemption
nested
late-resources
safe-static-mut-ref
generics
full-syntax
)
The original issue was about some build errors. I now realize that I missed some important pieces of documentation. My entry point into this was the cortex-m-rtfm package, whose documentation doesn't reference some other important parts. Maybe a cortex-m-rtfm-quickstart, or just a link or two would help others out are not familiar your related work.
The cortex-m
crate defines a Peripheral
type which is a new type around the address block address of the peripheral. We use that Peripheral
in our Peripheral
constructor. svd2rust generates a bunch of cortex_m::Peripheral
"instances" (as pub const
s) which the user later uses to create a rtfm::Peripheral
static
.
The problem is that cortex_m::Peripheral
provides a borrow
method that grants access to the peripheral within a cortex_m::interrupt::free
critical section but that borrow
doesn't know about the SRP or task priorities. Problematic code looks like this:
use rtfm::{C1, P1, P2, Peripheral}
use stm32f30x::interrupt;
// RESOURCES
static GPIOA: Peripheral<stm32f30x::Gpioa, C1> = unsafe { Peripheral::new(stm32f30x::GPIOA) };
// TASKS
tasks!(stm32f30x, {
t1: Task {
interrupt: Exti0,
priority: P1,
enabled: true,
},
t2: Task {
interrupt: Exti0,
priority: P2,
enabled: true,
},
});
fn t1(_task: interrupt::Gpioa, prio: P1) {
let ceil = prio.as_ceiling();
// NOTE GPIOA has type `cortex_m_rtfm::Peripheral`
let gpioa = GPIOA.access(&prio, ceil);
// Immediately preempts
rtfm::request(t2);
}
fn t2(_task: interrupt::Gpiob, _prio: P2) {
cortex_m::interrupt::free(|cs| {
// this GPIOA has type `cortex_m::peripheral::Peripheral`
// this breaks SRP because GPIOA has ceiling C1 which is less than this task priority (P2)
let gpioa = stm32f30x::GPIOA.borrow(cs);
});
}
Remove cortex_m::interrupt::free
or have svd2rust generated code not create cortex_m::peripheral::Peripheral
instances. Instead svd2rust should generate some other type for peripheral instances but that doesn't have a borrow
method that works with cortex_m::interrupt::free
Make cortex_m::Peripheral::borrow
unsafe
if and only if the cortex-m-rtfm
crate is used with the cortex-m
crate. I don't know if this is possible at all: Perhaps add a "rtfm" Cargo feature to cortex-m
that makes cortex_m::Peripheral::borrow
unsafe
and have cortex-m-rtfm
depend on cortex-m
with that feature enabled. Because Cargo features are additive if one cortex-m
instance in the dependency graph has the "rtfm" feature enabled then the cortex-m
crate will always be compiled with that feature enabled. If this does work, I think it would probably be considered a misuse of Cargo features...
Some Cortex-M3+ chips (tiva?) only use 3 priority bits. Thus the implementation of logical
and the number of ceilings must be configurable somehow.
A standard trick to avoid deinitialization of controller's peripherals one by one before jump to bootloader is to set a cookie somewhere in RAM unaffected by reset, perform reset of the chip and check that cookie in reset vector jumping to bootloader if set. Right now quite a lot is happening before call to init, so it's possible for that code to interfere with bootloader call. It would be nice to have an ability to run user code just before anything else for such use case.
Another approach would be adding RCC deinitialization and peripheral reset functions to HAL.
See also: japaric/stm32f30x-hal#24
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.