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


     

    经过漫长的战斗以后,我们终于来到最后。对于普通人而言,页读写就是一名战士的墓碑(最终战役) ... 然而,怕死的笔者想透过这个实验告诉读者,旅程的终点就是旅程的起点。一直以来,笔者都在烦恼“SDRAM是否应该成为储存类?”SDRAM作为一介储存资源(储存器),它的好处就是大容量空间,坏处则就是麻烦的控制规则,还有中规中矩的沟通速率。

    相比之下,片上内存无论是控制的难度,还是沟通的速率,它都远远领先SDRAM。俗语常说,愈是强力的资源愈是珍贵 ... 对此,片上内容的容量可谓是稀罕的程度。实验二十二的要求非常单纯:

    ”请问如何建立基于SDRAM储存资源的FIFO存储模块呢?“,笔者问道。

    clip_image002

    图22.1 SDRAM基础模块。

    图22.1是基于实验十八修改而成的SDRAM基础模块,修改对象除了SDRAM控制模块以外,SDRAM功能模块保持实验十八的状态,即单字读写。SDRAM控制模块,除了多出Tag以外,Addr的驱动也由该模块负责。具体的内容,让我们来看代码吧:

    1.    module sdram_ctrlmod
    2.    (
    3.        input CLOCK,
    4.        input RESET,
    5.        input [1:0]iCall, // [1]Write, [0]Read
    6.        output [1:0]oDone,
    7.        output [3:0]oCall,
    8.        input iDone,
    9.        output [23:0]oAddr,
    10.        output [1:0]oTag
    11.    );
    12.        parameter WRITE = 4'd1, READ = 4'd4, REFRESH = 4'd7, INITIAL = 4'd8;
    13.        parameter TREF = 11'd1040;
    14.        

    以上内容为相关的出入端声明以及常量。其中多了24位宽的oAddr与2位宽的oTag。

    15.         reg [1:0]C7;
    16.         reg [1:0]isDo;
    17.         
    18.         always @ ( posedge CLOCK or negedge RESET ) // sub
    19.             if( !RESET )
    20.                  begin
    21.                         C7 <= 2'b10;
    22.                         isDo <= 2'b00;
    23.                    end
    24.              else
    25.                  begin
    26.                    
    27.                        if( iCall[1] & C7[1] ) isDo[1] <= 1'b1;
    28.                        else if( iCall[0] & C7[0] ) isDo[0] <= 1'b1;
    29.                         
    30.                        if( isDo[1] & isDone[1] ) isDo[1] <= 1'b0;
    31.                        else if( isDo[0] & isDone[0] ) isDo[0] <= 1'b0;
    32.                    
    33.                        if( isDone ) C7 <= {isDo[0],isDo[1]};
    34.                        else if( iCall ) C7 <= { C7[0], C7[1] };
    35.                    
    36.                    end
    37.        

    以上内容为轮流协调的周边操作。具体内容与实验十七一样。

    38.        reg [3:0]i;
    39.        reg [10:0]C1;
    40.        reg [3:0]isCall; //[3]Write [2]Read [1]A.Refresh [0]Initial
    41.        reg [1:0]isDone;
    42.        reg [23:0]D1;
    43.        reg [24:0]C2,C3;  // N + 1
    44.        
    45.        always @ ( posedge CLOCK or negedge RESET ) // core
    46.            if( !RESET )
    47.                 begin
    48.                         i <= INITIAL;          // Initial SDRam at first 
    49.                         C1 <= 11'd0;
    50.                        isCall <= 4'b0000;
    51.                        isDone <= 2'b00;
    52.                        D1 <= 24'd0;
    53.                        C2 <= 25'd0;
    54.                        C3 <= 25'd0;
    55.                  end

    以上内容为相关的寄存器声明与复位操作。其中C2是写指针,C3是读指针,位宽为oAddr + 1。D用来驱动oAddr。

    56.             else 
    57.                 case( i )
    58.                  
    59.                        0: // IDLE
    60.                        if( C1 >= TREF ) begin C1 <= 11'd0;  i <= REFRESH; end
    61.                        else if( isDo[1] ) begin C1 <= C1 + 1'b1; i <= WRITE; end 
    62.                        else if( isDo[0] ) begin C1 <= C1 + 1'b1; i <= READ; end 
    63.                         else begin C1 <= C1 + 1'b1; end
    64.    
    65.                        /***********************/
    66.                        

    以上内容为部分核心内容。步骤0是待机状态,其中61~62行改为 isDo[1] 与 isDo[2]。

    67.                        1: //Write 
    68.                        if( iDone ) begin isCall[3] <= 1'b0; C1 <= C1 + 1'b1; i <= i + 1'b1; end
    69.                        else begin isCall[3] <= 1'b1; D1 <= C2[23:0]; C1 <= C1 + 1'b1; end
    70.                        
    71.                        2:
    72.                        begin C2 <= C2 + 1'b1; isDone[1] <= 1'b1; C1 <= C1 + 1'b1; i <= i + 1'b1; end
    73.                        
    74.                        3:
    75.                        begin isDone[1] <= 1'b0; C1 <= C1 + 1'b1; i <= 4'd0; end
    76.                        
    77.                        /***********************/
    78.                        

    以上内容为部分核心内容。步骤1~3是写操作,步骤1将C2[23:0]的内容赋值D。步骤2~3产生完成信号之余也递增C2.

    79.                        4: // Read
    80.                        if( iDone ) begin isCall[2] <= 1'b0; C1 <= C1 + 1'b1; i <= i + 1'b1; end
    81.                        else begin isCall[2] <= 1'b1; D1 <= C3[23:0]; C1 <= C1 + 1'b1; end
    82.                        
    83.                        5:
    84.                        begin C3 <= C3 + 1'b1; isDone[0] <= 1'b1; C1 <= C1 + 1'b1; i <= i + 1'b1; end
    85.                        
    86.                        6:
    87.                        begin isDone[0] <= 1'b0; C1 <= C1 + 1'b1; i <= 4'd0; end
    88.                        
    89.                        /***********************/
    90.                        

    以上内容为部分核心内容。步骤4~6是写操作,步骤4将C3[23:0]的内容赋值D。步骤5~7产生完成信号之余也递增C3.

    91.                        7: // Auto Refresh 
    92.                        if( iDone ) begin isCall[1] <= 1'b0; i <= 4'd0; end
    93.                        else begin isCall[1] <= 1'b1; end
    94.                        
    95.                        /***********************/
    96.                        
    97.                        8: // Initial 
    98.                        if( iDone ) begin isCall[0] <= 1'b0; i <= 4'd0; end
    99.                        else begin isCall[0] <= 1'b1; end
    100.                        
    101.                  endcase
    102.        

    以上内容为部分核心内容。步骤7~8保持不变。

    103.        assign oDone = isDone;
    104.        assign oCall = isCall;
    105.        assign oAddr = D1;
    106.        assign oTag[1] = ( C2[24]^C3[24] & C2[23:0] == C3[23:0] ); 
    107.         assign oTag[0] = ( C2 == C3 ); 
    108.        
    109.    endmodule

    以上内容为相关的输出驱动。D1驱动oAddr,第106~107行是写满状态与读空状态的声明。

    sdram_funcmod.v

    该功能模块与实验十八的内容一模一样。

    sdram_demo.v

    该组合模块的连线部署完全参照图22.1。

    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]iCall,
    13.         output [1:0]oDone,
    14.         output [1:0]oTag,
    15.         input [15:0]iData,
    16.         output [15:0]oData
    17.    ); 
    18.         wire [3:0]CallU1; // [3]Refresh, [2]Read, [1]Write, [0]Initial
    19.         wire [23:0]AddrU2;
    20.         
    21.        sdram_ctrlmod U1
    22.         (
    23.              .CLOCK( CLOCK ),
    24.              .RESET( RESET ),
    25.              .iCall( iCall ),       // < top ,[1]Write [0]Read
    26.              .oDone( oDone ),     // > top ,[1]Write [0]Read
    27.              .oAddr( AddrU2 ),    // > U2
    28.              .oTag( oTag ),        // > top
    29.              .oCall( CallU1 ),      // > U2 
    30.              .iDone( DoneU2 )           // < U2
    31.         );
    32.         
    33.         wire DoneU2;
    34.         
    35.         sdram_funcmod U2
    36.         (
    37.              .CLOCK( CLOCK ),
    38.              .RESET( RESET ),
    39.              .S_CKE( S_CKE ),           // > top
    40.              .S_NCS( S_NCS ),           // > top
    41.              .S_NRAS( S_NRAS ),         // > top
    42.              .S_NCAS( S_NCAS ),         // > top
    43.              .S_NWE( S_NWE ),         // > top
    44.              .S_BA( S_BA ),           // > top
    45.              .S_A( S_A ),             // > top
    46.              .S_DQM( S_DQM ),         // > top
    47.              .S_DQ( S_DQ ),           // <> top        
    48.              .iCall( CallU1 ),            // < U1
    49.              .oDone( DoneU2 ),          // > U1
    50.              .iAddr( AddrU2 ),          // < U1
    51.              .iData( iData ),               // < top
    52.              .oData( oData )           // > top
    53.         );
    54.         
    55.    endmodule

    连线内容请自己看着办吧。

    sdram_demo.v

    clip_image004

    图22.3 实验二十二的建模图。

    图22.3是实验二十二的建模图,左边的周边操作负责写入数据,右边的核心操作负责读取数据并且经由TXD发送出去。具体内容我们还是来看代码吧。

    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模块的实例化。

    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.              .iCall( {isWR,isRD} ),
    40.              .oDone( DoneU2 ),
    41.              .iData( D2 ),
    42.              .oData( DataU2 ),
    43.              .oTag( TagU2 )
    44.         );

    以上内容为SDRAM基础模块的实例化,注意第39行的iCall是由 isWR与isRD联合驱动。此外,第43行也多了oTag。

    46.         reg [5:0]i;
    47.         reg [15:0]D2;
    48.         reg isWR;
    49.         
    50.         always @ ( posedge CLOCK1 or negedge RESET )
    51.             if( !RESET )
    52.                 begin
    53.                           i <= 6'd0;
    54.                          D2 <= 16'hA000;
    55.                          isWR <= 1'b0;
    56.                 end
    57.             else 
    58.                 case( i )
    59.                 
    60.                          0:
    61.                         if( !TagU2[1] ) i <= i + 1'b1;
    62.                        
    63.                         1:
    64.                         if( DoneU2[1] ) begin isWR <= 1'b0; i <= i + 1'b1; end
    65.                         else begin isWR <= 1'b1; end
    66.                         
    67.                         2:
    68.                         if( D2 == 16'hA1FF ) i <= i + 1'b1;
    69.                         else begin D2[11:0] <= D2[11:0] +  1'b1; i <= 6'd0; end
    70.                         
    71.                         3:
    72.                         i <= i;
    73.                    
    74.                endcase
    75.                

    以上内容为写作用的周边操作。步骤0检测是否写满,步骤1写入数据,步骤2判断是否写满512次,不是的话就递增内容。步骤3是写完发呆。

    76.         reg [5:0]j,Go;
    77.         reg [10:0]C1;
    78.         reg [15:0]D3;
    79.         reg [10:0]T;
    80.         reg isRD;
    81.         reg rTXD;    
    82.         
    83.         parameter B115K2 = 11'd1157, TXFUNC = 6'd16;
    84.                
    85.        always @ ( posedge CLOCK1 or negedge RESET )
    86.             if( !RESET )
    87.                 begin
    88.                     j <= 6'd0;
    89.                          Go <= 6'd0;
    90.                          C1 <= 11'd0;
    91.                          D3 <= 16'd0;
    92.                          T <= 11'd0;
    93.                          isRD <= 1'b0;
    94.                          rTXD <= 1'b1;
    95.                 end
    96.             else 
    97.                 case( j )
    98.                 
    99.                         0:
    100.                         if( !TagU2[0] ) j <= j + 1'b1;
    101.                         
    102.                         1:
    103.                         if( DoneU2[0] ) begin D3 <= DataU2; isRD <= 1'b0; j <= j + 1'b1; end
    104.                         else begin isRD <= 1'b1; end
    105.                         
    106.                         2:
    107.                         begin T <= { 2'b11, D3[15:8], 1'b0 }; j <= TXFUNC; Go <= j + 1'b1; end
    108.                         
    109.                         3:
    110.                         begin T <= { 2'b11, D3[7:0], 1'b0 }; j <= TXFUNC; Go <= j + 1'b1; end
    111.                         
    112.                         4:
    113.                         if( D3 == 16'hA1FF ) j <= j + 1'b1; 
    114.                         else j <= 6'd0;
    115.                         
    116.                        5:
    117.                         j <= j;
    118.                         
    119.                        /******************************/
    120.        

    以上内容为部分核心操作。步骤0检测是否读空,步骤1读出数据,步骤2~3将数据发送数据,步骤4判断是否执行512次?如果不是的话就返回步骤0,是的话就递增i进入发呆的步骤5。

    121.                          16,17,18,19,20,21,22,23,24,25,26:
    122.                         if( C1 == B115K2 -1 ) begin C1 <= 11'd0; j <= j + 1'b1; end
    123.                         else begin rTXD <= T[j - 16]; C1 <= C1 + 1'b1; end
    124.                         
    125.                         27:
    126.                         j <= Go;
    127.                         
    128.                endcase
    129.    
    130.         assign S_CLK = CLOCK2;
    131.         assign TXD = rTXD;
    132.    
    133.    endmodule

    以上内容为核心操作以及输出驱动·。综合完毕便下载程序,如果串口调试软件出现 A000~A1FF,表示实验成功。

    细节一:完整的个体模块

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

    细节二:小谈储存类

    FIFO机制的SDRAM很可能实用性不强。相对低级建模II而言,SDRAM已经脱离死板的印象,任何畸形的储存方式,都有可能实现在任何储存资源之上。举例而言,片上内存储存资源可以实现FIFO功能,IIC储存资源也可以实现FIFO功能,SDRAM储存资源也可以实现FIFO功能。

    如果按照这条思路逆向思考的话,如果SDRAM储存资源可以实现功能C,那么IIC储存资源还有片上内存储存资源同样也可以实现功能C。如此一来,建模的自由度会大大增高,喜爱建模的孩子也会开心至极。如果读者是变态,那么一块小小的SDRAM储存资源实现多功能读写如:单字读写,多字读写,页读写,甚至FIFO读写,集于一身是有可能的。不管怎么样,实验二十二所要表达的信息已经非常清晰,即储存类的伸缩性与可塑性。

  • 相关阅读:
    Codeforces Round #625 (Div. 2, based on Technocup 2020 Final Round)(A-C题解)
    最小生成树
    并查集
    字符串的匹配算法
    CTU Open Contest 2019 AB题
    Codeforces Round #624 (Div. 3)(题解)
    Educational Codeforces Round 53 (Rated for Div. 2) (前五题题解)
    Lyft Level 5 Challenge 2018
    终于在博客写完第一篇题解啦
    Codeforces Round #519 by Botan Investments(前五题题解)
  • 原文地址:https://www.cnblogs.com/alinx/p/4447037.html
Copyright © 2020-2023  润新知