当前位置:   article > 正文

FPGA实现与资源消耗-CDC(clock domain crossing 跨时钟域)一_fpga cdc

fpga cdc

总结:本节重点讨论解决跨时钟域问题,在本节设计实现了异步fifo,在设计实现时不同时钟对同一个寄存器进行了修改,激励仿真通过,最后布局布线不通过。

1、定义

跨时钟域问题是在一个数字系统中使用多个时钟域(时钟信号)时可能面临的挑战。当不同的时钟域存在时,由于时钟的相位差异、频率不同或者时钟边沿的不同步等原因,可能会导致数据在时钟域之间的传输出现问题。

2、代码描述

简单定义一个输入时钟和一个输出时钟模块

  1. `timescale 1ns / 1ps
  2. module CDC(
  3. input clk1,
  4. input clk2,
  5. input rst,
  6. input [7:0] data,
  7. output [7:0] out
  8. );
  9. reg [7:0] reg1;
  10. reg [7:0] reg2;
  11. always @(posedge clk1 or posedge rst)begin
  12. if(rst)
  13. reg1 <= 0;
  14. else
  15. reg1 <= data;
  16. end
  17. always @(posedge clk2 or posedge rst)begin
  18. if(rst)
  19. reg2 <= 0;
  20. else
  21. reg2 <= reg1;
  22. end
  23. assign out = reg2;
  24. endmodule

波形展现

  1. module CDC_tb(
  2. );
  3. // Inputs
  4. reg clk1;
  5. reg clk2;
  6. reg rst;
  7. reg [7:0] data;
  8. // Outputs
  9. wire [7:0] out;
  10. // Instantiate the module
  11. CDC uut (
  12. .clk1(clk1),
  13. .clk2(clk2),
  14. .rst(rst),
  15. .data(data),
  16. .out(out)
  17. );
  18. // Clock generation
  19. initial begin
  20. clk1 = 0;
  21. clk2 = 0;
  22. end
  23. always #5 clk1 = ~clk1;
  24. always #3 clk2 = ~clk2;
  25. // Stimulus
  26. initial begin
  27. // Test case 1
  28. rst = 1;
  29. data = 8'b00000000;
  30. #6 rst = 0;
  31. // Test case 2
  32. data = 8'b10101010;
  33. #12;
  34. // Test case 3
  35. data = 8'b11001100;
  36. #18;
  37. // Test case 4
  38. data = 8'b11001111;
  39. #24;
  40. // Test case 5
  41. data = 8'b10101010;
  42. #30;
  43. // Test case 6
  44. data = 8'b11001111;
  45. $finish;
  46. end
  47. endmodule

可以看到的输入时钟频率慢(10ns),输出时钟频率快(6ns),最终得到的数据不稳定,没有按照输出时钟周期固定输出。其中输出第一个aa值时在一个周期内输出,而cc则在4个始终周期内输出,这样不确定始终周期的读数会导致最终结果的异常,我们希望的是一个时钟周期输出一个值。

3、使用异步FIFO解决

设计实现一个简单的FIFO,输入与输出时钟不同,包括简单的FIFO空满验证,空满验证的方式采用计数器,即,计数器等于0为空,计数器为fifo深度为满。

  1. `timescale 1ns / 1ps
  2. module Asyn_fifo(
  3. input clk1,
  4. input clk2,
  5. input rst,
  6. input [7:0] data,
  7. output [7:0] out
  8. );
  9. reg [7:0] fifo [0:15];
  10. reg [3:0] read_ptr, write_ptr;
  11. reg [3:0] count;
  12. reg [7:0] out_reg;
  13. always @(posedge clk1 or posedge rst) begin
  14. if(rst) begin
  15. read_ptr <= 4'b0;
  16. write_ptr <= 4'b0;
  17. count <= 4'b0;
  18. end
  19. else if(count < 16) begin
  20. fifo[read_ptr] <= data;
  21. read_ptr <= read_ptr + 1;
  22. count <= count + 1;
  23. end
  24. else begin
  25. read_ptr <= read_ptr;
  26. count <= count;
  27. end
  28. end
  29. always @(posedge clk2 or posedge rst)begin
  30. if(rst) begin
  31. read_ptr <= 4'b0;
  32. write_ptr <= 4'b0;
  33. count <= 4'b0;
  34. end
  35. else if(count > 0) begin
  36. out_reg <= fifo[write_ptr] ;
  37. write_ptr <= write_ptr + 1;
  38. count <= count - 1;
  39. end
  40. else begin
  41. write_ptr <= write_ptr;
  42. count <= count;
  43. end
  44. end
  45. assign out = out_reg;
  46. endmodule

在上面设计中出现了问题,问题为在两个不同时钟中对同一个寄存器进行了写,其中的count用于判断当前fifo的空满,所以在两个模块中都进行了修改,在读模块(输入数据)中进行加,写模块(输出数据)中进行减,最终testbench是正常的,但是布局布线是异常的,这是由于最终实现过程count由触发器实现,触发器是边沿触发,只能在一个时钟边沿触发,在综合时会查看到两个count寄存器,寄存器中包含触发器。

测试代码如下

  1. `timescale 1ns / 1ps
  2. module Asyn_fifo_tb(
  3. );
  4. // Inputs
  5. reg clk1;
  6. reg clk2;
  7. reg rst;
  8. reg [7:0] data;
  9. // Outputs
  10. wire [7:0] out;
  11. // Instantiate the module
  12. Asyn_fifo uut (
  13. .clk1(clk1),
  14. .clk2(clk2),
  15. .rst(rst),
  16. .data(data),
  17. .out(out)
  18. );
  19. // Clock generation
  20. initial begin
  21. clk1 = 0;
  22. clk2 = 0;
  23. end
  24. always #5 clk1 = ~clk1;
  25. always #6 clk2 = ~clk2;
  26. // Stimulus
  27. initial begin
  28. // Test case 1
  29. rst = 1;
  30. data = 8'b00000000;
  31. #10
  32. rst = 0;
  33. // Test case 2
  34. data = 8'b10101010;
  35. #10;
  36. // Test case 3
  37. data = 8'b11001100;
  38. #10;
  39. // Test case 4
  40. data = 8'b11001111;
  41. #10;
  42. // Test case 5
  43. data = 8'b10101010;
  44. #10;
  45. // Test case 6
  46. data = 8'b11001111;
  47. # 100
  48. $finish;
  49. end
  50. endmodule


在测试代码中输入的时钟频率要比输出时钟频率更快,输入时钟周期为10ns,输出时钟周期为12ns,数据就会缓存在FIFO中,测试使用的FIFO的深度比较小,当一次传输的数据量大时,要么采用更深的FIFO,要么就采用握手,当FIFO满时,就要限制输入数据。

测试波形

可以看到输出数据是比较稳定与正常的。

4、RTL实现

RTL实现的比较容易理解,reg1模块按照clk1存储输入数据,reg2模块按照clk2从右边

5、综合

综合存在问题,原本的一个count最终被综合为两个触发器,与原本设计是不相符合的。

6、实现

实现失败

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

闽ICP备14008679号