一、MMIO 基本概念
1. 定义
MMIO(Memory-Mapped I/O)是一种将 设备寄存器映射到处理器物理地址空间 的机制,CPU通过读写内存地址来访问设备寄存器,而非专用的I/O指令。
2. 核心特点
-
统一地址空间:设备寄存器与内存共享同一地址空间。
-
访问方式 :使用普通内存访问指令(如
MOV
、LDR/STR
)。 -
硬件支持:需要设备支持地址解码和总线响应。
二、MMIO 工作原理
1. 地址空间分配
-
物理地址布局示例:
0x00000000-0x3FFFFFFF: DRAM 0x40000000-0x4000FFFF: UART 设备寄存器 0x40010000-0x4001FFFF: GPIO 控制器
-
硬件解码:设备监听特定地址范围,当CPU访问该范围时,设备响应操作。
2. 访问流程
CPU发出地址 0x40000000 → 内存控制器识别为MMIO区域 → 转发请求到PCIe/SoC总线 → 目标设备响应读写
3. 寄存器操作示例
volatile uint32_t *uart_reg = (uint32_t *)0x40000000;
*uart_reg = 0x55; // 写入数据寄存器
uint32_t status = *(uart_reg + 1); // 读取状态寄存器
关键点:
volatile
禁止编译器优化,确保每次访问真实硬件。地址偏移需对齐设备寄存器布局。
三、MMIO 硬件需求
1. 必需硬件支持
组件 | 要求 |
---|---|
地址解码器 | 识别CPU访问的地址范围(如PCI BAR空间) |
总线接口 | 支持PCIe/AXI/AHB等协议 |
寄存器文件 | 设备需实现可读写的寄存器,通常为32/64位宽度 |
中断机制 | 可选,但需支持MSI或线中断以通知CPU |
2. 典型硬件实现(Verilog示例)
module mmio_device (
input wire clk,
input wire [31:0] addr,
input wire [31:0] wdata,
output reg [31:0] rdata,
input wire wr_en
);
reg [31:0] reg_file[0:15]; // 16个32位寄存器
always @(posedge clk) begin
if (wr_en) begin
reg_file[addr[5:2]] <= wdata; // 字地址对齐
end
rdata <= reg_file[addr[5:2]];
end
endmodule
四、MMIO 软件实现
1. Linux内核驱动示例
#include <linux/ioport.h>
#include <linux/io.h>
void __iomem *regs;
static int my_probe(struct pci_dev *dev) {
// 申请MMIO区域
regs = pci_iomap(dev, BAR_NUM, 0);
// 读写寄存器
iowrite32(0x12345678, regs + REG_OFFSET);
uint32_t val = ioread32(regs + STATUS_REG);
}
2. 用户空间访问(通过/dev/mem)
int fd = open("/dev/mem", O_RDWR | O_SYNC);
void *map = mmap(NULL, PAGE_SIZE, PROT_READ | PROT_WRITE,
MAP_SHARED, fd, MMIO_BASE_ADDR);
*(volatile uint32_t *)(map + 0x10) = 0x55AA; // 写寄存器
munmap(map, PAGE_SIZE);
注意 :需
CAP_SYS_RAWIO
权限,生产环境建议使用UIO/VFIO。
五、MMIO 高级特性
1. 原子操作支持
-
需求:设备寄存器需实现硬件级原子性。
-
实现方式:
// 使用内核原子API atomic64_t *reg = (atomic64_t *)(regs + ATOMIC_REG); atomic64_add(1, reg);
2. 缓存控制
-
非缓存访问 :通过
ioremap_nocache()
避免CPU缓存污染。 -
写合并 :使用
writel_relaxed()
优化频繁写操作。
3. 大端与小端
-
问题:设备可能与CPU字节序不一致。
-
解决方案:
u32 val = __cpu_to_be32(0x12345678); // 转换字节序 iowrite32(val, regs);
六、MMIO 与 PMIO 对比
特性 | MMIO | PMIO (Port-Mapped I/O) |
---|---|---|
地址空间 | 共享内存地址空间 | 专用I/O地址空间(x86的in/out指令) |
性能 | 更高(支持突发传输) | 较低(每次访问需单独指令) |
硬件复杂度 | 需地址解码逻辑 | 简单,但需要独立I/O总线 |
典型应用 | 现代PCIe设备、SoC外设 | 传统x86设备(如键盘控制器) |
七、MMIO 性能优化
1. 减少访问延迟
-
批处理:合并多个寄存器写入。
-
预取:提前读取状态寄存器。
2. DMA协同
// 配置DMA源地址为MMIO区域
dma_src_addr = MMIO_DATA_BUFFER;
dma_start_transfer();
3. NUMA优化
-
确保MMIO设备与CPU在同一NUMA节点:
numactl --cpunodebind=0 --membind=0 ./mmio_app
八、调试与问题排查
1. 常见问题
-
访问挂起:检查设备是否响应(逻辑分析仪抓取总线信号)。
-
数据损坏:确认数据宽度对齐(32位设备需32位访问)。
2. 调试工具
# 查看已映射的MMIO区域
cat /proc/iomem
# 监控MMIO访问(需CONFIG_IO_STRACE)
perf trace -e ioread*,iowrite*
九、现代扩展
1. MMIO over PCIe
-
原子操作:支持PCIe AtomicOps(如FetchAdd、CAS)。
-
TLP处理:利用PCIe事务层包优化传输。
2. CXL设备
- 内存语义:CXL 2.0+设备支持一致性MMIO访问。
MMIO是硬件与CPU高效交互的核心机制,正确使用时能实现纳秒级延迟的设备控制。设计时需综合考虑地址分配、原子性需求和缓存一致性,高性能场景可结合DMA与中断优化。