当前位置:   article > 正文

[FPGA系列] “乒乓操作”实战总结_fpga ddr3乒乓操作

fpga ddr3乒乓操作

一、基本概念

        乒乓操作FPGA开发中的一种数据缓冲优化设计技术,可以看成是另一种形式的流水线技术,具有节约缓冲空间、对数据流无缝处理等优点,其操作原理如图所示。

        在输入数据流到达时,输入数据流选择单元对其流向进行控制,其执行流程:

在第一个缓冲周期,输入数据流写入数据缓冲模块1,写完之后进入第二个缓冲周期。

在第二个缓冲周期,输入数据流写入数据缓冲模块2,同时将数据缓冲模块1中的数据读出。

在第三个缓冲周期,输入数据流写入数据缓冲模块1,同时将数据缓冲模块2中的数据读出。

        如此反复循环地操作,即为乒乓操作。

        乒乓操作特点:实现跨时钟域的数据传输,其基本原理:

        输入数据流的 面积 × 速度 = 输出数据流的 面积 × 速度

        面积:即数据传输线的位宽,bit。

        速度:即数据传输的时钟频率,hz。

        例:输入数据流为 50Mhz × 8bit,则输出数据流为 25Mhz × 16bit,这样就实现了跨时钟域的数据传输。

 二、题目要求

        使用数据产生模块输出 50Mhz × 8bit 的数据 8'b0 ~ 8'b199,通过乒乓操作读取数据,对其缓存并输出为 25Mhz × 16bit 的数据 16'h0100、16'h0302 ... 16'h6362 ... 16'hc7c6、16'h0100...

三、思路整理

        将其划分为四个模块,如图所示。

        模块框图如图所示。 

        执行流程:

data_gen输出数据 0 ~ 199(50Mhz × 8bit) 。

在第一个缓冲周期,ram_ctrl 将数据流接入 ram1,ram1存入 0 ~ 99。

在第二个缓冲周期,ram_ctrl 将数据流接入 ram2,ram2存入 100 ~ 199,同时 ram_ctrl 将ram1中的数据输出,即16'h0100、16'h0302 ... 16'h6362(10'd99 10'd98)。

在第三个缓冲周期,ram_ctrl 将数据流接入 ram1,ram1存入 0 ~ 99,同时 ram_ctrl 将ram2中的数据输出,即16'h6463、16'h6665 ... 16'hc7c6(10'd199 10'd198)。

        用状态机来实现这个功能。

