當(dāng)在目標(biāo) FPGA 芯片中布局和布線時(shí),首先在 Vivado 中確定時(shí)序要求.
將 FIR 作為RTL 模塊導(dǎo)入到block design中,其中通過AXI DMA 從存儲(chǔ)器傳輸相位增量偏移值的DDS可以輸入可變頻率正弦曲線,這樣就可以演示FIR的行為。
在 Vivado 中綜合布局布線并打開設(shè)計(jì)后,會(huì)彈出嚴(yán)重警告,告知設(shè)計(jì)不符合時(shí)序要求。
為了能夠準(zhǔn)確查看設(shè)計(jì)時(shí)序失敗的原因,在已完成綜合設(shè)計(jì)的底部窗口包含一個(gè)選項(xiàng)卡,用于 Vivado 在綜合期間對(duì)設(shè)計(jì)執(zhí)行的時(shí)序分析。當(dāng)存在時(shí)序失敗的信號(hào)路徑時(shí),用戶可以過濾此時(shí)序分析以僅使用下圖中顯示的紅色圓圈感嘆號(hào)查看這些違規(guī)路徑:
在這個(gè)特定的設(shè)計(jì)中,有幾個(gè)信號(hào)路徑未能達(dá)到其分配的時(shí)序,這意味著信號(hào)的物理距離太遠(yuǎn)而無法穿過芯片和/或在信號(hào)出去之前需要通過太多的邏輯級(jí)別。保持時(shí)間太長的信號(hào)意味著當(dāng)將其計(jì)時(shí)到下一級(jí)寄存器中時(shí),不能依賴它的值是否有效,從而使其余下游邏輯的行為不可靠/不可預(yù)測(cè)。
s_axis_fir_tdata在這種情況下,進(jìn)入 FIR 模塊的 AXI Stream 輸入接口的數(shù)據(jù)信號(hào)需要很長時(shí)間才能到達(dá)m_axis_fir_tdata目標(biāo)寄存器處的輸出。要查看比屏幕底部的時(shí)序分析窗口中的內(nèi)容更多的詳細(xì)信息,右鍵單擊底部時(shí)序分析窗口中的違規(guī)信號(hào)路徑,然后選擇“查看路徑報(bào)告(View Path Report)”選項(xiàng)。然后,將能夠看到 Vivado 如何計(jì)算出該信號(hào)的允許建立時(shí)間,并與它實(shí)際給出的 HDL 設(shè)計(jì)編寫方式進(jìn)行比較。這會(huì)給一些提示,說明是什么導(dǎo)致建立時(shí)間延長。然而,我發(fā)現(xiàn)要真正可視化保持時(shí)序違規(guī)比在示意圖中查看信號(hào)會(huì)更直觀。
要在原理圖中打開特定信號(hào)路徑,再次右鍵單擊底部時(shí)序分析窗口中的違規(guī)信號(hào)路徑,然后選擇“Schematic”選項(xiàng)。將打開一個(gè)新選項(xiàng)卡,顯示信號(hào)路徑在設(shè)計(jì)的物理布局中經(jīng)過的邏輯。
在為axis_fir_tdata的數(shù)據(jù)總線中的一個(gè)位打開信號(hào)路徑時(shí),它揭示了設(shè)計(jì)在芯片中的布線,從圖中可以看出信號(hào)必須通過 11 級(jí)邏輯串行后才能到達(dá)其目的地。
既然對(duì)已實(shí)施設(shè)計(jì)的分析已經(jīng)揭示了哪些信號(hào)路徑是哪個(gè)時(shí)序違規(guī)的問題,現(xiàn)在的問題是我們?nèi)绾谓鉀Q它?在這種情況下,很明顯需要重新設(shè)計(jì)當(dāng)前邏輯,以更并行的方式處理更小的數(shù)據(jù)塊,從而縮短數(shù)據(jù)到其目標(biāo)寄存器的總路徑。
個(gè)人更喜歡在嘗試編寫任何實(shí)際的 Verilog 代碼之前繪制出邏輯。當(dāng)有這種設(shè)計(jì)執(zhí)行的操作的可視化表示時(shí),調(diào)試設(shè)計(jì)會(huì)容易得多,特別是對(duì)于跟蹤此類時(shí)序違規(guī)等問題。
檢查當(dāng)前 FIR 模塊的邏輯設(shè)計(jì),其中數(shù)據(jù)總線違反了建立時(shí)序,很明顯循環(huán)緩沖區(qū)串行填充然后將所有 15 個(gè)數(shù)據(jù)發(fā)送到累加塊時(shí),立即求和會(huì)產(chǎn)生大量的處理延遲。
核心的想法是嘗試填充循環(huán)緩沖區(qū),將每個(gè)緩沖區(qū)乘以適當(dāng)?shù)南禂?shù),最后一次性對(duì) 15 個(gè)算子的每一個(gè)求和,但是這次我們考慮重新設(shè)計(jì)邏輯,讓循環(huán)緩沖區(qū)中僅花費(fèi)乘法和累加(求和)兩個(gè)寄存器一個(gè)級(jí)聯(lián)的時(shí)間。
新 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)