生活随笔
收集整理的這篇文章主要介紹了
异步fifo_跨时钟域同步(异步FIFO)
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
本文使用 Zhihu On VSCode 創作并發布
跨時鐘域同步(異步FIFO)
之前學習了跨時鐘域下的單bit信號同步的方法,這些單bit信號多是作為控制信號或者標志信號來使用,再實際的項目中,處理多bit數據也是十分常見的,即數據的同步。
異步FIFO的實現其實本質上和雙口RAM是一樣的,其實現思路就是將數據在src_clk的時鐘下寫入自己設定大小的ram中,然后通過讀時鐘des_clk從ram中將數據讀出來即可。
本次還是以一個具體的例子來說明其實現,現在我們從一個較低的時鐘域進入到一個較高的時鐘域中,低時鐘域的數據信號為8bit數據,現在需要將其緩存,并轉換成32bit的信號送入32位寬的總線上進行傳輸。具體實現代碼如下:
module nsync_fifo(input src_clk,input rst_n,input des_clk,input [8-1:0] fifo_data_in,input fifo_data_in_vaild,output reg fifo_data_out_vaild,output [32-1:0] fifo_data_out);// write fifo
reg [1:0] buffer_wr_addr;
reg [32-1:0] temp_buffer;
always @(posedge src_clk or negedge rst_n)
beginif (!rst_n)beginbuffer_wr_addr <= 2'b00;endelsebeginbuffer_wr_addr <= (fifo_data_in_vaild) ? buffer_wr_addr + 1'b1 : buffer_wr_addr;end
endalways @(posedge src_clk)
beginif (fifo_data_in_vaild)begincase(buffer_wr_addr)2'd0:temp_buffer[0+:8] <= fifo_data_in;2'd1:temp_buffer[8+:8] <= fifo_data_in;2'd2:temp_buffer[16+:8] <= fifo_data_in;2'd3:temp_buffer[24+:8] <= fifo_data_in;endcaseend
end//gen wr_fifo signal && sync
reg fifo_wr;
reg fifo_wr_sync;
wire fifo_wr_clr;
reg fifo_wr_dfb;
always @(posedge src_clk)
beginfifo_wr <= (&buffer_wr_addr & fifo_data_in_vaild);
endassign fifo_wr_clr = !rst_n | fifo_wr_sync; // feed backalways @(posedge fifo_wr or posedge fifo_wr_clr)
beginif (fifo_wr_clr)beginfifo_wr_dfb <= 1'b0;endelse beginfifo_wr_dfb <= 1'b1;end
endalways @(posedge des_clk) // under des_clk sampled
beginfifo_wr_sync <= fifo_wr_dfb;
end// recv fifo
wire recv_fifo_wr;
reg [1:0] recv_fifo_wr_addr;assign recv_fifo_wr = fifo_wr_sync;
always @(posedge des_clk or rst_n)
beginif (!rst_n)beginrecv_fifo_wr_addr <= 2'b00;endelsebeginrecv_fifo_wr_addr <= (recv_fifo_wr) ? recv_fifo_wr_addr + 1'b1 : recv_fifo_wr_addr;end
end// sync fifo data
reg [32-1:0] fifo_0;
reg [32-1:0] fifo_1;
always @(posedge des_clk)
beginif (recv_fifo_wr) // only buffer_wr_addr == 3 genbegincase (recv_fifo_wr_addr[0])1'b0 : fifo_0 <= temp_buffer;1'b1 : fifo_1 <= temp_buffer; endcaseend
end// read data from recv fifo
wire [1:0] recv_fifo_cnt;
wire recv_fifo_full;
wire recv_fifo_ready;
reg [1:0] recv_fifo_rd_addr;
assign recv_fifo_ready = | recv_fifo_cnt;
assign recv_fifo_full = recv_fifo_cnt[1]; // 2'b11 = 3
assign recv_fifo_cnt = recv_fifo_wr_addr - recv_fifo_rd_addr;always @(posedge des_clk or negedge rst_n)
beginif (!rst_n)beginrecv_fifo_rd_addr <= 2'b00;endelsebeginrecv_fifo_rd_addr <= (recv_fifo_ready) ? recv_fifo_rd_addr + 1'b1 : recv_fifo_rd_addr;end
endreg [32-1:0] recv_fifo_data;
always @(*)
begincase(recv_fifo_rd_addr[0])1'b0 : recv_fifo_data = fifo_0;1'b1 : recv_fifo_data = fifo_1;endcase
end// out
assign fifo_data_out = recv_fifo_data;
always @(posedge des_clk or negedge rst_n)
beginif(!rst_n)beginfifo_data_out_vaild <= 1'b0;endelsebeginfifo_data_out_vaild <= recv_fifo_ready;end
endendmodule
從上面的代碼可以發現,我這里將src_clk中的寫信號fifo_wr通過上章講解的單bit反饋同步方法同步到了des_clk的時鐘域下fifo_wr_sync;這樣后續的信號處理造作就可以在我們的目標時鐘des_clk下進行操作了。因為這里需要緩存的數據很少,地址線就顯得比較簡單了,通過一個簡單的乒乓操作不斷將數據從buffer中寫入fifo_0和fifo_1中;只要fifo不空,就開始讀數據。時序如下:
Image
總結
以上是生活随笔為你收集整理的异步fifo_跨时钟域同步(异步FIFO)的全部內容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。