實驗二十五: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卡也必須健康無患,不然該基礎模塊會運行失敗。