2个版本

0.1.0-alpha.52020年4月14日
0.1.0-alpha.42019年10月15日
0.1.0-alpha.3 2019年9月15日
0.1.0-alpha.2 2019年7月11日
0.1.0-alpha.1 2019年5月10日

10#asymmetric 中排名

每月25 次下载
3 个crate中使用(通过 microamp

MIT/Apache

5KB
78

μ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.xcore1.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链接部分。在上面的示例中,共享变量是在第一个图像被加载到内存时初始化的。

许可证

所有源代码(包括代码片段)都受以下其中一种许可证的许可:

由您选择。

书中的书面文字受Creative Commons CC-BY-SA v4.0许可证的条款约束 (LICENSE-CC-BY-SAhttps://creativecommons.org/licenses/by-sa/4.0/legalcode)。

贡献

除非您明确表示,否则根据Apache-2.0许可证定义的,您有意提交供作品包含的贡献,将按上述许可进行许可,没有附加条款或条件。

依赖关系

~1.5MB
~36K SLoC