新唐MCU技术
直播中

mintsy

9年用户 1573经验值
擅长:MEMS/传感技术
私信 关注
[问答]

如何在 MA35 系列微处理器 (MPU) 上开发 AMP(非对称多处理)应用程序?

如何在 MA35 系列微处理器 (MPU) 上开发 AMP(非对称多处理)应用程序,并通过建立多个端点的过程促进与其他内核的多通道数据传输。

回帖(1)

那年我十七_

2025-8-21 18:25:12

新唐科技 (Nuvoton) MA35 系列微处理器(如 MA35D1, MA35H0)上开发 AMP (Asymmetric Multiprocessing) 应用程序并实现多通道数据传输,需要充分利用其双核异构架构(典型的组合是一个或多个高性能 Cortex-A35 内核 + 一个或多个实时 Cortex-M4 内核)并建立高效的核间通信 (Inter-Processor Communication, IPC) 机制。以下是开发步骤和关键技术的详细指南:




1. 理解 MA35 AMP 架构与启动流程



  • 异构核心:

    • Cortex-A Core(s) (e.g., A35): 通常运行富操作系统如 Linux(Yocto Project, Buildroot),处理复杂的应用逻辑、网络、文件系统、GUI等。

    • Cortex-M Core(s) (e.g., M4): 通常运行实时操作系统 (RTOS) 如 FreeRTOS、RT-Thread、Zephyr 或裸机程序,处理实时性要求高的任务(电机控制、传感器采集、快速响应中断、协议栈等)。


  • AMP 模式: 两个核心独立运行自己的操作系统或裸机程序,彼此通过定义的 IPC 机制协作,而非共享内存空间和调度(这与 SMP 不同)。

  • 启动流程 (BootROM):

    1. 上电后,BootROM 执行。

    2. 确定启动设备(如 eMMC, SPI-NAND, SD Card)。

    3. 加载并验证 BL31 (ARM Trusted Firmware, ATF): 处理安全启动、电源管理、平台初始化。

    4. BL31 负责启动配置:

      • A Core 的镜像(如 U-Boot SPL/U-Boot 或直接 Linux Kernel)加载到其内存区域并启动它。

      • M Core 的固件(如 RTOS 镜像)加载到其专用的 TCM (Tightly Coupled Memory) 或共享内存中的指定区域。

      • 关键点: BL31 配置决定了哪个核是主核(通常是 A Core 先启动)、内存映射(特别是共享内存区域)、IPC 初始化(邮箱、中断路由)。






2. 建立核间通信 (IPC) 基础


核间通信是 AMP 应用的核心。MA35 提供了硬件机制:



  • 硬件邮箱 (Mailbox):

    • 物理上通常是几个硬件 FIFO 寄存器和中断控制器。

    • 工作方式: 核 A 将消息写入核 B 的邮箱 FIFO -> 触发核 B 的邮箱中断 -> 核 B 在中断服务程序 (ISR) 中读取消息并处理。

    • 特点: 低延迟、可靠、硬实时性(对 M 核重要)。


  • 共享内存 (Shared Memory):

    • 在系统内存 (DDR) 中划分出一块非缓存 (Non-Cacheable) 或带 Cache Coherence 的区域(如果硬件支持,如 MA35 的 CCI)。

    • 作用: 传输大量数据、状态信息、缓冲区指针、配置块等。

    • 关键: 必须确保双方对内存区域的地址映射一致,并使用正确的内存属性(强烈推荐使用 memremap 或设备树配置为 non-cacheablecoherent 以避免缓存一致性问题)。


  • 硬件信号量 (Semaphore): 用于保护对共享资源的并发访问(如共享内存中的数据结构)。

  • 中断 (Interrupts): 除了邮箱中断,也可以配置其他外设中断信号用于核间通知(但邮箱是最常用和优化的方式)。


配置共享内存



  1. 定义区域: 在 BL31/ATF 或 U-Boot 的早期启动代码中,或者在系统设备树 (Device Tree) 中,定义一个固定的物理内存区域用作共享内存。例如,预留 DDR 中 0xA0000000 - 0xA00FFFFF (1MB)。

  2. 属性设置:

    • Linux 设备树 (dts) 中: 将此区域定义为 reserved-memory 节点,并设置属性如 no-map(防止 Linux 使用)和 compatible = "shared-dma-pool" (可选)。在驱动中通过 of_reserved_mem_lookupmemremap 获取映射。

    • RTOS (M4) 端: 在链接脚本 (Linker Script) 中将共享内存区域的物理地址映射到 M4 的地址空间(可能需要 MMU 配置或直接访问)。使用 MPU (Memory Protection Unit) 配置该区域为 Non-cacheable(若无硬件一致性)和可读写。

    • 一致性: 如果 MA35 的硬件支持(查阅文档),启用 SCU (Snoop Control Unit) 或 CCI (Cache Coherent Interconnect) 以实现硬件级缓存一致性,可以简化开发(不需要软件刷缓存)。否则,必须使用软件 cache flush (A核) / cache invalidate (M核) 操作。





