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. output[ADDR_BITS-1:0] app_addr,
  24. output[2:0] app_cmd,
  25. output app_en,
  26. output [MEM_DATA_BITS-1:0] app_wdf_data,
  27. output app_wdf_end,
  28. output [MEM_DATA_BITS/8-1:0] app_wdf_mask,
  29. output app_wdf_wren,
  30. input [MEM_DATA_BITS-1:0] app_rd_data,
  31. input app_rd_data_end,
  32. input app_rd_data_valid,
  33. input app_rdy,
  34. input app_wdf_rdy,
  35. input ui_clk_sync_rst,
  36. input init_calib_complete
  37. );
  38. assign app_wdf_mask = {MEM_DATA_BITS/8{1'b0}};
  39. localparam IDLE = 3'd0;
  40. localparam MEM_READ = 3'd1;
  41. localparam MEM_READ_WAIT = 3'd2;
  42. localparam MEM_WRITE = 3'd3;
  43. localparam MEM_WRITE_WAIT = 3'd4;
  44. localparam READ_END = 3'd5;
  45. localparam WRITE_END = 3'd6;
  46. localparam MEM_WRITE_FIRST_READ = 3'd7;
  47. /*parameter IDLE = 3'd0;
  48. parameter MEM_READ = 3'd1;
  49. parameter MEM_READ_WAIT = 3'd2;
  50. parameter MEM_WRITE = 3'd3;
  51. parameter MEM_WRITE_WAIT = 3'd4;
  52. parameter READ_END = 3'd5;
  53. parameter WRITE_END = 3'd6;
  54. parameter MEM_WRITE_FIRST_READ = 3'd7;*/
  55. reg[2:0] state;
  56. reg[9:0] rd_addr_cnt;
  57. reg[9:0] rd_data_cnt;
  58. reg[9:0] wr_addr_cnt;
  59. reg[9:0] wr_data_cnt;
  60. reg[2:0] app_cmd_r;
  61. reg[ADDR_BITS-1:0] app_addr_r;
  62. reg app_en_r;
  63. reg app_wdf_end_r;
  64. reg app_wdf_wren_r;
  65. assign app_cmd = app_cmd_r;
  66. assign app_addr = app_addr_r;
  67. assign app_en = app_en_r;
  68. assign app_wdf_end = app_wdf_end_r;
  69. assign app_wdf_data = wr_burst_data;
  70. assign app_wdf_wren = app_wdf_wren_r & app_wdf_rdy;
  71. assign rd_burst_finish = (state == READ_END);
  72. assign wr_burst_finish = (state == WRITE_END);
  73. assign burst_finish = rd_burst_finish | wr_burst_finish;
  74. assign rd_burst_data = app_rd_data;
  75. assign rd_burst_data_valid = app_rd_data_valid;
  76. assign wr_burst_data_req = (state == MEM_WRITE) & app_wdf_rdy ;
  77. always@(posedge mem_clk or posedge rst)
  78. begin
  79. if(rst)
  80. begin
  81. app_wdf_wren_r <= 1'b0;
  82. end
  83. else if(app_wdf_rdy)
  84. app_wdf_wren_r <= wr_burst_data_req;
  85. end
  86. always@(posedge mem_clk or posedge rst)
  87. begin
  88. if(rst)
  89. begin
  90. state <= IDLE;
  91. app_cmd_r <= 3'b000;
  92. app_addr_r <= 0;
  93. app_en_r <= 1'b0;
  94. rd_addr_cnt <= 0;
  95. rd_data_cnt <= 0;
  96. wr_addr_cnt <= 0;
  97. wr_data_cnt <= 0;
  98. app_wdf_end_r <= 1'b0;
  99. end
  100. else if(init_calib_complete === 1'b1)
  101. begin
  102. case(state)
  103. IDLE:
  104. begin
  105. if(rd_burst_req)
  106. begin
  107. state <= MEM_READ;
  108. app_cmd_r <= 3'b001;
  109. app_addr_r <= {rd_burst_addr,3'd0};
  110. app_en_r <= 1'b1;
  111. end
  112. else if(wr_burst_req)
  113. begin
  114. state <= MEM_WRITE;
  115. app_cmd_r <= 3'b000;
  116. app_addr_r <= {wr_burst_addr,3'd0};
  117. app_en_r <= 1'b1;
  118. wr_addr_cnt <= 0;
  119. app_wdf_end_r <= 1'b1;
  120. wr_data_cnt <= 0;
  121. end
  122. end
  123. MEM_READ:
  124. begin
  125. if(app_rdy)
  126. begin
  127. app_addr_r <= app_addr_r + 8;
  128. if(rd_addr_cnt == rd_burst_len - 1)
  129. begin
  130. state <= MEM_READ_WAIT;
  131. rd_addr_cnt <= 0;
  132. app_en_r <= 1'b0;
  133. end
  134. else
  135. rd_addr_cnt <= rd_addr_cnt + 1;
  136. end
  137. if(app_rd_data_valid)
  138. begin
  139. //app_addr_r <= app_addr_r + 8;
  140. if(rd_data_cnt == rd_burst_len - 1)
  141. begin
  142. rd_data_cnt <= 0;
  143. state <= READ_END;
  144. end
  145. else
  146. begin
  147. rd_data_cnt <= rd_data_cnt + 1;
  148. end
  149. end
  150. end
  151. MEM_READ_WAIT:
  152. begin
  153. if(app_rd_data_valid)
  154. begin
  155. if(rd_data_cnt == rd_burst_len - 1)
  156. begin
  157. rd_data_cnt <= 0;
  158. state <= READ_END;
  159. end
  160. else
  161. begin
  162. rd_data_cnt <= rd_data_cnt + 1;
  163. end
  164. end
  165. end
  166. MEM_WRITE_FIRST_READ:
  167. begin
  168. app_en_r <= 1'b1;
  169. state <= MEM_WRITE;
  170. wr_addr_cnt <= 0;
  171. end
  172. MEM_WRITE:
  173. begin
  174. if(app_rdy)
  175. begin
  176. app_addr_r <= app_addr_r + 8;
  177. if(wr_addr_cnt == wr_burst_len - 1)
  178. begin
  179. app_wdf_end_r <= 1'b0;
  180. app_en_r <= 1'b0;
  181. end
  182. else
  183. begin
  184. wr_addr_cnt <= wr_addr_cnt + 1;
  185. end
  186. end
  187. if(wr_burst_data_req)
  188. begin
  189. //app_addr_r <= app_addr_r + 8;
  190. if(wr_data_cnt == wr_burst_len - 1)
  191. begin
  192. state <= MEM_WRITE_WAIT;
  193. end
  194. else
  195. begin
  196. wr_data_cnt <= wr_data_cnt + 1;
  197. end
  198. end
  199. end
  200. READ_END:
  201. state <= IDLE;
  202. MEM_WRITE_WAIT:
  203. begin
  204. if(app_rdy)
  205. begin
  206. app_addr_r <= app_addr_r + 'b1000;
  207. if(wr_addr_cnt == wr_burst_len - 1)
  208. begin
  209. app_wdf_end_r <= 1'b0;
  210. app_en_r <= 1'b0;
  211. if(app_wdf_rdy)
  212. state <= WRITE_END;
  213. end
  214. else
  215. begin
  216. wr_addr_cnt <= wr_addr_cnt + 1;
  217. end
  218. end
  219. else if(~app_en_r & app_wdf_rdy)
  220. state <= WRITE_END;
  221. end
  222. WRITE_END:
  223. state <= IDLE;
  224. default:
  225. state <= IDLE;
  226. endcase
  227. end
  228. end
  229. 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的时序重要的还是这两点,其他的至于如何精确地址和数据对应,可以具体分析,会发现程序写的还是很严谨的啊。。。

(0)

相关推荐