4个版本

0.1.0-alpha.72021年9月27日
0.1.0-alpha.62021年3月3日
0.1.0-alpha.52020年4月14日
0.1.0-alpha.42019年10月15日
0.1.0-alpha.1 2019年5月10日

699嵌入式开发 中排名

Download history 62/week @ 2024-03-31

每月下载量:56
2 crates 中使用

MIT/Apache

16KB
58

μ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-SA》或 https://creativecommons.org/licenses/by-sa/4.0/legalcode》)。

贡献

除非您明确表示,否则根据 Apache-2.0 许可证定义,您有意提交的、旨在包含在作品中的任何贡献都应按上述方式许可,无需任何附加条款或条件。

依赖项

~1.5MB
~34K SLoC