理論知識
FIFO(First In First Out, 先入先出 ),是一種數(shù)據(jù)緩沖器,用來實現(xiàn)數(shù)據(jù)先入先出的讀寫方式。數(shù)據(jù)按順序?qū)懭?FIFO,先被寫入的數(shù)據(jù)同樣在讀取的時候先被讀出,所以 FIFO存儲器沒有地址線,有一個寫端口和一個讀端口。
FIFO 存儲器主要是作為緩存,應(yīng)用在同步時鐘系統(tǒng)和異步時鐘系統(tǒng)中,分為 SCFIFO(同步 FIFO)和 DCFIFO(異步 FIFO)。后面實例中如:多比特數(shù)據(jù)做跨時鐘域的轉(zhuǎn)換、前后帶寬不同步等都用到了FIFO。
同步FIFO-SCFIFO
SCFIFO IP核配置
full:寫滿標(biāo)志位,有效表示 FIFO 已經(jīng)存儲滿了,此時禁止再往FIFO中寫入數(shù)據(jù),防止數(shù)據(jù)溢出丟失。當(dāng)寫入數(shù)據(jù)量達到FIFO設(shè)置的最大空間時,時鐘上升沿寫入最后一個數(shù)據(jù)同時full拉高;讀取數(shù)據(jù)時隨時鐘上升沿觸發(fā)同時拉低。
empty:讀空標(biāo)志位,有效表示 FIFO 中已經(jīng)沒有數(shù)據(jù)了,此時禁止FIFO繼續(xù)再讀出數(shù)據(jù),否則讀出的將是無效數(shù)據(jù)。寫入數(shù)據(jù)同時拉低;讀到最后一個數(shù)據(jù)同時拉高。
usedw:顯示當(dāng)前FIFO中已存數(shù)據(jù)個數(shù),寫第一個數(shù)據(jù)時就置1,空或滿時值為0(滿是因為寄存器溢出)。
almost full:幾乎滿標(biāo)志信號,我們可以控制FIFO快要被寫滿的時候和full信號的作用一樣。
almost empty:幾乎空標(biāo)志信號,我們可以控制FIFO快要被讀空的時候和empty信號的作用一樣。
Asynchronous clear:異步復(fù)位信號,用于清空FIFO。
Synchronous clear:同步復(fù)位信號,用于清空FIFO。
后面三個沒有使用
SCFIFO IP核調(diào)用
我們需要寫一個頂層模塊,并通過testbench定義激勵來觀察信號變化,驗證SCFIFO IP核。我們可以看到生成的模塊文件中有以下幾個端口,頂層模塊需要進行實例化
輸入信號有:sys_clk、輸入256個8bit的數(shù)據(jù)pi_data(值為十進制0~255),輸入數(shù)據(jù)有效的標(biāo)志信號pi_flag,寫請求信號rdreq。輸出信號有:讀取的數(shù)據(jù)po_data、空標(biāo)志信號empty、滿標(biāo)志信號full、指示FIFO中存在數(shù)據(jù)個數(shù)的信號usedw。
編寫代碼
module fifo(
input wire sys_clk ,
input wire [7:0] pi_data ,
input wire pi_flag ,
input wire rdreq ,
output wire [7:0] po_data ,
output wire empty ,
output wire full ,
output wire [7:0] usedw
);
scfifo_256x8 scfifo_256x8_inst(
.clock (sys_clk ),
.data (pi_data ),
.rdreq (rdreq ),
.wrreq (pi_flag ),
.empty (empty ),
.full (full ),
.q (po_data ),
.usedw (usedw )
);
endmodule
編寫testbench
//reg define
reg sys_clk ;
reg [7:0] pi_data ;
reg pi_flag ;
reg rdreq ;
reg sys_rst_n ;
reg [1:0] cnt_baud ;
//wire define
wire [7:0] po_data ;
wire empty ;
wire full ;
wire [7:0] usedw ;
//初始化系統(tǒng)時鐘、復(fù)位
initial begin
sys_clk = 1'b1;
sys_rst_n <= 1'b0;
#100;
sys_rst_n <= 1'b1;
end
//sys_clk:模擬系統(tǒng)時鐘,每10ns電平翻轉(zhuǎn)一次,周期為20ns,頻率為50MHz
always #10 sys_clk = ~sys_clk;
//cnt_baud:計數(shù)從0到3的計數(shù)器,用于產(chǎn)生輸入數(shù)據(jù)間的間隔
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
cnt_baud <= 2'b0;
else if(&cnt_baud == 1'b1)
cnt_baud <= 2'b0;
else
cnt_baud <= cnt_baud + 1'b1;
//pi_flag:輸入數(shù)據(jù)有效標(biāo)志信號,也作為FIFO的寫請求信號
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
pi_flag <= 1'b0;
else if((cnt_baud == 2'd0) && (rdreq == 1'b0))
pi_flag <= 1'b1;
else
pi_flag <= 1'b0;
//pi_data:輸入頂層模塊的數(shù)據(jù),要寫入到FIFO中的數(shù)據(jù)
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
pi_data <= 8'b0;
else if((pi_data == 8'd255) && (pi_flag == 1'b1))
pi_data <= 8'b0;
else if(pi_flag == 1'b1)
pi_data <= pi_data + 1'b1;
//rdreq:FIFO讀請求信號
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
rdreq <= 1'b0;
else if(full == 1'b1)
rdreq <= 1'b1;
else if(empty == 1'b1)
rdreq <= 1'b0;
fifo fifo_inst(
.sys_clk (sys_clk ),
.pi_data (pi_data ),
.pi_flag (pi_flag ),
.rdreq (rdreq ),
.po_data (po_data ),
.empty (empty ),
.full (full ),
.usedw (usedw )
);
endmodule
初始化:初始時鐘為高電平,復(fù)位有效,延遲100ns后復(fù)位釋放
模擬時鐘:每隔10ns翻轉(zhuǎn),時鐘周期20ns,頻率50MHz
輸入間隔計數(shù)器cnt_baud:從0-3計數(shù),復(fù)位和溢出歸0,其他情況+1,這里溢出判斷條件(cnt_baud=11)用的是位與為1
輸入有效標(biāo)志信號pi_flag:也是寫請求信號,變化條件是時鐘上升沿和復(fù)位下降沿。復(fù)位有效時歸0;計數(shù)0且沒有讀請求時拉高相當(dāng)于四個時鐘周期產(chǎn)生一次;其他情況歸0
輸入數(shù)據(jù)pi_data:是要寫到FIFO中的數(shù)據(jù),變化條件是時鐘上升沿和復(fù)位下降沿。復(fù)位有效時歸0;輸入255且pi_flag有效時歸0;其他情況+1,意味著輸入數(shù)據(jù)從0-255循環(huán)
讀請求rdreq:變化條件是時鐘上升沿和復(fù)位下降沿。復(fù)位有效時歸0;full信號拉高時也拉高說明存滿該讀了;empty拉高時就歸0說明讀空禁讀了
實例化
波形變化
我們選擇的是普通模式,讀出數(shù)據(jù)比讀使能晚一拍
對比一下"先出數(shù)據(jù)FIFO模式"的波形,就可以看出延遲不延遲一拍的區(qū)別,這里沒有演示,實際上只需要在下面這一步時選擇先出數(shù)據(jù)模式即可
異步FIFO-SCFIFO
DCFIFO IP核配置
命名為"dcfifo_256x8to128x16",我們調(diào)用的dcfifo是輸入256個深度8位寬、輸出128個深度16位寬
DSCFIFO IP核調(diào)用
我們需要寫一個頂層模塊,并通過testbench定義激勵來觀察信號變化,驗證DCFIFO IP核。我們可以看到生成的模塊文件中有以下幾個端口,頂層模塊需要進行實例化
輸入信號 :50MHz寫時鐘wrclk,輸入256個8bit的數(shù)據(jù)pi_data(值為十進制0~255),輸入數(shù)據(jù)有效的標(biāo)志信號pi_flag,25MHz的讀時鐘rdclk,寫請求信號rdreq。 輸出信號 :同步于wrclk的空標(biāo)志信號wrempty,同步于wrclk的滿標(biāo)志信號wrfull,同步于wrclk指示FIFO中存在數(shù)據(jù)個數(shù)的信號wrusedw,從FIFO中讀取的數(shù)據(jù)po_data,同步于rdclk的FIFO空標(biāo)志信號rdempty,同步于rdclk 的FIFO滿標(biāo)志信號rdfull,同步于rdclk指示FIFO中存在數(shù)據(jù)個數(shù)的信號rdusedw。
編寫代碼
module fifo
(
//同步于FIFO寫時鐘
input wire wrclk ,
input wire [7:0] pi_data ,
input wire pi_flag ,
//同步于FIFO讀時鐘
input wire rdclk ,
input wire rdreq ,
//同步于FIFO寫時鐘
output wire wrempty ,
output wire wrfull ,
output wire [7:0] wrusedw ,
//同步于FIFO讀時鐘
output wire [15:0] po_data ,
output wire rdempty ,
output wire rdfull ,
output wire [6:0] rdusedw
);
dcfifo_256x8to128x16 dcfifo_256x8to128x16_inst
(
.data (pi_data), //input [7:0] data
.rdclk (rdclk ), //input rdclk
.rdreq (rdreq ), //input rdreq
.wrclk (wrclk ), //input wrclk
.wrreq (pi_flag), //input wrreq
.q (po_data), //output [15:0] q
.rdempty(rdempty), //output rdempty
.rdfull (rdfull ), //output rdfull
.rdusedw(rdusedw), //output [6:0] rdusedw
.wrempty(wrempty), //output wrempty
.wrfull (wrfull ), //output wrfull
.wrusedw(wrusedw) //output [7:0] wrusedw
);
endmodule
編寫testbench
`timescale 1ns/1ns
module tb_fifo();
//reg define
reg wrclk ;
reg [7:0] pi_data ;
reg pi_flag ;
reg rdclk ;
reg rdreq ;
reg sys_rst_n ;
reg [1:0] cnt_baud ;
reg wrfull_reg0 ;
reg wrfull_reg1 ;
//wire define
wire wrempty ;
wire wrfull ;
wire [7:0] wrusedw ;
wire [15:0] po_data ;
wire rdempty ;
wire rdfull ;
wire [6:0] rdusedw ;
//初始化時鐘、復(fù)位
initial begin
wrclk = 1'b1;
rdclk = 1'b1;
sys_rst_n <= 1'b0;
#100;
sys_rst_n <= 1'b1;
end
//wrclk:模擬FIFO的寫時鐘,每10ns電平翻轉(zhuǎn)一次,周期為20ns,頻率為50MHz
always #10 wrclk = ~wrclk;
//rdclk:模擬FIFO的讀時鐘,每20ns電平翻轉(zhuǎn)一次,周期為40ns,頻率為25MHz
always #20 rdclk = ~rdclk;
//cnt_baud:計數(shù)從0到3的計數(shù)器,用于產(chǎn)生輸入數(shù)據(jù)間的間隔
always@(posedge wrclk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
cnt_baud <= 2'b0;
else if(&cnt_baud == 1'b1)
cnt_baud <= 2'b0;
else
cnt_baud <= cnt_baud + 1'b1;
//pi_flag:輸入數(shù)據(jù)有效標(biāo)志信號,也作為FIFO的寫請求信號
always@(posedge wrclk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
pi_flag <= 1'b0;
else if((cnt_baud == 2'd0) && (rdreq == 1'b0))
pi_flag <= 1'b1;
else
pi_flag <= 1'b0;
//pi_data:輸入頂層模塊的數(shù)據(jù),要寫入到FIFO中的數(shù)據(jù)
always@(posedge wrclk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
pi_data <= 8'b0;
else if((pi_data == 8'd255) && (pi_flag == 1'b1))
pi_data <= 8'b0;
else if(pi_flag == 1'b1)
pi_data <= pi_data + 1'b1;
//將同步于rdclk時鐘的寫滿標(biāo)志信號wrfull在rdclk時鐘下打兩拍
always@(posedge rdclk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
begin
wrfull_reg0 <= 1'b0;
wrfull_reg1 <= 1'b0;
end
else
begin
wrfull_reg0 <= wrfull;
wrfull_reg1 <= wrfull_reg0;
end
//rdreq:FIFO讀請求信號同步于rdclk時鐘
always@(posedge rdclk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
rdreq <= 1'b0;
//如果wrfull信號有效就立刻讀,則不會看到rd_full信號拉高,
//所以此處使用wrfull在rdclk時鐘下打兩拍后的信號
else if(wrfull_reg1 == 1'b1)
rdreq <= 1'b1;
else if(rdempty == 1'b1)//當(dāng)FIFO中的數(shù)據(jù)被讀空時停止讀取FIFO中的數(shù)據(jù)
rdreq <= 1'b0;
fifo fifo_inst(
.wrclk (wrclk ),
.pi_data(pi_data),
.pi_flag(pi_flag),
.rdclk (rdclk ),
.rdreq (rdreq ),
.wrempty(wrempty),
.wrfull (wrfull ),
.wrusedw(wrusedw),
.po_data(po_data),
.rdempty(rdempty),
.rdfull (rdfull ),
.rdusedw(rdusedw)
);
endmodule
初始化:和同步fifo的區(qū)別在于讀寫時鐘是異步的,需要初始化兩個時鐘
時鐘模擬:寫時鐘50MHz每隔10ns翻轉(zhuǎn)一次,讀時鐘25MHz每隔20ns翻轉(zhuǎn)一次
cnt_baud:計數(shù)器是基于寫時鐘的,將變化條件中時鐘上升沿改為寫時鐘的上升沿
pi_flag:輸入數(shù)據(jù)有效標(biāo)志信號,基于寫時鐘,同上
pi_data:輸入數(shù)據(jù),基于寫時鐘,同上
wrfull:寫滿標(biāo)志信號,基于讀時鐘,變化條件是讀時鐘上升沿和復(fù)位下降沿。wrfull在讀時鐘下打兩拍,(always塊中的語句是順序執(zhí)行的,使用兩個寄存器b,c,令b=輸入a,c=b,并輸出c,那么c相對于a而言波形不會產(chǎn)生變化,只是有兩個時鐘周期的延遲,這個就叫打拍,使用幾個寄存器就是延遲幾個周期就是打幾拍),這里打兩拍的原因是如果wrfull有效立刻就讀,就看不到rd_full拉高
rdreq:讀請求信號,基于讀時鐘,變化條件是讀時鐘上升沿和復(fù)位下降沿。復(fù)位時歸0;寫滿信號打兩拍之后的信號拉高就拉高表示寫滿需要讀,讀空信號拉高就拉低表示讀空不能讀
波形變化
可以看到當(dāng)pi_flag為高且pi_data為255的同時wrfull滿標(biāo)志信號先拉高了,延后一段時間rdfull滿標(biāo)志信號也拉高了,說明FIFO的存儲空間已經(jīng)滿了。wrfull滿標(biāo)志信號和rdfull滿標(biāo)志信號同步于 不同的時鐘 ,所以拉高的時間不同步。
還可以看到wrusedw信號是8位的計數(shù)到255,而rdusedw信號是7位的計數(shù)到127,因為輸入是8btit的,輸出是16bit的,8256=16128。wrusedw信號從255變成了0、rdusedw信號從127變成0的原因和SCFIFO中的情況一樣,都是因為數(shù)據(jù)存儲滿了,F(xiàn)IFO內(nèi)部的計數(shù)器溢出所導(dǎo)致的。
另外可以看出讀出的16bit數(shù)據(jù),是兩次的輸入8bit數(shù)據(jù)拼湊而成,先輸入的在低位,后輸入的在高位,例如輸入00,01,02,03...,兩次輸入的數(shù)據(jù)拼湊為01_00,03_02進行輸出。
如果不打兩拍會發(fā)生什么呢?我們來看波形
-
FPGA
+關(guān)注
關(guān)注
1629文章
21736瀏覽量
603384 -
存儲器
+關(guān)注
關(guān)注
38文章
7492瀏覽量
163834 -
緩沖器
+關(guān)注
關(guān)注
6文章
1922瀏覽量
45485 -
fifo
+關(guān)注
關(guān)注
3文章
388瀏覽量
43679 -
IP核
+關(guān)注
關(guān)注
4文章
327瀏覽量
49497
發(fā)布評論請先 登錄
相關(guān)推薦
評論