嵌入式底层入门:RP2040 里 ROM / SRAM / Flash 的真实用途

存储器

这里,我们先先充几个核心 "映射" 概念(这是存储器访问的基础),再讲解存储器和 SRAM 的地址映射表,芯片里的资源(内存、寄存器、外设)要被访问,必须先 "编门牌号",这就是 "映射" 的本质:

  • 地址映射:给芯片里的所有资源分配唯一的 "数字门牌号"(即系统地址),比如 SRAM 的门牌号从 0x20000000 开始、ROM 从 0x00000000 开始。内核 / DMA 通过 "门牌号" 就能精准找到要访问的资源,相当于 "资源与门牌号的对应表";
  • 寄存器映射:是地址映射的子集, 给芯片里的寄存器(存配置、状态的小内存)分配门牌号,比如 "0x40008000 对应 UART 的波特率寄存器",软件写这个地址就能配置 UART,相当于 "寄存器功能与门牌号的对应表";
  • 重映射 :是 "重新分配门牌号"------ 比如原本 A 门牌号对应内存,重映射后 A 门牌号可以对应外设,灵活调整资源的访问地址,适配不同场景。
  • STM32 寄存器版的基础知识---内存映射
  • 【经验分享】STM32 入门系列-STM32 外设地址映射
  • 单片机内存及内存映射(本地地址,逻辑地址,全局地址)

存储器的访问完全基于地址映射,下面具体看 RP2040 的存储器:RP2040 有内置的 ROM 和 SRAM,并通过 QSPI 接口访问外部 Flash。

1.ROM

16kB 的只读内存(ROM)地址 0x00000000。ROM 中的内容在芯片被制造后是固定的,相当于 "芯片自带的预装工具包",它包含:

  1. 初始化启动引导例程
  2. 闪存启动顺序
  3. 闪存编程例程
  4. 支持 UF2 的大容量存储设备 USB
  5. 实用程序库,如快速浮点数

ROM 启动程序可在该链接中下载:https://github.com/raspberrypi/pico-bootrom

该 ROM 提供单周期只读总线访问,并且是在一个专用的 AHB-Lite 仲裁器上,因此它可以与其他内存设备同时访问,试图写入 ROM 没有任何效果(不会生成总线故障)。

2.SRAM

片上 SRAM 共计 264kB。在物理上,这被划分为六个 bank,这可以极大地提高了多个主服务器的内存带宽​ (即让多个总线主机(比如 Core0、Core1、DMA)能 "同时访问不同 bank",避免抢资源) ,但对于软件来说可以将其视为一个 264kB 的内存区域进行读写访问(​但物理 bank 的访问是通过地址映射实现的​)。

对每个 bank 中存储的内容没有限制:可以放应用程序、数据缓冲区等。

bank 可以分为四个 16kx32 位(每个 64kB)和两个 1kx32 位(每个 4kB)。

每个 SRAM bank 都可以通过一个专用的 AHB-Lite 仲裁器进行访问。这意味着不同的总线主机可以并行访问不同的 SRAM bank,因此每个系统时钟周期最多可以进行 4 个 32 位的 SRAM 访问(每个主机一个)。

在内存映射中,SRAM 的起始地址为 0x20000000,具体每个 bank 的基地址如下:

图 2.19 RP2040 SRAM Bank 系统地址与字地址映射表

我们可以看到,这个映射表中包含以下信息:

  • ystem address(系统地址):SRAM 的 "门牌号",内核 / DMA 通过这个地址访问 SRAM(比如 0x20000000 是 SRAM 的第一个门牌号);
  • SRAM Bank(SRAM 物理分区):SRAM 实际的物理存储块(这里显示了 Bank0~Bank3,另外还有 2 个 4kB 的小 bank);
  • SRAM word address(SRAM 字地址) :"字" 是 32 位(对应 4 个字节),这个地址是该 bank 内部的偏移门牌号(比如 "字地址 0" 是 bank 里的第一个 32 位数据)。

我们可以看到,系统地址每增加 4(因为 32 位 = 4 字节),会按 "Bank0→Bank1→Bank2→Bank3" 的顺序轮询不同 bank,然后再回到 Bank0 递增 "字地址"------ 比如:

  • 0x20000000(门牌号)→ Bank0、字地址 0;
  • 0x20000004(+4)→ Bank1、字地址 0;
  • 0x20000010(再 + 4)→ 回到 Bank0、字地址 1。

