赞
踩
总结:本节重点讨论解决跨时钟域问题,在本节设计实现了异步fifo,在设计实现时不同时钟对同一个寄存器进行了修改,激励仿真通过,最后布局布线不通过。
跨时钟域问题是在一个数字系统中使用多个时钟域(时钟信号)时可能面临的挑战。当不同的时钟域存在时,由于时钟的相位差异、频率不同或者时钟边沿的不同步等原因,可能会导致数据在时钟域之间的传输出现问题。
简单定义一个输入时钟和一个输出时钟模块
- `timescale 1ns / 1ps
- module CDC(
- input clk1,
- input clk2,
- input rst,
- input [7:0] data,
- output [7:0] out
- );
- reg [7:0] reg1;
- reg [7:0] reg2;
- always @(posedge clk1 or posedge rst)begin
- if(rst)
- reg1 <= 0;
- else
- reg1 <= data;
- end
-
- always @(posedge clk2 or posedge rst)begin
- if(rst)
- reg2 <= 0;
- else
- reg2 <= reg1;
- end
-
- assign out = reg2;
-
- endmodule
波形展现
- module CDC_tb(
-
- );
- // Inputs
- reg clk1;
- reg clk2;
- reg rst;
- reg [7:0] data;
-
- // Outputs
- wire [7:0] out;
-
- // Instantiate the module
- CDC uut (
- .clk1(clk1),
- .clk2(clk2),
- .rst(rst),
- .data(data),
- .out(out)
- );
-
- // Clock generation
- initial begin
- clk1 = 0;
- clk2 = 0;
- end
- always #5 clk1 = ~clk1;
- always #3 clk2 = ~clk2;
-
- // Stimulus
- initial begin
- // Test case 1
- rst = 1;
- data = 8'b00000000;
- #6 rst = 0;
- // Test case 2
- data = 8'b10101010;
- #12;
-
- // Test case 3
- data = 8'b11001100;
- #18;
- // Test case 4
- data = 8'b11001111;
-
-
- #24;
-
- // Test case 5
- data = 8'b10101010;
- #30;
- // Test case 6
- data = 8'b11001111;
-
- $finish;
- end
-
- endmodule
可以看到的输入时钟频率慢(10ns),输出时钟频率快(6ns),最终得到的数据不稳定,没有按照输出时钟周期固定输出。其中输出第一个aa值时在一个周期内输出,而cc则在4个始终周期内输出,这样不确定始终周期的读数会导致最终结果的异常,我们希望的是一个时钟周期输出一个值。
设计实现一个简单的FIFO,输入与输出时钟不同,包括简单的FIFO空满验证,空满验证的方式采用计数器,即,计数器等于0为空,计数器为fifo深度为满。
- `timescale 1ns / 1ps
-
- module Asyn_fifo(
- input clk1,
- input clk2,
- input rst,
- input [7:0] data,
- output [7:0] out
- );
- reg [7:0] fifo [0:15];
- reg [3:0] read_ptr, write_ptr;
- reg [3:0] count;
- reg [7:0] out_reg;
-
- always @(posedge clk1 or posedge rst) begin
- if(rst) begin
- read_ptr <= 4'b0;
- write_ptr <= 4'b0;
- count <= 4'b0;
- end
- else if(count < 16) begin
- fifo[read_ptr] <= data;
- read_ptr <= read_ptr + 1;
- count <= count + 1;
- end
- else begin
- read_ptr <= read_ptr;
- count <= count;
- end
- end
- always @(posedge clk2 or posedge rst)begin
- if(rst) begin
- read_ptr <= 4'b0;
- write_ptr <= 4'b0;
- count <= 4'b0;
- end
- else if(count > 0) begin
- out_reg <= fifo[write_ptr] ;
- write_ptr <= write_ptr + 1;
- count <= count - 1;
- end
- else begin
- write_ptr <= write_ptr;
- count <= count;
- end
- end
- assign out = out_reg;
- endmodule
在上面设计中出现了问题,问题为在两个不同时钟中对同一个寄存器进行了写,其中的count用于判断当前fifo的空满,所以在两个模块中都进行了修改,在读模块(输入数据)中进行加,写模块(输出数据)中进行减,最终testbench是正常的,但是布局布线是异常的,这是由于最终实现过程count由触发器实现,触发器是边沿触发,只能在一个时钟边沿触发,在综合时会查看到两个count寄存器,寄存器中包含触发器。
测试代码如下
- `timescale 1ns / 1ps
-
- module Asyn_fifo_tb(
-
- );
- // Inputs
- reg clk1;
- reg clk2;
- reg rst;
- reg [7:0] data;
-
- // Outputs
- wire [7:0] out;
-
- // Instantiate the module
- Asyn_fifo uut (
- .clk1(clk1),
- .clk2(clk2),
- .rst(rst),
- .data(data),
- .out(out)
- );
-
- // Clock generation
- initial begin
- clk1 = 0;
- clk2 = 0;
- end
- always #5 clk1 = ~clk1;
- always #6 clk2 = ~clk2;
-
- // Stimulus
- initial begin
- // Test case 1
- rst = 1;
- data = 8'b00000000;
- #10
- rst = 0;
- // Test case 2
- data = 8'b10101010;
- #10;
-
- // Test case 3
- data = 8'b11001100;
- #10;
- // Test case 4
- data = 8'b11001111;
-
-
- #10;
-
- // Test case 5
- data = 8'b10101010;
- #10;
- // Test case 6
- data = 8'b11001111;
- # 100
- $finish;
- end
-
- endmodule
在测试代码中输入的时钟频率要比输出时钟频率更快,输入时钟周期为10ns,输出时钟周期为12ns,数据就会缓存在FIFO中,测试使用的FIFO的深度比较小,当一次传输的数据量大时,要么采用更深的FIFO,要么就采用握手,当FIFO满时,就要限制输入数据。
测试波形
可以看到输出数据是比较稳定与正常的。
RTL实现的比较容易理解,reg1模块按照clk1存储输入数据,reg2模块按照clk2从右边
综合存在问题,原本的一个count最终被综合为两个触发器,与原本设计是不相符合的。
实现失败
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。