驅動SD卡是件容易讓人抓狂的事情,驅動SD卡好比SDRAM執行頁讀寫,SD卡雖然不及SDRAM的麻煩要求(時序參數),但是驅動過程卻有猥瑣操作。除此此外,描述語言只要稍微比較一下C語言,描述語言一定會淚流滿面,因為嵌套循環,嵌套判斷,或者嵌套函數等都是它的痛。.
史萊姆模塊是多模塊建模的通病,意指結構能力非常脆弱的模塊,暴力的嵌套行為往往會擊垮模塊的美麗身軀,好讓脆弱結構更加脆弱還有慘不忍睹,最終搞垮模塊的表達能力。描述語言預想駕馭SD卡,關鍵的地方就是如何提升模塊的結構能力。簡單而言,描述語言如何不失自身的美麗,又用自身的方法,去實現嵌套循環或者嵌套函數等近似的內容呢?
低級建模I之際,論結構能力它確實有點勉強,所以SD卡的實驗才姍姍來遲。如今病貓已經進化為老虎,而且進化之初的新生兒都會肌餓如心焚,理智也不健全。因為如此,低級建模II才會不停舔着嘴唇,然后渴望新生的第一祭品。遇見SD卡,它仿佛遇見美味的獵物,口水都下流到一塌糊塗。
諸位少年少女們,讓我們一起歡呼活祭儀式的開始吧!
二十一世紀的今天,SD卡演化的速度簡直迅雷不及掩耳,如今SD卡已經逐漸突破64GB大關。對此,SD卡也存在N多版本,如版本SDV1.×,版本SDV2,或者SDHCV2等,當然未來還會繼續演化下去。所謂版本是指建造工藝還有協議,粗略而言,版本SDV1.×是指容量為2GB以下的SD卡,版本SDV2則指容量為2GB~4GB之間的SD卡,版本SDHCV2則是容量為4GB以上的SD卡。
話雖如此,不過實際情況還要根據各個廠商的心情而定,有些廠商的SD卡雖為4GB,但是版本卻是SDV1.×,還有廠商的SD卡的雖為 2GB,不過版本卻是SDV2,情況盡是讓人哭笑不得。此外,版本不會印刷在硬件的表面上,而且不同版本也有不同驅動方法。俗語有雲,擒賊先擒卒——凡事從娃娃抓起,所以筆者遵循偉大的智慧,從版本SDV1.×開始動手。
圖24.1 SPI模式。
SD卡有SDIO還有SPI兩種模式,后者簡單又省事,所以SPI模式都是眾多懶惰鬼的喜愛。SPI模式一般只用4只引腳,而且主機(FPGA)與從機(SD卡)之間的鏈接如圖24.1所示,至於引腳的聶榮如表24.1所示:
表24.1 SD卡SPI模式的引腳說明。
引腳 |
說明 |
SD_CLK |
串行時鍾,閑置為高 |
SD_NCS |
片選,閑置為高,拉低有效 |
SD_DI |
數據輸入,也是主機輸出 |
SD_DOUT |
數據輸出,也是主機輸入 |
雖然DS1302也有SPI,但是數據線是雙向IO,反之SD卡則是一對出入的數據線。話雖如此,它們兩者都有乖乖遵守SPI的傳輸協議,即下降沿設置數據,上升沿鎖存數據。
圖24.2 寫一個字節(主機視角)。
圖24.2是主機視角寫一個字節的理想時序。主機會利用時鍾的下降沿,由高至低發送一個字節的數據。
圖24.3 讀一個字節(主機視角)。
圖24.2是主機視角讀一個字節的理想時序。從機會利用時鍾的下降沿,由高至低發送一個字節的數據,主機則會利用時鍾信號的上升沿,由高至低讀取一個字節的數據。
圖24.4 同時讀寫一個字節(主機視角)。
我們知道SD卡有一對讀寫的數據線,為了節省時間,數據讀寫是同時發生的。如圖24.4所示,那是主機在同時讀寫的理想時序,讀者可以看成是圖24.2 還有圖24.3的結合體。
對此,Verilog可以這樣描述,結果如代碼24.1所示:
1. case(i)
2.
3. 0,1,2,3,4,5,6,7:
4. begin
5. rDI <= iData[ 7-i ];
6.
7. if( C1 == 0 ) rSCLK <= 1'b0;
8. else if( C1 == isHalf ) rSCLK <= 1'b1;
9.
10. if( C1 == isQuarter ) D1[ 7-i ] <= SD_DOUT;
11.
12. if( C1 == isFull -1 ) begin C1 <= 10'd0; i <= i + 1'b1; end
13. else begin C1 <= C1 + 1'b1; end
14. end
代碼24.1
如代碼24.1所示,第12~13行表示步驟逗留的時間,其中isFull表示一個時鍾周期。第7~8行表示,C1為0拉低時鍾,C1為半個周期則拉高時鍾。第5行表示,任何時候都更新數據,也可以看成C1為0輸出數據。第10行表示,C1為四分之一周期鎖存數據。
第3行表示,步驟0~7造就一個字節的讀寫。還有第5~10行的 D1[7-i] 表示,讀寫數據由高至低。
好奇的朋友一定會疑惑道,為何第10行的鎖存行為不是時鍾的半周期(上升沿),而是四分之一呢?原因很單純,因為數據在這個時候最為有效。
圖24.5 寫命令(主機視角)。
當然,SD卡不是給足兩只骨頭就會滿足的哈士奇 ... 為此,除了單純的讀寫數據意外,SD卡還有所謂的寫命令,而寫命令則是讀寫字節的復合體。如圖24.5所示,那是主機寫命令的理想時序,主機先由高至低發送6個字節的命令。SD卡接受完畢以后,便會反饋一個字節的數據。期間,片選信號必須處於拉低狀態。對此,Verilog可以這樣表示,結果如代碼24.2所示:
1. case( i )
2.
3. 0:
4. begin rCMD <= iAddr; i <= i + 1'b1; end
5.
6. 1,2,3,4,5,6:
7. begin T <= rCMD[47:40]; rCMD <= rCMD << 8; i <= FF_Write; Go <= i + 1'b1; end
8.
9. 7:
10. begin i <= FF_Read; Go <= i + 1'b1; end
11.
12. 8:
13. if( C2 == 100 ) begin C2 <= 10'd0; i <= i + 1'b1; end
14. else if( D1 != 8'hff ) begin C2 <= 10'd0; i <= i + 1'b1; end
15. else begin C2 <= C2 + 1'b1; i <= FF_Read; Go <= i; end
16.
17. ...
18.
19. 12,13,14,15,16,17,18,19:
20. begin
21. rDI <= T[ 19-i ];
22. if( C1 == 0 ) rSCLK <= 1'b0;
23. else if( C1 == isHalf ) rSCLK <= 1'b1;
24.
25. if( C1 == isQuarter ) D1[ 19-i ] <= SD_DOUT;
26.
27. if( C1 == isFull -1 ) begin C1 <= 10'd0; i <= i + 1'b1; end
28. else begin C1 <= C1 + 1'b1; end
29. end
30.
31. 20:
32. begin i <= Go; end
代碼24.2
步驟12~20是讀寫一個字節的偽函數,步驟0准備6個字節的命令,步驟1~6由高至低發送命令,並且進入偽函數。步驟7進入偽函數,並且讀取一個字節的反饋數據(注意FF_Write與FF_Read都指向步驟12)。反饋數據一般都是 8’hff 以外的結果,如果不是則重復讀取反饋數據100次,如果SD卡反應正常,都會在這100次以內反饋 8’hff以外的結果。
簡單而言,如何驅動SD卡就是如何使用相關的命令。版本SDV1.×的SD卡只需4個命令而已,亦即:
(一)CMD0,復位命令;
(二)CMD1,初始化命令;
(三)CMD24,寫命令;
(四)CMD17,讀命令。
CMD0用來復位SD卡,好讓SD卡處於(IDLE)待機狀態。CMD1用來初始化SD卡,好讓SD卡處於(Transfer)傳輸狀態。CMD24將512字節數據寫入指定的地址,CMD17則將512字節數據從指定的地址讀出來。
圖24.6 CMD0的理想時序圖。
圖24.6是CMD0的理想時序圖,首先在T1延遲1ms給予SD卡熱身時間,然后再在T2給予80個准備的時鍾。T3之際拉低CS,T4之際則發送命令CMD0 { 8’h40, 32’d0, 8’h95},然后等待SD卡反饋數據R1。如果SD卡成功接收命令CMD0,內容則是8’h01。T5之際拉高CS,T6之際再8個結束時鍾。對此,Verilog可以這樣描述,結果如代碼24.3所示:
1. case( i )
2.
3. 0: // Disable cs, prepare Cmd0
4. begin rCS <= 1'b1; D4 <= {8'h40, 32'd0, 8'h95}; i <= i + 1'b1; end
5.
6. 1: // Wait 1MS for warm up;
7. if( C1 == T1MS -1) begin C1 <= 16'd0; i <= i + 1'b1; end
8. else begin C1 <= C1 + 1'b1; end
9.
10. 2: // Send 80 free clock
11. if( C1 == 10'd10 ) begin C1 <= 16'd0; i <= i + 1'b1; end
12. else if( iDone ) begin isCall[0] <= 1'b0; C1 <= C1 + 1'b1; end
13. else begin isCall[0] <= 1'b1; D1 <= 8'hff; end
14.
15. 3: // Enable cs
16. begin rCS <= 1'b0; i <= i + 1'b1; end
17.
18. 4: // Try 200 time, ready error code.
19. if( C1 == 10'd200 ) begin D2 <= CMD0ERR; C1 <= 16'd0; i <= 4'd8; 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: // Disable cs
25. begin rCS <= 1'b1 ; i <= i + 1'b1; end
26.
27. 6: // Send free clock
28. if( iDone ) begin isCall[0] <= 1'b0; i <= i + 1'b1; end
29. else begin isCall[0] <= 1'b1; D1 <= 8'hff; end
30.
31. 7: // Disable cs, ready OK code
32. begin D2 <= CMD0OK; i <= i + 1'b1; end //;
33.
34. 8: // Disbale cs, generate done signal
35. begin rCS <= 1'b1; isDone <= 1'b1; i <= i + 1'b1; end
36.
37. 9:
38. begin isDone <= 1'b0; i <= 4'd0; end
代碼24.3
我們先假設 isCall[1]執行寫命令,isCall[0]則是執行讀寫字節。如代碼24.3所示,步驟0用來准備CMD0命令。步驟1延遲1ms。步驟2執行10次無意義的讀寫,以示給予80個准備時鍾。在此讀者稍微注意一下第12行,每當完成一次讀寫C1便會遞增一下,C1遞增10次便表示讀寫執行10次。
步驟3拉低CS,並且步驟4發送命令。步驟4可能會嚇壞一群小朋友,不過只要耐心解讀,其它它並不可怕。首先執行第22行的寫命令,如果反饋數據不為8’h01(第20行),消除isDo便遞增C1,然后再返回第22行。如果反饋數據為 8’h01(第21行)
,消除isDo與C1然后繼續步驟。如果重復執行100次都失敗,D2賦值CMD0的失敗信息,消除C1並且i直接步驟8。
步驟5拉低CS,步驟6則給予8個結束時鍾。步驟7為D2賦值CMD0的成功信息,步驟8~9拉高CS並且產生完成信號。
圖24.7 CMD1的理想時序圖。
圖24.7是CMD1的理想時序圖,T0&T1之際拉低CS並且發送六個字節的命令CMD1 {8’h41,32’d0,8’hff}。SD卡接受命令以后便反饋數據R1——8’h00。T2&T3之際拉高CS並且給予8個結束時鍾。Verilog的描述結果如代碼24.4所示:
1. case( i )
2.
3. 0: // Enable cs, prepare Cmd1
4. begin rCS <= 1'b0; D4 <= { 8'h41,32'd0,8'hff }; i <= i + 1'b1; end
5.
6. 1: // Try 100 times, ready error code.
7. if( C1 == 10'd100 ) begin D2 <= CMD1ERR; C1 <= 16'd0; i <= 4'd5; end
8. else if( iDone && iData != 8'h00) begin isCall[1]<= 1'b0; C1 <= C1 + 1'b1; end
9. else if( iDone && iData == 8'h00 ) begin isCall[1] <= 1'b0; C1 <= 16'd0; i <= i + 1'b1; end
10. else isCall[1] <= 1'b1;
11.
12. 2: // Disable cs
13. begin rCS <= 1'b1; i <= i + 1'b1; end
14.
15. 3: // Send free clock
16. if( iDone ) begin isCall[0] <= 1'b0; i <= i + 1'b1; end
17. else begin isCall[0] <= 1'b1; D1 <= 8'hff; end
18.
19. 4: // Disable cs, ready OK code.
20. begin D2 <= CMD1OK; i <= i + 1'b1; end
21.
22. 5: // Disable cs, generate done signal
23. begin rCS <= 1'b1; isDone <= 1'b1; i <= i + 1'b1; end
24.
25. 6:
26. begin isDone <= 1'b0; i <= 4'd0; end
代碼24.4
如代碼24.4所示,步驟0准備命令CMD1。步驟1重復發送CMD1命令100次,直至反饋數據R1為8’h00為止,否則反饋錯誤信息。步驟2拉高CS,步驟3則給予結束時鍾。步驟4反饋成功信息,步驟5~6拉高CS之余也產生完成信號。
好奇的同學一定會覺得疑惑,命令CMD0與命令CMD1同樣反饋數據R1,為何前者是8’h01,后者則是8’h00呢?事實上,R1的內容也反應SD卡的當前狀態,SD卡有待機狀態(IDLE)還有傳輸狀態(Transfer)等兩個常見狀態。
圖24.8 版本V1.×的初始化流程圖。
如圖24.8所示,那是版本V1.x的初始化流程圖。主機先發送CMD0,SD卡接收以后如果反饋R1為8’h01便繼續流程,否則重復發送CMD0。主機接着發送CMD1,如果SD卡接收並且反饋R1為8’h00,該結果表示SD卡以從待機狀態進入傳輸狀態,余下CMD24還有CMD17才有效。
圖24.9 CMD24的理想時序圖。
圖24.9是CMD24的理想時序圖。T0~1之際,主機拉低CS之余,主機也向SD卡發送寫命令CMD24,其中Addr 3~Addr 0是寫入地址。SD卡接收以后便以反饋數據8’h00表示接收成功。保險起見,主機在T2給足800個准備時鍾,如果讀者嫌准備時鍾給太多,讀者可以自行縮小至80。T3之際,主機發送8’hfe以示寫512字節開始,T4~T7則是寫512字節的過程。T8~T9分別寫入兩個CRC字節(CRC校驗)。
完后,SD卡便會反饋8’h05以示寫512字節成功,此刻(T10~11)主機讀取並且檢測。事后直至SD卡發送8’hff為止,SD卡都處於忙狀態。換言之,如果主機在T12成功讀取8’hff,結果表示SD卡已經忙完了。T13之際,主機再拉高CS。對此,Verilog可以這樣描述,結果如代碼24.5所示:
1. case(i)
2.
3. 0: // Enable cs, prepare cmd24
4. begin rCS <= 1'b0; D4 = { 8'h58, iAddr, 9'd0, 8'hFF }; i <= i + 1'b1; end
5.
6. 1: // Try 100 times, ready error code.
7. if( C1 == 100 ) begin D2 <= CMD24ERR; C1 <= 16'd0; i <= 4'd14; end
8. else if( iDone && iData != 8'h00) begin isCall[1] <= 1'b0; C1 <= C1 + 1'b1; end
9. else if( iDone && iData == 8'h00 ) begin isCall[1] <= 1'b0; C1 <= 16'd0; i <= i + 1'b1; end
10. else isCall[1] <= 1'b1;
11.
12. 2: // Send 800 free clock
13. if( C1 == 100 ) begin C1 <= 16'd0; i <= i + 1'b1; end
14. else if( iDone ) begin isCall[0] <= 1'b0; C1 <= C1 + 1'b1; end
15. else begin isCall[0] <= 1'b1; D1 <= 8'hFF; end
16.
17. 3: // Send Call byte 0xfe
18. if( iDone ) begin isCall[0] <= 1'b0; i <= i + 1'b1; end
19. else begin isCall[0] <= 1'b1; D1 <= 8'hFE; end
20.
21. 4: // Pull up read req.
22. begin isEn[0] <= 1'b1; i <= i + 1'b1; end
23.
24. 5: // Pull down read req.
25. begin isEn[0] <= 1'b0; i <= i + 1'b1; end
26.
27. 6: // Write byte from fifo
28. if( iDone ) begin isCall[0] <= 1'b0; i <= i + 1'b1; end
29. else begin isCall[0] <= 1'b1; D1 <= iDataFF; end
30.
31. 7: // Repeat 512 times
32. if( C1 == 10'd511 ) begin C1 <= 16'd0; i <= i + 1'b1; end
33. else begin C1 <= C1 + 1'b1; i <= 4'd4; end
34.
35. 8: // Write 1st CRC
36. if( iDone ) begin isCall[0] <= 1'b0; i <= i + 1'b1; end
37. else begin isCall[0] <= 1'b1; D1 <= 8'hff; end
38.
39. 9: // Write 2nd CRC
40. if( iDone ) begin isCall[0] <= 1'b0; i <= i + 1'b1; end
41. else begin isCall[0] <= 1'b1; D1 <= 8'hff; end
42.
43. 10: // Read Respond
44. if( iDone ) begin isCall[0] <= 1'b0; i <= i + 1'b1; end
45. else begin isCall[0] <= 1'b1; end
46.
47. 11: // if not 8'h05, faild and ready error code
48. if( (iData & 8'h1F) != 8'h05 ) begin D2 <= CMD24ERR; i <= 4'd14; end
49. else i <= i + 1'b1;
50.
51. 12: // Wait unitl sdcard free
52. if( iDone && iData == 8'hff ) begin isCall[0] <= 1'b0; i <= i + 1'b1; end
53. else if( iDone ) begin isCall[0] <= 1'b0; end
54. else begin isCall[0] <= 1'b1; end
55.
56. 13: // Disable cs, ready OK code;
57. begin D2 <= CMD24OK; i <= i + 1'b1; end
58.
59. 14: // Disable cs, generate done signal
60. begin rCS <= 1'b1; isDone <= 1'b1; i <= i + 1'b1; end
61.
62. 15:
63. begin isDone <= 1'b0; i <= 4'd0; end
代碼24.5
步驟0拉低CS之余,它也准備寫命令CMD24,其中 { iAddr, 9’d0 } 表示地址有512偏移量,內容等價 iAddr << 9。步驟1嘗試寫命令100次,直至反饋內容為8’h00,否則便准備錯誤信息。步驟2發送800個准備時鍾,如果嫌多可以自行縮小。步驟3寫入開始字節 8’hfe。步驟4~5主要是從FIFO取得數據,步驟6則是將數據寫入SD卡,步驟7用來控制循環的次數。步驟8~9分別寫入兩個CRC字節,內容隨意。
步驟10讀取反饋數據,步驟11則檢測反饋數據是否為8’h05,是則繼續,不是則准備錯誤信息,並且跳轉步驟14(結束操作)。步驟12不斷讀取數據,直至讀取內容為8’hff位置。步驟13准備成功信息。步驟14~15拉高CS之余也產生完成信號。在此,讀者要稍微注意一下,步驟4~7組合起來,類似先執行后判斷的 do ... while 循環。
圖24.10 CMD17的理想時序圖。
圖24.10是CMD17的理想時序圖。T0~T1之際,主機先拉低CS再發送命令CMD17,其中Addr3~Addr0是指定的讀地址,事后SD卡便會反饋數據8’h00以示接收成功。T2之際,主機會不斷讀取數據,如果讀取內容是8’hfe,結果表示SD卡已經准備發送512字節數據。T3~T6之際,主機不斷從SD卡那里讀取512個字節的數據。T7~T8之際,主機讀取兩個字節的CRC,然后在T9~10拉高CS之余也給足8個結束時鍾。
換之,Verilog的描述結果如代碼24.6所示:
1. case( i )
2.
3. 0: // Enable cs, prepare cmd17
4. begin rCS <= 1'b0; D4 <= { 8'h51, iAddr, 9'd0, 8'hFF }; i <= i + 1'b1; end
5.
6. 1: // Try 100 times, ready error code
7. if( C1 == 100 ) begin D2 <= CMD17ERR; C1 <= 16'd0; i <= 4'd12; end
8. else if( iDone && iData != 8'h00 ) begin isCall[1] <= 1'b0; C1 <= C1 + 1'b1; end
9. else if( iDone && iData == 8'h00 ) begin isCall[1] <= 1'b0; C1 <= 16'd0; i <= i + 1'b1; end
10. else isCall[1] <= 1'b1;
11.
12. 2: // Wait read ready
13. if( iDone && iData == 8'hfe ) begin isCall[0] <= 1'b0; i <= i + 1'b1; end
14. else if( iDone && iData != 8'hfe ) begin isCall[0] <= 1'b0; end
15. else isCall[0] <= 1'b1;
16.
17. 3: // Read byte
18. if( iDone ) begin D3 <= iData; isCall[0] <= 1'b0; i <= i + 1'b1; end
19. else begin isCall[0] <= 1'b1; end
20.
21. 4: // Pull up write req.
22. begin isEn[1] <= 1'b1; i <= i + 1'b1; end
23.
24. 5: // Pull down write req.
25. begin isEn[1] <= 1'b0; i <= i + 1'b1; end
26.
27. 6: // Repeat 512 times
28. if( C1 == 10'd511 ) begin C1 <= 16'd0; i <= i + 1'b1; end
29. else begin C1 <= C1 + 1'b1; i <= 4'd3; end
30.
31. 7,8: // Read 1st and 2nd byte CRC
32. if( iDone ) begin D3 <= iData; isCall[0] <= 1'b0; i <= i + 1'b1; end
33. else isCall[0] <= 1'b1;
34.
35. 9: // Disable cs
36. begin rCS <= 1'b1; i <= i + 1'b1; end
37.
38. 10: // Send free clock
39. if( iDone ) begin isCall[0] <= 1'b0; i <= i + 1'b1; end
40. else begin isCall[0] <= 1'b1; D1 <= 8'hFF; end
41.
42. 11: // Ready OK code
43. begin D2 <= CMD17OK; i <= i + 1'b1; end
44.
45. 12: // Disable cs, generate done signal
46. begin rCS <= 1'b1; isDone <= 1'b1; i <= i + 1'b1; end
47.
48. 13:
49. begin isDone <= 1'b0; i <= 4'd0; end
代碼24.6
步驟0拉低CS之余也准備命令CMD17,其中 {iAddr,9’d0} 為 512的偏移量。步驟1重復100次寫命令,直至SD卡反饋8’h00,否則准備錯誤信息,然后跳轉步驟12(結束操作)。步驟2不斷讀取數據,直至讀取內容為8’hfe為止。步驟3讀取數據,步驟4~5則將數據寫入FIFO,步驟6用來控制循環。步驟7~8讀取兩個字節CRC。步驟9拉高CS,步驟10則給足8個結束時鍾。步驟11准備成功信息。步驟12拉高CS之余,也產生完成信號。
上述內容理解完畢以后,我們便可以開始建模了。
圖24.11 SD卡基礎模塊的建模圖。
圖24.11是SD卡基礎模塊的建模圖,其中內容包括SD卡控制模塊,SD卡功能模塊,還有兩個fifo儲存模塊。SD卡功能模塊的溝通信號Call/Done有兩位位寬,其中[1]為寫命令,[0]為字節讀寫。SD卡控制模塊的Call/Done位寬有四,表示它支持4個命令,其中[3]為CMD24,[2]為CMD17,[1]為CMD1,[0]為CMD0。兩只FIFO儲存模塊充當寫緩存(上)還有讀緩存(下),它們被外界調用以外,它們也被SD卡控制模塊調用。
sdcard_funcmod.v
圖24.12 SD卡功能模塊的建模圖。
圖24.12是SD卡功能模塊的建模圖,右邊是驅動SD卡的頂層信號,左邊則是溝通用,還有命令,iData與oData等數據信號。Call/Done位寬有兩,其中[1]為寫命令,[0]為讀寫數據。
圖24.13 不同狀態之間的傳輸速率。
話題繼續之前,請允許筆者作足一些小補充。如圖24.13所示,待機狀態SD卡為低速狀態,速率推薦為100Khz~500Khz之間。保險起見,筆者取為100Khz。反之,傳輸狀態SD卡處於高速狀態,速率推薦為2Mhz或者以上。筆者衡量各種因數以后,筆者決定選擇10Mhz。喪心病狂的讀者當然可以選擇10Mhz以上的速率,如果硬件允許的話 ... 據說,100Mhz也沒有問題。
1. module sdcard_funcmod
2. (
3. input CLOCK, RESET,
4. input SD_DOUT,
5. output SD_CLK,
6. output SD_DI,
7.
8. input [1:0]iCall,
9. output oDone,
10. input [47:0]iAddr,
11. input [7:0]iData,
12. output [7:0]oData
13. );
以上內容為出入端聲明。
14. parameter FLCLK = 10'd500, FLHALF = 10'd250, FLQUARTER = 10'd125; //T20US = 100Khz
15. parameter FHCLK = 10'd5, FHHALF = 10'd1, FHQUARTER = 10'd2; // T1us = 10Mhz
16.
17. reg [9:0]isFull,isHalf,isQuarter;
18.
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( iAddr[47:40] == 8'h41 && isDone )
27. begin
28. isFull <= FHCLK;
29. isHalf <= FHHALF;
30. isQuarter <= FHQUARTER;
31. end
32.
第14~15行是100Khz還有10Mhz的常量聲明,F×CLK為一個周期,F×HALF為半個周期,F×QUARTER為四分之一周期,其中×為L表示低速,×為H表示高速。第17~31行是設置速率的周邊操作,起始下為低速(第22~24)。不過,當命令CMD1執行成功以后,速率轉為為高速(第26~31行)。
33. parameter FF_Write = 5'd12, FF_Read = 5'd12;
34.
35. reg [5:0]i,Go;
36. reg [9:0]C1,C2;
37. reg [7:0]T,D1;
38. reg [47:0]rCMD;
39. reg rSCLK,rDI;
40. reg isDone;
41.
42. always @ ( posedge CLOCK or negedge RESET )
43. if( !RESET )
44. begin
45. { i,Go } <= { 6'd0,6'd0};
46. { C1,C2 } <= { 10'd0, 10'd0 };
47. { T,D1 } <= { 8'd0,8'd0 };
48. rCMD <= 48'd0;
49. { rSCLK,rDI } <= 2'b11;
50. isDone <= 1'b0;
51. end
以上內容為相關的寄存器聲明還有復位操作。其中第33行是偽函數的入口地址。
52. else if( iCall[1] )
53. case( i )
54.
55. 0:
56. begin rCMD <= iAddr; i <= i + 1'b1; end
57.
58. 1,2,3,4,5,6:
59. begin T <= rCMD[47:40]; rCMD <= rCMD << 8; i <= FF_Write; Go <= i + 1'b1; end
60.
61. 7:
62. begin i <= FF_Read; Go <= i + 1'b1; end
63.
64. 8:
65. if( C2 == 100 ) begin C2 <= 10'd0; i <= i + 1'b1; end
66. else if( D1 != 8'hff ) begin C2 <= 10'd0; i <= i + 1'b1; end
67. else begin C2 <= C2 + 1'b1; i <= FF_Read; Go <= i; end
68.
69. 9:
70. begin isDone <= 1'b1; i <= i + 1'b1; end
71.
72. 10:
73. begin isDone <= 1'b0; i <= 6'd0; end
74.
75. /******************************/
76.
77. 12,13,14,15,16,17,18,19:
78. begin
79. rDI <= T[ 19-i ];
80.
81. if( C1 == 0 ) rSCLK <= 1'b0;
82. else if( C1 == isHalf ) rSCLK <= 1'b1;
83.
84. if( C1 == isQuarter ) D1[ 19-i ] <= SD_DOUT;
85.
86. if( C1 == isFull -1 ) begin C1 <= 10'd0; i <= i + 1'b1; end
87. else begin C1 <= C1 + 1'b1; end
88. end
89.
90. 20:
91. begin i <= Go; end
92.
93. endcase
以上內容為寫命令。
94. else if( iCall[0] )
95. case( i )
96.
97. 0,1,2,3,4,5,6,7:
98. begin
99. rDI <= iData[ 7-i ];
100.
101. if( C1 == 0 ) rSCLK <= 1'b0;
102. else if( C1 == isHalf ) rSCLK <= 1'b1;
103.
104. if( C1 == isQuarter ) D1[ 7-i ] <= SD_DOUT;
105.
106. if( C1 == isFull -1 ) begin C1 <= 10'd0; i <= i + 1'b1; end
107. else begin C1 <= C1 + 1'b1; end
108. end
109.
110. 8:
111. begin isDone <= 1'b1; i <= i + 1'b1; end
112.
113. 9:
114. begin isDone <= 1'b0; i <= 6'd0; end
115.
116. endcase
117.
以上內容為讀寫一個字節。
118. assign SD_CLK = rSCLK;
119. assign SD_DI = rDI;
120. assign oDone = isDone;
121. assign oData = D1;
122.
123. endmodule
以上內容為相關的輸出驅動。
fifo_savemod.v
圖24.14 FIFO儲存模塊的建模圖。
圖24.14是大伙看爛的FIFO儲存模塊,具體內容讓我們來看代碼吧。
1. module fifo_savemod
2. (
3. input CLOCK, RESET,
4. input [1:0]iEn,
5. input [7:0]iData,
6. output [7:0]oData,
7. output [1:0]oTag
8. );
9. initial begin
10. for( C1 = 0; C1 < 1024; C1 = C1 + 1'b1 )
11. begin RAM[ C1 ] <= 8'd0; end
12. end
13.
14. reg [7:0] RAM [1023:0];
15. reg [10:0] C1 = 11'd0,C2 = 11'd0; // N+1
16. reg [7:0]D1;
17.
18. always @ ( posedge CLOCK or negedge RESET )
19. if( !RESET )
20. begin
21. C1 <= 11'd0;
22. end
23. else if( iEn[1] )
24. begin
25. RAM[ C1[9:0] ] <= iData;
26. C1 <= C1 + 1'b1;
27. end
28.
29. always @ ( posedge CLOCK or negedge RESET )
30. if( !RESET )
31. begin
32. C2 <= 11'd0;
33. D1 <= 8'd0;
34. end
35. else if( iEn[0] )
36. begin
37. D1 <= RAM[ C2[9:0] ];
38. C2 <= C2 + 1'b1;
39. end
40.
41. assign oData = D1;
42. assign oTag[1] = ( C1[10]^C2[10] & C1[9:0] == C2[9:0] ); // Full Left
43. assign oTag[0] = ( C1 == C2 ); // Empty Right
44.
45. endmodule
由於數據緩沖對象不是SDRAM,所以第41行的oData由D1驅動而不是RAM直接驅動。余下內容,讀者自己看着辦吧。
sdcard_ctrlmod.v
圖24.15 SD卡控制模塊的建模圖。
圖24.15是SD卡控制模塊的建模圖,它好比一只刺蝟,全身上下都長滿箭頭,讓人看見也怕怕。右邊是調用功能模塊的信號群,上下則是調用儲存模塊的信號群。左邊則是被外界調用的信號群,其中頂層信號SD_NCS是SD卡的片選信號。此外,Call/Done位寬有4,表示該模塊支持4個命令,[3]為CMD24, [2]為CMD17, [1]為CMD1,[0]為CMD0。至於oTag則是用來反饋命令的執行狀態。
1. module sdcard_ctrlmod
2. (
3. input CLOCK, RESET,
4. output SD_NCS,
5.
6. input [3:0]iCall,
7. output oDone,
8. input [22:0]iAddr,
9. output [7: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. );
以上內容為相關的出入端聲明,第6~9行是外界調用的信號,第11~13行是調用FIFO的信號,第15~19則是調用功能模塊的信號。
21. parameter CMD0ERR = 8'hA1, CMD0OK = 8'hA2, CMD1ERR = 8'hA3, CMD1OK = 8'hA4;
22. parameter CMD24ERR = 8'hA5, CMD24OK = 8'hA6, CMD17ERR = 8'hA7, CMD17OK = 8'hA8;
23. parameter T1MS = 16'd10;
24.
以上內容為各個命令的成功信息還有失敗信息之間的常量聲明。
25. reg [3:0]i;
26. reg [15:0]C1;
27. reg [7:0]D1,D2,D3; // D1 WrData, D2 FbData, D3 RdData
28. reg [47:0]D4; // D4 Cmd
29. reg [1:0]isCall,isEn;
30. reg rCS;
31. reg isDone;
32.
33. always @ ( posedge CLOCK or negedge RESET )
34. if( !RESET )
35. begin
36. i <= 4'd0;
37. C1 <= 16'd0;
38. { D1,D2,D3 } <= { 8'd0, 8'd0, 8'd0 };
39. D4 <= 48'd0;
40. { isCall, isEn } <= { 2'd0,2'd0 };
41. rCS <= 1'b1;
42. end
以上內容為相關的寄存器聲明還有復位操作,其中D1暫存寫數據,D2暫存反饋信息,D3暫存讀數據,D4暫存命令,isDo控制寫命令還有字節讀寫,isEn控制FIFO的讀寫。
所有寄存器的復位值為0,rCS除外。
43. else if( iCall[3] ) // cmd24
44. case( i )
45.
46. 0: // Enable cs, prepare cmd24
47. begin rCS <= 1'b0; D4 = { 8'h58, iAddr, 9'd0, 8'hFF }; i <= i + 1'b1; end
48.
49. 1: // Try 100 times, ready error code.
50. if( C1 == 100 ) begin D2 <= CMD24ERR; C1 <= 16'd0; i <= 4'd14; end
51. else if( iDone && iData != 8'h00) begin isCall[1] <= 1'b0; C1 <= C1 + 1'b1; end
52. else if( iDone && iData == 8'h00 ) begin isCall[1] <= 1'b0; C1 <= 16'd0; i <= i + 1'b1; end
53. else isCall[1] <= 1'b1;
54.
55. 2: // Send 800 free clock
56. if( C1 == 100 ) begin C1 <= 16'd0; i <= i + 1'b1; end
57. else if( iDone ) begin isCall[0] <= 1'b0; C1 <= C1 + 1'b1; end
58. else begin isCall[0] <= 1'b1; D1 <= 8'hFF; end
59.
60. 3: // Send Call byte 0xfe
61. if( iDone ) begin isCall[0] <= 1'b0; i <= i + 1'b1; end
62. else begin isCall[0] <= 1'b1; D1 <= 8'hFE; end
63.
64. /*****************/
65.
66. 4: // Pull up read req.
67. begin isEn[0] <= 1'b1; i <= i + 1'b1; end
68.
69. 5: // Pull down read req.
70. begin isEn[0] <= 1'b0; i <= i + 1'b1; end
71.
72. 6: // Write byte from fifo
73. if( iDone ) begin isCall[0] <= 1'b0; i <= i + 1'b1; end
74. else begin isCall[0] <= 1'b1; D1 <= iDataFF; end
75.
76. 7: // Repeat 512 times
77. if( C1 == 10'd511 ) begin C1 <= 16'd0; i <= i + 1'b1; end
78. else begin C1 <= C1 + 1'b1; i <= 4'd4; end
79.
80. /*****************/
81.
82. 8: // Write 1st CRC
83. if( iDone ) begin isCall[0] <= 1'b0; i <= i + 1'b1; end
84. else begin isCall[0] <= 1'b1; D1 <= 8'hff; end
85.
86. 9: // Write 2nd CRC
87. if( iDone ) begin isCall[0] <= 1'b0; i <= i + 1'b1; end
88. else begin isCall[0] <= 1'b1; D1 <= 8'hff; end
89.
90. 10: // Read Respond
91. if( iDone ) begin isCall[0] <= 1'b0; i <= i + 1'b1; end
92. else begin isCall[0] <= 1'b1; end
93.
94. 11: // if not 8'h05, faild and ready error code
95. if( (iData & 8'h1F) != 8'h05 ) begin D2 <= CMD24ERR; i <= 4'd14; end
96. else i <= i + 1'b1;
97.
98. 12: // Wait unitl sdcard free
99. if( iDone && iData == 8'hff ) begin isCall[0] <= 1'b0; i <= i + 1'b1; end
100. else if( iDone ) begin isCall[0] <= 1'b0; end
101. else begin isCall[0] <= 1'b1; end
102.
103. /*****************/
104.
105. 13: // Disable cs, ready OK code;
106. begin D2 <= CMD24OK; i <= i + 1'b1; end
107.
108. 14: // Disable cs, generate done signal
109. begin rCS <= 1'b1; isDone <= 1'b1; i <= i + 1'b1; end
110.
111. 15:
112. begin isDone <= 1'b0; i <= 4'd0; end
113.
114. endcase
以上內容為命令CMD24。
115. else if( iCall[2] ) // cmd17
116. case( i )
117.
118. 0: // Enable cs, prepare cmd17
119. begin rCS <= 1'b0; D4 <= { 8'h51, iAddr, 9'd0, 8'hFF }; i <= i + 1'b1; end
120.
121. 1: // Try 100 times, ready error code
122. if( C1 == 100 ) begin D2 <= CMD17ERR; C1 <= 16'd0; i <= 4'd12; end
123. else if( iDone && iData != 8'h00 ) begin isCall[1] <= 1'b0; C1 <= C1 + 1'b1; end
124. else if( iDone && iData == 8'h00 ) begin isCall[1] <= 1'b0; C1 <= 16'd0; i <= i + 1'b1; end
125. else isCall[1] <= 1'b1;
126.
127. 2: // Wait read ready
128. if( iDone && iData == 8'hfe ) begin isCall[0] <= 1'b0; i <= i + 1'b1; end
129. else if( iDone && iData != 8'hfe ) begin isCall[0] <= 1'b0; end
130. else isCall[0] <= 1'b1;
131.
132. /********/
133.
134. 3: // Read byte
135. if( iDone ) begin D3 <= iData; isCall[0] <= 1'b0; i <= i + 1'b1; end
136. else begin isCall[0] <= 1'b1; end
137.
138. 4: // Pull up write req.
139. begin isEn[1] <= 1'b1; i <= i + 1'b1; end
140.
141. 5: // Pull down write req.
142. begin isEn[1] <= 1'b0; i <= i + 1'b1; end
143.
144. 6: // Repeat 512 times
145. if( C1 == 10'd511 ) begin C1 <= 16'd0; i <= i + 1'b1; end
146. else begin C1 <= C1 + 1'b1; i <= 4'd3; end
147.
148. /********/
149.
150. 7,8: // Read 1st and 2nd byte CRC
151. if( iDone ) begin D3 <= iData; isCall[0] <= 1'b0; i <= i + 1'b1; end
152. else isCall[0] <= 1'b1;
153.
154. 9: // Disable cs
155. begin rCS <= 1'b1; i <= i + 1'b1; end
156.
157. 10: // Send free clock
158. if( iDone ) begin isCall[0] <= 1'b0; i <= i + 1'b1; end
159. else begin isCall[0] <= 1'b1; D1 <= 8'hFF; end
160.
161. 11: // Ready OK code
162. begin D2 <= CMD17OK; i <= i + 1'b1; end
163.
164. 12: // Disable cs, generate done signal
165. begin rCS <= 1'b1; isDone <= 1'b1; i <= i + 1'b1; end
166.
167. 13:
168. begin isDone <= 1'b0; i <= 4'd0; end
169.
170. endcase
以上內容為命令CMD17。
171. else if( iCall[1] ) // cmd1
172. case( i )
173.
174. 0: // Enable cs, prepare Cmd1
175. begin rCS <= 1'b0; D4 <= { 8'h41,32'd0,8'hff }; i <= i + 1'b1; end
176.
177. 1: // Try 100 times, ready error code.
178. if( C1 == 10'd100 ) begin D2 <= CMD1ERR; C1 <= 16'd0; i <= 4'd5; end
179. else if( iDone && iData != 8'h00) begin isCall[1]<= 1'b0; C1 <= C1 + 1'b1; end
180. else if( iDone && iData == 8'h00 ) begin isCall[1] <= 1'b0; C1 <= 16'd0; i <= i + 1'b1; end
181. else isCall[1] <= 1'b1;
182.
183. 2: // Disable cs
184. begin rCS <= 1'b1; i <= i + 1'b1; end
185.
186. 3: // Send free clock
187. if( iDone ) begin isCall[0] <= 1'b0; i <= i + 1'b1; end
188. else begin isCall[0] <= 1'b1; D1 <= 8'hff; end
189.
190. /******************/
191.
192. 4: // Disable cs, ready OK code.
193. begin D2 <= CMD1OK; i <= i + 1'b1; end
194.
195. 5: // Disable cs, generate done signal
196. begin rCS <= 1'b1; isDone <= 1'b1; i <= i + 1'b1; end
197.
198. 6:
199. begin isDone <= 1'b0; i <= 4'd0; end
200.
201. endcase
以上內容為命令CMD1。
202. else if( iCall[0] ) // cmd0
203. case( i )
204.
205. 0: // Disable cs, prepare Cmd0
206. begin rCS <= 1'b1; D4 <= {8'h40, 32'd0, 8'h95}; i <= i + 1'b1; end
207.
208. 1: // Wait 1MS for warm up;
209. if( C1 == T1MS -1) begin C1 <= 16'd0; i <= i + 1'b1; end
210. else begin C1 <= C1 + 1'b1; end
211.
212. 2: // Send 80 free clock
213. if( C1 == 10'd10 ) begin C1 <= 16'd0; i <= i + 1'b1; end
214. else if( iDone ) begin isCall[0] <= 1'b0; C1 <= C1 + 1'b1; end
215. else begin isCall[0] <= 1'b1; D1 <= 8'hff; end
216.
217. 3: // Enable cs
218. begin rCS <= 1'b0; i <= i + 1'b1; end
219.
220. 4: // Try 200 time, ready error code.
221. if( C1 == 10'd200 ) begin D2 <= CMD0ERR; C1 <= 16'd0; i <= 4'd8; end
222. else if( iDone && iData != 8'h01) begin isCall[1] <= 1'b0; C1 <= C1 + 1'b1; end
223. else if( iDone && iData == 8'h01 ) begin isCall[1] <= 1'b0; C1 <= 16'd0; i <= i + 1'b1; end
224. else isCall[1] <= 1'b1;
225.
226. 5: // Disable cs
227. begin rCS <= 1'b1 ; i <= i + 1'b1; end
228.
229. 6: // Send free clock
230. if( iDone ) begin isCall[0] <= 1'b0; i <= i + 1'b1; end
231. else begin isCall[0] <= 1'b1; D1 <= 8'hff; end
232.
233. 7: // Disable cs, ready OK code
234. begin D2 <= CMD0OK; i <= i + 1'b1; end
235.
236. 8: // Disbale cs, generate done signal
237. begin rCS <= 1'b1; isDone <= 1'b1; i <= i + 1'b1; end
238.
239. 9:
240. begin isDone <= 1'b0; i <= 4'd0; end
241.
242. endcase
243.
以上內容為命令CMD0。
244. assign SD_NCS = rCS;
245. assign oDone = isDone;
246. assign oTag = D2;
247. assign oEn = isEn;
248. assign oDataFF = D3;
249. assign oCall = isCall;
250. assign oAddr = D4;
251. assign oData = D1;
252.
253. endmodule
以上內容為相關的輸出驅動聲明。
sdcard_basemod.v
該模塊為SD卡基礎模塊,連線部署請參考圖24.11。
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 [3:0]iCall,
10. output oDone,
11. input [22:0]iAddr,
12. output [7:0]oTag,
13.
14. input [1:0]iEn,
15. input [7:0]iData,
16. output [7:0]oData
17. );
18. wire [1:0]EnU1;
19. wire [7:0]DataFFU1;
20. wire [1:0]CallU1;
21. wire [47:0]AddrU1;
22. wire [7:0]DataU1;
23.
24. sdcard_ctrlmod U1
25. (
26. .CLOCK( CLOCK ),
27. .RESET( RESET ),
28. .SD_NCS( SD_NCS ), // > top
29. .iCall( iCall ), // < top
30. .oDone( oDone ), // < top
31. .iAddr( iAddr ), // < top
32. .oTag( oTag ), // > top
33. .oEn( EnU1 ), // > U2 & U3
34. .iDataFF( DataFFU2 ), // < U2
35. .oDataFF( DataFFU1 ), // > U3
36. .oCall( CallU1 ), // > U4
37. .iDone( DoneU4 ), // < U4
38. .oAddr( AddrU1 ), // > U4
39. .iData( DataU4 ), // < U4
40. .oData( DataU1 ) // > U4
41. );
42.
43. wire [7:0]DataFFU2;
44.
45. fifo_savemod U2
46. (
47. .CLOCK ( CLOCK ),
48. .RESET( RESET ),
49. .iEn ( {iEn[1],EnU1[0]} ), // < top & U1
50. .iData ( iData ), // < top
51. .oData ( DataFFU2 ), // > U1
52. .oTag ()
53. );
54.
55. fifo_savemod U3
56. (
57. .CLOCK ( CLOCK ),
58. .RESET( RESET ),
59. .iEn ( {EnU1[1],iEn[0]} ), // < top & U1
60. .iData ( DataFFU1 ), // < U1
61. .oData ( oData ), // > top
62. .oTag ()
63. );
64.
65. wire DoneU4;
66. wire [7:0]DataU4;
67.
68. sdcard_funcmod U4
69. (
70. .CLOCK( CLOCK ),
71. .RESET( RESET ),
72. .SD_CLK( SD_CLK ), // > top
73. .SD_DOUT( SD_DOUT ), // < top
74. .SD_DI( SD_DI ), // > top
75. .iCall( CallU1 ), // < U1
76. .oDone( DoneU4 ), // > U1
77. .iAddr( AddrU1 ), // < U1
78. .iData( DataU1 ), // < U1
79. .oData( DataU4 ) // > U1
80. );
81.
82. endmodule
以上內容,讀者自己看着辦吧,筆者犯懶了。
sdcard_demo.v
圖24.16 實驗二十四的建模圖。
圖24.16是實驗二十四的建模圖,右邊是SD卡基礎模塊,右邊則是調用該模塊的核心程序。核心程序先初始化SD卡,期間也將反饋信息經由TXD發送出去。再者,它將512個字節寫入SD卡,又從中讀出,然后經由TXD發送出去。具體內容讓我們來看代碼吧:
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 [7: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卡基礎模塊實例化,其中isCall驅動iCall,D1驅動iAddr,isEn驅動iEn,D2驅動iData。
32. parameter B115K2 = 11'd434, TXFUNC = 6'd16;
33.
34. reg [5:0]i,Go;
35. reg [10:0]C1,C2;
36. reg [22:0]D1;
37. reg [7:0]D2;
38. reg [10:0]T;
39. reg [3: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 } <= { 23'd0,8'd0,11'd0 };
49. { isCall,isEn } <= { 4'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, 1'b0 }; i <= TXFUNC; Go <= i + 1'b1; end
61.
62. /********************/
63.
步驟0執行CMD0,然后步驟1反饋執行結果。
64. 2: // cmd1
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, 1'b0 }; i <= TXFUNC; Go <= i + 1'b1; end
70.
71. /*********************/
72.
步驟2執行CMD1,然后步驟3反饋執行結果。
73. 4: // write data to fifo
74. begin isEn[1] <= 1'b1; i <= i + 1'b1; end
75.
76. 5:
77. begin isEn[1] <= 1'b0; i <= i + 1'b1; end
78.
79. 6:
80. if( C2 == 511 ) begin C2 <= 11'd0; i <= i + 1'b1; end
81. else begin D2 <= D2 + 1'b1; C2 <= C2 + 1'b1; i <= 6'd4; end
82.
83. /**************/
84.
步驟4~6將數據00~FF寫入FIFO兩遍。
85. 7: // cmd24
86. if( DoneU1 ) begin isCall[3] <= 1'b0; i <= i + 1'b1; end
87. else begin isCall[3] <= 1'b1; D1 <= 23'd0; end
88.
89. 8:
90. begin T <= { 2'b11, TagU1, 1'b0 }; i <= TXFUNC; Go <= i + 1'b1; end
91.
92. /***************/
93.
步驟7執行CMD24,寫入地址為23’d0。步驟8反饋執行結果。
94. 9: // cmd17
95. if( DoneU1 ) begin isCall[2] <= 1'b0; i <= i + 1'b1; end
96. else begin isCall[2] <= 1'b1; D1 <= 23'd0; end
97.
98. 10:
99. begin T <= { 2'b11, TagU1, 1'b0 }; i <= TXFUNC; Go <= i + 1'b1; end
100.
101. /****************/
102.
步驟9執行CMD17,步驟10則反饋執行結果。
103. 11: // Read data from fifo
104. begin isEn[0] <= 1'b1; i <= i + 1'b1; end
105.
106. 12:
107. begin isEn[0] <= 1'b0; i <= i + 1'b1; end
108.
109. 13:
110. begin T <= { 2'b11, DataU1, 1'b0 }; i <= TXFUNC; Go <= i + 1'b1; end
111.
112. 14:
113. if( C2 == 511 ) begin C2 <= 11'd0; i <= i + 1'b1; end
114. else begin C2 <= C2 + 1'b1; i <= 6'd11; end
115.
116. 15:
117. i <= i;
118.
119. /****************/
120.
步驟11~14從FIFO哪里讀出數據512次,然后再經由TXD發送出去。
121. 16,17,18,19,20,21,22,23,24,25,26:
122. if( C1 == B115K2 -1 ) begin C1 <= 11'd0; i <= i + 1'b1; end
123. else begin rTXD <= T[i - 16]; C1 <= C1 + 1'b1; end
124.
125. 27:
126. i <= Go;
127.
128. endcase
129.
130. assign TXD = rTXD;
131.
132. endmodule
步驟16~27是發送一幀數據的偽函數。綜合完畢,插入版本V1.×的SD卡,例如筆者手上 IProc制,容量為256MB的SD卡,然后下載程序。演示過程如下:
A2 // CMD0 執行成功
A4 // CMD1 執行成功
A6 // CMD24 執行成功
A8 // CMD17 執行成功
00~FF // 讀出數據 0~255
00~FF // 讀出數據 256~511
圖24.17 SD卡的內容。
為了驗證SD卡是否成功寫入 00~FF 兩遍,筆者稍微瞧瞧 SD卡的內容 ... 如圖24.17所示,地址0x00~0xF0(0~255)的內容是 00~FF,地址0x0100~0x01F0(256~511)的內容也是 00~FF。
細節一:完整的個體模塊
雖然本實驗的SD卡基礎模塊已經就緒,不過SD卡的前提條件必須是版本SDV1.×,還有健康的硬件。嘛,SD卡基礎模塊傻是傻了一點,不過它還可以繼續擴展。