这种映射的好处是:当多个模块同时访问不同的系统地址时,其实是在访问不同的物理 bank------ 比如 Core0 访问 0x20000000(Bank0),DMA 访问 0x20000004(Bank1),两者不会互相干扰,实现 "并行访问",极大提升了 SRAM 的读写速度。

此外,SRAM 里还有 2 个 4kB 的小 bank(地址从 0x20040000 开始),可以专门分给两个核心用(比如存堆栈、高频执行的代码)------ 因为它们是独立 bank,核心访问时不会被其他模块卡住,保证了运行效率。

除了片上的 SRAM 外,还有两个专用 RAM 块可在某些情况下使用:

  • 如果禁用 flash XIP cache,则 cache 缓存将从 0x15000000 开始
  • 如果不使用 USB,USB data DPRAM 可以用作 4kB 内存,从 0x50100000 开始

3.Flash

这里,我们先引入两个关键概念:

  • QSPI 接口 ​:芯片和外部 Flash 之间的 "高速数据线"(比普通 SPI 快),负责芯片和外部 Flash 的通信。

  • XIP(Execute In Place) ​:字面是 "在原地执行"------ 不用把外部 Flash 里的程序复制到片上 SRAM,​直接访问外部 Flash 就能运行程序 ​,相当于把外部 Flash 当成 "扩展内存" 用,省了 SRAM 空间。

    图 2.20 RP2040 外部 Flash XIP(本地执行)硬件架构框图

    整个结构的核心是让外部 Flash 能像内部内存一样被访问,同时用缓存提速,还要保证通信稳定:

  • ​SSI 模块:​直接连接 QSPI 接口,是和外部 Flash 通信的 "物理层"------ 负责发送 / 接收 QSPI 信号,实际和外部 Flash 传数据的就是它。

  • ​Atomic RWType Interposer 原子操作保护器:​保证对 Flash 的读写操作是"完整不中断" 的(比如读一个 32 位数据时,不会被其他操作打断),避免数据读错 / 写乱。

  • ​Mux 多路选择器:​相当于 "数据路径开关",选择数据是从 Cache(缓存) 里读,还是直接从 Flash 里读,优先走 Cache(更快)。

  • ​Read-only Cache 只读缓存:​因为外部 Flash 速度比片上 SRAM 慢,Cache 会把 Flash 里常用的内容(比如频繁执行的程序指令)存在这里;内核访问时先查 Cache,有就直接读,不用等慢 Flash,大幅提升 XIP 的速度。

  • ​Streaming FIFO 连续数据缓冲区:​当要从 Flash 读大量连续数据(比如一大段程序、大文件)时,数据会先存在这个 FIFO 里,再传给内核,避免数据传输 "断断续续",让连续访问更流畅。

  • Decode and Config 地址解析配置单元:

    • 解析内核发来的地址:确定这个地址对应的是外部 Flash 的哪个位置;
    • 管理整个 XIP 硬件的配置:比如打开 / 关闭 Cache、设置 QSPI 的通信速度、配置 FIFO 的大小等。
  • ​Main AHBL Slave / Aux AHBL Slave:​这个 XIP 硬件连接到系统总线(AHBL)的 "接口":

    • Main 接口:负责常规的 Flash 访问(比如读零散的指令 / 数据);
    • Aux 接口:只负责Streaming FIFO 的高速连续数据传输,专门服务大流量场景。
  • ​AHBL-APB Bridge for SSI Configuration:​是 SSI 的配置接口,内核通过 APB 总线给 SSI 模块设置参数(比如 QSPI 的时钟频率、传输模式),让 SSI 能和不同型号的外部 Flash 匹配通信。

    当内核要读外部 Flash 里的程序指令时:

  1. 内核发地址给 Decode and Config,解析出是 Flash 的哪个位置;
  2. Mux 先查 Read-only Cache:如果 Cache 里有这个地址的内容,直接返回给内核;
  3. 如果 Cache 里没有,就通过 SSI 模块用 QSPI 从外部 Flash 读数据;
  4. 读回的数据先存到 Cache 里(方便下次读),同时传给内核;
  5. 如果是读一大段连续指令,数据会先存到 Streaming FIFO 里,再批量传给内核。

总而言之,外部 Flash 可以通过 QSPI 接口使用本地执行(XIP)硬件进行访问。这允许系统像访问内部内存一样寻址和访问外部闪存。