当前位置:   article > 正文

多比特跨时钟域之异步FIFO_fifoip核作为跨时钟域示例代码

fifoip核作为跨时钟域示例代码

一、异步FIFO结构图

 具体代码直接按照结构图来写,分为读控制模块,写控制模块,同步模块,及顶层模块。

二、各模块代码

1、写控制模块

  1. module wr_ctrl#(
  2. parameter DEPTH = 16,
  3. parameter ADDR_WIDTH = $clog2(DEPTH)
  4. )(
  5. input wire wr_clk,
  6. input wire wr_rst_n,
  7. input wire [ADDR_WIDTH : 0] rd_addr_gray,
  8. input wire wr_en,
  9. output wire [ADDR_WIDTH : 0] wr_addr_p_gray,
  10. output wire [ADDR_WIDTH - 1 : 0] wr_addr,
  11. output wire wr_full
  12. );
  13. reg [ADDR_WIDTH : 0] wr_addr_p;
  14. reg [ADDR_WIDTH : 0] rd_addr_p;
  15. always@(posedge wr_clk or negedge wr_rst_n)begin
  16. if(!wr_rst_n)
  17. wr_addr_p <= {ADDR_WIDTH+1{1'b0}};
  18. else if(wr_en && (~wr_full))
  19. wr_addr_p <= wr_addr_p + 1'b1;
  20. end
  21. assign wr_addr = wr_addr_p[ADDR_WIDTH - 1 : 0];
  22. assign wr_addr_p_gray = {wr_addr_p[ADDR_WIDTH],wr_addr_p[ADDR_WIDTH:1] ^ wr_addr_p[ADDR_WIDTH-1:0]};
  23. integer i ;
  24. always@(*)begin
  25. rd_addr_p[ADDR_WIDTH] = rd_addr_gray[ADDR_WIDTH];
  26. for(i = ADDR_WIDTH - 1 ; i >= 0 ; i = i - 1)
  27. rd_addr_p[i] = rd_addr_p[i+1] ^ rd_addr_gray[i];
  28. end
  29. assign wr_full = (wr_addr_p[ADDR_WIDTH] != rd_addr_p[ADDR_WIDTH]) && (wr_addr_p[ADDR_WIDTH-1:0] == rd_addr_p[ADDR_WIDTH-1:0]);
  30. endmodule

写控制模块作用包括:产生写地址指针,写地址,将读时钟域的格雷码转换,产生写满标志位。

这里我格雷码转二进制用了for循环,因为考虑到模块复用的情况,地址宽度是变化的,我没有想到更好的方法。下面读控制同理。(有好的方法欢迎提出)

2、读控制模块

  1. module rd_ctrl#(
  2. parameter DEPTH = 16,
  3. parameter ADDR_WIDTH = $clog2(DEPTH)
  4. )(
  5. input wire rd_clk,
  6. input wire rd_rst_n,
  7. input wire rd_en,
  8. input wire [ADDR_WIDTH:0] wr_addr_gray,
  9. output wire rd_empty,
  10. output wire [ADDR_WIDTH-1:0] rd_addr,
  11. output wire [ADDR_WIDTH:0] rd_addr_p_gray
  12. );
  13. reg [ADDR_WIDTH:0] rd_addr_p;
  14. reg [ADDR_WIDTH:0] wr_addr_p;
  15. always@(posedge rd_clk or negedge rd_rst_n)begin
  16. if(!rd_rst_n)
  17. rd_addr_p <= {ADDR_WIDTH+1{1'b0}};
  18. else if(rd_en && (~rd_empty))
  19. rd_addr_p <= rd_addr_p + 1'b1;
  20. end
  21. assign rd_addr = rd_addr_p[ADDR_WIDTH-1:0];
  22. assign rd_addr_p_gray = {rd_addr_p[ADDR_WIDTH],rd_addr_p[ADDR_WIDTH:1] ^ rd_addr_p[ADDR_WIDTH-1:0]};
  23. integer j ;
  24. always@(*)begin
  25. wr_addr_p[ADDR_WIDTH] = wr_addr_gray[ADDR_WIDTH];
  26. for(j = ADDR_WIDTH - 1 ; j >= 0 ; j = j - 1)
  27. wr_addr_p[j] = wr_addr_p[j+1] ^ wr_addr_gray[j];
  28. end
  29. assign rd_empty = wr_addr_p == rd_addr_p;
  30. endmodule

读控制模块作用包括:产生读地址指针,读地址,将写时钟域的格雷码转换,产生读空标志位。

3、同步模块

  1. module sync2#(
  2. parameter DEPTH = 16,
  3. parameter ADDR_WIDTH = $clog2(DEPTH)
  4. )(
  5. input wire clk,
  6. input wire rst_n,
  7. input wire [ADDR_WIDTH : 0] addr_in,
  8. output wire [ADDR_WIDTH : 0] addr_out
  9. );
  10. reg [ADDR_WIDTH : 0] addr_d1;
  11. reg [ADDR_WIDTH : 0] addr_d2;
  12. always@(posedge clk or negedge rst_n)begin
  13. if(!rst_n)begin
  14. addr_d1 <= 0;
  15. addr_d2 <= 0;
  16. end
  17. else begin
  18. addr_d1 <= addr_in;
  19. addr_d2 <= addr_d1;
  20. end
  21. end
  22. assign addr_out = addr_d2;
  23. endmodule

同步模块作用:格雷码传输,相邻格雷码间只有1位变化,使用两级同步器。

4、顶层模块

  1. module async_fifo
  2. #(
  3. parameter DEPTH = 16,
  4. parameter WIDTH = 8
  5. )(
  6. input wire wr_clk,
  7. input wire wr_req,
  8. input wire wr_rst_n,
  9. input wire [WIDTH - 1:0] data_in,
  10. output wire wr_full,
  11. input wire rd_clk,
  12. input wire rd_req,
  13. input wire rd_rst_n,
  14. output wire rd_empty,
  15. output reg [WIDTH - 1:0] data_out
  16. );
  17. parameter ADDR_WIDTH = $clog2(DEPTH);
  18. reg [WIDTH - 1 : 0] regs_array [DEPTH - 1 : 0];
  19. wire [ADDR_WIDTH : 0] wptr,rptr;
  20. wire [ADDR_WIDTH : 0] wptr_d2,rptr_d2;
  21. wire [ADDR_WIDTH - 1 : 0] wr_addr,rd_addr;
  22. always@(posedge wr_clk)begin
  23. if(wr_req && (!wr_full))
  24. regs_array[wr_addr] <= data_in;
  25. end
  26. always@(posedge rd_clk)begin
  27. if(rd_req && (!rd_empty))
  28. data_out <= regs_array[rd_addr];
  29. end
  30. wr_ctrl #(
  31. .DEPTH (16)
  32. )wr_ctrl_inst(
  33. .wr_clk (wr_clk),
  34. .wr_rst_n (wr_rst_n),
  35. .wr_en (wr_req),
  36. .rd_addr_gray (rptr_d2),
  37. .wr_addr (wr_addr),
  38. .wr_addr_p_gray (wptr),
  39. .wr_full (wr_full)
  40. );
  41. rd_ctrl #(
  42. .DEPTH (16)
  43. )rd_ctrl_inst(
  44. .rd_clk (rd_clk),
  45. .rd_rst_n (rd_rst_n),
  46. .rd_en (rd_req),
  47. .wr_addr_gray (wptr_d2),
  48. .rd_addr (rd_addr),
  49. .rd_addr_p_gray (rptr),
  50. .rd_empty (rd_empty)
  51. );
  52. sync2 #(
  53. .DEPTH (16)
  54. )sync2_inst1(
  55. .clk (wr_clk),
  56. .rst_n (wr_rst_n),
  57. .addr_in (rptr),
  58. .addr_out (rptr_d2)
  59. );
  60. sync2#(
  61. .DEPTH (16)
  62. )sync2_inst2(
  63. .clk (rd_clk),
  64. .rst_n (rd_rst_n),
  65. .addr_in (wptr),
  66. .addr_out (wptr_d2)
  67. );
  68. endmodule

