硬件
参考:
https://zhuanlan.zhihu.com/p/97491454
https://blog.csdn.net/qq_22222449/article/details/106492469
https://zhuanlan.zhihu.com/p/26327347
https://zhuanlan.zhihu.com/p/582524766
包括野火、正点原子的资料
一片内存是 1Gbit 128MByte 16bit DDR3,也就是下图里的 64Meg x 16,mig 里要选 …64M16…,这个关系到地址线的个数,下图中同容量不同位宽的内存芯片的 ‘… addressing’ 部分是有不同的,有的少有的多。在硬件上只要连 A0-A12 就行了,除非是要兼容更大的内存
行地址(row addr)总线为 A0-A12,行地址位宽为 13 位 , 行地址数为 8192(213)(8K) 行,列地址(column addr)为 A0-A9,位宽为 10 位 , 列地址数为1024(210)(1K)列,单个 Bank 中包含的存储单元个数为行地址数(8192)与列地址数(1024)相乘,8K×1K=8M(8Meg);“16”表示数据位宽,即每个存储单元存储数据的 bit 数;8Meg 与 16 相乘表示单个 Bank 中可存储的 Bit 数;“8 banks”表示一片 SDRAM 中包含的 Bank 个数,此 DDR3 SDRAM 芯片包含 8 个 Bank;由此可得 DDR3 SDRAM 芯片的存储容量为:1024MBit (8Meg×16×8 banks)
如果是 256MByte 16bit 位宽的内存,banks 同样为 8 的话,那就是 16Meg(地址数) x 16bit x 8 banks,Row addr 就有 14 位,那么 mig 里就选 …128M16…
假设 mig ip 里的 Memory Part 选择 MT41J128M16XX-125,结尾数字里的意思:
Data Rate 是传输频率,电脑上的 DDR3 1866 就是这个意思
-125 所支持的最小时钟周期 tCK = 1.25ns,对应芯片支持的最大 IO 时钟频率为 800MHz,数据传输频率就是 1600MHz。传输频率 x 位宽 = 传输速率,MT41J128M16XX-125 的位宽是 16bit,最大传输速率就是 1600MHz x 16bit(2Byte) = 3200MB = 3.125GiB
下图是各频率之间的关系,100MHz 核心频率 = 400MHz IO 频率 = 800MHz 传输频率。mig ip 传输 400MHz IO 频率给 ddr3,ddr3 内部获得 100MHz 的核心频率
MIG ip 配置
下图:
勾选了 AXI
下图:
Clock Period:400MHz,是给 DDR3 的 IO 时钟,一片内存的传输速率就是 800MHz * 16bit(2Byte) = 1600MB = 1.5625GiByte,两片就是 3200MB = 3.125GiByte
PHY to Controller Clock Ratio:mig ip 反给用户的时钟,因为之前勾选了 axi,也就是 axi 给用户用的时钟,Clock Period 除以 4,所以 axi 的时钟是 100MHz
Data Width:两片内存就是 32bit
Number of Bank Machines: 不是 banks 数,是一种 bank 控制策略,保持默认
下图:
Data Width:AXI 的位宽,AXI 时钟(100MHz) x AXI 的位宽(32Byte) = 3200MByte,这样才可能跑满两片 ddr3 的速率
下图:
Input Clock Period:用户给 mig ip 的时钟,用 mmcm 传给 mig
下图:
Reference Clock:前一步 mig ip 的时钟输入选 200MHz 这里才能选 Use System Clock,这样 ip 实例化的时候就可以少一个 ref_clk 的接口
Internal Vref:
使用内部 Vref。Vref 是 FPGA 的 IO Bank 参考电压引脚,一般要外接参考电压,但是这里若传输速率小于等于 800MHz 就可以不用外接,转而使用内部 Vref,然后该 Vref 引脚就可以空出来给用户使用,这里 mig 需要两个 IO Bank,就能空出两个 Vref 引脚,然后就可以把这两个 Vref 连到 ddr 的地址线上。如果 ddr 的 IO 已经连了 Vref 就必选了,不然 IO Bank 没参考电压
下图:
先读取 .ucf,再校验
ddr3.ucf:
# clock, reset
NET "ddr3_ck_p[0]" LOC = "R3";
NET "ddr3_ck_n[0]" LOC = "R2";
NET "ddr3_reset_n" LOC = "W6";
# global control
NET "ddr3_cs_n[0]" LOC = "AB3";
NET "ddr3_cke[0]" LOC = "T5";
NET "ddr3_odt[0]" LOC = "U5";
NET "ddr3_we_n" LOC = "AA1";
# address control
NET "ddr3_ba[0]" LOC = "AA3";
NET "ddr3_ba[1]" LOC = "Y3";
NET "ddr3_ba[2]" LOC = "Y4";
NET "ddr3_ras_n" LOC = "V4";
NET "ddr3_cas_n" LOC = "W4";
# address
NET "ddr3_addr[0]" LOC = "AA4";
NET "ddr3_addr[1]" LOC = "AB2";
NET "ddr3_addr[2]" LOC = "AA5";
NET "ddr3_addr[3]" LOC = "AB5";
NET "ddr3_addr[4]" LOC = "AB1";
NET "ddr3_addr[5]" LOC = "U3";
NET "ddr3_addr[6]" LOC = "W1";
NET "ddr3_addr[7]" LOC = "T1";
NET "ddr3_addr[8]" LOC = "V2";
NET "ddr3_addr[9]" LOC = "U2";
NET "ddr3_addr[10]" LOC = "Y1";
NET "ddr3_addr[11]" LOC = "W2";
NET "ddr3_addr[12]" LOC = "Y2";
NET "ddr3_addr[13]" LOC = "U1";
# data control
NET "ddr3_dqs_p[0]" LOC = "E1";
NET "ddr3_dqs_p[1]" LOC = "K2";
NET "ddr3_dqs_p[2]" LOC = "M1";
NET "ddr3_dqs_p[3]" LOC = "P5";
NET "ddr3_dqs_n[0]" LOC = "D1";
NET "ddr3_dqs_n[1]" LOC = "J2";
NET "ddr3_dqs_n[2]" LOC = "L1";
NET "ddr3_dqs_n[3]" LOC = "P4";
NET "ddr3_dm[0]" LOC = "D2";
NET "ddr3_dm[1]" LOC = "G2";
NET "ddr3_dm[2]" LOC = "M2";
NET "ddr3_dm[3]" LOC = "M5";
# data
NET "ddr3_dq[0]" LOC = "C2";
NET "ddr3_dq[1]" LOC = "G1";
NET "ddr3_dq[2]" LOC = "A1";
NET "ddr3_dq[3]" LOC = "F3";
NET "ddr3_dq[4]" LOC = "B2";
NET "ddr3_dq[5]" LOC = "F1";
NET "ddr3_dq[6]" LOC = "B1";
NET "ddr3_dq[7]" LOC = "E2";
NET "ddr3_dq[8]" LOC = "H3";
NET "ddr3_dq[9]" LOC = "G3";
NET "ddr3_dq[10]" LOC = "H2";
NET "ddr3_dq[11]" LOC = "H5";
NET "ddr3_dq[12]" LOC = "J1";
NET "ddr3_dq[13]" LOC = "J5";
NET "ddr3_dq[14]" LOC = "K1";
NET "ddr3_dq[15]" LOC = "H4";
NET "ddr3_dq[16]" LOC = "L4";
NET "ddr3_dq[17]" LOC = "M3";
NET "ddr3_dq[18]" LOC = "L3";
NET "ddr3_dq[19]" LOC = "J6";
NET "ddr3_dq[20]" LOC = "K3";
NET "ddr3_dq[21]" LOC = "K6";
NET "ddr3_dq[22]" LOC = "J4";
NET "ddr3_dq[23]" LOC = "L5";
NET "ddr3_dq[24]" LOC = "P1";
NET "ddr3_dq[25]" LOC = "N4";
NET "ddr3_dq[26]" LOC = "R1";
NET "ddr3_dq[27]" LOC = "N2";
NET "ddr3_dq[28]" LOC = "M6";
NET "ddr3_dq[29]" LOC = "N5";
NET "ddr3_dq[30]" LOC = "P6";
NET "ddr3_dq[31]" LOC = "P2";
代码
mig.v:
module mig (
input clk,
input rst_n,
// ddr3
output [13 : 0] ddr3_addr, // output [13 : 0] ddr3_addr
output [ 2 : 0] ddr3_ba, // output [ 2 : 0] ddr3_ba
output ddr3_cas_n, // output ddr3_cas_n
output ddr3_ck_n, // output ddr3_ck_n
output ddr3_ck_p, // output ddr3_ck_p
output ddr3_cke, // output ddr3_cke
output ddr3_ras_n, // output ddr3_ras_n
output ddr3_reset_n, // output ddr3_reset_n
output ddr3_we_n, // output ddr3_we_n
inout [31 : 0] ddr3_dq, // inout [31 : 0] ddr3_dq
inout [ 3 : 0] ddr3_dqs_n, // inout [ 3 : 0] ddr3_dqs_n
inout [ 3 : 0] ddr3_dqs_p, // inout [ 3 : 0] ddr3_dqs_p
output [ 0 : 0] ddr3_cs_n, // output [ 0 : 0] ddr3_cs_n
output [ 3 : 0] ddr3_dm, // output [ 3 : 0] ddr3_dm
output [ 0 : 0] ddr3_odt, // output [ 0 : 0] ddr3_odt
// user
// axi
output ui_clk, // output ui_clk
output ui_clk_sync_rst, // output ui_clk_sync_rst
// read address
input [28 : 0] s_axi_araddr, // input [28 : 0] s_axi_araddr
input [ 7 : 0] s_axi_arlen, // input [ 7 : 0] s_axi_arlen
input s_axi_arvalid, // input s_axi_arvalid
output s_axi_arready, // output s_axi_arready
// read data
output [255 : 0] s_axi_rdata, // output [255 : 0] s_axi_rdata
output s_axi_rlast, // output s_axi_rlast
input s_axi_rready, // input s_axi_rready
output s_axi_rvalid, // output s_axi_rvalid
// write address
input [28 : 0] s_axi_awaddr, // input [28 : 0] s_axi_awaddr
input [ 7 : 0] s_axi_awlen, // input [ 7 : 0] s_axi_awlen
input s_axi_awvalid, // input s_axi_awvalid
output s_axi_awready, // output s_axi_awready
// wirte data
input [255 : 0] s_axi_wdata, // input [255 : 0] s_axi_wdata
input [ 31 : 0] s_axi_wstrb, // input [ 31 : 0] s_axi_wstrb
input s_axi_wlast, // input s_axi_wlast
input s_axi_wvalid, // input s_axi_wvalid
output s_axi_wready, // output s_axi_wready
// write response
output s_axi_bvalid, // output s_axi_bvalid
input s_axi_bready // input s_axi_bready
);
mig_7series_0 mig_7series_0_i (
.sys_clk_i(clk),
.sys_rst (rst_n),
// ddr3
.ddr3_addr (ddr3_addr), // output [13 : 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 ddr3_ck_n
.ddr3_ck_p (ddr3_ck_p), // output ddr3_ck_p
.ddr3_cke (ddr3_cke), // output 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(), // 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
// user
// axi
.ui_clk (ui_clk), // output ui_clk
.ui_clk_sync_rst(ui_clk_sync_rst), // output ui_clk_sync_rst
.mmcm_locked (), // output mmcm_locked
.aresetn (rst_n), // input aresetn
.app_sr_req (0), // input app_sr_req
.app_ref_req (0), // input app_ref_req
.app_zq_req (0), // input app_zq_req
.app_sr_active (), // output app_sr_active
.app_ref_ack (), // output app_ref_ack
.app_zq_ack (), // output app_zq_ack
// read address
.s_axi_arid (0), // input [ 0 : 0] s_axi_arid
.s_axi_araddr (s_axi_araddr), // input [28 : 0] s_axi_araddr
.s_axi_arlen (s_axi_arlen), // input [ 7 : 0] s_axi_arlen
.s_axi_arsize (5), // input [ 2 : 0] s_axi_arsize
.s_axi_arburst(1), // input [ 1 : 0] s_axi_arburst
.s_axi_arlock (0), // input [ 0 : 0] s_axi_arlock
.s_axi_arcache(0), // input [ 3 : 0] s_axi_arcache
.s_axi_arprot (0), // input [ 2 : 0] s_axi_arprot
.s_axi_arqos (0), // 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
// read data
.s_axi_rid (), // output [ 0 : 0] s_axi_rid
.s_axi_rdata (s_axi_rdata), // output [255 : 0] s_axi_rdata
.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
// write address
.s_axi_awid (0), // input [ 0 : 0] s_axi_awid
.s_axi_awaddr (s_axi_awaddr), // input [28 : 0] s_axi_awaddr
.s_axi_awlen (s_axi_awlen), // input [ 7 : 0] s_axi_awlen
.s_axi_awsize (5), // input [ 2 : 0] s_axi_awsize
.s_axi_awburst(1), // input [ 1 : 0] s_axi_awburst
.s_axi_awlock (0), // input [ 0 : 0] s_axi_awlock
.s_axi_awcache(0), // input [ 3 : 0] s_axi_awcache
.s_axi_awprot (0), // input [ 2 : 0] s_axi_awprot
.s_axi_awqos (0), // 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
// wirte data
.s_axi_wdata (s_axi_wdata), // input [255 : 0] s_axi_wdata
.s_axi_wstrb (s_axi_wstrb), // input [ 31 : 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
// write response
.s_axi_bid (), // output [0 : 0] s_axi_bid
.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
);
endmodule
work:
module work (
input clk,
input rst_n,
// ddr3
output [13 : 0] ddr3_addr,
output [ 2 : 0] ddr3_ba,
output ddr3_cas_n,
output ddr3_ck_n,
output ddr3_ck_p,
output ddr3_cke,
output ddr3_ras_n,
output ddr3_reset_n,
output ddr3_we_n,
inout [31 : 0] ddr3_dq,
inout [ 3 : 0] ddr3_dqs_n,
inout [ 3 : 0] ddr3_dqs_p,
output [ 0 : 0] ddr3_cs_n,
output [ 3 : 0] ddr3_dm,
output [ 0 : 0] ddr3_odt
);
// mig
wire mig_clk;
wire mig_ui_clk;
wire mig_ui_clk_sync_rst;
reg [28 : 0] mig_s_axi_araddr;
reg [ 7 : 0] mig_s_axi_arlen;
reg mig_s_axi_arvalid;
wire mig_s_axi_arready;
wire [255 : 0] mig_s_axi_rdata;
wire mig_s_axi_rlast;
wire mig_s_axi_rvalid;
reg [28 : 0] mig_s_axi_awaddr;
reg [ 7 : 0] mig_s_axi_awlen;
reg mig_s_axi_awvalid;
wire mig_s_axi_awready;
reg [255 : 0] mig_s_axi_wdata;
reg [ 31 : 0] mig_s_axi_wstrb;
reg mig_s_axi_wlast;
reg mig_s_axi_wvalid;
wire mig_s_axi_wready;
wire mig_s_axi_bvalid;
mig mig_i (
.clk (mig_clk),
.rst_n(rst_n),
// ddr3
.ddr3_addr (ddr3_addr), // output [13 : 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 ddr3_ck_n
.ddr3_ck_p (ddr3_ck_p), // output ddr3_ck_p
.ddr3_cke (ddr3_cke), // output 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
// user
// axi
.ui_clk (mig_ui_clk), // output ui_clk
.ui_clk_sync_rst(mig_ui_clk_sync_rst), // output ui_clk_sync_rst
// read address
.s_axi_araddr (mig_s_axi_araddr), // input [28 : 0] s_axi_araddr
.s_axi_arlen (mig_s_axi_arlen), // input [ 7 : 0] s_axi_arlen
.s_axi_arvalid(mig_s_axi_arvalid), // input s_axi_arvalid
.s_axi_arready(mig_s_axi_arready), // output s_axi_arready
// read data
.s_axi_rdata (mig_s_axi_rdata), // output [255 : 0] s_axi_rdata
.s_axi_rlast (mig_s_axi_rlast), // output s_axi_rlast
.s_axi_rvalid(mig_s_axi_rvalid), // output s_axi_rvalid
.s_axi_rready(mig_s_axi_rvalid), // input s_axi_rready
// write address
.s_axi_awaddr (mig_s_axi_awaddr), // input [28 : 0] s_axi_awaddr
.s_axi_awlen (mig_s_axi_awlen), // input [ 7 : 0] s_axi_awlen
.s_axi_awvalid(mig_s_axi_awvalid), // input s_axi_awvalid
.s_axi_awready(mig_s_axi_awready), // output s_axi_awready
// wirte data
.s_axi_wdata (mig_s_axi_wdata), // input [255 : 0] s_axi_wdata
.s_axi_wstrb (mig_s_axi_wstrb), // input [ 31 : 0] s_axi_wstrb
.s_axi_wlast (mig_s_axi_wlast), // input s_axi_wlast
.s_axi_wvalid(mig_s_axi_wvalid), // input s_axi_wvalid
.s_axi_wready(mig_s_axi_wready), // output s_axi_wready
// write response
.s_axi_bvalid(mig_s_axi_bvalid), // output s_axi_bvalid
.s_axi_bready(mig_s_axi_bvalid) // input s_axi_bready
);
// mig 读写测试
reg [255 : 0] wdata;
reg [3 : 0] step;
always @(posedge mig_ui_clk) begin
if(mig_ui_clk_sync_rst) begin
mig_s_axi_araddr <= 0;
mig_s_axi_arlen <= 0;
mig_s_axi_arvalid <= 0;
mig_s_axi_awaddr <= 0;
mig_s_axi_awlen <= 0;
mig_s_axi_awvalid <= 0;
mig_s_axi_wdata <= 0;
mig_s_axi_wstrb <= 0;
mig_s_axi_wlast <= 0;
mig_s_axi_wvalid <= 0;
wdata <= 0;
step <= 0;
end
else begin
case (step)
0: begin
// 准备写地址
mig_s_axi_awaddr <= 0;
mig_s_axi_awlen <= 1 - 1;
mig_s_axi_awvalid <= 1; // 写地址有效
wdata <= wdata + 1;
step <= step + 1;
end
1: begin
// 待地址写入成功
if (mig_s_axi_awready) begin
mig_s_axi_awvalid <= 0; // 写地址无效
// 准备写数据
mig_s_axi_wdata <= wdata;
mig_s_axi_wstrb <= {256{1'b1}};
mig_s_axi_wlast <= 1;
mig_s_axi_wvalid <= 1; // 写数据有效
step <= step + 1;
end
end
2: begin
// 待数据写入成功
// 只写了一次可以直接判断
if (mig_s_axi_wready) begin
mig_s_axi_wlast <= 0;
mig_s_axi_wvalid <= 0; // 写数据无效
step <= step + 1;
end
end
3: begin
// 待写响应有效
if (mig_s_axi_bvalid) begin
step <= step + 1;
end
end
// 至此,写入成功
4: begin
// 准备读地址
mig_s_axi_araddr <= 0;
mig_s_axi_arlen <= 1 - 1;
mig_s_axi_arvalid <= 1; // 读地址有效
step <= step + 1;
end
5: begin
// 待地址写入成功
if (mig_s_axi_arready) begin
mig_s_axi_arvalid <= 0; // 读地址无效
step <= step + 1;
end
end
6: begin
// 待数据接收
if (mig_s_axi_rvalid) begin
step <= 0;
end
end
endcase
end
end
clk_wiz_0 clk_wiz_0_i (
.clk_in1(clk), // input clk_in1
.resetn (rst_n), // input reset
// user
.clk_out1(mig_clk), // output clk_out1 200M
);
ila_0 ila_0_i (
.clk(mig_ui_clk), // input wire clk
.probe0(step), // input wire [3:0] probe0
.probe1(mig_s_axi_rdata) // input wire [255:0] probe1
);
endmodule