赞
踩
本实验所用开发板为xc7a35t-2fgg484I
FPGA设备名称命名规则如上图所示
XC7A:代表FPGA的家族系列 (X表示XILINX产品,C表示商用(Commercial),7A表示7系列产品)
35t:代表拥有的Logic Cells的数目
-2:表示速度等级
fgg484:表示封装类型和引脚数目
DDR SDRAM 全称为 Double Data Rate SDRAM,中文名为“双倍数据流 SDRAM”。DDR SDRAM 在原有的 SDRAM 的基础上改进而来。
存储器分类:
RAM:即随机存储内存,就是计算机的内存,这种存储器在断电时将丢失其存储内容,故主要用于存储短时间使用的程序。
ROM:即只读内存,是一种只能读出事先所存储数据的固态半导体存储器。
FLASH:它结合了ROM和RAM的长处,不仅具备电子可擦出可编程(EEPROM)的性能,还不会断电丢失数据同时可以快速读取数据(NVRAM的优势), 用作存储Bootloader以及操作系统或者程序代码或者直接当硬盘使用(U盘).
AX7035开发板上搭载1个Micron DDR3的颗MT41J128M16HA,芯片容量为2Gb,即256MB。在 DDR SDRAM 中,突发长度只有 2、4、8 三种选择,没有了随机存取的操作(突发长度为 1)和全页式突发。这是因为L-Bank 一次就存取两倍于芯片位宽的数据,所以芯片至少也要进行两次传输才可以。
MIG IP 控制器是 Xilinx 为用户提供的一个 DDR 控制的 IP,用户可以不需要了解DDR3的控制和读写时序,只需要通过DDR3控制器对DDR3的读写进行控制。
7系列的DDR3控制器接口如下:
DDR3控制器包含3个部分:用户接口模块(User interface Block),存储器控制模块(Memory Controller),DDR3的物理接口(Physical Layer)。
DDR3使用流程:
①打开IP Catalog界面
②在 IP Catalog 界面里双击 Memories & Storage Elements\ Memory Interface Generators 下的Memory Interface Generator (MIG 7 Series)。
③点击next
④修改 Component Name 为你想要命名的IP名字,点击 Next。
⑤这里可以选择兼容芯片,不需要直接点击next。
⑥选择默认的DDR3
⑦DDR3设置
7035 开发板 Memory Part 选择开发板的型号"MT41J128m16xx-125", Data Width 数据宽度选择 16 位。
⑧选择 PLL 输入时钟的频率为 200Mhz, 这个时钟需要跟开发板上的时钟频率一致,其它设置输出阻抗值和内部的 ODT 内部上拉电阻值来改善 DDR3 的信号完整性,一般不需要修改。
⑨System Clock 选择差分"No Buffer", Reference Clock 因为开发板上没有提供单独的 DDR 参考时钟,所以选择"Use System Clock"。System Reset Polarity 选择"ACTIVE LOW",AX7035 还需勾选 Internal Verf,其它保留默认配置。
⑩之后按照默认值进行设定。在最后的界面需要设置DDR3的数据、地址和控制信号的FPGA管脚分配和IO电平,用户可以根据开发板原理图进行手动配置或者使用Read XDC/UCF按键导入.xdc或者.ucf文件进行自动配置。最后validate成功即可正确生成DDR3的IP核。
DDR3的时钟分析总结:
步骤⑦中:
Clock_Period:表示MIG核对DDR3接口的速率,乘以2则表示双边沿传输。AX7035开发板设置成400MHz,根据不同开发板设置。
PHY to Controller Clock Ratio:一个比值,表示MIG输出到app接口上的时钟ui_clk。AX7035开发板设置成4:1,表明ui_clk为100MHz。
步骤⑧中:
Input Clock Period:表示DDR3中top层输入时钟,即用户需要输入到DDR3的时钟。AX7035设置为200MHz。
步骤⑨中:
reference clock:use system clock,与步骤⑧中的Input Clock Period表示同一个时钟,如果不设置此时钟,最后在工程中MIG生成.v会生成一个接口向我们索要该200MHz的时钟。
DDR3用户接口时序图分析:
写数据时序图:
add_cmd:输入信号。DDR3命令端口,0为写入,1为读出。
app_addr:输入信号。DDR3的地址信号。
app_en:DDR3使能信号。
app_rdy:输出端口。高电平有效,表示MIG准备好接收命令和数据了。
app_wdf_mask:高电平有效,将其置1可以屏蔽某些字节。可以指示哪些字节写入外部存储器,哪些保持其当前状态。
app_wdf_rdy:输出端口,表示mig准备好接收写数据和写命令。
app_wdf_data:写入的数据。
app_wdf_wren:写入数据接口app_wdf_data的使能信号。
app_wdf_end:指示当前周期数据已经写结束了。
提示:只有当app_en和app_rdy同时为高电平时add_cmd和app_addr才有效。app_name和app_wdf_name是分开的,若app_wdf_rdy有效,而app_rdy无效,数据也会被存入fifo中,只有app_rdy有效了,数据才会写入到DDR3中。由时序图可知,写数据和写控制信号可以早于写命令、写当前地址、其他写控制信号一个时钟周期或迟于两个时钟周期之内都可。
读数据时序:只要当读命令(app_cmd)和当前读地址(app_addr)以及读控制信号(app_en,app_rdy)同时有效时,等待读数据有效信号(app_rd_data_valid)有效时读数据(app_rd_data)有效。
DDR3用户接口驱动代码:
module mem_burst #( parameter MEM_DATA_BITS = 128, parameter ADDR_BITS = 28 ) ( //用户控制接口 input rst, /*复位*/ input mem_clk, /*接口时钟*/ input rd_burst_req, /*读请求*/ input wr_burst_req, /*写请求*/ input[15:0] rd_burst_len, /*读数据长度*/ input[15:0] wr_burst_len, /*写数据长度*/ input[ADDR_BITS - 1:0] rd_burst_addr, /*读首地址*/ input[ADDR_BITS - 1:0] wr_burst_addr, /*写首地址*/ output rd_burst_data_valid, /*读出数据有效*/ output wr_burst_data_req, /*写数据信号*/ output[MEM_DATA_BITS - 1:0] rd_burst_data, /*读出的数据*/ input[MEM_DATA_BITS - 1:0] wr_burst_data, /*写入的数据*/ output rd_burst_finish, /*读完成*/ output wr_burst_finish, /*写完成*/ output burst_finish, /*读或写完成*/ //DDR接口 output[ADDR_BITS-1:0] app_addr, output[2:0] app_cmd, output app_en, output [MEM_DATA_BITS-1:0] app_wdf_data, output app_wdf_end, output [MEM_DATA_BITS/8-1:0] app_wdf_mask, output app_wdf_wren, input [MEM_DATA_BITS-1:0] app_rd_data, input app_rd_data_end, input app_rd_data_valid, input app_rdy, input app_wdf_rdy, input ui_clk_sync_rst, input init_calib_complete ); assign app_wdf_mask = {MEM_DATA_BITS/8{1'b0}}; localparam IDLE = 3'd0; localparam MEM_READ = 3'd1; localparam MEM_READ_WAIT = 3'd2; localparam MEM_WRITE = 3'd3; localparam MEM_WRITE_WAIT = 3'd4; localparam READ_END = 3'd5; localparam WRITE_END = 3'd6; localparam MEM_WRITE_FIRST_READ = 3'd7; reg[2:0] state; reg[9:0] rd_addr_cnt; reg[9:0] rd_data_cnt; reg[9:0] wr_addr_cnt; reg[9:0] wr_data_cnt; reg[2:0] app_cmd_r; reg[ADDR_BITS-1:0] app_addr_r; reg app_en_r; reg app_wdf_end_r; reg app_wdf_end_r_m0; reg app_wdf_wren_r; assign app_cmd = app_cmd_r; assign app_addr = app_addr_r; assign app_en = app_en_r; assign app_wdf_end = app_wdf_end_r_m0; //延迟了一个时钟周期 assign app_wdf_data = wr_burst_data; assign app_wdf_wren = app_wdf_wren_r & app_wdf_rdy; assign rd_burst_finish = (state == READ_END); assign wr_burst_finish = (state == WRITE_END); assign burst_finish = rd_burst_finish | wr_burst_finish; assign rd_burst_data = app_rd_data; assign rd_burst_data_valid = app_rd_data_valid; assign wr_burst_data_req = (state == MEM_WRITE) & app_wdf_rdy ; always@(posedge mem_clk or posedge rst) begin if(rst) begin app_wdf_wren_r <= 1'b0; end else if(app_wdf_rdy) app_wdf_wren_r <= wr_burst_data_req; end always@(posedge mem_clk or posedge rst) begin if(rst) app_wdf_end_r_m0 <= 1'b0; else app_wdf_end_r_m0 <= app_wdf_end_r; end always@(posedge mem_clk or posedge rst) begin if(rst) begin state <= IDLE; app_cmd_r <= 3'b000; app_addr_r <= 0; app_en_r <= 1'b0; rd_addr_cnt <= 0; rd_data_cnt <= 0; wr_addr_cnt <= 0; wr_data_cnt <= 0; app_wdf_end_r <= 1'b0; end else if(init_calib_complete == 1'b1) begin case(state) IDLE: begin if(rd_burst_req) begin state <= MEM_READ; app_cmd_r <= 3'b001; app_addr_r <= {rd_burst_addr,3'd0}; app_en_r <= 1'b1; end else if(wr_burst_req) begin state <= MEM_WRITE; app_cmd_r <= 3'b000; app_addr_r <= {wr_burst_addr,3'd0}; app_en_r <= 1'b1; wr_addr_cnt <= 0; app_wdf_end_r <= 1'b1; wr_data_cnt <= 0; end end MEM_READ: begin if(app_rdy) begin app_addr_r <= app_addr_r + 8; if(rd_addr_cnt == rd_burst_len - 1) begin state <= MEM_READ_WAIT; rd_addr_cnt <= 0; app_en_r <= 1'b0; end else rd_addr_cnt <= rd_addr_cnt + 1; end if(app_rd_data_valid) begin if(rd_data_cnt == rd_burst_len - 1) begin rd_data_cnt <= 0; state <= READ_END; end else begin rd_data_cnt <= rd_data_cnt + 1; end end end MEM_READ_WAIT: begin if(app_rd_data_valid) begin if(rd_data_cnt == rd_burst_len - 1) begin rd_data_cnt <= 0; state <= READ_END; end else begin rd_data_cnt <= rd_data_cnt + 1; end end end MEM_WRITE_FIRST_READ: begin app_en_r <= 1'b1; state <= MEM_WRITE; wr_addr_cnt <= 0; end MEM_WRITE: begin if(app_rdy) begin app_addr_r <= app_addr_r + 8; if(wr_addr_cnt == wr_burst_len - 1) begin app_wdf_end_r <= 1'b0; app_en_r <= 1'b0; end else begin wr_addr_cnt <= wr_addr_cnt + 1; end end if(wr_burst_data_req) begin // app_addr_r <= app_addr_r + 8; if(wr_data_cnt == wr_burst_len - 1) begin state <= MEM_WRITE_WAIT; end else begin wr_data_cnt <= wr_data_cnt + 1; end end end READ_END: state <= IDLE; MEM_WRITE_WAIT: begin if(app_rdy) begin app_addr_r <= app_addr_r + 'b1000; if(wr_addr_cnt == wr_burst_len - 1) begin app_wdf_end_r <= 1'b0; app_en_r <= 1'b0; if(app_wdf_rdy) state <= WRITE_END; end else begin wr_addr_cnt <= wr_addr_cnt + 1; end end else if(~app_en_r & app_wdf_rdy) state <= WRITE_END; end WRITE_END: state <= IDLE; default: state <= IDLE; endcase end end endmodule
注意:wr_burst_addr,rd_burst_addr和app_addr的区别,DDR3的突发长度为8,wr_burst_addr或者rd_burst_addr地址以1递增,而DDR3的内部地址以8递增。
app_addr_r <= {rd_burst_addr,3’d0}; //ddr3每8个数据进行传输一次,因此每次读写的时候起始地址为用户操纵地址的8倍,因此左移三位。
app_addr_r <= app_addr_r + 8; //ddr3每读写一次其地址自增8
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。