• 【黑金原创教程】【FPGA那些事儿-驱动篇I 】实验二十一:SDRAM模块④ — 页读写 β


    实验二十一:SDRAM模块④ — 页读写 β

    未进入主题之前,让我们先来谈谈一些重要的体外话。《整合篇》之际,笔者曾经比拟Verilog如何模仿for循环,我们知道for循环是顺序语言的产物,如果Verilog要实现属于自己的for循环,那么它要考虑的东西除了步骤以外,还有非常关键的时钟。

    for( i=0; i<4; i++ ) 操作A;
     
    i = 0;
    while ( i<4 ) { 操作A;i++;}
     
    i = 0;
    do { 操作A; i++; } while(i = 3)

    代码21.1

    代码2.11有三段经典的循环操作,即for循环,while循环,还有do ... while循环。如果三个循环互相交流的话,谁又是谁的朋友呢?为了解决这个问题,我们必须先建立交友标准,即先执行后判断,还是先判断后执行? 如此一来,我们可以百分百确信 for循环与while循环才是好朋友,因为两者都是先判断后执行的类型。反之do ... while循环则是先执行后判断的类型。

    为了验证上述的接着,我们试着解剖一下种循环的执行过程 ...

    for循环:

    清零i

    i为0,判断i是否小于4,如是执行操作A第0次,递增i为1;

    i为1,判断i是否小于4,如是执行操作A第1次,递增i为2;

    i为2,判断i是否小于4,如是执行操作A第2次,递增i为3;

    i为3,判断i是否小于4,如是执行操作A第3次,递增i为4;

    i为4,判断i是否小于4,不是结束循环。

    while循环:

    清零i;

    i为0,判断i是否小于4,如是执行操作A第0次,递增i为1;

    i为1,判断i是否小于4,如是执行操作A第1次,递增i为2;

    i为2,判断i是否小于4,如是执行操作A第2次,递增i为3;

    i为3,判断i是否小于4,如是执行操作A第3次,递增i为4;

    i为4,判断i是否小于4,不是结束循环。

    do ... while循环:

    清零i;

    i为0,执行操作A第0次,i递增为1,判断i是否小于4,如是继续;

    i为1,执行操作A第1次,i递增为2,判断i是否小于4,如是继续;

    i为2,执行操作A第2次,i递增为3,判断i是否小于4,如是继续;

    i为3,执行操作A第3次,i递增为4,判断i是否小于4,不是则结束循环;

    读者可能会很奇怪,执行与判断的次序究竟有什么好困惑?作为一只小气鬼,笔者会非常执著小细节。Verilog是并行性质的语言,执行与判断有可能同时执行,也有可能并非同时执行 ... 根据直觉,笔者相信Verilog的循环操作更加适合先执行后判断,而不是先判断后执行。

    1.     0:
    2.     begin
    3.         if( C1 == 0 ) isEn <= 1’b1;
    4.         else if( C1 == 4-2 ) isEn <= 1’b0;
    5.    
    6.         if( C1 == 4 -1 ) begin C1 <= 4’d0; i <= i + 1’b1; end
    7.         else C1 <= C1 + 1’b1;
    8.     end

    代码21.1

    如代码21.1所示,第6~7行表示步骤0保持4个时钟,其中 C1为0拉高isEn,C1为4-2的拉低 isEn;代码21.1告诉我们,执行与判断是同时进行,然而从解读代码的顺序来看,笔者更加倾向“先执行后判断”这种结构性

    clip_image002

    图21.1 代码21.1的时序图。

    图21.1是代码21.1所描述的时序图,其中isEn拉高是否完全根据C1的计数。T0之前也是复位的状态,C1为0。T0之际,C1为0(过去值)拉高isEn,C1为2(过去值)拉低isEn。

    1.    0: 
    2.    begin isEn <= 1’b1; i <= i + 1’b1; end
    3.    1: 
    4.    begin isEn <= 1’b0; i <= i + 1’b1; end
    5.    2:
    6.    if( C1 == 4 -1 ) begin C1 <= 4’d0; i <= i + 1’b1; end
    7.    else begin C1 <= C1 + 1’b1; i <= 4’d0; end

    代码21.2

    同样的结构性甚至可以衍生至不同的操作,如代码21.2所示。步骤0拉高isEn,步骤1拉低isEn,步骤2判断。如果步骤0~2来回重复,isEn一共拉高4次,或者说isEn产生四个高脉冲。

    clip_image004

    图21.2 代码21.2的理想时序图。

    图21.2是代码21.2所产生的时序图。只要稍微比较图21.1与图21.2,我们会发现两者之间都有相似的“结构性”,因为两者都是倾向“先执行后判断”。

    1.    0:
    2.    if( Done ) begin isCall[0] <= 1’b0; i <= i + 1’b1; end
    3.    else begin isCall[0] <= 1’b1; end
    4.    1:
    5.    if( Done ) begin isCall[1] <= 1’b0; i <= i + 1’b1; end
    6.    else begin isCall[1] <= 1’b1; end
    7.    2:
    8.    if( C1 == 4 -1 ) begin C1 <= 4’d0; i <= i + 1’b1; end
    9.    else begin C1 <= C1 + 1’b1; i <= 4’d0; end

    代码21.3

    再举例而言,如代码21.3所示,步骤0执行功能0,步骤1执行功能1,步骤2用来判断循环次数。步骤0~2之间会来回重复,直至功能0与1都执行四次,这种感觉好比代码21.4。

    1.    int main()
    2.    {
    3.        for( int i = 0; i < 4; i ++ )
    4.        {
    5.            Function0();
    6.            Function1();
    7.           }
    8.        return -1;
    9.    }

    代码21.4

    不过代码21.1~21.3都有相似的结构性,即都是先执行后判断。笔者作为热爱结构的男人,笔者会尝尽一切挖掘结构的可能性。此外,这种先执行后判断的模仿对象,笔者称为伪循环(Fake Iteration)。说完题外话,接下来让我们进入本实验的主题。

    页读写之所以分为α与β,那是因为过多的数据吞吐量导致读写变成非常麻烦。天真的朋友可能会认为,如果Burst Length 为4,那么数据位宽就是 4 * 16bit。再如果 Burst Length 为 512,那么数据位宽就是 512 * 16 bit。理论上,这样说是没错,不过那样做会撑爆 FPGA的嘴巴。为此,读写入口必须加入缓冲机制才行。此刻,实验十五所学过的知识(同步FIFO)就派上用场了。

    clip_image006

    图21.3 SDRAM基础模块的建模图。

    加入FIFO储存模块的SDRAM基础模块,大致上如图21.3所示。为了简化连线,笔者稍微加大FIFO的深度,对此FIFO有没有反馈状态都没有问题。还没有进入建模之前,让笔者先来解释一下,页读写与FIFO之间,究竟有什么细节需要注意。

    页写操作(请求FIFO):

    clip_image008

    图21.4 页写操作的理想时序图。

    图21.4是也写操作的理想时序图,相较实验二十的页写时序,实验二十一的页写时序夹杂了FIFO储存模块,其中isEn[0]是SDRAM功能模块向FIFO的读请求。时序的大致过程如下:

    l T1,发送ACT命令,BANK地址与行地址;

    l T1半周期,SDRAM读取;

    l T2,满足TRCD;

    l T3,拉高isEn[0];

    l T4,发送WR命令,BANK地址与列地址,FIFO发送第0数据,;

    l T4半周期,SDRAM读取

    l T5,FIFO发送第1~511数据,C1为510拉低isEn[0],C1为511发送BSTP命令。

    图22.4基本上已经表达非常清楚,为使FIFO可以同步发送数据,因为SDRAM功能模块必须提前一个时钟拉高isEn[0],即T3拉高isEn[0]。此外,为了不使FIFO吐出过多的数据,C1为510的时候便拉低isEn[1]。C1为511的时候则发送BSTP命令。对此,Verilog则可以这样描述,结果如代码21.5所示:

    1.    1: // Send Active Command with Bank and Row address
    2.    begin rCMD <= _ACT; rBA <= iAddr[23:22]; rA <= iAddr[21:9]; i <= i + 1'b1; end
    3.                         
    4.    2: // wait TRCD 20ns
    5.    if( C1 == TRCD -1 ) begin C1 <= 14'd0; i <= i + 1'b1; end
    6.    else begin rCMD <= _NOP; C1 <= C1 + 1'b1; end             
    7.                    
    8.    3: 
    9.    begin isEn[0] <= 1'b1; i <= i + 1'b1; end
    10.                         
    11.    4: // Send Write command with row address,
    12.    begin rCMD <= _WR; rBA <= iAddr[23:22]; rA <= { 4'b0000, iAddr[8:0] }; i <= i + 1'b1; end
    13.                         
    14.    5: // continue write until end and send BSTP 
    15.    begin
    16.        if( C1 == 512 -2 ) begin isEn[0] <= 1'b0; end
    17.        if( C1 == 512 -1 ) begin rCMD <= _BSTP; C1 <= 14'd0; i <= i + 1'b1; end
    18.        else begin rCMD <= _NOP; C1 <= C1 + 1'b1; end
    19.    end

    代码21.5

    如代码21.5所示,步骤1发送Active命令,步骤2满足TRCD,步骤3提前拉高读请求,即isEn[0]。步骤4发送 Write命令,还有写入第0数据。步骤5写入第1~511数据,C1为510的时候拉低isEn[0],C1为511的时候发送 Burst Stop 命令。

    页读操作(请求FIFO):

    clip_image010

    图21.5 页读操作的理想时序图。

    图21.5还是笔者自定义的页写的时序图,相较实验二十,其中多了FIFO的写请求 isEn[1],FIFO的iData,还有C1计数。一旦 CAS Latency 得到满足,SDRAM便会开始读出数据。半个周期之后(T5)FPGA读取,并且拉高isEn[1],然后将数据转交FIFO。

    T6之际,512个数据读写完毕,拉低isEn[1],然后发送BSTP命令。

    时序的大致过程如下:

    l T1,发送ACT命令,BANK地址与行地址;

    l T1半周期,SDRAM读取;

    l T2,满足TRCD;

    l T3,发送RD命令,BANK地址与列地址;

    l T3半周期,SDRAM读取命令。

    l T4,满足 CAS Latency。

    l T5,拉高isEn[1],读取第0~511数据,并且向FIFO的iData写入。

    l T6,拉低isEn[1],发送BSTP命令。

    l T6半周期,SDRAM读取。

    对此,Verilog可以这样描述,结果如代码21.6所示:

    1.    1: // Send Active command with Bank and Row address
    2.    begin rCMD <= _ACT; rBA <= iAddr[23:22]; rA <= iAddr[21:9]; i <= i + 1'b1; end
    3.                         
    4.    2: // wait TRCD 20ns
    5.    if( C1 == TRCD -1 ) begin C1 <= 14'd0; i <= i + 1'b1; end
    6.    else begin rCMD <= _NOP; C1 <= C1 + 1'b1; end 
    7.                    
    8.    3: // Send Read command and column address
    9.    begin rCMD <= _RD; rBA <= iAddr[23:22]; rA <= { 4'b0000, iAddr[8:0]}; i <= i + 1'b1; end
    10.    
    11.    4: // wait CL 3 clock
    12.    if( C1 == CL -1 ) begin C1 <= 14'd0; i <= i + 1'b1; end
    13.    else begin rCMD <= _NOP; C1 <= C1 + 1'b1; end 
    14.                         
    15.    5: // Read Data
    16.    begin 
    17.         D1 <= S_DQ; isEn[1] <= 1'b1; 
    18.         if( C1 == 512 -1 ) begin C1 <= 14'd0; i <= i + 1'b1; end
    19.         else begin C1 <= C1 + 1'b1; end
    20.    end
    21.                                     
    22.    6:
    23.    begin isEn[1] <= 1'b0; rCMD <= _BSTP; i <= i + 1'b1; end

    代码21.6

    如代码21.6所示,步骤4满足CL以后,步骤5便拉高isEn[1],并且读取512个数据。步骤6将isEn[1]拉低,并且发送命令 BSTP。理解完毕以后,我们可以开始建模了。

    fifo_savemod.v

    clip_image012

    图21.6 FIFO储存模块的建模图。

    图21.6是FIFO储存模块的建模图,为了简化设计,笔者不小心吃掉FIFO的输出标签,取而代之就是增加深度。虽然实验只有两只FIFO储存模块,方向也不一样,不过母体都是一样的东西。具体内容我们还是来看代码吧:

    1.    module fifo_savemod
    2.    (
    3.         input CLOCK, RESET, 
    4.         input [1:0]iEn,
    5.         input [15:0]iData,
    6.         output [15:0]oData,
    7.         output [1:0]oTag
    8.    );

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

    9.        initial begin
    10.             for( C1 = 0; C1 < 1024; C1 = C1 + 1'b1 )
    11.                  begin  RAM[ C1 ] <= 16'd0; end    
    12.         end
    13.        
    14.        reg [15:0] RAM [1023:0]; 

    以上内容为声明位宽为16,深度为1024的RAM,,然后将其初始化。

    15.        reg [10:0] C1 = 11'd0,C2 = 11'd0; // N+1
    16.                  
    17.       always @ ( posedge CLOCK or negedge RESET )
    18.            if( !RESET )
    19.                 begin
    20.                      C1 <= 11'd0;
    21.                  end
    22.           else if( iEn[1] ) 
    23.                 begin   
    24.                    RAM[ C1[9:0] ] <= iData; 
    25.                     C1 <= C1 + 1'b1; 
    26.                  end
    27.                  
    28.        always @ ( posedge CLOCK or negedge RESET )
    29.            if( !RESET )
    30.                 begin
    31.                        C2 <= 11'd0;
    32.                  end
    33.             else if( iEn[0] )
    34.                    begin 
    35.                        //D1 <= RAM[ C2[9:0] ]; 
    36.                         C2 <= C2 + 1'b1; 
    37.                    end
    38.        

    以上内容为核心内容。第15行声明写指针C1,还有读指针C2,位宽为 RAM的深度+1。第17~26行是FIFO的写操作,第28~37行是FIFO的读操作,笔者将第35行注释掉。

    39.          assign oData = RAM[ C2[9:0]];                
    40.          assign oTag[1] = ( C1[10]^C2[10] & C1[9:0] == C2[9:0] ); // Full Left
    41.           assign oTag[0] = ( C1 == C2 ); // Empty Right
    42.    
    43.    endmodule

    取而代之,笔者将RAM直接驱动 oData。好奇的朋友一定觉得疑惑?其实笔者是为了偷时钟,如果用D1驱动oData,FIFO的读数据(未来值)就会慢了半拍。所以,FIFO与SDRAM功能模块之间会同步失败。反之,RAM直接驱动输出,好比组合逻辑直接驱动输出,读取数据都是即时值。第40~41行是写满状态还有读空状态的输出驱动声明。

    sdram_funcmod.v
    1.    module sdram_funcmod
    2.    (
    3.         input CLOCK,
    4.         input RESET,
    5.         
    6.         output S_CKE, S_NCS, S_NRAS, S_NCAS, S_NWE,
    7.         output [1:0]S_BA,  
    8.         output [12:0]S_A,  
    9.         output [1:0]S_DQM,
    10.         inout [15:0]S_DQ,
    11.         
    12.         output [1:0]oEn, //[1]Write [0]Read
    13.         input [23:0]iAddr,  // [23:22]BA,[21:9]Row,[8:0]Column
    14.         input [15:0]iData,
    15.         output [15:0]oData,
    16.         
    17.         input [3:0]iCall,
    18.         output oDone
    19.    );

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

    20.        parameter T100US = 14'd13300;
    21.        // tRP 20ns, tRRC 63ns, tRCD 20ns, tMRD 2CLK, tWR/tDPL 2CLK, CAS Latency 3CLK
    22.        parameter TRP = 14'd3, TRRC = 14'd9, TMRD = 14'd2, TRCD = 14'd3, TWR = 14'd2, CL = 14'd3;
    23.        parameter _INIT = 5'b01111, _NOP = 5'b10111, _ACT = 5'b10011, _RD = 5'b10101, _WR = 5'b10100,
    24.                _BSTP = 5'b10110, _PR = 5'b10010, _AR = 5'b10001, _LMR = 5'b10000;
    25.                

    以上内容为相关的常量声明。

    26.        reg [4:0]i;
    27.        reg [13:0]C1;
    28.        reg [15:0]D1;
    29.        reg [4:0]rCMD;
    30.        reg [1:0]rBA;
    31.        reg [12:0]rA;
    32.        reg [1:0]rDQM;
    33.        reg [1:0]isEn;
    34.        reg isOut;
    35.        reg isDone;
    36.    
    37.        always @ ( posedge CLOCK or negedge RESET )
    38.            if( !RESET )
    39.                begin
    40.                    i <= 4'd0;
    41.                    C1 <= 14'd0;
    42.                    D1 <= 16'd0;
    43.                    rCMD <= _NOP;
    44.                    rBA <= 2'b11;
    45.                    rA <= 13'h1fff;
    46.                    rDQM <= 2'b00;
    47.                    isEn <= 2'b00;
    48.                    isOut <= 1'b1;
    49.                    isDone <= 1'b0;
    50.                end

    以上内容为相关的寄存器声明与复位操作。

    51.              else if( iCall[3] )
    52.                case( i )
    53.                    
    54.                    0: // Set IO to output Tag
    55.                    begin isOut <= 1'b1; i <= i + 1'b1; end
    56.                       
    57.                    1: // Send Active Command with Bank and Row address
    58.                    begin rCMD <= _ACT; rBA <= iAddr[23:22]; rA <= iAddr[21:9]; i <= i + 1'b1; end
    59.                         
    60.                    2: // wait TRCD 20ns
    61.                    if( C1 == TRCD -1 ) begin C1 <= 14'd0; i <= i + 1'b1; end
    62.                    else begin rCMD <= _NOP; C1 <= C1 + 1'b1; end             
    63.                    
    64.                    /*********************************************/
    65.                    
    66.                   3: 
    67.                   begin isEn[0] <= 1'b1; i <= i + 1'b1; end
    68.                         
    69.                    4: // Send Write command with row address
    70.                    begin rCMD <= _WR; rBA <= iAddr[23:22]; rA <= { 4'b0000, iAddr[8:0] }; i <= i + 1'b1; end
    71.                         
    72.                     5: // continue write until end and send BSTP 
    73.                    begin
    74.                        if( C1 == 512 -2 ) begin isEn[0] <= 1'b0; end
    75.                        if( C1 == 512 -1 ) begin rCMD <= _BSTP; C1 <= 14'd0; i <= i + 1'b1; end
    76.                        else begin rCMD <= _NOP; C1 <= C1 + 1'b1; end
    77.                    end
    78.    
    79.                    /**********************************************/
    80.                         
    81.                    6: // Generate done signal
    82.                    begin rCMD <= _NOP; isDone <= 1'b1; i <= i + 1'b1; end
    83.                        
    84.                   7:
    85.                   begin isDone <= 1'b0; i <= 4'd0; end
    86.                    
    87.                endcase

    以上内容为部分核心操作。以上内容是写操作,步骤3提前请求FIFO,步骤4写入第0数据,步骤5写入第1~511数据,然后发送BSTP命令。步骤6发送 NOP命令之余,也用来产生完成信号。

    88.            else if( iCall[2] )
    89.                case( i )
    90.                    
    91.                    0:
    92.                    begin isOut <= 1'b0; D <= 16'd0; i <= i + 1'b1; end
    93.    
    94.                    1: // Send Active command with Bank and Row address
    95.                    begin rCMD <= _ACT; rBA <= iAddr[23:22]; rA <= iAddr[21:9]; i <= i + 1'b1; end
    96.                         
    97.                    2: // wait TRCD 20ns
    98.                    if( C1 == TRCD -1 ) begin C1 <= 14'd0; i <= i + 1'b1; end
    99.                    else begin rCMD <= _NOP; C1 <= C1 + 1'b1; end 
    100.                
    101.                    /********************/
    102.                    
    103.                    3: // Send Read command and column address
    104.                    begin rCMD <= _RD; rBA <= iAddr[23:22]; rA <= { 4'b0000, iAddr[8:0]}; i <= i + 1'b1; end
    105.    
    106.                    4: // wait CL 3 clock
    107.                    if( C1 == CL -1 ) begin C1 <= 14'd0; i <= i + 1'b1; end
    108.                    else begin rCMD <= _NOP; C1 <= C1 + 1'b1; end 
    109.                                       
    110.                    /********************/ 
    111.                         
    112.                    5: // Read Data
    113.                   begin 
    114.                      D1 <= S_DQ; isEn[1] <= 1'b1; 
    115.                      if( C1 == 512 -1 ) begin C1 <= 14'd0; i <= i + 1'b1; end
    116.                      else begin C1 <= C1 + 1'b1; end
    117.                   end
    118.                    
    119.                    /********************/
    120.                         
    121.                   6:
    122.                   begin isEn[1] <= 1'b0; rCMD <= _BSTP; i <= i + 1'b1; end
    123.                    
    124.                   /******************/
    125.                         
    126.                    7: // Generate done signal
    127.                    begin rCMD <= _NOP; isDone <= 1'b1; i <= i + 1'b1; end
    128.                        
    129.                   8:
    130.                   begin isDone <= 1'b0; i <= 4'd0; end
    131.           
    132.                endcase

    以上内容为部分核心操作。以上内容是读操作。步骤4满足CL以后,步骤5读取并且发送512个数据给FIFO。步骤6,拉低isEn[1]然后发送BSTP命令。步骤7发送NOP命令,然后产生完成信号。

    133.              else if( iCall[1] )
    134.                case( i )
    135.                    
    136.                   0: // Send Precharge Command
    137.                   begin rCMD <= _PR; i <= i + 1'b1; end
    138.                         
    139.                   1: // wait TRP 20ns
    140.                   if( C1 == TRP -1 ) begin C1 <= 14'd0; i <= i + 1'b1; end
    141.                    else begin rCMD <= _NOP; C1 <= C1 + 1'b1; end
    142.                         
    143.                    2: // Send Auto Refresh Command
    144.                    begin rCMD <= _AR; i <= i + 1'b1; end
    145.                   
    146.                    3: // wait TRRC 63ns
    147.                   if( C1 == TRRC -1 ) begin C1 <= 14'd0; i <= i + 1'b1; end
    148.                    else begin rCMD <= _NOP; C1 <= C1 + 1'b1; end
    149.                         
    150.                    4: // Send Auto Refresh Command
    151.                    begin rCMD <= _AR; i <= i + 1'b1; end
    152.                   
    153.                    5: // wait TRRC 63ns
    154.                   if( C1 == TRRC -1 ) begin C1 <= 14'd0; i <= i + 1'b1; end
    155.                    else begin rCMD <= _NOP; C1 <= C1 + 1'b1; end
    156.                    
    157.                    /********************/
    158.                    
    159.                    6: // Generate done signal
    160.                    begin isDone <= 1'b1; i <= i + 1'b1; end
    161.                        
    162.                   7:
    163.                   begin isDone <= 1'b0; i <= 4'd0; end
    164.    
    165.                endcase

    以上内容为部分核心操作。以上内容是刷新操作。

    166.              else if( iCall[0] )
    167.                case( i )
    168.                    
    169.                   0:  // delay 100us
    170.                   if( C1 == T100US -1 ) begin C1 <= 14'd0; i <= i + 1'b1; end
    171.                   else begin C1 <= C1 + 1'b1; end 
    172.                   
    173.                   /********************/
    174.                   
    175.                   1: // Send Precharge Command
    176.                   begin rCMD <= _PR; { rBA, rA } <= 15'h3fff; i <= i + 1'b1; end
    177.                        
    178.                   2: // wait TRP 20ns
    179.                  if( C1 == TRP -1 ) begin C1 <= 14'd0; i <= i + 1'b1; end
    180.                   else begin rCMD <= _NOP; C1 <= C1 + 1'b1; end
    181.                   
    182.                   3: // Send Auto Refresh Command
    183.                   begin rCMD <= _AR; i <= i + 1'b1; end
    184.                   
    185.                   4: // wait TRRC 63ns
    186.                  if( C1 == TRRC -1 ) begin C1 <= 14'd0; i <= i + 1'b1; end
    187.                   else begin rCMD <= _NOP; C1 <= C1 + 1'b1; end
    188.                        
    189.                   5: // Send Auto Refresh Command
    190.                   begin rCMD <= _AR; i <= i + 1'b1; end
    191.                   
    192.                   6: // wait TRRC 63ns
    193.                  if( C1 == TRRC -1 ) begin C1 <= 14'd0; i <= i + 1'b1; end
    194.                   else begin rCMD <= _NOP; C1 <= C1 + 1'b1; end
    195.                
    196.                   /********************/
    197.                   
    198.                   7: // Send LMR Cmd. Burst Read & Write,  3'b010 mean CAS latecy = 3, Sequential, 1 burst length
    199.                   begin rCMD <= _LMR; rBA <= 2'b11; rA <= { 3'd0, 1'b0, 2'd0, 3'b011, 1'b0, 3'b111 }; i <= i + 1'b1; end
    200.                        
    201.                   8: // Send 2 nop CLK for tMRD
    202.                   if( C1 == TMRD -1 ) begin C1 <= 14'd0; i <= i + 1'b1; end
    203.                   else begin rCMD <= _NOP; C1 <= C1 + 1'b1; end
    204.                   
    205.                   /********************/
    206.                   
    207.                   9: // Generate done signal
    208.                   begin isDone <= 1'b1; i <= i + 1'b1; end
    209.                        
    210.                  10:
    211.                  begin isDone <= 1'b0; i <= 4'd0; end
    212.                   
    213.                endcase
    214.      

    以上内容为部分核心操作。以上内容是初始化,注意步骤7的设置内容,Burst Length 设置为 3’b111。

    215.         assign { S_CKE, S_NCS, S_NRAS, S_NCAS, S_NWE } = rCMD;
    216.         assign { S_BA, S_A } = { rBA, rA };
    217.         assign S_DQM = rDQM;
    218.         assign S_DQ  = isOut ? iData : 16'hzzzz;
    219.         assign oEn = isEn;
    220.         assign oDone = isDone;
    221.         assign oData = D1;
    222.    
    223.    endmodule

    以上内容为相关的输出驱动。注意,第218行表示FIFO直接驱动 S_DQ的输出。

    sdram_ctrlmod.v

    该控制模块不曾修改,所以笔者就不用重复粘贴了。

    sdram_basemod.v

    clip_image014

    图21.7 SDRAM基础模块的建模图。

    图21.7是SDRAM基础模块的建模图,其中控制模块只是负责上级调用,还有SDRAM功能模块的调用而已。SDRAM功能模块的读出操作经由FIFO储存模块缓冲,然后再由上级读写数据。Addr地址信号也是上级调用。注意,由于SDRAM基础模块的连线部署稍微复杂一点,为此图21.7稍微不遵守格式。

    1.    module sdram_basemod
    2.    (
    3.         input CLOCK,
    4.         input RESET,
    5.         
    6.         output S_CKE, S_NCS, S_NRAS, S_NCAS, S_NWE,
    7.         output [1:0]S_BA,
    8.         output [12:0]S_A, 
    9.         output [1:0]S_DQM,
    10.         inout [15:0]S_DQ,
    11.         
    12.         input [1:0]iEn,
    13.         input [23:0]iAddr,
    14.         input [15:0]iData,
    15.         output [15:0]oData,
    16.         output [1:0]oTag,
    17.         
    18.         input [1:0]iCall,
    19.         output [1:0]oDone
    20.    );
    21.         wire [3:0]CallU1; // [3]Write, [2]Read, [1]A.Ref, [0]Initial
    22.    
    23.        sdram_ctrlmod U1
    24.         (
    25.              .CLOCK( CLOCK ),
    26.              .RESET( RESET ),
    27.              .iCall( iCall ),            // < top ,[1]Write [0]Read
    28.              .oDone( oDone ),             // > top ,[1]Write [0]Read
    29.              .oCall( CallU1 ),           // > U4
    30.              .iDone( DoneU4 )          // < U4
    31.    
    32.         );
    33.         
    34.         wire [15:0]DataU2;
    35.         
    36.         fifo_savemod U2
    37.         ( 
    38.              .CLOCK( CLOCK ),
    39.                .RESET( RESET ),
    40.                .iEn( {iEn[1],EnU4[0]} ),      // < top
    41.                .iData( iData ),                 // < top
    42.                .oData( DataU2 ),             // > top
    43.                .oTag() // 
    44.         );
    45.         
    46.         fifo_savemod U3
    47.         ( 
    48.              .CLOCK( CLOCK ),
    49.                .RESET( RESET ),
    50.                .iEn( {EnU4[1],iEn[0]} ),     // < U4 & top
    51.                .iData( DataU4 ),             // < U4
    52.                .oData( oData ),             // > top
    53.                .oTag()                     // 
    54.         );
    55.         
    56.         wire DoneU4;
    57.         wire [1:0]EnU4;
    58.         wire [15:0]DataU4;
    59.         
    60.         sdram_funcmod U4
    61.         (
    62.             .CLOCK( CLOCK ),
    63.              .RESET( RESET ),
    64.              .S_CKE( S_CKE ),           // > top
    65.              .S_NCS( S_NCS ),           // > top
    66.              .S_NRAS( S_NRAS ),         // > top
    67.              .S_NCAS( S_NCAS ),         // > top
    68.              .S_NWE( S_NWE ),         // > top
    69.              .S_BA( S_BA ),           // > top
    70.              .S_A( S_A ),             // > top
    71.              .S_DQM( S_DQM ),         // > top
    72.              .S_DQ( S_DQ ),           // <> top        
    73.              .oEn( EnU4 ),            // > U2 && U3
    74.              .iAddr( iAddr ),           // < top
    75.              .iData( DataU2 ),              // < U2
    76.              .oData( DataU4 ),          // > top
    77.              .iCall( CallU1 ),            // < U1
    78.              .oDone( DoneU4 )          // > U1
    79.         );
    80.         
    81.    endmodule

    该组合模块的连线部署完全遵照图21.7。读者自己看着办吧。

    sdram_demo.v

    clip_image016

    图21.8 实验二十一的建模图。

    图21.8是实验二十一的建模图,内容上的改变也只有 En 多出来而已,不过核心操作的内容却有很大的改变。具体内容让我们来看代码吧。

    1.    module sdram_demo
    2.    (
    3.        input CLOCK,
    4.        input RESET,
    5.        output S_CLK,
    6.        output S_CKE, S_NCS, S_NRAS, S_NCAS, S_NWE,
    7.        output [12:0]S_A, 
    8.        output [1:0]S_BA,
    9.        output [1:0]S_DQM,
    10.        inout [15:0]S_DQ,
    11.        output TXD
    12.    ); 

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

    13.         wire CLOCK1,CLOCK2;
    14.         
    15.         pll_module U1
    16.         (
    17.                 .inclk0 ( CLOCK ), // 50Mhz
    18.                .c0 ( CLOCK1 ),  // 133Mhz -210 degree phase
    19.                .c1 ( CLOCK2 )   // 133Mhz 
    20.         );
    21.         

    以上内容为PLL模块的实例化。

    13.         wire CLOCK1,CLOCK2;
    14.         
    15.         pll_module U1
    16.         (
    17.                 .inclk0 ( CLOCK ), // 50Mhz
    18.                .c0 ( CLOCK1 ),  // 133Mhz -210 degree phase
    19.                .c1 ( CLOCK2 )   // 133Mhz 
    20.         );
    21.         
    22.         wire [1:0]DoneU2;
    23.         wire [15:0]DataU2;
    24.         wire [1:0]TagU2;
    25.         
    26.         sdram_basemod U2
    27.         (
    28.             .CLOCK( CLOCK1 ),
    29.             .RESET( RESET ),
    30.              .S_CKE( S_CKE ),
    31.              .S_NCS( S_NCS ),
    32.              .S_NRAS( S_NRAS ),
    33.              .S_NCAS( S_NCAS ),
    34.              .S_NWE( S_NWE ),
    35.              .S_A( S_A ),
    36.              .S_BA( S_BA ),
    37.              .S_DQM( S_DQM ),
    38.              .S_DQ( S_DQ ),
    39.              .iEn( isEn ), 
    40.              .iAddr( {D1,9’d0} ),
    41.              .iData( D2 ),
    42.              .oData( DataU2 ),
    43.              .oTag( TagU2 ),
    44.              .iCall( isCall ),
    45.              .oDone( DoneU2 )
    46.         );
    47.         

    以上内容为SDRAM基础模块的实例化。

    48.         parameter B115K2 = 11'd1157, TXFUNC = 6'd16;
    49.         
    50.         reg [5:0]i,Go;
    51.         reg [10:0]C1,C2;
    52.         reg [14:0]D1;
    53.         reg [15:0]D2,D3;
    54.         reg [10:0]T;
    55.         reg [1:0]isCall,isEn;
    56.         reg rTXD;
    57.         
    58.         always @ ( posedge CLOCK1 or negedge RESET )
    59.             if( !RESET )
    60.                 begin
    61.                           i <= 6'd0;
    62.                          Go <= 6'd0;
    63.                          C1 <= 11'd0;
    64.                          C2 <= 11'd0;
    65.                           D1 <= 15'd0;
    66.                          D2 <= 16'hA000;
    67.                          D3 <= 16'd0;
    68.                          T <= 11'd0;
    69.                          isCall <= 2'b00;
    70.                          isEn <= 2'b00;
    71.                          rTXD <= 1'b1;
    72.                 end
    73.             else 

    以上内容为相关的寄存器声明与复位操作。第48行是波特率为115200与伪函数入口的实例化。注意,由于本实验是页读写,所以 iAddr[8:0] 基本作废,所以D1也只有15位宽而已。然后 { D1,9’d0 } 联合驱动 iAddr。

    74.                 case( i )
    75.                        
    76.                         0:
    77.                         begin isEn[1] <= 1'b1; i <= i + 1'b1; end
    78.                         
    79.                         1:
    80.                         begin isEn[1] <= 1'b0; i <= i + 1'b1; end
    81.                         
    82.                         2:
    83.                         if( C2 == 511 ) begin C2 <= 11'd0; i <= i + 1'b1; end
    84.                         else begin D2[11:0] <= (D2[11:0] + 1'b1); C2 <= C2 + 1'b1; i <= 6'd0; end
    85.                        
    86.                         3:
    87.                         if( DoneU2[1] ) begin isCall[1] <= 1'b0; i <= i + 1'b1; end
    88.                         else begin isCall[1] <= 1'b1; D1 <= 15'd0; end
    89.                         
    90.                         4:
    91.                         if( DoneU2[0] ) begin  isCall[0] <= 1'b0; i <= i + 1'b1; end
    92.                         else begin isCall[0] <= 1'b1; D1 <= 15'd0; end
    93.                         
    94.                         5:
    95.                         begin isEn[0] <= 1'b1; i <= i + 1'b1; end
    96.                         
    97.                         6:
    98.                         begin D3 <= DataU2; isEn[0] <= 1'b0; i <= i + 1'b1; end
    99.                         
    100.                         7:
    101.                         begin T <= { 2'b11, D3[15:8], 1'b0 }; i <= TXFUNC; Go <= i + 1'b1; end
    102.                         
    103.                         8:
    104.                         begin T <= { 2'b11, D3[7:0], 1'b0 }; i <= TXFUNC; Go <= i + 1'b1; end
    105.                         
    106.                         9:
    107.                         if( C2 == 24'd511 ) begin C2 <= 11'd0; i <= i + 1'b1; end
    108.                         else begin C2 <= C2 + 1'b1; i <= 6'd5; end
    109.                                              
    110.                         10:
    111.                         i <= i;
    112.                         
    113.                        /******************************/
    114.

    以上内容为部分核心操作。步骤0~2是用来写满FIFO。步骤3将FIFO的内容写入SDRAM。步骤4又将内容读至FIFO。步骤5~9从FIFO读出内容,并且发送出去,直至512个数据读完为止。步骤10发呆。

    115.                          16,17,18,19,20,21,22,23,24,25,26:
    116.                         if( C1 == B115K2 -1 ) begin C1 <= 11'd0; i <= i + 1'b1; end
    117.                         else begin rTXD <= T[i - 16]; C1 <= C1 + 1'b1; end
    118.                         
    119.                         27:
    120.                         i <= Go;
    121.                         
    122.                endcase
    123.    
    124.          assign S_CLK = CLOCK2;
    125.          assign TXD = rTXD;
    126.    
    127.    endmodule

    以上内容为部分核心操作。步骤16~27是发送一帧数据的伪函数。第124~125行是相关输出驱动声明。综合完毕并且下载程序,如果串口调试软件出现数据 A000~A1FF 表示实验成功。

    细节一:完整的个体模块

    本实验的SDRAM基础模块已经准备就绪。

  • 相关阅读:
    Ubuntu18.04 环境下 解决VScode中空格长度减小的问题
    IPython notebook(Jupyter notebook) 设置密码
    IPython notebook(Jupyter notebook)指定IP和端口运行
    ubuntu系统下 vscode中如何指定conda环境
    《Bitcoin: A Peer-to-Peer Electronic Cash System》 中本聪写的比特币白皮书
    QT-vs各个版本的编译器号对应的vs版本号
    惯性导航的组成
    论文引用格式
    SCI正刊和特刊(专刊/增刊)的区别是什么?
    python glob.glob() 函数
  • 原文地址:https://www.cnblogs.com/alinx/p/4421438.html
Copyright © 2020-2023  润新知