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启用后:可以重新映射这些物理区域到任意的虚拟地址
相关推荐
sramdram4 天前
什么是SRAM,双端口SRAM芯片型号有哪些
sram·sram芯片·双端口sram
雪碧聊技术9 天前
计算机存储基石:DRAM与SRAM的核心区别与分工协作
cache·dram·sram·主存
sramdram9 天前
dual port sram,国产双口sram芯片
sram·sram芯片·双口sram芯片·国产sram芯片
mounter62510 天前
破局与守正:eBPF 在 Linux 内存管理中的应用、演进与重构构想
linux·服务器·网络·mmu·ebpf·linux kernel
JXNL@1 个月前
MRAM和DRAM有什么区别
sram
EVERSPIN2 个月前
低功耗片外扩展芯片Netsol异步sram
sram·异步sram·netsol sram·netsol
EVERSPIN2 个月前
国产异步SRAM单片机外扩专用存储芯片
单片机·嵌入式硬件·sram·国产sram·异步sram·国产异步sram
EVERSPIN2 个月前
高速串行SPI SRAM易失性存储器解决方案
sram·spi sram·高速sram·高速spi sram·串行spi sram
EVERSPIN2 个月前
sram存储器是什么,sram存储芯片选型要点
sram·存储器·sram存储器
tianrun12342 个月前
ARMv8 两级页表内存属性合并原理
虚拟化·mmu·armv8