草庐IT

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

虚无缥缈vs威武 2023-05-27 原文

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

因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'd32)
                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'd16)
                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'd32)
                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显示再加入场信号复位,如果写的有问题欢迎各位指正。

如若转载,请注明出处

有关AX7A200教程(3): DDR3突发读写的更多相关文章

  1. postman接口测试工具-基础使用教程 - 2

    1.postman介绍Postman一款非常流行的API调试工具。其实,开发人员用的更多。因为测试人员做接口测试会有更多选择,例如Jmeter、soapUI等。不过,对于开发过程中去调试接口,Postman确实足够的简单方便,而且功能强大。2.下载安装官网地址:https://www.postman.com/下载完成后双击安装吧,安装过程极其简单,无需任何操作3.使用教程这里以百度为例,工具使用简单,填写URL地址即可发送请求,在下方查看响应结果和响应状态码常用方法都有支持请求方法:getpostputdeleteGet、Post、Put与Delete的作用get:请求方法一般是用于数据查询,

  2. 在VMware16虚拟机安装Ubuntu详细教程 - 2

    在VMware16.2.4安装Ubuntu一、安装VMware1.打开VMwareWorkstationPro官网,点击即可进入。2.进入后向下滑动找到Workstation16ProforWindows,点击立即下载。3.下载完成,文件大小615MB,如下图:4.鼠标右击,以管理员身份运行。5.点击下一步6.勾选条款,点击下一步7.先勾选,再点击下一步8.去掉勾选,点击下一步9.点击下一步10.点击安装11.点击许可证12.在百度上搜索VM16许可证,复制填入,然后点击输入即可,亲测有效。13.点击完成14.重启系统,点击是15.双击VMwareWorkstationPro图标,进入虚拟机主

  3. hadoop安装之保姆级教程(二)之YARN的配置 - 2

    1.1.1 YARN的介绍 为克服Hadoop1.0中HDFS和MapReduce存在的各种问题⽽提出的,针对Hadoop1.0中的MapReduce在扩展性和多框架⽀持⽅⾯的不⾜,提出了全新的资源管理框架YARN. ApacheYARN(YetanotherResourceNegotiator的缩写)是Hadoop集群的资源管理系统,负责为计算程序提供服务器计算资源,相当于⼀个分布式的操作系统平台,⽽MapReduce等计算程序则相当于运⾏于操作系统之上的应⽤程序。 YARN被引⼊Hadoop2,最初是为了改善MapReduce的实现,但是因为具有⾜够的通⽤性,同样可以⽀持其他的分布式计算模

  4. ruby - 在 RUBY 上的 PADRINO 框架上使用 RSPEC 进行测试的教程 - 2

    我是Ruby新手,并被要求在我们的新项目中使用它。我们还被要求使用Padrino(Sinatra)作为后端/框架。我们被要求使用Rspec进行测试。我一直在寻找可以指导在Padrino上使用RspecforRuby的教程。我得到的主要是引用RoR。但是,我需要RubyonPadrino。请在任何入门/指南/引用/讨论等方面指导我。如有不妥之处请指正。可能是我没有针对我的问题搜索正确的词/短语组合。我正在使用Ruby1.9.3和Padrinov.0.10.6。注意:我还提到了SOquestion,但它没有帮助。 最佳答案 我没用过Pa

  5. 华为OD机试真题 C++ 实现【带传送阵的矩阵游离】【2023 Q2 | 200分】 - 2

            所有题目均有五种语言实现。C实现目录、C++实现目录、Python实现目录、Java实现目录、JavaScript实现目录题目n行m列的矩阵,每个位置上有一个元素你可以上下左右行走,代价是前后两个位置元素值差的绝对值.另外,你最多可以使用一次传送阵(只能从一个数跳到另外一个相同的数)求从走上角走到右下角最少需要多少时间。输入描述:第一行两个整数n,m,分别代表矩阵的行和列。后面n行,每行m个整数,分别代表矩阵中的元素。输出描述:一个整数,表示最少需要多少时间。

  6. 区块链入门教程(6)--WeBASE-Front节点前置服务安装 - 2

    文章目录1.任务背景2.任务目标3.相关知识点4.任务实操4.1安装配置JDK4.2启动FISCOBCOS4.3下载解压WeBASE-Front4.4拷贝sdk证书文件4.5启动节点4.6访问节点4.7检查运行状态5.任务总结1.任务背景FISCOBCOS其实是有控制台管理工具,用来对区块链系统进行各种管理操作。但是对于初学者来说,还是可视化界面更友好,本节就来介绍WeBASE管理平台,这是一款微众银行开源的自研区块链中间件平台,可以降低区块链使用的门槛,大幅提高区块链应用的开发效率。微众银行是腾讯牵头设立的民营银行,在国内民营银行里还是比较出名的。微众银行参与FISCOBCOS生态建设,一定

  7. ruby-on-rails - Rails 在记录 200 OK 后在做什么? (调试响应时间慢) - 2

    我试图在我的RubyonRails应用程序中调试一个极其缓慢的请求调用。我已设法根据自己的喜好优化Controller方法,Rails的日志告诉我它已在XX毫秒内完成操作(Completed200OKin5049ms(Views:34.9ms|ActiveRecord:76.3ms)).但是,在加载页面时,在浏览器中实际呈现任何内容之前打印此消息很长;最多约15秒的等待时间。Rackmini-profiler证实了这一点,告诉我GET操作(不计算完成Controller操作所花费的时间)花费了14秒左右。(分析器还确认Controller操作的执行时间约为5秒)。我可以接受Contro

  8. ruby-on-rails - rails 教程 : Putting flash messages in partial yields error "undefined method ` each' for nil:NilClass"? - 2

    这个问题在这里已经有了答案:关闭10年前。PossibleDuplicate:FlashMessagesinPartials(Rails3)我正在做MichaelHartl的Railstutorial和listing7.26将flash消息添加到应用程序布局:...">...这很好用。但是,我试图通过在我的部分文件夹中创建一个_flash.html.erb来清理这段代码...">-->...并且比使用......在我的应用程序布局中,我的所有Rspec测试开始失败,每个测试都显示以下消息:Failure/Error:before{visitsignup_path}ActionView:

  9. ruby-on-rails - Ruby on Rails 教程 - 5.26 - Sublime Text "Unable to Save"新文件 "spec/support/utilities.rb" - 2

    我正在使用SublimeText2,同时遵循MichaelHartl的RubyonRails教程。可以在http://ruby.railstutorial.org/book/ruby-on-rails-tutorial找到我所指的教程的具体部分。(ctrl+F“list5.26”)。我能够创建规范/支持文件。但是,在尝试创建spec/support/utilities.rb文件时,我收到消息“无法保存~/rails_projects/sample_app/spec/support/utilities.rb”。有人知道为什么会这样吗?SublimeText论坛上有人似乎遇到了完全相同的问

  10. ruby-on-rails - Rails Controller 中未定义的方法呈现 - 尝试使用 200 状态代码响应 Sendgrid - 2

    我正在使用SendgridParseAPI和Griddlergem来接受传入的电子邮件。在大多数情况下,这工作正常;但是,如果您未使用状态代码200响应Sendgrid,他们将假定该应用程序未正确接收POST请求并继续尝试进行POST3天。我正在尝试使用状态代码进行响应,但遇到了问题。在常规的RESTful路由中,您可以执行类似...render:status=>200但是,我认为这必须在Controller中完成才能识别渲染方法。Griddler建议您创建一个EmailProcessor模型并使用“处理”方法来处理电子邮件。据我了解,您不能在模型中使用渲染方法。因此,我使用类方法创建

随机推荐