天天看點

基于FPGA的SDRAM控制器設計(3)SDRAM工作狀态轉移圖SDRAM寫時序圖SDRAM的讀時序圖SDRAM寫子產品狀态機SDRAM的讀寫操作代碼SDRAM讀寫測試子產品SDRAM仿真測試參考文獻總結

基于FPGA的SDRAM讀寫子產品設計

  • SDRAM工作狀态轉移圖
  • SDRAM寫時序圖
  • SDRAM的讀時序圖
  • SDRAM寫子產品狀态機
  • SDRAM的讀寫操作代碼
  • SDRAM讀寫測試子產品
  • SDRAM仿真測試
  • 參考文獻
  • 總結

SDRAM工作狀态轉移圖

SDRAM晶片是按照一定的狀态時序操作的,是以我們為了更好的寫代碼需要清楚SDRAM的狀态轉移圖,如下:

基于FPGA的SDRAM控制器設計(3)SDRAM工作狀态轉移圖SDRAM寫時序圖SDRAM的讀時序圖SDRAM寫子產品狀态機SDRAM的讀寫操作代碼SDRAM讀寫測試子產品SDRAM仿真測試參考文獻總結

其中粗線是執行完該狀态後,不需要我們給什麼指令,自動轉移到下一個狀态,但是細線需要我們給出相應的指令。

這裡引用Kevin老師對其中一個過程的描述,從“IDLE”狀态跳到“WRITE”狀态的路線以及從“WRITE”狀态跳到“IDLE”狀态的路線:

IDLE 狀态到 WRITE 狀态:

  1. 在 IDLE 狀态需要先給 ACT 指令激活某一行, 此時處于 Row Active 狀态;
  2. 在 Row Active 狀态之後, 給 Write 指令則會進入 WRITE 狀态;
  3. 在 WRITE 狀态後, 再給一次 Write 指令, 就可以繼續寫入資料。

    WRITE 狀态到 IDLE 狀态:

  4. 在 WRITE 狀态給 PRE 指令, 則 SDRAM 将跳出 WRITE 狀态進入 Precharge

    狀态;

  5. 在 Precharge 狀态後, 就會自動進入 IDLE 狀态了

    要從 WRITE 狀态跳到 IDLE 狀态的一個原因是, 我們需要進行重新整理操作, 進入重新整理操作, 必須從 IDLE 狀态進入。我們注意到WRITE 狀态下邊還有一個 WRITEA 狀态,執行完一次WRITE狀态它會自動的進入到

    Precharge 狀态,是以比在 WRITE 狀态的工作效率要低很多,也比較少使用。

SDRAM寫時序圖

從SDRAM的技術手冊中我們很容易找到對應的寫時序圖,如下:

基于FPGA的SDRAM控制器設計(3)SDRAM工作狀态轉移圖SDRAM寫時序圖SDRAM的讀時序圖SDRAM寫子產品狀态機SDRAM的讀寫操作代碼SDRAM讀寫測試子產品SDRAM仿真測試參考文獻總結

上面對應的時間也可以從手冊中找到,在上兩篇關于SDRAM的文章中我們已經給出了每個時間的最小值。

SDRAM的讀時序圖

同理,我們給出SDRAM的讀時序圖,如下:

基于FPGA的SDRAM控制器設計(3)SDRAM工作狀态轉移圖SDRAM寫時序圖SDRAM的讀時序圖SDRAM寫子產品狀态機SDRAM的讀寫操作代碼SDRAM讀寫測試子產品SDRAM仿真測試參考文獻總結

這裡是給出讀延遲2個時鐘的時序圖,注意我們模式寄存器中設定的是三個時鐘延遲。同時,我們注意,***SDRAM的狀态轉移圖,讀寫操作是對稱的,讀寫的時序也是對稱的,是以我們在寫完寫操作之後可以利用notepad++的全部替換功能,替換一下寫子產品的名稱、寫操作的指令、删除寫子產品的資料端口并增加都子產品的使能端口,便可以把寫子產品轉換成讀子產品。***是以接下來我們将以寫子產品為例進行設計SDRAM的讀寫子產品。

同學們也可以對比上面兩個時序圖與SDRAM的狀态轉移圖會發現兩者完美契合。

SDRAM寫子產品狀态機

假設我們現在需要往 SDRAM 中寫入兩行資料, 那什麼時候可以退出仲裁狀

态機的寫狀态:

  1. 資料已經寫完;
  2. SDRAM 需要進行重新整理操作;
  3. 資料未寫完, 需要激活下一行繼續寫。

為什麼按照上面的條件進行設計SDRAM的寫子產品,因為上面的條件比較齊全,可以将我們所需要考慮的情況考慮的比較完善。

這裡也是使用了Kevin的狀态機,但是沒有按照該老師的時序圖設計,因為自我感覺有了狀态機再看時序圖反倒有些多餘。

基于FPGA的SDRAM控制器設計(3)SDRAM工作狀态轉移圖SDRAM寫時序圖SDRAM的讀時序圖SDRAM寫子產品狀态機SDRAM的讀寫操作代碼SDRAM讀寫測試子產品SDRAM仿真測試參考文獻總結

這裡雖然沒使用Kevin老師的時序圖,這裡也給出來,友善大家調試,對這裡有問題的同學可以參考該老師的教學視訊,但是還是支援大家自我完成該子產品的設計。

基于FPGA的SDRAM控制器設計(3)SDRAM工作狀态轉移圖SDRAM寫時序圖SDRAM的讀時序圖SDRAM寫子產品狀态機SDRAM的讀寫操作代碼SDRAM讀寫測試子產品SDRAM仿真測試參考文獻總結
基于FPGA的SDRAM控制器設計(3)SDRAM工作狀态轉移圖SDRAM寫時序圖SDRAM的讀時序圖SDRAM寫子產品狀态機SDRAM的讀寫操作代碼SDRAM讀寫測試子產品SDRAM仿真測試參考文獻總結

至此我們便完成了SDRAM的讀寫理論設計,接下來我們便完成該子產品的代碼設計。

