當(dāng)在目標(biāo) FPGA 芯片中布局和布線時,首先在 Vivado 中確定時序要求.
將 FIR 作為RTL 模塊導(dǎo)入到block design中,其中通過AXI DMA 從存儲器傳輸相位增量偏移值的DDS可以輸入可變頻率正弦曲線,這樣就可以演示FIR的行為。
在 Vivado 中綜合布局布線并打開設(shè)計后,會彈出嚴(yán)重警告,告知設(shè)計不符合時序要求。
為了能夠準(zhǔn)確查看設(shè)計時序失敗的原因,在已完成綜合設(shè)計的底部窗口包含一個選項卡,用于 Vivado 在綜合期間對設(shè)計執(zhí)行的時序分析。當(dāng)存在時序失敗的信號路徑時,用戶可以過濾此時序分析以僅使用下圖中顯示的紅色圓圈感嘆號查看這些違規(guī)路徑:
在這個特定的設(shè)計中,有幾個信號路徑未能達到其分配的時序,這意味著信號的物理距離太遠而無法穿過芯片和/或在信號出去之前需要通過太多的邏輯級別。保持時間太長的信號意味著當(dāng)將其計時到下一級寄存器中時,不能依賴它的值是否有效,從而使其余下游邏輯的行為不可靠/不可預(yù)測。
s_axis_fir_tdata在這種情況下,進入 FIR 模塊的 AXI Stream 輸入接口的數(shù)據(jù)信號需要很長時間才能到達m_axis_fir_tdata目標(biāo)寄存器處的輸出。要查看比屏幕底部的時序分析窗口中的內(nèi)容更多的詳細信息,右鍵單擊底部時序分析窗口中的違規(guī)信號路徑,然后選擇“查看路徑報告(View Path Report)”選項。然后,將能夠看到 Vivado 如何計算出該信號的允許建立時間,并與它實際給出的 HDL 設(shè)計編寫方式進行比較。這會給一些提示,說明是什么導(dǎo)致建立時間延長。然而,我發(fā)現(xiàn)要真正可視化保持時序違規(guī)比在示意圖中查看信號會更直觀。
要在原理圖中打開特定信號路徑,再次右鍵單擊底部時序分析窗口中的違規(guī)信號路徑,然后選擇“Schematic”選項。將打開一個新選項卡,顯示信號路徑在設(shè)計的物理布局中經(jīng)過的邏輯。
在為axis_fir_tdata的數(shù)據(jù)總線中的一個位打開信號路徑時,它揭示了設(shè)計在芯片中的布線,從圖中可以看出信號必須通過 11 級邏輯串行后才能到達其目的地。
既然對已實施設(shè)計的分析已經(jīng)揭示了哪些信號路徑是哪個時序違規(guī)的問題,現(xiàn)在的問題是我們?nèi)绾谓鉀Q它?在這種情況下,很明顯需要重新設(shè)計當(dāng)前邏輯,以更并行的方式處理更小的數(shù)據(jù)塊,從而縮短數(shù)據(jù)到其目標(biāo)寄存器的總路徑。
個人更喜歡在嘗試編寫任何實際的 Verilog 代碼之前繪制出邏輯。當(dāng)有這種設(shè)計執(zhí)行的操作的可視化表示時,調(diào)試設(shè)計會容易得多,特別是對于跟蹤此類時序違規(guī)等問題。
檢查當(dāng)前 FIR 模塊的邏輯設(shè)計,其中數(shù)據(jù)總線違反了建立時序,很明顯循環(huán)緩沖區(qū)串行填充然后將所有 15 個數(shù)據(jù)發(fā)送到累加塊時,立即求和會產(chǎn)生大量的處理延遲。
核心的想法是嘗試填充循環(huán)緩沖區(qū),將每個緩沖區(qū)乘以適當(dāng)?shù)南禂?shù),最后一次性對 15 個算子的每一個求和,但是這次我們考慮重新設(shè)計邏輯,讓循環(huán)緩沖區(qū)中僅花費乘法和累加(求和)兩個寄存器一個級聯(lián)的時間。
新 FIR 模塊的 Verilog 代碼:
timescale 1ns / 1ps
module FIR(
input clk,
input reset,
input signed [15:0] s_axis_fir_tdata,
input [3:0] s_axis_fir_tkeep,
input s_axis_fir_tlast,
input s_axis_fir_tvalid,
input m_axis_fir_tready,
output reg m_axis_fir_tvalid,
output reg s_axis_fir_tready,
output reg m_axis_fir_tlast,
output reg [3:0] m_axis_fir_tkeep,
output reg signed [31:0] m_axis_fir_tdata
);
/* This loop controls tkeep signal on AXI Stream interface */
always @ (posedge clk)
begin
m_axis_fir_tkeep <= 4'hf;
end
/* This loop controls tlast signal on AXI Stream interface */
always @ (posedge clk)
begin
if (s_axis_fir_tlast == 1'b1)
begin
m_axis_fir_tlast <= 1'b1;
end
else
begin
m_axis_fir_tlast <= 1'b0;
end
end
// 15-tap FIR
reg enable_fir;
reg signed [15:0] buff0, buff1, buff2, buff3, buff4, buff5, buff6, buff7, buff8, buff9, buff10, buff11, buff12, buff13, buff14;
wire signed [15:0] tap0, tap1, tap2, tap3, tap4, tap5, tap6, tap7, tap8, tap9, tap10, tap11, tap12, tap13, tap14;
reg signed [31:0] acc0, acc1, acc2, acc3, acc4, acc5, acc6, acc7, acc8, acc9, acc10, acc11, acc12, acc13, acc14;
/* Taps for LPF running @ 1MSps */
assign tap0 = 16'hFC9C; // twos(-0.0265 * 32768) = 0xFC9C
assign tap1 = 16'h0000; // 0
assign tap2 = 16'h05A5; // 0.0441 * 32768 = 1445.0688 = 1445 = 0x05A5
assign tap3 = 16'h0000; // 0
assign tap4 = 16'hF40C; // twos(-0.0934 * 32768) = 0xF40C
assign tap5 = 16'h0000; // 0
assign tap6 = 16'h282D; // 0.3139 * 32768 = 10285.8752 = 10285 = 0x282D
assign tap7 = 16'h4000; // 0.5000 * 32768 = 16384 = 0x4000
assign tap8 = 16'h282D; // 0.3139 * 32768 = 10285.8752 = 10285 = 0x282D
assign tap9 = 16'h0000; // 0
assign tap10 = 16'hF40C; // twos(-0.0934 * 32768) = 0xF40C
assign tap11 = 16'h0000; // 0
assign tap12 = 16'h05A5; // 0.0441 * 32768 = 1445.0688 = 1445 = 0x05A5
assign tap13 = 16'h0000; // 0
assign tap14 = 16'hFC9C; // twos(-0.0265 * 32768) = 0xFC9C
/* This loop controls tready & tvalid signals on AXI Stream interface */
always @ (posedge clk)
begin
if(reset == 1'b0 || m_axis_fir_tready == 1'b0 || s_axis_fir_tvalid == 1'b0)