3. 实现多通道数据传输(多个端点)


目标是建立类似“端口”或“队列”的抽象,允许多个独立的数据流在双核间并行传输。


设计与实现策略




  1. 通道结构定义 (共享内存中):



    • 在共享内存中定义一个数据结构数组(如 channel_t channels[MAX_CHANNELS])。

    • 每个 channel_t 包含:

      • uint8_t id; // 通道 ID

      • volatile uint32_t head; // 生产者索引 (写指针)

      • volatile uint32_t tail; // 消费者索引 (读指针)

      • uint32_t size; // 环形缓冲区大小

      • uint8_t* buffer; // 指向环形缓冲区起始地址的指针 (也在共享内存内)

      • volatile uint32_t flags; // 状态标志 (e.g., CHAN_FULL, CHAN_EMPTY, CHAN_ERROR)

      • uint32_t mailbox_int_id; // 与此通道关联的邮箱命令/中断号 (自定义)





  2. 初始化通道:



    • A 核和 M 核在各自的初始化代码中,根据共享内存中预定义的结构初始化各自的通道管理变量(指向 channels[]buffer)。

    • 复位 head, tail, flags




  3. 数据发送流程 (例如 A核 -> M核):


    // A核 (Linux) 发送数据到 Channel ID
    int amp_send_data(int chan_id, const void* data, size_t len) {
        channel_t* chan = &channels[chan_id];
        // 1. 检查通道状态 (是否有足够空间) - 考虑使用原子操作
        // 2. 获取环形缓冲区写锁 (例如使用共享内存中的自旋锁或原子操作)
        // 3. 将数据复制到 chan->buffer + chan->head
        // 4. 更新 chan->head (环形计算: head = (head + len) % chan->size)
        // 5. 更新状态标志 (e.g., 设置非空标志)
        // 6. 释放锁
        // 7. **触发 IPC:** 通过 MA35 Mailbox 驱动程序发送一个消息到 M核。
        //    消息内容: (MSG_TYPE_DATA_READY | chan_id) 或其他自定义命令。
        //    linux_mailbox_send(M4_CORE_MAILBOX, command);
        return 0; // 或成功字节数
    }



  4. 数据接收流程 (M核 处理来自 A核 的数据):


    // M核 (RTOS) 邮箱中断服务程序 (ISR)
    void Mailbox_ISR(void) {
        uint32_t cmd = read_mailbox_fifo();
        uint32_t msg_type = cmd & MSG_TYPE_MASK;
        uint32_t chan_id = cmd & CHAN_ID_MASK;

        if (msg_type == MSG_TYPE_DATA_READY) {
            // 1. 查找对应通道 chan_id 的 channel_t* chan
            // 2. 通知任务/线程: 通常使用 RTOS 的信号量、队列或事件标志组,唤醒处理此通道数据的任务。
            //    例如: xSemaphoreGiveFromISR(sem_channel[chan_id], ...);
        }
        // ... 处理其他类型消息
    }

    // M核 数据处理任务 (例如 FreeRTOS Task)
    void data_rx_task(void* pvParameters) {
        int my_chan_id = ...; // 此任务负责的通道 ID
        while (1) {
            // 等待该通道有数据的信号量 (由 ISR 给出)
            if (xSemaphoreTake(sem_channel[my_chan_id], portMAX_DELAY) == pdTRUE) {
                channel_t* chan = &channels[my_chan_id];
                // 1. 获取环形缓冲区读锁
                // 2. 计算可读数据长度
                // 3. 从 chan->buffer + chan->tail 读取数据
                // 4. 更新 chan->tail (环形计算)
                // 5. 更新状态标志 (e.g., 清除非空标志)
                // 6. 释放锁
                // 7. 处理接收到的数据 (data_ptr, data_len)
            }
        }
    }



  5. 反向通道 (M核 -> A核):



    • 原理完全相同,方向相反。

    • M 核写共享内存中的通道缓冲区,然后通过 Mailbox 触发 A 核的中断。

    • A 核需要:

      • Linux 驱动: 实现一个字符设备驱动或 rpmsg 驱动。

      • Mailbox 驱动: 注册 Mailbox 控制器驱动和客户端驱动,实现接收到 M 核中断后的回调函数。

      • 回调函数: 在中断下半部(如 tasklet, workqueue)或内核线程中,解析收到的命令(包含通道 ID),找到对应通道,读取数据,并可能通过 wake_up_interruptible 唤醒等待该通道数据的用户空间进程(如果使用 read 系统调用)或通过回调通知内核其他模块。





  6. 多通道管理:



    • 使用 chan_id 区分不同通道。

    • 每个通道可以独立配置缓冲区大小。

    • 不同的通道可以由不同的内核任务/线程处理,实现真正的并行。