SDRAM的讀寫操作代碼

SDRAM頂層子產品的設計,這裡包括仲裁子產品:

`timescale 1ns / 1ps
// *********************************************************************************
// Project Name : OSXXXX
// Author       : zhangningning
// Email        : [email protected]
// Website      : 
// Module Name  : sdram_top.v
// Create Time  : 2020-02-09 17:22:24
// Editor       : sublime text3, tab size (4)
// CopyRight(c) : All Rights Reserved
//
// *********************************************************************************
// Modification History:
// Date             By              Version                 Change Description
// -----------------------------------------------------------------------
// XXXX       zhangningning          1.0                        Original
//  
// *********************************************************************************

module sdram_top(
    //System Interfaces
    input                   sclk            ,
    input                   rst_n           ,
    //SDRAM Interfaces
    output  wire            sdram_clk       ,
    output  wire            sdram_cke       ,
    output  reg             sdram_cs_n      ,
    output  reg             sdram_cas_n     ,
    output  reg             sdram_ras_n     ,
    output  reg             sdram_we_n      ,
    output  reg     [ 1:0]  sdram_bank      ,
    output  reg     [11:0]  sdram_addr      ,
    output  wire    [ 1:0]  sdram_dqm       ,
    inout           [15:0]  sdram_dq        ,
    //Others
    input                   wr_trig         ,
    input                   rd_trig                 
);
 
//========================================================================================\
//**************Define Parameter and  Internal Signals**********************************
//========================================================================================/
localparam      NOP     =   4'b0111         ;

localparam      IDLE    =   5'b0_0001       ;
localparam      ARBIT   =   5'b0_0010       ;
localparam      AREF    =   5'b0_0100       ;
localparam      WRITE   =   5'b0_1000       ;
localparam      READ    =   5'b1_0000       ;
//sdram_init
wire                [ 3:0]  init_cmd        ;
wire                [11:0]  init_addr       ;
wire                        init_done       ;
//AREF
wire                        aref_req        ;
reg                         aref_en         ;
wire                        aref_end        ;
wire                [ 3:0]  aref_cmd        ;
wire                [11:0]  aref_addr       ;
//WRITE
wire                [ 3:0]  wr_cmd          ;
wire                [11:0]  wr_addr         ;
wire                [ 1:0]  wr_bank_addr    ;
wire                [15:0]  wr_data         ;    
reg                         wr_en           ;
wire                        wr_end          ;
wire                        wr_req          ;
//READ
wire                [ 3:0]  rd_cmd          ;
wire                [11:0]  rd_addr         ;
wire                [ 1:0]  rd_bank_addr    ;
reg                         rd_en           ;
wire                        rd_end          ;
wire                        rd_req          ;
wire                        rd_data_en      ;
//ARBIT
reg                 [ 4:0]  state           ;

//Others
reg                 [15:0]  sdram_dq1       ;
reg                         sdram_dq_en     ;
 
//========================================================================================\
//**************     Main      Code        **********************************
//========================================================================================/
assign      sdram_dqm       =       2'b00;
assign      sdram_clk       =       ~sclk;
assign      sdram_cke       =       1'b1;
assign      sdram_dq        =       sdram_dq_en == 1'b1 ? sdram_dq1 : 16'hzzzz;

always @(posedge sclk or negedge rst_n)
    if(rst_n == 1'b0)
        state       <=      IDLE;
    else case(state)
        IDLE    :   if(init_done == 1'b1)
                        state           <=      ARBIT;
                    else 
                        state           <=      state;
        ARBIT   :   if(aref_req == 1'b1)
                        state           <=      AREF;
                    else if(wr_req == 1'b1)
                        state           <=      WRITE;
                    else if(rd_req == 1'b1)
                        state           <=      READ;
                    else
                        state           <=      state;
        AREF    :   if(aref_end == 1'b1)
                        state           <=      ARBIT;
                    else
                        state           <=      state;
        WRITE   :   if(wr_end == 1'b1)
                        state           <=      ARBIT;
                    else 
                        state           <=      state;
        READ    :   if(rd_end == 1'b1)
                        state           <=      ARBIT;
                    else
                        state           <=      state;                        
        default :   state           <=      IDLE;
    endcase

always @(*)
    case(state)
        IDLE    :   begin
                        sdram_addr      =      init_addr;
                        {sdram_cs_n, sdram_ras_n, sdram_cas_n, sdram_we_n}      =       init_cmd;
                        sdram_dq_en     =       1'b0;
                    end
        AREF    :   begin
                        sdram_addr      =      aref_addr;
                        {sdram_cs_n, sdram_ras_n, sdram_cas_n, sdram_we_n}      =       aref_cmd;
                    end
        WRITE   :   begin
                        sdram_addr      =      wr_addr;
                        {sdram_cs_n, sdram_ras_n, sdram_cas_n, sdram_we_n}      =       wr_cmd;
                        sdram_dq1       =      wr_data;
                        sdram_bank      =      wr_bank_addr;
                        sdram_dq_en     =      1'b1;
                    end
        READ    :   begin
                        sdram_addr      =      rd_addr;
                        {sdram_cs_n, sdram_ras_n, sdram_cas_n, sdram_we_n}      =       rd_cmd;
                        sdram_bank      =      rd_bank_addr;
                        sdram_dq_en     =      1'b0;  
                    end
        default :   begin
                        sdram_addr      =      12'd0;
                        {sdram_cs_n, sdram_ras_n, sdram_cas_n, sdram_we_n}      =       NOP;
                        sdram_dq1       =      16'd0;
                        sdram_bank      =      2'b00;
                        sdram_dq_en     =      1'b0;
                    end
    endcase

always @(posedge sclk or negedge rst_n)
    if(rst_n == 1'b0)
        aref_en             <=      1'b0;
    else if(state == ARBIT && aref_req == 1'b1) 
        aref_en             <=      1'b1;
    else
        aref_en             <=      1'b0;

always @(posedge sclk or negedge rst_n)
    if(rst_n == 1'b0)
        wr_en               <=      1'b0;
    else if(state == ARBIT && aref_req == 1'b1) 
        wr_en               <=      1'b0;
    else if(state == ARBIT && wr_req == 1'b1) 
        wr_en               <=      1'b1;
    else
        wr_en               <=      1'b0;
        
always @(posedge sclk or negedge rst_n)
    if(rst_n == 1'b0)
        rd_en               <=      1'b0;
    else if(state == ARBIT && aref_req == 1'b1) 
        rd_en               <=      1'b0;
    else if(state == ARBIT && wr_req == 1'b1)
        rd_en               <=      1'b0;
    else if(state == ARBIT && rd_req == 1'b1)
        rd_en               <=      1'b1;
    else
        rd_en               <=      1'b0;

sdram_init sdram_init_inst(
    //System Interfaces
    .sclk                   (sclk                   ),
    .rst_n                  (rst_n                  ),
    //SDRAM Interfaces
    .sdram_cmd              (init_cmd               ),
    .sdram_addr             (init_addr              ),
    //Others
    .init_done              (init_done              )
);

sdram_aref sdram_aref_inst(
    //Sysytem Interfaces
    .sclk                   (sclk                   ),
    .rst_n                  (rst_n                  ),
    //SDRAM Interfaces
    .aref_cmd               (aref_cmd               ),
    .aref_addr              (aref_addr              ),
    //Others
    .aref_req               (aref_req               ),
    .aref_end               (aref_end               ),
    .aref_en                (aref_en                ),
    .init_done              (init_done              )
);

sdram_write sdram_write_inst(
    //System Interfaces
    .sclk                   (sclk                   ),
    .rst_n                  (rst_n                  ),
    //SDRAM Interfaces
    .wr_cmd                 (wr_cmd                 ),
    .wr_addr                (wr_addr                ),
    .bank_addr              (wr_bank_addr           ),
    .wr_data                (wr_data                ), 
    //Communication Interfaces
    .wr_trig                (wr_trig                ),
    .wr_en                  (wr_en                  ),
    .wr_end                 (wr_end                 ),
    .wr_req                 (wr_req                 ),
    .aref_req               (aref_req               )      
);

sdram_read sdram_read_inst(
    //System Interfaces
    .sclk                   (sclk                   ),
    .rst_n                  (rst_n                  ),
    //SDRAM Interfaces
    .rd_cmd                 (rd_cmd                 ),
    .rd_addr                (rd_addr                ),
    .bank_addr              (rd_bank_addr           ),    
    //Communication Interfaces
    .rd_trig                (rd_trig                ),
    .rd_en                  (rd_en                  ),
    .rd_end                 (rd_end                 ),
    .rd_req                 (rd_req                 ),
    .aref_req               (aref_req               ),
    .rd_data_en             (rd_data_en             )
);

 
endmodule

           

SDRAM上電初始化子產品:

`timescale 1ns / 1ps
// *********************************************************************************
// Project Name : OSXXXX
// Author       : zhangningning
// Email        : [email protected]
// Website      : 
// Module Name  : sdram_init.v
// Create Time  : 2020-02-09 16:20:31
// Editor       : sublime text3, tab size (4)
// CopyRight(c) : All Rights Reserved
//
// *********************************************************************************
// Modification History:
// Date             By              Version                 Change Description
// -----------------------------------------------------------------------
// XXXX       zhangningning          1.0                        Original
//  
// *********************************************************************************

