【黑金原创教程】【FPGA那些事儿-驱动篇I 】实验二十五:SDHC模块

来源:互联网 时间:1970-01-01

实验二十五:SDHC模块

笔者曾经说过,SD卡发展至今已经衍生许多版本,实验二十四就是针对版本SDV1.×的SD卡。实验二十四也说过,CMD24还有CMD17会故意偏移地址29,让原本范围指向从原本的232 变成 223,原因是SD卡读写一次都有512个字节。为此我们可以这样计算:

SDV1.x = 223 * 512 * Bytes // 512个字节读写

= 4.294967296e9 Bytes

= 4.194304e6 kBytes

= 4096 MBytes

= 4 GBytes

SDV1.x = 232 * Bytes // 单字节读写

= 4.294967296e9 Bytes

= 4.194304e6 kBytes

= 4096 MBytes

= 4 GBytes

不管是223 乘以512字节读写,还是232直接进行单字节读写,该内容显示版本SDV1.x可以支持的最大容量只有4GB而已。不过,容量4GB再也无法满足现今的饿狼,为此SD卡被迫提升版本,即版本SDV2 还有版本SDHCV2。所谓版本SDV2就是 SD Card Version 2,所谓SDHCV2就是 SD Card High Capacity Version 2。

好奇的朋友一定会觉得疑惑,为何进化以后的SD卡会有版本SDV2 还有版本SDHCV2之分呢?版本SDV2可谓是版本SDHCV2的脚垫,为何笔者会怎么说呢?其实事情的背后有隐藏这样一起凄惨的故事,无疑听着会伤心,闻者会流泪,就连坚强的比卡丘也不再放电。故事是这样的 ...

西元两千年之际,SD卡因为其方便性还有大容量等特征,结果一炮而红,名声也随之传遍各个人类居住的大陆。根据杰克斯的理论,备受需求的东西都会急剧发展。为此,好评如潮的SD卡也在短短的几年内,从原本小小的64MB膨胀至4GB容量,但是需求必定超过结构的负荷。

SD卡为了满足日益剧增的大容量需求,结果它不得不放弃版本SDV1.×,取而代之就是版本SDV2,还有版本SDHCV2。版本SDV1.×与版本SDV2之间的差别是前者从单字节开始读写,而后者则是从512字节可是读写。为此,我们可以这样计算:

SDV2 = 232 * 512 * Bytes

= 2.199023255552e12 Bytes

= 2.147483648e9 kBytes

= 2.097152e6 MBytes

= 2028 GBytes

= 2 TBytes

简单来说,版本2DV2 单个地址是指向 512字节,因此232可以指向2TB的范围。理论而言,版本SDV2的确可以支持2TB的大容量,但是闹肚子的厂商们,根本来不及实验便纷纷将老版本的SD卡改为版本SDV2。云之间,市场便充斥许多版本SDV1.×与版本SDV2的SD卡,同样是4GB的SD卡,不过同时兼有版本SDV1.×与版本SDV2,可谓是SD卡的浑沌时代。

随着科技发展,SD卡的容量也直线上升,8GB,16GB,32GB,64GB等各种大容量SD卡也随之面世,如今128GB的SD卡也是近在眼前。此刻,问题发生了 ... 我们知道版本SDV2是倡促之下衍生的产物,理论上它虽然可以支持2TB的容量,但是它并不适合支持大容量的SD卡,因为有许多小细节都顾及不到,结果版本SDHCV2诞生了。

现实总是太残酷,版本SDHCV2有如剧毒般,慢慢侵蚀版本SDV2,版本SDV2也随之失去为期不长的光亮,最终沦落为脚垫 ... 版本SDV2实在太可怜,让我们为它默哀一下吧。听完故事以后,笔者希望读者始理解,与其驱动版本SDV2还不如驱动版本SDHCV2,只要理解后者前者自然不学而会。那么,我们开始实验吧。

版本SDHCV2需要用到以下几个命令:

(一)CMD0,复位命令,令SD卡处于待机(IDLE)状态;

(二)CMD8,配置命令,配置一些物理参数;

(三)CMD58,状态命令,令SD卡反馈状态;

(四)CMD55,扩充命令,告诉SD卡下一个命令为扩充命令;

(五)ACMD41,扩充命令,配置高容量标示(HCS);

(六)CMD16,配置命令,配置读写字节的长度,默认为512;

(七)CMD24,写命令;

(八)CMD24,读命令。

看着看着,双腿也会开始发软,因为遇见那么多不认识的命令,驱动大容量SD卡确实吓人,不过笔者会陪伴左右,所以不要太担心。首先是CMD0,这个命令曾在实验二十三解释过,请怒笔者不重复了,CMD0的作用主要是令SD卡处于待机状态。

图25.1 CMD8的理想时序图。

图25.1是CMD8的理想时序图。T0之际,主机拉高CS并且给足8个时钟。T1~2之际,主机拉低CS并且给足8个时钟。T3~4之际,主机发送命令 { 8’h48, 16’d0, 8’h01, 8’haa, 8’h87 },其中 { 8’h01,8’haa }是一些默认物理配置,好奇的朋友可以翻查手册。T5之际,SD卡接收命令以后便开始反馈数据R7,主机在T5~T9期间接收反馈数据R7。

R7是由5个字节组成的反馈数据,第4个字节类似R1,内容8’h01表示SD卡处于待机状态。接续四个字节的内容是命令CMD8的倒影——{ 16’d0, 8’h01, 8’haa }。T10~11之际,主机拉高CS并且给足8个时钟。T12之际,主机再给足8个结束时钟。对此,Verilog可以这样描述,结果如代码25.1所示:

1. case( i )

2. 

3. 0: // Send free clock

4. if( iDone ) begin isCall[0] <= 1'b0; i <= i + 1'b1; end

5. else begin isCall[0] <= 1'b1; D1 <= 8'hff; end

6. 

7. 1: // Enable cs

8. begin rCS <= 1'b0; i <= i + 1'b1; end

9. 

10. 2: // Send free clock

11. if( iDone ) begin isCall[0] <= 1'b0; i <= i + 1'b1; end

12. else begin isCall[0] <= 1'b1; D1 <= 8'hff; end

13. 

14. 3: // Prepare Cmd8 // 8'h01 = 2.7~3.6v, 8'hAA default check pattern

