Xilinx DDR3 —— MIG IP核的读写仿真(APP接口)

在上一篇中Xilinx DDR3 —— MIG IP核的配置(APP接口),已经观看了Xilinx官方提供的MIG IP核读写例程仿真波形,本着学习的目的,本篇开始自己编写MIG IP核读写程序,用于驱动MIG IP核进行DDR 3数据的读写。由于没有DDR实物,这里直接借助官方提供的MIG IP核读写例程中的DDR3模拟程序,即直接在Xilinx官方提供的MIG IP核读写例程进行修改。

1. 如下图所示,新建一个ddr3_rw.v,用于编写控制DDR3读写的程序

Xilinx DDR3 —— MIG IP核的读写仿真(APP接口)
2. 如下图所示,首先定义输入输出

MIG IP核有两组接口,一组是Memory interface ports ,它连接到DDR3,无需用户来操作,只需要连接好就行;另一组是Application interface ports,它是用户端的接口,供用户直接操作的。MIG IP 会将从Application interface ports接收到的信号在内部进行时序转换,变成直接控制DDR的时序,由Memory interface ports 输出给DDR3,这个逻辑一定要清楚,因此要控制DDR的读写,只需控制好Application interface ports即可。
Xilinx DDR3 —— MIG IP核的读写仿真(APP接口)

3. 总体代码如下:

module ddr3_rw(
    input                ui_clk,                // MIG IP核产生的用户时钟,用于
    input                ui_clk_sync_rst,       // MIG IP核产生的复位信号,高有效
    input                init_calib_complete,   // DDR3初始化完成初始化完成,高电平有效,
    input                app_rdy,               // MIG IP核读写命令接收准备完成,高电平有效。
    input                app_wdf_rdy,           // MIG IP核数据接收准备完成,高电平有效。
    input                app_rd_data_valid,     // 读数据有效
    input      [127:0]   app_rd_data,           // 用户要读取的数据

    output reg [28:0]    app_addr,              // 地址,该地址位宽ADDR_WIDTH = RANK位宽 + BANK位宽+ ROW位宽 + COL位宽                     
    output reg           app_en,                // MIG IP核命令写入使能,高有效。写命令时需要拉高该信号
    output               app_wdf_wren,          // 用户写数据使能
    output               app_wdf_end,           // 突发写当前时钟最后一个数据 
    output     [2:0]     app_cmd,               // MIG IP核读写控制命令,000位写,001为读。
    output reg [127:0]   app_wdf_data           // 用户要写入的数据
    );

//***************************************************** //
//**                  DDR3读写逻辑                    **//
//*****************************************************//


reg [4:0] wr_data_cnt;
reg [4:0] rd_data_cnt;
reg [4:0] rd_addr_cnt;

reg error_flag;
wire error;

parameter TEST_LENGTH = 20;

localparam IDLE  = 2'd0, 
           WRITE = 2'd1,
           WAIT  = 2'd2,
           READ  = 2'd3;

reg [1:0] cur_state;
reg [1:0] next_state;

assign rst_n = !ui_clk_sync_rst;



always @(*) begin
    if ((cur_state == WRITE) && (app_rdy && app_wdf_rdy)) begin  // 当处于写状态时,若app_rdy和app_wdf_rdy拉高,则将app_en拉高。
        app_en = 1'd1;
    end
    else begin
        if ((cur_state == READ) && app_rdy) begin       // 当处于读状态时,若app_rdy拉高,则将app_en拉高。
            app_en = 1'd1;
        end
        else begin
            app_en = 1'd0;
        end
    end
end
                
