DDR3(MIG核配置官方demoFPGA代码实现及仿真)
由于直接对 DDR3 进行控制很复杂,因此一般使用 MIG IP 来实现,同时为了更简单地使用 MIG IP,我们采用 AXI4 总线协议进行控制。下面首先介绍 MIG IP 的配置,然后看看官方 demo (里面包含一个仿真要用到的 DDR3 模型)及其仿真结果,最后进行我们自己的控制代码实现。
MIG IP 生成
在 IP Catalog 里搜索 MIG,如下
第一步里,勾选 AXI4 Interface 选项,使用 AXI4 接口
下一步是选择 FPGA 型号,读者自行选择自己的 FPGA 型号;下一步是选择控制器类型,我们直接选择 DDR3 SDRAM;这几步很简单,就不放图了。再下一步,Controller Options,这里要重点介绍下
首先配置 DDR3 的工作时钟,这个时钟也就是 DDR3_CLK_P/N,下一个 PHY to Controller Clock Ratio 是 FPGA 与 MIG 间通信使用的时钟(ui_clk)与 DDR3 时钟的比值,笔者使用 200MHz,因此这里设置为 2:1;Memory Type 需读者按照自己的 DDR 型号进行选择;Data Width 是 DDR3 用到的数据位宽,比如笔者用到 4 片 DDR,每片位宽 16bit,菊花链连接,因此这里需要设为 64。
下一步,这里的 Data Width 是 FPGA 与 MIG 通信用到的数据位宽,为方便起见,我使用了与 DDR 数据同位宽;下一个是设置读写优先级的,TDM 是读写同级,也可修改为读/写优先、循环读写等。
下一步,Input Clock Period 是设置 sys_clk_i 的频率,MIG IP 会使用这个时钟生成 DDR3_CK 信号以及供用户使用的 ui_clk,所有关于 MIG 的用户操作均应在 ui_clk 下进行;最下面的 Memory Address Mapping Selection 是 MIG 与 DDR 进行读写通信时的地址写入顺序。
下一步,System Clock 设为 No Buffer,因为我们用到的 sys_clk_i 是从 FPGA 内部提供的,如果是从外部引脚提供,则需要修改这里(有单引脚时钟,也有差分时钟信号);参考时钟直接使用 sys_clk_i;System Reset Polarity 设置 sys_rst 的极性,这里我设为低电平有效。
下一步是设置引脚阻抗;再下一步,勾选 Fixed Pin Out;再下一步,这里配置 DDR3 的引脚,可以手动配置也可以 read XDC 来配置,然后 Validate 一下,如果通过了,就可以下一步了(不 validate 那个 next 无法点击)。
下一步,由于这三个信号我均做为 FPGA 内部信号,因此都 No connect
再之后就是 summary 啥的,一路 next 到底就行。
- 注意事项
在使用 MIG IP 时,mmcm_locked 信号指示是否稳定给出 ui_clk,为高表示给出了稳定的 ui_clk;init_calib_complete 信号表示会否完成 DDR3 的初始化,为高表示完成初始化,在这之后才可以进行 DDR3 的读写。
注意 MIG IP 的 app_sr_req、app_ref_req、app_zq_req 要置零,否则无法完成 DDR 初始化,init_calib_complete 将无法拉高。
此外,MIG IP 有三个 reset 信号,分别是 sys_rst、ui_clk_sync_rst 和 aresetn 。ui_clk_sync_rst 是同步于 ui_clk 的 sys_rst,高电平有效;但 MIG 并不会因为 sys_rst 而 reset,而是通过 aresetn 进行 reset 的,且 aresetn 应当同步于 ui_clk。因此应当如下书写三者的关系
always @(posedge ui_clk) beginaresetn <= ~ui_clk_sync_rst;
end
官方 demo
在生成的 MIG IP 上右击,Open IP Example Design,会新建一个名为 mig_7series_0_ex.xpr 的工程,打开工程,可以看到一个顶层文件 example_top.v 和一个仿真文件 sim_tb_top.v
可以直接运行仿真,结果如下
在工程文件夹下, ./imports 文件夹中,有两个文件,ddr3_model.sv 和 ddr3_model_parameters.vh,前者是 ddr3 的模型,引用了后边这个 parameter 文件,parameter 文件里是关于 ddr3 模型的参数配置,在 ddr3_model 中修改 `define 值,可以配置 ddr3_model 的各类参数。后面仿真我们自己写的控制代码就要用到这个 DDR3 模型文件。
自己的FPGA实现
MIG IP 用到 AXI 总线协议,因此建议读者首先对该总线协议进行初步了解,具体可见我之前的文章。这里也推荐一个知乎大佬写的 AXI 协议介绍,链接在此。
其余不多说,代码如下,该模块自动完成从 W_FIFO 向 DDR 的写入,以及从 DDR 读数据到 R_FIFO,用户只需要读写两个 FIFO 即可
/* * file : DDR3_top.v* author : 今朝无言* Lab : WHU-EIS-LMSWE* date : 2023-04-21* version : v1.0* description : DDR3控制模块*/
module DDR3_top(
input clk_200M,
input rst_n,
output rst_busy,//--------------------ddr3------------------------
inout [63:0] ddr3_dq, //DQ,4片 x16 DDR的数据线output [14:0] ddr3_addr, //Address
output [2:0] ddr3_ba, //Bank Addressinout [7:0] ddr3_dqs_p, //DQ Select,0,2,4,6分别为四片DDR的DQSL(Lower Byte),
inout [7:0] ddr3_dqs_n, //1,3,5,7分别为四片DDR的DQSU(Upper Byte)
//Output with read data. Edge-aligned with read data.
//Input with write data. Center-aligned to write data.output [0:0] ddr3_ck_p, //4片DR3共用ck、cke、odt、cs信号,故[0:0]
output [0:0] ddr3_ck_n,
//differential clock inputs. All control and address input signals are sampled
//on the crossing of the positive edge of CK and the negative edge of CK#output [7:0] ddr3_dm, //Input Data Mask,0,2,4,6为DML,1,3,5,7为DMU
output [0:0] ddr3_cke, //Clock Enable
output [0:0] ddr3_cs_n, //Chip Select
output ddr3_ras_n, //Row Address Enable
output ddr3_cas_n, //Column Address Enable
output ddr3_we_n, //Write Enable
output [0:0] ddr3_odt, //On-die termination enable
output ddr3_reset_n,//-------------FPGA FIFO Control-------------------
input wr_en, //高电平有效
input [63:0] wrdat,
output full, //fifo_w fullinput rd_en, //高电平有效
output [63:0] rddat,
output empty //fifo_r empty
);
//本方案采用4片 MT41K256M16TW-107 芯片,每片 256M * 16bit = 512MB 容量
//8个Bank,故 BA 为 3 位;行地址用到全部的 15 根地址线,列地址用到 log2(256M/2^15/2^3)=10 根地址线parameter AXI_ID = 4'h00; //读写事务ID
parameter DATA_NUM = 32; //一次突发传输的数据个数,1~256localparam AXI_LEN = DATA_NUM - 1'b1;
localparam FIFO_LEN = 1024; //w_fifo、r_fifo的长度//----------------------------------state define-----------------------------------------
localparam S_IDLE = 8'h01; //等待初始化
localparam S_ARB = 8'h02; //判决接下来进行WR还是RD
localparam S_WR_ADDR = 8'h04; //写地址
localparam S_WR_DATA = 8'h08; //写数据
localparam S_WR_RESP = 8'h10; //写回复
localparam S_RD_ADDR = 8'h20; //读地址
localparam S_RD_DATA = 8'h40; //读数据
localparam S_RD_RESP = 8'h80; //读回复//---------------------------------------------------------------------------------------
reg [7:0] state = S_IDLE;
reg [7:0] next_state = S_IDLE;
//尽管 AXI 的读、写可以独立,然而 DDR 读写是分时复用,因此这里的读写也必须分时进行//FIFO
wire w_fifo_full;
wire w_fifo_empty;
wire r_fifo_full;
wire r_fifo_empty;reg w_fifo_rden = 1'b0;
reg r_fifo_wren = 1'b0;wire fifo_rst;wire [9:0] w_fifo_rddat_cnt;
wire [9:0] r_fifo_wrdat_cnt;wire w_fifo_wr_rst_busy;
wire w_fifo_rd_rst_busy;
wire r_fifo_wr_rst_busy;
wire r_fifo_rd_rst_busy;wire w_fifo_rst_busy;
wire r_fifo_rst_busy;//Application interface ports
wire ui_clk; //MIG输出的时钟,一切关于mig的操作均应在此时钟域下进行
wire ui_clk_sync_rst; //mig输出的rst信号,同步于ui_clk,高电平有效
wire mmcm_locked;
reg aresetn;
wire init_calib_complete; //指示MIG是否完成初始化wire app_sr_req;
wire app_ref_req;
wire app_zq_req;
wire app_sr_active;
wire app_ref_ack;
wire app_zq_ack;//Slave Interface Write Address Ports
wire [3:0] s_axi_awid;
reg [30:0] s_axi_awaddr; //4*512MB=2GB,对应 2^31 Byte,故 AXI 地址线位宽为 31
wire [7:0] s_axi_awlen;
wire [2:0] s_axi_awsize;
wire [1:0] s_axi_awburst;
wire [0:0] s_axi_awlock;
wire [3:0] s_axi_awcache;
wire [2:0] s_axi_awprot;
wire [3:0] s_axi_awqos;
reg s_axi_awvalid;
wire s_axi_awready;//Slave Interface Write Data Ports
wire [63:0] s_axi_wdata;
wire [7:0] s_axi_wstrb;
reg s_axi_wlast;
reg s_axi_wvalid;
wire s_axi_wready;//Slave Interface Write Response Ports
wire [3:0] s_axi_bid;
wire [1:0] s_axi_bresp;
wire s_axi_bvalid;
reg s_axi_bready;//Slave Interface Read Address Ports
wire [3:0] s_axi_arid;
reg [30:0] s_axi_araddr;
wire [7:0] s_axi_arlen;
wire [2:0] s_axi_arsize;
wire [1:0] s_axi_arburst;
wire [0:0] s_axi_arlock;
wire [3:0] s_axi_arcache;
wire [2:0] s_axi_arprot;
wire [3:0] s_axi_arqos;
reg s_axi_arvalid;
wire s_axi_arready;//Slave Interface Read Data Ports
wire [3:0] s_axi_rid;
wire [63:0] s_axi_rdata;
wire [1:0] s_axi_rresp;
wire s_axi_rlast;
wire s_axi_rvalid;
reg s_axi_rready;//ddr3读写请求
reg wr_ddr3_req;
reg rd_ddr3_req;//突发读写的已传输数据计数
reg [8:0] data_cnt;//------------------------------------write FIFO------------------------------------------
//异步FIFO 采用 First Word Fall Through 模式 1024*64
fifo_generator_DDR_W fifo_w(.rst (fifo_rst),.wr_clk (clk_200M),.rd_clk (ui_clk),.din (wrdat),.wr_en (wr_en),.rd_en (w_fifo_rden),.dout (s_axi_wdata),.full (w_fifo_full),.empty (w_fifo_empty),.rd_data_count (w_fifo_rddat_cnt), //仿真显示,w_fifo_rddat_cnt 和 r_fifo_wrdat_cnt 计数不可信,...IP 在做什么!!!.wr_data_count (),.wr_rst_busy (w_fifo_wr_rst_busy),.rd_rst_busy (w_fifo_rd_rst_busy)
);//------------------------------------read FIFO-------------------------------------------
//异步FIFO
fifo_generator_DDR_R fifo_r(.rst (fifo_rst),.wr_clk (ui_clk),.rd_clk (clk_200M),.din (s_axi_rdata),.wr_en (r_fifo_wren),.rd_en (rd_en),.dout (rddat),.full (r_fifo_full),.empty (r_fifo_empty),.rd_data_count (),.wr_data_count (r_fifo_wrdat_cnt),.wr_rst_busy (r_fifo_wr_rst_busy),.rd_rst_busy (r_fifo_rd_rst_busy)
);//----------------------------------MIG IP, AXI4-----------------------------------------
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 [63:0] ddr3_dq.ddr3_dqs_n (ddr3_dqs_n), // inout [7:0] ddr3_dqs_n.ddr3_dqs_p (ddr3_dqs_p), // inout [7:0] ddr3_dqs_p.ddr3_cs_n (ddr3_cs_n), // output [0:0] ddr3_cs_n.ddr3_dm (ddr3_dm), // output [7:0] ddr3_dm.ddr3_odt (ddr3_odt), // output [0:0] ddr3_odt// Application interface ports.ui_clk (ui_clk), // output ui_clk.ui_clk_sync_rst (ui_clk_sync_rst), // output ui_clk_sync_rst.mmcm_locked (mmcm_locked), // output mmcm_locked.aresetn (aresetn), // input aresetn.init_calib_complete (init_calib_complete), // output init_calib_complete.app_sr_req (app_sr_req), // input app_sr_req.app_ref_req (app_ref_req), // input app_ref_req.app_zq_req (app_zq_req), // 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// Slave Interface Write Address Ports.s_axi_awid (s_axi_awid), // input [3:0] s_axi_awid.s_axi_awaddr (s_axi_awaddr), // input [30:0] s_axi_awaddr.s_axi_awlen (s_axi_awlen), // input [7:0] s_axi_awlen.s_axi_awsize (s_axi_awsize), // input [2:0] s_axi_awsize.s_axi_awburst (s_axi_awburst), // input [1:0] s_axi_awburst.s_axi_awlock (s_axi_awlock), // input [0:0] s_axi_awlock.s_axi_awcache (s_axi_awcache), // input [3:0] s_axi_awcache.s_axi_awprot (s_axi_awprot), // input [2:0] s_axi_awprot.s_axi_awqos (s_axi_awqos), // input [3:0] s_axi_awqos.s_axi_awvalid (s_axi_awvalid), // input s_axi_awvalid.s_axi_awready (s_axi_awready), // output s_axi_awready// Slave Interface Write Data Ports.s_axi_wdata (s_axi_wdata), // input [63:0] s_axi_wdata.s_axi_wstrb (s_axi_wstrb), // input [7:0] s_axi_wstrb.s_axi_wlast (s_axi_wlast), // input s_axi_wlast.s_axi_wvalid (s_axi_wvalid), // input s_axi_wvalid.s_axi_wready (s_axi_wready), // output s_axi_wready// Slave Interface Write Response Ports.s_axi_bid (s_axi_bid), // output [3:0] s_axi_bid.s_axi_bresp (s_axi_bresp), // output [1:0] s_axi_bresp.s_axi_bvalid (s_axi_bvalid), // output s_axi_bvalid.s_axi_bready (s_axi_bready), // input s_axi_bready// Slave Interface Read Address Ports.s_axi_arid (s_axi_arid), // input [3:0] s_axi_arid.s_axi_araddr (s_axi_araddr), // input [30:0] s_axi_araddr.s_axi_arlen (s_axi_arlen), // input [7:0] s_axi_arlen.s_axi_arsize (s_axi_arsize), // input [2:0] s_axi_arsize.s_axi_arburst (s_axi_arburst), // input [1:0] s_axi_arburst.s_axi_arlock (s_axi_arlock), // input [0:0] s_axi_arlock.s_axi_arcache (s_axi_arcache), // input [3:0] s_axi_arcache.s_axi_arprot (s_axi_arprot), // input [2:0] s_axi_arprot.s_axi_arqos (s_axi_arqos), // input [3:0] s_axi_arqos.s_axi_arvalid (s_axi_arvalid), // input s_axi_arvalid.s_axi_arready (s_axi_arready), // output s_axi_arready// Slave Interface Read Data Ports.s_axi_rid (s_axi_rid), // output [3:0] s_axi_rid.s_axi_rdata (s_axi_rdata), // output [63:0] s_axi_rdata.s_axi_rresp (s_axi_rresp), // output [1:0] s_axi_rresp.s_axi_rlast (s_axi_rlast), // output s_axi_rlast.s_axi_rvalid (s_axi_rvalid), // output s_axi_rvalid.s_axi_rready (s_axi_rready), // input s_axi_rready// System Clock Ports.sys_clk_i (clk_200M), // input sys_clk_i 200M.sys_rst (rst_n) // input sys_rst
);//---------------------------------------FSM----------------------------------------------
always @(posedge ui_clk or posedge ui_clk_sync_rst) beginif(ui_clk_sync_rst) beginstate <= S_IDLE;endelse beginstate <= next_state;end
endalways @(*) begincase(state)S_IDLE: beginif(mmcm_locked && init_calib_complete) beginnext_state <= S_ARB;endelse beginnext_state <= S_IDLE;endendS_ARB: beginif(wr_ddr3_req) beginnext_state <= S_WR_ADDR;endelse if(rd_ddr3_req && (s_axi_araddr != s_axi_awaddr)) begin //DDR3不空,才可读取next_state <= S_RD_ADDR;endelse beginnext_state <= S_ARB;endendS_WR_ADDR: beginif(s_axi_awvalid && s_axi_awready) beginnext_state <= S_WR_DATA;endelse beginnext_state <= S_WR_ADDR;endendS_WR_DATA: beginif(data_cnt >= DATA_NUM) beginnext_state <= S_WR_RESP;endelse beginnext_state <= S_WR_DATA;endendS_WR_RESP: beginif(s_axi_bvalid && s_axi_bready) beginnext_state <= S_ARB;endelse beginnext_state <= S_WR_RESP;endendS_RD_ADDR: beginif(s_axi_arvalid && s_axi_arready) beginnext_state <= S_RD_DATA;endelse beginnext_state <= S_RD_ADDR;endendS_RD_DATA: beginif(data_cnt >= DATA_NUM) begin //m_axi_rready && m_axi_rvalid && m_axi_rlastnext_state <= S_RD_RESP;endelse beginnext_state <= S_RD_DATA;endendS_RD_RESP: beginnext_state <= S_ARB;enddefault: beginnext_state <= S_IDLE;endendcase
end//-------------------------------------Control--------------------------------------------
assign s_axi_awid = AXI_ID;
assign s_axi_arid = AXI_ID;assign s_axi_awlen = AXI_LEN;
assign s_axi_arlen = AXI_LEN;assign s_axi_awsize = 3'd3; //每个数据的大小为2^awsize = 2^3 = 8Bytes = 64bit
assign s_axi_awburst = 2'b01; //突发传输模式为INCR,地址一直增加,递增的值为(2^Burst_size)
assign s_axi_awlock = 1'b0; //正常传输,非独占传输
assign s_axi_awcache = 4'b0000; //指明了总线中的存储类型
assign s_axi_awprot = 3'b000; //指明访问是否被允许的信号
assign s_axi_awqos = 4'b0000;
assign s_axi_wstrb = 8'hff; //写字节通道选通assign s_axi_arsize = 3'd3;
assign s_axi_arburst = 2'b01; //突发传输模式为INCR
assign s_axi_arlock = 1'b0;
assign s_axi_arcache = 4'b0000;
assign s_axi_arprot = 3'b000;
assign s_axi_arqos = 4'b0000;assign full = w_fifo_full;
assign empty = r_fifo_empty;always @(posedge ui_clk) beginaresetn <= ~ui_clk_sync_rst;
endassign fifo_rst = ~mmcm_locked;
//fifo的rst必须同时存在rd_clk和wr_clk,否则将初始化失败,一直处在rst_busy阶段assign app_sr_req = 1'b0; //此三者要置零,否则 refresh DDR
assign app_ref_req = 1'b0;
assign app_zq_req = 1'b0;assign w_fifo_rst_busy = w_fifo_wr_rst_busy | w_fifo_rd_rst_busy;
assign r_fifo_rst_busy = r_fifo_wr_rst_busy | r_fifo_rd_rst_busy;assign rst_busy = w_fifo_rst_busy | r_fifo_rst_busy | (~init_calib_complete);//-----------s_axi_awvalid--------------
always @(*) begincase(state)S_WR_ADDR: begins_axi_awvalid <= 1'b1;enddefault: begins_axi_awvalid <= 1'b0;endendcase
end//-----------s_axi_wvalid---------------
always @(*) begincase(state)S_WR_DATA: beginif(~w_fifo_empty && data_cnt < DATA_NUM) begins_axi_wvalid <= 1'b1;endelse begins_axi_wvalid <= 1'b0;endenddefault: begins_axi_wvalid <= 1'b0;endendcase
end//-----------s_axi_wlast---------------
always @(*) begincase(state)S_WR_DATA: beginif(data_cnt == DATA_NUM - 1) begins_axi_wlast <= 1'b1;endelse begins_axi_wlast <= 1'b0;endenddefault: begins_axi_wlast <= 1'b0;endendcase
end//-----------s_axi_bready---------------
always @(*) begincase(state)S_WR_RESP: begins_axi_bready <= 1'b1;enddefault: begins_axi_bready <= 1'b0;endendcase
end//-----------s_axi_arvalid--------------
always @(*) begincase(state)S_RD_ADDR: begins_axi_arvalid <= 1'b1;enddefault: begins_axi_arvalid <= 1'b0;endendcase
end//-----------s_axi_rready---------------
always @(*) begincase(state)S_RD_DATA: beginif(~r_fifo_full && data_cnt < DATA_NUM) begins_axi_rready <= 1'b1;endelse begins_axi_rready <= 1'b0;endendS_RD_RESP: begins_axi_rready <= 1'b1;enddefault: begins_axi_rready <= 1'b0;endendcase
end//------------w_fifo_rden---------------
always @(*) begincase(state)S_WR_DATA: beginw_fifo_rden <= s_axi_wvalid & s_axi_wready;enddefault: beginw_fifo_rden <= 1'b0;endendcase
end//------------r_fifo_wren---------------
always @(*) begincase(state)S_RD_DATA: beginr_fifo_wren <= s_axi_rready & s_axi_rvalid;enddefault: beginr_fifo_wren <= 1'b0;endendcase
end//-------------data_cnt-----------------
always @(posedge ui_clk) begincase(state)S_IDLE, S_ARB: begindata_cnt <= 9'd0;endS_WR_DATA: beginif(s_axi_wvalid && s_axi_wready) begindata_cnt <= data_cnt + 1'b1;endelse begindata_cnt <= data_cnt;endendS_RD_DATA: beginif(s_axi_rvalid && s_axi_rready) begindata_cnt <= data_cnt + 1'b1;endelse begindata_cnt <= data_cnt;endenddefault: begindata_cnt <= data_cnt;endendcase
end//------------s_axi_awaddr--------------
always @(posedge ui_clk) begincase(state)S_IDLE: begins_axi_awaddr <= 31'd0;endS_WR_RESP: beginif(s_axi_bvalid && s_axi_bready) begins_axi_awaddr <= s_axi_awaddr + DATA_NUM * 8; //s_axi_awaddr为字节地址,每次写NUM个64bit数据end //超出最大范围后自动回到0else begins_axi_awaddr <= s_axi_awaddr;endenddefault: begins_axi_awaddr <= s_axi_awaddr;endendcase
end//------------s_axi_araddr--------------
always @(posedge ui_clk) begincase(state)S_IDLE: begins_axi_araddr <= 31'd0;endS_RD_RESP: beginif(s_axi_rvalid && s_axi_rready) begins_axi_araddr <= s_axi_araddr + DATA_NUM * 8;endelse begins_axi_awaddr <= s_axi_awaddr;endenddefault: begins_axi_araddr <= s_axi_araddr;endendcase
end//------------wr_ddr3_req---------------
always @(posedge ui_clk) begincase(state)S_ARB: beginif(w_fifo_rddat_cnt >= DATA_NUM) beginwr_ddr3_req <= 1'b1; //w_FIFO中数据足够一次突发传输,发起写请求endelse beginwr_ddr3_req <= 1'b0;endenddefault: beginwr_ddr3_req <= 1'b0;endendcase
end//------------rd_ddr3_req---------------
always @(posedge ui_clk) begincase(state)S_ARB: beginif((FIFO_LEN - r_fifo_wrdat_cnt) > DATA_NUM) beginrd_ddr3_req <= 1'b1; //r_FIFO中空闲位置足够一次突发传输,发起读请求endelse beginrd_ddr3_req <= 1'b0;endenddefault: beginrd_ddr3_req <= 1'b0;endendcase
endendmodule
testbench 如下
`timescale 100ps/100psmodule DDR3_tb();reg clk_200M = 1'b1;
always #25 beginclk_200M <= ~clk_200M;
endreg rst_n;
wire rst_busy;//--------------------ddr3------------------------
wire [63:0] ddr3_dq; //DQ,4片 x16 DDR的数据线wire [14:0] ddr3_addr; //Address
wire [2:0] ddr3_ba; //Bank Addresswire [7:0] ddr3_dqs_p; //DQ Select,0,2,4,6分别为四片DDR的DQSL(Lower Byte),
wire [7:0] ddr3_dqs_n; //1,3,5,7分别为四片DDR的DQSU(Upper Byte)
//Output with read data. Edge-aligned with read data.
//Input with write data. Center-aligned to write data.wire [0:0] ddr3_ck_p; //4片DR3共用ck、cke、odt、cs信号,故[0:0]
wire [0:0] ddr3_ck_n;
//differential clock inputs. All control and address input signals are sampled
//on the crossing of the positive edge of CK and the negative edge of CK#wire [7:0] ddr3_dm; //Input Data Mask,0,2,4,6为DML,1,3,5,7为DMU
wire [0:0] ddr3_cke; //Clock Enable
wire [0:0] ddr3_cs_n; //Chip Select
wire ddr3_ras_n; //Row Address Enable
wire ddr3_cas_n; //Column Address Enable
wire ddr3_we_n; //Write Enable
wire [0:0] ddr3_odt; //On-die termination enable
wire ddr3_reset_n;//-------------FPGA FIFO Control-------------------
reg wr_en; //高电平有效
reg [63:0] wrdat = 64'd0;
wire full; //fifo_w fullreg rd_en; //高电平有效
wire [63:0] rddat;
wire empty; //fifo_r empty//--------------------DDR3 Control-----------------------------------
DDR3_top DDR3_top_inst(.clk_200M (clk_200M),.rst_n (rst_n),.rst_busy (rst_busy),//--------------------ddr3------------------------.ddr3_dq (ddr3_dq),.ddr3_addr (ddr3_addr),.ddr3_ba (ddr3_ba),.ddr3_dqs_p (ddr3_dqs_p),.ddr3_dqs_n (ddr3_dqs_n),.ddr3_ck_p (ddr3_ck_p),.ddr3_ck_n (ddr3_ck_n),.ddr3_dm (ddr3_dm),.ddr3_cke (ddr3_cke),.ddr3_cs_n (ddr3_cs_n),.ddr3_ras_n (ddr3_ras_n),.ddr3_cas_n (ddr3_cas_n),.ddr3_we_n (ddr3_we_n),.ddr3_odt (ddr3_odt),.ddr3_reset_n (ddr3_reset_n),//-------------FPGA FIFO Control-------------------.wr_en (wr_en),.wrdat (wrdat),.full (full),.rd_en (rd_en),.rddat (rddat),.empty (empty)
);//--------------------DDR3 Model-----------------------------------
ddr3_model ddr3_b1 (.rst_n (ddr3_reset_n),.ck (ddr3_ck_p),.ck_n (ddr3_ck_n),.cke (ddr3_cke),.cs_n (ddr3_cs_n),.ras_n (ddr3_ras_n),.cas_n (ddr3_cas_n),.we_n (ddr3_we_n),.dm_tdqs (ddr3_dm[1:0]),.ba (ddr3_ba),.addr (ddr3_addr),.dq (ddr3_dq[15:0]),.dqs (ddr3_dqs_p[1:0]),.dqs_n (ddr3_dqs_n[1:0]),.tdqs_n (),.odt (ddr3_odt)
);ddr3_model ddr3_b2 (.rst_n (ddr3_reset_n),.ck (ddr3_ck_p),.ck_n (ddr3_ck_n),.cke (ddr3_cke),.cs_n (ddr3_cs_n),.ras_n (ddr3_ras_n),.cas_n (ddr3_cas_n),.we_n (ddr3_we_n),.dm_tdqs (ddr3_dm[3:2]),.ba (ddr3_ba),.addr (ddr3_addr),.dq (ddr3_dq[31:16]),.dqs (ddr3_dqs_p[3:2]),.dqs_n (ddr3_dqs_n[3:2]),.tdqs_n (),.odt (ddr3_odt)
);ddr3_model ddr3_b3 (.rst_n (ddr3_reset_n),.ck (ddr3_ck_p),.ck_n (ddr3_ck_n),.cke (ddr3_cke),.cs_n (ddr3_cs_n),.ras_n (ddr3_ras_n),.cas_n (ddr3_cas_n),.we_n (ddr3_we_n),.dm_tdqs (ddr3_dm[5:4]),.ba (ddr3_ba),.addr (ddr3_addr),.dq (ddr3_dq[47:32]),.dqs (ddr3_dqs_p[5:4]),.dqs_n (ddr3_dqs_n[5:4]),.tdqs_n (),.odt (ddr3_odt)
);ddr3_model ddr3_b4 (.rst_n (ddr3_reset_n),.ck (ddr3_ck_p),.ck_n (ddr3_ck_n),.cke (ddr3_cke),.cs_n (ddr3_cs_n),.ras_n (ddr3_ras_n),.cas_n (ddr3_cas_n),.we_n (ddr3_we_n),.dm_tdqs (ddr3_dm[7:6]),.ba (ddr3_ba),.addr (ddr3_addr),.dq (ddr3_dq[63:48]),.dqs (ddr3_dqs_p[7:6]),.dqs_n (ddr3_dqs_n[7:6]),.tdqs_n (),.odt (ddr3_odt)
);//--------------------wrdat, rddat-----------------------------------
always @(posedge clk_200M) beginif(wr_en) beginwrdat <= wrdat + 1'b1;endelse beginwrdat <= wrdat;end
end//-----------------------tb----------------------------------------
initial beginwr_en <= 1'b0;rd_en <= 1'b0;rst_n <= 1'b1;#100;rst_n <= 1'b0;#100;rst_n <= 1'b1;wait(~rst_busy);#200;fork begin: w_rwrite(16);#100;write(16);#100;write(16);#100;// #10000;read(16);#100;write(16);#100;read(64);#2000;disable shut_down;$stop;endbegin: shut_down#100000;disable w_r;$stop;endjoin
endtask write;input [7:0] num;integer i;beginfor(i=0; i<num; i=i) beginwait(clk_200M);if(~full) beginwr_en <= 1'b1;i <= i+1;endelse beginwr_en <= 1'b0;endwait(~clk_200M);endwait(clk_200M);wr_en <= 1'b0;end
endtasktask read;input [7:0] num;integer i;beginfor(i=0; i<num; i=i) beginwait(clk_200M);if(~empty) beginrd_en <= 1'b1;i <= i+1;endelse beginrd_en <= 1'b0;endwait(~clk_200M);endwait(clk_200M);rd_en <= 1'b0;end
endtaskendmodule
仿真结果如下
放大 FIFO 读写部分的结果如下
相关文章:
DDR3(MIG核配置官方demoFPGA代码实现及仿真)
由于直接对 DDR3 进行控制很复杂,因此一般使用 MIG IP 来实现,同时为了更简单地使用 MIG IP,我们采用 AXI4 总线协议进行控制。下面首先介绍 MIG IP 的配置,然后看看官方 demo (里面包含一个仿真要用到的 DDR3 模型&am…...
传奇人物《周兴和》书连载之67 不辱神圣的使命
不辱神圣的使命 这里,先前还是一个十分神秘的地方。 外人和车辆要想进入这片区域,那是绝对不允许的。这片区域隐于群山之中,且戒备森严,外人若想进入,那是要经过好几道政治审查和随身检查的。近年来,随着…...
Spring框架中的单例Beans是线程安全的么?
在Spring框架中,单例Beans默认是线程安全的。 当你在Spring框架中声明一个单例Bean并配置为默认的单例作用域时,Spring会确保对该Bean的并发访问是线程安全的。以下是一个简单的代码演示: 假设我们有一个名为 SingletonBean 的单例 Bean 类…...
AI脚本插件开发-链接图自动建立档名-插件制作源码-illustrator插件开发
文章目录 1.illustrator1.1.app.activeDocument1.2.selection2.模块分析3.源码工程4.功能描述5.作者答疑本文主要分析一款插件的源码,链接图自动建立档名,代码一般较长,读者耐心阅读,对于学习插件开发具有不小的帮助。先介绍了一下基础资料,如有不懂的地方,就去这些资料里…...
rust智能指针
智能指针 智能指针虽然也号称指针,但是它是一个复杂的家伙:通过比引用更复杂的数据结构,包含比引用更多的信息,例如元数据,当前长度,最大可用长度等。引用和智能指针的另一个不同在于前者仅仅是借用了数据…...
Git、Gitee、Github、Gitlab区别与联系
Git:本地软件,无需联网即可使用,实现本地代码的管理。 分布式版本控制系统,是一种工具,用于代码的存储和版本控制。 将本地文件通过一定的操作将其同步上传到Github或Gitee Gitee:是一家中…...
接口优化的策略
1.批处理 批量思想:批量操作数据库,这个很好理解,我们在循环插入场景的接口中,可以在批处理执行完成后一次性插入或更新数据库,避免多次IO。 //批量入库 batchInsert();List的安全操作有以下几种方式: 使…...
android 隐藏底部虚拟按键
方法一 滑动屏幕 可重新显示出来 protected void hideBottomUIMenu() { //隐藏虚拟按键,并且全屏 if (Build.VERSION.SDK_INT <11 && Build.VERSION.SDK_INT < 19) { // lower api View v this.getWindow().getDecorView(); v.setSyst…...
基于电流控制的并网逆变器(Simulink)
💥💥💞💞欢迎来到本博客❤️❤️💥💥 🏆博主优势:🌞🌞🌞博客内容尽量做到思维缜密,逻辑清晰,为了方便读者。 ⛳️座右铭&a…...
learn_C_deep_9 (汇编角度理解return的含义、const 的各种应用场景)
return 关键字 不知道我们大家是否有一个疑惑:我们下载一个大型游戏软件(王者荣耀),都要花几个小时去下载,但是一旦我们游戏连输,想要删除这个软件的时候,它仅仅只需要十几秒,这是为…...
基于深度学习的OCR技术
随着数字化时代的到来,图片识别技术越来越受到人们的关注。其中,OCR技术作为图片处理的一个重要分支,可以将扫描的图片进行自动识别和分类,极大地提高了工作效率。本文将介绍有道实况OCR技术的相关内容,帮助读者更好地…...
『python爬虫』09. bs4实战之下载精美壁纸(保姆级图文)
目录 爬取思路代码思路1.拿到主页面的源代码. 然后提取到子页面的链接地址, href2.通过href拿到子页面的内容. 从子页面中找到图片的下载地址 img -> src3.下载图片 3. 完整实现代码总结 欢迎关注 『python爬虫』 专栏,持续更新中 欢迎关注 『python爬虫』 专栏&…...
【Linux学习】多线程——线程控制 | 线程TCB
🐱作者:一只大喵咪1201 🐱专栏:《Linux学习》 🔥格言:你只管努力,剩下的交给时间! 线程控制 | 线程TCB 🧰线程控制🎴线程创建🎴线程结束…...
Node 10 接口
接口 简介 接口是什么 接口是 前后端通信的桥梁 简单理解:一个接口就是 服务中的一个路由规则 ,根据请求响应结果 接口的英文单词是 API (Application Program Interface),所以有时也称之为 API 接口 这里的接口指的是『数据接口』&#…...
大型互联网企业大流量高并发电商领域核心项目已上线(完整流程+项目白皮书)
说在前面的话 面对近年来网络的飞速发展,大家已经都习惯了网络购物,从而出现了一些衍生品例如:某宝/某东/拼夕夕等大型网站以及购物APP~ 并且从而导致很多大型互联网企业以及中小厂都需要有完整的项目经验,以及优秀处理超大流量…...
汇编语言学习笔记六
flag 寄存器 CF:进位标志位,产生进位CF1,否则为0 PF:奇偶位,如010101b,则该数的1有3个,则PF0,如果该数的1的个数为偶数,则PF1。0也是偶数 ZF:在相关指令执行后(运算和逻辑指令,传送指…...
多商户商城系统-v2.2.3版本发布
likeshop多商户商城系统-v2.2.3版本发布了!主要更新内容如下 新增 1.用户端退出账号功能 优化 1.平台添加营业执照保存异常问题 2.平台端分销商品优化-只显示参与分销的商品 3.优化订单详情显示营销价格标签 4.平台交易设置增加默认值 5.种草社区评论调整&a…...
科研人必看入门攻略(收藏版)
来源:投稿 作者:小灰灰 编辑:学姐 本文主要以如何做科研,日常内功修炼,常见科研误区,整理日常‘好论文’四个部分做以介绍,方便刚入门的科研者进行很好的规划。 1.如何做科研 1.1 选方向 当我…...
第5章 循环和关系表达式
1. strcmp()//比较字符串数组是否相等| string 可以直接用“”来判断 char word[5] "aaaa"; strcmp(word,"aaab");//相同输出0,不同输出1; 2. 延时函数 #include<ctime>float sec 2.3;long delay sec*CLOCKS_PER_SEC;long start c…...
Scalable Vector Graphics (SVG)中的svg、clipPath、mask元素
Scalable Vector Graphics (SVG)是一种用于描述二维向量图形的XML基础标记语言。使用SVG可以实现丰富的图形效果,而不需要像使用位图那样考虑分辨率和像素密度的问题,可以在不同设备上展示出相同的高质量图像。 在SVG中,除了基本形状如circl…...
Java基础(十五)集合框架
1. 集合框架概述 1.1 生活中的容器 1.2 数组的特点与弊端 一方面,面向对象语言对事物的体现都是以对象的形式,为了方便对多个对象的操作,就要对对象进行存储。另一方面,使用数组存储对象方面具有一些弊端,而Java 集合…...
安装gitea
1、安装包(gitea-1.13.1-linux-amd64)上传到服务器,并添加执行权限 链接:https://pan.baidu.com/s/1SAxko0RhVmmD21Ev_m5JFg 提取码:ft07 chmod x gitea-1.13.1-linux-amd64 2、执行 ./gitea-1.13.1-linux-amd64 web…...
Java异常处理传递规范总结
java 异常分类 Thorwable类(表示可抛出)是所有异常和错误的超类,两个直接子类为Error和Exception,分别表示错误和异常。其中异常类Exception又分为运行时异常(RuntimeException)和非运行时异常, 这两种异常有很大的区别…...
2d俯视视角游戏,可以切换多种枪械
文章目录 一、 介绍二、 人物移动、鼠标控制转向三、子弹脚本四、子弹随机抛壳五、 爆炸特效六、 发射子弹七、 子弹、弹壳对象池八、 散弹枪九、 火箭弹、发射火箭十、 下载工程文件 一、 介绍 2d俯视视角游戏。 人物视角跟随鼠标移动 多种枪械 抛壳效果 多种设计效果 对象池…...
大四的告诫
保研/考研方向就绩点,(各种)比赛,(考研)刷题为主 工作就算法(比赛),项目,实习为主 👂 LOCK OUT - $atori Zoom/KALONO - 单曲 - 网易云音乐 &…...
滚珠螺杆在设备上的应用
滚珠螺杆跟直线导轨一样,是很多机械设备上不可或缺的重要部件,它是确保机器能够具备高加工精度的前提条件,因此本身对于精度的要求也相当地高。今天,我们就来了解一下滚珠螺杆在不同设备上的应用吧! 1、大型的加工中心…...
Day41线程同步
线程同步 案例:三个窗口卖100张票 //定义一个类SellTicket实现Runnable接口,定义成员变量100张票 public class SellTicket implements Runnable{private int tickets 100;//重写run方法Overridepublic void run(){while (true){ //没有票后&…...
设计模式之享元模式
参考资料 曾探《JavaScript设计模式与开发实践》;「设计模式 JavaScript 描述」享元模式设计模式之享元模式Javascript 设计模式 - 享元模式 定义 享元模式的英文叫:Flyweight Design Pattern。享元设计模式是用于性能优化的模式,这种设计…...
【GAMES101】05 Rasterization(Triangles)
光栅化过程:将一系列变换后的三角形转换为像素的过程。 三角形在图形学中得到很多的应用。 最基础的多边形(边数最少)。任何多边形都可以拆成三角形。性质:三角形内部一定是平面的。三角形内外部定义非常清楚。定义三个顶点后&a…...
13. Pod 从入门到深入理解(二)
本章讲解知识点 Pod 容器共享 VolumeConfigMapSecretDownward APIEmptyDir VolumeHostPath Volume1. Pod 容器共享 Volume 1.1. Volume 的背景及需要解决的问题 存储是必不可少的,对于服务运行产生的日志、数据,必须有一个地方进行保存,但是我们的容器每一次重启都是“恢复…...
wordpress分类目录优化/注册一个公司网站需要多少钱
嵌套函数:什么是嵌套函数 使用外部函数中变量 def out():x 5def inn():print("inn函数中 x {}".format(y))print("out函数中 x {}".format(x))inn()out()结果: inn函数中 x 5 out函数中 x 5 内部函数是可以引用外部函数的变量…...
玻璃行业做的非常有设计感的网站/品牌网站建设方案
Java输入输出流 生活中无处不在,只要涉及到传输。复制粘贴操作;修改头像,将本地数据上传到网络服务器。 System.out.println("mtianyan");将字符串输出到屏幕上。 字符从河流中依次通过,形成了字符流。 流就是指一连串流动的字符,以…...
公司的网站如何进行修改布局/南宁seo费用服务
AngularJS的四大特性的思维导图如下: 将AngularJS应用于工作:其思维导图如下: AngularJS服务思维导图: 转载于:https://www.cnblogs.com/PHM64123/p/7660985.html...
专业做破碎机的网站/日本免费服务器ip地址
配置tokenstore.js,state中的数据一刷新就会消失,所以需要在localStorage中重新获取state: {//从localStorage中获取,没有则为空字符串token:localStorage.getItem(token)||},mutations: {//设置state.token对象setUtoken(state,token){state…...
自己做的网站根目录哪里找到/seo是如何优化
有谁需要阿里云一键安装包吗?https://market.aliyun.com/products/56014009/cmgj000262.html 可以到这里去下载使用https://github.com/drinkboyyu/opt_shell 大家如果对于使用有问题,或者以前使用过,现在想升级nginx、php、mysql等版本&…...