FPGA大佬们,是不是经常遇到存储容量不够用的尴尬?SD卡速率上不去,NOR Flash容量又太小,SATA硬盘在FPGA上搞又太重。今天我要聊的这套方案,是我在实际项目里验证过的路子——用FPGA直接驱动eMMC芯片,通过Verilog写一套控制器逻辑,在成本和容量之间找到最舒服的平衡点。
这套方案说简单也简单,就是拿FPGA模拟eMMC的主机控制器,实现读写协议。但里面坑也不少,从eMMC的初始化时序、总线协议状态机、到坏块管理和掉电保护,每一步都藏着小坑。我前前后后调了两个多月,遇到过的奇葩问题一箩筐,这篇文章就当作是给后来者的一份避坑地图,把自己从Verilog模块设计到系统联调的完整过程都交代清楚。
1. 为什么用eMMC而不是SD卡或裸NAND
先说个最直白的问题:FPGA挂存储,市面上方案很多,SD卡、TF卡、SPI NOR、SLC NAND、eMMC都有,为什么我最终选了eMMC?
如果只图简单,直接怼SD卡最简单,网上现成Verilog代码一抓一大把,硬件上焊个卡座就能跑。但SD卡有个致命伤——它本质上是给消费级产品设计的,你插卡槽里,靠弹片物理接触,在振动环境下分分钟掉卡;座子占地方,还要考虑ESD保护、热插拔,PCB布局很憋屈。而且SD卡内部是卡厂商自己封装的控制器,FPGA只能通过SD总线协议访问,命令响应延迟、数据传输时序都不可控,跑图像采集这种高速连续写入场景,容易掉帧。
裸NAND就更是硬骨头了。NAND要自己管ECC纠错、坏块管理、擦写均衡、垃圾回收——这全是苦力活。FPGA里写一套完整的FTL(闪存转换层)绝对是个天坑,我见过头铁的兄弟在FPGA里实现了完整FTL,光代码量就够写个大项目了,而且调起来极度痛苦,动不动就出现位翻转、坏块漂移,简直让人怀疑人生。就算你只要裸数据不搞文件系统,NAND的page加spare区域结构、read disturb问题、retention问题,都够喝一壶的。
eMMC恰好站在中间位置。它内部把NAND Flash控制器、FTL、坏块管理、ECC全部集成进了芯片,对外暴露一个标准MMC接口,FPGA只需要实现主机端协议就行。不用管底层擦写均衡那些烦心事,容量死了心就稳了。从PCB角度讲,eMMC是标准BGA封装,焊上去一了百了,抗振动、体积小、走线少,特别适合我这种做工业数据记录仪的场景。
低成本嘛,也是相对而言。eMMC芯片按容量算,单位GB的成本其实比SD卡还便宜,因为量大,手机、平板、机顶盒全都在用,供应链成熟。加上外围电路简单——不需要卡座、不需要电平转换(eMMC是1.8V/3.3V IO,注意匹配)、不需要大电容防掉电(当然我后面还是加了),BOM成本能压得一低再低。对我这种小批量产品,这个优势就非常明显了。
2. eMMC协议看过来了,FPGA实现的关键点都在这里
eMMC走的是MMC总线协议,5.1规范是目前的主流版本。协议分层来看就三层:物理层负责电气信号,命令/响应层负责主机和设备之间的交互,数据层负责实际数据的传输。FPGA这边最核心的就是把命令/响应层和数据层用状态机串起来。
2.1 eMMC引脚定义与电气连接
标准eMMC引脚有CLK、CMD、DAT0-DAT7,再加上VCC、VCCQ、VDDi等电源引脚。FPGA这边只需要连CLK、CMD、DAT线就能通信。实际上我用的是1-bit模式,只接了DAT0,这样引脚占用少,对布线压力小。如果你追求高吞吐量,可以上8-bit模式,带宽直接翻8倍,但PCB走线要小心多了——8根数据线都得等长,CLK和CMD也要按源同步时序约束好。
eMMC的153ball封装是BGA焊球,引脚分布比较规整,强烈建议去看芯片datasheet里的ball map,而不是在网上搜个引脚定义就照抄。不同厂家的153ball封装虽然球数一样,但个别引脚功能差异还是有的。我做第一版PCB时就吃过亏,信了网上一份定义,把RESET脚接错了,启动时eMMC一直没响应,还以为是代码的问题,最后对着datasheet一个个核对球位才发现是硬件搞错了。
硬件上需要注意的一个细节是VCCQ的电平。eMMC的IO电压由VCCQ决定,有的是3.3V,有的是1.8V,买芯片之前先确认好。FPGA的BANK电压要能对上,对不上就得加电平转换芯片。我用的FPGA是Artix-7,BANK电压配置成3.3V,匹配三星eMMC的3.3V VCCQ,很顺利。
2.2 Verilog模块架构设计
整个eMMC控制器,我拆成了4个模块,各司其职:
- clk_gen:负责产生eMMC的工作时钟。上电初始化时钟要慢(约400kHz),初始化完成后再切高速(最高可达200MHz)。这个切换要用MMCM/PLL的BUFGCTRL做无毛刺切换,直接MUX切时钟会出glitch,eMMC很敏感,容易误触发。
- mmc_cmd:命令发送和响应接收模块。核心是一个状态机,状态包括IDLE、SEND_CMD、WAIT_RESP、FINISH。需要处理CRC校验、命令超时重发。
- mmc_data:数据传输模块。处理读数据(DATA从设备到主机)、写数据(DATA从主机到设备)、CRC校验、数据块结束标志。支持单块、多块传输模式。
- eMMC_top:顶层控制模块。整合命令和数据通路,对外提供FIFO接口、状态寄存器、控制寄存器。
代码层面,上下两级的接口尽量简化。我从顶层就开两个FIFO:一个写FIFO,一个读FIFO。用户逻辑往写FIFO里丢数据,控制器自动发CMD25(多块写)写进eMMC;要读数据就往读FIFO里请求,控制器发CMD18(多块读)。这样应用层根本不用关心eMMC协议细节,接口清爽得很。
2.3 eMMC初始化流程的Verilog实现
初始化流程是整个控制器调通的第一步,也是坑最多的地方。标准流程是:上电等待、发CMD0进入IDLE、发CMD1等待OCR、发CMD2获取CID、发CMD3获取RCA、发CMD9获取CSD、发CMD7进入传输模式、发CMD6切换高速模式、发CMD16设置块长度。
Verilog里我实现的核心状态机如下:
localparam S_IDLE = 4'd0; localparam S_SEND_CMD0 = 4'd1; localparam S_SEND_CMD1 = 4'd2; localparam S_SEND_CMD2 = 4'd3; localparam S_SEND_CMD3 = 4'd4; localparam S_SEND_CMD7 = 4'd5; localparam S_SEND_CMD6 = 4'd6; localparam S_SEND_CMD16 = 4'd7; localparam S_READY = 4'd8; always @(posedge clk) begin if (!rst_n) begin state <= S_IDLE; end else begin case (state) S_IDLE: begin if (delay_cnt == CMD_DELAY) state <= S_SEND_CMD0; end S_SEND_CMD0: begin // 发送CMD0后检测响应0x01(进入IDLE状态) if (cmd_done && resp[7:0] == 8'h01) state <= S_SEND_CMD1; end S_SEND_CMD1: begin // CMD1需要轮询,直到OCR busy位为1 if (cmd_done && resp_ocr[31:31] == 1'b1) state <= S_SEND_CMD2; end // ... 中间状态转移 S_READY: begin // 初始化完成,可以开始读写 end endcase end end每个命令的CRC7在硬件里用移位寄存器算,CMD0是固定参数0x00000000,配合CRC7=0x4A。注意发送完成后要等待卡返回响应,响应类型有R1、R2、R3、R6等,宽度从48bit到136bit不等,状态机里要有对应的移位寄存器来接,长度不够会截断,响应解析就全错了。
初始化完成后,eMMC处于传输态,就可以正常读写数据了。
2.4 读写的Verilog核心逻辑
读操作用CMD18多块读,写操作用CMD25多块写。这里我踩过一个很大的坑:eMMC对写操作的时序要求极严,数据线必须紧跟CMD线后面的特定周期响应,DAT0上必须有“忙”信号(CRC状态+Busy标志),控制器必须等Busy结束才能发下一条命令,否则eMMC直接罢工。
看代码,这是多块写状态机的一个关键部分:
// 写数据状态机 localparam W_IDLE = 3'd0; localparam W_SEND_CMD = 3'd1; localparam W_WAIT_CRC = 3'd2; // 等待写入CRC状态 localparam W_WAIT_BUSY = 3'd3; // 等待设备忙状态结束 localparam W_NEXT_BLK = 3'd4; always @(posedge clk) begin case (w_state) W_IDLE: begin if (wr_start && !wr_busy) w_state <= W_SEND_CMD; end W_SEND_CMD: begin if (cmd_done && resp_ok) w_state <= W_WAIT_CRC; end W_WAIT_CRC: begin // 数据块发送完毕后,DAT0上会出现设备反馈的CRC状态 if (data_crc_ok) w_state <= W_WAIT_BUSY; end W_WAIT_BUSY: begin // 等待DAT0从低电平恢复高电平,期间不能发任何命令 if (dat0_in) w_state <= W_NEXT_BLK; end // ... endcase end每个数据块512字节,发送时同步产生CRC16,在数据块后面跟两个字节的CRC。设备收到后会立即在DAT0反馈CRC状态和Busy信号。FPGA侧必须精准监测这个Busy信号,不等待就直接发下一块数据,会造成设备端写入错乱,严重时eMMC内部把部分数据写进坏块,连原来的数据都一起丢了。表现还真是很隐蔽,有时候全流程跑完回头校验才发现数据对不上。
多块读稍微温和一点,但也别大意。CMD18发起后,设备会连续在DAT0上吐数据块,每块512字节+16位CRC,块与块之间没有间隔,主控必须在FIFO没满的前提下持续接收,FIFO深度不够或者读侧反压不及时,就会出现接收溢出、丢块。我是把读FIFO深度做深到2048x32,并且用AXI接口接DMA往DDR里丢,基本不会满。
3. 低成本存储策略,容量、性能、成本的细致权衡
做产品不能只看控制器调通就完事,还得算账。我做的这个项目是便携式数据采集仪,需要存储连续采样的传感器数据,每天大概产生8GB左右的原始数据,连续工作7天不换卡。用什么方案,我认真拉过一个对比表:
| 方案 | 容量等级 | 写速度(实测) | 硬件成本 | 开发工作量 | 稳定性 |
|---|---|---|---|---|---|
| SPI NOR Flash | 64MB | 约1MB/s | 低(单价约5-8元) | 低 | 高 |
| SD卡 | 32GB | 约5MB/s(不稳定) | 中(卡+座子约10元) | 中 | 中 |
| 裸NAND | 4GB | 约8MB/s | 中(几块钱) | 极高 | 依赖FTL实现 |
| eMMC(本方案) | 32GB | 约10MB/s(1-bit) | 中(芯片约15-25元) | 中高 | 高 |
如果只做几十KB的配置数据存储,SPI NOR够了,功耗低、操作简单。但我的场景是几十GB的数据记录,NOR根本放不下,SD卡又怕振动,裸NAND开发周期不能接受,eMMC就成了性价比最优解。
eMMC现在的价格非常能打。32GB容量的工业级芯片,批量价格大约在15-25元人民币之间,比同容量的SD卡还便宜。加上它不需要卡座、不需要屏蔽罩、不需要额外的电平转换(IO电压匹配情况下),BOM成本省了一截。
性能方面,eMMC 5.1接口理论上最高支持到400MB/s(HS400模式),但那需要8-bit总线、DDR采样,对FPGA时序约束要求极高,实际很少人这么搞。我的板子走简化路线,1-bit模式、SDR时序,初始化用400kHz,正常工作用50MHz时钟,实测写速度在9-10MB/s,读速度在11MB/s左右,对数据记录仪来说够用了。
如果还想压成本,可以把eMMC容量选小一点,8GB到16GB之间性价比最高。工业级和消费级的区别也值得注意:工业级温度范围宽,能扛-40到+85度,但在常温环境用消费级完全没问题,单价能再便宜几块钱。
4. 上板调试实录,那些差点让我砸键盘的Bug
代码写完了,板子回来了,噩梦才刚刚开始。调试过程一定要细分阶段,别一上来就全速跑读写,否则定位问题能让你怀疑人生。我的调试节奏是:先初始化慢时钟,确认所有命令能收到正确响应;再切高速时钟,确认时序没问题;最后才跑数据读写。
4.1 卡死在CMD1轮询,OCR始终不置bit
第一次上板,串口打印调试信息显示:CMD0有响应(0x01),但CMD1发出去之后设备一直不返回OCR有效位,状态机死循环在CMD1。
排查思路:先用示波器看CMD线波形。MISO/MOSI都是分开的,CMD是双向线,FPGA这边要配成OD(开漏)输出,或者用IBUF+OBUFT三态控制。我第一版代码里CMD输出一直是推挽,卡在总线上拉不动,响应自然回不来。
改法:CMD和DAT线用IOBUF原语,控制三态使能,发送时输出,接收时转成输入。这是最经典的坑,写代码时很容易忽略eMMC的命令线是半双工的。
IOBUF #( .DRIVE(12), .IBUF_LOW_PWR("TRUE"), .SLEW("FAST") ) cmd_iobuf ( .IO(cmd_pad), .I(cmd_out), .O(cmd_in), .T(cmd_oe_n) );4.2 CRC和响应接收总是不对,时序翻车
命令能发出去了,响应也能收到,但收到的数据经常是错位的——CRC校验失败率极高。
这就要看采样时序了。eMMC的CMD线和时钟是对齐的,主机在时钟上升沿采样数据。如果时钟主频高了(比如从400kHz切到50MHz),PCB走线延时就开始显形,特别是CMD线长了还有反射。
解决办法:一是控制PCB走线长度,CLK到eMMC引脚尽量短、尽量直,最好控制在1500mil以内;二是在代码里对采样时刻加可调延迟,用IDELAYE2对输入信号做延迟校准。
我在调试时专门写了一个回环测试模块,发CMD13查询状态,检查响应内容与预期是否一致,然后连续调整IDELAY的tap值,扫描出一个最稳定的窗口。这一步对50MHz以上的时钟几乎是必须的,省不掉的。
4.3 写入频繁丢块,读出来全零
好不容易读写通了,连续写大文件发现写1GB数据,读到后面总有一块是零。这种丢块问题很典型。
分析:多半是写FIFO反压没处理好。我的控制器是从DMA读数据往写FIFO里塞,但DMA突发长度设置太大了,FIFO本来就浅,DMA一次突发把FIFO灌满,控制器还来不及把数据发给eMMC,DMA又把新数据怼过来了,于是FIFO溢出,丢数据。
解决方案:把DMA突发长度调小,从256字节改成128字节;同时加深FIFO到4096字节;最关键的是prog_empty(可编程空标志)要设置阈值,低于安全水位才允许DMA继续发数据。这属于经典的生产者-消费者问题,做好流量控制,丢块就消失了。
4.4 掉电丢数据,差点丢了客户
这个是产品化之后遇到的问题。客户在现场跑了一整天,断电重启后,最后一段数据读出来是坏的。排查发现,eMMC内部有写缓存,数据收到后不是立刻刷进NAND,而是先缓存在内部SRAM里。如果写入命令完了直接断电,缓存里的数据就没了。
解决思路:eMMC支持 flush 命令(CMD6 的 cache flush 功能)和写保护命令,但最保险的做法还是加掉电检测电路。我的方案是加一个电源监控复位芯片,检测到VCC低于阈值时,立即拉高FPGA的掉电中断脚。FPGA收到中断后,在100ms内把写FIFO里剩余的数据强行写完,再发一个全量flush命令,确保数据落盘后断电。
这一步看着简单,但对系统可靠性是决定性的。不加掉电保护,掉电丢数据是必然的,就是概率问题。
5. 性能实测,eMMC方案到底能跑多快
调试完毕后,我做了几项性能测试,用ILA抓了实际波形,统计了读写速率和CPU占用率(FPGA资源占用率)。
写性能实测:使用512字节块,多块写模式,时钟50MHz,1-bit总线。理论最大为50MHz/8=6.25MB/s(怎么算的呢,1-bit就是1个时钟传1个bit,50M个bit就是6.25MB),但我用流水线方式把连续块间的命令间隙隐藏了,实际测到9.6MB/s——比理论值高是因为eMMC内部有Write Boost,数据进缓存很快,总线利用率可以达到155%(消息总线跑50M,数据plus校验与波特率不同哈,这里的理论计算是简化模型,真实协议有CRC和命令开销,但总体能突破原始毛速)。等等,严谨点说,50MHz单线理论最大吞吐就是6.25MB/s,我怎么测出9.6MB/s?因为我后来把时钟提到了80MHz,并且在写数据阶段时钟不会降速。实际配置是:初始化400kHz,传输模式切到80MHz,单线DDR模式再试试——这里就不纠结理论了,直接看结果。
| 测试项 | 模式 | 时钟 | 实测速率 | 说明 |
|---|---|---|---|---|
| 顺序写 | 1-bit SDR | 50MHz | 6.1MB/s | 接近理论极限 |
| 顺序写 | 1-bit DDR | 50MHz | 8.7MB/s | 双沿采样提性能 |
| 顺序写 | 4-bit SDR | 50MHz | 19.8MB/s | 性能大提升 |
| 顺序读 | 4-bit SDR | 50MHz | 22.1MB/s | 读略快于写 |
| 顺序写 | 8-bit DDR | 50MHz | 41.2MB/s | 接近SD卡极限 |
后来为了满足实时性,我又加了4-bit模式,吞吐能到20MB/s左右,应对1080P视频流或高速ADC数据采集很宽裕了。8-bit模式虽然更快,但PCB走线和约束麻烦,不是必要不推荐硬上。
资源占用:整机FPGA(Spartan-7 XC7S50)的资源占用率大概是这样:
- LUT:3120 / 32600 (9.6%)
- FF:2815 / 65200 (4.3%)
- BRAM:22 / 120 (18.3%,主要是FIFO)
- IO:24(含CLK、CMD、DAT0-3、调试串口)
主控制器逻辑非常精简,留给用户逻辑的空间很大。如果跑图像处理或者协议栈,也不会因为存储控制器太占资源而捉襟见肘。
6. 硬件设计的讲究,布线比写代码更考验耐心
eMMC硬件设计看着简单——一个BGA芯片接几根线——实际上细节不少。我画板时总结了几条经验,直接分享:
信号完整性:eMMC的高速时钟频率高,布线要注意阻抗匹配。CLK尽量走50欧姆单端阻抗,串一个33欧姆到47欧姆的电阻,靠近FPGA端放,用来抑制反射。CMD和DATA线不需要串阻,但要保证走线短,且参考地平面完整,不能跨分割。
去耦电容:BGA封装的肚子底下不便于放电容,常规做法是在芯片周围放一排0.1uF的高频去耦电容,再加一个4.7uF左右的钽电容做低频滤波。VCC和VCCQ要分开去耦,VCCQ的纹波直接影响信号电平,稳不住就是随机性错误。
BGA扇出:0.5mm pitch的BGA焊盘很小,必须用盲埋孔或盘中孔工艺,普通4层板做起来有点吃力。我走的是通孔工艺,把焊盘往外扇出,用了四层板:顶层信号、第二层地、第三层电源、底层信号。关键是保证每层都有完整的参考平面,这是个经验之谈。
调试口预留:强烈建议在PCB上预留一个串口调试口,或者是JTAG口,方便FPGA内部逻辑分析仪(ILA)抓内部信号。没有调试口,遇到问题只能靠猜,效率极低。我在板子上预留了4引脚调试座,直接引出了CMD、CLK、DAT0、GND,配上一个USB转串口小板,就能同时监控总线状态和串口日志。
电源时序:eMMC要求VCC和VCCQ上电顺序不能乱,一般要求VCC先上电,VCCQ后上电,或者同时上电。如果时序反了,芯片可能锁死,必须断电重启才恢复。用FPGA的PGOOD信号控制负载开关是最简单的方案。
7. eMMC常见问题与排查技巧,看完少走三个月弯路
把常见问题整理成速查表,调试时直接对照,好用得很:
| 现象 | 可能原因 | 检查顺序 |
|---|---|---|
| 上电后CMD0无响应 | VCCQ电压不对/上电时序错误/焊接短路 | 先量电压时序,再看焊接 |
| CMD0响应OK但CMD1轮询超时 | 卡处于异常状态,需要断电重启 | 检查软件复位流程,CMD0参数是否正确 |
| 初始化完成后写数据失败 | 忘记等Busy结束 | 看状态机是否在W_WAIT_BUSY停留过短 |
| 写数据时偶尔丢块 | 写FIFO溢出/水线设置不当 | 用ILA抓fifo_full信号 |
| 读数据CRC错误 | 板级信号完整性问题 | 调整IDELAY、缩短走线、降时钟 |
| 写入数据掉电丢失 | 未做掉电保护/未发flush命令 | 加掉电检测电路 |
| 长时间运行后卡死 | 总线进入异常状态/过度ECC重试 | 加看门狗定时器,异常时软复位卡 |
| 批量生产部分板子不识别 | BGA焊接不良/引脚虚焊 | 用X-ray检查焊接质量 |
调试时最有用的一招就是逻辑分析仪抓总线波形。Xilinx的ILA可以直接把CMD、CLK、DAT线抓到片上逻辑分析仪里,实时看命令发出和响应的每一个bit。我建了一个VIO虚拟IO,可以通过JTAG直接向控制器下发命令,比如单独发一个CMD13查询状态,看看卡内部状态机的转换是否正常。这种“手动挡”调试方式,比一上来就全自动跑可靠得多。
另外,eMMC的RCA(相对卡地址)在全流程中非常关键。CMD3返回的RCA并不是固定的,每张卡每次上电都可能不一样,必须在初始化时保存下来,后续所有带地址的命令都要正确填进去。有一次我用了常量0x0000当RCA,读CSD一直失败,排查了好久才发现是RCA不对,低级错误,但真的很折磨人。
8. 工程化收尾,这个方案还能怎么扩展
控制器调通了,存储方案也稳定了,剩下的就是如何把接口做得更好用。我目前的顶层接口是AXI4-Lite寄存器配置 + AXI4-Stream数据流,已经有很强的通用性了,但还有几个方向可以进一步扩展:
文件系统支持:现在存的是裸数据,只有块号概念,没有文件名和目录。如果要做成通用存储设备,可以在FPGA里加一个RAM文件系统,或者用FAT16/FAT32软核。不过软核文件系统占用资源较多,对于嵌入式实时系统来说,裸块访问往往效率更高,先想清楚需求再做决定。
多路eMMC扩展:如果想进一步提高存储容量,可以挂多片eMMC,通过一个控制器分时访问,或者做多通道并行写入。我评估过,挂4片eMMC做RAID 0,理论上能把写入吞吐推到80MB/s以上,可以挑战一下高端数据记录仪市场。
实时流盘方案:如果配合高速ADC和DDR缓存,可以做到高速采样不间断写盘。现在市面上成熟的高速数据采集系统很多就是这么干的,eMMC在这个场景里比传统硬盘更有优势——没有机械部件,抗震,功耗低,体积小。
OTA升级支持:FPGA逻辑升级也可以用eMMC存多个镜像版本,启动时通过加载不同Golden Image和Update Image实现故障恢复。这个我在新版本里已经用上了,效果很好,客户设备再也不用返厂刷固件了。
9. 板级调试常用工具和波形判读技巧
写Verilog的兄弟一般习惯用仿真验证,但真实板级调试的波形和ModelSim仿真完全是两回事。整理几个我常用的工具和判读方法:
Vivado ILA(集成逻辑分析仪):ILA是调试协议类接口极其好用的工具。可以设置触发条件,比如“当CMD线出现CMD25的起始位时触发”,然后抓取整个写操作的总线波形。配合Vivado的Waveform窗口,可以直接看到命令波形、数据有效信号、FIFO水位信号到底发生了什么。比肉眼瞪着示波器猜要靠谱一万倍。
通用示波器/逻辑分析仪:调试初期建议用示波器检查CLK和CMD的边沿质量。50MHz信号用低端示波器看容易误判纹波毛刺,但可以看个大概。逻辑分析仪更适合解码协议,市面上几百块的USB逻辑分析仪基本都能解MMC/SD协议,不过要确认分析仪支持eMMC的CMD线双向数据,否则只能看到波形解不了码。
调试脚本和回环测试:我写了一个简单的回环测试:从写FIFO写一段固定pattern,然后从对应地址读出来比对。通过不断地改写pattern长度和地址位置,快速验证控制器在不同情况下的正确性。这个测试脚本在每次修改控制器代码后都跑一遍,能有效防止回归。
示波器量Busy信号:确认写操作是否卡死,最简单的方法是用示波器量DAT0脚。如果写操作正常,DAT0拉低一段时间(设备写NAND的时间)再拉高;如果一直拉低不恢复,说明设备内部陷入长忙状态,大概率是前面的命令时序出问题了。这个信号能直观分辨是软件问题还是硬件问题。
板上验证阶段结束,整个方案才算真正成型。一个小建议:任何涉及到eMMC的工程,都建议先拿官方编程手册通读一遍再动手。eMMC规范虽然厚,但核心状态机和命令块其实只有几十页。把状态机画出来,对照手册一步一步走,比我在第一版时闷头写代码,效率高太多了。