vivado下ddr3的读写和测试详解
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
vivado下ddr3的读写和测试详解
最近博主在根据例程做ddr3的读写测试,发现根本看不到好吧,虽然之前博主做过SDRAM的读写测试,但是ddr3更加复杂,时序写起来很吃力,所以需要用到vivado下自带的ip核。
具体来看下面例化过程:
1.在ip核下搜索mig 双击打开
2.直接next 然后在当前界面修改你的ddr3ip核的名字
这里博主是因为已经例化了ip核,所以名字不能修改,然后next
3.这是要不要兼容芯片,不选,点击next
4.勾选你的存储器类型,我的是ddr3,点击next
5.
这个配置比较多,第一个时钟为ddr3实际工作的时钟,然后选择你的内存型号,数据宽度即可,点击next
6.
然后输入时钟可能需要pll倍频得到,一般是200Mhz,这里注意看下最后一行的用户地址类型,它是由bank+row+column组成的,这个在后面的读写测试会进一步提到。
7.
时钟选择不差分,然后参考时钟为用户时钟。
8.下面就是默认next,然后就是分配管脚了,这个你买的开发板一般都会提高ucf文件,直接复制就行。
然后next,生成。
以上就是ip核的简单例化过程,这个步骤网上有很多类似的,博主就不一一讲解了,把精力放在读写测试这块。
首先来看老三样:ip核用户界面下的控制命令,读和写
这是控制命令,可以让用户来发送读或者写命令,需要注意的事只有当app_rdy和app_en同为高时才有效,命令被发出。
这里博主通过ila上电分析发现app_rdy为ip核自己产生的输出信号,但是它并不是一直都是高电平,所以在后续的读写测试时需要判断,至于怎么判断,我们后面代加上电分析。
上面是写命令,可以看到当add_wdf_wren和add_wdf_end同为高时数据才能有效被写进去,同时app_wdf_rdy也要为高。
需要注
意的一点是,写数据和写命令此时不再有关系,为什么,因为写数据其实是通过fifo缓存,当写命令有效时,由于先进先出的特性会把它所对应数据给写入,当然这个很拗口,下面会给出示例
上面的是读过程,可以看出当读命令发出后需要一个延迟读数据才会有效。
下面来看代码进行讲解:
1.module mem_burst
2.#(
3.parameter MEM_DATA_BITS = 64,
4.parameter ADDR_BITS = 24
5.)
6.(
7.input rst, /*复位*/
8.input mem_clk, /*接口时钟*/
9.input rd_burst_req, /*读请求*/
10.input wr_burst_req, /*写请求*/
11.input[9:0] rd_burst_len, /*读数据长度*/
12.input[9:0] wr_burst_len, /*写数据长度*/
13.input[ADDR_BITS - 1:0] rd_burst_addr, /*读首地址*/
14.input[ADDR_BITS - 1:0] wr_burst_addr, /*写首地址*/
15.output rd_burst_data_valid, /*读出数据有效*/
16.output wr_burst_data_req, /*写数据信号*/
17.output[MEM_DATA_BITS - 1:0] rd_burst_data, /*读出的数据*/
18.input[MEM_DATA_BITS - 1:0] wr_burst_data, /*写入的数据*/
19.output rd_burst_finish, /*读完成*/
20.output wr_burst_finish, /*写完成*/
21.output burst_finish, /*读或写完成*/
22.
23.///
24.output[ADDR_BITS-1:0] app_addr,
25.output[2:0] app_cmd,
26.output app_en,
27.output [MEM_DATA_BITS-1:0] app_wdf_data,
28.output app_wdf_end,
29.output [MEM_DATA_BITS/8-1:0] app_wdf_mask,
30.output app_wdf_wren,
31.input [MEM_DATA_BITS-1:0] app_rd_data,
32.input app_rd_data_end,
33.input app_rd_data_valid,
34.input app_rdy,
35.input app_wdf_rdy,
36.input ui_clk_sync_rst,
37.input init_calib_complete
38.);
39.
40.assign app_wdf_mask = {MEM_DATA_BITS/8{1'b0}};
41.
42.localparam IDLE = 3'd0;
43.localparam MEM_READ = 3'd1;
44.localparam MEM_READ_WAIT = 3'd2;
45.localparam MEM_WRITE = 3'd3;
46.localparam MEM_WRITE_WAIT = 3'd4;
47.localparam READ_END = 3'd5;
48.localparam WRITE_END = 3'd6;
49.localparam MEM_WRITE_FIRST_READ = 3'd7;
50.
51./*parameter IDLE = 3'd0;
52.parameter MEM_READ = 3'd1;
53.parameter MEM_READ_WAIT = 3'd2;
54.parameter MEM_WRITE = 3'd3;
55.parameter MEM_WRITE_WAIT = 3'd4;
56.parameter READ_END = 3'd5;
57.parameter WRITE_END = 3'd6;
58.parameter MEM_WRITE_FIRST_READ = 3'd7;*/
59.reg[2:0] state;
60.reg[9:0] rd_addr_cnt;
61.reg[9:0] rd_data_cnt;
62.reg[9:0] wr_addr_cnt;
63.reg[9:0] wr_data_cnt;
64.
65.reg[2:0] app_cmd_r;
66.reg[ADDR_BITS-1:0] app_addr_r;
67.reg app_en_r;
68.reg app_wdf_end_r;
69.reg app_wdf_wren_r;
70.assign app_cmd = app_cmd_r;
71.assign app_addr = app_addr_r;
72.assign app_en = app_en_r;
73.assign app_wdf_end = app_wdf_end_r;
74.assign app_wdf_data = wr_burst_data;
75.assign app_wdf_wren = app_wdf_wren_r & app_wdf_rdy;
76.assign rd_burst_finish = (state == READ_END);
77.assign wr_burst_finish = (state == WRITE_END);
78.assign burst_finish = rd_burst_finish | wr_burst_finish;
79.
80.assign rd_burst_data = app_rd_data;
81.assign rd_burst_data_valid = app_rd_data_valid;
82.
83.assign wr_burst_data_req = (state == MEM_WRITE) & app_wdf_rdy ;
84.
85.always@(posedge mem_clk or posedge rst)
86.begin
87.if(rst)
88.begin
89.app_wdf_wren_r <= 1'b0;
90.end
91.else if(app_wdf_rdy)
92.app_wdf_wren_r <= wr_burst_data_req;
93.end
94.
95.always@(posedge mem_clk or posedge rst)
96.begin
97.if(rst)
98.begin
99.state <= IDLE;
100.app_cmd_r <= 3'b000;
101.app_addr_r <= 0;
102.app_en_r <= 1'b0;
103.rd_addr_cnt <= 0;
104.rd_data_cnt <= 0;
105.wr_addr_cnt <= 0;
106.wr_data_cnt <= 0;
107.app_wdf_end_r <= 1'b0;
108.end
109.else if(init_calib_complete === 1'b1) 110.begin
111.case(state)
112.IDLE:
113.begin
114.if(rd_burst_req)
115.begin
116.state <= MEM_READ;
117.app_cmd_r <= 3'b001;
118.app_addr_r <= {rd_burst_addr,3'd0}; 119.app_en_r <= 1'b1;
120.end
121.else if(wr_burst_req)
122.begin
123.state <= MEM_WRITE;
124.app_cmd_r <= 3'b000;
125.app_addr_r <= {wr_burst_addr,3'd0}; 126.app_en_r <= 1'b1;
127.wr_addr_cnt <= 0;
128.app_wdf_end_r <= 1'b1;
129.wr_data_cnt <= 0;
130.end
131.end
132.MEM_READ:
133.begin
134.if(app_rdy)
135.begin
136.app_addr_r <= app_addr_r + 8; 137.if(rd_addr_cnt == rd_burst_len - 1) 138.begin
139.state <= MEM_READ_WAIT;
140.rd_addr_cnt <= 0;
141.app_en_r <= 1'b0;
142.end
143.else
144.rd_addr_cnt <= rd_addr_cnt + 1; 145.end
146.
147.if(app_rd_data_valid)
148.begin
149.//app_addr_r <= app_addr_r + 8; 150.if(rd_data_cnt == rd_burst_len - 1) 151.begin
152.rd_data_cnt <= 0;
153.state <= READ_END;
154.end
155.else
156.begin
157.rd_data_cnt <= rd_data_cnt + 1; 158.end
159.end
160.end
161.MEM_READ_WAIT:
162.begin
163.if(app_rd_data_valid)
164.begin
165.if(rd_data_cnt == rd_burst_len - 1) 166.begin
167.rd_data_cnt <= 0;
168.state <= READ_END;
169.end
170.else
171.begin
172.rd_data_cnt <= rd_data_cnt + 1; 173.end
174.end
175.end
176.MEM_WRITE_FIRST_READ: 177.begin
178.app_en_r <= 1'b1;
179.state <= MEM_WRITE;
180.wr_addr_cnt <= 0;
181.end
182.MEM_WRITE:
183.begin
184.if(app_rdy)
185.begin
186.app_addr_r <= app_addr_r + 8; 187.if(wr_addr_cnt == wr_burst_len - 1) 188.begin
189.app_wdf_end_r <= 1'b0;
190.app_en_r <= 1'b0;
191.end
192.else
193.begin
194.wr_addr_cnt <= wr_addr_cnt + 1; 195.end
196.end
197.
198.if(wr_burst_data_req)
199.begin
200.//app_addr_r <= app_addr_r + 8; 201.if(wr_data_cnt == wr_burst_len - 1) 202.begin
203.state <= MEM_WRITE_WAIT; 204.end
205.else
206.begin
207.wr_data_cnt <= wr_data_cnt + 1; 208.end
209.end
210.
211.end
212.READ_END:
213.state <= IDLE;
214.MEM_WRITE_WAIT:
215.begin
216.if(app_rdy)
217.begin
218.app_addr_r <= app_addr_r + 'b1000;
219.if(wr_addr_cnt == wr_burst_len - 1)
220.begin
221.app_wdf_end_r <= 1'b0;
222.app_en_r <= 1'b0;
223.if(app_wdf_rdy)
224.state <= WRITE_END;
225.end
226.else
227.begin
228.wr_addr_cnt <= wr_addr_cnt + 1;
229.end
230.end
231.else if(~app_en_r & app_wdf_rdy)
232.state <= WRITE_END;
233.
234.end
235.WRITE_END:
236.state <= IDLE;
237.default:
238.state <= IDLE;
239.endcase
240.end
241.end
242.endmodule
这个是黑金给的例程,一开始没看懂,搞了好几天才看懂整个细
节,下面来讲解一下:首先state在IDLE状态,当wr_burst_req有效时进入MEM_WRITE状态,这时候有两个条件判断,第一个if(app_rdy)为真,说明写命令是有效的,那么随之伴随的是地址的累加,同时也会计数,如果写命令发送了128次,就结束。
第二个if(wr_burst_data_req)为真,注意wr_burst_data_req为真实际就是app_wdf_rdy为真,所以写的数据是被缓存到了fifo并且当读命令有效时会依次传入,这里大家会问,为啥不让app_rdy和app_wdf_rdy 同时为真才地址增加和写数据呀,这是因为app_rdy和app_wdf_rdy 并不是一直都为高电平,下面是上电结果;
看到没,rdy为低时,app_wdf_rdy为高,这说明数据此时相对于地址来说多写进去一次,那么多的那个数据就被缓存了,等到下一个rdy为高就会去写入之前那个缓存的数据而不是当前时刻的数据。
这也就是为什么每个条件判断语句都会去计数,一个计的是多少个写命令被发出,另一个是多少个写的数据被发送。
下面来看下读过程,首先state在IDLE状态,当rd_burst_req有效时进入MEM_READ状态,这里同样有两个if判断,第一个if(app_rdy)是用来判断读命令是否有效并且地址累加,第二个if(app_rd_data_valid)是读数据有效,根据上面的读流程,读数据有效并不会随着读命令有效就马上出现,一般会延迟多个周期,所以同样需要分开判断并且计数。
来看时序:
看到没,当读请求有效时,下一个时钟周期地址就开始计数并且累计了,但是app_rd_data_valid还需延迟一会才能有效。
其实把读写原理搞懂后就很简单,博主一开始卡住的地方就是写的那块,以为写数据需要app_rdy和app_wdf_rdy同时有效才能成功写入,没有搞懂命令和数据的关系,因为ip核的写数据是先缓存在fifo中的,所以即使当前写命令无效时,写数据依旧可以成功写入。
感觉是不是和SDRAM不一样啊,可能没用ip核和用了还是有区别的吧。
感觉ddr3的时序重要的还是这两点,其他的至于如何精确地址和数据对应,可以具体分析,会发现程序写的还是很严谨的啊。