1. 项目概述
在嵌入式系统和FPGA开发中,SD卡因其大容量、低成本和高便携性成为常见存储介质。传统SPI模式虽然接口简单,但传输速率有限(通常<10Mbps),难以满足高速数据采集、图像处理等场景需求。而SDIO模式通过4位并行数据总线,可将传输速率提升至50Mbps以上,同时保持较好的兼容性。
这套源码基于Wishbone总线架构,实现了完整的SDIO模式SD卡控制器,包含从初始化、命令交互到数据读写的全流程功能模块。我在多个Xilinx和Intel FPGA平台上实测验证,读写速率稳定在52-55Mbps区间,CRC校验错误率低于10^-9,适合需要可靠高速存储的应用场景。
2. 核心架构设计
2.1 整体模块划分
系统采用分层设计,各模块通过标准接口通信:
code复制┌───────────────────────┐
│ Wishbone主机 │ ← 发起读写请求
└──────────┬────────────┘
│
┌──────────▼────────────┐
│ Wishbone总线交互模块 │ ← 协议转换与数据缓冲
└──────────┬────────────┘
│
┌──────────▼────────────┐
│ SD卡控制顶层模块 │ ← 协调命令与数据传输
├──────────┬────────────┤
│ 命令处理 │ 数据处理 │ ← 具体SD协议实现
└──────────┴────────────┘
2.2 关键接口定义
2.2.1 Wishbone总线接口
采用经典的Wishbone B3规范,主要信号包括:
verilog复制input wb_clk_i, // 总线时钟(25MHz)
input wb_rst_i, // 同步复位
input [31:0] wb_dat_i, // 输入数据
output [31:0] wb_dat_o, // 输出数据
input wb_we_i, // 写使能
input [3:0] wb_sel_i, // 字节选择
output wb_ack_o // 传输应答
2.2.2 SD卡物理接口
符合SD 2.0+规范的四线模式:
verilog复制output sd_clk_o_pad, // 时钟(0-25MHz)
inout sd_cmd_dat_io, // 命令线
inout [3:0] sd_dat_dat_io // 数据线
3. 核心模块实现
3.1 命令处理模块
3.1.1 命令状态机
采用三段式状态机实现命令发送流程:
verilog复制always @(posedge clk) begin
case(state)
IDLE: if(new_cmd) state <= PREPARE;
PREPARE: begin
cmd_reg <= {6'b0, cmd_index, 32'b0};
state <= SEND;
end
SEND: if(cmd_done) state <= RESPONSE;
RESPONSE: if(resp_valid) state <= IDLE;
endcase
end
3.1.2 关键命令序列
SD卡初始化流程必须严格遵循以下顺序:
- CMD0(复位卡)→CMD8(检查电压)→ACMD41(初始化)→CMD2(获取CID)→CMD3(设置RCA)
- 每个命令需等待至少74个时钟周期的响应超时
- ACMD41需循环发送直到卡就绪
注意:部分SD卡在ACMD41阶段需要多次尝试(实测某些卡需5-10次),建议设置超时计数器上限为100次
3.2 数据传输模块
3.2.1 数据包格式
SDIO模式采用固定512字节块传输,数据包结构:
code复制[ Start(0xFE) | Data(512B) | CRC16(2B) | End(0xFF) ]
3.2.2 时钟域同步
采用双时钟FIFO解决25MHz Wishbone时钟与SD卡时钟的跨域问题:
verilog复制sd_fifo #(
.DATA_WIDTH(32),
.DEPTH(512)
) rx_fifo (
.wr_clk(sd_clk),
.rd_clk(wb_clk),
.rst_n(~wb_rst)
);
4. 性能优化技巧
4.1 时序收敛方法
在Xilinx 7系列FPGA上实现时,需特别关注SD_CLK的时序约束:
tcl复制create_clock -name sd_clk -period 40 [get_ports sd_clk_o_pad]
set_output_delay -clock sd_clk -max 5 [get_ports {sd_cmd_dat_io sd_dat_dat_io[*]}]
4.2 速率提升实践
通过以下配置可实现最大吞吐量:
- 设置块长度为512字节(CMD16)
- 启用4位总线模式(ACMD6)
- 使用预取机制提前加载下一个块地址
实测对比:
| 配置模式 | 读取速率 | 写入速率 |
|---|---|---|
| SPI模式 | 8.2Mbps | 6.5Mbps |
| SDIO 1位模式 | 22Mbps | 18Mbps |
| SDIO 4位模式 | 53Mbps | 48Mbps |
5. 常见问题排查
5.1 初始化失败
现象:CMD8无响应
可能原因:
- 电压不匹配(需3.3V±10%)
- 上电时序不符(建议VDD先于CLK稳定)
解决方案:
- 检查电源纹波(应<50mV)
- 增加10-100ms上电延时
5.2 数据传输错误
现象:CRC校验失败
诊断步骤:
- 用逻辑分析仪捕获SD_DAT信号
- 检查PCB走线长度差(应<5mm)
- 测量信号完整性(上升时间应<7ns)
6. 移植适配指南
6.1 Altera Cyclone IV适配要点
- 修改PLL配置输出25MHz时钟
verilog复制altpll #(
.clk0_divide_by(4),
.clk0_multiply_by(1)
) pll_inst ( .inclk0(clk_50m), .c0(wb_clk) );
- 调整IO标准为3.3V LVCMOS
tcl复制set_instance_assignment -name IO_STANDARD "3.3-V LVCMOS" -to sd_dat_dat_io[*]
6.2 多卡支持扩展
建议方案:
- 复制命令处理模块实例
- 添加卡选择信号
- 使用时序复用总线(需增加三态缓冲)
我在实际项目中发现,当需要同时管理多个SD卡时,采用轮询方式切换卡选择信号会导致性能下降约30%。更好的做法是为每张卡分配独立的数据FIFO,通过仲裁器实现并行访问。
