异步时钟FIFO的基本原理

       在现代大规模ASIC设计中,经常会遇到跨时钟域的数据传输,此时就需要用到异步时钟FIFO。既然是先进先出的fifo,那就有push端和pop端。假设push端时钟为clcok A,pop端时钟为clock B。clock A和clock B可能前者主频高;也可能后者主频高;还有可能两边主频一样,但存在相位差 (片间互联时用的较多)。

      要完成一个异步fifo的功能,至少要有以下信号(假设fifo深度为4,一层可以传输64bit数据)

Signal

功能

Push

ClockA,FIFO not full 且前级模块有新data时,push拉高,往某一个entry中push数据

Push_data[63:0]

ClockA,新来的data,将要被push到异步fifo中。

Full

ClockA,一般根据WPRT_Bin和RPTR_Bin来判断是否为full,当满足(WPRT_Bin[1:0] == RPTR_Bin[1:0]) && (WPRT_Bin[2] != RPTR_Bin[2]) 时,表明full。

WPRT_Bin_clkA[2:0]

ClockA,write ptr的二进制表示, 用于指导push 的时候新数据push到哪个entry,

初始为0,成功push到一个entry后,加1。只增不减,循环进行,000 -> 001 -> …  -> 111 -> 000 …。

Entry为4层,2bit就可以用来指示4个entry了,但一般为了能方便的得到full和empty信号,会使用多1bit。

WPRT_Gray_clkA[2:0]

ClockA,write ptr的格雷码表示,格雷码任意两个相邻的代码只有一位二进制数不同,一般设计异步FIFO时,两个时钟域之间的ptr 都通过格雷码传递。

Write ptr会以格雷码形式送给clockB,最终将用于clockB的empty信号的计算。

在数字系统中,常要求代码按一定顺序变化。例如,按自然数递增计数,若采用8421码,则数0111变到1000时四位均要变化,而在实际电路中,4位的变化不可能绝对同时发生,则计数中可能出现短暂的其它代码(1100、1111等)。在特定情况下可能导致电路状态错误或输入错误。使用格雷码可以避免这种错误。

RPRT_Gray_clkA[2:0]

ClockA,read ptr的格雷码表示,read ptr从clock B送来,RPRT_Gray_clkB信号需要先同步到clock A的上升沿,然后再延时一定的拍数(此处延时是为了让信号更加稳定),得到clock A的read ptr格雷码。

RPRT_Bin_clkA[2:0]

ClockA,read ptr的二进制表示,主要用于clockA的full信号的计算。

 

 

Pop

ClockB,FIFO not empty 且后级模块not full情况下,pop拉高,从某一个entry中读取数据

Pop_data[63:0]

ClockB,从异步fifo的某一个entry中读取的数据。

Empty

ClockB,一般根据WPRT_Bin和RPTR_Bin来判断是否为empty,当满足(WPRT_Bin[1:0] == RPTR_Bin[1:0]) && (WPRT_Bin[2] == RPTR_Bin[2]) 时,表明empty。

WPRT_Gray_clkB[2:0]

ClockB,write ptr格雷码

WPRT_Bin_clkB[2:0]

ClockB,write ptr二进制码

RPRT_Bin_clkB[2:0]

ClockB,read ptr二进制码

RPRT_Gray_clkB[2:0]

ClockB,read ptr格雷码

          一般情况下,两个时钟域的两侧分别对应的是两个不同模块,异步时钟fifo的实现也是一分两半,一部分在clock A,一部分在clockB,两部分之间的信号连接主要有以下。因为push在clockA,pop在clockB, clockA 并不知道clockB pop某一层entry的具体时刻,也就无从控制某一个时刻具体输出哪一层给clockB。故一般做法是,所有entry的数据在clockA和clockB侧都分别进行存储,也就是说clockB可以看到所有entry的具体数据,接口上,clockA的所有entry的数据信号都是output。

Signal

方向

Entry0[63:0]

Entry1[63:0]

….

Clock A  --> clock B

WPTR_Gray

Clock A  --> clock B

RPTR_Gray

Clock B  --> clock A

  • 2
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
module AsyncClockFifo( input wire clk, input wire rst, input wire wr, input wire rd, input wire [DATA_WIDTH-1:0] data_in, output reg [DATA_WIDTH-1:0] data_out ); parameter DEPTH = 16; // FIFO深度 parameter DATA_WIDTH = 8; // 数据位宽 reg [DATA_WIDTH-1:0] fifo [0:DEPTH-1]; reg [4:0] wr_ptr; reg [4:0] rd_ptr; reg [4:0] count; always @(posedge clk or posedge rst) begin if (rst) begin wr_ptr <= 0; rd_ptr <= 0; count <= 0; end else begin if (wr && !rd && count < DEPTH) begin fifo[wr_ptr] <= data_in; wr_ptr <= wr_ptr + 1; count <= count + 1; end if (!wr && rd && count > 0) begin data_out <= fifo[rd_ptr]; rd_ptr <= rd_ptr + 1; count <= count - 1; end end end endmodule ``` 这是一个使用 Verilog 编写的简单的异步时钟 FIFO,它有以下端口: - `clk`:时钟输入 - `rst`:复位输入 - `wr`:写使能输入 - `rd`:读使能输入 - `data_in`:数据输入 - `data_out`:数据输出 参数 `DEPTH` 表示 FIFO 的深度,参数 `DATA_WIDTH` 表示数据位宽。 在时钟上升沿或复位上升沿触发的始终块中,根据写使能和读使能的状态,以及 FIFO 的当前状态,进行数据的写入和读取操作。同时,维护写指针 `wr_ptr`、读指针 `rd_ptr` 和当前 FIFO 中的数据个数 `count`。 这里使用了一个简单的数组实现 FIFO,并通过指针进行读写操作。当写使能有效且读使能无效且 FIFO 未满时,将输入数据写入到指定位置,并增加写指针和数据个数。当写使能无效且读使能有效且 FIFO 非空时,从指定位置读取数据并输出,并增加读指针和减少数据个数。 请注意,这只是一个简单的示例,实际应用中可能需要添加更多的控制逻辑和状态机以确保正确的读写操作。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

123axj

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值