小言_互联网的博客

【DDR3 控制器设计】(7)DDR3 的用户端口读写模块设计

412人阅读  评论(0)

写在前面

本系列为 DDR3 控制器设计总结,此系列包含 DDR3 控制器相关设计:认识 MIG、初始化、读写操作、FIFO 接口等。通过此系列的学习可以加深对 DDR3 读写时序的理解以及 FIFO 接口设计等,附上汇总博客直达链接。

【DDR3 控制器设计】系列博客汇总篇(附直达链接)


目录

实验任务

实验环境

实验介绍

接口详解

用户写控制模块

接口申明

用户读控制模块

程序设计

写控制模块

读控制模块

顶层模块

仿真测试

汇总篇


实验任务

在之前设计的 DDR3 控制器的基础上,添加用户写、读模块,便于在用户端更容易的对 DDR3 进行写读控制。

实验环境

开发环境:Vivado 2018.2

FPGA 芯片型号:xc7a100tffg484-2

DDR3 型号:MT41J256M16HA-125

实验介绍

经过前几部分的控制器设计,已经包含有DDR3的读写模块、读写仲裁模块,读写指令 FIFO以及读写数据 FIFO 模块,现在便可以对此项设计进行写读验证,但是直接对 FIFO 端的信号进行控制的话,信号过多设计起来较为麻烦,因此考虑在外围添加用户端的写、读控制模块,减少信号交互数,便于用户端进行写读控制。

接口详解

用户写控制模块

用户端的写控制模块是与写指令 FIFO 和写数据 FIFO 的接口进行交互的,因此可以依据 FIFO 的相关信号进行用户写模块的设计,大致设计思路为:当用户写使能信号拉高时,依次将写数据累加,并且定义一个计数器用于计算写入数据的个数,这个可以作为突发长度的设计,当数据完全写入写数据 FIFO 中时,输出相应的指令信号:指令类型、突发长度、初始地址。其中指令类型可以固定为全0表示进行写操作,突发长度可以根据写入数据计数值得到,初始地址根据每次指令使能信号拉高累加。最后当写数据 FIFO 的空信号拉高时,拉个一个时钟周期的单次写完成信号。

接口申明

信号

方向

描述

clk

input

用户端时钟信号

rst_n

input

用户端复位,低有效

wr_en

input

写数据使能信号

wr_data

input

写入的数据

fifo_wr_data_empty

input

写fifo为空信号

fifo_wr_data_en

output

写数据fifo写使能信号

fifo_wr_data

output

写入fifo的数据

fifo_wr_cmd_en

output

写指令fifo写使能信号

fifo_wr_cmd_brust_len

output

写数据突发长度

fifo_wr_cmd_addr

output

此次写数据初始地址

fifo_wr_cmd_instr

output

操作指令信号

user_wr_end

output

单次写操作完成标志信号

用户读控制模块

用户端的读控制模块是与读指令 FIFO 和读数据 FIFO 的接口进行交互的,因此可以依据 FIFO 的相关信号进行用户读模块的设计,大致设计思路为:当开始读信号拉高时,依次累加读地址信号,并定义一个计数器,用于计算读出数据的个数,当读出的数据数量达到单次突发长度,并拉低读使能信号,并且拉高读完成信号,表示单次的读操作完成。

程序设计