15. begin D4 <= { 8'h48,16'd0,8'h01,8'hAA,8'h87 }; i <= i + 1'b1; end

16. 

17. 4: // Try 100 times, ready error code.

18. if( C1 == 10'd100 ) begin D2[7:0] <= CMD8ERR; C1 <= 16'd0; i <= 4'd13; end

19. else if( (iDone && iData != 8'h01) ) begin isCall[1]<= 1'b0; C1 <= C1 + 1'b1; end

20. else if( (iDone && iData == 8'h01) ) begin isCall[1] <= 1'b0; C1 <= 16'd0; i <= i + 1'b1; end 

21. else isCall[1] <= 1'b1; 

22. 

23. 5: // Store R7

24. begin D2[39:32] <= iData; i <= i + 1'b1; end

25. 

26. 6: // Read and store R7

27. if( iDone ) begin D2[31:24] <= iData; isCall[0] <= 1'b0; i <= i + 1'b1; end

28. else begin isCall[0] <= 1'b1; end

29. 

30. 7: // Read and store R7

31. if( iDone ) begin D2[23:16] <= iData; isCall[0] <= 1'b0; i <= i + 1'b1; end

32. else begin isCall[0] <= 1'b1; end

33. 

34. 8: // Read and store R7

35. if( iDone ) begin D2[15:8] <= iData; isCall[0] <= 1'b0; i <= i + 1'b1; end

36. else begin isCall[0] <= 1'b1; end

37. 

38. 9: // Read and store R7

39. if( iDone ) begin D2[7:0] <= iData; isCall[0] <= 1'b0; i <= i + 1'b1; end

40. else begin isCall[0] <= 1'b1; end

41. 

42. 10: // Disable cs

43. begin rCS <= 1'b1; i <= i + 1'b1; end

44. 

45. 11,12: // Send free clock

46. if( iDone ) begin isCall[0] <= 1'b0; i <= i + 1'b1; end

47. else begin isCall[0] <= 1'b1; D1 <= 8'hFF; end

48. 

49. 13: // Disable cs, generate done signal

50. begin rCS <= 1'b1; isDone <= 1'b1; i <= i + 1'b1; end

51. 

52. 14:

53. begin isDone <= 1'b0; i <= 4'd0; end


代码25.1

如代码25.1所示,步骤0给足8个时钟,步骤1拉低CS,步骤2再给足8个时钟。步骤3准备命令CMD8,步骤4则重复100次写命令,直至第4字节的反馈数据为8’h01为止,否则准备错误信息,然后跳转步骤13。步骤5暂存第一字节的反馈数据,步骤6~9则是执行读写并且暂存接续4个字节的反馈数据。步骤10拉高CS,步骤11~12则给足8个时钟。步骤13拉高CS之余也产生完成信号。

图25.2 CMD58的理想时序图(待机状态)。

图25.2是CMD58的理想时序图。T0之际,主机拉高CS并且给足8个时钟。T1~2之际,主机拉低CS并且给足8个时钟。T3~4之际,主机发送命令CMD58—{ 8’h7A, 32’d0, 8’h01 }。T5之际,SD卡完成接收并且开始反馈数据R3,主机也在T6~9期间读取它们。反馈数据R3与R7一样,它们都是由5个字节组成,其中第4字节也类似R1,内容为8’h01表示SD卡还处于待机状态。

除此之外,R3的第3字节是有意义的,而接续的3个字节只是哈拉哈拉而已。R3第3字节的位意义如表25.1所示:

表25.1 R3第3字节的位意义。

R3的第3字节

[7]

[6]

[5]

[4]

[3]

[2]

[1]

[0]

Busy

CCS

无视

无视

无视

无视

无视

无视

如表25.1所示,第7位为0表示SD卡处于“忙状态”或者“待机状态”,反之就是“传输状态”。第6位CCS为1表示SD卡支持高容量,反之亦然。余下内容笔者就无视了,爱八卦的朋友请自行查阅手册。

T10~11之际,主机给足8个时钟,然后主机在T12拉高CS。对此,Verilog可以这样描述,结果如代码25.2所示:

1. case( i )

2. 

3. 0: // Send free clock

4. if( iDone ) begin isCall[0] <= 1'b0; i <= i + 1'b1; end

5. else begin isCall[0] <= 1'b1; D1 <= 8'hff; end

6. 

7. 1: // Enable cs 

8. begin rCS <= 1'b0; i <= i + 1'b1; end

9. 

10. 2: // Send free clock

11. if( iDone ) begin isCall[0] <= 1'b0; i <= i + 1'b1; end

12. else begin isCall[0] <= 1'b1; D1 <= 8'hff; end

13. 

14. 

15. 3: // prepare cmd 58

16. begin D4 <= { 8'h7A,32'd0,8'h01 }; i <= i + 1'b1; end

17. 

18. 4: // Try 100 time, ready error code.

19. if( C1 == 10'd100 ) begin D2[7:0] <= CMD58ERR; C1 <= 16'd0; i <= 4'd12; end

20. else if( (iDone && iData != 8'h01) ) begin isCall[1] <= 1'b0; C1 <= C1 + 1'b1; end

21. else if( (iDone && iData == 8'h01) ) begin isCall[1] <= 1'b0; C1 <= 16'd0; i <= i + 1'b1; end 

22. else isCall[1] <= 1'b1; 

23. 

24. 5: // Store R3

25. begin D2[39:32] <= iData; i <= i + 1'b1; end

26. 

27. 6: // Read and store R3

28. if( iDone ) begin D2[31:24] <= iData; isCall[0] <= 1'b0; i <= i + 1'b1; end

29. else begin isCall[0] <= 1'b1; end

30. 

31. 7: // Read and store R3

32. if( iDone ) begin D2[23:16] <= iData; isCall[0] <= 1'b0; i <= i + 1'b1; end

33. else begin isCall[0] <= 1'b1; end

34. 

35. 8: // Read and store R3

36. if( iDone ) begin D2[15:8] <= iData; isCall[0] <= 1'b0; i <= i + 1'b1; end

37. else begin isCall[0] <= 1'b1; end

38. 

39. 9: // Read and store R3

40. if( iDone ) begin D2[7:0] <= iData; isCall[0] <= 1'b0; i <= i + 1'b1; end

41. else begin isCall[0] <= 1'b1; end

42. 

43. 10,11: // Send free clock

44. if( iDone ) begin isCall[0] <= 1'b0; i <= i + 1'b1; end

45. else begin isCall[0] <= 1'b1; D1 <= 8'hFF; end

46. 

47. 12: // Disable cs, genarate done signal

48. begin rCS <= 1'b1; isDone <= 1'b1; i <= i + 1'b1; end

49. 

50. 13:

51. begin isDone <= 1'b0; i <= 4'd0; end


代码25.2

如代码25.2所示,步骤0给足8个时钟,步骤1拉低CS,步骤2则再给足8个时钟。

步骤3准备命令CMD58,步骤4则重复100次写命令,直至第4字节的反馈数据为8’h01为止,否则准备错误信息,并且跳转步骤12。步骤5暂存第4字节的内容,步骤6~9则是读取并且暂存接续的内容。步骤10~11分别给足8个时钟,然后步骤12~13拉高CS之余也产生完成信号。

图25.3 CMD55+ACMD41的理想时序图。

图25.3是 CMD55+ACMD41的理想时序图。ACMD××是版本SDV2才有的扩展命令,凡是发送扩展命令之前,主机必须事先发送命令CMD55示意SD卡下一个命令为扩展命令。T0之际,主机拉高CS并且给足8个时钟。T1~2之际,主机拉低CS并且给足8个时钟。T3~4之际主机发送命令CMD55—{ 8’h77,32’d0,8’hff },然后SD卡会返回数据8’h01以示接收成功。

T5~6之际,主机发送命令ACMD41—{ 8’h69, 8’h40, 24’d0,8’hff },然后SD卡在T7接收并且反馈数据8’h00以示接收成功,同时也告诉主机SD卡的当前状态已经切至“传输状态”。T8~9之际,主机拉高CS并且给足80个结束时钟。在此笔者需要补充一下,命令ACMD41的 8’h40,亦即8’b0100_0000,恰好针对R3第三字节的标示位CCS。简言之,命令ACMD41的作用是手动为SD卡设置CCS标志位。

对此,Verilog的描述结果如代码25.3所示:

1. case( i )

2. 

3. 0: // Send free clock

4. if( iDone ) begin isCall[0] <= 1'b0; i <= i + 1'b1; end

5. else begin isCall[0] <= 1'b1; D1 <= 8'hff; end

6. 

7. 1: // Enable cs

8. begin rCS <= 1'b0; i <= i + 1'b1; end

9. 

10. 2: // Send free clock

11. if( iDone ) begin isCall[0] <= 1'b0; i <= i + 1'b1; end

12. else begin isCall[0] <= 1'b1; D1 <= 8'hff; end 

13. 

14. 3: // Prepare cmd55

15. begin D4 <= { 8'h77,32'd0,8'hff }; i <= i + 1'b1; end

16. 

17. 4: // Send and store R1 

18. if( iDone ) begin D2[39:32] <= iData; isCall[1] <= 1'b0; i <= i + 1'b1; end 

19. else isCall[1] <= 1'b1; 

20. 

21. 5: // Prepare acmd41

22. begin D4 <= { 8'h69,8'h40,24'd0,8'hff }; i <= i + 1'b1; end

23. 

24. 6: // Send and store R1

25. if( iDone ) begin D2[31:24] <= iData; isCall[1] <= 1'b0; i <= i + 1'b1; end 

26. else isCall[1] <= 1'b1; 

27. 

28. 7: // Try 1000 times, ready error code.

29. if( C1 == 16'd1000 ) begin D2[7:0] <= CMD41ERR; C1 <= 16'd0; i <= 4'd10; end

30. else if( iData != 8'h00 ) begin C1 <= C1 + 1'b1; i <= 4'd3; end

31. else if( iData == 8'h00 ) begin C1 <= 16'd0; i <= i + 1'b1; end

32. 

33. 8: // Disable cs

34. begin rCS <= 1'b1; i <= i + 1'b1; end

35. 

36. 9: // Send free clock

37. if( C1 == 10 ) begin C1 <= 16'd0; i <= i + 1'b1; end

38. else if( iDone ) begin isCall[0] <= 1'b0; C1 <= C1 + 1'b1; end

39. else begin isCall[0] <= 1'b1; D1 <= 8'hFF; end

40. 

41. 10: // Disable cs, generate done signal

42. begin rCS <= 1'b1; isDone <= 1'b1; i <= i + 1'b1; end

43. 

44. 11:

45. begin isDone <= 1'b0; i <= 4'd0; end


代码25.3

如代码25.3所示,步骤0拉高CS并且给足8个时钟,步骤1拉低CS,步骤2则给足8个时钟。步骤3准备命令CMD55,然后再步骤4将其写入,反馈数据则暂存至D2[39:32],。步骤5准备命令ACMD41,并且手动设置CCS标示位—8’h40,然后在步骤6将其写入

,事后才将反馈数据暂存D2[31:24]当中。

步骤7检测ACMD41的反馈数据,如果内容不为8’h00便跳转步骤3,并且重复1000次同样的操作,直至反馈数据为8’h00为止,否则准备错误信息,然后跳转步骤10。简单来说,步骤3~7组成简单的do ... while 循环,其中步骤7用来控制循环。步骤8拉高CS,然后步骤9给足80个结束时钟。步骤10~11拉高CS之余也产生完成信号。

图25.4 CMD58的理想时序图(传输状态)。

当主机成功写入命令CMD55+ACMD41的时候,CMD58的反馈数据R3也会跟着发生变化。如图25.4所示,T0之际主机拉高CS之余也给足8个时钟。T1~T2之际,主机拉低CS也给足8个时钟。T3~T4之际,主机发送命令CMD58。T5之际,SD卡接收以后便会反馈5个字节的R3,其中字节4与字节3的内容已经发生变化。

字节4为8’h00,表示SD卡已经处于传输状态。字节3为8’hC0(也是8’b1100_0000),表示SD卡结束忙碌之余,它也成功认识自己是大容量的储存器。对此,Verilog可以这样描述,结果如代码25.4所示:

1. ...... 

2. 4: // Try 100 times, ready error code

3. if( C1 == 10'd100 ) begin D2[7:0] <= CMD58ERR; C1 <= 16'd0; i <= 4'd12; end

4. else if( (iDone && iData != 8'h00) ) begin isCall[1] <= 1'b0; C1 <= C1 + 1'b1; end

5. else if( (iDone && iData == 8'h00) ) begin isCall[1] <= 1'b0; C1 <= 16'd0; i <= i + 1'b1; end 

6. else isCall[1] <= 1'b1; 

7. ......


代码25.4

如代码25.4所示,代码25.4与代码25.2的内容大同小异,除了第4~5行 8’h00以外。

图25.5 版本SDV2与SDHCV2初始化的流程图。

图25.5是SD卡版本SDV2与版本SDHCV2的初始化流程图,而且前提条件是SD卡是健康又不顺坏的硬件资源。主机首先发送CMD0,如果SD卡反馈8’h01,SD卡便进入待机模式。再者,主机发送CMD8并且跟随一些参数内容—{ 8’h00,8’h00,8’h01,8’haa },如果反馈内容为8’h01流程便继续。主机紧接着检测字节1和字节0的内容是否为8’h01与8’haa,如果任一不是便可确知那是版本SDV1.×的SD卡。换之,如果字节1和字节0的内容是8’h01与8’haa。那么继续流程。

主机随后发送CMD58要求SD卡反馈内部状态,如果字节4为8’h01便继续读取字节3,如果字节3为8’h00,则表示SD卡不仅忙碌中,而且还未认识CCS标示。再来主机发送CMD55+ACMD41,并且伴随参数 { 8’h40,8’h00,8’h00,8’h00}。如果反馈内容为8’h00就表示CCS的设置动作不仅成功,而且SD卡也正在步入传输模式。

最后,主机发送CMD58再次要求SD卡反馈内容状态。如果反馈内容字节4为8’h00便继续读取字节3,如果字节3的内容为8’hC0便可确认那是版本SDHCV2的SD卡。反之,字节3为8’h80便可确认那是版本SDV2的SD卡。完后,SD卡便全面进入传输模式,初始化过程也因此结束,真是可喜可贺!

图25.6 CMD16的理想时序图。

CMD16的作用主要是设置多字节读写的长度,默认下一次性多字节读写设置为512。SD卡进化为版本SDV2或者SDHCV2以后,我们也知道它们不用偏移地址,因此多字节读写的长度才可以更改。当然,更改内容也不是任由我们随心所欲 ... 根据手册,更改内容要么是 512(默认),要么是1024,要么就是2048。

图25.6是CMD16的理想时序图,而CMD16也是可有可无的可怜虫,不过笔者还是大发慈悲介绍它吧。T0之际,主机拉高CS并且给足8个时钟。T1~2之际,主机拉低CS至于它也给足8个时钟。T3~5之际,主机发送命令CMD16—{ 8’h50,32’d512,8’hff },其中32’d512示意多字节读写的长度为512,结果SD卡返回8’h00以示了解。T6~7之际,主机分别给足8个结束时钟,然后再T8拉高CS。

对此,Verilog的描述结果如代码25.5所示:

1. case( i )

2. 

3. 0: // Send free clock

4. if( iDone ) begin isCall[0] <= 1'b0; i <= i + 1'b1; end

5. else begin isCall[0] <= 1'b1; D1 <= 8'hff; end

6. 

7. 1: // Enable CS

8. begin rCS <= 1'b0; i <= i + 1'b1; end

9. 

10. 2: // Send free clock

11. if( iDone ) begin isCall[0] <= 1'b0; i <= i + 1'b1; end

12. else begin isCall[0] <= 1'b1; D1 <= 8'hff; end

13. 

14. 3: // Prepare cmd 16, 512 block length

15. begin D4 <= { 8'h50,32'd512,8'hff }; i <= i + 1'b1; end

16. 

17. 4: // Try 100 times, ready error code.

18. if( C1 == 10'd100 ) begin D2[7:0] <= CMD16ERR; C1 <= 16'd0; i <= 4'd8; end

19. else if( (iDone && iData != 8'h00) ) begin isCall[1] <= 1'b0; C1 <= C1 + 1'b1; end

20. else if( (iDone && iData == 8'h00) ) begin isCall[1] <= 1'b0; C1 <= 16'd0; i <= i + 1'b1; end 

21. else isCall[1] <= 1'b1; 

22. 

23. 5: // Ready OK code

24. begin D2[7:0] <= CMD16OK; i <= i + 1'b1; end

25. 

26. 6,7: // Send free clock

27. if( iDone ) begin isCall[0] <= 1'b0; i <= i + 1'b1; end

28. else begin isCall[0] <= 1'b1; D1 <= 8'hFF; end

29. 

30. 8: // Disable cs , generate done signal

31. begin rCS <= 1'b1; isDone <= 1'b1; i <= i + 1'b1; end

32. 

33. 9:

34. begin isDone <= 1'b0; i <= 4'd0; end


代码25.5

如代码25.5所示,步骤0拉高CS之余也给足8个时钟,步骤1拉低CS,步骤2再给足8个时钟。步骤3准备CMD16并且伴随参数32’d512,内容意指多字节读写的长度为512。随后,步骤4将其写入100次,直至反馈数据为8’h00为止,否则准备错误信息,然后跳转步骤8。步骤5准备成功信息,步骤6~7分别给足8个时钟,然后步骤8~9拉高CS之余也产生完成信号。

虽然版本SDV2与版本SDHCV2多少也有影响CMD24与CMD17,不过不打紧,事后笔者会继续解释的 ... 所以暂请读者憋着蛋蛋一下。上述内容理解完毕以后,我们便可以开始建模了。

图25.7 SD卡基础模块的建模图。

图25.7是SD卡基础模块的建模图 ... 目视下,更改的内容却不多,如SD卡控制模块的oTag有40位宽,iAddr有32位宽,Call/Done则有8位宽,位分配如表25.2所示:

表25.2 Call/Done的位宽内容。

位分配

说明

Call[7]

调用CMD24,写命令

Call[6]

调用CMD17,读命令

Call[5]

调用CMD16,设置读写长度

Call[4]

调用CMD58,读取SD卡状态(传输状态)

Call[3]

调用CMD55+ACMD41,设置CCS标示位

Call[2]

调用CMD58,读取SD卡状态(待机状态)

Call[1]

调用CMD8,配置物理参数

Call[0]

调用CMD0,复位SD卡

sdcard_funcmod.v

图25.8 SD卡功能模块的建模图。

图25.8是SD卡功能模块的建模图,这家伙相较实验二十四的兄弟差别并不大,不过具体内容还是直接窥视代码吧。

19. always @ ( posedge CLOCK or negedge RESET )

20. if( !RESET )

21. begin 

22. isFull <= FLCLK;

23. isHalf <= FLHALF;

24. isQuarter <= FLQUARTER;

25. end 

26. else if( iCmd[47:40] == 8'h50 && isDone )

27. begin

28. isFull <= FHCLK;

29. isHalf <= FHHALF;

30. isQuarter <= FHQUARTER;

31. end


以上内容为周边操作,改变非内容是26行的 8’h50,该行表示CMD16调用完毕以后便更动速率。

sdcard_ctrlmod.v

图25.9 SD卡控制模块的建模图。

如图25.9所示,该控制模块依然还是一只长满箭头的刺猬,不过改变内容也只有左边的 Call/Done信号,Addr信号,还有Tag信号而已,具体内容我们还是来看代码吧。

1. module sdcard_ctrlmod

2. (

3. input CLOCK, RESET,

4. output SD_NCS,

5. 

6. input [7:0]iCall, 

7. output oDone,

8. input [31:0]iAddr,

9. output [39:0]oTag,

10. 

11. output [1:0]oEn, // [1] Write [0] Read

12. input [7:0]iDataFF,

13. output [7:0]oDataFF,

14. 

15. output [1:0]oCall,

16. input iDone,

17. output [47:0]oAddr,

18. input [7:0]iData,

19. output [7:0]oData

20. );


以上内容为相关的出入端声明。

21. parameter CMD0ERR = 8'hA1, CMD0OK = 8'hA2, CMD1ERR = 8'hA3, CMD1OK = 8'hA4;

22. parameter CMD24ERR = 8'hA5, CMD24OK = 8'hA6, CMD17ERR = 8'hA9, CMD17OK = 8'hAA;

23. parameter CMD16ERR = 8'hA7,CMD16OK = 8'hA8;

24. parameter CMD8ERR = 8'hB1, CMD41ERR = 8'hC0, CMD58ERR = 8'hC1;

25. parameter SDV2 = 8'hD1, SDV2HC = 8'hD2;

26. parameter T1MS = 16'd10;

27. 


以上内容为失败信息与成功信息的常量声明,此外也有版本信息还有延迟常量。

28. reg [3:0]i;

29. reg [15:0]C1;

30. reg [7:0]D1,D3; // D1 WrData, D2 FbData, D3 RdData

31. reg [39:0]D2;

32. reg [47:0]D4; // D4 Cmd

33. reg [1:0]isCall,isEn;

34. reg rCS;

35. reg isDone;

36. 

37. always @ ( posedge CLOCK or negedge RESET )

38. if( !RESET )

39. begin

40. i <= 4'd0;

41. C1 <= 16'd0;

42. { D1,D3 } <= { 8'd0,8'd0 };

43. D2 <= 40'd0;

44. D4 <= 48'd0;

45. { isCall, isEn } <= { 2'd0,2'd0 }; 

46. rCS <= 1'b1;

47. end


以上内容为相关的寄存器声明还有复位操作,注意那只暂存反馈信息的D2已经扩展至40位宽。

48. else if( iCall[7] ) // write block

49. case( i )

50. 

51. 0: // Enable cs and prepare cmd 24

52. begin rCS <= 1'b0; D4 = { 8'h58, iAddr, 8'hFF }; i <= i + 1'b1; end

53. 

54. 1: // Try 100 times , 8'h03 for error code.

55. if( C1 == 100 ) begin D2[7:0] <= CMD24ERR; C1 <= 16'd0; i <= 4'd14; end

56. else if( iDone && iData != 8'h00) begin isCall[1] <= 1'b0; C1 <= C1 + 1'b1; end

57. else if( iDone && iData == 8'h00 ) begin isCall[1] <= 1'b0; C1 <= 16'd0; i <= i + 1'b1; end

58. else isCall[1] <= 1'b1;

59. 

60. 2: // Send 800 free clock 

61. if( C1 == 100 ) begin C1 <= 16'd0; i <= i + 1'b1; end

62. else if( iDone ) begin isCall[0] <= 1'b0; C1 <= C1 + 1'b1; end 

63. else isCall[0] <= 1'b1;

64. 

65. 3: // Send Call Byte 0xfe

66. if( iDone ) begin isCall[0] <= 1'b0; i <= i + 1'b1; end

67. else begin isCall[0] <= 1'b1; D1 <= 8'hFE; end

68. 

69. /*****************/

70. 

71. 4: // Pull up read req.

72. begin isEn[0] <= 1'b1; i <= i + 1'b1; end

73. 

74. 5: // Pull down read req.

75. begin isEn[0] <= 1'b0; i <= i + 1'b1; end

76. 

77. 6: // Write byte read from fifo

78. if( iDone ) begin isCall[0] <= 1'b0; i <= i + 1'b1; end

79. else begin isCall[0] <= 1'b1; D1 <= iDataFF; end

80. 

81. 7: // Repeat 512 times 

82. if( C1 == 10'd511 ) begin C1 <= 16'd0; i <= i + 1'b1; end

83. else begin C1 <= C1 + 1'b1; i <= 4'd4; end

84. 

85. /*****************/

86. 

87. 8: // Write 1st CRC

88. if( iDone ) begin isCall[0] <= 1'b0; i <= i + 1'b1; end

89. else begin isCall[0] <= 1'b1; D1 <= 8'hff; end

90. 

91. 9: // Write 2nd CRC

92. if( iDone ) begin isCall[0] <= 1'b0; i <= i + 1'b1; end

93. else begin isCall[0] <= 1'b1; D1 <= 8'hff; end

94. 

95. 10: // Read respond

96. if( iDone ) begin isCall[0] <= 1'b0; i <= i + 1'b1; end

97. else begin isCall[0] <= 1'b1; end

98. 

99. 11: // if not 8'h05, write block faild, 8'h03 for error code.

100. if( (iData & 8'h1F) != 8'h05 ) begin D2[7:0] <= CMD24ERR; i <= 4'd14; end

101. else i <= i + 1'b1;

102. 

103. 12: // Wait unitl sdcard free

104. if( iDone && iData == 8'hff ) begin isCall[0] <= 1'b0; i <= i + 1'b1; end

105. else if( iDone ) begin isCall[0] <= 1'b0; end

106. else begin isCall[0] <= 1'b1; end

107. 

108. /*****************/

109. 

110. 13: // Read OK code;

111. begin D2[7:0] <= CMD24OK; i <= i + 1'b1; end

112. 

113. 14: // Disable cs and generate done signal

114. begin rCS <= 1'b1; isDone <= 1'b1; i <= i + 1'b1; end

115. 

116. 15:

117. begin isDone <= 1'b0; i <= 4'd0; end

118. 

119. endcase


以上内容为命令CMD24,注意第52行的写地址,地址没有左移9位。

120. else if( iCall[6] ) // read block

121. case( i )

122. 

123. 0: // Enable cs and prepare cmd 17;

124. begin rCS <= 1'b0; D4 <= { 8'h51, iAddr, 8'hff }; i <= i + 1'b1; end

125. 

126. 1: // Try 100 times, ready error code;

127. if( C1 == 100 ) begin D2[7:0] <= CMD17ERR; C1 <= 16'd0; i <= 4'd11; end

128. else if( iDone && iData != 8'h00 ) begin isCall[1] <= 1'b0; C1 <= C1 + 1'b1; end

129. else if( iDone && iData == 8'h00 ) begin isCall[1] <= 1'b0; C1 <= 16'd0; i <= i + 1'b1; end

130. else isCall[1] <= 1'b1;

131. 

132. 2: // Waiting read ready 8'hfe

133. if( iDone && iData == 8'hfe ) begin isCall[0] <= 1'b0; i <= i + 1'b1; end

134. else if( iDone && iData != 8'hfe ) begin isCall[0] <= 1'b0; end

135. else isCall[0] <= 1'b1;

136. 

137. /********/

138. 

139. 3: // Read 1 byte form sdcard

140. if( iDone ) begin D3 <= iData; isCall[0] <= 1'b0; i <= i + 1'b1; end

141. else begin isCall[0] <= 1'b1; end

142. 

143. 4: // Pull up write req.

144. begin isEn[1] <= 1'b1; i <= i + 1'b1; end

145. 

146. 5: // Pull down write req.

147. begin isEn[1] <= 1'b0; i <= i + 1'b1; end 

148. 

149. 6: // Repeat 512 times

150. if( C1 == 10'd511 ) begin C1 <= 16'd0; i <= i + 1'b1; end

151. else begin C1 <= C1 + 1'b1; i <= 4'd3; end

152. 

153. /********/

154. 

155. 7,8: // Read 1st and 2nd byte CRC

156. if( iDone ) begin isCall[0] <= 1'b0; i <= i + 1'b1; end 

157. else isCall[0] <= 1'b1;

158. 

159. 9: // Disable CS, ready OK code.

160. begin D2[7:0] <= CMD17OK; rCS <= 1'b1; i <= i + 1'b1; end

161. 

162. /********/

163. 

164. 10: // Send 8 free clock

165. if( iDone ) begin isCall[0] <= 1'b0; i <= i + 1'b1; end 

166. else begin isCall[0] <= 1'b1; D1 <= 8'hFF; end

167. 

168. 11: // Disable cs, generate done signal

169. begin rCS <= 1'b1; isDone <= 1'b1; i <= i + 1'b1; end

170. 

171. 12:

172. begin isDone <= 1'b0; i <= 4'd0; end

173. 

174. endcase


175. else if( iCall[5] ) // cmd16 

176. case( i )

177. 

178. 0: // Send free clock

179. if( iDone ) begin isCall[0] <= 1'b0; i <= i + 1'b1; end

180. else begin isCall[0] <= 1'b1; D1 <= 8'hff; end

181. 

182. 1: // Enable CS

183. begin rCS <= 1'b0; i <= i + 1'b1; end

184. 

185. 2: // Send free clock

186. if( iDone ) begin isCall[0] <= 1'b0; i <= i + 1'b1; end

187. else begin isCall[0] <= 1'b1; D1 <= 8'hff; end

188. 

189. /************/

190. 

191. 3: // Prepare cmd 16, 512 block length

192. begin D4 <= { 8'h50,32'd512,8'hff }; i <= i + 1'b1; end

193. 

194. 4: // Try 100 times, ready error code.

195. if( C1 == 10'd100 ) begin D2[7:0] <= CMD16ERR; C1 <= 16'd0; i <= 4'd8; end

196. else if( (iDone && iData != 8'h00) ) begin isCall[1] <= 1'b0; C1 <= C1 + 1'b1; end

197. else if( (iDone && iData == 8'h00) ) begin isCall[1] <= 1'b0; C1 <= 16'd0; i <= i + 1'b1; end 

198. else isCall[1] <= 1'b1; 

199. 

200. 5: // Ready OK code

201. begin D2[7:0] <= CMD16OK; i <= i + 1'b1; end

202. 

203. /******************/

204. 

205. 6,7: // Send free clock

206. if( iDone ) begin isCall[0] <= 1'b0; i <= i + 1'b1; end

207. else begin isCall[0] <= 1'b1; D1 <= 8'hFF; end

208. 

209. 8: // Disable cs , generate done signal

210. begin rCS <= 1'b1; isDone <= 1'b1; i <= i + 1'b1; end

211. 

212. 9:

213. begin isDone <= 1'b0; i <= 4'd0; end

214. 

215. endcase


 

以上内容为命令CMD16。

216. else if( iCall[4] ) // cmd58 transfer mode

217. case( i )

218. 

219. 0: // Send free clock

220. if( iDone ) begin isCall[0] <= 1'b0; i <= i + 1'b1; end

221. else begin isCall[0] <= 1'b1; D1 <= 8'hff; end

222. 

223. 1: // Enable cs

224. begin rCS <= 1'b0; i <= i + 1'b1; end

225. 

226. 2: // Send free clock

227. if( iDone ) begin isCall[0] <= 1'b0; i <= i + 1'b1; end

228. else begin isCall[0] <= 1'b1; D1 <= 8'hff; end

229. 

230. /************/

231. 

232. 3: // Prepare cmd 58

233. begin D4 <= { 8'h7A,32'd0,8'h01 }; i <= i + 1'b1; end

234. 

235. 4: // Try 100 times, ready error code

236. if( C1 == 10'd100 ) begin D2[7:0] <= CMD58ERR; C1 <= 16'd0; i <= 4'd12; end

237. else if( (iDone && iData != 8'h00) ) begin isCall[1] <= 1'b0; C1 <= C1 + 1'b1; end

238. else if( (iDone && iData == 8'h00) ) begin isCall[1] <= 1'b0; C1 <= 16'd0; i <= i + 1'b1; end 

239. else isCall[1] <= 1'b1; 

240. 

241. 5: // Store R3

242. begin D2[39:32] <= iData; i <= i + 1'b1; end

243. 

244. 6: // Read and store R3

245. if( iDone ) begin D2[31:24] <= iData; isCall[0] <= 1'b0; i <= i + 1'b1; end

246. else begin isCall[0] <= 1'b1; end

247. 

248. 7: // Read and store R3

249. if( iDone ) begin D2[23:16] <= iData; isCall[0] <= 1'b0; i <= i + 1'b1; end

250. else begin isCall[0] <= 1'b1; end

251. 

252. 8: // Read and store R3

253. if( iDone ) begin D2[15:8] <= iData; isCall[0] <= 1'b0; i <= i + 1'b1; end

254. else begin isCall[0] <= 1'b1; end

255. 

256. 9: // Read and store R3

257. if( iDone ) begin D2[7:0] <= iData; isCall[0] <= 1'b0; i <= i + 1'b1; end

258. else begin isCall[0] <= 1'b1; end

259. 

260. /******************/

261. 

262. 10,11: // Send free clock

263. if( iDone ) begin isCall[0] <= 1'b0; i <= i + 1'b1; end

264. else begin isCall[0] <= 1'b1; D1 <= 8'hFF; end

265. 

266. 12: // Disable cs, generate done signal

267. begin rCS <= 1'b1; isDone <= 1'b1; i <= i + 1'b1; end

268. 

269. 13:

270. begin isDone <= 1'b0; i <= 4'd0; end

271. 

272. endcase 


以上内容为命令CMD58(传输状态)。

273. else if( iCall[3] ) // cmd55 + acmd41

274. case( i )

275. 

276. 0: // Send free clock

277. if( iDone ) begin isCall[0] <= 1'b0; i <= i + 1'b1; end

278. else begin isCall[0] <= 1'b1; D1 <= 8'hff; end

279. 

280. 1: // Enable cs

281. begin rCS <= 1'b0; i <= i + 1'b1; end

282. 

283. 2: // Send free clock

284. if( iDone ) begin isCall[0] <= 1'b0; i <= i + 1'b1; end

285. else begin isCall[0] <= 1'b1; D1 <= 8'hff; end

286. 

287. /*************/ 

288. 

289. 3: // Prepare cmd55

290. begin D4 <= { 8'h77,32'd0,8'hff }; i <= i + 1'b1; end

291. 

292. 4: // Send and store R1 

293. if( iDone ) begin D2[39:32] <= iData; isCall[1] <= 1'b0; i <= i + 1'b1; end 

294. else isCall[1] <= 1'b1; 

295. 

296. 5: // Prepare acmd41

297. begin D4 <= { 8'h69,8'h40,24'd0,8'hff }; i <= i + 1'b1; end

298. 

299. 6: // Send and store R1

300. if( iDone ) begin D2[31:24] <= iData; isCall[1] <= 1'b0; i <= i + 1'b1; end 

301. else isCall[1] <= 1'b1; 

302. 

303. 7: // Try 1000 times, ready error code.

304. if( C1 == 16'd1000 ) begin D2[7:0] <= CMD41ERR; C1 <= 16'd0; i <= 4'd10; end

305. else if( iData != 8'h00 ) begin C1 <= C1 + 1'b1; i <= 4'd3; end

306. else if( iData == 8'h00 ) begin C1 <= 16'd0; i <= i + 1'b1; end

307. 

308. /******************/

309. 

310. 8: // Disable cs

311. begin rCS <= 1'b1; i <= i + 1'b1; end

312. 

313. 9: // Send free clock

314. if( C1 == 10 ) begin C1 <= 16'd0; i <= i + 1'b1; end

315. else if( iDone ) begin isCall[0] <= 1'b0; C1 <= C1 + 1'b1; end

316. else begin isCall[0] <= 1'b1; D1 <= 8'hFF; end

317. 

318. 10: // Disable cs, generate done signal

319. begin rCS <= 1'b1; isDone <= 1'b1; i <= i + 1'b1; end

320. 

321. 11:

322. begin isDone <= 1'b0; i <= 4'd0; end

323. 

324. endcase


以上内容为命令CMD55+ACMD41。

325. else if( iCall[2] ) // cmd58 idle mode

326. case( i )

327. 

328. 0: // Send free clock

329. if( iDone ) begin isCall[0] <= 1'b0; i <= i + 1'b1; end

330. else begin isCall[0] <= 1'b1; D1 <= 8'hff; end

331. 

332. 1: // Enable cs 

333. begin rCS <= 1'b0; i <= i + 1'b1; end

334. 

335. 2: // Send free clock

336. if( iDone ) begin isCall[0] <= 1'b0; i <= i + 1'b1; end

337. else begin isCall[0] <= 1'b1; D1 <= 8'hff; end

338. 

339. /************/

340. 

341. 3: // prepare cmd 58

342. begin D4 <= { 8'h7A,32'd0,8'h01 }; i <= i + 1'b1; end

343. 

344. 4: // Try 100 time, ready error code.

345. if( C1 == 10'd100 ) begin D2[7:0] <= CMD58ERR; C1 <= 16'd0; i <= 4'd12; end

346. else if( (iDone && iData != 8'h01) ) begin isCall[1] <= 1'b0; C1 <= C1 + 1'b1; end

347. else if( (iDone && iData == 8'h01) ) begin isCall[1] <= 1'b0; C1 <= 16'd0; i <= i + 1'b1; end 

348. else isCall[1] <= 1'b1; 

349. 

350. 5: // Store R3

351. begin D2[39:32] <= iData; i <= i + 1'b1; end

352. 

353. 6: // Read and store R3

354. if( iDone ) begin D2[31:24] <= iData; isCall[0] <= 1'b0; i <= i + 1'b1; end

355. else begin isCall[0] <= 1'b1; end

356. 

357. 7: // Read and store R3

358. if( iDone ) begin D2[23:16] <= iData; isCall[0] <= 1'b0; i <= i + 1'b1; end

359. else begin isCall[0] <= 1'b1; end

360. 

361. 8: // Read and store R3

362. if( iDone ) begin D2[15:8] <= iData; isCall[0] <= 1'b0; i <= i + 1'b1; end

363. else begin isCall[0] <= 1'b1; end

364. 

365. 9: // Read and store R3

366. if( iDone ) begin D2[7:0] <= iData; isCall[0] <= 1'b0; i <= i + 1'b1; end

367. else begin isCall[0] <= 1'b1; end

368. 

369. /******************/

370. 

371. 10,11: // Send free clock

372. if( iDone ) begin isCall[0] <= 1'b0; i <= i + 1'b1; end

373. else begin isCall[0] <= 1'b1; D1 <= 8'hFF; end

374. 

375. 12: // Disable cs, genarate done signal

376. begin rCS <= 1'b1; isDone <= 1'b1; i <= i + 1'b1; end

377. 

378. 13:

379. begin isDone <= 1'b0; i <= 4'd0; end

380. 

381. endcase


以上内容为命令CMD58(待机状态)。

382. else if( iCall[1] ) // Cmd8

383. case( i )

384. 

385. 0: // Send free clock

386. if( iDone ) begin isCall[0] <= 1'b0; i <= i + 1'b1; end

387. else begin isCall[0] <= 1'b1; D1 <= 8'hff; end

388. 

389. 1: // Enable cs

390. begin rCS <= 1'b0; i <= i + 1'b1; end

391. 

392. 2: // Send free clock

393. if( iDone ) begin isCall[0] <= 1'b0; i <= i + 1'b1; end

394. else begin isCall[0] <= 1'b1; D1 <= 8'hff; end

395. 

396. /************/

397. 

398. 3: // Prepare Cmd8 // 8'h01 = 2.7~3.6v, 8'hA0A0 default check pattern

399. begin D4 <= { 8'h48,16'd0,8'h01,8'hAA,8'h87 }; i <= i + 1'b1; end

400. 

401. 4: // Try 100 times, ready error code.

402. if( C1 == 10'd100 ) begin D2[7:0] <= CMD8ERR; C1 <= 16'd0; i <= 4'd13; end

403. else if( (iDone && iData != 8'h01) ) begin isCall[1]<= 1'b0; C1 <= C1 + 1'b1; end

404. else if( (iDone && iData == 8'h01) ) begin isCall[1] <= 1'b0; C1 <= 16'd0; i <= i + 1'b1; end 

405. else isCall[1] <= 1'b1; 

406. 

407. 5: // Store R7

408. begin D2[39:32] <= iData; i <= i + 1'b1; end

409. 

410. 6: // Read and store R7

411. if( iDone ) begin D2[31:24] <= iData; isCall[0] <= 1'b0; i <= i + 1'b1; end

412. else begin isCall[0] <= 1'b1; end

413. 

414. 7: // Read and store R7

415. if( iDone ) begin D2[23:16] <= iData; isCall[0] <= 1'b0; i <= i + 1'b1; end

416. else begin isCall[0] <= 1'b1; end

417. 

418. 8: // Read and store R7

419. if( iDone ) begin D2[15:8] <= iData; isCall[0] <= 1'b0; i <= i + 1'b1; end

420. else begin isCall[0] <= 1'b1; end

421. 

422. 9: // Read and store R7

423. if( iDone ) begin D2[7:0] <= iData; isCall[0] <= 1'b0; i <= i + 1'b1; end

424. else begin isCall[0] <= 1'b1; end

425. 

426. 10: // Disable cs

427. begin rCS <= 1'b1; i <= i + 1'b1; end

428. 

429. /******************/

430. 

431. 11,12: // Send free clock

432. if( iDone ) begin isCall[0] <= 1'b0; i <= i + 1'b1; end

433. else begin isCall[0] <= 1'b1; D1 <= 8'hFF; end

434. 

435. 13: // Disable cs, generate done signal

436. begin rCS <= 1'b1; isDone <= 1'b1; i <= i + 1'b1; end

437. 

438. 14:

439. begin isDone <= 1'b0; i <= 4'd0; end

440. 

441. endcase


以上内容为命令CMD8。

442. else if( iCall[0] ) // cmd0

443. case( i )

444. 

445. 0: // Prepare Cmd0

446. begin D4 <= 48'h40_00_00_00_00_95; i <= i + 1'b1; end

447. 

448. 1: // Wait 1MS for warm up;

449. if( C1 == T1MS -1) begin C1 <= 16'd0; i <= i + 1'b1; end

450. else begin C1 <= C1 + 1'b1; end

451. 

452. 2: // Send free clock

453. if( C1 == 10'd10 ) begin C1 <= 16'd0; i <= i + 1'b1; end

454. else if( iDone ) begin isCall[0] <= 1'b0; C1 <= C1 + 1'b1; end

455. else begin isCall[0] <= 1'b1; D1 <= 8'hff; end

456. 

457. 3: // Disable cs

458. begin rCS <= 1'b0; i <= i + 1'b1; end

459. 

460. 4: // Try 200 time, ready error code.

461. if( C1 == 10'd200 ) begin D2[7:0] <= CMD0ERR; C1 <= 16'd0; i <= 4'd8; end

462. else if( iDone && iData != 8'h01) begin isCall[1] <= 1'b0; C1 <= C1 + 1'b1; end

463. else if( iDone && iData == 8'h01 ) begin isCall[1] <= 1'b0; D2<= iData; C1 <= 16'd0; i <= i + 1'b1; end 

464. else isCall[1] <= 1'b1; 

465. 

466. 5: // Disable cs

467. begin rCS <= 1'b1 ; i <= i + 1'b1; end

468. 

469. 6: // Send free clock

470. if( iDone ) begin isCall[0] <= 1'b0; i <= i + 1'b1; end

471. else begin isCall[0] <= 1'b1; D1 <= 8'hff; end

472. 

473. 7: // Ready OK code.

474. begin D2[7:0] <= CMD0OK; i <= i + 1'b1; end

475. 

476. 8: // Disbale cs, generate done signal

477. begin rCS <= 1'b1; isDone <= 1'b1; i <= i + 1'b1; end

478. 

479. 9:

480. begin isDone <= 1'b0; i <= 4'd0; end

481. 

482. endcase

483. 


以上内容为命令CMD0。

484. assign SD_NCS = rCS;

485. assign oDone = isDone;

486. assign oTag = D2;

487. assign oEn = isEn; 

488. assign oDataFF = D3;

489. assign oCall = isCall;

490. assign oAddr = D4;

491. assign oData = D1;

492. 

493. endmodule


以上内容为相关的驱动声明。

fifo_savemod.v

该模块与实验二十四一样。

sdcard_basemod.v

连线部署的内容请参考图25.7。此外,相较实验二十四,该模块的修改内容只有部分位宽而已。

1. module sdcard_basemod

2. (

3. input CLOCK, RESET,

4. input SD_DOUT,

5. output SD_CLK,

6. output SD_DI,

7. output SD_NCS, 

8. 

9. input [7:0]iCall,

10. output oDone,

11. input [31:0]iAddr,

12. output [39:0]oTag,

13. 

14. input [1:0]iEn,

15. input [7:0]iData,

16. output [7:0]oData

17. ); 

18. ......


修改的内容有第9行的 iCall,第11行的iAddr,还有第12行的oTag。

sdcard_demo.v

图25.10 实验二十五的建模图。

图25.10是实验二十五的建模图,目视之下的修改内容也是 Call/Done等信号的位宽而已。不过,核心程序的内容相较实验二十四却有天壤之别,具体内容让我们来看代码吧。

1. module sdcard_demo

2. (

3. input CLOCK,RESET,

4. output SD_NCS, 

5. output SD_CLK,

6. input SD_DOUT,

7. output SD_DI,

8. output TXD

9. );


以上内容为相关的出入端声明。

10. wire DoneU1;

11. wire [39:0]TagU1;

12. wire [7:0]DataU1;

13. 

14. sdcard_basemod U1

15. (

16. .CLOCK( CLOCK ), 

17. .RESET( RESET ),

18. .SD_DOUT( SD_DOUT ),

19. .SD_CLK( SD_CLK ),

20. .SD_DI( SD_DI ),

21. .SD_NCS( SD_NCS ), 

22. .iCall( isCall ),

23. .oDone( DoneU1 ),

24. .iAddr( D1 ),

25. .oTag( TagU1 ),

26. /**********/

27. .iEn( isEn ),

28. .iData( D2 ),

29. .oData( DataU1 )

30. );

31. 


 

以上内容为SD卡基础模块的实例化。

32. parameter B115K2 = 11'd434, TXFUNC = 6'd48;

33. 

34. reg [5:0]i,Go;

35. reg [10:0]C1,C2;

36. reg [31:0]D1;

37. reg [7:0]D2;

38. reg [10:0]T;

39. reg [7:0]isCall;

40. reg [1:0]isEn;

41. reg rTXD;

42. 

43. always @ ( posedge CLOCK or negedge RESET )

44. if( !RESET )

45. begin

46. { i,Go } <= { 6'd0,6'd0 };

47. { C1,C2 } <= { 11'd0,11'd0 };

48. { D1,D2,T } <= { 32'd0,8'd0,11'd0 };

49. { isCall,isEn } <= { 8'd0,2'd0 };

50. rTXD <= 1'b1;

51. end

52. else


以上内容为相关寄存器声明,复位操作,还有波特率与入口地址的常量声明。

53. case( i )

54. 

55. 0: // cmd0

56. if( DoneU1 ) begin isCall[0] <= 1'b0; i <= i + 1'b1; end

57. else begin isCall[0] <= 1'b1; end

58. 

59. 1:

60. begin T <= { 2'b11, TagU1[7:0], 1'b0 }; i <= TXFUNC; Go <= i + 1'b1; end

61. 

62. /********************/

63. 


步骤0执行CMD0,步骤1输出反馈结果。

64. 2: // cmd8

65. if( DoneU1 ) begin isCall[1] <= 1'b0; i <= i + 1'b1; end

66. else begin isCall[1] <= 1'b1; end

67. 

68. 3:

69. begin T <= { 2'b11, TagU1[39:32], 1'b0 }; i <= TXFUNC; Go <= i + 1'b1; end

70. 

71. 4:

72. begin T <= { 2'b11, TagU1[31:24], 1'b0 }; i <= TXFUNC; Go <= i + 1'b1; end

73. 

74. 5:

75. begin T <= { 2'b11, TagU1[23:16], 1'b0 }; i <= TXFUNC; Go <= i + 1'b1; end

76. 

77. 6:

78. begin T <= { 2'b11, TagU1[15:8], 1'b0 }; i <= TXFUNC; Go <= i + 1'b1; end

79. 

80. 7:

81. begin T <= { 2'b11, TagU1[7:0], 1'b0 }; i <= TXFUNC; Go <= i + 1'b1; end

82. 

83. /********************/

84. 


步骤2执行CMD8,步骤3~7输出反馈结果。

85. 8: // cmd58

86. if( DoneU1 ) begin isCall[2] <= 1'b0; i <= i + 1'b1; end

87. else begin isCall[2] <= 1'b1; end

88. 

89. 9:

90. begin T <= { 2'b11, TagU1[39:32], 1'b0 }; i <= TXFUNC; Go <= i + 1'b1; end

91. 

92. 10:

93. begin T <= { 2'b11, TagU1[31:24], 1'b0 }; i <= TXFUNC; Go <= i + 1'b1; end

94. 

95. 11:

96. begin T <= { 2'b11, TagU1[23:16], 1'b0 }; i <= TXFUNC; Go <= i + 1'b1; end

97. 

98. 12:

99. begin T <= { 2'b11, TagU1[15:8], 1'b0 }; i <= TXFUNC; Go <= i + 1'b1; end

100. 

101. 13:

102. begin T <= { 2'b11, TagU1[7:0], 1'b0 }; i <= TXFUNC; Go <= i + 1'b1; end

103. 

104. /********************/

105. 


步骤8执行CMD58,步骤9~13输出反馈结果。

106. 14: // cmd55 + acmd41

107. if( DoneU1 ) begin isCall[3] <= 1'b0; i <= i + 1'b1; end

108. else begin isCall[3] <= 1'b1; end

109. 

110. 15:

111. begin T <= { 2'b11, TagU1[39:32], 1'b0 }; i <= TXFUNC; Go <= i + 1'b1; end

112. 

113. 16:

114. begin T <= { 2'b11, TagU1[31:24], 1'b0 }; i <= TXFUNC; Go <= i + 1'b1; end

115. 

116. 17:

117. begin T <= { 2'b11, TagU1[23:16], 1'b0 }; i <= TXFUNC; Go <= i + 1'b1; end

118. 

119. 18:

120. begin T <= { 2'b11, TagU1[15:8], 1'b0 }; i <= TXFUNC; Go <= i + 1'b1; end

121. 

122. 19:

123. begin T <= { 2'b11, TagU1[7:0], 1'b0 }; i <= TXFUNC; Go <= i + 1'b1; end

124. 

125. /********/

126. 


步骤14执行CMD55+ACMD41,步骤15~19输出反馈结果,其中步骤17~19的内容纯属花瓶而已。

127. 20: // cmd58

128. if( DoneU1 ) begin isCall[4] <= 1'b0; i <= i + 1'b1; end

129. else begin isCall[4] <= 1'b1; end

130. 

131. 21:

132. begin T <= { 2'b11, TagU1[39:32], 1'b0 }; i <= TXFUNC; Go <= i + 1'b1; end

133. 

134. 22:

135. begin T <= { 2'b11, TagU1[31:24], 1'b0 }; i <= TXFUNC; Go <= i + 1'b1; end

136. 

137. 23:

138. begin T <= { 2'b11, TagU1[23:16], 1'b0 }; i <= TXFUNC; Go <= i + 1'b1; end

139. 

140. 24:

141. begin T <= { 2'b11, TagU1[15:8], 1'b0 }; i <= TXFUNC; Go <= i + 1'b1; end

142. 

143. 25:

144. begin T <= { 2'b11, TagU1[7:0], 1'b0 }; i <= TXFUNC; Go <= i + 1'b1; end

145. 

146. /********************/

147. 


步骤20执行CMD58,步骤21~15输出反馈结果。

148. 26: // cmd16

149. if( DoneU1 ) begin isCall[5] <= 1'b0; i <= i + 1'b1; end

150. else begin isCall[5] <= 1'b1; end

151. 

152. 27:

153. begin T <= { 2'b11, TagU1[7:0], 1'b0 }; i <= TXFUNC; Go <= i + 1'b1; end

154. 

155. /*****************/

156. 


步骤26执行CMD16,步骤27输出反馈结果。

157. 28: // Write Data 00~FF

158. begin isEn[1] <= 1'b1; i <= i + 1'b1; end

159. 

160. 29:

161. begin isEn[1] <= 1'b0; i <= i + 1'b1; end

162. 

163. 30:

164. if( C2 == 511 ) begin C2 <= 11'd0; i <= i + 1'b1; end

165. else begin D2 <= D2 + 1'b1; C2 <= C2 + 1'b1; i <= 6'd28; end

166. 

167. /**************/ 

168. 

169. 31: // cmd24

170. if( DoneU1 ) begin isCall[7] <= 1'b0; i <= i + 1'b1; end

171. else begin isCall[7] <= 1'b1; D1 <= 32'd0; end

172. 

173. 32:

174. begin T <= { 2'b11, TagU1[7:0], 1'b0 }; i <= TXFUNC; Go <= i + 1'b1; end

175. 

176. /***************/

177. 


步骤28~30写入两遍8’h00~8’hFF至FIFO里边,然后步骤31执行CMD24将其写入SD卡里边,步骤32随之输出反馈结果。

178. 33: // cmd17

179. if( DoneU1 ) begin isCall[6] <= 1'b0; i <= i + 1'b1; end

180. else begin isCall[6] <= 1'b1; D1 <= 32'd0; end

181. 

182. 34:

183. begin T <= { 2'b11, TagU1[7:0], 1'b0 }; i <= TXFUNC; Go <= i + 1'b1; end

184. 

185. /****************/

186. 

187. 35: // Read Data 00~FF

188. begin isEn[0] <= 1'b1; i <= i + 1'b1; end

189. 

190. 36:

191. begin isEn[0] <= 1'b0; i <= i + 1'b1; end

192. 

193. 37:

194. begin T <= { 2'b11, DataU1, 1'b0 }; i <= TXFUNC; Go <= i + 1'b1; end

195. 

196. 38:

197. if( C2 == 511 ) begin C2 <= 11'd0; i <= i + 1'b1; end

198. else begin C2 <= C2 + 1'b1; i <= 6'd35; end

199. 


步骤33执行CMD17,步骤34则输出反馈结果。步骤35~38分别从FIFO哪里读出512个字节,并且经由TXD输出。

200. 39: 

201. i <= i;

202. 

203. /****************/

204. 

205. 48,49,50,51,52,53,54,55,56,57,58:

206. if( C1 == B115K2 -1 ) begin C1 <= 11'd0; i <= i + 1'b1; end

207. else begin rTXD <= T[i - 48]; C1 <= C1 + 1'b1; end

208. 

209. 59:

210. i <= Go;

211. 

212. endcase

213. 

214. assign TXD = rTXD;

215. 

216. endmodule


步骤39为发呆。步骤48~59则是发送一帧数据的伪函数。总结完毕,便插入健康的大容量SD卡,如笔者手上 Kingston 制 16GB 的SD卡,然后再下载程序。操作过程如下所示:

A2 // CMD0 执行成功

01 00 00 01 AA // CMD8 执行成功, 字节4为0x01,字节1为0x01,字节0为 0xaa。

01 00 FF 80 00 // CMD58执行成功,字节4为0x01,字节3为0x00

01 00 (FF 80 00) // 0x01表示CMD55执行成功,0x00表示ACMD41执行成功。后边3个字节作废。

00 C0 FF 80 00 // CMD58执行成功,字节4为0x00,字节3为0xC0

A8 // CMD16执行成功

A6 // CMD24 执行成功

AA // CMD17 执行成功

00~FF // 地址0~255的读取数据

00~FF // 地址256~511的读取数据

读者稍微注意一下第二次执行CMD58的反馈结果 ... 其中 8’hC0表示SD卡已经结束忙碌,而且也认识CCS的标示位。

图25.11 SDHC卡,地址0~511的内容。

图25.11是SDHC卡的五脏六腑,地址0x00~0xf0 的数据为 0x00~0xff,地址0x0100~0x01f0的数据也是 0x00~0xff。对此,表示实验已经成功。

细节一:完整的个体模块

实验二十五的SD卡基础模块虽然已经准备就绪,不过它不聪明也不支持版本 SDV1.×的SD卡。此外,SD卡也必须健康无患,不然该基础模块会运行失败。


相关阅读:
Top