New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[RFC] multi-core support #204
Comments
@japaric Regarding the unanwered question, it is possible that the same interrupt number have different meanings on the same SoC, especially if one core is M class and another is A class (and on armv8, there needs to be some duplication, depending on the EL level. |
I have revised the RFC to include homogeneous multi-core support and have better documented the contract with the {device} crate. Examples of the homogeneous multi-core support can be found in the lpcxpresso55S69 repository, though the examples are unexciting because the API is still the same. I have also added a new unresolved question about compile time checking uses of the @eddyp There's an |
Overall I am very positive to this as I was not sure how SRP would handle the multi-core support! |
I think we can go ahead and FCP merge this. There isn't much choice in term of syntax and the biggest piece of design work before this can be called stable is going to be the contract with the
I have been using the lpcxpresso54114 (+pyocd) in heterogeneous mode and lpcxpresso55S69 (+jlink) in homogeneous mode (though this device is asymmetric the first core has an FPU so one should use a different compilation target for that one) with great success. The support for debugging (GDB) the second core is rather bad, IME, so you have to be a bit creative to figure out what the second core is doing :-). I also tried the nucleo WB55 but that was a waste because the second core comes with locked down firmware (read / write protection is enabled) which seems like it going to be pain in the neck to overwrite with any firmware other than ST's signed blobs. (plus this device has a radio with almost zero documentation about it; just 2 pages in the reference manual that include a block diagram of the radio and no register map) |
OK for me |
Cool, I will be playing a bit with this! |
🎉 This RFC has been formally approved. Implementation is in PR #205 (I'm going to keep this open until the PR lands). |
205: rtfm-syntax refactor + heterogeneous multi-core support r=japaric a=japaric this PR implements RFCs #178, #198, #199, #200, #201, #203 (only the refactor part), #204, #207, #211 and #212. most cfail tests have been removed because the test suite of `rtfm-syntax` already tests what was being tested here. The `rtfm-syntax` crate also has tests for the analysis pass which we didn't have here -- that test suite contains a regression test for #183. the remaining cfail tests have been upgraded into UI test so we can more thoroughly check / test the error message presented to the end user. the cpass tests have been converted into plain examples EDIT: I forgot, there are some examples of the multi-core API for the LPC541xx in [this repository](https://github.com/japaric/lpcxpresso54114) people that would like to try out this API but have no hardware can try out the x86_64 [Linux port] which also has multi-core support. [Linux port]: https://github.com/japaric/linux-rtfm closes #178 #198 #199 #200 #201 #203 #204 #207 #211 #212 closes #163 cc #209 (documents how to deal with errors) Co-authored-by: Jorge Aparicio <jorge@japaric.io>
205: rtfm-syntax refactor + heterogeneous multi-core support r=japaric a=japaric this PR implements RFCs #178, #198, #199, #200, #201, #203 (only the refactor part), #204, #207, #211 and #212. most cfail tests have been removed because the test suite of `rtfm-syntax` already tests what was being tested here. The `rtfm-syntax` crate also has tests for the analysis pass which we didn't have here -- that test suite contains a regression test for #183. the remaining cfail tests have been upgraded into UI test so we can more thoroughly check / test the error message presented to the end user. the cpass tests have been converted into plain examples EDIT: I forgot, there are some examples of the multi-core API for the LPC541xx in [this repository](https://github.com/japaric/lpcxpresso54114) people that would like to try out this API but have no hardware can try out the x86_64 [Linux port] which also has multi-core support. [Linux port]: https://github.com/japaric/linux-rtfm closes #178 #198 #199 #200 #201 #203 #204 #207 #211 #212 closes #163 cc #209 (documents how to deal with errors) Co-authored-by: Jorge Aparicio <jorge@japaric.io>
Done in PR #205 |
204: Slightly improve the bash scripts r=adamgreig a=jonas-schievink cf. rust-embedded/cortex-m#165 Co-authored-by: Jonas Schievink <jonasschievink@gmail.com>
EDIT(2019-06-18): revised with both homogeneous and heterogeneous multi-core support and better documented the contract with the
{device}
crate.Summary
Add experimental heterogeneous and homogeneous multi-core support behind
Cargo features.
Detailed design
Semantics
In multi-core applications each core will basically run its own independent RTFM
sub-application. Each core will have its own tasks and resources (
static mut
variables) and the cores will be able to communicate using message passing:
cross-core
spawn
&schedule
API.It's not possible to share resources (
static mut
variables) between cores.However, it is possible for one core to initialize another core's resources and
it's also possible to share
static
variables between cores.Feature gating
The heterogeneous multi-core support will live behind a
heterogeneous
Cargofeature considered experimental. Likewise, homogeneous multi-core support will
be gated by a
homogeneous
Cargo feature.Semver exception
We'll reserve the right to do breaking changes to both multi-core APIs in patch
releases and will recommend end users to pin to a exact version of
cortex-m-rtfm
when using either API (i.e.=0.5.0
inCargo.toml
).It's unlikely that that these breaking changes will include changes in the
end-user API or the syntax of the
#[rtfm::app]
macro. The breaking changes weforesee are at the level of the binary contract with the
{device}
crate.There's no multi-core equivalent to the
cortex-m-rt
crate orsvd2rust
-generated crates; we eventually will move to whatever the ecosystemsettles on -- this is a breaking change we expect to occur but want to avoid it
requiring a minor version bump for users of the single-core API.
Syntax
The
#[app]
attribute will gain acores
argument that indicates the number ofcores the application will use. The argument expects an integer value equal
or greater than 2. Omitting this argument indicates that the application is a
single-core application.
In multi-core mode all tasks,
#[init]
and#[idle]
functions need to indicatein which core they'll run using the
core
argument.External interrupts will also need to indicate which core may use the
interrupt. This distinction is required because one core may have an interrupt
that the other cores don't have. The distinction is also required to forward the
interrupt attributes to the right task dispatcher.
There's no syntax to indicate which core owns which resource. Resource ownership
is inferred from the
resources
argument used in tasks,#[init]
and#[idle]
functions.
late
To support flexible cross-core initialization of late resources the
init
attribute will gain a
late
argument. This argument takes a list of lateresources (identifiers) that the
#[init]
function will initialize. Thisargument can only be used in multi-core mode.
The
late
argument is not required in all scenarios. The framework will inferwhich core initializes which resources based on the presence of the
LateResources
return type. Some examples below:the cores. Use the
late
argument to disambiguate.Compile-time checks
The
#[app]
macro will reject applications where a resource is shared betweendifferent cores.
The
#[app]
macro will also reject applications where more than one softwaretask is given the same name.
It is possible, however, to name hardware tasks the same as long as the same
name is not used within one core.
It is also possible to have each core bind the "same" Cortex-M exception. The
reason for that is that each core has its own set of independent Cortex-M
exceptions.
Synchronization barriers
During the initialization phase synchronization barriers may be required for
correctness or memory safety. The
#[app]
macro will insert them whererequired. Some examples of where they are required:
Before core
#0
can send a message to (spawn
a task on) core#1
, core#1
must be out of the reset state. Thus core
#0
must wait until core#1
hasbooted before it attempts to send a message. This synchronization barrier
could be located before
init
is invoked or afterinit
returns but beforeinterrupts are enabled.
If core
#0
initializes a resource owned by core#1
then core#1
must waituntil the resource has been initialized before it enables its interrupts.
Heterogeneous vs homogeneous
When writing a multi-core application the user needs to pick between the
homogeneous
andheterogeneous
features.With the
homogeneous
feature the application will be compiled for a single(compilation) target and the output will be a single ELF image. This variant is
meant to be used with homogeneous multi-core (e.g. 2x Cortex-M33 cores)
devices, however it is also possible to use it with heterogeneous multi-core
devices with compatible instruction sets. For example, the
thumbv6m-none-eabi
compilation target can be used to target a Cortex-M4F + Cortex-M0+ device;
however, one would not be able to use the
AtomicU32.fetch_add
API orhardware accelerated floating point math on the M4F core using this approach.
With the
heterogeneous
feature the same application will be compiled formultiple (compilation) targets and the output will N (N > 1) ELF images. This
variant is meant to be used with heterogeneous multi-core devices. For
example, combining the
thumbv7em-none-eabihf
andthumbv6m-none-eabi
(compilation) targets would let the programmer fully utilize the features of a
Cortex-M4F + Cortex-M0+ device.
Examples of
homogeneous
RTFM applications can be found in the lpcxpresso55s69 repository.Examples of
heterogeneous
RTFM applications can be found in the lpcxpresso54114 repository.cargo-microamp
To support heterogeneous multi-core devices the
cortex-m-rtfm
crate willleverage v0.1.0 of the
microamp
framework. Meaning thatheterogeneous
RTFMapplications will need to be build using the
cargo-microamp
subcommand.This also means that the
{device}
crate or the application author will have tospecify the memory layout of each image using
core*.x
linker scripts. See thedocumentation of the
microamp
framework for more details.Device crate
In single-core mode the
device
argument takes a path to a crate generatedusing
svd2rust
. Assvd2rust
doesn't support multi-core devices this sectiondescribes what RTFM expects of the
{device}
module / crate in multi-core mode.Boot process
In multi-core mode the
cortex-m-rtfm
crate will not link to thecortex-m-rt
crate; This crate takes care of initializing static variables butonly supports single-core systems.
Multi-core systems have complex and non-standardized boot processes. RTFM
expects that the
{device}
crate takes care of initializing static variablesand booting all the cores in the system. It also expects the
{device}
crate tocall a function named
main
(inheterogeneous
mode) ormain_{i}
(inhomogeneous
mode) after the memory initialization is complete.By the time control is transferred to
main*
RTFM expects that:Each image (core)
.bss
and.data
sections have been initializedheterogeneous
mode only: The.shared
section, which all images share, hasbeen initialized exactly once
Vector table
In single-core mode the
{device}
crate places the device-specific part of thevector table in the right memory location. This vector table contains several
interrupt handler symbols that are weakly aliased to some default handler. The
{device}
crate also provides anInterrupt
enumeration of all the interruptsthe device has; the names of the variants of this
enum
must match the symbolnames of the interrupt handlers in the vector table. RTFM uses this fact to
check if the interrupts specified by the user exist on the device. This
enumeration also implements the
bare_metal::Nr
trait which maps each variantto its position in the vector table.
In multi-core mode, RTFM expects the
{device}
crate to provide a fullvector table for each core. Because each core could dispatch different
interrupts, instead of a single
Interrupt
enum
RTFM expects one enum percore named
Interrupt_0
,Interrupt_1
, etc.In
homogeneous
mode a single image is produced. Because more than one core mayservice the same interrupt a suffix is added to all interrupt handler symbols
to prevent symbol collisions. The suffix will have the form
_{i}
where{i}
is the core number which will be in the inclusive range
0..={cores}
. Note thatthese suffixes do not appear in application code.
xpend
The NVIC peripheral has no mechanism to "pend" an interrupt on a different core.
Also the NVIC peripheral sits on a private bus so one core can not access the
NVIC peripheral of other cores. Thus interrupt signaling between cores is a
device specific feature.
To accommodate this fact the runtime expects the
{device}
crate / module tocontain an
xpend
function that implements cross-core interrupt signaling.The
xpend
function must have the following signature:The runtime will only use this function for cross-core interrupt signaling. So
the implementer can rely on the fact that RTFM will never invoke
xpend(0, some_interrupt)
from core#0
. However, the end user might usethe API like that.
For an example implementation of this function refer to the
lpc541xx
prototype, which uses the device-specific MAILBOX peripheral.
Unresolved questions
Hardware tasks
Should we allow more than one core bind a hardware task to the same interrupt?
This seems like it should be supported by the hardware: the semantics would be
that both cores will start executing some hardware task when a peripheral
fires the corresponding interrupt signal. Note that if one core executes
rtfm::pend(SomeInterrupt)
this will have no effect on other cores.pend
As
Interrupt_{i}
andxpend
are public and safe APIs these incorrectoperations are possible:
Note that none of these operation break memory safety. The question is whether
we want to walk the extra mile and try to use the type system to prevent these
operations.
Do consider that we do not try to prevent any of these incorrect, but memory
safe, operations in single-core mode:
The text was updated successfully, but these errors were encountered: