SRAM与DDR映射:MMU如何工作

文章目录

SRAM和DDR的映射也有MMU完成吗?

答案是:不完全是 ,MMU主要负责的是虚拟地址到物理地址的映射,而SRAM和DDR的物理地址分配是由更底层的硬件机制决定的。

完整的内存映射层次

1. 第一层:硬件固定映射(固定不可变)

这是物理世界的连接方式:

c 复制代码
/* ARM64的物理地址空间布局(硬件设计时决定) */
#define PHYS_SRAM_BASE  0x00000000  // 物理SRAM起始
#define PHYS_SRAM_SIZE  0x04000000  // 64MB

#define PHYS_DDR_BASE   0x40000000  // 物理DDR起始  
#define PHYS_DDR_SIZE   0xC0000000  // 3GB

#define PHYS_PERIPH_BASE 0x80000000 // 外设起始

// 这个映射是硬件连接时就确定的:
// SRAM芯片的片选信号 → 连接到地址线A[31:0]=0x0
// DDR芯片的片选信号 → 连接到地址线A[31:0]=0x40000000

2. 第二层:启动时的初始映射(由启动代码配置)

在MMU启用前,CPU通过物理地址直接访问内存:

assembly 复制代码
/* 启动阶段的物理地址访问 */
_start:
    // 1. 复位后CPU从物理地址0x0开始执行(就是SRAM)
    ldr r0, =0x0           // 物理地址0x0 -> 访问SRAM
    ldr r1, [r0]           // 从SRAM读取
    
    // 2. 初始化DDR控制器后,可以访问DDR
    ldr r0, =0x40000000    // 物理地址0x40000000 -> 访问DDR
    ldr r1, [r0]           // 从DDR读取
    
    // 3. 访问外设
    ldr r0, =0x80000000    // 物理地址0x80000000 -> 访问GPIO
    str r1, [r0]           // 写入GPIO

3. 第三层:MMU虚拟地址映射(软件配置)

MMU启用后,虚拟地址 → 物理地址

c 复制代码
/* MMU页表配置示例 */
// 虚拟地址空间布局(软件决定)
#define VA_SRAM_BASE   0xFFFF000000000000  // 高端虚拟地址
#define VA_DDR_BASE    0xFFFF000040000000
#define VA_PERIPH_BASE 0xFFFF000080000000

// 建立映射关系(在页表中)
void setup_mmu_mappings(void) {
    // 虚拟地址 → 物理地址 映射
    // VA_SRAM_BASE  → PHYS_SRAM_BASE
    // VA_DDR_BASE   → PHYS_DDR_BASE  
    // VA_PERIPH_BASE → PHYS_PERIPH_BASE
    
    // 也可以有多个虚拟地址映射到同一个物理地址
    // 0x00000000 (虚拟) → PHYS_SRAM_BASE (物理)
    // 0x20000000 (虚拟) → PHYS_SRAM_BASE (物理) ← 别名映射
}

详细流程分析

阶段1:复位后的硬件固定映射

assembly 复制代码
          CPU视图                 硬件连接
        ┌─────────┐             ┌─────────┐
        │  0x0    │ ←───────────┤ SRAM    │
        │         │   片选信号   │ 芯片    │
        │0x40000000│ ←───────────┤ DDR     │
        │         │   片选信号   │ 控制器  │
        │0x80000000│ ←───────────┤ 外设    │
        │         │   片选信号   │ 总线    │
        └─────────┘             └─────────┘
        物理地址空间             物理设备

这个映射由芯片的Memory Controller(内存控制器) 完成,通过芯片的引脚连接内部地址解码逻辑实现。

阶段2:MMU启用后的虚拟映射

c 复制代码
/*
MMU映射关系:
┌─────────────────┐        ┌─────────────────┐
│ 虚拟地址空间     │        │ 物理地址空间     │
│                 │        │                 │
│ 0x0000_0000     │ ──────→│ 0x0000_0000     │
│    (用户空间)    │        │    (SRAM)       │
│                 │        │                 │
│ 0x4000_0000     │ ──────→│ 0x4000_0000     │
│    (用户空间)    │        │    (DDR)        │
│                 │        │                 │
│ 0xFFFF_8000_0000│ ──────→│ 0x0000_0000     │
│    (内核空间)    │        │    (SRAM)       │
│                 │        │                 │
│ 0xFFFF_C000_0000│ ──────→│ 0x4000_0000     │
│    (内核空间)    │        │    (DDR)        │
└─────────────────┘        └─────────────────┘
*/

实际启动过程分析

步骤1:芯片复位(物理地址访问)

c 复制代码
/*
复位向量表位于物理地址0x0(SRAM)
┌─────────────────┐
│ 0x0: reset_vector │
│ 0x8: undef_vector │
│ 0x10: svc_vector  │
│ ...              │
└─────────────────┘
CPU直接通过物理地址访问SRAM
*/

步骤2:初始化DDR(仍然使用物理地址)