// 在写状态,命令接收(app_rdy)和数据接收(app_wdf_rdy)都准备好,此时拉高写使能
assign app_wdf_wren = ((cur_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 = (cur_state == READ) ? 3'd1 :3'd0;  


always @(posedge ui_clk or negedge rst_n) begin
    if (!rst_n) begin
        cur_state <= IDLE;
    end
    else begin
        cur_state <= next_state;
    end
end

always @(*) begin
    if (!rst_n) begin
        next_state = IDLE;
    end
    else begin
        case (cur_state)
            IDLE: begin
                if (init_calib_complete) begin
                    next_state = WRITE;
                end
                else begin
                    next_state = IDLE;
                end
            end
            WRITE:begin
                if (wr_data_cnt < TEST_LENGTH) begin
                    next_state = WRITE;
                end
                else begin
                    next_state = WAIT;
                end
            end
            WAIT:begin
                next_state = READ;
            end
            READ:begin
                if (rd_addr_cnt < TEST_LENGTH) begin
                    next_state = READ;
                end
                else begin
                    next_state = IDLE;
                end
            end
            default: begin
                if (init_calib_complete) begin
                    next_state = WRITE;
                end
                else begin
                    next_state = IDLE;
                end
            end
        endcase
    end
end

always @(posedge ui_clk or negedge rst_n) begin
    if (!rst_n) begin
        app_addr <= 29'd0;
        wr_data_cnt <= 5'd0;
    end
    else begin
        case (next_state)
            IDLE: begin
                app_wdf_data <= 256'd0;   
                wr_data_cnt  <= 0;   
                rd_addr_cnt  <= 0;      
                app_addr     <= 0;
            end
            WRITE:begin
                if ((app_rdy)&&(app_wdf_rdy)&&(~app_cmd)) begin
                    app_addr     <= app_addr + 8;
                    app_wdf_data <= app_wdf_data + 1;
                    wr_data_cnt  <= wr_data_cnt + 1;
                end
                else begin
                    app_addr     <= app_addr;
                    app_wdf_data <= app_wdf_data;
                    wr_data_cnt  <= wr_data_cnt;
                end
            end
            WAIT:begin
                app_addr <= 0;
            end
            READ:begin
                if ((app_rdy)&&(app_cmd)) begin
                    app_addr     <= app_addr + 8;
                    rd_addr_cnt  <= rd_addr_cnt + 1;  
                end
                else begin
                    app_addr     <= app_addr;
                    rd_addr_cnt  <= rd_addr_cnt;  
                end
            end
            default: begin
                app_wdf_data <= 256'd0;   
                wr_data_cnt  <= 0;           
                app_addr     <= 0;
            end
        endcase
    end
end

always @(posedge ui_clk or negedge rst_n) begin
    if (!rst_n) begin
        rd_data_cnt <= 5'd0;
    end
    else begin
        if (app_rd_data_valid) begin
            if (rd_data_cnt == TEST_LENGTH) begin
                rd_data_cnt <= 5'd0;
            end
            else begin
                rd_data_cnt <= rd_data_cnt + 1;
            end
        end
        else begin
            rd_data_cnt <= rd_data_cnt;
        end
    end
end

//读信号有效,且读出的数不是写入的数时,将错误标志位拉高
assign error = (app_rd_data_valid && (rd_data_cnt!=app_rd_data));  
                      
//寄存状态标志位
always @(posedge ui_clk or negedge rst_n) begin
    if(~rst_n) 
        error_flag <= 0;
    else if(error)
        error_flag <= 1;
end


endmodule

4. 将ddr3_rw.v例化进example_top.v:

Xilinx DDR3 —— MIG IP核的读写仿真(APP接口)
5. 同时将example_top.v中的mig_7series_v4_1_traffic_gen_top注释掉:

Xilinx DDR3 —— MIG IP核的读写仿真(APP接口)

6. 点击run simulation仿真即可出现如下波形

Xilinx DDR3 —— MIG IP核的读写仿真(APP接口)
7. 如图中紫线所示,error_flag一直为低,说明读出的数据与写入的数据一致。

Xilinx DDR3 —— MIG IP核的读写仿真(APP接口)

上一篇:辩论会场app使用手册(doge)


下一篇:Inno Setup制作安装程序