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启用后:可以重新映射这些物理区域到任意的虚拟地址
相关推荐
EVERSPIN20 天前
VTI低功耗SRAM存储器VTI508HB08
sram·vti sram·sram存储器·低功耗sram存储器·vti sram存储器
Jia ming22 天前
SRAM与DDR地址为何不同?
ddr·sram
大熊背25 天前
ISP 节点 DDR 读写位置图
ddr·isp pipeline
s09071361 个月前
Xilinx FPGA ISERDES 使用详细介绍
fpga开发·xilinx·ddr·iserdes
EVERSPIN1 个月前
并行sram芯片介绍,并行sram芯片应用场景
sram·sram芯片·并行sram芯片·并行sram
DeeplyMind2 个月前
第4章: MMU notifier内核实现机制
linux·驱动开发·mmu·mmu notifier
【ql君】qlexcel2 个月前
DDR学习笔记1
学习·ddr
JiMoKuangXiangQu2 个月前
Linux 内存管理:页表管理简析
linux·mmu·内存管理·页表管理
赖small强3 个月前
【Linux C/C++开发】Linux 系统野指针崩溃机制深度解析
linux·mmu·crash·core dump·野指针