摘要:本次實驗學習記錄主題為“FIFO_IP核實現算術求和”,主要內容是上位機透過串列埠向FPGA傳送一定規格的數字矩陣,FPGA對矩陣處理,按規定邏輯實現求和運算,將結果返回串列埠轉發至上位機。
晶片型號:cyclone Ⅳ EP4CE10F17C8
平臺工具:Quartus II 15.0 (64-bit)、Modelsim SE-64 10.4
最終框圖:
【FIFO IP核概述及呼叫】
FIFO(First In First Out,先入先出) IP核作為資料緩衝區,能臨時儲存從資料來源接收的資料,直到資料被其他處理單元再次讀取。FIFO IP核通常用於多位元資料的跨時鐘域處理以及前後頻寬不同步情況,平衡資料來源和處理單元之間的速度差異,同時減少因速率不匹配而導致的等待時間或資料丟失。
FIFO IP核支援同步(SCFIFO)和非同步(DCFIFO)操作模式,在同步模式下,讀寫操作在同一時鐘域下進行。其支援可配置的引數(如資料寬度、深度等,調整以適應不同的需求。針對不同模式的選擇,需要考慮方面包括時鐘源、存取位寬和深度、以及一系列輔助設計的標誌訊號和操作訊號。
下圖為Quartus Ⅱ構建IP核能產生的全部介面,同步模式下,除了基本的外介面如資料位、時鐘、寫標誌和讀標誌、計數位usedw
外,還有清零操作(同步sclr
/非同步aclr
)、滿/近滿/空/近空/校檢eccstatus
訊號。而非同步模式下,對於入棧和出棧輔助設計的分為了兩批,具體結構如下圖。
訪問IP Catalog:在Quartus Ⅱ的選單欄中,點選“Tools”選項,然後選擇“IP Catalog”或者“MegaWizard Plug-In Manager”,開啟“fifo”選項即可。FIFO配置流程分為三部分:parameter settings、EDA和summary。如下圖的配置介面,在其左上可以實時看到配置產生的介面,左下角看到FIFO在FPGA所產生的資源消耗。
在配置完基本引數後,FIFO還支援功能等設定趨向,rdreq
讀取驅動:訊號作為請求,資料滯後一個時鐘週期輸出;訊號作為確認,資料同時輸出。儲存方式和最大深度選擇自動匹配即可。FIFO效能支援最大速度和最小消耗資源空間兩種,可根據具體工程需求選定。黃色方框內是上級檢測和下級檢測保護電路,即儲存棧滿和棧空情況下的繼續操作保護,最小面是儲存空間位置選擇,這裡預設選定內部儲存塊即可。
非同步模式下,還需配置速度和穩定性的最佳化方式,一是保持最低延遲,但需要同步時鐘,沒有亞穩態保護,佔用資源空間最小,提供良好效能;二是具備兩個同步階段和良好的亞穩態保護,資源空間消耗中等;三是提高最佳的亞穩態保護,具有三個或更多同步階段。
【IP核的同步、非同步呼叫及模擬驗證】
首先,構建一個同步FIFO_IP核,具體配置如下:
almost_empty_value = 20, //近空閾值
almost_full_value = 220, //近滿閾值
intended_device_family = "Cyclone IV E", //FPGA IP核型號
lpm_numwords = 256, //FIFO深度
lpm_showahead = "OFF", //rdreq模式選擇
lpm_type = "scfifo", //FIFO工作模式(同步,單時鐘模式)
lpm_width = 8, //時鐘源同步下,進入FIFO位寬
lpm_widthu = 8, //計數位寬
IP的直接呼叫inst.v模組檔案即可,例項化應用後,透過一個簡單的錄入核/退出核模擬(如下兩圖)。可以看到,程式啟動,持續向核內寫入256個8bit資料,模擬設定,寫入週期是讀入週期的四倍。
計數到20時,退出近空閾值,近空訊號拉低;計數到220,達到近滿閾值,近滿訊號拉高,等到寫入完畢(這裡計數單元usedw_sig
溢位,顯示8'h00),滿訊號拉高。下一週期,讀標誌拉高,讀取一個8bit資料後,滿訊號拉低,持續讀取完畢。
構建一個非同步混合FIFO_IP核,具體配置如下:
add_usedw_msb_bit = "ON", //為計數位擴充一位,避免溢位
intended_device_family = "Cyclone IV E", //FPGA IP核型號
lpm_numwords = 256, //FIFO深度
lpm_showahead = "OFF", //rdreq模式選擇
lpm_type = "dcfifo_mixed_widths", //混合非同步fifo模式,意思是錄入核和退出核位寬不一致
lpm_width = 8, //錄入核位寬
lpm_widthu = 9, //計數位寬+1 = 9
lpm_widthu_r = 8, //讀取退出核位寬
lpm_width_r = 16, //讀取退出核計數位寬
非同步模式,需要關注時序上的同步(打了兩拍),50MHz的寫時鐘wrclk,25MHz的讀時鐘rdclk。這裡由於寫位寬和讀位寬的不同,要區別寫計數和讀計數的計數方式。
【呼叫FIFO實現求和運算】
呼叫Quartus Ⅱ的IP核實現普通求和運算(便於Sobel演算法FPGA學習),左邊是求和模組的框圖,需要複用兩個相同位寬及深度的FIFO IP核,以m x n(5x4)矩陣為例,先對上三行求運算後,持續向下降一行運算,形成一個新的矩陣(m-2) x n形式。
FPGA運算:pi_data持續接入資料,先將第一、二行資料分佈存入FIFO 1核和2核內,在第三行資料開始,同步讀取兩核一個資料,並對其作求和運算,透過po_data輸出。求和的同時,將FIFO 2核內資料寫入1核(1、2核此時為空),即第二行充當原先的第一行。第三行寫入2核,第四行持續運算.......
時序圖如下,pi_flag
和pi_data
是串列埠rx模組接收上位機處理後的資料,錄入此fifo_disp模組。矩陣的列和行計數器cnt_row
和cnt_rol
作為的順序標誌,方便確認求和準備。dout_flag
條件(wr_en2)&&(rd_en),標誌建立用於1核資料再次寫入。借入標誌訊號sum_flag
,觸發求和po_data
=data_out1
+data_out2
+pi_data
。
對應的各訊號時序條件處理,程式碼如下:
always@(posedge sys_clk or negedge sys_rst)begin //dispose cnt_row counter
if(!sys_rst) cnt_row <= 8'd0;
else if((cnt_row == CNT_ROW_MAX)&&(pi_flag)) cnt_row <= 8'd0;
else if(pi_flag) cnt_row <= cnt_row + 1'b1;
end
always@(posedge sys_clk or negedge sys_rst)begin //dispose cnt_col counter
if(!sys_rst) cnt_col <= 8'd0;
else if((cnt_col == CNT_COL_MAX)&&(pi_flag)&&(cnt_row == CNT_ROW_MAX))
cnt_col <= 8'd0;
else if((cnt_row == CNT_ROW_MAX)&&(pi_flag))cnt_col <= cnt_col + 1'b1;
end
always@(posedge sys_clk or negedge sys_rst)begin //dispose wr_en1 drive
if(!sys_rst) wr_en1 <= 1'b0;
else if((cnt_col == 8'd0) && (pi_flag)) wr_en1 <= 1'b1;
else wr_en1 <= dout_flag;
end
always@(posedge sys_clk or negedge sys_rst)begin //dispose data_in1 sequence
if(!sys_rst) data_in1 <= 8'd0;
else if((pi_flag)&&(cnt_col == 8'd0)) data_in1 <= pi_data;
else if(dout_flag == 1'b1) data_in1 <= data_out2;
else data_in1 <= data_in1;
end
always@(posedge sys_clk or negedge sys_rst)begin //dispose wr_en2 drive
if(!sys_rst) wr_en2 <= 1'b0;
else if((cnt_col >= 8'd1)&&(cnt_col <= CNT_COL_MAX - 1'b1)&&(pi_flag))
wr_en2 <= 1'b1;
else wr_en2 <= 1'b0;
end
always@(posedge sys_clk or negedge sys_rst)begin //dispose data_in2 sequence
if(!sys_rst) data_in2 <= 8'b0;
else if((pi_flag)&&(cnt_col >= 8'd1)&&(cnt_col <= (CNT_COL_MAX - 1'b1)))
data_in2 <= pi_data;
else data_in2 <= data_in2;
end
always@(posedge sys_clk or negedge sys_rst)begin //dispose rd_en drive
if(!sys_rst) rd_en <= 1'b0;
else if((pi_flag)&&(cnt_col >= 8'd2)&&(cnt_col <= CNT_COL_MAX)) rd_en <= 1'b1;
else rd_en <= 1'b0;
end
always@(posedge sys_clk or negedge sys_rst)begin //dispose dout_flag sequence
if(!sys_rst) dout_flag <= 0;
else if((wr_en2)&&(rd_en)) dout_flag <= 1'b1;
else dout_flag <= 1'b0;
end
always@(posedge sys_clk or negedge sys_rst)begin //dispose sum_flag sequence
if(!sys_rst) sum_flag <= 1'b0;
else if(rd_en) sum_flag <= 1'b1;
else sum_flag <= 1'b0;
end
always@(posedge sys_clk or negedge sys_rst)begin //dispose po_data result
if(!sys_rst) po_data <= 8'b0;
else if(sum_flag) po_data <= data_out1 + data_out2 + pi_data;
else po_data <= po_data;
end
always@(posedge sys_clk or negedge sys_rst)begin //dispose po_flag sequence
if(!sys_rst) po_flag <= 1'b0;
else po_flag <= sum_flag;
end
模擬分析:很明顯,模擬圖與上面的時序圖一致,tx、rx模組在之前的實驗經過模擬驗證了。
最後,將程式下載至開發板,得到的資料與模擬結果一樣,簡單做了兩次測試,結果都正確。
文獻參考:
[1] FIFO求和實驗 野火FPGA Verilog開發實戰指南——基於Altera EP4CE10 征途Pro開發板 文件 (embedfire.com);
[2] 掰開揉碎講 FIFO(同步FIFO和非同步FIFO) - Doreen的FPGA自留地 - 部落格園 (cnblogs.com);
本篇文章中使用的Verilog程式模組,若有需見網頁左欄Gitee倉庫連結:https://gitee.com/silly-big-head/little-mouse-funnyhouse/tree/FPGA-Verilog/