You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

能否用arm-none-eabi-gcc以软件方式实现类MMU的SPI RAM内存访问?

软件模拟外部内存访问的arm-none-eabi-gcc实现方案

针对你的Cortex-M0+(SAML21)场景,无需修改libaom/libavif源码,可通过虚拟地址映射+HardFault异常拦截的方式,将特定地址范围的内存访问自动转为SPI RAM的读写函数调用,以下是具体实现思路和技巧:

核心方案:虚拟地址+HardFault拦截

1. 定义虚拟外部RAM地址范围

选择一块与内部SRAM(0x20000000~0x2000FFFF)不重叠的地址空间作为外部SPI RAM的虚拟映射区,例如:

#define EXTERNAL_RAM_BASE 0x20010000
#define EXTERNAL_RAM_SIZE 0x100000  // 1MB,根据实际SPI RAM大小调整

2. 配置MPU触发HardFault

SAML21的Cortex-M0+内置MPU,可通过配置将虚拟地址范围设为不可访问,这样当代码访问该区域时会直接触发HardFault异常。示例配置代码:

void mpu_config_external_ram(void) {
    // 禁用MPU
    SCB->MPU_CTRL &= ~SCB_MPU_CTRL_ENABLE_Msk;
    // 配置外部RAM虚拟地址区域为不可访问
    MPU->RBAR = EXTERNAL_RAM_BASE | MPU_RBAR_VALID_Msk;
    MPU->RASR = MPU_RASR_SIZE_Msk(MPU_REGION_SIZE_1MB) |
                MPU_RASR_AP_Msk(MPU_AP_NONE) |
                MPU_RASR_ENABLE_Msk;
    // 启用MPU并允许硬 fault 触发
    SCB->MPU_CTRL |= SCB_MPU_CTRL_ENABLE_Msk | SCB_MPU_CTRL_HFNMIENA_Msk;
}

3. 编写HardFault异常处理函数

在HardFault handler中解析栈帧,识别内存访问指令(LDR/STR等),提取目标地址、操作类型(读/写)和数据,调用SPI RAM读写函数后,修改栈帧中的PC指针跳过当前指令,继续执行。

示例框架(需根据Thumb-1指令集完善指令解析逻辑):

void HardFault_Handler(void) {
    // 获取栈帧指针(异常发生时自动保存的上下文)
    uint32_t *stack_frame = (uint32_t *)__get_MSP();
    uint32_t pc = stack_frame[6];  // 栈帧中PC的位置
    uint16_t instr = *(uint16_t *)pc;  // 获取当前执行的Thumb-1指令

    // 判断是否为访问虚拟外部RAM的指令
    uint32_t target_addr = 0;
    uint8_t is_read = 0;
    uint32_t data = 0;
    uint8_t rd = 0;

    // 解析LDR/STR指令(示例仅处理简单的立即数偏移寻址,需扩展其他寻址方式)
    if ((instr & 0xF000) == 0x6000) {  // STR Rd, [Rn, #imm]
        is_read = 0;
        rd = (instr >> 8) & 0x0F;
        uint8_t rn = (instr >> 0) & 0x0F;
        uint32_t imm = (instr >> 4) & 0x0F;
        target_addr = stack_frame[rn] + imm;
        data = stack_frame[rd];
    } else if ((instr & 0xF000) == 0x7000) {  // LDR Rd, [Rn, #imm]
        is_read = 1;
        rd = (instr >> 8) & 0x0F;
        uint8_t rn = (instr >> 0) & 0x0F;
        uint32_t imm = (instr >> 4) & 0x0F;
        target_addr = stack_frame[rn] + imm;
    }

    // 检查目标地址是否在虚拟外部RAM范围内
    if (target_addr >= EXTERNAL_RAM_BASE && target_addr < (EXTERNAL_RAM_BASE + EXTERNAL_RAM_SIZE)) {
        uint32_t offset = target_addr - EXTERNAL_RAM_BASE;
        if (is_read) {
            // 调用SPI RAM读函数
            data = spi_ram_read(offset);
            stack_frame[rd] = data;  // 将读取的数据放回栈帧的对应寄存器
        } else {
            // 调用SPI RAM写函数
            spi_ram_write(offset, data);
        }
        // 修改PC指针,跳过当前16位指令
        stack_frame[6] += 2;
        // 退出异常,继续执行
        return;
    }

    // 非外部RAM访问的HardFault,进入死循环(可添加调试逻辑)
    while (1);
}

辅助技巧:自动分配虚拟地址

为了让libaom/libavif的内存分配自动落在虚拟外部RAM区域,可通过链接器--wrap选项包装标准内存分配函数:

  1. 在编译选项中添加链接器参数:
-Wl,--wrap=malloc -Wl,--wrap=calloc -Wl,--wrap=free
  1. 实现包装函数:
void *__wrap_malloc(size_t size) {
    // 简单顺序分配(需根据实际需求实现外部RAM的内存管理逻辑)
    static uint32_t current_offset = 0;
    if (current_offset + size > EXTERNAL_RAM_SIZE) return NULL;
    void *addr = (void *)(EXTERNAL_RAM_BASE + current_offset);
    current_offset += size;
    return addr;
}

void __wrap_free(void *ptr) {
    // 实现外部RAM的内存释放逻辑(若无需动态释放可简化)
    if ((uint32_t)ptr >= EXTERNAL_RAM_BASE && (uint32_t)ptr < (EXTERNAL_RAM_BASE + EXTERNAL_RAM_SIZE)) {
        // 标记对应偏移为可用(需配合内存管理链表实现)
    }
}

void *__wrap_calloc(size_t nmemb, size_t size) {
    void *ptr = __wrap_malloc(nmemb * size);
    if (ptr) {
        // 清零外部RAM对应区域
        for (size_t i = 0; i < nmemb * size; i++) {
            spi_ram_write((uint32_t)ptr - EXTERNAL_RAM_BASE + i, 0);
        }
    }
    return ptr;
}

注意事项

  • 指令解析需覆盖libaom/libavif中用到的所有内存访问指令类型(如寄存器偏移、多寄存器加载/存储等),否则会触发未处理的HardFault。
  • HardFault handler需尽量精简,避免在handler中执行可能再次触发内存访问的操作(如printf),防止嵌套异常。
  • 该方案速度极慢,每次内存访问都需经历异常进入、指令解析、SPI传输、异常退出,仅适合对性能要求极低的场景。
  • 确保SPI RAM读写函数spi_ram_read/spi_ram_write是可重入且能在HardFault环境下安全执行(如关闭全局中断)。

内容的提问来源于stack exchange,提问作者unknownperson

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.07 12:35:18