assembly 复制代码
init_ddr:
    // 配置DDR控制器(通过外设总线)
    ldr x0, =0x80000000      // 外设基址(物理)
    bl  ddr_init
    
    // 测试DDR访问
    ldr x0, =0x40000000      // DDR物理地址
    ldr x1, =0x12345678
    str x1, [x0]             // 写入DDR
    ldr x2, [x0]             // 读取验证

步骤3:配置MMU(建立虚拟映射)

c 复制代码
void setup_virtual_memory(void) {
    // 创建页表
    uint64_t *ttbr0 = (uint64_t *)PHYS_SRAM_BASE + 0x1000;
    
    // 映射SRAM: 虚拟地址0x0和0xFFFF000000000000都映射到物理0x0
    map_page(ttbr0, 0x0,              PHYS_SRAM_BASE,  64*1024*1024);
    map_page(ttbr0, 0xFFFF000000000000, PHYS_SRAM_BASE, 64*1024*1024);
    
    // 映射DDR: 虚拟地址0x40000000和0xFFFF000040000000映射到物理0x40000000
    map_page(ttbr0, 0x40000000,       PHYS_DDR_BASE,   1*1024*1024*1024);
    map_page(ttbr0, 0xFFFF000040000000, PHYS_DDR_BASE, 1*1024*1024*1024);
    
    // 启用MMU
    set_ttbr0(ttbr0);
    enable_mmu();
}

步骤4:MMU启用后的访问

c 复制代码
// MMU启用后,所有地址都经过转换
uint32_t read_sram(void) {
    // 这两个虚拟地址都映射到同一个物理SRAM
    uint32_t *addr1 = (uint32_t *)0x0;              // 用户空间虚拟地址
    uint32_t *addr2 = (uint32_t *)0xFFFF000000000000; // 内核空间虚拟地址
    
    // 实际访问的是物理SRAM的同一个位置
    return *addr1;  // == *addr2
}

为什么需要这样设计?

1. 灵活性

c 复制代码
// 可以通过MMU重新映射
void remap_for_security(void) {
    // 将SRAM映射到随机地址,增加安全性
    uint64_t random_base = get_random_address();
    map_page(ttbr0, random_base, PHYS_SRAM_BASE, SRAM_SIZE);
    
    // 旧的映射可以删除
    unmap_page(ttbr0, 0x0);
}

2. 内存保护

c 复制代码
// 设置不同的访问权限
void setup_memory_protection(void) {
    // SRAM: 可执行、可读写(用于代码)
    map_page_attr(VA_SRAM, PA_SRAM, SIZE_64MB, 
                  AP_RW | XN_DISABLE);
    
    // DDR数据区: 可读写,不可执行(防代码注入)
    map_page_attr(VA_DDR_DATA, PA_DDR_DATA, SIZE_1GB,
                  AP_RW | XN_ENABLE);
    
    // 外设: 只允许特权模式访问
    map_page_attr(VA_PERIPH, PA_PERIPH, SIZE_256MB,
                  AP_PRIV_RW | USER_NO_ACCESS);
}

3. 内存别名

c 复制代码
// 同一个物理内存可以映射到多个虚拟地址
void setup_aliases(void) {
    // 方式1:写时复制(Copy-on-Write)
    map_page(ttbr0, VA_PROCESS1, PA_SHARED, SIZE_4KB, RO);  // 只读
    map_page(ttbr0, VA_PROCESS2, PA_SHARED, SIZE_4KB, RO);  // 只读
    
    // 方式2:DMA缓冲区的不同视图
    map_page(ttbr0, VA_CPU,      PA_DMA_BUF, SIZE_4KB, CACHE_WB);  // 缓存使能
    map_page(ttbr0, VA_DEVICE,   PA_DMA_BUF, SIZE_4KB, DEVICE_MEM); // 非缓存
}

总结:三层映射关系

关键点

  1. SRAM在0x0,DDR在0x40000000是硬件设计:由芯片引脚和内存控制器固定
  2. MMU不决定这个物理布局:只负责虚拟到物理的映射
  3. 启动初期没有MMU:CPU直接使用物理地址
  4. MMU启用后:可以重新映射这些物理区域到任意的虚拟地址
相关推荐
Jia ming1 天前
SRAM与DDR地址为何不同?
ddr·sram
大熊背4 天前
ISP 节点 DDR 读写位置图
ddr·isp pipeline
s09071365 天前
Xilinx FPGA ISERDES 使用详细介绍
fpga开发·xilinx·ddr·iserdes
EVERSPIN6 天前
并行sram芯片介绍,并行sram芯片应用场景
sram·sram芯片·并行sram芯片·并行sram
DeeplyMind1 个月前
第4章: MMU notifier内核实现机制
linux·驱动开发·mmu·mmu notifier
【ql君】qlexcel2 个月前
DDR学习笔记1
学习·ddr
JiMoKuangXiangQu2 个月前
Linux 内存管理:页表管理简析
linux·mmu·内存管理·页表管理
赖small强2 个月前
【Linux C/C++开发】Linux 系统野指针崩溃机制深度解析
linux·mmu·crash·core dump·野指针
赖small强2 个月前
【Linux驱动开发】DDR 内存架构与 Linux 平台工作机制深度解析
linux·驱动开发·ddr·sdram·ddr controller