四、Verilog实战

 1、data_gen模块

  1. module data_gen
  2. (
  3. input wire clk , //50MHZ
  4. input wire rst ,
  5. output wire data_en ,
  6. output reg [7:0] data
  7. );
  8. always@(posedge clk or negedge rst)
  9. if(!rst)
  10. data <= 8'b0;
  11. else if(data == 'd199)
  12. data <= 8'b0;
  13. else
  14. data <= data + 1'b1;
  15. assign data_en = (rst == 1'b1);
  16. endmodule

 2、ram_ctrl模块

  1. module ram_ctrl
  2. (
  3. input wire clk_25m ,
  4. input wire clk_50m ,
  5. input wire rst ,
  6. input wire [15:0] ram1_data ,
  7. input wire [15:0] ram2_data ,
  8. input wire data_en ,
  9. input wire [7:0] data_in ,
  10. output wire ram1_wr_en ,
  11. output reg [6:0] ram1_wr_addr ,
  12. output wire [7:0] ram1_wr_data ,
  13. output wire ram1_rd_en ,
  14. output reg [5:0] ram1_rd_addr ,
  15. output wire ram2_wr_en ,
  16. output reg [6:0] ram2_wr_addr ,
  17. output wire [7:0] ram2_wr_data ,
  18. output wire ram2_rd_en ,
  19. output reg [5:0] ram2_rd_addr ,
  20. output reg [7:0] data_in_reg ,
  21. output wire [15:0] data_out
  22. );
  23. parameter IDLE = 4'b0001,
  24. WRAM1 = 4'b0010,
  25. R1_W2 = 4'b0100,
  26. W1_R2 = 4'b1000;
  27. reg [3:0] state,next_state;
  28. //data_in_reg:读取数据并打拍
  29. always@(posedge clk_50m or negedge rst)
  30. if(!rst)
  31. data_in_reg <= 8'b0;
  32. else if(data_en == 1'b1)
  33. data_in_reg <= data_in;
  34. //state:现态转移
  35. always@(posedge clk_50m or negedge rst)
  36. if(!rst)
  37. state <= IDLE;
  38. else
  39. state <= next_state;
  40. //next_state:次态改变
  41. always@(*)
  42. case(state)
  43. IDLE : next_state = WRAM1;
  44. WRAM1 : next_state = (data_in_reg == 'd99 )?R1_W2:WRAM1;
  45. R1_W2 : next_state = (data_in_reg == 'd199)?W1_R2:R1_W2;
  46. W1_R2 : next_state = (data_in_reg == 'd99 )?R1_W2:W1_R2;
  47. default : next_state = IDLE;
  48. endcase
  49. //ram1、ram2读写使能
  50. assign ram1_wr_en = (state == WRAM1 || state == W1_R2);
  51. assign ram1_rd_en = (next_state == R1_W2 || state == R1_W2);
  52. assign ram2_wr_en = (state == R1_W2);
  53. assign ram2_rd_en = (next_state == W1_R2 || state == W1_R2);
  54. //ram1_wr_addr,ram2_wr_addr:写地址计数
  55. always@(posedge clk_50m or negedge rst)
  56. if(!rst)
  57. begin
  58. ram1_wr_addr <= 7'b0;
  59. ram2_wr_addr <= 7'b0;
  60. end
  61. else if(ram1_wr_addr == 'd99 || ram2_wr_addr == 'd99)
  62. begin
  63. ram1_wr_addr <= 7'b0;
  64. ram2_wr_addr <= 7'b0;
  65. end
  66. else
  67. case(state)
  68. WRAM1 : ram1_wr_addr <= ram1_wr_addr + 1'b1;
  69. R1_W2 : ram2_wr_addr <= ram2_wr_addr + 1'b1;
  70. W1_R2 : ram1_wr_addr <= ram1_wr_addr + 1'b1;
  71. default :
  72. begin
  73. ram1_wr_addr <= 7'b0;
  74. ram2_wr_addr <= 7'b0;
  75. end
  76. endcase
  77. //ram1_rd_addr,ram2_rd_addr:读地址计数
  78. always@(posedge clk_25m or negedge rst)
  79. if(!rst)
  80. begin
  81. ram1_rd_addr <= 6'b0;
  82. ram2_rd_addr <= 6'b0;
  83. end
  84. else if(ram1_rd_addr == 'd49 || ram2_rd_addr == 'd49)
  85. begin
  86. ram1_rd_addr <= 6'b0;
  87. ram2_rd_addr <= 6'b0;
  88. end
  89. else
  90. case(state)
  91. R1_W2 : ram1_rd_addr <= ram1_rd_addr + 1'b1;
  92. W1_R2 : ram2_rd_addr <= ram2_rd_addr + 1'b1;
  93. default :
  94. begin
  95. ram1_rd_addr <= 6'b0;
  96. ram2_rd_addr <= 6'b0;
  97. end
  98. endcase
  99. //ram1、ram2输入数据选择
  100. assign ram1_wr_data = (state == WRAM1 || state == W1_R2) ? data_in_reg:8'b0;
  101. assign ram2_wr_data = (state == R1_W2) ? data_in_reg:8'b0;
  102. //ram1、ram2输出数据选择
  103. assign data_out = (state == R1_W2) ? ram1_data:((state == W1_R2) ? ram2_data:16'b0);
  104. endmodule

3、pingpang模块

  1. module pingpang
  2. (
  3. input wire clk ,
  4. input wire rst ,
  5. output wire [15:0] data_out
  6. );
  7. wire clk_25m ;
  8. wire clk_50m ;
  9. wire locked ;
  10. wire rst_n ;
  11. wire [15:0] ram1_data ;
  12. wire [15:0] ram2_data ;
  13. wire data_en ;
  14. wire [7:0] data_in ;
  15. wire ram1_wr_en ;
  16. wire [6:0] ram1_wr_addr ;
  17. wire [7:0] ram1_wr_data ;
  18. wire ram1_rd_en ;
  19. wire [5:0] ram1_rd_addr ;
  20. wire ram2_wr_en ;
  21. wire [6:0] ram2_wr_addr ;
  22. wire [7:0] ram2_wr_data ;
  23. wire ram2_rd_en ;
  24. wire [5:0] ram2_rd_addr ;
  25. wire [7:0] data_in_reg ;
  26. assign rst_n = locked & rst;
  27. clk_gen clk_gen_inst
  28. (
  29. .areset (~rst ),
  30. .inclk0 (clk ),
  31. .c0 (clk_50m ),
  32. .c1 (clk_25m ),
  33. .locked (locked )
  34. );
  35. data_gen data_gen_inst
  36. (
  37. .clk (clk_50m ), //50MHZ
  38. .rst (rst_n ),
  39. .data_en (data_en ),
  40. .data (data_in )
  41. );
  42. ram_ctrl ram_ctrl_inst
  43. (
  44. .clk_25m (clk_25m ),
  45. .clk_50m (clk_50m ),
  46. .rst (rst_n ),
  47. .ram1_data (ram1_data ),
  48. .ram2_data (ram2_data ),
  49. .data_en (data_en ),
  50. .data_in (data_in ),
  51. .ram1_wr_en (ram1_wr_en ),
  52. .ram1_wr_addr (ram1_wr_addr ),
  53. .ram1_wr_data (ram1_wr_data ),
  54. .ram1_rd_en (ram1_rd_en ),
  55. .ram1_rd_addr (ram1_rd_addr ),
  56. .ram2_wr_en (ram2_wr_en ),
  57. .ram2_wr_addr (ram2_wr_addr ),
  58. .ram2_wr_data (ram2_wr_data ),
  59. .ram2_rd_en (ram2_rd_en ),
  60. .ram2_rd_addr (ram2_rd_addr ),
  61. .data_in_reg (data_in_reg ),
  62. .data_out (data_out )
  63. );
  64. ram ram1_inst
  65. (
  66. .data (ram1_wr_data ),
  67. .rdaddress (ram1_rd_addr ),
  68. .rdclock (~clk_25m ),
  69. .rden (ram1_rd_en ),
  70. .wraddress (ram1_wr_addr ),
  71. .wrclock (~clk_50m ),
  72. .wren (ram1_wr_en ),
  73. .q (ram1_data )
  74. );
  75. ram ram2_inst
  76. (
  77. .data (ram2_wr_data ),
  78. .rdaddress (ram2_rd_addr ),
  79. .rdclock (~clk_25m ),
  80. .rden (ram2_rd_en ),
  81. .wraddress (ram2_wr_addr ),
  82. .wrclock (~clk_50m ),
  83. .wren (ram2_wr_en ),
  84. .q (ram2_data )
  85. );
  86. endmodule

五、Modelsim仿真

        可以看到数据流实现了无缝衔接,达到预期的效果,实验成功。

 

参考资料:野火《FPGA Verilog开发实战指南——基于Altera EP4CE10》

声明:本文内容由网友自发贡献,转载请注明出处:【wpsshop博客】
推荐阅读
相关标签
  

闽ICP备14008679号