AX7A200教程(3): DDR3突发读写

上一个章节我们新建工程,然后进行基本的初始化操作,这个章节将在上个工程的基础上进行突发读写

因ddr3读写部分控制信号比较多,所以ddr3读写控制模块比较复杂,本章节着重于一个256位数据的突发读写,ddr读写控制模块暂不引出行复位部分,简化了ddr读写控制模块也让各种童鞋理解更清晰。

因本章的工程是在上一篇博客的基础上进行改进的,加入了ddr读写控制模块,和突发读写模块,所以务必按照我博客的顺序来看

ddr3突发读写工程顶层

`timescale 1ns / 1ps
//
// Company: 
// Engineer: 
// 
// Create Date: 2023/01/20 20:24:41
// Design Name: 
// Module Name: ddr3_top
// Project Name: 
// Target Devices: 
// Tool Versions: 
// Description: 
// 
// Dependencies: 
// 
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
// 
//


module ddr3_top(
    //clock
    input               sys_clk_i,//200M
    //reset
    input               sys_rst,
   // Inouts
   inout [31:0]         ddr3_dq,
   inout [3:0]          ddr3_dqs_n,
   inout [3:0]          ddr3_dqs_p,
   // Outputs
   output [14:0]        ddr3_addr,
   output [2:0]         ddr3_ba,
   output               ddr3_ras_n,
   output               ddr3_cas_n,
   output               ddr3_we_n,
   output               ddr3_reset_n,
   output [0:0]         ddr3_ck_p,
   output [0:0]         ddr3_ck_n,
   output [0:0]         ddr3_cke,
   output [0:0]         ddr3_cs_n,   
   output [3:0]         ddr3_dm,
   output [0:0]         ddr3_odt
   
    );

  ddr_test ddr_test_inst (
    // Memory interface ports
    .ddr3_addr                      (ddr3_addr),  // output [14:0]        ddr3_addr
    .ddr3_ba                        (ddr3_ba),  // output [2:0]        ddr3_ba
    .ddr3_cas_n                     (ddr3_cas_n),  // output            ddr3_cas_n
    .ddr3_ck_n                      (ddr3_ck_n),  // output [0:0]        ddr3_ck_n
    .ddr3_ck_p                      (ddr3_ck_p),  // output [0:0]        ddr3_ck_p
    .ddr3_cke                       (ddr3_cke),  // output [0:0]        ddr3_cke
    .ddr3_ras_n                     (ddr3_ras_n),  // output            ddr3_ras_n
    .ddr3_reset_n                   (ddr3_reset_n),  // output            ddr3_reset_n
    .ddr3_we_n                      (ddr3_we_n),  // output            ddr3_we_n
    .ddr3_dq                        (ddr3_dq),  // inout [31:0]        ddr3_dq
    .ddr3_dqs_n                     (ddr3_dqs_n),  // inout [3:0]        ddr3_dqs_n
    .ddr3_dqs_p                     (ddr3_dqs_p),  // inout [3:0]        ddr3_dqs_p
    .ddr3_cs_n                      (ddr3_cs_n),  // output [0:0]        ddr3_cs_n
    .ddr3_dm                        (ddr3_dm),  // output [3:0]        ddr3_dm
    .ddr3_odt                       (ddr3_odt),  // output [0:0]        ddr3_odt
    // System Clock Ports
    .sys_clk_i                      (sys_clk_i),
    .sys_rst                        (sys_rst) // input sys_rst

    );     
 
endmodule

ddr3测试模块(测试模块包含:mig控制器,ddr3读写控制模块,ddr3突发读写模块)

`timescale 1ns / 1ps
//
// Company: 
// Engineer: 
// 
// Create Date: 2023/01/21 18:58:16
// Design Name: 
// Module Name: ddr_test
// Project Name: 
// Target Devices: 
// Tool Versions: 
// Description: 
// 
// Dependencies: 
// 
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
// 
//


