转载:Vivado中MIG核中DDR的读写控制

本文使用Vivado 2015.4在Nexys4 DDR(以下简称N4DDR)开发板上实现DDR的读写。 
· FPGA如果需要对DDR进行读写,则需要一个DDR的控制器。根据官方的文档(UG586,下载链接在文末),DDR控制器的时序主要有三: 
(1)首先是控制信号,如下图: 

· 从上图可以看出,只有当app_rdy信号有效时,程序所发出的读写命令才会被控制器接收。这点必须注意。 
(2)然后是写操作时序,如下图: 
这里写图片描述
· 由图可知,在向DDR写数据时,需要提供写命令app_cmd、地址app_addr、数据app_wdf_data等信号,且写入的数据最多可以比app_cmd提前一个时钟周期有效,最迟可以比app_cmd晚两个时钟周期有效。 
【特别注意】在写数据的时候必须检测app_rdy和app_wdf_rdy信号是否同时有效,否则写入命令无法成功写入到DDR控制器的命令FIFO中,从而导致写操作失败。 
(3)最后是读操作时序,如下图所示: 
这里写图片描述
· 读操作的时序比较简单,只需要注意app_rdy是否有效即可,其余不再赘述。


· Xilinx在Vivado中提供的Memory Interface Generator的IP核就是我们需要的DDR控制器,如下图所示。 
Vivado中的MIG核
· 这里我们可以直接双击上面的MIG的IP核,开始例化我们所需的DDR控制器。(此时Win7以后的Windows版本(不含Win7)打开此IP核会报错,解决方法见http://blog.csdn.net/qq_20091945/article/details/53862467) 
· 打开后是如下图所示的界面,点Next。 
打开MIG
· 给模块起个名字,根据实际情况选择控制器数量(这里笔者选择1),继续Next,如下图所示。 
给MIG IP核起名字
· 在开发板芯片型号所对应的方框前打勾,如下图所示。 
选择芯片型号
· 根据开发板上的DDR芯片选择DDR的种类,如N4DDR的开发板上的DDR芯片是DDR2的,因此如下图选择。 
选择DDR类型
· 然后在Clock Period中输入合适的时钟周期长度(N4DDR的官方文档建议DDR的时钟为325MHz,故此处填3077ps); 
· 接着在Memory Part中选择开发板上的DDR芯片的具体型号(N4DDR官方文档上说明为MT47H64M16HR-25E); 
· 然后输入Data Width,此处以16为例。如下图所示。 
这里写图片描述
· 选择Input Clock Period,这里填开发板的系统时钟(N4DDR为100MHz)。根据应用需要选择地址映射方式(这里保持默认的Bank-Row-Column)。 
选择时钟和地址映射方式
· 然后,这里的System ClockReference Clock建议选择No Buffer,System Reset Polarity则根据应用需要灵活选择(这里设置为低电平有效),如下图所示。 
这里写图片描述
· Internal Termination Impedence的选取应当参考开发板的官方文档说明,这里选50欧姆即可,继续Next。 
这里写图片描述
· 选择Fixed Pin Out。 
这里写图片描述
· 接下来是DDR芯片的引脚分配。官网应该能找到,这里直接给出。文末会给出与此对应的引脚约束文件(n4ddr_ddr2_io_assign.ucf)。 
这里写图片描述
这里写图片描述
这里写图片描述
· 耐心填完之后点击Validate按钮,没有错误的话会弹出一个对话框提示“Current Pinout is valid.” 
· 然后的3个信号建议选择No connect,后面由我们自己根据需要连接到板上的相应引脚。 
这里写图片描述
· 后面一直Next下去,点Accept,然后就可以点击Generate了。后面会再弹出一个对话框,直接点默认选中的按钮即可。


· 好了,下面是笔者自己编写的测试DDR2读写的程序。文末将提供对应工程的下载链接。

 
  1. //*****************************************************************************

  2. // Author : Z.M.J. @ CSE, SEU

  3. // Application : MIG v2.4

  4. // Filename : example_top.v

  5. // Date Created : Fri Dec 30 2016

  6. //

  7. // Device : 7 Series (Nexys 4 DDR)

  8. // Design Name : DDR2 SDRAM

  9. // Purpose : A demo of DDR2's read and write

  10. // Reference : ug586_7Series_MIS_v2.4.pdf

  11. //*****************************************************************************

  12.  
  13. `timescale 1ps/1ps

  14.  
  15. module example_top (

  16. // system signals

  17. input sys_rst,

  18. input sys_clk_i,

  19. // application signals

  20. input [15:0] switch_i,

  21. output [15:0] led,

  22. output [7:0] an,

  23. output [7:0] select_seg,

  24. // DDR2 chip signals

  25. inout [15:0] ddr2_dq,

  26. inout [1:0] ddr2_dqs_n,

  27. inout [1:0] ddr2_dqs_p,

  28. output [12:0] ddr2_addr,

  29. output [2:0] ddr2_ba,

  30. output ddr2_ras_n,

  31. output ddr2_cas_n,

  32. output ddr2_we_n,

  33. output [0:0] ddr2_ck_p,

  34. output [0:0] ddr2_ck_n,

  35. output [0:0] ddr2_cke,

  36. output [0:0] ddr2_cs_n,

  37. output [1:0] ddr2_dm,

  38. output [0:0] ddr2_odt

  39. );

  40.  
  41. parameter DQ_WIDTH = 16;

  42. parameter ECC_TEST = "OFF";

  43. parameter ADDR_WIDTH = 27;

  44. parameter nCK_PER_CLK = 4;

  45.  
  46. localparam DATA_WIDTH = 16;

  47. localparam PAYLOAD_WIDTH = (ECC_TEST == "OFF") ? DATA_WIDTH : DQ_WIDTH;

  48. localparam APP_DATA_WIDTH = 2 * nCK_PER_CLK * PAYLOAD_WIDTH;

  49. localparam APP_MASK_WIDTH = APP_DATA_WIDTH / 8;

  50.  
  51. // Wire declarations

  52. reg app_en, app_wdf_wren, app_wdf_end;

  53. reg [2:0] app_cmd;

  54. reg [ADDR_WIDTH-1:0] app_addr;

  55. reg [APP_DATA_WIDTH-1:0] app_wdf_data;

  56. wire [APP_DATA_WIDTH-1:0] app_rd_data;

  57. wire [APP_MASK_WIDTH-1:0] app_wdf_mask;

  58. wire app_rdy, app_rd_data_end, app_rd_data_valid, app_wdf_rdy;

  59.  
  60.  
  61.  
  62. //***************************************************************************

  63. wire [7:0] an;

  64. wire [7:0] select_seg;

  65. reg [31:0] digit_data;

  66. always@ (posedge sys_clk_i) begin

  67. if (switch_i[3])

  68. digit_data <= app_addr;

  69. else case (switch_i[1:0])

  70. 2'b00 : digit_data <= read_data[31:0];

  71. 2'b01 : digit_data <= read_data[63:32];

  72. 2'b10 : digit_data <= read_data[95:64];

  73. 2'b11 : digit_data <= read_data[127:96];

  74. endcase

  75. end

  76.  
  77. digit U2(

  78. .wb_clk_i(sys_clk_i),

  79. .wb_rst_i(~sys_rst),

  80. .wb_dat_i(digit_data),

  81. .an(an),

  82. .select_seg(select_seg)

  83. );

  84.  
  85. reg [1:0] read_valid = 2'b0;

  86. reg [127:0] read_data = 128'h0;

  87. always@ (posedge app_rd_data_valid) begin

  88. read_data = app_rd_data;

  89. read_valid[0] = (app_rd_data == data0);

  90. read_valid[1] = (app_rd_data == data1);

  91. end

  92.  
  93. assign led[15] = app_en;

  94. assign led[14] = init_calib_complete;

  95. assign led[13] = app_rdy;

  96. assign led[12] = app_wdf_rdy;

  97. assign led[4] = sys_rst ? read_valid[1] : 1'b0;

  98. assign led[3] = sys_rst ? read_valid[0] : 1'b0;

  99. assign led[2] = stop_w[1];

  100. assign led[1] = stop_w[0];

  101. assign led[0] = app_cmd[0];

  102.  
  103. reg [15:0] counter = 16'h0;

  104. parameter cnt_init = 16'h1; // minimum: 1

  105. reg [26:0] addr0 = 27'h000_0008;

  106. reg [26:0] addr1 = 27'h003_0100;

  107. reg [127:0] data0 = 128'h1111_2222_3333_4444_5555_6666_7777_8888;

  108. reg [127:0] data1 = 128'h9999_0000_aaaa_bbbb_cccc_dddd_eeee_ffff;

  109. reg [1:0] stop_w = 2'b00;

  110. always@ (posedge sys_clk_i or negedge sys_rst) begin

  111. if (sys_rst == 1'b0) begin

  112. counter = 12'b0;

  113. stop_w = 2'b0;

  114. app_en = 1'b0;

  115. app_addr = 27'h0;

  116. app_cmd = 3'b1;

  117. app_wdf_data = 128'h0;

  118. app_wdf_end = 1'b0;

  119. app_wdf_wren = 1'b0;

  120. end else begin

  121. if (counter == cnt_init && ~stop_w[0])

  122. if (app_rdy & app_wdf_rdy) begin

  123. app_wdf_data = data0;

  124. app_addr = addr0;

  125. app_cmd = 3'b0;

  126. app_wdf_wren = 1'b1;

  127. app_wdf_end = 1'b1;

  128. app_en = 1'b1;

  129. end else // Hold specific signals until app_wdf_rdy is asserted.

  130. counter = counter - 16'h1;

  131. else if (counter == cnt_init + 1 && ~stop_w[0])

  132. if (app_rdy & app_wdf_rdy) begin

  133. app_wdf_end = 1'b0;

  134. app_wdf_wren = 1'b0;

  135. app_en = 1'b0;

  136. app_cmd = 3'b1;

  137. stop_w[0] = 1'b1;

  138. end else // Hold specific signals until app_wdf_rdy is asserted.

  139. counter = counter - 16'h1;

  140. else if (counter == cnt_init + 8 && ~stop_w[1])

  141. if (app_rdy & app_wdf_rdy) begin

  142. app_wdf_data = data1;

  143. app_addr = addr1;

  144. app_cmd = 3'b0;

  145. app_wdf_wren = 1'b1;

  146. app_wdf_end = 1'b1;

  147. app_en = 1'b1;

  148. end else // Hold specific signals until app_wdf_rdy is asserted.

  149. counter = counter - 16'h1;

  150. else if (counter == cnt_init + 9 && ~stop_w[1])

  151. if (app_rdy & app_wdf_rdy) begin

  152. app_wdf_end = 1'b0;

  153. app_wdf_wren = 1'b0;

  154. app_en = 1'b0;

  155. app_cmd = 3'b1;

  156. stop_w[1] = 1'b1;

  157. end else // Hold specific signals until app_wdf_rdy is asserted.

  158. counter = counter - 16'h1;

  159. else if (counter == cnt_init + 88) begin

  160. app_addr = switch_i[2] ? addr1 : addr0;

  161. app_en = 1'b1;

  162. if (~app_rdy) counter = counter - 16'h1;

  163. end else if (counter == cnt_init + 89)

  164. app_en = 1'b0;

  165.  
  166. counter = counter + 16'h1;

  167. end

  168. end

  169.  
  170. // Start of User Design top instance

  171. //***************************************************************************

  172. // The User design is instantiated below. The memory interface ports are

  173. // connected to the top-level and the application interface ports are

  174. // connected to the traffic generator module. This provides a reference

  175. // for connecting the memory controller to system.

  176. //***************************************************************************

  177. my_ddr u_my_ddr (

  178. // Memory interface ports

  179. .ddr2_cs_n (ddr2_cs_n),

  180. .ddr2_addr (ddr2_addr),

  181. .ddr2_ba (ddr2_ba),

  182. .ddr2_we_n (ddr2_we_n),

  183. .ddr2_ras_n (ddr2_ras_n),

  184. .ddr2_cas_n (ddr2_cas_n),

  185. .ddr2_ck_n (ddr2_ck_n),

  186. .ddr2_ck_p (ddr2_ck_p),

  187. .ddr2_cke (ddr2_cke),

  188. .ddr2_dq (ddr2_dq),

  189. .ddr2_dqs_n (ddr2_dqs_n),

  190. .ddr2_dqs_p (ddr2_dqs_p),

  191. .ddr2_dm (ddr2_dm),

  192. .ddr2_odt (ddr2_odt),

  193. // Application interface ports

  194. .app_addr (app_addr),

  195. .app_cmd (app_cmd),

  196. .app_en (app_en),

  197. .app_wdf_rdy (app_wdf_rdy),

  198. .app_wdf_data (app_wdf_data),

  199. .app_wdf_end (app_wdf_end),

  200. .app_wdf_wren (app_wdf_wren),

  201. .app_rd_data (app_rd_data),

  202. .app_rd_data_end (app_rd_data_end),

  203. .app_rd_data_valid (app_rd_data_valid),

  204. .app_rdy (app_rdy),

  205. .app_sr_req (1'b0),

  206. .app_ref_req (1'b0),

  207. .app_zq_req (1'b0),

  208. .app_wdf_mask (16'h0000),

  209. .init_calib_complete (init_calib_complete),

  210. // System Clock Ports

  211. .sys_clk_i (sys_clk_i),

  212. // Reference Clock Ports

  213. .clk_ref_i (sys_clk_i),

  214. .sys_rst (sys_rst)

  215. );

  216.  
  217. endmodule

  •  
  •  

· 保存后直接生成比特流就可以下板验证了。 
· 在摸索过程中笔者发现,写入了数据之后最快要到发出写命令的第8个系统时钟才能读出所写入的数据,且读操作必须在写操作后经过8的整数倍个时钟后进行。有时将比特流下载到N4DDR上面之后读写的数据有误,但是重启开发板再重新下载即可解决问题,知道个中缘由的朋友欢迎在评论中告知笔者,笔者在此先行谢过。 
· 需要说明的是,此处突发长度(BL)为8,因此app_addr必须是8对齐的地址。同时,由于前面选择的Data Width为16,因此每次读写数据的长度为8*16bit==128bit。

  • 1
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值