module sdram_init(
    //System Interfaces
    input                   sclk            ,
    input                   rst_n           ,
    //SDRAM Interfaces
    output  reg     [ 3:0]  sdram_cmd       ,
    output  reg     [11:0]  sdram_addr      ,
    //Others
    output  reg             init_done  
);
 
//========================================================================================\
//**************Define Parameter and  Internal Signals**********************************
//========================================================================================/
localparam  DELAY_200US =  20000           ;
//SDRAM Command
localparam  NOP         =  4'b0111          ;
localparam  PRE         =  4'b0010          ;
localparam  AREF        =  4'b0001          ;
localparam  MSET        =  4'b0000          ;

reg                 [14:0]  cnt_200us       ;
reg                         flag_200us      ;
reg                 [ 4:0]  cnt_cmd         ;
 
//========================================================================================\
//**************     Main      Code        **********************************
//========================================================================================/
always @(posedge sclk or negedge rst_n)
    if(rst_n == 1'b0)
        cnt_200us       <=      11'd0;
    else if(flag_200us == 1'b0)
        cnt_200us       <=      cnt_200us + 1'b1;
    else
        cnt_200us       <=      cnt_200us;

always @(posedge sclk or negedge rst_n)
    if(rst_n == 1'b0)
        flag_200us      <=      1'b0;
    else if(cnt_200us >= DELAY_200US) 
        flag_200us      <=      1'b1;
    else
        flag_200us      <=      flag_200us;

always @(posedge sclk or negedge rst_n)
    if(rst_n == 1'b0)
        cnt_cmd         <=      5'd0;
    else if(flag_200us == 1'b1 && cnt_cmd <= 5'd19)
        cnt_cmd         <=      cnt_cmd + 1'b1;
    else 
        cnt_cmd         <=      cnt_cmd;

always @(posedge sclk or negedge rst_n)
    if(rst_n == 1'b0)
        sdram_cmd       <=      NOP;
    else case(cnt_cmd)
        1       :   sdram_cmd       <=      PRE;
        3       :   sdram_cmd       <=      AREF;
        11      :   sdram_cmd       <=      AREF;
        19      :   sdram_cmd       <=      MSET;
        default :   sdram_cmd       <=      NOP;
    endcase

always @(posedge sclk or negedge rst_n)
    if(rst_n == 1'b0)
        sdram_addr      <=      12'b0100_0000_0000;
    else if(cnt_cmd == 5'd19) 
        sdram_addr      <=      12'b0000_0011_0010;
    else
        sdram_addr      <=      12'b0100_0000_0000;

always @(posedge sclk or negedge rst_n)
    if(rst_n == 1'b0)
        init_done       <=      1'b0; 
    else if(cnt_cmd > 5'd19) 
        init_done       <=      1'b1;
    else
        init_done       <=      init_done;
        
endmodule

           

SDRAM自重新整理子產品:

`timescale 1ns / 1ps
// *********************************************************************************
// Project Name : OSXXXX
// Author       : zhangningning
// Email        : [email protected]
// Website      : 
// Module Name  : sdram_aref.v
// Create Time  : 2020-02-10 13:34:03
// Editor       : sublime text3, tab size (4)
// CopyRight(c) : All Rights Reserved
//
// *********************************************************************************
// Modification History:
// Date             By              Version                 Change Description
// -----------------------------------------------------------------------
// XXXX       zhangningning          1.0                        Original
//  
// *********************************************************************************

module sdram_aref(
    //Sysytem Interfaces
    input                   sclk            ,
    input                   rst_n           ,
    //SDRAM Interfaces
    output  reg     [ 3:0]  aref_cmd        ,
    output  wire    [11:0]  aref_addr       ,
    //Others
    input                   init_done       ,
    output  reg             aref_req        ,
    output  reg             aref_end        ,
    input                   aref_en         
);
 
//========================================================================================\
//**************Define Parameter and  Internal Signals**********************************
//========================================================================================/
localparam  NOP         =   4'b0111         ;
localparam  PRE         =   4'b0010         ;
localparam  AREF        =   4'b0001         ;
localparam  DELAY_15US  =   11'd1500        ;

reg                         aref_flag       ;
reg             [ 2:0]      cnt_cmd         ;
reg             [10:0]      cnt_15ms        ;

 
//========================================================================================\
//**************     Main      Code        **********************************
//========================================================================================/
assign  aref_addr           =       12'b0100_0000_0000;

always @(posedge sclk or negedge rst_n)
    if(rst_n == 1'b0)
        aref_flag           <=      1'b0; 
    else if(cnt_cmd >= 3'd7)
        aref_flag           <=      1'b0;
    else if(aref_en == 1'b1)
        aref_flag           <=      1'b1;
    else
        aref_flag           <=      aref_flag;

always @(posedge sclk or negedge rst_n)
    if(rst_n == 1'b0)
        cnt_cmd             <=      3'd0;
    else if(cnt_cmd >= 3'd7)
        cnt_cmd             <=      3'd0;
    else if(aref_flag == 1'b1)
        cnt_cmd             <=      cnt_cmd + 1'b1; 
    
always @(posedge sclk or negedge rst_n)
    if(rst_n == 1'b0)
        aref_cmd            <=      NOP;    
    else case(cnt_cmd)
        1       :   aref_cmd            <=      PRE;
        4       :   aref_cmd            <=      AREF;
        default :   aref_cmd            <=      NOP;
    endcase
   
always @(posedge sclk or negedge rst_n)
    if(rst_n == 1'b0)
        aref_end            <=      1'b0;
    else if(cnt_cmd >= 3'd7)
        aref_end            <=      1'b1;
    else
        aref_end            <=      1'b0;   
          
always @(posedge sclk or negedge rst_n)
    if(rst_n == 1'b0)
        cnt_15ms            <=      20'd0;
    else if(cnt_15ms == DELAY_15US)
        cnt_15ms            <=      20'd0;
    else if(init_done == 1'b1) 
        cnt_15ms            <=      cnt_15ms + 1'b1;
    else
        cnt_15ms            <=      20'd0;

always @(posedge sclk or negedge rst_n)
    if(rst_n == 1'b0)
        aref_req            <=      1'b0; 
    else if(cnt_15ms == DELAY_15US)
        aref_req            <=      1'b1;
    else if(aref_en == 1'b1)
        aref_req            <=      1'b0;
    else
        aref_req            <=      aref_req;
            

endmodule

           

SDRAM寫子產品:

`timescale 1ns / 1ps
// *********************************************************************************
// Project Name : OSXXXX
// Author       : zhangningning
// Email        : [email protected]
// Website      : 
// Module Name  : sdram_write.v
// Create Time  : 2020-02-10 20:05:26
// Editor       : sublime text3, tab size (4)
// CopyRight(c) : All Rights Reserved
//
// *********************************************************************************
// Modification History:
// Date             By              Version                 Change Description
// -----------------------------------------------------------------------
// XXXX       zhangningning          1.0                        Original
//  
// *********************************************************************************

module sdram_write(
    //System Interfaces
    input                   sclk            ,
    input                   rst_n           ,
    //SDRAM Interfaces
    output  reg     [ 3:0]  wr_cmd          ,
    output  reg     [11:0]  wr_addr         ,
    output  wire    [ 1:0]  bank_addr       ,
    output  reg     [15:0]  wr_data         ,     
    //Communication Interfaces
    input                   wr_trig         ,
    input                   wr_en           ,
    output  reg             wr_end          ,
    output  reg             wr_req          ,
    input                   aref_req        
);
 
//========================================================================================\
//**************Define Parameter and  Internal Signals**********************************
//========================================================================================/
// Define State
localparam  S_IDLE      =   5'b0_0001       ;
localparam  S_REQ       =   5'b0_0010       ;
localparam  S_ACT       =   5'b0_0100       ;
localparam  S_WR        =   5'b0_1000       ;
localparam  S_PRE       =   5'b1_0000       ;
// SDRAM Command
localparam  CMD_NOP     =   4'b0111         ;
localparam  CMD_PRE     =   4'b0010         ;
localparam  CMD_AREF    =   4'b0001         ;
localparam  CMD_ACT     =   4'b0011         ;
localparam  CMD_WR      =   4'b0100         ;

reg                 [ 4:0]  state           ;
reg                         flag_act_end    ;
reg                         row_end         ;
reg                 [ 1:0]  burst_cnt       ;
reg                         data_end        ;
reg                         flag_row_end    ;
reg                         flag_data_end   ;
reg                         flag_aref_req   ;
reg                 [ 8:0]  col_addr        ;
reg                 [ 1:0]  burst_cnt_r     ;
reg                 [11:0]  row_addr        ;
reg                         data_end_r      ;
reg                         data_end_r2     ;
reg                         row_end_r       ;
reg                         row_end_r2      ; 
reg                         flag_s_wr       ;


//========================================================================================\
//**************     Main      Code        **********************************
//========================================================================================/
assign      bank_addr       =       2'b00;


always @(posedge sclk or negedge rst_n)
    if(rst_n == 1'b0)
        state           <=      S_IDLE;
    else case(state)
        S_IDLE    :   if(wr_trig == 1'b1)
                        state           <=      S_REQ;
                    else
                        state           <=      state;                        
        S_REQ   :   if(wr_en == 1'b1)
                        state           <=      S_ACT;
                    else
                        state           <=      state;                        
        S_ACT   :   if(flag_act_end == 1'b1)
                        state           <=      S_WR;
                    else
                        state           <=      state;                        
        S_WR    :   if(data_end_r == 1'b1 || row_end_r == 1'b1)
                        state           <=      S_PRE;
                    else if(burst_cnt_r == 2'd2 && aref_req == 1'b1)
                        state           <=      S_PRE;
                    else
                        state           <=      state;                        
        S_PRE   :   if(flag_data_end == 1'b1)
                        state           <=      S_IDLE;
                    else if(flag_aref_req == 1'b1)
                        state           <=      S_REQ;
                    else if(flag_row_end == 1'b1)
                        state           <=      S_ACT;
                    else 
                        state           <=      state;
        default :   state           <=      S_IDLE;
    endcase
  
always @(posedge sclk or negedge rst_n)
    if(rst_n == 1'b0)
        flag_aref_req       <=      1'b0;
    else if(state == S_PRE && aref_req == 1'b1)
        flag_aref_req       <=      1'b1;
    else
        flag_aref_req       <=      1'b0; 

always @(posedge sclk or negedge rst_n)
    if(rst_n == 1'b0)
        flag_act_end    <=      1'b0;    
    else if(state == S_ACT && wr_cmd == CMD_ACT)
        flag_act_end    <=      1'b1;
    else 
        flag_act_end    <=      1'b0; 

always @(posedge sclk or negedge rst_n)
    if(rst_n == 1'b0)
        row_end         <=      1'b0;
    else if(col_addr == 9'd508 && burst_cnt == 2'd1)
        row_end         <=      1'b1;
    else
        row_end         <=      1'b0;

always @(posedge sclk)begin
    row_end_r   <=      row_end;
    row_end_r2  <=      row_end_r;
end
          
always @(posedge sclk or negedge rst_n)
    if(rst_n == 1'b0)
        burst_cnt       <=      2'd0;
    else if(state == S_WR)
        burst_cnt       <=      burst_cnt + 1'b1;
    else
        burst_cnt       <=      2'd0;

always @(posedge sclk)
    burst_cnt_r     <=      burst_cnt;

 always @(posedge sclk or negedge rst_n)
    if(rst_n == 1'b0)
        data_end    <=      1'b0;     
    else if(row_addr == 12'd1 && col_addr == 9'd508 && burst_cnt == 2'd1) 
        data_end    <=      1'b1;
    else
        data_end    <=      1'b0;
            
always @(posedge sclk)begin
    data_end_r      <=      data_end;
    data_end_r2     <=      data_end_r;  
end  

always @(posedge sclk or negedge rst_n)
    if(rst_n == 1'b0)
        flag_row_end    <=      1'b0;   
    else if(state == S_PRE && row_end_r2 == 1'b1)
        flag_row_end    <=      1'b1;
    else 
        flag_row_end    <=      1'b0;
         
always @(posedge sclk or negedge rst_n)
    if(rst_n == 1'b0)
        flag_data_end   <=      1'b0;  
    else if(state == S_PRE && data_end_r2 == 1'b1) 
        flag_data_end   <=      1'b1;
    else
        flag_data_end   <=      1'b0;
        
always @(posedge sclk or negedge rst_n)
    if(rst_n == 1'b0)
        col_addr        <=      12'd0;
    else if(state == S_WR && wr_cmd == CMD_WR && col_addr == 'd508)
        col_addr        <=      12'd0;   
    else if(state == S_WR && wr_cmd == CMD_WR)
        col_addr        <=      col_addr + 3'd4;
    else
        col_addr        <=      col_addr;

always @(posedge sclk or negedge rst_n)
    if(rst_n == 1'b0)
        row_addr        <=      12'd0;  
    else  if(data_end == 1'b1)
        row_addr        <=      12'd0;
    else if(state == S_WR && wr_cmd == CMD_WR && col_addr == 'd508)
        row_addr        <=      row_addr + 1'b1;
    else
        row_addr        <=      row_addr;
          
    
          
always @(posedge sclk or negedge rst_n)
    if(rst_n == 1'b0)
        wr_cmd          <=      CMD_NOP;    
    else case(state)
        S_ACT   :   if(wr_cmd != CMD_ACT && flag_act_end == 1'b0)
                        wr_cmd          <=      CMD_ACT;
                    else
                        wr_cmd          <=      CMD_NOP;
        S_WR    :   if(data_end == 1'b1 || row_end == 1'b1)
                        wr_cmd          <=      CMD_NOP;
                    else if(burst_cnt_r == 2'd3 && aref_req == 1'b1)
                        wr_cmd          <=      CMD_NOP;
                    else if(burst_cnt == 2'd0)
                        wr_cmd          <=      CMD_WR;
                    else
                        wr_cmd          <=      CMD_NOP; 
        S_PRE   :   if(wr_cmd != CMD_PRE)
                        wr_cmd          <=      CMD_PRE;
                    else 
                        wr_cmd          <=      CMD_NOP;
                
        default :   wr_cmd          <=      CMD_NOP;
    endcase

always @(posedge sclk or negedge rst_n)
    if(rst_n == 1'b0)
        wr_addr         <=      12'd0;    
    else case(state)
        S_ACT   :   if(wr_cmd != CMD_ACT)
                        wr_addr         <=      row_addr;
                    else
                        wr_addr         <=      12'd0;
        S_WR    :   if(data_end == 1'b1 || row_end == 1'b1)
                        wr_addr         <=      12'd0;
                    else if(burst_cnt_r == 2'd3 && aref_req == 1'b1)
                        wr_addr         <=      12'd0;
                    else if(burst_cnt == 2'd0)
                        wr_addr         <=      {3'b000,col_addr};
                    else
                        wr_addr         <=      12'd0; 
        S_PRE   :   wr_addr         <=      12'b0100_0000_0000;
        default :   wr_addr         <=      12'd0;
    endcase

always @(posedge sclk or negedge rst_n)
    if(rst_n == 1'b0)
        flag_s_wr       <=      1'b0;
    else if(state == S_WR) 
        flag_s_wr       <=      1'b1;
    else
        flag_s_wr       <=      1'b0;
        

always @(posedge sclk or negedge rst_n)
    if(rst_n == 1'b0)
        wr_data         <=      16'd0;    
    else if(flag_s_wr == 1'b1)
        wr_data         <=      wr_data + 1'b1;
    else
        wr_data         <=      wr_data;   

always @(posedge sclk or negedge rst_n)
    if(rst_n == 1'b0)
        wr_end          <=      1'b0;   
    else if(flag_data_end == 1'b1)
        wr_end          <=      1'b1;
    else if(state != S_IDLE && flag_aref_req == 1'b1 && wr_end == 1'b0)
        wr_end          <=      1'b1;
    else
        wr_end          <=      1'b0;
          
always @(posedge sclk or negedge rst_n)
    if(rst_n == 1'b0)
        wr_req          <=      1'b0;  
    else if(wr_en == 1'b1)
        wr_req          <=      1'b0;
    else if(state == S_REQ)
        wr_req          <=      1'b1;
    else
        wr_req          <=      wr_req;
          
endmodule
           

SDRAM讀子產品:

`timescale 1ns / 1ps
// *********************************************************************************
// Project Name : OSXXXX
// Author       : zhangningning
// Email        : [email protected]
// Website      : 
// Module Name  : sdram_read.v
// Create Time  : 2020-02-11 20:48:41
// Editor       : sublime text3, tab size (4)
// CopyRight(c) : All Rights Reserved
//
// *********************************************************************************
// Modification History:
// Date             By              Version                 Change Description
// -----------------------------------------------------------------------
// XXXX       zhangningning          1.0                        Original
//  
// *********************************************************************************

module sdram_read(
    //System Interfaces
    input                   sclk            ,
    input                   rst_n           ,
    //SDRAM Interfaces
    output  reg     [ 3:0]  rd_cmd          ,
    output  reg     [11:0]  rd_addr         ,
    output  wire    [ 1:0]  bank_addr       ,    
    //Communication Interfaces
    input                   rd_trig         ,
    input                   rd_en           ,
    output  reg             rd_end          ,
    output  reg             rd_req          ,
    input                   aref_req        ,
    output  reg             rd_data_en 
);
 
//========================================================================================\
//**************Define Parameter and  Internal Signals**********************************
//========================================================================================/
// Define State
localparam  S_IDLE      =   5'b0_0001       ;
localparam  S_REQ       =   5'b0_0010       ;
localparam  S_ACT       =   5'b0_0100       ;
localparam  S_RD        =   5'b0_1000       ;
localparam  S_PRE       =   5'b1_0000       ;
// SDRAM Command
localparam  CMD_NOP     =   4'b0111         ;
localparam  CMD_PRE     =   4'b0010         ;
localparam  CMD_AREF    =   4'b0001         ;
localparam  CMD_ACT     =   4'b0011         ;
localparam  CMD_RD      =   4'b0101         ;

reg                 [ 4:0]  state           ;
reg                         flag_act_end    ;
reg                         row_end         ;
reg                 [ 1:0]  burst_cnt       ;
reg                         data_end        ;
reg                         flag_row_end    ;
reg                         flag_data_end   ;
reg                         flag_aref_req   ;
reg                 [ 8:0]  col_addr        ;
reg                 [ 1:0]  burst_cnt_r     ;
reg                 [11:0]  row_addr        ;
reg                         data_end_r      ;
reg                         data_end_r2     ;
reg                         row_end_r       ;
reg                         row_end_r2      ; 
reg                 [ 3:0]  cnt_data        ;



//========================================================================================\
//**************     Main      Code        **********************************
//========================================================================================/
assign      bank_addr       =       2'b00;

always @(posedge sclk or negedge rst_n)
    if(rst_n == 1'b0)
        state           <=      S_IDLE;
    else case(state)
        S_IDLE    :   if(rd_trig == 1'b1)
                        state           <=      S_REQ;
                    else
                        state           <=      state;                        
        S_REQ   :   if(rd_en == 1'b1)
                        state           <=      S_ACT;
                    else
                        state           <=      state;                        
        S_ACT   :   if(flag_act_end == 1'b1)
                        state           <=      S_RD;
                    else
                        state           <=      state;                        
        S_RD    :   if(data_end_r == 1'b1 || row_end_r == 1'b1)
                        state           <=      S_PRE;
                    else if(burst_cnt_r == 2'd2 && aref_req == 1'b1)
                        state           <=      S_PRE;
                    else
                        state           <=      state;                        
        S_PRE   :   if(flag_data_end == 1'b1)
                        state           <=      S_IDLE;
                    else if(flag_aref_req == 1'b1)
                        state           <=      S_REQ;
                    else if(flag_row_end == 1'b1)
                        state           <=      S_ACT;
                    else 
                        state           <=      state;
        default :   state           <=      S_IDLE;
    endcase
  
always @(posedge sclk or negedge rst_n)
    if(rst_n == 1'b0)
        flag_aref_req       <=      1'b0;
    else if(state == S_PRE && aref_req == 1'b1)
        flag_aref_req       <=      1'b1;
    else
        flag_aref_req       <=      1'b0; 

always @(posedge sclk or negedge rst_n)
    if(rst_n == 1'b0)
        flag_act_end    <=      1'b0;    
    else if(state == S_ACT && rd_cmd == CMD_ACT)
        flag_act_end    <=      1'b1;
    else 
        flag_act_end    <=      1'b0; 

always @(posedge sclk or negedge rst_n)
    if(rst_n == 1'b0)
        row_end         <=      1'b0;
    else if(col_addr == 9'd508 && burst_cnt == 2'd1)
        row_end         <=      1'b1;
    else
        row_end         <=      1'b0;

always @(posedge sclk)begin
    row_end_r   <=      row_end;
    row_end_r2  <=      row_end_r;
end
          
always @(posedge sclk or negedge rst_n)
    if(rst_n == 1'b0)
        burst_cnt       <=      2'd0;
    else if(state == S_RD)
        burst_cnt       <=      burst_cnt + 1'b1;
    else
        burst_cnt       <=      2'd0;

always @(posedge sclk)
    burst_cnt_r     <=      burst_cnt;

 always @(posedge sclk or negedge rst_n)
    if(rst_n == 1'b0)
        data_end    <=      1'b0;     
    else if(row_addr == 12'd1 && col_addr == 9'd508 && burst_cnt == 2'd1) 
        data_end    <=      1'b1;
    else
        data_end    <=      1'b0;
            
always @(posedge sclk)begin
    data_end_r      <=      data_end;
    data_end_r2     <=      data_end_r;  
end  

always @(posedge sclk or negedge rst_n)
    if(rst_n == 1'b0)
        flag_row_end    <=      1'b0;   
    else if(state == S_PRE && row_end_r2 == 1'b1)
        flag_row_end    <=      1'b1;
    else 
        flag_row_end    <=      1'b0;
         
always @(posedge sclk or negedge rst_n)
    if(rst_n == 1'b0)
        flag_data_end   <=      1'b0;  
    else if(state == S_PRE && data_end_r2 == 1'b1) 
        flag_data_end   <=      1'b1;
    else
        flag_data_end   <=      1'b0;
        
always @(posedge sclk or negedge rst_n)
    if(rst_n == 1'b0)
        col_addr        <=      12'd0;
    else if(state == S_RD && rd_cmd == CMD_RD && col_addr == 'd508)
        col_addr        <=      12'd0;   
    else if(state == S_RD && rd_cmd == CMD_RD)
        col_addr        <=      col_addr + 3'd4;
    else
        col_addr        <=      col_addr;

always @(posedge sclk or negedge rst_n)
    if(rst_n == 1'b0)
        row_addr        <=      12'd0;  
    else  if(data_end == 1'b1)
        row_addr        <=      12'd0;
    else if(state == S_RD && rd_cmd == CMD_RD && col_addr == 'd508)
        row_addr        <=      row_addr + 1'b1;
    else
        row_addr        <=      row_addr;
          
    
          
always @(posedge sclk or negedge rst_n)
    if(rst_n == 1'b0)
        rd_cmd          <=      CMD_NOP;    
    else case(state)
        S_ACT   :   if(rd_cmd != CMD_ACT && flag_act_end == 1'b0)
                        rd_cmd          <=      CMD_ACT;
                    else
                        rd_cmd          <=      CMD_NOP;
        S_RD    :   if(data_end == 1'b1 || row_end == 1'b1)
                        rd_cmd          <=      CMD_NOP;
                    else if(burst_cnt_r == 2'd3 && aref_req == 1'b1)
                        rd_cmd          <=      CMD_NOP;
                    else if(burst_cnt == 2'd0)
                        rd_cmd          <=      CMD_RD;
                    else
                        rd_cmd          <=      CMD_NOP; 
        S_PRE   :   if(rd_cmd != CMD_PRE)
                        rd_cmd          <=      CMD_PRE;
                    else 
                        rd_cmd          <=      CMD_NOP;
                
        default :   rd_cmd          <=      CMD_NOP;
    endcase

always @(posedge sclk or negedge rst_n)
    if(rst_n == 1'b0)
        rd_addr         <=      12'd0;    
    else case(state)
        S_ACT   :   if(rd_cmd != CMD_ACT)
                        rd_addr         <=      row_addr;
                    else
                        rd_addr         <=      12'd0;
        S_RD    :   if(data_end == 1'b1 || row_end == 1'b1)
                        rd_addr         <=      12'd0;
                    else if(burst_cnt_r == 2'd3 && aref_req == 1'b1)
                        rd_addr         <=      12'd0;
                    else if(burst_cnt == 2'd0)
                        rd_addr         <=      {3'b000,col_addr};
                    else
                        rd_addr         <=      12'd0; 
        S_PRE   :   rd_addr         <=      12'b0100_0000_0000;
        default :   rd_addr         <=      12'd0;
    endcase

always @(posedge sclk or negedge rst_n)
    if(rst_n == 1'b0)
        rd_end          <=      1'b0;   
    else if(flag_data_end == 1'b1)
        rd_end          <=      1'b1;
    else if(state != S_IDLE && flag_aref_req == 1'b1 && rd_end == 1'b0)
        rd_end          <=      1'b1;
    else
        rd_end          <=      1'b0;
          
always @(posedge sclk or negedge rst_n)
    if(rst_n == 1'b0)
        rd_req          <=      1'b0;  
    else if(rd_en == 1'b1)
        rd_req          <=      1'b0;
    else if(state == S_REQ)
        rd_req          <=      1'b1;
    else
        rd_req          <=      rd_req;
          
always @(posedge sclk or negedge rst_n)
    if(rst_n == 1'b0)
        cnt_data        <=      4'd0;
    else if(cnt_data == 4'd7)
        cnt_data        <=      4'd0;
    else if(cnt_data >= 4'd1)
        cnt_data        <=      cnt_data + 1'b1;
    else if(rd_cmd == CMD_RD)
        cnt_data        <=      cnt_data + 1'b1;
    else
        cnt_data        <=      4'd0;

always @(posedge sclk or negedge rst_n)
    if(rst_n == 1'b0)
        rd_data_en      <=      1'b0;
    else if(cnt_data >= 4'd3 && cnt_data <= 4'd5)
        rd_data_en      <=      1'b1; 
    else
        rd_data_en      <=      1'b0;  

endmodule

           

觀察上述子產品很容易發現讀寫子產品中最重要的就是上面的那個狀态機。

SDRAM讀寫測試子產品

這裡給出頂層測試子產品的編寫:

`timescale 1ns / 1ps
`define CLOCK    10
// *********************************************************************************
// Project Name : OSXXXX
// Author       : zhangningning
// Email        : [email protected]
// Website      : 
// Module Name  : sdram_init_tb.v
// Create Time  : 2020-02-09 17:10:08
// Editor       : sublime text3, tab size (4)
// CopyRight(c) : All Rights Reserved
//
// *********************************************************************************
// Modification History:
// Date             By              Version                 Change Description
// -----------------------------------------------------------------------
// XXXX       zhangningning          1.0                        Original
//  
// *********************************************************************************

module sdram_init_tb;
reg                         sclk            ;
reg                         rst_n           ;
wire                        sdram_clk       ;
wire                        sdram_cke       ;
wire                        sdram_cs_n      ;
wire                        sdram_cas_n     ;
wire                        sdram_ras_n     ;
wire                        sdram_we_n      ;
wire    [ 1:0]              sdram_bank      ;
wire    [11:0]              sdram_addr      ;
wire    [ 1:0]              sdram_dqm       ;
wire    [15:0]              sdram_dq        ;
reg                         wr_trig         ;
reg                         rd_trig         ;

initial begin
    sclk        =       1'b0;
    rst_n       <=      1'b0;
    wr_trig     <=      1'b0;
    rd_trig     <=      1'b0;
    #(100*`CLOCK);
    rst_n       <=      1'b1;
    #(250_000)

    wr_trig     <=      1'b1;
    #(`CLOCK)
    wr_trig     <=      1'b0;
    #(20_000)
    rd_trig     <=      1'b1;
    #(`CLOCK)
    rd_trig     <=      1'b0;
    #(20_000);

    wr_trig     <=      1'b1;
    #(`CLOCK)
    wr_trig     <=      1'b0;
    #(20_000);
    rd_trig     <=      1'b1;
    #(`CLOCK)
    rd_trig     <=      1'b0;
    #(20_000);
      
    $stop;
end
always  #(`CLOCK/2)     sclk        =       ~sclk;  

sdram_top sdram_top_inst(
    //System Interfaces
    .sclk                   (sclk                   ),
    .rst_n                  (rst_n                  ),
    //SDRAM Interfaces
    .sdram_clk              (sdram_clk              ),
    .sdram_cke              (sdram_cke              ),
    .sdram_cs_n             (sdram_cs_n             ),
    .sdram_cas_n            (sdram_cas_n            ),
    .sdram_ras_n            (sdram_ras_n            ),
    .sdram_we_n             (sdram_we_n             ),
    .sdram_bank             (sdram_bank             ),
    .sdram_addr             (sdram_addr             ),
    .sdram_dqm              (sdram_dqm              ),
    .sdram_dq               (sdram_dq               ),
    //Others
    .wr_trig                (wr_trig                ),
    .rd_trig                (rd_trig                )
);

defparam        sdram_model_plus_inst.addr_bits =       12;
defparam        sdram_model_plus_inst.data_bits =       16;
defparam        sdram_model_plus_inst.col_bits  =       9;
defparam        sdram_model_plus_inst.mem_sizes =       2*1024*1024;            // 2M

sdram_model_plus sdram_model_plus_inst(
    .Dq                     (sdram_dq               ), 
    .Addr                   (sdram_addr             ), 
    .Ba                     (sdram_bank             ), 
    .Clk                    (sdram_clk              ), 
    .Cke                    (sdram_cke              ), 
    .Cs_n                   (sdram_cs_n             ), 
    .Ras_n                  (sdram_ras_n            ), 
    .Cas_n                  (sdram_cas_n            ), 
    .We_n                   (sdram_we_n             ), 
    .Dqm                    (sdram_dqm              ),
    .Debug                  (1'b1                   )
);

endmodule

           

這裡為了部落格的簡潔性不再給出相應的仿真模型檔案,同學們可以關注前幾篇SDRAM的部落格,也可以進群下載下傳相應的工程。

SDRAM仿真測試

這裡也不給出具體的仿真圖形,因為資料太長,截圖不友善,感興趣的同學可以自己仿真試試,但是這裡要給出一個之前所說奇怪的實驗現象,就是4突發的最後一個資料的有限長度比一個時鐘周期短的多:

基于FPGA的SDRAM控制器設計(3)SDRAM工作狀态轉移圖SDRAM寫時序圖SDRAM的讀時序圖SDRAM寫子產品狀态機SDRAM的讀寫操作代碼SDRAM讀寫測試子產品SDRAM仿真測試參考文獻總結

其實這裡隻需要下闆驗證一下,但是因為在家開發闆沒帶回來,也就無法驗證,這裡很有可能是仿真模型的問題。即使SDRAM就是有這個坑人的特性,我們也可以用SDRAM來捕捉讀資料,實在不行,我們還可以對sclk進行PLL移相操作。

參考文獻

[1]、開源騷客

總結

創作不易,認為文章有幫助的同學們可以關注點贊支援。(工程也都在群中)對文章有什麼看法或者需要更近一步交流的同學,可以加入下面的群:

基于FPGA的SDRAM控制器設計(3)SDRAM工作狀态轉移圖SDRAM寫時序圖SDRAM的讀時序圖SDRAM寫子產品狀态機SDRAM的讀寫操作代碼SDRAM讀寫測試子產品SDRAM仿真測試參考文獻總結