module ddr_test(
   input                sys_clk_i,//200M
   //reset
   input                sys_rst,
   // Inouts
   inout [31:0]         ddr3_dq,
   inout [3:0]          ddr3_dqs_n,
   inout [3:0]          ddr3_dqs_p,
   // Outputs
   output [14:0]        ddr3_addr,
   output [2:0]         ddr3_ba,
   output               ddr3_ras_n,
   output               ddr3_cas_n,
   output               ddr3_we_n,
   output               ddr3_reset_n,
   output [0:0]         ddr3_ck_p,
   output [0:0]         ddr3_ck_n,
   output [0:0]         ddr3_cke,
   output [0:0]         ddr3_cs_n,   
   output [3:0]         ddr3_dm,
   output [0:0]         ddr3_odt
   
    );
    
  wire [28:0]                           app_addr;               //DDR3地址
  wire [2:0]                            app_cmd;                //MIG IP核操作命令,读或者写
  wire                                  app_en;                 //MIG IP发送命令使能
  wire                                  app_rdy;                //MIG 命令接收准备好标志
  wire [255:0]                          app_rd_data;            //用户读数据
  wire                                  app_rd_data_end;        //突发读当前时钟最后一个数据
  wire                                  app_rd_data_valid;      //读数据有效
  wire [255:0]                          app_wdf_data;           //用户写数据
  wire                                  app_wdf_end;            //突发写当前时钟最后一个数据
  wire [31:0]                           app_wdf_mask;
  wire                                  app_wdf_rdy;            //MIG数据接收准备好
  wire                                  app_sr_active;
  wire                                  app_ref_ack;
  wire                                  app_zq_ack;
  wire                                  app_wdf_wren;    
  wire                                  ui_clk;                //用户时钟
  wire                                  ui_clk_sync_rst;       //复位,高有效
  wire [24:0]                           ddr_addr_max;
  
  //mig控制器  
  mig_7series_0 u_mig_7series_0 (
    // Memory interface ports
    .ddr3_addr                      (ddr3_addr),  // output [14:0]        ddr3_addr
    .ddr3_ba                        (ddr3_ba),  // output [2:0]        ddr3_ba
    .ddr3_cas_n                     (ddr3_cas_n),  // output            ddr3_cas_n
    .ddr3_ck_n                      (ddr3_ck_n),  // output [0:0]        ddr3_ck_n
    .ddr3_ck_p                      (ddr3_ck_p),  // output [0:0]        ddr3_ck_p
    .ddr3_cke                       (ddr3_cke),  // output [0:0]        ddr3_cke
    .ddr3_ras_n                     (ddr3_ras_n),  // output            ddr3_ras_n
    .ddr3_reset_n                   (ddr3_reset_n),  // output            ddr3_reset_n
    .ddr3_we_n                      (ddr3_we_n),  // output            ddr3_we_n
    .ddr3_dq                        (ddr3_dq),  // inout [31:0]        ddr3_dq
    .ddr3_dqs_n                     (ddr3_dqs_n),  // inout [3:0]        ddr3_dqs_n
    .ddr3_dqs_p                     (ddr3_dqs_p),  // inout [3:0]        ddr3_dqs_p
    .init_calib_complete            (init_calib_complete),  // output    init_calib_complete
    .ddr3_cs_n                      (ddr3_cs_n),  // output [0:0]        ddr3_cs_n
    .ddr3_dm                        (ddr3_dm),  // output [3:0]        ddr3_dm
    .ddr3_odt                       (ddr3_odt),  // output [0:0]        ddr3_odt
    // Application interface ports
    .app_addr                       (app_addr),  // input [28:0]        app_addr
    .app_cmd                        (app_cmd),  // input [2:0]        app_cmd
    .app_en                         (app_en),  // input                app_en
    .app_wdf_data                   (app_wdf_data),  // input [255:0]        app_wdf_data
    .app_wdf_end                    (app_wdf_end),  // input                app_wdf_end
    .app_wdf_wren                   (app_wdf_wren),  // input                app_wdf_wren
    .app_rd_data                    (app_rd_data),  // output [255:0]        app_rd_data
    .app_rd_data_end                (app_rd_data_end),  // output            app_rd_data_end
    .app_rd_data_valid              (app_rd_data_valid),  // output       app_rd_data_valid
    .app_rdy                        (app_rdy),  // output            app_rdy
    .app_wdf_rdy                    (app_wdf_rdy),  // output            app_wdf_rdy
    .app_sr_req                     (1'b0),  // input            app_sr_req
    .app_ref_req                    (1'b0),  // input            app_ref_req
    .app_zq_req                     (1'b0),  // input            app_zq_req
    .app_sr_active                  (app_sr_active),  // output            app_sr_active
    .app_ref_ack                    (app_ref_ack),  // output            app_ref_ack
    .app_zq_ack                     (app_zq_ack),  // output            app_zq_ack
    .ui_clk                         (ui_clk),  // output            ui_clk
    .ui_clk_sync_rst                (ui_clk_sync_rst),  // output    ui_clk_sync_rst
    .app_wdf_mask                   (32'd0),  // input [31:0]        app_wdf_mask
    // System Clock Ports
    .sys_clk_i                      (sys_clk_i),
    .sys_rst                        (sys_rst) // input sys_rst

    ); 
    
  assign ddr_addr_max = 32-8;   
 //读写控制模块   
 ddr3_rw_control  ddr3_rw_control_inst (              
    .ui_clk                         (ui_clk),   //用户时钟
    .ui_clk_sync_rst                (ui_clk_sync_rst),   //复位,高有效
    .init_calib_complete            (init_calib_complete),   //DDR3初始化完成
    .app_rdy                        (app_rdy),   //MIG 命令接收准备好标致
    .app_wdf_rdy                    (app_wdf_rdy),   //MIG数据接收准备好
    .app_rd_data_valid              (app_rd_data_valid),   //读数据有效
    .app_addr                       (app_addr),   //DDR3地址                      
    .app_en                         (app_en),   //MIG IP发送命令使能
    .app_wdf_wren                   (app_wdf_wren),   //用户写数据使能
    .app_wdf_end                    (app_wdf_end),   //突发写当前时钟最后一个数据 
    .app_cmd                        (app_cmd),   //MIG IP核操作命令,读或者写
    //用户接口
    .ddr_addr_max                   (ddr_addr_max),
    .wr_len_en                      (wr_len_en),
    .rd_len_en                      (rd_len_en),
    .wr_len_done                    (wr_len_done),
    .rd_len_done                    (rd_len_done),
    .ddr_wr_end                     (ddr_wr_end),
    .ddr_rd_end                     (ddr_rd_end)

     );     

//突发读写
 burst_rw    burst_rw_inst(
    .ui_clk                         (ui_clk),
    .i_rst_n                        (!ui_clk_sync_rst),
    .init_calib_complete            (init_calib_complete),
    //突发读写控制信号
    .wr_len_en                      (wr_len_en),//突发写使能
    .rd_len_en                      (rd_len_en),//突发读使能
    .wr_len_done                    (wr_len_done),//突发写完成
    .rd_len_done                    (rd_len_done),//突发读完成
    .ddr_wr_end                     (ddr_wr_end),//ddr写结束
    .ddr_rd_end                     (ddr_rd_end),//ddr读结束
    //读写数据接口
    .app_wdf_wren                   (app_wdf_wren),//ddr写有效
    .app_wdf_data                   (app_wdf_data),//ddr写入的数据
    .app_rd_data_valid              (app_rd_data_valid),//ddr读有效
    .app_rd_data                    (app_rd_data)//ddr读出的数据
    
    );    
        
endmodule

ddr3读写控制模块

`timescale 1ns / 1ps
//
// Company: 
// Engineer: 
// 
// Create Date: 2023/01/22 08:27:58
// Design Name: 
// Module Name: ddr3_rw_control
// Project Name: 
// Target Devices: 
// Tool Versions: 
// Description: 
// 
// Dependencies: 
// 
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
// 
//


 module ddr3_rw_control (              
     input                    ui_clk,                //用户时钟
     input                    ui_clk_sync_rst,       //复位,高有效
     input                    init_calib_complete,   //DDR3初始化完成
     input                    app_rdy,               //MIG 命令接收准备好标致
     input                    app_wdf_rdy,           //MIG数据接收准备好
     input                    app_rd_data_valid,     //读数据有效
     output         [27:0]    app_addr,              //DDR3地址                      
     output                   app_en,                //MIG IP发送命令使能
     output                   app_wdf_wren,          //用户写数据使能
     output                   app_wdf_end,           //突发写当前时钟最后一个数据 
     output          [2:0]    app_cmd,               //MIG IP核操作命令,读或者写
     output reg     [2 :0]    state,                 //读写状态
     output reg     [23:0]    rd_addr_cnt,           //用户读地址计数
     output reg     [23:0]    wr_addr_cnt,           //用户写地址计数
     input          [24:0]    ddr_addr_max,
     input                     wr_len_en,
     input                     rd_len_en,
     output                    wr_len_done,
     output                    rd_len_done,
     output reg               ddr_wr_end,
     output reg               ddr_rd_end,
     input                     vin_vs,
     input                     vout_vs,
     output  reg               wr_reset, 
     output  reg               rd_reset
     );
 
 //parameter define
 parameter  WRITE_LENGTH = 4;
 parameter  READ_LENGTH = 4;
 parameter  IDLE        = 3'd0;            //空闲状态
 parameter  WRITE       = 3'd1;            //写状态
 parameter  WRITE_DONE  = 3'd2;            //读到写过度等待
 parameter  READ        = 3'd3;            //读状态
 parameter  READ_DONE   = 3'd4;
 
 wire         rst_n;     //复位,低有效
 
 reg   [27:0]   app_addr_wr;
 reg   [27:0]   app_addr_rd;
 reg  [2:0]     wr_page;
 reg  [2:0]     rd_page;
 reg   [15:0]   wr_load_r;
 reg   [15:0]   rd_load_r;



  //*****************************************************
 //**                    main code
 //***************************************************** 
 
 assign rst_n = ~ui_clk_sync_rst;
 
 //在写状态MIG IP 命令接收和数据接收都准备好,或者在读状态命令接收准备好,此时拉高使能信号,
 assign app_en = ((state == WRITE && (app_rdy && app_wdf_rdy))
                 ||(state == READ && app_rdy)) ? 1'b1:1'b0;
                 
 //在写状态,命令接收和数据接收都准备好,此时拉高写使能
 assign app_wdf_wren = (state == WRITE && (app_rdy && app_wdf_rdy)) ? 1'b1:1'b0;
 
 //由于DDR3芯片时钟和用户时钟的分频选择4:1,突发长度为8,故两个信号相同
 assign app_wdf_end = app_wdf_wren; 
 
 //处于读的时候命令值为1,其他时候命令值为0
 assign app_cmd = (state == READ) ? 3'd1 :3'd0;  
 
 assign app_addr = (state == WRITE && (app_rdy && app_wdf_rdy))? {wr_page,app_addr_wr[24:0]}:{rd_page,app_addr_rd[24:0]};  
  
 assign wr_len_done =  ((state == WRITE_DONE) && wr_len_en)? 1'b1:1'b0;
 
 assign rd_len_done =  ((state == READ_DONE) && rd_len_en)? 1'b1:1'b0;
 

always @(posedge ui_clk or negedge rst_n) begin
        if(!rst_n)
            ddr_wr_end    <=  1'b0;
        else if(wr_reset)
            ddr_wr_end    <=  1'b0;    
        else if((state == WRITE_DONE) && app_addr_wr == ddr_addr_max)
            ddr_wr_end    <=  1'b1;
end

always @(posedge ui_clk or negedge rst_n) begin
        if(!rst_n)
            ddr_rd_end    <=  1'b0;
        else if(rd_reset)
            ddr_rd_end    <=  1'b0;    
        else if((state == READ_DONE) && app_addr_rd == ddr_addr_max)
            ddr_rd_end    <=  1'b1;
end


always@(posedge ui_clk or negedge rst_n)begin
        if(!rst_n)
            wr_load_r   <=  1'd0;
        else 
            wr_load_r   <=  {wr_load_r[14:0],vin_vs};
end 

always @(posedge ui_clk or negedge rst_n) begin
         if(!rst_n)
             wr_page    <=  3'd0;
         else if(wr_load_r[0] && !wr_load_r[14])
             wr_page    <=  wr_page + 1'b1;
end
      
always@(posedge ui_clk or negedge rst_n)begin
        if(!rst_n)
            wr_reset   <=  1'd0;
        else if(wr_load_r[0] && !wr_load_r[14])
            wr_reset   <=  1'd1;
        else if(app_addr_wr == 0 && !(wr_load_r[0] && !wr_load_r[14]))    
            wr_reset   <=  1'd0;
end



always@(posedge ui_clk or negedge rst_n)begin
        if(!rst_n)
            rd_load_r   <=  1'd0;
        else 
            rd_load_r   <=  {rd_load_r[14:0],vout_vs};
end

always @(posedge ui_clk or negedge rst_n) begin
         if(!rst_n)
             rd_page    <=  3'd0;
         else if(rd_load_r[0] && !rd_load_r[14])
             rd_page    <=  wr_page - 1'b1;
end 
       
always@(posedge ui_clk or negedge rst_n)begin
        if(!rst_n)
            rd_reset   <=  1'd0;
        else if(rd_load_r[0] && !rd_load_r[14])
            rd_reset   <=  1'd1;
        else if(app_addr_rd == 0 && !(rd_load_r[0] && !rd_load_r[14]))    
            rd_reset   <=  1'd0;
end 
                       

              
 //DDR3读写逻辑实现
 always @(posedge ui_clk or negedge rst_n) begin
     if(!rst_n) begin 
         state    <= IDLE;             
         wr_addr_cnt  <= 24'd0;      
         rd_addr_cnt  <= 24'd0;       
         app_addr_wr <= 25'd0;
         app_addr_rd <= 25'd0;         
     end
     else if(init_calib_complete)begin               //MIG IP核初始化完成
         case(state)
             IDLE:
                 if(rd_reset)begin
                        rd_addr_cnt  <= 24'd0;                        
                        app_addr_rd  <= 25'd0;
                        end
                 else if(wr_reset)begin
                        wr_addr_cnt  <= 24'd0;
                        app_addr_wr  <= 25'd0;
                        end
                 else if(rd_len_en)begin
                        state       <= READ;      
                        rd_addr_cnt <= 24'd0;
                        end              
                 else if(wr_len_en)begin
                        state       <= WRITE; 
                        wr_addr_cnt <= 24'd0;                 
                        end
                 else
                        state    <= IDLE;                         
             WRITE:begin
                 if(wr_addr_cnt == WRITE_LENGTH - 1 &&(app_rdy && app_wdf_rdy))
                     state    <= WRITE_DONE;                  //写到设定的长度跳到等待状态
                 else if(app_rdy && app_wdf_rdy)begin   //写条件满足
                     wr_addr_cnt  <= wr_addr_cnt + 1;   //写地址自加
                     app_addr_wr     <= app_addr_wr + 8;      //DDR3 地址加8
                 end
                 else begin                             //写条件不满足,保持当前值
                     wr_addr_cnt  <= wr_addr_cnt;
                     app_addr_wr  <= app_addr_wr; 
                 end
               end
             WRITE_DONE:begin   
                if(app_addr_wr == ddr_addr_max)begin               
                        app_addr_wr <=  25'd0;
                        wr_addr_cnt  <= 24'd0;  
                        state   <= IDLE;       
                        end
                else begin
                       app_addr_wr <=  app_addr_wr + 8;
                       wr_addr_cnt  <= 24'd0;
                       state   <= IDLE;
                       end                                                 
               end
             READ:begin                               //读到设定的地址长度    
                 if(rd_addr_cnt == READ_LENGTH - 1 && app_rdy)begin
                     state   <= READ_DONE; 
                     end                  //则跳到空闲状态 
                 else if(app_rdy)begin                  //若MIG已经准备好,则开始读
                     rd_addr_cnt <= rd_addr_cnt + 1'd1; //用户地址每次加一
                     app_addr_rd    <= app_addr_rd + 8;       //DDR3地址加8
                 end
                 else begin                             //若MIG没准备好,则保持原值
                     rd_addr_cnt <= rd_addr_cnt;
                     app_addr_rd <= app_addr_rd; 
                 end
               end
             READ_DONE:begin
                  if(app_addr_rd == ddr_addr_max)begin
                       app_addr_rd <=  25'd0;
                       rd_addr_cnt  <= 24'd0;
                       state   <= IDLE;   
                       end
                  else begin
                       app_addr_rd <=  app_addr_rd + 8;
                       rd_addr_cnt  <= 24'd0;
                       state   <= IDLE;
                       end 
               end                 
             default:begin
                 state    <= IDLE;
                 wr_addr_cnt  <= 24'd0;
                 rd_addr_cnt  <= 24'd0;
                 app_addr_wr  <= 25'd0;
                 app_addr_rd  <= 25'd0;
             end
         endcase
     end
 end   
                          
 
 endmodule

ddr3突发读写模块

`timescale 1ns / 1ps
//
// Company: 
// Engineer: 
// 
// Create Date: 2023/01/21 20:00:10
// Design Name: 
// Module Name: burst_rw
// Project Name: 
// Target Devices: 
// Tool Versions: 
// Description: 
// 
// Dependencies: 
// 
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
// 
//


module burst_rw(
input               ui_clk,
input               i_rst_n,
input               init_calib_complete,
output reg         wr_len_en,//突发写使能
output reg         rd_len_en,//突发读使能
input               wr_len_done,//突发写完成
input               rd_len_done,//突发读完成
input               ddr_wr_end,//ddr写结束
input               ddr_rd_end,//ddr读结束
input               app_wdf_wren,//ddr写有效
output  [255:0]     app_wdf_data,//ddr写入的数据
input               app_rd_data_valid,//ddr读有效
input   [255:0]     app_rd_data//ddr读出的数据

    );
    reg     [5:0]       count;
    reg     [255:0]     ddr3_w_data;
    wire    [255:0]     ddr3_read_in;
        
    
    assign app_wdf_data = ddr3_w_data;//ddr3写入的数据
    assign ddr3_read_in = (app_rd_data_valid)? app_rd_data:256'd0;//ddr3读出的数据
    
    //通过计数器产生突发读写使能信号
    always@(posedge ui_clk or negedge i_rst_n)begin
            if(!i_rst_n)    
                count   <=  6'd0;
            else if(count == 6'd64)
                count   <=  count;
            else if(init_calib_complete)
                count   <=  count + 1'b1;
    end 
    
    //ddr写入的数据
    always@(posedge ui_clk or negedge i_rst_n)begin
            if(!i_rst_n)    
                ddr3_w_data   <=  256'd0;
            else if(init_calib_complete && app_wdf_wren)
                ddr3_w_data   <=  ddr3_w_data + 1'b1;
            else
                ddr3_w_data   <=  256'd0;    
    end 
                        
    //突发写使能
    always@(posedge ui_clk or negedge i_rst_n)begin
            if(!i_rst_n)
                wr_len_en    <=  1'd0;    
            else if(ddr_wr_end)
                wr_len_en    <=  1'd0;    
            else if(rd_len_en)
                wr_len_en    <=  1'd0;    
            else if(wr_len_done)
                wr_len_en    <=  1'd0;                             
            else if(count == 6'd32)
                wr_len_en    <=  1'd1;                        
    end  
    
    //突发读使能
    always@(posedge ui_clk or negedge i_rst_n)begin
            if(!i_rst_n)
                rd_len_en    <=  1'd0;
            else if(ddr_rd_end)
                rd_len_en    <=  1'd0;            
            else if(wr_len_en)
                rd_len_en    <=  1'd0;     
            else if(rd_len_done)
                rd_len_en    <=  1'd0;                 
            else if(count == 6'd64)
                rd_len_en    <=  1'd1;                     
    end        
    
    
endmodule

从硬件上看两片16位ddr加在一起是32位的

可以看到读写的数据位宽都是[255:0]也就是256位,那是因为ddr3突发读写一般都是8位,ddr的数据位宽是32位,也就是32*8=256位,所以突发读或者突发写一次就是256位

因突发一次相当于写8个32位数据,也就是256位宽数据,所以ddr控制模块读写地址每次都是加8位

ddr型号为MT41J256M16,可以看出行为[14:0],列为[9:0]

从下图可以看出ddr一共有8个bank,也就是bank0~bank7,而且每个bank的存储空间都一样

行为[14:0],列为[9:0],那么一个bank的最大寻址空间为

2^15*2^10 = 32768*1024= 33554432=2^25
数据位宽相当于[24:0]

从下图可以看出bank选择,由BA0,BA1,BA2这三个管脚组成,也就是2^3=8(bank0~bank7)

再来看8个bank的寻址空间计算

bank0
33554432*1-1 = 33554431([24:0]),加上BA0,BA1,BA2,也就是[27:0],将33554431化为二进制
‭‭0   0   0     1111111111111111111111111‬‬
ba2 ba1  ba0    ([24:0])
所以[ba2,ba1,ba0] = 000

同样bank1为
33554432*2-1 = 67108863
‭‭0   0   1     1111111111111111111111111‬‬
ba2 ba1  ba0    ([24:0])
所以[ba2,ba1,ba0] = 001

同样bank2为
33554432*3-1 = 100663295
‭‭0   1   0     1111111111111111111111111‬‬
ba2 ba1  ba0    ([24:0])
所以[ba2,ba1,ba0] = 010

同样bank3为
33554432*4-1 = 134217727
‭‭0   1   1     1111111111111111111111111‬‬
ba2 ba1  ba0    ([24:0])
所以[ba2,ba1,ba0] = 011

同样bank4为
33554432*5-1 = 167772159
‭‭1   0   0     1111111111111111111111111‬‬
ba2 ba1  ba0    ([24:0])
所以[ba2,ba1,ba0] = 100

同样bank5为
33554432*6-1 = 201326591‬
‭‭1   0   1     1111111111111111111111111‬‬
ba2 ba1  ba0    ([24:0])
所以[ba2,ba1,ba0] = 101

同样bank6为
33554432*7-1 = 234881023
1   1   0     1111111111111111111111111‬‬
ba2 ba1  ba0    ([24:0])
所以[ba2,ba1,ba0] = 110

同样bank7为
33554432*8-1 = 268435455
‭‭1   1   1     1111111111111111111111111‬‬
ba2 ba1  ba0    ([24:0])
所以[ba2,ba1,ba0] = 111

从上面可以看出,ddr寻址范围[24:0]为每个bank的寻址空间,而高三位[27:25]为ba2,ba1,ba0为bank选择

所以ddr控制模块里面读写寻址空间为[27:0],后[24:0]为寻址空间,前面三位为bank选择,因本ddr一共有8个bank,所以可以缓存8页数据

 assign app_addr = (state == WRITE && (app_rdy && app_wdf_rdy))? {wr_page,app_addr_wr[24:0]}:{rd_page,app_addr_rd[24:0]}; 

再来看工程仿真波形

当count计数器计数到32时产生一次wr_len_en突发写使能

    //突发写使能
    always@(posedge ui_clk or negedge i_rst_n)begin
            if(!i_rst_n)
                wr_len_en    <=  1'd0;    
            else if(ddr_wr_end)
                wr_len_en    <=  1'd0;    
            else if(rd_len_en)
                wr_len_en    <=  1'd0;    
            else if(wr_len_done)
                wr_len_en    <=  1'd0;                             
            else if(count == 8'd32)
                wr_len_en    <=  1'd1;                        
    end 

当发出wr_len_en写突发使能后,可以看到app_rdy和app_wdf_rdy都为1,表示可以写数据,app_cmd为0表示写命令,这里一共进行四次突发写,所以地址为0,8,10,18(所以ddr地址为32-8),wr_addr_cnt为突发写的次数,这个次数是可以设置的,但我们这里设置为4,截图中可以看到进行了四次计数分别是000000,000001,000002,000003。

当wr_len_en为1时,app_wdf_wren为高写有效,可以看到app_wdf_data写入了四个256位宽度的数据,分别为0,1,2,3这四个数据,写完成后产生一个wr_len_done突发写完成信号,因前面我们设置了写四个数据,数据写完成后会产生一个ddr_wr_end写结束信号。

    //ddr写入的数据
    always@(posedge ui_clk or negedge i_rst_n)begin
            if(!i_rst_n)    
                ddr3_w_data   <=  256'd0;
            else if(init_calib_complete && app_wdf_wren)
                ddr3_w_data   <=  ddr3_w_data + 1'b1;
            else
                ddr3_w_data   <=  256'd0;    
    end 

当count计数器计数到64时产生一次rd_len_en突发读使能

    //突发读使能
    always@(posedge ui_clk or negedge i_rst_n)begin
            if(!i_rst_n)
                rd_len_en    <=  1'd0;
            else if(ddr_rd_end)
                rd_len_en    <=  1'd0;            
            else if(wr_len_en)
                rd_len_en    <=  1'd0;     
            else if(rd_len_done)
                rd_len_en    <=  1'd0;                 
            else if(count == 8'd64)
                rd_len_en    <=  1'd1;                     
    end

突发读时,当app_rdy和app_wdf_rdy为高时,app_en使能为高,写入需要读数据的地址,也就是0,8,10,18这四个地址,而且此时的app_cmd为1表示进行ddr读命令,rd_addr_cnt为突发读次数,可以看到进行了四次突发读分别是000000,000001,000002,000003,并产生ddr_rd_end读突发结束信号。

读出数据,可以看到app_rd_data_valid为高表示读出数据有效,app_rd_data读出0,1,2,3四个数据和写入的数据一致(一般读出数据是有延迟的,并不是写完控制命令后,立即就能读出数据,具体需要多少个周期,可以参考ddr3数据手册)

因怕很多人对突发读写不理解,所以将ddr读写控制和突发读写模块简化了很多,本来想将突发读写,写一篇博客的,但感觉涉及的东西太多了,写到这里还没有将ddr复位信号加入进去,之后使用hdmi显示再加入场信号复位,如果写的有问题欢迎各位指正。

如若转载,请注明出处

  • 7
    点赞
  • 45
    收藏
    觉得还不错? 一键收藏
  • 15
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 15
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值