MiS603开发板 第十五章 MCB DDR3内存测试

作者:MiS603开发团队

日期:20150911

公司:南京米联电子科技有限公司

论坛:www.osrc.cn

网址:www.milinker.com

网店:http://osrc.taobao.com

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 重新封装构建,方便了后续开发的使用。

时间: 2024-12-18 23:29:49

MiS603开发板 第十五章 MCB DDR3内存测试的相关文章

MiS603开发板 第十四章 UDP协议网络传输

作者:MiS603开发团队 日期:20150911 公司:南京米联电子科技有限公司 论坛:www.osrc.cn 网址:www.milinker.com 网店:http://osrc.taobao.com EAT博客:http://blog.chinaaet.com/whilebreak 博客园:http://www.cnblogs.com/milinker/ MiS603开发板 第十四章 UDP协议网络传输 本实验将实现FPGA 芯片和PC 之间进行千兆以太网数据通信, 通信协议采用Ether

MiS603开发板 第十二章 SLAVE FIFO流传输

作者:MiS603开发团队 日期:20150911 公司:南京米联电子科技有限公司 论坛:www.osrc.cn 网址:www.milinker.com 网店:http://osrc.taobao.com EAT博客:http://blog.chinaaet.com/whilebreak 博客园:http://www.cnblogs.com/milinker/ MiS603开发板 第十二章 SLAVE FIFO流传输 15.1 USB 固件源码分析 SLAVE FIFOUSB 固件源码仍然采用上

MiS603开发板 第十六章 图像之VGA接口测试

作者:MiS603开发团队 日期:20150911 公司:南京米联电子科技有限公司 论坛:www.osrc.cn 网址:www.milinker.com 网店:http://osrc.taobao.com EAT博客:http://blog.chinaaet.com/whilebreak 博客园:http://www.cnblogs.com/milinker/ MiS603开发板 第十六章 图像之VGA接口测试 第十六章图像之VGA接口测试 在本章开始介绍视频图像处理开发平台的硬件结构,主要包括

MiS603开发板 第十八章 模拟视频输入及测试

作者:MiS603开发团队 日期:20150911 公司:南京米联电子科技有限公司 论坛:www.osrc.cn 网址:www.milinker.com 网店:http://osrc.taobao.com EAT博客:http://blog.chinaaet.com/whilebreak 博客园:http://www.cnblogs.com/milinker/ MiS603开发板 第十八章 模拟视频输入及测试 18.1模拟视频概述 大自然的信号都是模拟的,视频信号也不例外.视频信号是指电视信号.

MiS603开发板 第五章 按钮去抖实验

作者:MiS603开发团队 日期:20150911 公司:南京米联电子科技有限公司 论坛:www.osrc.cn 网址:www.milinker.com 网店:http://osrc.taobao.com EAT博客:http://blog.chinaaet.com/whilebreak 博客园:http://www.cnblogs.com/milinker/ MiS603开发板 第五章 按钮去抖实验 按键的消抖,是指按键在闭合或松开的瞬间伴随着一连串的抖动,这样的抖动将直接影响设计系统的稳定性

MiS603开发板 第十三章 千兆网RGMII PHY测试

作者:MiS603开发团队 日期:20150911 公司:南京米联电子科技有限公司 论坛:www.osrc.cn 网址:www.milinker.com 网店:http://osrc.taobao.com EAT博客:http://blog.chinaaet.com/whilebreak 博客园:http://www.cnblogs.com/milinker/ MiS603开发板 第十三章 千兆网RGMII PHY测试 13.1 RGMII 简介 RGMII(Reduced Gigabit Me

MiS603开发板 第三章 多路分频器设计

作者:MiS603开发团队 日期:20150911 公司:南京米联电子科技有限公司 论坛:www.osrc.cn 网址:www.milinker.com 网店:http://osrc.taobao.com EAT博客:http://blog.chinaaet.com/whilebreak 博客园:http://www.cnblogs.com/milinker/ MiS603开发板 第三章 多路分频器设计 设计思想:利用FPGA内部的逻辑单元对FPGA输入的50MHZ高频时钟信号进行计数分频,得到

MiS603开发板 第四章 流水灯实验

作者:MiS603开发团队 日期:20150911 公司:南京米联电子科技有限公司 论坛:www.osrc.cn 网址:www.milinker.com 网店:http://osrc.taobao.com EAT博客:http://blog.chinaaet.com/whilebreak 博客园:http://www.cnblogs.com/milinker/ MiS603开发板 第四章 流水灯实验 关于流水灯,网上有太多的例子了.其实只要掌握上面分频计数的技巧,设计流水灯是件极其简单的事情.从

MiS603开发板 第九章 IP-CORE BRAM

作者:MiS603开发团队 日期:20150911 公司:南京米联电子科技有限公司 论坛:www.osrc.cn 网址:www.milinker.com 网店:http://osrc.taobao.com EAT博客:http://blog.chinaaet.com/whilebreak 博客园:http://www.cnblogs.com/milinker/ MiS603开发板 第九章 IP-CORE BRAM 上一章节笔者讲解了FIFO的简单使用,那么FPGA的Block RAM(BRAM)