作者:MiS603开发团队
日期:20150911
公司:南京米联电子科技有限公司
论坛:www.osrc.cn
EAT博客:http://blog.chinaaet.com/whilebreak
博客园:http://www.cnblogs.com/milinker/
MiS603开发板 第十五章 MCB DDR3内存测试
15.1 DDR3存储器模块及其测试
图像算法硬件最关键的一部分就是内存,内存保存着图像数据,开始图像学习前,先搞定内存控制,Spartan6系列FPGA自带内存控制器,控制DDR3内存变得很容易。
15.2 主流存储器简介
存储器有很多种,目前常见的存储器主要分掉电丢失数据和掉电不丢失数据2种类型。常见的掉电丢失数据的存储器是SRAM、SDRAM、DDR等,MIS603自带了一块2Gbit的DDR3存储器,用于缓存数据。常见的掉电不丢失数据的存储器是EEPROM、Flash、硬盘等,MIS603的FPGA标配了一块64Mbit的串行Flash,用于存储配置代码。
SRAM存储器结构比较复杂,是通过多个晶体管组合起来存储数字0和1的,每个存储单元比较复杂,因此能够生产的SRAM容量不是很大,价格也比较高,但是操作简单。
随着半导体技术发展出现了动态存储器SDRAM、DDR等,动态RAM是靠电容的有电和无电存储数字0和1的,但是由于半导体加工技术不足,这个电容会慢慢放电,这就导致数据误码,因此需要一个刷新电路动态给电容充电。这种存储器由于存储单元结构简单,容量可一做到很大,目前单片DDR3存储器可达4Gbit,价格相对较低,但是这类存储器操作比较复杂。
目前Spartan6系列FPGA自带了DDR存储器控制接口,使用起来就像操作FIFO一样简单,大大简化了DDR存储器的使用门槛,应用方便。
15.3 基于FPGA的DDR3硬件电路
1)、DDR3原理图设计
2)、PCB高速布线
15.4 MCB DDR3 MIG 核生成
Step1:开始菜单>程序>XILINX Design Tools 启动Core Generator
Step2:启动完成后选择新建一个工程
Step3:取名工程名称,然后保存工程
Step4:芯片的型号及参数设置
Step5:设置产生的IP CORE 采用的语言然后单击OK
Step6:双击启动MIG设置向导
Step7:单击Next
Step8:设置如下图,元件名改为mis603后单击Next
Step9:设置如下图,勾选兼容xc6slx16-ftg256,然后单击Next
Step10:选择BANK3上的DDR3控制器,然后单击Next
Step11:选择工作频率,和内存型号,实际上DDR数据频率还得乘以2,单击Next
Step11:全部采用默认设置,然后单击Next
Step12:设置为128bit位宽,双向,实现最大带宽,然后单击Next
Step13:单击Next
Step14:设置终端补偿电阻PIN脚、单时钟输入,然后单击Next
Step15:单击Next
Step16:单击Next
Step16:单击Next
Step17:单击Generate生产MIG Core
Step18:重点是user_design和docs文件下的内存
Step19:user_design文件下的内存
15.5 MCB控制器时序分析
1)、控制命令时序图:
px_cmd_clk:为MCB系统时钟,
px_cmd_en:为MCB控制命令使能信号,高电有效
px_cmd_instr[2:0]:为MCB控制命令,一般为读或者写命令
px_cmd_bl[2:0]:为MCB读或者写一次的深度最大64
px_cmd_byte_addr[29:0]:为地址空间
px_cmd_empty:为控制命令FIFO空标志,控制命令FIFO最多可以缓冲3个命令
px_cmd_full:为控制命令FIFO满标志,控制命令FIFO最多可以缓冲3个命令
2)、写FIFO时序:
px_wr_clk:为MCB系统时钟
px_wr_en:为MCB写FIFO使能标准,高电平期间,每一个时钟的上升沿写入FIFO一个数据
px_wr_mask[3:0]: MCB写FIFO屏蔽控制
px_wr_empy:MCB写FIFO空
px_wr_full:MCB写FIFO满
px_wr_underrun:MCB写FIFO溢出
px_wr_count[6:0]:MCB写FIFO中写入的数据个数,这个计数器只能大概评估,并不精确
3)、读时序图:
px_rd_clk:为MCB系统时钟
px_rd_en:为MCB 读FIFO使能标准,高电平期间,每一个时钟的上升沿读入FIFO一个数据
px_rd_empy:MCB读FIFO空
px_rd_full:MCB读FIFO满
px_rd_underrun:MCB读FIFO溢出
px_rd_count[6:0]:MCB读FIFO中读入的数据个数,这个计数器只能大概评估,并不精确
4)、MCB控制命令
5)、MCB支持内存的最大带宽和对时钟的要求
15.6 DDR3测试程序全地址空间测试
1)、这个工程中,笔者设计了一个用户控制模块多MCB进行二次封装,实现流传输情况下,数据带宽的最大化,非常适合一些需要流传输的控制场合,应用起来非常方便。构架框如下图。
2)、下图的红色方框内,就是本次构架的关键代码
3)、mcb_user.v文件关键代码分析
读写命令控制模块
always @(posedge clk)begin
if(!rst_n)begin
mcb_cmd_instr <= MCB_CMD_RD;
mcb_cmd_byte_addr <= u_rd_addr[29:0];
mcb_cmd_bl <= mcb_rd_bl;
mcb_cmd_wr_p <=1‘b0;
mcb_cmd_rd_p <=1‘b0;
end
else begin
if(u_wr_cmd_en)// write
begin
mcb_cmd_instr <= MCB_CMD_WP;
mcb_cmd_byte_addr <= u_wr_addr[29:0];
mcb_cmd_bl <= mcb_wr_bl;
mcb_cmd_wr_p <=1‘b1;
mcb_cmd_rd_p <=1‘b0;
end
else if(u_rd_cmd_en)//read
begin
mcb_cmd_instr <= MCB_CMD_RP;
mcb_cmd_byte_addr <= u_rd_addr[29:0];
mcb_cmd_bl <= mcb_rd_bl;
mcb_cmd_wr_p <=1‘b0;
mcb_cmd_rd_p <=1‘b1;
end
else begin
mcb_cmd_wr_p <=1‘b0;
mcb_cmd_rd_p <=1‘b0;
end
end
end
mcb_cmd_instr :控制命令,一般是读命令或者写命名
mcb_cmd_byte_addr :控制命令地址,一般是写地址或者读地址
mcb_cmd_bl :控制命令长度,一般是写入数据的深度、或者需要读出数据的深度
mcb_cmd_wr_p:写使能同步
mcb_cmd_rd_p:读使能同步
以上模块实现了MCB读写命令控制,可以看出来当u_wr_cmd_en信号使能后就会实现一次写控制命令发送,当u_rd_cmd_en信号使能后就会实现一次读控制命令操作。并且从程序的接口看出来,写命令的优先级要高于读命令的优先级。
此文件中还有以下代码
mcb_cmd_en信号是读写命令使能信号
u_wr_cmd_done0 写命令完成
u_rd_cmd_done0 读命令完成
assign u_wr_cmd_done0 = mcb_cmd_en&(mcb_cmd_instr== MCB_CMD_WP);// user write cmd is done
assign u_rd_cmd_done0 = mcb_cmd_en&(mcb_cmd_instr== MCB_CMD_RP);// user read cmd is done
assign mcb_cmd_en = ((~mcb_cmd_wr_p1)&mcb_cmd_wr_p)||((~mcb_cmd_rd_p1)&mcb_cmd_rd_p);// mcb cmd enable
以下这个模块 mcb_cmd_wr_p和mcb_cmd_rd_p,u_rd_cmd_done1 ,u_wr_cmd_done1实现信号的一个周期的延迟,u_rd_cmd_done通知用户读命令完成,u_wr_cmd_done
assign u_rd_cmd_done=u_rd_cmd_done1[1];
assign u_wr_cmd_done=u_wr_cmd_done1[1];
[email protected](posedge clk)
begin
if(!rst_n)
begin
mcb_cmd_wr_p1 <= 1‘b0;
mcb_cmd_rd_p1 <= 1‘b0;
u_wr_cmd_done1 <= 2‘b0;
u_rd_cmd_done1 <= 2‘b0;
end
else begin
mcb_cmd_wr_p1 <= mcb_cmd_wr_p;
mcb_cmd_rd_p1 <= mcb_cmd_rd_p;
u_rd_cmd_done1 <= {u_rd_cmd_done1[0:0],u_rd_cmd_done0};
u_wr_cmd_done1 <= {u_wr_cmd_done1[0:0],u_wr_cmd_done0};
end
end
读写FIFO使能模块
assign u_wr_rdy = mcb_wr_en;// write fifo is ready
assign u_rd_rdy = mcb_rd_en;// read fifo is ready
//write fifo enable and read fifo enable
assign mcb_wr_en = (~mcb_wr_full)&&u_wr_en&&rst_n;
assign mcb_rd_en = (~mcb_rd_empty)&&u_rd_en&&rst_n;
以上代码mcb_wr_en代码写MCB FIFO使能,当mcb_wr_en为1时候数据写入MCB FIFO同时,u_wr_rdy为1通知写模块可以写数据。
以上代码mcb_rd_en代码读MCB FIFO使能,当mcb_rd_en为1时候数据从MCB FIFO读出,u_rd_rdy为1通知读模块可以读数据。
4)、u_mcb_write模块代码分析
module u_mcb_write(
input clk,
input rst_n,
input u_wr_cmd_done,//user write cmd done
input u_wr_rdy,// user write ready,data can be written in in to mcb
output reg u_wr_cmd_en,//user write cmb cmd enable
output reg u_wr_en,//user write enable
output reg [127:0]u_wr_data,//user write data
output reg [29:0]u_wr_addr,//user write address
output reg [6 :0]u_wr_len // user data len
);
(*KEEP = "TRUE" *) wire [1:0] u_wr_s_r;
(*KEEP = "TRUE" *)wire [2:0] u_wr_en_dly1;
parameter WR_IDLE = 2‘d0;
parameter WR_BEGIN = 2‘d1;
parameter WR_WAIT = 2‘d2;
assign u_wr_s_r = u_wr_s;
reg [6 :0]u_wr_cnt; // user write counter
reg [1 :0]u_wr_s; // user write state
//when mcb write fifo more than 2 data enable write data mcb
//写命令请求
always @(posedge clk)
if(~rst_n)
begin
u_wr_cmd_en <= 1‘b0;
end
else begin
if(u_wr_cmd_done) u_wr_cmd_en <= 1‘b0; // clear mcb user write enable signal
else if(u_wr_cnt==40) u_wr_cmd_en <= 1‘b1; // enable user mcb user cmd signal
end
//写测试数据产生
always @(posedge clk)
if(~rst_n)
begin
u_wr_data<=128‘hAA_AA_AA_AA_AA_AA_AA_AA_AA_AA_AA_AA_AA_AA_AA_AA;
end
else begin
if(u_wr_rdy) // mcb fifo is valid
u_wr_data <= ~u_wr_data; // count up data write to mcb fifo
else if(~u_wr_en)
u_wr_data<=128‘hAA_AA_AA_AA_AA_AA_AA_AA_AA_AA_AA_AA_AA_AA_AA_AA;
end
//写数据计数器
always @(posedge clk)
if(~rst_n)
begin
u_wr_cnt<=0;
end
else begin
if(u_wr_rdy) // mcb fifo is valid
u_wr_cnt <= u_wr_cnt + 1‘b1; // count up data write to mcb fifo
else if(u_wr_s==WR_IDLE)
u_wr_cnt <= 7‘d0;
end
reg [28:0] u_wr_addr_set;
//写数据状态机
always @(posedge clk)
if(~rst_n)
begin
u_wr_len <= 7‘d64; // user write len
u_wr_addr <= 29‘d0; // user write address
u_wr_en <= 1‘b0; // user write enable
u_wr_s <= WR_IDLE;
end
else begin
case(u_wr_s)
WR_IDLE:
begin
u_wr_en <= 1‘b0;
if(u_wr_cmd_en==1‘b0) // start new write
u_wr_s <= WR_BEGIN;
end
WR_BEGIN:
begin
u_wr_len <= 7‘d64;
u_wr_addr <= u_wr_addr_set;
u_wr_en <= 1‘b1;
u_wr_s <= WR_WAIT;
end
WR_WAIT:
if(u_wr_cnt==(u_wr_len-1))
begin
u_wr_s <= WR_IDLE;
u_wr_en <= 1‘b0;
end
endcase
end
//888888888888888888888888888888888888888888888888888888888888888888888888
//地址空间起始地址产生
parameter ADDR_INC = 12‘h400;
parameter END_ADDR = 29‘h10000000 - ADDR_INC;
always @(posedge clk)
if(~rst_n)
begin
u_wr_addr_set<=29‘b0;
end
else begin
if(u_wr_cmd_done&&(u_wr_addr_set<END_ADDR))
u_wr_addr_set<=u_wr_addr_set+ADDR_INC;
else if(u_wr_addr_set==END_ADDR)
u_wr_addr_set<=0;
end
endmodule
从上面的程序还可以看出,如果同时有读写命令,优先处理写命令。
以上关键代码在于写命令请求的时机,当写MCB FIFO的数据非满就可以控制往MCB FIFO继续写数据。同时当FIFO有数据就可以发送控制命令往DDR把FIFO的数据搬运到DDR中。
另外地址空间的产生,MCB采用的地址空间是连续的,由于一次写入128bitX64的数据,因此一次写入的数据是1024 因此ADDR_INC = 12‘h400;每次完成一次写命令地址空间增加一次。本开发板配备的内存大小为128MBX16 全地址空间为268435456B 那么最后一次的增量结束地址为
END_ADDR = 29‘h10000000 - ADDR_INC
4)、u_mcb_read模块代码分析
module u_mcb_read
(
input clk,
input rst_n,
input u_rd_cmd_done, //user read cmd done
input u_rd_rdy, // user read ready,data can be written in in to mcb
output reg u_rd_cmd_en, //user read cmb cmd enable
output reg u_rd_en, //user read enable
input [127:0]u_rd_data, //user read data
output reg [29:0]u_rd_addr,//user read address
output reg [6 :0]u_rd_len // user data len
);
reg [1 :0]u_rd_s; // read state
reg [6 :0]u_rd_cnt;// user read data counter
reg read_error;
(*KEEP = "TRUE" *) wire [1:0] u_rd_s_r;// debug signal
assign u_rd_s_r = u_rd_s;
(*KEEP = "TRUE" *) wire read_error_dg;
assign read_error_dg = read_error;
parameter RD_IDLE = 2‘d0;
parameter RD_BEGIN = 2‘d1;
parameter RD_WAIT = 2‘d2;
parameter RD_RST = 2‘d3;
reg u_rd_en_dly;
always @(posedge clk)
if(~rst_n)
begin
u_rd_en_dly<=1‘b0;
end
else begin
u_rd_en_dly <= u_rd_en;
end
//读命令请求
always @(posedge clk)
if(~rst_n)
begin
u_rd_cmd_en <= 1‘b0;
end
else begin
if(u_rd_cmd_done) u_rd_cmd_en <= 1‘b0; // clear user read mcb cmd signal
else if(u_rd_en&&(~u_rd_en_dly)) u_rd_cmd_en <= 1‘b1; // enable user read mbb cmd signal
end
reg s;
//读数据比较
always @(posedge clk)
if(~rst_n)
begin
read_error<=1‘b0;
s<=0;
end
else begin
if((u_rd_s==RD_WAIT)&&u_rd_rdy)// valid data
case(s)
0:
if(u_rd_data==128‘hAA_AA_AA_AA_AA_AA_AA_AA_AA_AA_AA_AA_AA_AA_AA_AA) s <= 1‘b1;
else read_error<=1‘b1;
1:
if(u_rd_data==128‘h55_55_55_55_55_55_55_55_55_55_55_55_55_55_55_55) s <= 1‘b0;
else read_error<=1‘b1;
endcase
else if(~u_rd_en)begin
read_error<=1‘b0;
s<=0;
end
end
//读数据计数器
always @(posedge clk)
if(~rst_n)
begin
u_rd_cnt<=0;
end
else begin
if(u_rd_rdy)// valid data
u_rd_cnt<=u_rd_cnt+1‘b1; // count up
else if(u_rd_s==RD_IDLE)u_rd_cnt<=7‘d0; // clear counter
end
reg [28:0]u_rd_addr_set;
//读状态机
always @(posedge clk)
if(~rst_n)
begin
u_rd_len <= 7‘d64; // user read length
u_rd_addr <= 29‘d0; // user read address
u_rd_en <= 1‘b0; // user read enable
u_rd_s <= RD_IDLE;
end
else begin
case(u_rd_s)
RD_IDLE:
begin
u_rd_en <= 1‘b0;
if(u_rd_cmd_en==1‘b0)// when mcb read cmd is done
u_rd_s <= RD_BEGIN;
end
RD_BEGIN://read cmd
begin
u_rd_len <= 7‘d64;
u_rd_addr <= u_rd_addr_set;
u_rd_en <= 1‘b1;
u_rd_s <= RD_WAIT;
end
RD_WAIT://read cmd done
if(u_rd_cnt==(u_rd_len))begin
u_rd_s <= RD_IDLE;
u_rd_en <= 1‘b0;
end
endcase
end
//888888888888888888888888888888888888888888888888888888888888888888888888
//地址空间起始地址产生
parameter ADDR_INC = 12‘h400;
parameter END_ADDR = 29‘h10000000 - ADDR_INC;
always @(posedge clk)
if(~rst_n)
begin
u_rd_addr_set<=29‘b0;
end
else begin
if(u_rd_cmd_done&&(u_rd_addr_set<END_ADDR))
u_rd_addr_set<=u_rd_addr_set+ADDR_INC;
else if(u_rd_addr_set==END_ADDR)
u_rd_addr_set<=0;
end
endmodule
以上关键代码在于读命令请求的时机,当写MCB FIFO的数据非空就可以控制从MCB FIFO继续读数据。同时当读FIFO非满数据就可以发送控制命令从DDR把数据搬运到读FIFO。
另外地址空间的产生,MCB采用的地址空间是连续的,由于一次读入128bitX64的数据,因此一次读入的数据是1024 因此ADDR_INC = 12‘h400;每次完成一次读命令地址空间增加一次。本开发板配备的内存大小为128MBX16 全地址空间为268435456B 那么最后一次的增量结束地址为
END_ADDR = 29‘h10000000 - ADDR_INC
15.7 MCB DDR3测试结果
15.8 小结
本小结详细讲解了MCB内存控制 MIG CORE的产生过程,以及编写了全地址内存测试程序,对MIG CORE 重新封装构建,方便了后续开发的使用。