4. 开发环境与工具链



  • Cortex-A (Linux):

    • 工具链: ARM GNU Toolchain (aarch64-linux-gnu-gcc 等)。

    • SDK: 新唐提供的 Linux BSP (Board Support Package),包含 U-Boot, Linux Kernel (带 MA35 驱动支持,确保 Mailbox 驱动已启用并正确配置),Yocto/Buildroot 配置文件。

    • 开发: 在 x86 主机上交叉编译内核、驱动、应用程序。使用 scp/nfs 部署到目标板。


  • Cortex-M (RTOS):

    • 工具链: ARM GNU Toolchain (arm-none-eabi-gcc)。Keil MDK, IAR EWARM 也可用。

    • SDK: 新唐提供的 NuMaker 开发板 SDK,包含 RTOS 移植示例 (FreeRTOS, RT-Thread 等)、外设驱动库、启动文件、链接脚本。

    • 开发: 在 IDE (Keil, IAR, VS Code + PlatformIO) 或命令行中编译项目,生成 .axf.bin 文件。通过 NuLink 或 J-Link 烧录到 M 核存储器区域。





5. 关键开发步骤总结



  1. 硬件设计: 确认板级设计满足电源、时钟和外设需求。

  2. 获取 BSP: 下载新唐官方提供的 MA35 Linux BSP 和 M4 RTOS SDK/NuMaker SDK。

  3. 配置启动镜像:

    • 定制 U-Boot 或 ATF:包含共享内存地址定义。

    • 编译 Linux Kernel:确保启用 Mailbox、共享内存驱动支持。

    • 编译 M4 固件:配置链接脚本映射共享内存。


  4. 设计 IPC 协议:

    • 定义 Mailbox 消息格式(命令字 + 通道 ID + ...)。

    • 定义共享内存中的通道数据结构。

    • 确定环形缓存大小和数量。


  5. 实现双端驱动/库:

    • A 核 (Linux): 实现 Mailbox 通信驱动、共享内存管理驱动、提供用户空间 API (如字符设备 /dev/amp_chanX) 或内核 API。

    • M 核 (RTOS): 实现 Mailbox ISR、通道管理结构、数据读写函数、RTOS 任务同步机制。


  6. 应用程序开发:

    • A 核: 开发主应用(如网络服务器、GUI),调用 IPC API 与 M 核交换数据。

    • M 核: 开发实时任务(如 PID 控制环路、高速 ADC 采集),调用 IPC API 与 A 核交换数据或报告状态。


  7. 集成与调试:

    • 分别烧录 A 核和 M 核镜像。

    • 使用 devmem2 (Linux) 或调试器查看共享内存内容。

    • 使用逻辑分析仪或示波器抓取 Mailbox 中断信号。

    • Linux 端使用 dmesg 查看内核驱动日志。

    • M4 端使用 SWO/JTAG/Semihosting 输出调试信息。

    • 逐步测试每个通道的收发功能。





6. IPC 机制选择建议



  • 小型控制消息/通知: 优先使用 Mailbox。它低延迟、可靠、硬实时。

  • 中等规模数据流 (KB 级): 使用 Mailbox + 共享内存环形缓冲区。这是实现多通道传输的最佳实践。

  • 大型数据传输 (>100KB): 仍然使用共享内存环形缓冲区,但可能需要更复杂的流控和分片机制。避免在 Mailbox 中传输大块数据。

  • 协议封装 (可选): 对于更复杂的应用,可以在共享内存通道之上实现更高层协议,如 rpmsg (Remote Processor Messaging)。新唐 BSP 可能提供 rpmsg 支持,它建立在 Mailbox 和共享内存之上,提供了类似 socket 的抽象(/dev/rpmsgX),简化开发但可能增加开销。评估是否需要其特性。




7. 挑战与注意事项



  • 缓存一致性: 这是最大痛点!严格配置共享内存区域属性non-cacheablecoherent)。如果不支持硬件一致性,发送方在写入后必须 flush cache,接收方在读取前必须 invalidate cache。错误会导致数据损坏且难以调试。

  • 内存屏障: 在读写共享内存的指针 (head, tail, flags) 时,使用内存屏障指令确保执行顺序和内存可见性。

  • 原子访问: 对共享内存中的标志、计数器等进行操作时,使用原子操作或锁(自旋锁、互斥锁)保护。

  • 中断处理: RTOS 端的 Mailbox ISR 必须快速,尽快唤醒处理任务。Linux 端的 Mailbox 驱动通常在下半部处理。

  • 同步与死锁: 双核间复杂的同步逻辑容易引入死锁。设计要简洁,超时机制很重要。

  • 性能分析: 监控通道利用率、延迟、抖动,优化缓冲区大小和优先级。

  • 调试难度: AMP 调试比 SMP 困难得多(两个独立调试会话)。充分利用日志、内存查看和硬件信号调试。




遵循以上步骤并仔细处理缓存和同步问题,你就能在 MA35 系列 MPU 上成功开发出高效、可靠的 AMP 应用程序,并通过精心设计的多通道数据传输机制,充分利用其异构多核的强大能力。务必详细参阅新唐官方提供的 MA35 技术参考手册 (TRM)数据手册 (DS) 以及 BSP 包中的示例代码和应用笔记

举报

更多回帖

发帖
×
20
完善资料,
赚取积分