4. 存储器映射与地址空间
在 PolarFire SoC 上做软硬件协同开发,第一件必须建立起来的“共同语言”就是存储器映射。CPU 发出的每一个取指、取数和外设访问,最终都要落到一个物理地址上;而 FPGA fabric 里的自定义逻辑、DDR 中的大块数据、eNVM 中的启动代码,也都要通过地址被 CPU 看见。如果地址空间理解错了,现象往往不是“报错”,而是读到全 0、全 F、总线挂死或数据错位,排查成本很高。因此本节先梳理 PolarFire SoC 的地址空间结构,再给出一个可实际运行的地址访问验证程序,用“读—写—回读—比对”的方式确认各区域是否按预期可达。
一、PolarFire SoC 地址空间总览
PolarFire SoC 采用 64 位地址空间,但实际使用中主要落在低 4GB 范围内。其核心特点是:CPU 集群(E51 监控核 + 4 个 U54 应用核)通过 AXI 交换网络访问 DDR、L2 cache、eNVM、sNVM 以及各类外设;而 FPGA fabric 中的逻辑则通过 FIC(Fabric Interface Controller)与 SoC 总线互连。理解地址映射,本质上是理解“谁在总线的哪一端、通过哪个接口、映射到哪个地址窗口”。
几个关键区域的作用如下:
- DDR:外部大容量内存,通常映射在 0x8000_0000 起始(具体以 Libero 生成的 MSS 配置和 HSS 设备树为准),用于运行 Linux、存放应用数据和大缓冲区。
- L2 cache:CPU 集群共享的二级缓存,可配置为 cache 或紧耦合的 scratchpad(LIM)。作为 scratchpad 时,它有一段独立地址窗口,访问延迟远低于 DDR,适合放中断向量、关键代码或核间共享数据。
- eNVM:非易失性存储器,存放启动镜像(HSS、U-Boot、FPGA 位流等),通常映射在 0x2022_0000 附近,容量约 128KB 级别,是只读或受保护写入的启动介质。
- sNVM:安全非易失性存储器,用于存放密钥、配置参数、设备序列号等,容量小(KB 级),访问需通过特定控制器,不是普通内存式读写。
- FIC:Fabric Interface Controller,是 CPU 侧总线与 FPGA fabric 之间的桥。CPU 通过 FIC 的 AXI 从端口访问 fabric 内实例化的 IP;fabric 内的主设备也可通过 FIC 反向访问 DDR 或 L2。
- AXI/AHB 总线:SoC 内部高速互连以 AXI 为主,低速外设(UART、SPI、GPIO、I2C 等)多挂在 AHB/APB 上,地址窗口彼此独立。
需要特别强调的是:FPGA fabric 的地址分配不是固定的。在 Libero 中为 fabric 内的 AXI 从设备分配基地址时,该地址必须落在 FIC 的地址窗口内,并与 MSS 配置、设备树保持一致。如果 fabric IP 的基地址与 DDR 或外设窗口重叠,就会出现访问冲突。
二、地址访问验证程序的设计思路
验证程序的目标不是“跑通一个功能”,而是回答三个问题:该地址是否可访问?写入后能否正确回读?不同区域的行为是否符合其介质特性?为此,程序采用“模式写入 + 回读比对”的方法,并针对不同区域设置不同的测试策略。
测试模式选择上,常用 0x55AA55AA、0xAA55AA55、0x00000000、0xFFFFFFFF 以及地址相关模式(如写入地址本身)。地址相关模式能有效发现地址线粘连或译码错误:如果写入 addr 却读回另一个值,说明地址译码或数据通路存在问题。
需要区分三类区域:
- 可读写 RAM 类(DDR、L2 scratchpad):可执行完整写入—回读—比对,并做多地址遍历。
- 只读或受保护类(eNVM):只做读取验证,确认能读到非全 0/全 F 的合理数据,不执行破坏性写入。
- 控制器访问类(sNVM):通过专用寄存器/驱动接口读取,不按普通内存指针直接解引用。
三、验证程序示例
下面给出一个基于裸机/HSS 环境的 C 语言验证片段。地址为示例值,实际使用时请替换为当前工程的 MSS 配置与设备树中的真实地址。
#include <stdint.h>
#include <stdio.h>
#define DDR_BASE 0x80000000UL
#define L2_SCRATCH_BASE 0x08000000UL /* 示例:L2 LIM 窗口 */
#define ENVM_BASE 0x20220000UL /* 示例:eNVM 窗口 */
#define FABRIC_BASE 0x60000000UL /* 示例:FIC 窗口内 fabric IP */
static const uint32_t patterns[] = {
0x55AA55AA, 0xAA55AA55, 0x00000000, 0xFFFFFFFF
};
/* 对可读写区域做写入-回读比对 */
int verify_rw(const char *name, volatile uint32_t *base, uint32_t words)
{
int errors = 0;
for (uint32_t i = 0; i < words; i++) {
for (int p = 0; p < 4; p++) {
uint32_t addr_val = (uint32_t)(base + i);
uint32_t w = patterns[p];
base[i] = w;
__sync_synchronize(); /* 内存屏障,确保写入完成 */
uint32_t r = base[i];
if (r != w) {
printf("[FAIL] %s off=0x%08X wr=0x%08X rd=0x%08X\n",
name, i * 4, w, r);
errors++;
}
}
/* 地址相关模式 */
base[i] = addr_val;
__sync_synchronize();
if (base[i] != addr_val) {
printf("[FAIL] %s addr-pattern off=0x%08X\n", name, i * 4);
errors++;
}
}
printf("[%s] %s, tested %u words\n",
errors ? "FAIL" : "PASS", name, words);
return errors;
}
/* 只读区域:确认可读且非全 0/全 F */
int verify_ro(const char *name, volatile uint32_t *base, uint32_t words)
{
uint32_t all0 = 1, allf = 1;
for (uint32_t i = 0; i < words; i++) {
uint32_t r = base[i];
if (r != 0x00000000) all0 = 0;
if (r != 0xFFFFFFFF) allf = 0;
}
if (all0 || allf) {
printf("[FAIL] %s readback all %s\n", name, all0 ? "0" : "F");
return 1;
}
printf("[PASS] %s readable, not stuck\n", name);
return 0;
}
int main(void)
{
int err = 0;
err += verify_rw("DDR", (volatile uint32_t *)DDR_BASE, 64);
err += verify_rw("L2_LIM", (volatile uint32_t *)L2_SCRATCH_BASE, 64);
err += verify_ro("eNVM", (volatile uint32_t *)ENVM_BASE, 16);
err += verify_rw("FABRIC", (volatile uint32_t *)FABRIC_BASE, 16);
printf("total errors = %d\n", err);
return err;
}
程序中的 __sync_synchronize() 用于保证写入在回读前真正到达目标,避免编译器优化或写缓冲导致“假通过”。对 DDR 和 L2 各测 64 个字,对 fabric 测 16 个字,是因为 fabric 寄存器组通常较小,且部分寄存器可能有副作用,测试范围不宜过大。
四、结果判读与常见问题
运行后应关注三类现象:
- 全部 PASS:说明地址译码、数据通路、时序均正常,可继续做功能开发。
- 某区域全 0 或全 F:通常表示该地址窗口未使能、时钟未开、或 fabric IP 未正确例化。例如 FIC 窗口读回全 0,应检查 Libero 中 FIC 是否使能、fabric IP 基地址是否与代码一致。
- 地址相关模式失败:说明地址线存在粘连或译码错误,需核对 MSS 地址分配与设备树。
此外,若访问 DDR 时出现数据错位但模式测试通过,应怀疑 cache 一致性问题:DDR 区域可能被 cache 缓存,写入后未回写。此时可在测试前后执行 cache flush/invalidate,或将测试区域配置为非缓存属性。对 L2 scratchpad,则要注意其作为 LIM 时地址窗口与 cache 模式不同,不能混用。
掌握地址映射并具备一套可复用的验证程序,是后续在 PolarFire SoC 上做 fabric 加速器、DMA 搬运和核间通信的基础。地址对了,数据通路才可信;数据通路可信,协同开发才有稳定的起点。