zoukankan      html  css  js  c++  java
  • 【黑金原创教程】【FPGA那些事儿-驱动篇I 】实验二十五:SDHC模块

    实验二十五: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卡处于待机状态。

    clip_image002

    图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之余也产生完成信号。

    clip_image004

    图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之余也产生完成信号。

    clip_image006

    图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之余也产生完成信号。

    clip_image008

    图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以外。

    clip_image010

    图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卡便全面进入传输模式,初始化过程也因此结束,真是可喜可贺!

    clip_image012

    图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,不过不打紧,事后笔者会继续解释的 ... 所以暂请读者憋着蛋蛋一下。上述内容理解完毕以后,我们便可以开始建模了。

    clip_image014

    图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

    clip_image016

    图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

    clip_image018

    图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

    clip_image020

    图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的标示位。

    clip_image022

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

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

    细节一:完整的个体模块

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

  • 相关阅读:
    jvisualm 结合 visualGC 进行jvm监控,并分析垃圾回收
    linux 查看服务器cpu 与内存配置
    arthas 使用总结
    selinux contexts 安全上下文的临时更改
    Android 8.1 Doze模式分析(五) Doze白名单及Debug方式
    Window 任意窗口置顶软件Window TopMost Control
    Android ApkToolPlus一个可视化的跨平台 apk 分析工具
    SVN Please execute the 'Cleanup' command.
    Android 如何在64位安卓系统中使用32位SO库
    Android cmd命令查看apk是32位还是64位?
  • 原文地址:https://www.cnblogs.com/alinx/p/4531897.html
Copyright © 2011-2022 走看看