rtic-rs / rtic

Real-Time Interrupt-driven Concurrency (RTIC) framework for ARM Cortex-M microcontrollers
https://rtic.rs
Apache License 2.0
1.83k stars 210 forks source link

[RFC] supporting other architectures #203

Closed japaric closed 5 years ago

japaric commented 5 years ago

Current state

cortex-m-rtfm is the main port of the RTFM framework. There are two outdated ports of RTFM for MSP430 and RISCV and two experimental Cortex-R and Linux ports. None of these ports shares code with one another so they have diverged or stalled since their inception.

The cortex-m-rtfm port is under @japaric's GitHub account and currently has 3 people maintaining it.

Proposal

This RFC proposes refactoring out the RTFM syntax (meta-language) and analysis pass that currently live in cortex-m-rtfm into a crate that can be used to easily implement ports for different architectures.

Rationale

We want to make RTFM available on as many architectures as possible with minimal maintenance effort and with as similar APIs / syntax as possible.

Detailed design

This RFC proposes the following crate hierarchy:

rtfm-core

This crate will only contain the Mutex trait and the Exclusive newtype and it's meant to be used to implement generic library code.

pub trait Mutex {
    type Data;

    fn lock<T>(&mut self, f: impl FnOnce(&mut Self::Data) -> T) -> T;
}

pub struct Exclusive<T>(pub &'a mut T);

// trivial implementation (no-op)
impl<T> Mutex for Exclusive<T> { .. }

rtfm-syntax

This crate will contain the parser of the #[app] and the analysis pass (e.g. priority ceiling analysis) of the RTFM specification.

The main function in this crate is the parse function which parse the input of the #[app] attribute.

use syn::Parse;

use crate::{ast::App, analyze::Analysis};

pub fn parse(
    args: TokenStream,
    input: TokenStream,
    settings: Settings,
) -> parse::Result<(App, Analysis) {
    ..
}

To accommodate the different capabilities of the many RTFM ports this function takes a Settings struct that indicates which extensions the port implements.

pub struct Settings {
    // multi-core support: `#[app(cores = ..)]`, `#[task(core = ..)]`, etc.
    pub parse_cores: bool,

    // `#[exception]`
    pub parse_exception: bool,

    // `#[interrupt]`
    pub parse_interrupt: bool,

    // `schedule` API
    pub parse_schedule: bool,

    // `extern { fn interrupt(); }`
    pub parse_extern_interrupt: bool,

    // ..
}

$ARCH-rtfm

These are the actual ports. These crates will use the rtfm-syntax crate to implement the #[app] attribute and they'll re-export the rtfm-core API.

All ports must implement the following API:

And they may implement any of these extensions:

GitHub org

All these crates will be owned by the rtfm-rs GitHub organization and maintained by the members of the organization. The idea is to grow the membership of the organization to include people with expertise on architectures other than ARM Cortex-M.


I have already done the refactor and have tested the portability of the rtfm-syntax crate by implementing a Linux port (base features + schedule + multi-core support) and the single-core and multi-core variants of cortex-m-rtfm (see PR #205).

cc'ing people that may be interested in other ports:

@disasm (rust-embedded/riscv) @cr1901 @pftbest (rust-embedded/msp430)

Disasm commented 5 years ago

I'm interested in having RTFM for RISC-V, however I need to understand possible RISC-V-related limitations and how to deal with them (if possible).

P.S. I'm not quite familiar with RTFM, so I need to study it first in detail.

cr1901 commented 5 years ago

I need to view @korken89's presentation on RTFM before I could meaningfully contribute a port, but I am very interested and supportive of this proposal.

This would also be a good opportunity to flesh out what features are feasible to support on archs more constrained than cortex-m (e.g. msp430 has nowhere near the amount of priority levels NVIC does).

japaric commented 5 years ago

@Disasm I have a prototype implementation that targets the HiFive1 board here. The prototype implements the core API plus the schedule and #[interrupt] extensions.

IRQs are different and defined on device-level and even may not exists at all.

Even if you have zero IRQs, you can use the (machine) software (MSI) / timer (MTI) interrupt to implement (software) tasks.

We have priorities only for IRQs

But you can individually mask interrupts (MSIE, MTIE, etc.); it's possible to implement some form of prioritization (and critical sections) using interrupt masking.

SMP (not AMP) support on some chips.

(I have one of those K210 chips; too bad my "development" board has no on-board debugger (and I have no JTAG probe around) so I can't actually do any actual development on it ...)

Timers are accessed in a device-dependent way.

mtime and mtimecmp seem rather standard to me. They may be memory mapped registers and their address may be device-dependent but their binary interface and semantics are well specified. In any case, timers are not necessary to implement the core API.

Some "devices" are FPGA softcores that do not have Privileged Architecture parts at all.

We could state that some privileged parts are required to use RTFM on RISCV. If the softcore has zero support for interrupts / preemption then we can't run RTFM on it.

Disasm commented 5 years ago

@japaric Thanks for making the prototype!

too bad my "development" board has no on-board debugger (and I have no JTAG probe around)

I use BusBlaster based on FTDI chip, but I think that any JTAG adapter for FPGA would work.

mtime and mtimecmp seem rather standard to me. They may be memory mapped registers and their address may be device-dependent but their binary interface and semantics are well specified. In any case, timers are not necessary to implement the core API.

They are standard, but due to their device-dependent addressing I have no idea how to pass them to the RTFM implementation. Of cause, we could tie riscv-rtfm to the FE310 chip family, but this solution is far from perfect.

japaric commented 5 years ago

@Disasm

They are standard, but due to their device-dependent addressing I have no idea how to pass them to the RTFM implementation.

As part of the prototype I wrote a riscv crate from scratch (to force me to read the RISC-V spec). That crate contains an API to access the mtime and mtimecmp registers. To get around the device specific address I used symbols. In the riscv crate the address of the mtime register is an external symbol; this symbol must be provided by a device crate (PAC) like the hifive1 crate in that repository.

With this approach any crate can use the mtime register by depending only on riscv and not on any particular device crate. When building an application where the mtime API is used a device crate must appear in the dependency graph or you get a not really helpful error. The symbol, and the device crate, don't need to appear in the dependency graph when building libraries that use the mtime API.

There are other ways to pass device specific information to the RTFM runtime though. The Cortex-M port uses a device argument, which is a path to a module, in its #[app] attribute. This module is expected to contain an enumeration of all interrupts (Interrupt), the number of priority bits (const NVIC_PRIO_BITS: u8), etc. It's also possible for ports to define other custom #[app] arguments. One example is the monotonic argument proposed in RFC #200.

korken89 commented 5 years ago

I think this is a great idea! The power that SRP gives through this framework should fit a lot more than Cortex-M, and this is indeed a great way to get there. I also hope that something like this would help with the readability of the code, as this is quite a challenge today.

japaric commented 5 years ago

This proposal has been well received so I'm going to FCP it with disposition to merge it.

We can start right away with the refactor and take our time to roll out the org. (One extra thing I'd like to do is create an rfcs repo and archive all these RFCs as proper markdown files).

TeXitoi commented 5 years ago

OK for me

japaric commented 5 years ago

:tada: This RFC has been formally approved. The refactor part is in PR #205.

japaric commented 5 years ago

Done in PR #205