• 【黑金原创教程】【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卡也必须健康无患,不然该基础模块会运行失败。

  • 相关阅读:
    url末尾的斜杠作用
    awk 工具可以很灵活地对文本进行处理,这里的 awk '{print $2}'是指第二列的内容,是运行的程序 ID。 杀死指定进程
    nil和空切片
    WaitGroup源码设计,
    2
    t
    Go内置的定时器
    分布式定时器
    1 2 交换
    order by 1 DESC 2 desc
  • 原文地址:https://www.cnblogs.com/alinx/p/4531897.html
Copyright © 2020-2023  润新知