赞
踩
在大规模ASIC或FPGA设计中,多时钟系统往往是不可避免的,这样就产生了不同时钟域数据传输的问题,其中一个比较好的解决方案就是使用异步FIFO来作不同时钟域数据传输的缓冲区,这样既可以使相异时钟域数据传输的时序要求变得宽松,也提高了它们之间的传输效率。
FIFO在硬件上是一种地址依次自增的Simple Dual Port RAM,按读数据和写数据工作的时钟域是否相同分为同步FIFO和异步FIFO,其中同步FIFO是指读时钟和写时钟为同步时钟,常用于数据缓存和数据位宽转换;异步FIFO通常情况下是指读时钟和写时钟频率有差异,即由两个异步时钟驱动的FIFO,由于读写操作是独立的,故常用于多比特数据跨时钟域处理。
异步FIFO是通过比较读指针和写指针的位置来判断FIFO是否写满或读空,但是不可以直接比较两个指针,因为他们属于不同时钟域,直接相比可能会产生亚稳态从而引起误判,这就需要将两个指针分别进行跨时钟域处理,然后再判断。但是存在一个问题,自然二进制编码的地址在状态翻转的时候是多位变化,这就可能会产生竞争现象并有可能被另一个时钟域的触发器采样到,从而引发误判。最容易的解决方法就是将自然二进制编码的地址转为格雷码编码的地址。
从上图可以看出,格雷码如果每2^n个数一循环,首尾两个格雷码仍然是只有一位变化,如果不是2^n个数,那么首尾数据就不是仅有一位变化,那就不是真正的格雷码,所以这也是异步FIFO的存储深度只能是2^n的原因。
自然二进制码转换成二进制格雷码,其法则是保留自然二进制码的最高位作为格雷码的最高位,而次高位格雷码为二进制码的高位与次高位相异或,而格雷码其余各位与次高位的求法相类似。:
-
- module ASFIFO#
- (
- parameter WIDTH = 16, // FIFO数据总线位宽
- parameter PTR = 4 // FIFO存储深度(bit数,深度只能是2^n个)
- )
- (
- // write interface
- input wrclk , // 写时钟
- input wr_rst_n, // 写指针复位
- input [WIDTH-1:0] wr_data , // 写数据总线
- input wr_en , // 写使能
- output reg wr_full , // 写满标志
-
- //read interface
- input rdclk , // 读时钟
- input rd_rst_n, // 读指针复位
- input rd_en , // 读使能
- output [WIDTH-1:0] rd_data , // 读数据输出
- output reg rd_empty // 读空标志
- );
-
-
- // 写时钟域信号定义
- reg [PTR:0] wr_bin ; // 二进制写地址
- reg [PTR:0] wr_gray ; // 格雷码写地址
- reg [PTR:0] rd_gray_ff1 ; // 格雷码读地址同步寄存器1
- reg [PTR:0] rd_gray_ff2 ; // 格雷码读地址同步寄存器2
- reg [PTR:0] rd_bin_wr ; // 同步到写时钟域的二进制读地址
-
-
- // 读时钟域信号定义
- reg [PTR:0] rd_bin ; // 二进制读地址
- reg [PTR:0] rd_gray ; // 格雷码读地址
- reg [PTR:0] wr_gray_ff1 ; // 格雷码写地址同步寄存器1
- reg [PTR:0] wr_gray_ff2 ; // 格雷码写地址同步寄存器2
- reg [PTR:0] wr_bin_rd ; // 同步到读时钟域的二进制写地址
-
-
- // 解格雷码电路循环变量
- integer i ;
- integer j ;
-
-
- // DPRAM控制信号
- wire dpram_wr_en ; // DPRAM写使能
- wire [PTR-1:0] dpram_wr_addr ; // DPRAM写地址
- wire [WIDTH-1:0] dpram_wr_data ; // DPRAM写数据
- wire dpram_rd_en ; // DPRAM读使能
- wire [PTR-1:0] dpram_rd_addr ; // DPRAM读地址
- wire [WIDTH-1:0] dpram_rd_data ; // DPRAM读数据
-
-
-
- // ******************************** 写时钟域 ******************************** //
- // 二进制写地址递增
- always @(posedge wrclk or posedge wr_rst_n) begin
- if (!wr_rst_n) begin
- wr_bin <= 'b0;
- end
- else if ( wr_en == 1'b1 && wr_full == 1'b0 ) begin
- wr_bin <= wr_bin + 1'b1;
- end
- else begin
- wr_bin <= wr_bin;
- end
- end
-
-
- // 写地址:二进制转格雷码
- always @(posedge wrclk or posedge wr_rst_n) begin
- if (!wr_rst_n) begin
- wr_gray <= 'b0;
- end
- else begin
- wr_gray <= { wr_bin[PTR], wr_bin[PTR:1] ^ wr_bin[PTR-1:0] };
- end
- end
-
-
- // 格雷码读地址同步至写时钟域
- always @(posedge wrclk or posedge wr_rst_n) begin
- if(!wr_rst_n) begin
- rd_gray_ff1 <= 'b0;
- rd_gray_ff2 <= 'b0;
- end
- else begin
- rd_gray_ff1 <= rd_gray;
- rd_gray_ff2 <= rd_gray_ff1;
- end
- end
-
-
- // 同步后的读地址解格雷
- always @(*) begin
- rd_bin_wr[PTR] = rd_gray_ff2[PTR];
- for ( i=PTR-1; i>=0; i=i-1 )
- rd_bin_wr[i] = rd_bin_wr[i+1] ^ rd_gray_ff2[i];
- end
-
-
- // 写时钟域产生写满标志
- always @(*) begin
- if( (wr_bin[PTR] != rd_bin_wr[PTR]) && (wr_bin[PTR-1:0] == rd_bin_wr[PTR-1:0]) ) begin
- wr_full = 1'b1;
- end
- else begin
- wr_full = 1'b0;
- end
- end
-
-
- // ******************************** 读时钟域 ******************************** //
- always @(posedge rdclk or posedge rd_rst_n) begin
- if (!rd_rst_n) begin
- rd_bin <= 'b0;
- end
- else if ( rd_en == 1'b1 && rd_empty == 1'b0 ) begin
- rd_bin <= rd_bin + 1'b1;
- end
- else begin
- rd_bin <= rd_bin;
- end
- end
-
-
- // 读地址:二进制转格雷码
- always @(posedge rdclk or posedge rd_rst_n) begin
- if (!rd_rst_n) begin
- rd_gray <= 'b0;
- end
- else begin
- rd_gray <= { rd_bin[PTR], rd_bin[PTR:1] ^ rd_bin[PTR-1:0] };
- end
- end
-
-
- // 格雷码写地址同步至读时钟域
- always @(posedge rdclk or posedge rd_rst_n) begin
- if(!rd_rst_n) begin
- wr_gray_ff1 <= 'b0;
- wr_gray_ff2 <= 'b0;
- end
- else begin
- wr_gray_ff1 <= wr_gray;
- wr_gray_ff2 <= wr_gray_ff1;
- end
- end
-
-
- // 同步后的写地址解格雷
- always @(*) begin
- wr_bin_rd[PTR] = wr_gray_ff2[PTR];
- for ( j=PTR-1; j>=0; j=j-1 )
- wr_bin_rd[j] = wr_bin_rd[j+1] ^ wr_gray_ff2[j];
- end
-
-
- // 读时钟域产生读空标志
- always @(*) begin
- if( wr_bin_rd == rd_bin )
- rd_empty = 1'b1;
- else
- rd_empty = 1'b0;
- end
-
-
- // RTL双口RAM例化
- DPRAM
- # ( .WIDTH(16), .DEPTH(16), .ADDR(4) )
- U_DPRAM
- (
- .wrclk (wrclk ),
- .rdclk (rdclk ),
- .rd_rst_n (rd_rst_n ),
- .wr_en (dpram_wr_en ),
- .rd_en (dpram_rd_en ),
- .wr_data (dpram_wr_data ),
- .rd_data (dpram_rd_data ),
- .wr_addr (dpram_wr_addr ),
- .rd_addr (dpram_rd_addr )
- );
-
-
- // 产生DPRAM读写控制信号
- assign dpram_wr_en = ( wr_en == 1'b1 && wr_full == 1'b0 )? 1'b1 : 1'b0;
- assign dpram_wr_data = wr_data;
- assign dpram_wr_addr = wr_bin[PTR-1:0];
-
- assign dpram_rd_en = ( rd_en == 1'b1 && rd_empty == 1'b0 )? 1'b1 : 1'b0;
- assign rd_data = dpram_rd_data;
- assign dpram_rd_addr = rd_bin[PTR-1:0];
-
-
- endmodule
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。