概述
FPGA_MIG驱动DDR3
说明:
FPGA: zynq(7z100)。
DDR3:MT41K256M16TW-107:内存大小为512MB,数据接口为16bit。。
环境:Vivado2018.2。
IP核:Memory Interface Generator(MIG 7 Series)。
参考手册:ug586(7 Series Devices Memory Interface Solutions v4.1)。
源码:源码下载
文章目录
- 1.DDR型号解析
- 1.1DDR3地址
- 2.FPGA(MIG)与DDR3连接示意图.
- 3.DDR端口介绍
- 4.MIG_UI端口介绍
- 5.MIG 控制器概述及读写时序介绍
- 5.1.MIG内存控制器用户逻辑时序
- 5.2.BL8 突发长度
- 5.3.MIG时钟和DDR3时钟
- 5.4.掩码_Mask
- 6. MIG IP核配置
- 7.实战-DDR3读写
- 7.1原理图
- 7.2IP核配置以及Example代码修改及分析
- 8.附带源码
1.DDR型号解析
MT41K256M16TW-107:
内存大小为512MB,数据接口为16bit。
为什么是512MB:256M空间,数据线为16bit,1Byte=8bit,256M×16bit=512M×8bit=512MB。
下图为型号参数的具体解析:
1.1DDR3地址
DDR3的内部是一个存储阵列,将数据“填”进去,你可以它想象成一张表格。和表格的检索原理一样,先指定一个行(Row),再指定一个列(Column),我们就可以准确地找到所需要的单元格,这就是内存芯片寻址的基本原理。对于内存,这个单元格可称为存储单元,那么这个表格(存储阵列)就是逻辑 Bank(Logical Bank,下面简称Bank)。 DDR3内部Bank示意图,这是一个NXN的阵列,B代表Bank地址编号,C代表列地址编号,R代表行地址编号。
以下列举了不同容量的DDR3地址:
注意以上单位为GBit。比如MT41K256M16TW-107容量为512MB=512×8Mbit=4096Mbit=4Gbit,就要看表2.11.4 4Gb中的256M×16。
2.FPGA(MIG)与DDR3连接示意图.
如下图所示:FPGA用户逻辑↔MIG↔DDR3的端口连接示意图:
3.DDR端口介绍
符号 | 类型 | 功能 |
---|---|---|
CK、CK# | 输入 | 时钟:CK和CK#是差分时钟输入。所有地址和控制输入信号均为在CK的上升沿和CK#的下降沿的交点上采样。 |
CKE, (CKE0),(CKE1) | 输入 | 时钟使能。使能(高)和禁止(低)内部电路和DRAM上的时钟。由DDR3SDRAM配置和操作模式决定特定电路被使能和禁止。CKE为低,提供PRECHARGE POWER-DOWN和SELF REFRESH操作(所有Bank都处于空闲),或者有效掉电(在任何bank里的行有效)。CKE与掉电状态的进入退出以及自刷新的进入同步。CKE与自刷新的退出异步,输入Buffer (除了CK、CK#、RESE T#和ODT)在POWER-DOWN期间被禁止。输入Buffer (除了CKE和RESET#)在SEL _F REFRESH期间被禁止。CKE的参考是VREFCA 。 |
CS#, (CS0#), (CS1#),(CS2#), (CS3#) | 输入 | 片选:当CS#为高时,所有命令均被屏蔽。CS#提供外部 具有多个等级的系统上的等级选择。CS#被视为命令代码的一部分。 |
ODT, (ODT0),(ODT1) | 输入 | 片上终端使能: ODT 使能(高)和禁止(低)片内终端电阻。 |
RAS#. CAS#. WE# | 输入 | 命令输入:RAS#,CAS#和WE#(以及CS#)定义了要输入的命令。 |
DM, (DMU), (DML) | 输入 | 数据输入屏蔽:DM是用于写入数据的输入掩码信号。 在写访问期间采样到的高电平,输入数据被屏蔽 |
BA0 - BA2 | 输入 | BANK址输入:BA0-BA2定义ACTIVATE、读取、写入或PRECHARGE是对哪个BANK操作的。 |
A0 - A15 | 输入 | 地址输入:为ACTIVATE命令提供行地址,为读取/提供列地址 编写命令以从相应存储体的存储器阵列中选择一个位置。。地址输入还提供操作码 在模式寄存器设置命令期间。 |
A10 / AP | 输入 | 自动预充位 :A10在PRECHARGE命令期间被采样,已确定PRECHARGE是否应用于某个BANK;A10为低,这个BANK由BA[2:0]来选择,A10为高,对所有BANK。 |
A12 / BC# | 输入 | burst chop:在读取和写入命令期间对A12 / BC#进行采样,以确定是否存在burst chop将被执行。(高,无burst chop;低:burst chop)。见命令表以了解详细信息。 |
RESET# | 输入 | 低电平有效异步复位:当RESET#为低电平时,复位有效。在正常操作期间,RESET#必须为高。 |
DQ | 输入输出 | 数据输入/输出:双向数据总线。 |
DQU, DQL, DQS,DQS#, DQSU,DQSU#, DQSL,DQSL# | 输入输出 | 数据选通:读时输出,边缘与读出的数据对齐,写时输入,中心与写数据对齐 。 |
4.MIG_UI端口介绍
以下为FPGA的MIG IP核用户界面接口。
app_addr[ADD_WIDTH-1:0] | 输入 | 此输入指示当前提交给UI的请求的地址。 UI聚合外部SDRAM的所有地址字段,并提供平面地址空间(Rank,bank,row,colum)。 |
---|---|---|
app_cmd[2:0] | 输入 | 此输入指定当前提交给UI的请求的命令。 |
app_en | 输入 | 此输入在请求中变化。 您必须将所需的值应用于app_addr []和app_cmd [2:0],然后断言app_en以将请求提交给UI。 这会通过断言app_rdy来启动UI确认的握手。 |
app_wdf_data[APP_DATA_WIDTH – 1:0] | 输入 | 该总线提供当前正在写入外部存储器的数据。 |
app_wdf_end | 输入 | 此输入指示当前周期中app_wdf_data []总线上的数据是当前请求的最后一个数据。 |
app_wdf_wren | 输入 | 此输入表示app_wdf_data []总线上的数据有效。 |
app_wdf_rdy | 输出 | 此输出表示写数据FIFO已准备好接收数据。 当app_wdf_rdy和app_wdf_wren都被声明时,接受写入数据。 |
app_wdf_mask[APP_MASK_WIDTH – 1:0] | 输入 | 该总线指示app_wdf_data []的哪些字节写入外部存储器以及哪些字节保持其当前状态。 通过将值1设置为app_wdf_mask中的相应位来屏蔽字节。 例如,如果应用程序数据宽度为256,则掩码宽度取值为32. app_wdf_data的最低有效字节[7:0]使用app_wdf_mask的Bit [0]屏蔽,app_wdf_data的最高有效字节[255:248]使用app_wdf_mask的Bit [31]屏蔽。 因此,如果必须屏蔽最后一个DWORD,即app_wdf_data的字节0,1,2和3,则app_wdf_mask应设置为32’h0000_000F。 |
app_rdy | 输出 | 此输出指示您是否接受当前提交给UI的请求。 如果在确认app_en后UI未声明此信号,则必须重试当前请求。 如果出现以下情况,则不会声明app_rdy输出:1.PHY /内存初始化尚未完成; 2.所有bank都被占用(可以看作命令缓冲区已满);3.请求读取并且读取缓冲区已满;4.正在插入定期读取。 |
app_rd_data[APP_DATA_WIDTH – 1:0] | 输出 | 此输出包含从外部存储器读取的数据。 |
app_rd_data_end | 输出 | 此输出表示当前周期中app_rd_data []总线上的数据是当前请求的最后一个数据。 |
app_rd_data_valid | 输出 | 此输出表明app_rd_data []总线上的数据有效。 |
ui_clk_sync_rst | 输出 | reset信号来自于UI,与ui_clk同步。 |
ui_clk | 输出 | 这是UI的输出时钟。 它必须是输出到外部SDRAM的时钟频率的一半或四分之一,这取决于在GUI中选择的2:1或4:1模式。 |
init_calib_complete | 输出 | 校准完成后,PHY将init_calib_complete置‘1’。 在将命令发送到内存控制器之前,应用程序无需等待init_calib_complete。 |
app_ref_req | 输入 | 置位时,此高电平有效输入请求内存控制器向DRAM发送刷新命令。 它必须在一个周期内进行脉冲以发出请求,直到app_ref_ack信号被置位以确认请求并指示它已被发送,然后置为无效。 |
app_ref_ack | 输出 | 置位时,此高电平有效输入确认刷新请求,并指示该命令已从存储器控制器发送到PHY。 |
app_zq_req | 输入 | 置位时,此高电平有效输入请求存储器控制器向DRAM发送ZQ校准命令。 它必须在一个周期内进行脉冲以发出请求,直到app_zq_ack信号被置位以确认请求并指示它已被发送,然后取消置位。 |
app_zq_ack | 输出 | 置位时,此高电平有效输入确认ZQ校准请求,并指示该命令已从存储器控制器发送到PHY。 |
5.MIG 控制器概述及读写时序介绍
如上图所示,FPGA用户逻辑代码通过MIG控制器IP连接到DDR3的物理芯片。MIG提供的UI接口大大简化了DDR在FPGA上的应用。
5.1.MIG内存控制器用户逻辑时序
指令通道:
当用户逻辑app_en信号置位并且app_rdy置位命令被写入FIFO。app_rdy被取消置位时,UI会忽略该命令。用户逻辑需要将app_en保持为高电平以及有效的命令和地址值,直到app_rdy被置位。
写时序:
写入数据的三种场景(如上图的1、2、3):
1:写入命令的同时写入数据
2:写入数据在相应的写入命令之前
3:写入数据在相应的写命令之后,不应超过两个时钟周期的限制。
当app_wdf_wren被置位时且app_wdf_rdy为高时,数据写入FIFO,如果app_wdf_rdy取消置位,则用户逻辑需要保留app_wdf_wren和app_wdf_end以及有效的app_wdf_data值,直到app_wdf_rdy被置位。
读时序:
在app_rd_data_valid被置位时,数据由UI请求的顺序返回(即读信号与appen置位期间请求的地址顺序)。app_rd_data_end信号表示每个读命令脉冲串的结束。
5.2.BL8 突发长度
理解一下概念:
突发传输: 在通信领域中一般指在短时间内进行相对高带宽的数据传输。
Burst(突发): 是指在同一行中相邻的存储单元连续进行数据传输的方式。
BL突发长度: 连续传输的周期数就是突发长度。
在突发传输的模式下,多个数据单元(相当于一个数据块)来传送,从而提高传输效率。
BL8: 突发长度为8,app_addr必须是8对齐的地址,比如数据宽度Data Width为32,每次读写数据的长度为8*32=256bit。
5.3.MIG时钟和DDR3时钟
例如IP核 中配置如下:
1.
即MIG对DDR接口的速率为800M*2=1600M(双沿)。
MIG输出到app接口上的时钟ui_clk为800M/4=200M。
UI时钟频率ui_clk为DDR时钟频率的1/4,也就是一个用户时钟周期(两个边沿)对应8个DDR时钟边沿。burst length可以理解为MIG连续操作DDR地址的个数,故在4:1时钟比例下,一个用户时钟周期正好对8个地址进行了读/写操作,256bit数据分8次(32bit)写入DDR中,即逻辑用户以时钟ui_clk写256bit数据至MIG,MIG控制器将以时钟ddr_clk向DDR写8次(32bit(数据位宽配置为32bit))数据。由此分析,在写数据时让app_wdf_end = app_wdf_wren即可,并且读/写操作时地址递增步长为8。
2.
输入给MIG的时钟为200M。
5.4.掩码_Mask
掩码是一串二进制代码对目标字段进行位与运算,屏蔽当前的输入位。
在MIG的端口中有app_wdf_mask[APP_MASK_WIDTH – 1:0]信号充当掩码,该总线指示app_wdf_data []的哪些字节写入外部存储器以及哪些字节保持其当前状态。 通过将值1设置为app_wdf_mask中的相应位来屏蔽字节。 例如,如果应用程序数据宽度为256,则掩码宽度取值为32. app_wdf_data的最低有效字节[7:0]使用app_wdf_mask的Bit [0]屏蔽,app_wdf_data的最高有效字节[255:248]使用app_wdf_mask的Bit [31]屏蔽。 因此,如果必须屏蔽最后一个DWORD,即app_wdf_data的字节0,1,2和3,则app_wdf_mask应设置为32’h0000_000F。
6. MIG IP核配置
Step1:IP Catalog中搜索MIG,双击。
Step2:Name 看自己。
Step3:型号选择。
Step4:DDR3。
Step5:时钟、型号、位宽。
Step6:输入给MIG的时钟。
Step7:系统和参考时钟选择no buffer;MIG低电平复位。
Step8:终端阻抗50hms。
Step9:不New,Fixed Pin Out。
Step10:约束,可以根据原理图自己填也可以导入现有的约束。
Step11:Next、Next、Next、Next、Generate。
Step12:右击Open IP_Example Design…,并设置好路径,创建Example工程。
7.实战-DDR3读写
7.1原理图
通过两片512MB的MT41K256M16TW-107扩展为1GB的DDR3。两片DDR3的地址线互联连接到FPGA,数据线分别连接至FPGA扩展为高16位和低16位,扩展后的DDR3数据位宽为32bit。
以下为扩展示意图:
以下为电路连接原理图:
7.2IP核配置以及Example代码修改及分析
IP核配置如上所示。配置完IP核后生成Example工程,修改以下代码:
(1)、MIG输入时钟为200M,所以系统时钟通过PLL产生200M时钟。
(2)、修改MIG IP CORE
.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_we_n (ddr3_we_n),
.ddr3_dq (ddr3_dq),
.ddr3_dqs_n (ddr3_dqs_n),
.ddr3_dqs_p (ddr3_dqs_p),
.ddr3_reset_n (ddr3_reset_n),
.init_calib_complete (init_calib_complete),
.ddr3_cs_n (ddr3_cs_n),
.ddr3_dm (ddr3_dm),
.ddr3_odt (ddr3_odt),
// Application interface ports
.app_addr (app_addr), //输入,当前请求的地址
.app_cmd (app_cmd), //输入,当前请求的命令,001读 000写
.app_en (app_en), //输入,输出表明UI已准备好接受命令
.app_wdf_data (app_wdf_data), //输入,为写命令提供了数据
.app_wdf_end (app_wdf_end), //输入,高电平指示app_wdf_data上输入数据的最后一个周期
.app_wdf_wren (app_wdf_wren), //输入,高电平指示app_wdf_data总线上的数据有效
.app_rd_data (app_rd_data), //输出,读取命令的输出数据
.app_rd_data_end (app_rd_data_end), //输出,高电平指示app_rd_data上输出数据的最后一个周期
.app_rd_data_valid (app_rd_data_valid), //输出,高电平指示app_rd_data有效
.app_rdy (app_rdy), //输出,此输出表明UI已准备好接收命令
.app_wdf_rdy (app_wdf_rdy), //输出,此输出表明写数据FIFO已准备好接收数据。
.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),
.ui_clk (clk), //输出,必须是DDR时钟频率的一半或四分之一,取决于GUI中选择的2:1或4:1模式(UI配置的为4:1),800M/4=200M
.ui_clk_sync_rst (rst),
.app_wdf_mask (32'd0), //该总线指示app_wdf_data[]的哪些字节被写入外部存储器,并且哪些字节保持其当前状态,通过设置1来屏蔽。
//如果app data宽度为256,则app_wdf_mask为32bit,例:D0屏蔽app_wdf_data[7:0],D31屏蔽app_wdf_data[255:248]
// System Clock Ports
.sys_clk_i (sys_clk_i), //PLL产生的200M时钟
// Reference Clock Ports
.clk_ref_i (clk_ref_i), //PLL产生的200M时钟
.device_temp (device_temp),
.sys_rst (locked)
(3)、添加测试代码
代码说明:3次写入256bit数据(由计数产生)至DDR3,然后3次读出写入DDR3的数据。
/*--读写测试--*/
/* UI输入: UI输出
* --> app_addr app_rd_data -->
* --> app_wdf_data app_rd_data_end -->
* --> app_en app_rd_data_valid -->
* --> app_cmd app_rdy -->
* --> app_wdf_end app_wdf_rdy -->
* --> app_wdf_wren
*/
//写入3*256bit数据,需写入3次。
//状态机
reg[1:0] MIG_State=0;
parameter [1:0]IDLE = 2'd0;
parameter [1:0]WRITE = 2'd1;
parameter [1:0]WAIT = 2'd2;
parameter [1:0]READ = 2'd3;
//读写状态
parameter [2:0]CMD_WRITE = 3'b000;
parameter [2:0]CMD_READ = 3'b001;
//测试大小
parameter TEST_RANGE = 10'd3;
//计数值写入DDR
reg [9:0]cnt=0;
//app_addr 输入UI的地址 = RANK_WIDTH + BANK_WIDTH + ROW_WIDTH + COL_WIDTH;
reg [ADDR_WIDTH-1:0]app_addr_r=0;
//控制信号产生
//app_cmd:输入,当前请求的命令,001读 000写
assign app_cmd = (MIG_State==WRITE)? CMD_WRITE:CMD_READ;
//app_en:输入,输出表明UI已准备好接受命令
//写状态时app_rdy、app_wdf_rdy均置位时表明UI已经准备好接收,读状态时app_rdy置位表明UI已准备就绪。
assign app_en = (MIG_State==WRITE)? (app_rdy&&app_wdf_rdy):((MIG_State==READ)&&app_rdy);
//app_wdf_wren:输入至UI。app_wdf_wren高电平指示app_wdf_data总线上的数据有效
//在写状态时,app_rdy、app_wdf_rdy两个信号均为就绪状态时为1。
assign app_wdf_wren = (MIG_State==WRITE)?(app_rdy&&app_wdf_rdy):1'b0;
//app_wdf_end:输入,高电平指示app_wdf_data上输入数据的最后一个周期。
assign app_wdf_end = app_wdf_wren;
//地址
assign app_addr = app_addr_r;
//写入数据[255:0],通过8次32bit写入DDR
assign app_wdf_data ={
cnt[7:0],cnt[7:0],cnt[7:0],cnt[7:0], //1 32bit
cnt[7:0],cnt[7:0],cnt[7:0],cnt[7:0], //2 32bit
cnt[7:0],cnt[7:0],cnt[7:0],cnt[7:0], //3 32bit
cnt[7:0],cnt[7:0],cnt[7:0],cnt[7:0], //4 32bit
cnt[7:0],cnt[7:0],cnt[7:0],cnt[7:0], //5 32bit
cnt[7:0],cnt[7:0],cnt[7:0],cnt[7:0], //6 32bit
cnt[7:0],cnt[7:0],cnt[7:0],cnt[7:0], //7 32bit
cnt[7:0],cnt[7:0],cnt[7:0],cnt[7:0] //8 32bit
};
//状态机,clk为ui_clk,200MHz
always@(posedge clk)
begin
if(rst&!init_calib_complete)
begin
MIG_State <= IDLE;
cnt <= 10'd0;
end
else
begin
case(MIG_State)
IDLE:
begin
MIG_State <= WRITE;
cnt <= 10'd0; //复位计数(用来写)
app_addr_r <= 29'd0; //复位地址(用来写)
end
WRITE:
begin
if(cnt==TEST_RANGE)
begin
MIG_State <= WAIT;
end
else
begin
MIG_State <= MIG_State;
cnt <= (app_rdy&&app_wdf_rdy)? (cnt+10'd1):cnt;
app_addr_r <= (app_rdy&&app_wdf_rdy)? (app_addr_r+29'd8):app_addr_r;//跳到下一个(8*32=256)bit数据地址,数据app_wdf_data位宽为256bit。
end
end
WAIT:
begin
MIG_State <= READ;
cnt <= 10'd0; //复位计数(用来读)
app_addr_r <= 29'd0; //复位地址(用来读)
end
READ:
begin
if(cnt==TEST_RANGE)
begin
MIG_State <= IDLE;
end
else
begin
MIG_State <= MIG_State;
cnt <= app_rdy? (cnt+10'd1):cnt;
app_addr_r <= app_rdy? (app_addr_r+29'd8):app_addr_r; //跳到下一个(8*32=256)bit数据地址,数据app_wdf_data位宽为256bit。
end
end
default:
begin
MIG_State <= IDLE;
cnt <= 10'd0;
app_addr_r <= 29'd0;
end
endcase
end
end
ila_0 degbug_0 (
.clk(clk), // input wire clk
.probe0(app_cmd), // input wire [2:0] probe0
.probe1(app_en), // input wire [0:0] probe1
.probe2(app_wdf_wren), // input wire [0:0] probe2
.probe3(app_wdf_end), // input wire [0:0] probe3
.probe4(app_addr), // input wire [28:0] probe4
.probe5(app_rd_data), // input wire [255:0] probe5
.probe6(MIG_State), // input wire [2:0] probe6
.probe7(app_wdf_data), // input wire [255:0] probe7
.probe8(app_rdy), // input wire [0:0] probe8
.probe9(app_wdf_rdy), // input wire [0:0] probe9
.probe10(app_rd_data_end), // input wire [0:0] probe10
.probe11(app_rd_data_valid) // input wire [0:0] probe11
);
(4)、代码对应时序分析
写数据:
写入数据的三种场景:
1:写入命令的同时写入数据
2:写入数据在相应的写入命令之前
3:写入数据在相应的写命令之后,不应超过两个时钟周期的限制。
我们采用1.写入命令的同时写入数据。
当app_wdf_wren被置位时且app_wdf_rdy为高时,数据写入FIFO,如果app_wdf_rdy取消置位,则用户逻辑需要保留app_wdf_wren和app_wdf_end以及有效的app_wdf_data值,直到app_wdf_rdy被置位。
在MIG_UI中app_cmd为001时为读状态、为000时为写状态。
认读理解上面两行红字,看下图,看标注的即可:
上图中,当app_wdf_wren被置位时且app_wdf_rdy为高时,数据线的3个256bit数据被写入DDR3。
此时上图的三个app_addr为(上图看不清楚地址看下图):
读数据:
在app_rd_data_valid被置位时,数据由UI请求的顺序返回(即读信号与app_en置位期间请求的地址顺序)。观察上图app_rd_data_valid不是立即置位的。
注意:认真读上面那段红色的话,app_cmd为001时为读,观察app_cmd为001,同时app_en拉高时的地址app_addr(与写入时的地址是一样):
★★★此刻app_en拉高时的地址MIG控制器会记住的,数据会在app_rd_data_valid置位时按这个地址顺序返回数据。 如下图所示,标号①处为app_en拉高处,即MIG获取读取地址的时候,标号②处为app_rd_data_valid置位处标明表明此刻返回了有效数数据。观察和写入的数据一致。
8.附带源码
源码下载
★★★如有错误欢迎指导!!!
最后
以上就是谨慎砖头为你收集整理的FPGA_MIG驱动DDR3的全部内容,希望文章能够帮你解决FPGA_MIG驱动DDR3所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复