1 个不稳定版本
0.1.0-alpha.3 | 2019年10月15日 |
---|---|
0.1.0-alpha.2 |
|
0.1.0-alpha.1 |
|
#1882 in 嵌入式开发
19KB
459 行
μAMP
一个用于构建裸机 AMP (非对称多处理) 应用的 (微) 框架
特性
-
允许您编写单源多核应用程序
-
为处理器间通信提供零成本共享内存抽象。
-
使用内置的条件编译特性(
#[cfg(core = "0")]
/cfg!(core = "1")
)来在不同核心之间划分您的应用程序。
计划中的特性
- 多目标支持。
该框架目前使用 相同 的编译目标为所有核心。这限制了支持的系统为同构多核设备(所有核心使用完全相同的指令集)以及异构多核设备(核心共享最低公共指令集,例如,Cortex-M4 + Cortex-M0 设备中的两个核心都可以运行编译为 thumbv6m-none-eabi
的程序)。
我们需要设计(并测试)一个命令行标志来指定或覆盖每个核心的编译目标。
已知限制
- 该框架目前仅支持 ARM 架构。
为了解除这个限制,我们需要在 Rust 中实现 strip
命令的功能。具体来说,这个调用需要移植到 Rust:strip -R '*' -R '!.shared' --strip-unneeded
。如果您知道任何可以做到这一点的 crate,请在问题跟踪器中告诉我!
已知问题
- 在核心之间发送/共享函数指针或特质对象可能是不可靠的,或者至少是一个非常糟糕的想法,但在函数指针的情况下,这并没有被完全拒绝。
框架试图在编译时防止这种操作。目前所有特质对象都被拒绝,但只有具有 0 到 12 个参数的函数指针被拒绝。要拒绝所有函数指针,我们需要可变泛型 (VG) 语言特性。
示例
这是一个在均质双核设备(2x Cortex-R5 核心)上运行的程序。
#![no_main]
#![no_std]
use core::sync::atomic::{AtomicU8, Ordering};
use arm_dcc::dprintln;
use microamp::shared;
use panic_dcc as _; // panic handler
use zup_rt::entry;
// non-atomic variable
#[shared] // <- means: same memory location on all the cores
static mut SHARED: u64 = 0;
// used to synchronize access to `SHARED`
#[shared]
static SEMAPHORE: AtomicU8 = AtomicU8::new(CORE0);
// possible values for SEMAPHORE
const CORE0: u8 = 0;
const CORE1: u8 = 1;
const LOCKED: u8 = 2;
#[entry]
fn main() -> ! {
let (our_turn, next_core) = if cfg!(core = "0") {
(CORE0, CORE1)
} else {
(CORE1, CORE0)
};
dprintln!("START");
let mut done = false;
while !done {
// try to acquire the lock
while SEMAPHORE
.compare_exchange(our_turn, LOCKED, Ordering::AcqRel, Ordering::Relaxed)
.is_err()
{
// busy wait if the lock is held by the other core
}
// we acquired the lock; now we have exclusive access to `SHARED`
unsafe {
if SHARED >= 10 {
// stop at some arbitrary point
done = true;
} else {
dprintln!("{}", SHARED);
SHARED += 1;
}
}
// release the lock & unblock the other core
SEMAPHORE.store(next_core, Ordering::Release);
}
dprintln!("DONE");
loop {}
}
在这个示例中,我们有两个在共享内存中的静态变量,对两个核心都可见(*)。其中一个变量,SEMAPHORE
,用于同步对非原子的 SHARED
变量的访问。两个核心都会在引导时执行 main
函数,但由于使用了 cfg!
宏,它们将执行略微不同的代码路径。
构建应用程序时,我们使用以下命令
$ cargo microamp --bin app --release
Compiling zup-rtfm v0.1.0 (/tmp/firmware)
Finished dev [unoptimized + debuginfo] target(s) in 0.32s
Compiling zup-rtfm v0.1.0 (/tmp/firmware)
Finished dev [unoptimized + debuginfo] target(s) in 0.12s
Compiling zup-rtfm v0.1.0 (/tmp/firmware)
Finished dev [unoptimized + debuginfo] target(s) in 0.12s
默认情况下,该命令会产生两个镜像,每个核心一个。
$ # image for first core
$ size -Ax target/armv7r-none-eabi/release/examples/app-0
target/armv7r-none-eabi/release/examples/app-0 :
section size addr
.text 0x360 0x0
.local 0x0 0x20000
.bss 0x0 0xfffc0000
.data 0x0 0xfffc0000
.rodata 0x40 0xfffc0000
.shared 0x10 0xfffe0000
$ # image for second core
$ size -Ax target/armv7r-none-eabi/release/examples/app-1
target/armv7r-none-eabi/release/examples/app-1 :
section size addr
.text 0x360 0x0
.local 0x0 0x20000
.bss 0x0 0xfffd0000
.data 0x0 0xfffd0000
.rodata 0x40 0xfffd0000
.shared 0x10 0xfffe0000
如果我们在这台核心 #0 上运行镜像,我们会看到
$ # on another terminal: load and run the program
$ CORE=0 xsdb -interactive debug.tcl amp-shared-0
$ # output of core #0
$ tail -f dcc0.log
START
0
程序停止,因为它正在等待另一个核心。现在,我们在核心 #1 上运行另一个镜像。
$ # on another terminal: load and run the program
$ CORE=1 xsdb -interactive debug.tcl amp-shared-1
$ # output of core #1
$ tail -f dcc1.log
START
1
3
5
7
9
DONE
然后我们会从核心 #0 获得新的输出。
$ # output of core #0
$ tail -f dcc0.log
START
0
2
4
6
8
DONE
(*) 重要 所有未标记为 #[shared]
的静态变量将在每个核心中 实例化,并且很可能具有不同的地址(即使它们具有相同的符号名称),这是由于编译器优化和链接脚本差异造成的。例如,非 #[shared]
变量 static mut X: u32
在一个镜像中的地址可能是 0xffe20000
,而在另一个镜像中的地址可能是 0xffeb0000
。
要求
用户或 crate 必须为每个核心提供一个链接脚本 。cargo-microamp 工具将使用这些链接脚本为每个核心链接程序,并期望它们命名为 core0.x
,core1.x
等。
cargo-microamp
将在链接每个镜像时传递一个名为 microamp-data.o
的文件。这个目标文件包含所有名为 .shared
的 #[shared]
变量。这些变量必须放在名为 .shared
的输出部分。这个部分必须在所有镜像中位于 相同 的地址。例如
$ cat core0.x
SECTIONS
{
/* .. */
.shared : ALIGN(4)
{
KEEP(microamp-data.o(.shared));
. = ALIGN(4);
} > OCM0
/* .. */
}
$ cat core1.x
SECTIONS
{
/* .. */
/* NOTE(NOLOAD) core 0 will initialize this shared section */
.shared (NOLOAD) : ALIGN(4)
{
KEEP(microamp-data.o(.shared));
. = ALIGN(4);
} > OCM0
/* .. */
}
此外,必须注意 不要 多次初始化这个 .shared
链接部分。在上面的例子中,共享变量是在将第一个镜像加载到内存中时初始化的。
许可
所有源代码(包括代码片段)均受以下任一许可协议的许可:
-
Apache License,版本 2.0(LICENSE-APACHE 或 https://apache.ac.cn/licenses/LICENSE-2.0)
-
MIT 许可证(《LICENSE-MIT》或 https://opensource.org/licenses/MIT)
您可选择其中之一。
本书中的书面文本受 Creative Commons CC-BY-SA v4.0 许可协议的条款约束(《LICENSE-CC-BY-SA》或 https://creativecommons.org/licenses/by-sa/4.0/legalcode)。
贡献
除非您明确说明,否则根据 Apache-2.0 许可证定义的,您有意提交以包含在工作中的任何贡献,应按上述方式许可,不附加任何额外条款或条件。
依赖项
~2–10MB
~91K SLoC