顶层模块作用包括:例外各个子模块,定义存储器变量(图中双口ram),读写数据控制。

三、testbench

  1. `timescale 1ns/1ns;
  2. module async_fifo_tb();
  3. parameter WIDTH = 8;
  4. reg wr_clk;
  5. reg wr_req;
  6. reg wr_rst_n;
  7. reg [WIDTH - 1:0] data_in;
  8. reg rd_clk;
  9. reg rd_req;
  10. reg rd_rst_n;
  11. wire wr_full;
  12. wire rd_empty;
  13. wire [WIDTH - 1:0] data_out;
  14. reg init_done;
  15. always #2 wr_clk = ~wr_clk;
  16. always #4 rd_clk = ~rd_clk;
  17. initial begin
  18. wr_rst_n = 0;
  19. rd_rst_n = 0;
  20. wr_clk = 0;
  21. rd_clk = 0;
  22. wr_req = 0;
  23. rd_req = 0;
  24. data_in = 0;
  25. init_done = 0;
  26. #25;
  27. wr_rst_n = 1;
  28. rd_rst_n = 1;
  29. init_done = 1;
  30. #10000
  31. $finish;
  32. end
  33. always@(*)begin
  34. if(init_done)begin
  35. if(wr_full)
  36. wr_req = 0;
  37. else
  38. wr_req = 1;
  39. end
  40. end
  41. always@(*)begin
  42. if(init_done)begin
  43. if(rd_empty)
  44. rd_req = 0;
  45. else
  46. rd_req = 1;
  47. end
  48. end
  49. always@(posedge wr_clk)begin
  50. if(init_done)begin
  51. if(!wr_full)
  52. data_in <= data_in + 1'b1;
  53. else
  54. data_in <= data_in;
  55. end
  56. else
  57. data_in <= 0;
  58. end
  59. async_fifo #(
  60. .DEPTH (16),
  61. .WIDTH (8)
  62. )async_fifo_inst(
  63. .wr_clk (wr_clk),
  64. .wr_req (wr_req),
  65. .wr_rst_n (wr_rst_n),
  66. .data_in (data_in),
  67. .wr_full (wr_full),
  68. .rd_clk (rd_clk),
  69. .rd_req (rd_req),
  70. .rd_rst_n (rd_rst_n),
  71. .rd_empty (rd_empty),
  72. .data_out (data_out)
  73. );
  74. initial begin
  75. $fsdbDumpfile("tb.fsdb");
  76. $fsdbDumpvars;
  77. end
  78. endmodule

testbench作用为:给激励,产生读写数据,控制读写请求,例化顶层模块。

四、仿真结果

仿真了边读边写的情况,可以看到写满拉高,读写时钟域分开,方便观察中间信号。

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

闽ICP备14008679号