【FPGA】 DDR3读写(基于User Interface)
【FPGA】 DDR3读写(基于User Interface)
DDR3概述
DDR3 (double data rate 3 synchronous dynamic RAM) 第三代双倍数据速率同步动态随机存储器
- 同步:数据的速去和写入时钟同步
- 动态:数据掉电无法保存,需要周期性刷新才能保持数据
- 随机存取:能够对任意地址进行操作
- 双倍数据速率:时钟的上升沿和下降沿都发生数据传输
DDR3存储器模型
用户可通过MIG IP核控制DDR3
MIG IP核的配置
- 修改component name
- 选择是否使用AXI4接口(此处选用User Interface接口)
-
clock period
物理层时钟周期,即DDR3芯片的输入时钟
-
PHY to Controller Clock Ratio
DDR3输入时钟和MIG控制器时钟频率比例,此处选择4:1说明MIG控制器时钟为100MHz,因此用户侧逻辑也会按照100MHz进行编写
由于DDR3是时钟双边沿读写,假设DDR3的位宽是16bits,则带宽为400MHz*2*16bits
而对应的用户侧则通过扩展的方式匹配,其位宽为400MHz*2*16bits/100MHz = 128bits
-
Vccaux_io
-
Memory Type
DDR3内存类型,如果是DDR3芯片颗粒,则选用components,如果是内存条的形式,则选择对应的类型
-
Memory Part
根据DDR3型号选择,此处选用位宽为16bits
-
Data Width
-
ECC
-
Data Mask
是否启用数据掩码,即可通过app_data_mask端口去控制写入部分位(例如高8位)数据
-
Number of Bank Machines
与使用的DDR3有关,如果拉满则效率最高,看需求
-
ORDERING
Strict即保持数据的读写顺序,Normal即允许控制器改变执行cmd的顺序,提高读写速率
-
Input Clock Period
输入时钟周期,通过MIG内部的PLL得到最终输入DDR3的时钟
-
system clock
系统时钟类型
-
reference clock
DDR3的正常运行需要200MHz参考时钟,如果前面选择了200MHz的输入时钟,则此处可以复用为User System Clock
-
system reset polarity
- 后者是因为硬件已经设计好了,引脚是固定的
- 前者会自动选择最优的引脚去驱动
MIG IP核的使用
MIG控制器的三种时序:写命令、写数据、读数据都有对应的FIFO,因此操作起来更为灵活,限制较小
写命令
写数据
当app_wdf_rdy为高电平时,即可拉高app_wdf_en写入数据
数据可在命令之前、同时或最大不慢于2个周期写入
读数据
写入读命令后,数据可能在若干个周期后读出,伴随app_rd_data_valid信号拉高
如何对自己的DDR3读写模块进行仿真?
由于仿真需要DDR3模型,而自己写一个DDR3模型过于复杂,因此可通过利用example design的代码,并换上自己的DDR3读写模块实现,具体可参考该UP的教学ddr3搭建仿真平台_哔哩哔哩_bilibili
1、Open IP Example Design会自动生成一个项目
2、找到项目imports文件夹下的这些文件
并复制到原先的工程文件夹中,同时vivado中add sim文件
3、修改example_top.v文件,换成自己的模块
top_module.v:
`timescale 1ns / 1ps module top_module( // inouts ddr3_dq, ddr3_dqs_n, ddr3_dqs_p, // outputs ddr3_addr, ddr3_ba, ddr3_ras_n, ddr3_cas_n, ddr3_we_n, ddr3_reset_n, ddr3_ck_p, ddr3_ck_n, ddr3_cke, ddr3_cs_n, ddr3_dm, ddr3_odt, init_calib_complete, // inputs sys_clk_i, sys_rst ); inout [15:0] ddr3_dq; inout [1:0] ddr3_dqs_n; inout [1:0] ddr3_dqs_p; output [13: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 [1:0] ddr3_dm; output [0:0] ddr3_odt; output init_calib_complete; input sys_clk_i; input sys_rst; wire [27:0] app_addr; wire [2:0] app_cmd; wire app_en; wire [127:0] app_wdf_data; wire app_wdf_end; wire app_wdf_wren; wire [127:0] app_rd_data; wire app_rd_data_end; wire app_rd_data_valid; wire app_rdy; wire app_wdf_rdy; wire app_sr_req; wire app_ref_req; wire app_zq_req; wire app_sr_active; wire app_ref_ack; wire app_zq_ack; wire ui_clk; wire ui_clk_sync_rst; wire [15:0] app_wdf_mask; wire sys_clk_i; wire sys_rst; // mig_16bits instance mig_16bits mig_16bits_inst( // memory ports .ddr3_addr(ddr3_addr), .ddr3_ba(ddr3_ba), .ddr3_cas_n(ddr3_cas_n), .ddr3_ck_n(ddr3_ck_n), .ddr3_ck_p(ddr3_ck_p), .ddr3_cke(ddr3_cke), .ddr3_ras_n(ddr3_ras_n), .ddr3_reset_n(ddr3_reset_n), .ddr3_we_n(ddr3_we_n), .ddr3_dq(ddr3_dq), .ddr3_dqs_n(ddr3_dqs_n), .ddr3_dqs_p(ddr3_dqs_p), .init_calib_complete(init_calib_complete), .ddr3_cs_n(ddr3_cs_n), .ddr3_dm(ddr3_dm), .ddr3_odt(ddr3_odt), // user app ports .app_addr(app_addr), .app_cmd(app_cmd), .app_en(app_en), .app_wdf_data(app_wdf_data), .app_wdf_end(app_wdf_end), .app_wdf_wren(app_wdf_wren), .app_rd_data(app_rd_data), .app_rd_data_end(app_rd_data_end), .app_rd_data_valid(app_rd_data_valid), .app_rdy(app_rdy), .app_wdf_rdy(app_wdf_rdy), .app_wdf_mask(app_wdf_mask), .app_sr_req(1'b0), .app_ref_req(1'b0), .app_zq_req(1'b0), .app_sr_active(app_sr_active), .app_ref_ack(app_ref_ack), .app_zq_ack(app_zq_ack), // clock ports .ui_clk(ui_clk), .ui_clk_sync_rst(ui_clk_sync_rst), .sys_clk_i(sys_clk_i), .sys_rst(sys_rst) ); // mig data read write test wire test_reset; mig_test mig_test_inst( .init_calib_complete(init_calib_complete), .ui_clk(ui_clk), .ui_rst_in(ui_clk_sync_rst), .app_addr(app_addr), .app_cmd(app_cmd), .app_en(app_en), .app_wdf_data(app_wdf_data), .app_wdf_end(app_wdf_end), .app_wdf_mask(app_wdf_mask), .app_wdf_wren(app_wdf_wren), .app_wdf_rdy(app_wdf_rdy), .app_rd_data(app_rd_data), .app_rd_data_end(app_rd_data_end), .app_rd_data_valid(app_rd_data_valid), .app_rdy(app_rdy), .test_result(test_reset) ); endmodule
mig_test.v:
向DDR3中写入16个数据之后读出
`timescale 1ns / 1ps module mig_test( init_calib_complete, ui_clk, ui_rst_in, app_addr, app_cmd, app_en, app_wdf_data, app_wdf_end, app_wdf_mask, app_wdf_wren, app_wdf_rdy, app_rd_data, app_rd_data_end, app_rd_data_valid, app_rdy ); input init_calib_complete; input ui_clk; input ui_rst_in; wire ui_rst; assign ui_rst = ~ui_rst_in; output wire [27:0] app_addr; reg [27:0] app_addr_write = 0; reg [27:0] app_addr_read = 0; output wire [2:0] app_cmd; output reg app_en = 0; output reg [127:0] app_wdf_data = 0; output reg app_wdf_end = 0; output reg [15:0] app_wdf_mask = 0; output reg app_wdf_wren = 0; input app_wdf_rdy; input [127:0] app_rd_data; input app_rd_data_end; input app_rd_data_valid; input app_rdy; reg [127:0] rd_data_reg = 0; wire wr_done; wire rd_done; reg [7:0] counter_write_cmd = 16; reg [7:0] counter_write = 16; reg [7:0] counter_read_cmd = 0; reg [7:0] counter_read = 0; localparam STATE_IDLE = 4'b0001; localparam STATE_WRITE = 4'b0010; localparam STATE_READ = 4'b0100; localparam STATE_END = 4'b1000; reg [3:0] state = STATE_IDLE ; reg [3:0] next_state = STATE_IDLE ; // next state logic always @(*) begin if (~ui_rst | ~init_calib_complete) begin next_state = STATE_IDLE; end else begin case (state) STATE_IDLE: begin next_state = STATE_WRITE; end STATE_WRITE: begin if(wr_done) begin next_state = STATE_READ; end else begin next_state = STATE_WRITE; end end STATE_READ: begin if(rd_done) begin next_state = STATE_END; end else begin next_state = STATE_READ; end end STATE_END: begin next_state = STATE_END; end default: ; endcase end end // state change always @(posedge ui_clk) begin if(~ui_rst | ~init_calib_complete) begin state <= STATE_IDLE; end else begin state <= next_state; end end // output logic // app_cmd assign app_cmd = (state == STATE_WRITE)?3'b000:3'b001; //app_en always @(*) begin if(~ui_rst) begin app_en <= 0; end else begin // write data if(state == STATE_WRITE && app_rdy && counter_write_cmd != 0) begin app_en <= 1; // counter_write_cmd <= counter_write_cmd - 1; end else if(state == STATE_READ && app_rdy && counter_read_cmd != 8'd16) begin app_en <= 1; // counter_read_cmd <= counter_read_cmd + 1; end else begin app_en <= 0; end end end // counter_write_cmd / counter_read_cmd always @(posedge ui_clk) begin if(~ui_rst) begin counter_write_cmd <= 16; counter_read_cmd <= 0; end else begin if(state == STATE_WRITE && app_rdy && app_en && counter_write_cmd != 0) begin counter_write_cmd <= counter_write_cmd - 1; end else if(state == STATE_READ && app_rdy && app_en && counter_read_cmd != 16) begin counter_read_cmd <= counter_read_cmd + 1; end end end // app_wdf_data always @(posedge ui_clk) begin if(~ui_rst) begin app_wdf_data <= 0; app_wdf_wren <= 0; app_wdf_end <= 0; end else if(state == STATE_WRITE && app_wdf_rdy && counter_write != 0) begin app_wdf_wren <= 1; app_wdf_data <= app_wdf_data + 1'd1; app_wdf_end <= 1; counter_write <= counter_write - 1'd1; end else begin app_wdf_wren <= 0; app_wdf_end <= 0; end end // app_addr_write always @(posedge ui_clk) begin if(~ui_rst) begin app_addr_write <= 0; end else begin if(state == STATE_WRITE && app_rdy && counter_write_cmd!=0) begin app_addr_write <= app_addr_write + 28'd8; end end end // app_addr_read always @(posedge ui_clk) begin if(~ui_rst) begin app_addr_read <= 0; end else begin if(state == STATE_READ && app_rdy && counter_read_cmd!=16) begin app_addr_read <= app_addr_read + 28'd8; end end end // app_addr assign app_addr = (state == STATE_WRITE)?app_addr_write:app_addr_read; // app_rd_data always @(posedge ui_clk) begin if (~ui_rst) begin rd_data_reg <= 0; end else begin if(app_rd_data_valid) begin rd_data_reg <= app_rd_data; counter_read <= counter_read + 1'd1; end end end // wr_done assign wr_done = (counter_write_cmd == 0)&&(counter_write == 0); // rd_done assign rd_done = (counter_read_cmd == 16)&&(counter_read == 16); endmodule
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· winform 绘制太阳,地球,月球 运作规律
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· AI与.NET技术实操系列(五):向量存储与相似性搜索在 .NET 中的实现
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理