当前位置:   article > 正文

Xilinx官方DDR3调试总结_fpga ddr3上板子调试

fpga ddr3上板子调试

DDR3调试总结

一、开发板介绍

本实验所用开发板为xc7a35t-2fgg484I
在这里插入图片描述
FPGA设备名称命名规则如上图所示
XC7A:代表FPGA的家族系列 (X表示XILINX产品,C表示商用(Commercial),7A表示7系列产品)
35t:代表拥有的Logic Cells的数目
-2:表示速度等级
fgg484:表示封装类型和引脚数目

二、DDR3介绍

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 一次就存取两倍于芯片位宽的数据,所以芯片至少也要进行两次传输才可以。

三、DDR3设计

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 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141
  • 142
  • 143
  • 144
  • 145
  • 146
  • 147
  • 148
  • 149
  • 150
  • 151
  • 152
  • 153
  • 154
  • 155
  • 156
  • 157
  • 158
  • 159
  • 160
  • 161
  • 162
  • 163
  • 164
  • 165
  • 166
  • 167
  • 168
  • 169
  • 170
  • 171
  • 172
  • 173
  • 174
  • 175
  • 176
  • 177
  • 178
  • 179
  • 180
  • 181
  • 182
  • 183
  • 184
  • 185
  • 186
  • 187
  • 188
  • 189
  • 190
  • 191
  • 192
  • 193
  • 194
  • 195
  • 196
  • 197
  • 198
  • 199
  • 200
  • 201
  • 202
  • 203
  • 204
  • 205
  • 206
  • 207
  • 208
  • 209
  • 210
  • 211
  • 212
  • 213
  • 214
  • 215
  • 216
  • 217
  • 218
  • 219
  • 220
  • 221
  • 222
  • 223
  • 224
  • 225
  • 226
  • 227
  • 228
  • 229
  • 230
  • 231
  • 232
  • 233
  • 234
  • 235
  • 236
  • 237
  • 238
  • 239
  • 240
  • 241
  • 242
  • 243

注意: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

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/不正经/article/detail/615854
推荐阅读
相关标签
  

闽ICP备14008679号