An SD card emulator implemented using FPGA for debugging embedded bootloaders or bare-metal programs.
用于嵌入式 bootloader 开发/调试的 FPGA SD 卡模拟器。支持 SDHC v2 只读卡, 1 位和 4 位数据总线;镜像数据通过 UART 从 PC 预先加载到 FPGA 内部 RAM, 再由 SD 主机接口读出。纯 Verilog 实现,无厂商原语,可在任意主流 FPGA 上综合。
- SDHC v2 只读卡协议仿真(参考
ref/sd_fake.v):- CMD0 / CMD2 / CMD3 / CMD6 / CMD7 / CMD8 / CMD9 / CMD10 / CMD12 / CMD13 / CMD15 / CMD16 / CMD17 / CMD18 / CMD55 + ACMD6 / ACMD13 / ACMD41 / ACMD42 / ACMD51 等,未定义命令返回 illegal command
- R1 / R2 (CID/CSD) / R3 (OCR) / R6 (RCA) / R7 响应,CRC7 校验
- 单块读(CMD17)、多块连续读(CMD18 + CMD12)、停止传输
- 1 位模式与 4 位模式(ACMD6 切换),数据 CRC16 校验
- SCR / SD_STATUS / CMD6 状态寄存器回读
- 通过 UART 加载扇区数据(ASCII 与二进制两种协议,参考
ref/uart2bus-master) - 镜像存放在系统时钟域写、SD 时钟域读的双时钟 RAM 中;按约定 “数据先加载完、再由 SD 接口读取”,不处理读写竞争
- 只读卡:不实现任何 SD 写命令
rtl/
sd_fake_top.v 顶层:SD 协议 + RAM + UART 加载链路集成
sd_fake.v SDHC 只读卡协议仿真(自 ref/ 拷贝,含 1 处时序修正)
sd_ram.v 双时钟 RAM(UART 写口 + SD 读口,字节写使能)
uart_ram_bridge.v uart2bus 内部总线 <-> RAM 写口/读回
uart2bus_top.v UART 总线封装(自 ref/ 拷贝,参数传递修正)
uart_parser.v UART 命令解析器(自 ref/ 拷贝,新增扩展帧支持)
uart_top.v UART 收发器(自 ref/ 拷贝,TX 数据接错修正)
sim/
tb_sd_fake_top.v 完整 testbench(UART 上位机模型 + SD 主机模型)
tools/
load_sd_image.py 上位机镜像加载/校验脚本
┌──────────────────────────────────────────────┐
PC ──UART──▶ │ uart2bus_top ──int bus──▶ uart_ram_bridge ──▶│
│ │ │
│ ▼ │
│ sd_ram 双时钟 RAM │
│ ┌─────▲──────┐ │
│ │ port A │ port B │
│ │ (sysclk) │ (sdclk) │
│ └────────────┘ │
│ ▲ │
│ rdreq/rdaddr/rddata │
│ │ │
│ sd_fake │
SD 主机 ◀───sdclk/sdcmd/sddat──▶ (SD 协议仿真) │
└──────────────────────────────────────────────┘
顶层 sd_fake_top:
| 参数 | 默认值 | 说明 |
|---|---|---|
SYSCLK_HZ |
50_000_000 | 系统时钟(UART 与 RAM 写口) |
UART_BAUD |
115200 | UART 波特率 |
NUM_SECTORS |
128 | 扇区数(512 B/扇区)。受片上 BRAM 容量限制(Zynq-7010 约 270 KB,留出逻辑占用后约 400 扇区);扩展协议可寻址 16 MB(32768 扇区),超出部分回绕 |
| 端口 | 方向 | 说明 |
|---|---|---|
clk |
in | 系统时钟 |
rstn |
in | 异步复位,低有效 |
uart_rx / uart_tx |
in / out | 与 PC 相连的 UART |
sdclk |
in | SD 时钟(来自主机) |
sdcmd |
inout | SD 命令线(PCB 上需上拉) |
sddat[3:0] |
inout | SD 数据线(PCB 上需上拉) |
status[7:0] |
out | 调试状态 {resp_end, widebus, ready_for_data, app_cmd, state[3:0]} |
地址是字节地址:扇区 N 的 512 字节位于 N*512 .. N*512+511。
进入二进制模式:发送 0x00,随后:
00 <cmd> <addr_hi> <addr_lo> <len> [<data>...] (legacy)
00 <cmd> <addrH> <addrM> <addrL> <lenH> <lenL> [<data>...] (extended)
- 写:
cmd = 0x20(旧格式)/0x24(扩展格式);bit[1]=0 地址自动加 1 - 读:
cmd = 0x10(旧格式)/0x14(扩展格式) - 命令字节
bit[2]选择帧格式:- 旧格式(bit[2]=0):16 位地址 + 8 位长度,
len1..255 字节/命令 (len=0表示 256 字节) - 扩展格式(bit[2]=1):24 位地址 + 16 位长度,单命令最多 65535 字节
(
len=0表示 65536 字节),可寻址 16 MB
- 旧格式(bit[2]=0):16 位地址 + 8 位长度,
cmdbit0 = 1 时命令结束后返回一个状态字节0x5A
扩展格式是推荐格式(加载快、可寻址全镜像空间),旧格式保留兼容。
w <2位十六进制数据> <4位十六进制地址><CR> 写 1 字节
r <4位十六进制地址><CR> 读 1 字节,回显 "XX<CR><LF>"
python tools\load_sd_image.py COM3 boot.bin --baud 115200 --verify把 boot.bin(原始扇区数据,bootloader 镜像)加载进模拟卡;--verify
会再读回全部数据并逐字节比对。完整命令行参数如下(也可用
python tools\load_sd_image.py --help 查看):
| 参数 | 默认值 | 说明 |
|---|---|---|
port |
必填 | 串口名,如 COM3 或 /dev/ttyUSB0 |
image |
必填 | 原始镜像文件(扇区数据,如 bootloader 的 bin) |
--baud |
115200 | UART 波特率 |
--base |
0 | 镜像在 SD 镜像空间中的起始字节地址(0x 十六进制或十进制均可)。加载与 --verify 回读都从该地址开始 |
--verify |
关 | 加载后读回全部数据并逐字节比对;不一致时报 VERIFY FAILED 并以非零退出 |
--status |
关 | 每条写命令结束后要求返回一个状态字节 0x5A(对应命令字节 bit0=1),校验失败即中止 |
--legacy |
关 | 改用旧 16 位地址 / 8 位长度命令(默认使用扩展 24 位地址 / 16 位长度格式) |
--timeout |
5.0 | 串口读超时(秒);读回超时即报错退出 |
-h, --help |
- | 显示帮助并退出 |
默认的扩展格式单命令最多 65535 字节(len=0 表示 65536),可寻址 16 MB;
--legacy 切换为旧格式,单命令最多 255 字节(len=0 表示 256)。legacy
模式下若 --base + 镜像大小 超过 64 KiB,脚本会打印地址回绕警告。实际
RAM 容量由 NUM_SECTORS 决定,超出 NUM_SECTORS*512 的字节地址会回绕
到 RAM 起始处。
需要 Icarus Verilog。testbench 内含完整的 PC 模型(ASCII + 二进制 UART 收发)和 SD 主机模型(初始化序列、 R1/R2/R3/R6/R7 校验、CRC7/CRC16 校验、1 位/4 位/多块读、SCR 检查)。
快速模式(UART 每时钟 1 位,覆盖全部用例,约 2 秒):
iverilog -g2005 -DSIMULATION -o sim\tb_fast.vvp -s tb_sd_fake_top `
rtl\sd_fake.v rtl\uart_parser.v rtl\uart2bus_top.v rtl\uart_top.v `
rtl\sd_ram.v rtl\uart_ram_bridge.v rtl\sd_fake_top.v sim\tb_sd_fake_top.v
vvp sim\tb_fast.vvp真实波特率模式(115200,加载 1 个扇区,约 1.5 分钟):
iverilog -g2005 -DSMOKE -o sim\tb_smoke.vvp -s tb_sd_fake_top `
rtl\sd_fake.v rtl\uart_parser.v rtl\uart2bus_top.v rtl\uart_top.v `
rtl\sd_ram.v rtl\uart_ram_bridge.v rtl\sd_fake_top.v sim\tb_sd_fake_top.v
vvp sim\tb_smoke.vvp两者均应输出 *** ALL TESTS PASSED ***。
- 只读卡:SD 写命令(CMD24/25/27 等)会返回 illegal command,不实现写入。
- 加载与读取不竞争:UART 把数据全部写完后,SD 主机再开始读。若边写边读, 数据一致性不做保证。
- 扩展协议地址空间 24 位(16 MB),但实际容量由片上 BRAM 决定
(
NUM_SECTORS),超出NUM_SECTORS*512的地址会回绕到 RAM 起始处; 旧 16 位命令只能访问低 64 KiB。 - 扩展命令(
0x24/0x14)需要本工程的新版解析器,旧版固件不识别。 - SD 时钟由主机提供并自由运行;主机停止
sdclk时读口冻结,属正常。 sdcmd/sddat为开漏/三态,PCB 上必须加上拉(典型 10kΩ)。- 卡固定响应 RCA=0x0013;CSD 为 25 MHz SD-ROM 配置,若 bootloader 要求
更高速度等级可在
sd_fake.v中调整CSD_REG/OCR_REG。