写控制模块


  
  1. /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
  2. /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
  3. // Engineer : Linest-5
  4. // File : user_wr_ctrl.v
  5. // Create : 2022-09-29 15:07:20
  6. // Revise : 2022-09-29 16:26:45
  7. // Module Name : user_wr_ctrl
  8. // Description : 用户端写控制模块
  9. // Editor : sublime text3, tab size (4)
  10. /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
  11. /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
  12. module user_wr_ctrl(
  13. input clk, //用户端时钟
  14. input rst_n, //用户端复位,低有效
  15. input wr_en, //写数据使能信号
  16. input [ 127: 0] wr_data, //写入的数据
  17. input fifo_wr_data_empty, //写fifo为空信号
  18. output reg fifo_wr_data_en, //写数据fifo写使能信号
  19. output reg [ 127: 0] fifo_wr_data, //写入fifo的数据
  20. output reg fifo_wr_cmd_en, //写指令fifo写使能信号
  21. output reg [ 7: 0] fifo_wr_cmd_brust_len, //写数据突发长度
  22. output reg [ 28: 0] fifo_wr_cmd_addr, //此次写数据初始地址
  23. output [ 2: 0] fifo_wr_cmd_instr, //操作指令信号
  24. output reg user_wr_end //单次写操作完成标志信号
  25. );
  26. /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
  27. /* 信号定义 */
  28. /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
  29. reg [ 7: 0] data_cnt; //写入数据个数计数信号
  30. reg fifo_wr_data_empty_reg; //写数据fifo空标志打拍信号
  31. /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
  32. /* Main Code */
  33. /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
  34. //写指令:3'b000,读指令:3'b001
  35. assign fifo_wr_cmd_instr = 3'b000;
  36. //写入的数据个数计数信号
  37. always @(posedge clk or negedge rst_n) begin
  38. if (!rst_n) begin
  39. data_cnt <= 'd0;
  40. end
  41. else if (wr_en) begin
  42. data_cnt <= data_cnt + 'd1;
  43. end
  44. else begin
  45. data_cnt <= 'd0;
  46. end
  47. end
  48. //对数据使能信号打拍输出
  49. always @(posedge clk or negedge rst_n) begin
  50. if (!rst_n) begin
  51. fifo_wr_data_en <= 'd0;
  52. end
  53. else begin
  54. fifo_wr_data_en <= wr_en;
  55. end
  56. end
  57. //对写入的数据打拍输出
  58. always @(posedge clk or negedge rst_n) begin
  59. if (!rst_n) begin
  60. fifo_wr_data <= 'd0;
  61. end
  62. else begin
  63. fifo_wr_data <= wr_data;
  64. end
  65. end
  66. //当数据完全写入fifo中后,启动命令使能信号
  67. always @(posedge clk or negedge rst_n) begin
  68. if (!rst_n) begin
  69. fifo_wr_cmd_en <= 'd0;
  70. end
  71. else if ((~wr_en) && fifo_wr_data_en) begin
  72. fifo_wr_cmd_en <= 'd1;
  73. end
  74. else begin
  75. fifo_wr_cmd_en <= 'd0;
  76. end
  77. end
  78. //根据写入的数据计数得到突发长度输出
  79. always @(posedge clk or negedge rst_n) begin
  80. if (!rst_n) begin
  81. fifo_wr_cmd_brust_len <= 'd0;
  82. end
  83. else if ((~wr_en) && fifo_wr_data_en) begin
  84. fifo_wr_cmd_brust_len <= data_cnt;
  85. end
  86. else begin
  87. fifo_wr_cmd_brust_len <= 'd0;
  88. end
  89. end
  90. //根据每次的写数据的个数确定下一次写的初始地址
  91. always @(posedge clk or negedge rst_n) begin
  92. if (!rst_n) begin
  93. fifo_wr_cmd_addr <= 'd0;
  94. end
  95. else if (fifo_wr_cmd_en) begin
  96. fifo_wr_cmd_addr <= fifo_wr_cmd_addr + (fifo_wr_cmd_brust_len << 'd3);
  97. end
  98. else begin
  99. fifo_wr_cmd_addr <= fifo_wr_cmd_addr;
  100. end
  101. end
  102. //对写数据fifo的空标志拉高信号打拍
  103. always @(posedge clk or negedge rst_n) begin
  104. if (!rst_n) begin
  105. fifo_wr_data_empty_reg <= 'd0;
  106. end
  107. else begin
  108. fifo_wr_data_empty_reg <= fifo_wr_data_empty;
  109. end
  110. end
  111. //一次写完成信号生成
  112. always @(posedge clk or negedge rst_n) begin
  113. if (!rst_n) begin
  114. user_wr_end <= 'd0;
  115. end
  116. else if (fifo_wr_data_empty && (~fifo_wr_data_empty_reg)) begin
  117. user_wr_end <= 'd1;
  118. end
  119. else begin
  120. user_wr_end <= 'd0;
  121. end
  122. end
  123. endmodule

读控制模块


  
  1. /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
  2. /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
  3. // Engineer : Linest-5
  4. // File : user_rd_ctrl.v
  5. // Create : 2022-09-29 16:27:57
  6. // Revise : 2022-09-29 16:27:57
  7. // Module Name : user_rd_ctrl
  8. // Description : 用户端读控制模块
  9. // Editor : sublime text3, tab size (4)
  10. /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
  11. /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
  12. module user_rd_ctrl(
  13. input clk, //用户端时钟
  14. input rst_n, //用户端复位,低有效
  15. input rd_start,
  16. input [ 6: 0] fifo_rd_data_count,
  17. output reg fifo_rd_data_en,
  18. output reg fifo_rd_cmd_en,
  19. output [ 7: 0] fifo_rd_cmd_brust_len,
  20. output reg [ 28: 0] fifo_rd_cmd_addr,
  21. output [ 2: 0] fifo_rd_cmd_instr,
  22. output reg user_rd_end
  23. );
  24. /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
  25. /* 信号定义 */
  26. /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
  27. reg [ 7: 0] rd_data_cnt;
  28. /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
  29. /* Main Code */
  30. /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
  31. //对开始读信号打拍输出
  32. always @(posedge clk or negedge rst_n) begin
  33. if (!rst_n) begin
  34. fifo_rd_cmd_en <= 'd0;
  35. end
  36. else begin
  37. fifo_rd_cmd_en <= rd_start;
  38. end
  39. end
  40. assign fifo_rd_cmd_brust_len = 'd64;
  41. assign fifo_rd_cmd_instr = 3'b001;
  42. //每次读操作的初始地址
  43. always @(posedge clk or negedge rst_n) begin
  44. if (!rst_n) begin
  45. fifo_rd_cmd_addr <= 'd0;
  46. end
  47. else if (fifo_rd_cmd_en) begin
  48. fifo_rd_cmd_addr <= fifo_rd_cmd_addr + (fifo_rd_cmd_brust_len << 'd3);
  49. end
  50. else begin
  51. fifo_rd_cmd_addr <= fifo_rd_cmd_addr;
  52. end
  53. end
  54. always @(posedge clk or negedge rst_n) begin
  55. if (!rst_n) begin
  56. fifo_rd_data_en <= 'd0;
  57. end
  58. else if (fifo_rd_data_en && (rd_data_cnt == (fifo_rd_cmd_brust_len - 'd1))) begin
  59. fifo_rd_data_en <= 'd0;
  60. end
  61. else if (fifo_rd_data_count == (fifo_rd_cmd_brust_len - 'd6)) begin
  62. fifo_rd_data_en <= 'd1;
  63. end
  64. else begin
  65. fifo_rd_data_en <= fifo_rd_data_en;
  66. end
  67. end
  68. always @(posedge clk or negedge rst_n) begin
  69. if (!rst_n) begin
  70. rd_data_cnt <= 'd0;
  71. end
  72. else if (fifo_rd_data_en && (rd_data_cnt == (fifo_rd_cmd_brust_len - 'd1))) begin
  73. rd_data_cnt <= 'd0;
  74. end
  75. else if (fifo_rd_data_en) begin
  76. rd_data_cnt <= rd_data_cnt + 'd1;
  77. end
  78. else begin
  79. rd_data_cnt <= 'd0;
  80. end
  81. end
  82. always @(posedge clk or negedge rst_n) begin
  83. if (!rst_n) begin
  84. user_rd_end <= 'd0;
  85. end
  86. else if (fifo_rd_data_en && (rd_data_cnt == (fifo_rd_cmd_brust_len - 'd1))) begin
  87. user_rd_end <= 'd1;
  88. end
  89. else begin
  90. user_rd_end <= 'd0;
  91. end
  92. end
  93. endmodule

顶层模块


  
  1. /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
  2. /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
  3. /* Engineer : Linest-5
  4. /* File : top_ddr3_init.v
  5. /* Create : 2022-09-15 09:58:59
  6. /* Revise : 2022-09-24 21:11:50
  7. /* Module Name :
  8. /* Description :
  9. /* Editor : sublime text3, tab size (4)
  10. /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
  11. /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
  12. module top_ddr3_init(
  13. // Inouts
  14. inout [15:0] ddr3_dq,
  15. inout [1:0] ddr3_dqs_n,
  16. inout [1:0] ddr3_dqs_p,
  17. // Outputs
  18. output [14:0] ddr3_addr,
  19. output [2:0] ddr3_ba,
  20. output ddr3_ras_n,
  21. output ddr3_cas_n,
  22. output ddr3_we_n,
  23. output ddr3_reset_n,
  24. output [0:0] ddr3_ck_p,
  25. output [0:0] ddr3_ck_n,
  26. output [0:0] ddr3_cke,
  27. output [0:0] ddr3_cs_n,
  28. output [1:0] ddr3_dm,
  29. output [0:0] ddr3_odt,
  30. // Inputs
  31. // Differential system clocks
  32. input sys_clk,
  33. input rst_n
  34. );
  35. wire init_calib_complete;
  36. wire ui_clk;
  37. wire ui_clk_sync_rst;
  38. wire [ 127: 0] wr_data;
  39. wire [ 7: 0] wr_brust_len;
  40. wire wr_start;
  41. wire [ 28: 0] wr_addr;
  42. wire [ 2: 0] wr_cmd;
  43. wire [ 15: 0] wr_mask;
  44. wire data_req;
  45. wire wr_end;
  46. wire app_rdy;
  47. wire app_wdf_rdy;
  48. wire [ 2: 0] app_cmd;
  49. wire app_en;
  50. wire [ 28: 0] app_addr;
  51. wire [ 127: 0] app_wdf_data;
  52. wire app_wdf_wren;
  53. wire [ 15: 0] app_wdf_mask;
  54. wire app_wdf_end;
  55. wire [ 7: 0] rd_brust_len;
  56. wire rd_start;
  57. wire [ 28: 0] rd_addr;
  58. wire [ 2: 0] rd_cmd;
  59. wire [ 127: 0] rd_data;
  60. wire rd_data_valid;
  61. wire rd_end;
  62. wire [ 127: 0] app_rd_data;
  63. wire app_rd_data_end;
  64. wire app_rd_data_valid;
  65. wire [ 2: 0] app_wr_cmd;
  66. wire app_wr_en;
  67. wire [ 28: 0] app_wr_addr;
  68. wire [ 2: 0] app_rd_cmd;
  69. wire app_rd_en;
  70. wire [ 28: 0] app_rd_addr;
  71. /**************FIFO控制部分*******************/
  72. wire user_clk;
  73. //写指令部分
  74. wire fifo_wr_cmd_en;
  75. wire [ 7: 0] fifo_wr_cmd_brust_len;
  76. wire [ 28: 0] fifo_wr_cmd_addr;
  77. wire [ 2: 0] fifo_wr_cmd_instr;
  78. wire fifo_wr_cmd_empty;
  79. wire fifo_wr_cmd_full;
  80. //写数据部分
  81. wire fifo_wr_data_en;
  82. wire [ 127: 0] fifo_wr_data;
  83. wire fifo_wr_data_full ;
  84. wire fifo_wr_data_empty;
  85. wire [ 6: 0] fifo_wr_data_count;
  86. //读指令部分
  87. wire fifo_rd_cmd_en;
  88. wire [ 7: 0] fifo_rd_cmd_brust_len;
  89. wire [ 28: 0] fifo_rd_cmd_addr;
  90. wire [ 2: 0] fifo_rd_cmd_instr;
  91. wire fifo_rd_cmd_empty;
  92. wire fifo_rd_cmd_full;
  93. //读数据部分
  94. wire fifo_rd_data_en;
  95. wire [ 127: 0] fifo_rd_data;
  96. wire fifo_rd_data_full;
  97. wire fifo_rd_data_empty;
  98. wire [ 6: 0] fifo_rd_data_count;
  99. //用户写控制模块
  100. wire user_wr_en;
  101. wire [ 127: 0] user_wr_data;
  102. wire user_wr_end;
  103. //用户读控制模块
  104. wire user_rd_start;
  105. wire user_rd_end;
  106. assign app_en = app_wr_en | app_rd_en;
  107. assign app_addr = app_wr_addr | app_rd_addr;
  108. assign app_cmd = (app_wr_en == 'd1) ? 3'b000 : 3'b001;
  109. //DDR工作时钟PLL例化
  110. ddr3_clock ddr3_clock_inst(
  111. .clk_out1(sys_clk_in), // output clk_out1
  112. . clk_in1( sys_clk) // input clk_in1
  113. );
  114. //DDR初始化模块例化
  115. ddr3_init u_ddr3_init (
  116. // Memory interface ports
  117. .ddr3_addr (ddr3_addr), // output [14:0] ddr3_addr
  118. . ddr3_ba ( ddr3_ba), // output [2:0] ddr3_ba
  119. . ddr3_cas_n ( ddr3_cas_n), // output ddr3_cas_n
  120. . ddr3_ck_n ( ddr3_ck_n), // output [0:0] ddr3_ck_n
  121. . ddr3_ck_p ( ddr3_ck_p), // output [0:0] ddr3_ck_p
  122. . ddr3_cke ( ddr3_cke), // output [0:0] ddr3_cke
  123. . ddr3_ras_n ( ddr3_ras_n), // output ddr3_ras_n
  124. . ddr3_reset_n ( ddr3_reset_n), // output ddr3_reset_n
  125. . ddr3_we_n ( ddr3_we_n), // output ddr3_we_n
  126. . ddr3_dq ( ddr3_dq), // inout [15:0] ddr3_dq
  127. . ddr3_dqs_n ( ddr3_dqs_n), // inout [1:0] ddr3_dqs_n
  128. . ddr3_dqs_p ( ddr3_dqs_p), // inout [1:0] ddr3_dqs_p
  129. . init_calib_complete ( init_calib_complete), // output init_calib_complete
  130. . ddr3_cs_n ( ddr3_cs_n), // output [0:0] ddr3_cs_n
  131. . ddr3_dm ( ddr3_dm), // output [1:0] ddr3_dm
  132. . ddr3_odt ( ddr3_odt), // output [0:0] ddr3_odt
  133. // Application interface ports
  134. . app_addr ( app_addr), // input [28:0] app_addr
  135. . app_cmd ( app_cmd), // input [2:0] app_cmd
  136. . app_en ( app_en), // input app_en
  137. . app_wdf_data ( app_wdf_data), // input [127:0] app_wdf_data
  138. . app_wdf_end ( app_wdf_wren), // input app_wdf_end
  139. . app_wdf_wren ( app_wdf_wren), // input app_wdf_wren
  140. . app_rd_data ( app_rd_data), // output [127:0] app_rd_data
  141. . app_rd_data_end ( app_rd_data_end), // output app_rd_data_end
  142. . app_rd_data_valid ( app_rd_data_valid), // output app_rd_data_valid
  143. . app_rdy ( app_rdy), // output app_rdy
  144. . app_wdf_rdy ( app_wdf_rdy), // output app_wdf_rdy
  145. . app_sr_req ( 1'b0), // input app_sr_req
  146. . app_ref_req ( 1'b0), // input app_ref_req
  147. . app_zq_req ( 1'b0), // input app_zq_req
  148. . app_sr_active ( app_sr_active), // output app_sr_active
  149. . app_ref_ack ( app_ref_ack), // output app_ref_ack
  150. . app_zq_ack ( app_zq_ack), // output app_zq_ack
  151. . ui_clk ( ui_clk), // output ui_clk
  152. . ui_clk_sync_rst ( ui_clk_sync_rst), // output ui_clk_sync_rst
  153. . app_wdf_mask ( app_wdf_mask), // input [15:0] app_wdf_mask
  154. // System Clock Ports
  155. . sys_clk_i ( sys_clk_in), // input sys_clk_i
  156. . sys_rst ( rst_n) // input sys_rst
  157. );
  158. //用户端写控制模块例化
  159. user_wr_ctrl inst_user_wr_ctrl (
  160. .clk (user_clk),
  161. .rst_n ((~ui_clk_sync_rst) || (init_calib_complete)),
  162. .wr_en (user_wr_en),
  163. .wr_data (user_wr_data),
  164. .fifo_wr_data_empty (fifo_wr_data_empty),
  165. .fifo_wr_data_en (fifo_wr_data_en),
  166. .fifo_wr_data (fifo_wr_data),
  167. .fifo_wr_cmd_en (fifo_wr_cmd_en),
  168. .fifo_wr_cmd_brust_len (fifo_wr_cmd_brust_len),
  169. .fifo_wr_cmd_addr (fifo_wr_cmd_addr),
  170. .fifo_wr_cmd_instr (fifo_wr_cmd_instr),
  171. .user_wr_end (user_wr_end)
  172. );
  173. //用户端读控制模块
  174. user_rd_ctrl inst_user_rd_ctrl (
  175. .clk (user_clk),
  176. .rst_n ((~ui_clk_sync_rst) || (init_calib_complete)),
  177. .rd_start (user_rd_start),
  178. .fifo_rd_data_count (fifo_rd_data_count),
  179. .fifo_rd_data_en (fifo_rd_data_en),
  180. .fifo_rd_cmd_en (fifo_rd_cmd_en),
  181. .fifo_rd_cmd_brust_len (fifo_rd_cmd_brust_len),
  182. .fifo_rd_cmd_addr (fifo_rd_cmd_addr),
  183. .fifo_rd_cmd_instr (fifo_rd_cmd_instr),
  184. .user_rd_end (user_rd_end)
  185. );
  186. //写指令FIFO控制模块例化
  187. wr_cmd_fifo_ctrl inst_wr_cmd_fifo_ctrl (
  188. .wr_cmd_clk (user_clk), //input
  189. .ui_clk (ui_clk),//input
  190. .rst (ui_clk_sync_rst || (~init_calib_complete)),//input
  191. .fifo_wr_cmd_en (fifo_wr_cmd_en),//input
  192. .fifo_wr_cmd_brust_len (fifo_wr_cmd_brust_len),//input
  193. .fifo_wr_cmd_addr (fifo_wr_cmd_addr),//input
  194. .fifo_wr_cmd_instr (fifo_wr_cmd_instr),//input
  195. .fifo_wr_cmd_start (wr_start),//input
  196. .fifo_wr_cmd_empty (fifo_wr_cmd_empty),
  197. .fifo_wr_cmd_full (fifo_wr_cmd_full),
  198. .wr_brust_len (wr_brust_len),
  199. .wr_addr (wr_addr),
  200. .wr_cmd (wr_cmd)
  201. );
  202. //写数据FIFO控制模块例化
  203. wr_data_fifo_ctrl inst_wr_data_fifo_ctrl (
  204. .wr_data_clk (user_clk), //input
  205. .ui_clk (ui_clk),//input
  206. .rst (ui_clk_sync_rst || (~init_calib_complete)),//input
  207. .fifo_wr_data_en (fifo_wr_data_en),//input
  208. .fifo_wr_data (fifo_wr_data),//input
  209. .data_req (data_req),//input
  210. .wr_data (wr_data),
  211. .fifo_wr_data_full (fifo_wr_data_full),
  212. .fifo_wr_data_empty (fifo_wr_data_empty),
  213. .fifo_wr_data_count (fifo_wr_data_count)
  214. );
  215. //读指令FIFO控制模块例化
  216. rd_cmd_fifo_ctrl inst_rd_cmd_fifo_ctrl (
  217. .rd_cmd_clk (user_clk), //input
  218. .ui_clk (ui_clk),//input
  219. .rst (ui_clk_sync_rst || (~init_calib_complete)),//input
  220. .fifo_rd_cmd_en (fifo_rd_cmd_en),//input
  221. .fifo_rd_cmd_brust_len (fifo_rd_cmd_brust_len),//input
  222. .fifo_rd_cmd_addr (fifo_rd_cmd_addr),//input
  223. .fifo_rd_cmd_instr (fifo_rd_cmd_instr),//input
  224. .fifo_rd_cmd_start (rd_start),//input
  225. .fifo_rd_cmd_empty (fifo_rd_cmd_empty),
  226. .fifo_rd_cmd_full (fifo_rd_cmd_full),
  227. .rd_brust_len (rd_brust_len),
  228. .rd_addr (rd_addr),
  229. .rd_cmd (rd_cmd)
  230. );
  231. //读数据FIFO控制模块例化
  232. rd_data_fifo_ctrl inst_rd_data_fifo_ctrl (
  233. .rd_data_clk (user_clk), //input
  234. .ui_clk (ui_clk),//input
  235. .rst (ui_clk_sync_rst || (~init_calib_complete)),//input
  236. .fifo_rd_data_en (fifo_rd_data_en),//input
  237. .rd_data_valid (rd_data_valid),//input
  238. .rd_data (rd_data),//input
  239. .fifo_rd_data (fifo_rd_data),
  240. .fifo_rd_data_full (fifo_rd_data_full),
  241. .fifo_rd_data_empty (fifo_rd_data_empty),
  242. .fifo_rd_data_count (fifo_rd_data_count)
  243. );
  244. //DDR仲裁模块例化
  245. ddr3_arbit inst_ddr3_arbit (
  246. .ui_clk (ui_clk),
  247. .rst (ui_clk_sync_rst || (~init_calib_complete)),
  248. .wr_req (~fifo_wr_cmd_empty),
  249. .rd_req (~fifo_rd_cmd_empty),
  250. .wr_end (wr_end),
  251. .rd_end (rd_end),
  252. .wr_start (wr_start),
  253. .rd_start (rd_start)
  254. );
  255. //DDR写操作模块例化
  256. ddr3_wr_ctrl inst_ddr3_wr_ctrl (
  257. .ui_clk (ui_clk),
  258. .rst (ui_clk_sync_rst || (~init_calib_complete)),
  259. .wr_data (wr_data),
  260. .wr_brust_len (wr_brust_len),
  261. .wr_start (wr_start),
  262. .wr_addr (wr_addr),
  263. .wr_cmd (wr_cmd),
  264. .wr_mask (wr_mask),
  265. .data_req (data_req),
  266. .wr_end (wr_end),
  267. .app_rdy (app_rdy),
  268. .app_wdf_rdy (app_wdf_rdy),
  269. .app_cmd (app_wr_cmd),
  270. .app_en (app_wr_en),
  271. .app_addr (app_wr_addr),
  272. .app_wdf_data (app_wdf_data),
  273. .app_wdf_wren (app_wdf_wren),
  274. .app_wdf_mask (app_wdf_mask),
  275. .app_wdf_end (app_wdf_end)
  276. );
  277. //DDR读操作模块例化
  278. ddr3_rd inst_ddr3_rd (
  279. .ui_clk (ui_clk),
  280. .rst (ui_clk_sync_rst || (~init_calib_complete)),
  281. .init_calib_complete (init_calib_complete),
  282. .rd_brust_len (rd_brust_len),
  283. .rd_start (rd_start),
  284. .rd_addr (rd_addr),
  285. .rd_cmd (rd_cmd),
  286. .rd_data (rd_data),
  287. .rd_data_valid (rd_data_valid),
  288. .rd_end (rd_end),
  289. .app_rdy (app_rdy),
  290. .app_rd_data (app_rd_data),
  291. .app_rd_data_end (app_rd_data_end),
  292. .app_rd_data_valid (app_rd_data_valid),
  293. .app_cmd (app_rd_cmd),
  294. .app_en (app_rd_en),
  295. .app_addr (app_rd_addr)
  296. );
  297. endmodule

最后编写测试文件即可观察仿真波形,提供激励的信号不多,按照写读时序依次给出使能等信号即可。

仿真测试

模块信号分类

开启仿真将顶层信号调出,由于模块和信号众多,所以将各个模块信号分类,依次分析观察。分别为以下几类:

  • 用户写模块
  • 写指令 FIFO 模块
  • 写数据 FIFO 模块
  • DDR 写控制模块
  • DDR 读控制模块
  • 读指令 FIFO 模块
  • 读数据 FIFO 模块
  • 用户读模块
  • 仲裁模块

用户写模块

当用户端的写使能信号拉高,依次将写数据加1,数据 0-63 慢一拍写入 FIFO 中,同时写入的数据量计数,并指明指令有效信号和指令内容。

写指令 FIFO 模块

有用户端口传来的命令使能信号,当指令 FIFO 的空信号拉低时,表示有指令内容存入,给到仲裁模块输出写开始信号,并且指令会传递下去。可以看到首次写的地址从0开始,而下一次写的初始地址则为512,因为突发长度为64,每个地址16位,每次写入的数据为128,因此每次数据占据8个地址,突发长度64所以一共需要占据8*64=512位,也就是0-511,因此下一次写操作的初始地址为512。

写数据 FIFO 模块

首先将数据写入 FIFO 中,然后当数据请求来临时,将数据从 FIFO 中读出,当全部数据读出后,FIFO 空信号拉高,由于数据太多不好展开,和前面的读写 FIFO 仿真一样。

DDR 写控制模块

开始写信号拉高后,依次将数据写入 DDR3 中,并含有命令握手信号和写操作握手信号,当完成写操作后将写完成信号拉高。

DDR 写控制模块

初始化完成信号拉高,可以进行读写操作;读开始信号拉高,若干个时钟周期后数据读出以及同步的有效信号,由于速率比为4:1,采用的8突发模式,因此读最后一个数据标志信号和读出数据有效信号一致;剩余为命令握手信号以及读指令相关信号,当命令握手有效表示指令有效。

读指令 FIFO 模块

读使能信号拉高以及同步的指令内容,当指令 FIFO 被读空后,发起下一次的读指令开始信号。可以看到首次读的地址从0开始,而下一次读的初始地址则为512,因为突发长度为64,每个地址16位,每次读出的数据为128,因此每次数据占据8个地址,突发长度64所以一共需要占据8*64=512位,也就是0-511,因此下一次读操作的初始地址为512。

读数据 FIFO 模块

这个时序比较简单,读出的数据写入 FIFO,当写入的数据达到设定值时,开始将数据读出,注意利用 FIFO 自带的计数器不是绝对准确的,最初设计就是因为计数器不准的原因导致仿真出现错误,因此在设定读出阈值时,尽量比预期的小一些,我这里设定的是比突发长度小6,当然小十几也是可以,只是别卡在边界值,这样容易出错!

用户读模块

开始读信号拉高,用户端从 FIFO 中读出数据,这里没有设计读出数据信号,但是从读数据使能和读数据计数信号来看也是可以证明设计无误,当将数据全部读出后,拉高读完成信号。

仲裁模块

仲裁模块逻辑设计不复杂,优先写操作,写完成开始读,读完成后开始写,循环往复,井然有序,在仲裁模块那篇有详细描述仲裁的功能设计。

至此,完成了 DDR MIG 控制器外围用户接口的设计,通过此设计可以使得用户更加方便的对 DDR 进行读写操作!


汇总篇

本系列为 DDR3 控制器设计总结,此系列包含 DDR3 控制器相关设计:认识 MIG、初始化、读写操作、FIFO 接口等。通过此系列的学习可以加深对 DDR3 读写时序的理解以及 FIFO 接口设计等,附上汇总博客直达链接。

【DDR3 控制器设计】系列博客汇总篇(附直达链接)


转载:https://blog.csdn.net/m0_61298445/article/details/127954656
查看评论
* 以上用户言论只代表其个人观点,不代表本网站的观点或立场