4. SD卡控制器硬件架构:寄存器布局、DMA引擎、中断控制器、时钟分频器
SD卡控制器是连接SoC系统总线与SD卡物理层的桥梁。在QEMU模拟中,精确实现控制器的寄存器布局、DMA传输机制、中断状态管理以及时钟分频逻辑,是保证Guest OS能够正确识别并驱动SD卡设备的关键。本章将深入剖析SDHCI(SD Host Controller Interface)标准下的硬件架构核心模块。
4.1 寄存器布局:控制与状态的核心映射
SDHCI规范定义了一套标准的寄存器地址空间,通常占用系统内存映射I/O(MMIO)区域的0x1000字节(4KB)。QEMU模拟器通过注册一个MemoryRegion来捕获Guest OS对这些寄存器的读写操作。寄存器布局遵循“功能分组”原则,每个寄存器为32位宽,但部分寄存器仅使用低16位或低8位。
4.1.1 关键寄存器分组与偏移
| 偏移地址 | 寄存器名称 | 宽度 | 功能描述 |
|---|---|---|---|
| 0x00 | SDMA System Address | 32-bit | SDMA模式下,存放系统内存中的数据传输起始地址 |
| 0x04 | Block Size | 16-bit | 定义每次数据传输的块大小(字节),通常为512 |
| 0x06 | Block Count | 16-bit | 定义需要传输的总块数(用于多块传输) |
| 0x0C | Argument | 32-bit | 存放即将发送给SD卡的命令参数(如地址) |
| 0x10 | Transfer Mode | 16-bit | 配置数据传输方向(读/写)、DMA使能、多块模式等 |
| 0x12 | Command | 16-bit | 存放命令索引(CMD0-CMD63)及响应类型选择 |
| 0x20 | Response[0..3] | 128-bit | 存放SD卡返回的命令响应(R1/R2/R3/R6/R7) |
| 0x28 | Buffer Data Port | 32-bit | PIO模式下,CPU通过此端口读写数据缓冲区 |
| 0x2C | Present State | 32-bit | 反映控制器当前状态:卡检测、写保护、数据线电平、CMD线状态 |
| 0x30 | Host Control | 8-bit | 控制复位、中断使能、数据总线宽度(1-bit/4-bit) |
| 0x34 | Power Control | 8-bit | 控制SD卡供电电压(3.3V/1.8V)及电源开关 |
| 0x38 | Clock Control | 16-bit | 配置时钟分频系数、时钟使能、SD时钟频率选择 |
| 0x3C | Timeout Control | 8-bit | 设置数据超时计数值(基于SD时钟周期) |
| 0x40 | Software Reset | 8-bit | 软件复位数据线、CMD线或整个控制器 |
| 0x44 | Interrupt Status | 32-bit | 反映所有中断事件的状态(写1清除) |
| 0x48 | Interrupt Enable | 32-bit | 屏蔽或使能各个中断源 |
| 0x50 | Capabilities | 64-bit | 只读寄存器,报告控制器支持的特性(电压、DMA类型、时钟频率上限) |
4.1.2 寄存器访问流程示例(发送CMD0)
- 检查状态: 读取Present State寄存器,确认CMD线空闲且卡处于空闲状态。
- 设置参数: 向Argument寄存器写入CMD0的参数(GO_IDLE_STATE无参数,写入0x00000000)。
- 发送命令: 向Command寄存器写入命令索引0(CMD0)及响应类型(无响应)。
- 硬件触发: QEMU的写回调函数检测到Command寄存器写入后,立即构造SD命令帧并通过模拟的SD总线发送给卡模型。
- 等待完成: 轮询Interrupt Status寄存器中的Command Complete位,或等待中断触发。
4.2 DMA引擎:高效数据搬运的硬件加速器
SDHCI支持多种DMA传输模式,用于在SD卡数据缓冲区和系统内存之间搬运数据,无需CPU逐字干预。QEMU模拟的DMA引擎主要实现SDMA(Single Operation DMA)和ADMA2(Advanced DMA Version 2)两种模式。
4.2.1 SDMA模式
SDMA是最基础的DMA模式。驱动在SDMA System Address寄存器中设置一个32位物理地址,然后启动数据传输。每次传输完成后,控制器自动更新该地址寄存器(增加已传输的字节数),并触发DMA中断。SDMA适用于单次、连续的物理内存区域传输。
4.2.2 ADMA2模式
ADMA2使用描述符表(Descriptor Table)来支持分散-聚集(Scatter-Gather)传输。描述符表存放在系统内存中,每个描述符包含一个32位数据缓冲区地址、一个16位长度字段以及控制标志(如“结束”、“有效”、“中断”)。控制器自动从内存中读取描述符链,依次执行传输。
| 特性 | SDMA | ADMA2 |
|---|---|---|
| 地址空间 | 32位物理地址 | 32位物理地址 |
| 传输粒度 | 连续内存块 | 分散-聚集,支持非连续内存页 |
| 描述符位置 | 无描述符 | 系统内存中的描述符表 |
| CPU负载 | 中等(每次传输后需重新配置) | 低(一次配置可传输大量数据) |
| QEMU模拟复杂度 | 低 | 中(需模拟描述符解析循环) |
4.2.3 DMA传输状态机
4.3 中断控制器:事件通知与错误报告
SDHCI中断控制器集中管理所有需要通知CPU的事件。中断状态寄存器(Interrupt Status, 0x44)的每一位对应一个独立的中断源。中断使能寄存器(Interrupt Enable, 0x48)用于屏蔽不需要的中断。当状态位被置1且对应的使能位也为1时,控制器会向中断控制器(如GIC)发送一个中断信号。
4.3.1 关键中断源定义
| 位位置 | 中断名称 | 触发条件 |
|---|---|---|
| 0 | Command Complete | 命令发送完成,响应已接收 |
| 1 | Transfer Complete | 数据传输完成(PIO或DMA) |
| 2 | Block Gap Event | 多块传输中遇到块间隙 |
| 3 | DMA Interrupt | DMA描述符链中的“中断”标志被触发 |
| 4 | Buffer Write Ready | 数据缓冲区可写入(PIO写方向) |
| 5 | Buffer Read Ready | 数据缓冲区可读取(PIO读方向) |
| 6 | Card Insertion | 检测到SD卡插入 |
| 7 | Card Removal | 检测到SD卡拔出 |
| 8 | Card Interrupt | SD卡通过DAT[1]线向主机发送中断 |
| 15 | Error Interrupt | 任何错误中断的汇总(超时、CRC、命令、数据等) |
qemu_set_irq(s->irq, 1)来触发中断。中断状态位的置位发生在sdhci_write(写Command寄存器触发Command Complete)或数据传输完成回调函数中。Guest OS的中断服务程序(ISR)首先读取Interrupt Status寄存器,判断中断来源,处理完成后向该寄存器写入1来清除对应的状态位。
4.3.2 中断处理流程示例(数据传输完成)
- 硬件置位: DMA引擎完成最后一个字节的传输后,将Interrupt Status寄存器的Transfer Complete位(位1)置1。
- 中断信号: 由于Transfer Complete使能位(Interrupt Enable的位1)为1,控制器向CPU触发中断。
- CPU响应: CPU跳转到中断向量表,执行SDHCI中断服务程序。
- 读取状态: ISR读取Interrupt Status寄存器,发现位1为1。
- 清除状态: ISR向Interrupt Status寄存器写入0x00000002(仅位1为1),清除该中断源。
- 结束中断: ISR执行完毕,CPU恢复被中断的任务。
4.4 时钟分频器:SD总线时钟的生成与控制
SD卡通信需要两路时钟:基础时钟(Base Clock)由SoC提供(通常为50MHz-200MHz),SD时钟(SD Clock)由控制器内部的时钟分频器对基础时钟分频后产生,用于SD总线的数据同步。时钟分频器通过Clock Control寄存器(0x38)进行配置。
4.4.1 时钟分频计算公式
SD_CLK = Base_CLK / (2 * (SDCLK_Frequency_Select + 1))
其中:
- SD_CLK: 最终输出到SD卡的时钟频率(Hz)
- Base_CLK: 控制器输入的基础时钟频率(Hz)
- SDCLK_Frequency_Select: Clock Control寄存器[15:8]位的值(0-255)
例如,若Base_CLK = 100MHz,SDCLK_Frequency_Select = 1,则SD_CLK = 100MHz / (2 * 2) = 25MHz。这是SD卡高速模式(High Speed Mode)的典型时钟频率。
4.4.2 时钟分频器内部结构
4.4.3 时钟分频器在QEMU中的模拟策略
QEMU作为模拟器,并不需要模拟真实的时钟波形。时钟分频器的模拟主要体现在以下两个方面:
- 频率计算: 当Guest OS写入Clock Control寄存器时,QEMU根据分频系数计算出SD_CLK频率,并存储在设备状态结构体中。这个频率值用于后续的数据传输超时计算和命令时序模拟。
- 时序延迟: 在模拟数据传输时,QEMU使用SD_CLK频率来计算传输一个块所需的时钟周期数,进而模拟出数据传输的延迟。例如,传输512字节(4096位)在4-bit模式下需要1024个SD_CLK周期。如果SD_CLK=25MHz,则延迟约为41微秒。QEMU通过定时器(timer)来模拟这个延迟。
// QEMU SDHCI时钟计算伪代码
static void sdhci_clock_update(SDHCIState *s) {
uint32_t base_clk = s->base_clock; // 从SoC属性获取
uint32_t freq_select = (s->clock_control >> 8) & 0xFF;
if (freq_select == 0) {
// 分频系数为0时,SD_CLK = Base_CLK / 2
s->sd_clock = base_clk / 2;
} else {
// 标准分频公式
s->sd_clock = base_clk / (2 * (freq_select + 1));
}
// 如果时钟门控关闭,SD_CLK为0
if (!(s->clock_control & 0x01)) {
s->sd_clock = 0;
}
trace_sdhci_clock_update(s->sd_clock);
}