|
马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。
您需要 登录 才可以下载或查看,没有账号?注册
x
本帖最后由 杰克淡定 于 2016-6-18 12:36 编辑
FIFO用于为匹配读写速度而设置的数据缓冲buffer,当读写时钟异步时,就是异步FIFO。多bit的数据信号,并不是直接从写时钟域同步到读时钟域的,而是读写时钟域分别派遣了一个信使,去通知对方时钟域,当前本方所处的读写情况,来判断还能不能写以及可不可以读,这两个信使就是读写指针。
在《Verilog基本电路设计之一》里已讨论过,即使单bit的异步信号,通过两个相同的同步电路,达到clkb域时都可能“长”的不是一个模样,更加不用说多bit的异步信号同时传递到clkb域会变成什么五花八门的模样了。这里读写指针不是单bit信号,它们如何向对方时钟域去同步呢?格雷码!它的特点是每次只有一个bit发生变化,这样就把多bit信号同步转变为了单bit信号同步,这也是为什么多bit的格雷码信号,可以类似于单bit信号那样,直接使用两级DFF去同步的根本原因。
下面给出异步FIFO的主体部分,同样,省略了信号声明定义。
module asyn_fifo (
// input
af_wclk , // async-FIFO clear in write clock
af_rclk , // async-FIFO clear in read clock
rst_n , // system reset
af_wr_en , // async-FIFO write enable
af_rd_en , // async-FIFO read enable
af_dati , // async-FIFO data in
//output
af_full , // Async-FIFO full flag
af_empty , // Async-FIFO empty flag
af_dato // Async-FIFO data out
);
//-------------------------- data input --------------------------------
assign nxt_wptr_wclk = (af_wr_en && !af_full) ? (wptr_wclk + 1'b1) : wptr_wclk ;
assign nxt_wptr_gray = (nxt_wptr_wclk >> 1) ^ nxt_wptr_wclk ;
always @ (posedge af_wclk or negedge rst_n)
begin
if (rst_n == 1'b0) begin
wptr_wclk <= # DLY 5'b0 ;
wptr_gray <= # DLY 5'b0 ;
end
else begin
wptr_wclk <= # DLY nxt_wptr_wclk ;
wptr_gray <= # DLY nxt_wptr_gray ;
end
end
reg [31:0] ram[15:0] ; //
always @ (posedge af_wclk)
begin
if (af_wr_en == 1'b1)
ram[wptr_wclk[3:0]] <= # DLY af_dati ;
else ;
end
//-------------------------- data output ---------------------------------
assign nxt_rptr_rclk = (af_rd_en && !af_empty) ? (rptr_rclk + 1'b1) : rptr_rclk ;
assign nxt_rptr_gray = (nxt_rptr_rclk >> 1) ^ nxt_rptr_rclk ;
always @ (posedge af_rclk or negedge rst_n)
begin
if (rst_n == 1'b0) begin
rptr_rclk <= # DLY 5'b0 ;
rptr_gray <= # DLY 5'b0 ;
end
else begin
rptr_rclk <= # DLY nxt_rptr_rclk ;
rptr_gray <= # DLY nxt_rptr_gray ;
end
end
assign af_dato = ram[rptr_rclk[3:0]] ;
// sync read pointer
always @ (posedge af_wclk or negedge rst_n)
begin
if (rst_n == 1'b0) begin
rptr_sp1 <= # DLY 5'b0 ;
rptr_sp2 <= # DLY 5'b0 ;
end
else begin
rptr_sp1 <= # DLY rptr_gray ;
rptr_sp2 <= # DLY rptr_sp1 ;
end
end
// sync write pointer
always @ (posedge af_rclk or negedge rst_n)
begin
if (rst_n == 1'b0) begin
wptr_sp1 <= # DLY 5'b0 ;
wptr_sp2 <= # DLY 5'b0 ;
end
else begin
wptr_sp1 <= # DLY wptr_gray ;
wptr_sp2 <= # DLY wptr_sp1 ;
end
end
assign af_full = (wptr_gray == {~rptr_sp2[4],~rptr_sp2[3],rptr_sp2[2:0]}) ;
assign af_empty = (rptr_gray == wptr_sp2) ;
assign wptr_bin[4] = wptr_sp2[4] ;
assign wptr_bin[3] = (^wptr_sp2[4:3]) ;
assign wptr_bin[2] = (^wptr_sp2[4:2]) ;
assign wptr_bin[1] = (^wptr_sp2[4:1]) ;
assign wptr_bin[0] = (^wptr_sp2[4:0]) ;
assign rptr_bin[4] = rptr_sp2[4] ;
assign rptr_bin[3] = (^rptr_sp2[4:3]) ;
assign rptr_bin[2] = (^rptr_sp2[4:2]) ;
assign rptr_bin[1] = (^rptr_sp2[4:1]) ;
assign rptr_bin[0] = (^rptr_sp2[4:0]) ;
assign af_wlevel = wptr_wclk - rptr_bin ;
assign af_rlevel = wptr_bin - rptr_rclk ;
assign af_half_full = (af_rlevel >= 5'h7) ;
endmodule
上面给出的是深度16,宽度32的示例,大家可以使用parameter参数化定义深度和宽度,方便不同需求下的调用。除了空满信号标志,也可以根据需要做出半空半满之类信号。上面需要注意的一点就是,格雷码必须在本时钟域下DFF输出,再往另一个时钟域同步。同步FIFO呢,就不用有格雷码转换,设计更加简单,就不专门开贴描述了。 |
|