实验二十四:SD卡模块

 

驱动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

.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, "Courier New", courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }

代码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

.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, "Courier New", courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }

代码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

.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, "Courier New", courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }

代码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

.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, "Courier New", courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }

代码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

.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, "Courier New", courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }

代码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

.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, "Courier New", courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }

代码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.    );

.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, "Courier New", courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }

以上内容为出入端声明。

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.                      

.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, "Courier New", courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }

第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

.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, "Courier New", courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }

以上内容为相关的寄存器声明还有复位操作。其中第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    

.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, "Courier New", courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }

以上内容为写命令。

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.        

.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, "Courier New", courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }

以上内容为读写一个字节。

118.         assign SD_CLK = rSCLK;     
119.         assign SD_DI = rDI;     
120.         assign oDone = isDone;
121.         assign oData = D1;
122.    
123.    endmodule

.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, "Courier New", courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }

以上内容为相关的输出驱动。

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

.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, "Courier New", courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }

由于数据缓冲对象不是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.    );    

.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, "Courier New", courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }

以上内容为相关的出入端声明,第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.        

.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, "Courier New", courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }

以上内容为各个命令的成功信息还有失败信息之间的常量声明。

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

.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, "Courier New", courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }

以上内容为相关的寄存器声明还有复位操作,其中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

.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, "Courier New", courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }

以上内容为命令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    

.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, "Courier New", courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }

以上内容为命令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

.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, "Courier New", courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }

以上内容为命令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.        

.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, "Courier New", courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }

以上内容为命令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

.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, "Courier New", courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }

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

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

.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, "Courier New", courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }

以上内容,读者自己看着办吧,笔者犯懒了。

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.    );

.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, "Courier New", courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }

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

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.         

.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, "Courier New", courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }

以上内容为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

.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, "Courier New", courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }

以上内容为相关的寄存器声明还有复位操作,当然也包括波特率的常量声明还有伪函数入口。

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.                         

.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, "Courier New", courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }

步骤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.                         

.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, "Courier New", courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }

步骤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.                         

.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, "Courier New", courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }

步骤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.                         

.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, "Courier New", courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }

步骤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.                         

.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, "Courier New", courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }

步骤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.                        

.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, "Courier New", courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }

步骤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

.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, "Courier New", courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }

步骤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卡基础模块傻是傻了一点,不过它还可以继续扩展。

时间: 2024-10-14 20:52:38

实验二十四:SD卡模块的相关文章

CCNA实验二十四 路由更新的安全 &nbsp;

CCNA实验二十四 路由更新的安全 环境: Windows XP .GNS3.0.7.2 目的: 学会如何通过MD5 认证和被动端口保护路由更新,注意这两种方法在EIGRP .OSPF. RIP.IGRP等路由协议中都能用. 说明: MD5认证:通过邻居路由器之间的路由更新认证来防止无效的路由更新可能对网络造成的威胁和破坏. 被动接口:能够防止不必要的路由更新进入某个网络,可以禁止向不在安全管理范围的网段通告路由起到安全的作用,并且还能阻止EIGRP,OSPF,ISIS的HELLO包的通过.此外

第三百二十四节,web爬虫,scrapy模块介绍与使用

第三百二十四节,web爬虫,scrapy模块介绍与使用 Scrapy是一个为了爬取网站数据,提取结构性数据而编写的应用框架. 其可以应用在数据挖掘,信息处理或存储历史数据等一系列的程序中.其最初是为了页面抓取 (更确切来说, 网络抓取 )所设计的, 也可以应用在获取API所返回的数据(例如 Amazon Associates Web Services ) 或者通用的网络爬虫.Scrapy用途广泛,可以用于数据挖掘.监测和自动化测试. Scrapy 使用了 Twisted异步网络库来处理网络通讯.

嵌入式Linux裸机开发(十)——SD卡启动

嵌入式Linux裸机开发(十)--SD卡启动 存储设备分类: 磁存储设备:软盘.硬盘.光盘.CD.磁带 Flash:NandFlash.NorFlash 缺点:时序复杂,无坏块处理机制,接口不统一 NandFlash:MLC(可靠性差,容量大).SLC(可靠性高.容量小) 扩展卡式Flash:SD卡.MMC卡.MicroSD(TF卡) 内部为NnadFlash存储颗粒,外部封装了接口,接口标准统一.通用. 缺点:频繁使用导致卡槽接触不可靠 iNand.MoviNand.eSSD: 内部为Nand

二十四、Android文件的读写

Android的文件读写与JavaSE的文件读写相同,都是使用IO流.而且Android使用的正是JavaSE的IO流,下面我们通过一个练习来学习Android的文件读写. 1.创建一个Android工程 [html] view plaincopy Project name:File BuildTarget:Android2.2 Application name:文件读写 Package name:test.file Create Activity:DateActivity Min SDK Ve

全栈JavaScript之路( 二十四 )DOM2、DOM3, 不涉及XML命名空间的扩展

(一)DocumentType 类型的变化新增三个属性: publicId,systemId,internalSubset(内部子集) <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd" [<!ELEMENT name (#PCDATA)>] > 通过, document.doc

QT开发(二十四)——QT文件操作

QT开发(二十四)--QT文件操作 一.QT文件操作简介 QT中的IO操作通过统一的接口简化了文件与外部设备的操作方式,QT中文件被当作一种特殊的外部设备,文件操作与外部设备操作相同. 1.IO操作的主要函数接口 打开设备:bool open(OpenMode mode) 读取数据:QByteArray read(qint64 maxSize) 写入数据:qint64 write(const QByteArray & byteArray) 关闭设备:void close() IO操作的本质是连续

学习OpenCV范例(二十四)—ViBe前景检测(二)

最近导师没给什么项目做,所以有那么一点点小时间,于是就研究起了前景检测,既然前景检测有很多种算法,那干脆就把这些模型都学起来吧,以后用到前景检测时至少还有那么几种方法可以选择,上次介绍的是GMM模型,其实GMM模型本身就是一个很不错的模型,现在也很多人在研究,并且做改进,主要是OpenCV有函数调用,用起来非常方便,当我们都在兴高采烈的讨论GMM各种好的时候,B哥不爽了,他说老子是搞前景检测的,怎么可能让你们这么嚣张,而且老子就不按照你那套路来,什么高斯模型,混合高斯模型,我统统不用,就来个简单

angular学习笔记(二十四)-$http(2)-设置http请求头

1. angular默认的请求头: 其中,Accept 和 X-Requested-With是$http自带的默认配置 2. 修改默认请求头: (1) 全局修改(整个模块) 使用$httpProvider依赖 var myApp = angular.module('MyApp',[]); myApp.config(function($httpProvider){ console.log($httpProvider.defaults.headers.common) //修改/操作$httpProv

从零开始学android&lt;android事件的处理方式.二十四.&gt;

在android中一共有 多种事件,每种事件都有自己相对应的处理机制 如以下几种 1 单击事件 View.OnClickListener public abstract void onClick (View v) 单击组件时触发 2 单击事件 View.OnLongClickListener public abstract boolean onLongClick (View v) 长按组件时触发 3 键盘事件 View.OnKeyListener public abstract boolean