? 通常哭尝,我們使用正確的拓?fù)浣Y(jié)構(gòu)來減少面積哥攘,簡(jiǎn)而言之,將邏輯資源盡可能的復(fù)用,當(dāng)然這會(huì)帶來速度或者吞吐率的下降逝淹。因此常常用在需要迭代遞歸的運(yùn)算中耕姊,這些運(yùn)算的輸出往往需要反饋回到輸入端。
? 以下策略經(jīng)常被用于改善FPGA設(shè)計(jì)的面積:
-
卷起流水線
“卷起流水線”這個(gè)名字聽起來較難理解栅葡,實(shí)際上這是一種跟我們常用的插入流水線相反的操作茉兰,即將一些并行流水線的邏輯通過更簡(jiǎn)單的串行結(jié)構(gòu)來實(shí)現(xiàn),具體需結(jié)合實(shí)際用例更好理解欣簇。
module mult8( output [7:0] product, input [7:0] A, input [7:0] B, input clk); reg [15:0] prod16; assign product = prod16[15:8]; always @(posedge clk) prod16 <= A * B; endmodule
優(yōu)化后為:
module mult8( output done, output reg [7:0] product, input [7:0] A, input [7:0] B, input clk, input start); reg [4:0] multcounter; // counter for number of shift/adds reg [7:0] shiftB; // shift register for B reg [7:0] shiftA; // shift register for A wire adden; // enable addition assign adden = shiftB[7] & !done; assign done = multcounter[3]; always @(posedge clk) begin // increment multiply counter for shift/add ops if(start) multcounter <= 0; else if(!done) multcounter <= multcounter + 1; // shift register for B if(start) shiftB <= B; else shiftB[7:0] <= {shiftB[6:0], 1’b0}; // shift register for A if(start) shiftA <= A; else shiftA[7:0] <= {shiftA[7], shiftA[7:1]}; // calculate multiplication if(start) product <= 0; else if(adden) product <= product + shiftA; end endmodule
對(duì)應(yīng)RTL圖為:
image.png在之前的乘法器中规脸,數(shù)據(jù)可以流水線的形式每拍輸出一個(gè)結(jié)果,而將流水線卷起后熊咽,使用移位寄存器和加法來實(shí)現(xiàn)乘法運(yùn)算莫鸭,降低了邏輯資源,但這樣需要8個(gè)clk才能夠完成一次運(yùn)算横殴。
-
通過額外控制來復(fù)用邏輯
復(fù)用邏輯資源通常需要加入額外的控制來決定資源被誰使用被因,實(shí)際上在第1小節(jié)的例子中,便使用了循環(huán)移位寄存器(shiftB)的比特來控制累加器是否計(jì)算衫仑,而在其他應(yīng)用中梨与,控制的邏輯往往會(huì)更加復(fù)雜。因此文狱,在復(fù)用的邏輯比控制邏輯資源更大時(shí)我們可以增加額外的控制來指定計(jì)算邏輯的復(fù)用粥鞋,而控制邏輯我們慣用狀態(tài)機(jī)來實(shí)現(xiàn)。
下面以一個(gè)數(shù)字低通FIR濾波器為例
Y = coeffA*X[0] + coeffB*X[1] + coeffC*X[2]
優(yōu)化得到:
module lowpassfir( output reg [7:0] filtout, output reg done, input clk, input [7:0] datain, // X[0] input datavalid, // X[0] is valid input [7:0] coeffA, coeffB; coeffC); // coeffs for low pass filter // define input/output samples reg [7:0] X0, X1, X2; reg multdonedelay; reg multstart; // signal to multiplier to begin computation reg [7:0] multdat; reg [7:0] multcoeff; // the registers that are multiplied together reg [2:0] state; // holds state for sequencing through mults reg [7:0] accum; // accumulates multiplier products reg clearaccum; // sets accum to zero reg [7:0] accumsum; wire multdone; // multiplier has completed wire [7:0] multout; // multiplier product // shift-add multiplier for sample-coeff mults mult8x8 mult8x8( .clk(clk), .dat1(multdat), .dat2(multcoeff), .start(multstart), .done(multdone), .multout(multout) ); always @(posedge clk) begin multdonedelay <= multdone; // accumulates sample-coeff products accumsum <= accum + multout[7:0]; // clearing and loading accumulator if(clearaccum) accum <= 0; else if(multdonedelay) accum <= accumsum; // do not process state machine if multiply is not done case(state) 0: begin // idle state if(datavalid) begin // if a new sample has arrived // shift samples X0 <= datain; X1 <= X0; X2 <= X1; multdat <= datain; // load mult multcoeff <= coeffA; multstart <= 1; clearaccum <= 1; // clear accum state <= 1; end else begin multstart <= 0; clearaccum <= 0; done <= 0; end end 1: begin if(multdonedelay) begin // A*X[0] is done, load B*X[1] multdat <= X1; multcoeff <= coeffB; multstart <= 1; state <= 2; end else begin multstart <= 0; clearaccum <= 0; done <= 0; end end 2: begin if(multdonedelay) begin // B*X[1] is done, load C*X[2] multdat <= X2; multcoeff <= coeffC; multstart <= 1; state <= 3; end else begin multstart <= 0; clearaccum <= 0; done <= 0; end end 3: begin if(multdonedelay) begin // C*X[2] is done, load output filtout <= accumsum; done <= 1; state <= 0; end else begin multstart <= 0; clearaccum <= 0; done <= 0; end end default state <= 0; endcase end endmodule
其對(duì)應(yīng)RTL圖為
image.png
-
在不同功能的操作中共享邏輯資源
在這里所說的資源共享并不是指FPGA布局布線工具對(duì)底層的優(yōu)化如贷,而是指在頂層架構(gòu)級(jí)別的資源塊可以被兩個(gè)不同的功能模塊共享。
下面以一個(gè)在FPGA設(shè)計(jì)中最常用到的計(jì)數(shù)器為例
image.png在模塊A和模塊B中到踏,分別使用了8比特和11比特的計(jì)數(shù)器杠袱,將計(jì)數(shù)器資源放到更高層級(jí),讓A和B模塊共享使用窝稿,如下:
image.png
-
復(fù)位對(duì)面積優(yōu)化的影響
在我們進(jìn)行FPGA設(shè)計(jì)時(shí)楣富,一個(gè)很大的誤區(qū)就是復(fù)位只在全局層面進(jìn)行實(shí)現(xiàn)而對(duì)設(shè)計(jì)的大小影響極小伴榔;其實(shí)纹蝴,在考慮設(shè)計(jì)面積的時(shí)候,對(duì)大量信號(hào)都進(jìn)行復(fù)位是會(huì)帶來很大的時(shí)序和布線壓力的踪少。
聽起來為每一個(gè)觸發(fā)器都設(shè)置復(fù)位是一個(gè)比較好的設(shè)計(jì)習(xí)慣塘安,但這樣的代價(jià)是工程會(huì)變得更大也更慢,同時(shí)會(huì)抑制工具對(duì)區(qū)域的優(yōu)化援奢,而這是不必要的兼犯。
接下來會(huì)在具體場(chǎng)景中說明復(fù)位操作給設(shè)計(jì)的速度和面積帶來的影響,以及怎么進(jìn)行優(yōu)化。
4.1 無復(fù)位接口的資源
對(duì)于某些FPGA資源來說切黔,它們并不存在復(fù)位接口砸脊,例如簡(jiǎn)單的循環(huán)移位寄存器:
always @(posedge iClk)
if(!iReset)
sr <= 0;
else
sr <= {sr[14:0], iDat};
和
always @(posedge iClk)
sr <= {sr[14:0], iDat};
這兩種描述看似差別很細(xì)微,僅僅是前者在復(fù)位時(shí)將寄存器中的值置零而已纬霞,然而對(duì)于FPGA來說凌埂,第一種描述將會(huì)使用觸發(fā)器實(shí)現(xiàn),第二種描述可以使用FPGA內(nèi)置的循環(huán)移位寄存器SRL16來實(shí)現(xiàn)诗芜,下面給出兩種設(shè)計(jì)對(duì)應(yīng)RTL圖:
和
而這兩者實(shí)現(xiàn)時(shí)所消耗資源也會(huì)有所差異
4.2 無置位接口的資源
與4.1相似的瞳抓,F(xiàn)PGA中有些資源是沒有置位接口的,比如一個(gè)8*8的乘法器
module mult8(
output reg [15:0] oDat,
input iReset, iClk,
input [7:0] iDat1, iDat2,
);
always @(posedge iClk)
if(!iReset)
oDat <= 16’hffff;
else
oDat <= iDat1 * iDat2;
endmodule
其對(duì)應(yīng)RTL圖為
由于乘法器中只有復(fù)位接口而無置位接口绢陌,因此在代碼中如果我們使用置位操作挨下,那么復(fù)位端口被閑置的同時(shí)將會(huì)引入額外的邏輯完成置位操作。
4.3 沒有異步復(fù)位的資源
同樣的脐湾,F(xiàn)PGA中也有一些資源是不支持異步復(fù)位的臭笆,比如內(nèi)置DSP核:
module dspckt(
output reg [15:0] oDat,
input iReset, iClk,
input [7:0] iDat1, iDat2);
reg [15:0] multfactor;
always @(posedge iClk or negedge iReset)
if(!iReset) begin
multfactor <= 0;
oDat <= 0;
end
else begin
multfactor <= (iDat1 * iDat2);
oDat <= multfactor + oDat;
end
endmodule
由于在上述代碼中使用了異步復(fù)位,因此會(huì)在dsp核對(duì)外圍引入額外的邏輯
額外邏輯資源消耗情況如下
4.4 同步復(fù)位RAM
在很多FPGA內(nèi)置的RAM資源一般是支持同步復(fù)位秤掌,如果我們?cè)谟迷Z設(shè)計(jì)時(shí)采用了異步復(fù)位模式愁铺,可能會(huì)導(dǎo)致最后綜合出不同結(jié)構(gòu)的RAM
module resetckt(
output reg [15:0] oDat,
input iReset, iClk, iWrEn,
input [7:0] iAddr, oAddr,
input [15:0] iDat);
reg [15:0] memdat [0:255];
always @(posedge iClk or negedge iReset)
if(!iReset)
oDat <= 0;
else begin
if(iWrEn)
memdat[iAddr] <= iDat;
oDat <= memdat[oAddr];
end
endmodule
如果使用同步復(fù)位,將會(huì)綜合出一個(gè)BRAM
而使用異步復(fù)位闻鉴,將會(huì)綜合處一個(gè)distributed RAM茵乱,并增加額外解碼邏輯
這兩者的資源消耗情況為
如果使用了錯(cuò)誤的RAM復(fù)位方式,可能對(duì)綜合結(jié)果產(chǎn)生極大的影響孟岛。
筆者注:在當(dāng)前使用調(diào)用IP核的方式實(shí)現(xiàn)RAM時(shí)瓶竭,同步復(fù)位和異步復(fù)位的影響不會(huì)如上述情況。
4.5 利用觸發(fā)器的復(fù)位/置位功能實(shí)現(xiàn)某些邏輯
FPGA的觸發(fā)器往往是支持復(fù)位或者置位功能的渠羞,因此在進(jìn)行設(shè)計(jì)時(shí)斤贰,我們可以利用這一點(diǎn)將一些邏輯從組合邏輯中轉(zhuǎn)移出來,例如次询,使用置位端實(shí)現(xiàn)或門:
或是利用復(fù)位端實(shí)現(xiàn)與門
在下面的設(shè)計(jì)中荧恍,我們可以通過移除異步復(fù)位信號(hào)并利用復(fù)位端實(shí)現(xiàn)對(duì)應(yīng)邏輯
module setreset(
output reg oDat,
input iReset, iClk,
input iDat1, iDat2);
always @(posedge iClk or negedge iReset)
if(!iReset)
oDat <= 0;
else
oDat <= iDat1 | iDat2;
endmodule
其對(duì)應(yīng)RTL圖為:
可優(yōu)化為:
更進(jìn)一步的,在下面設(shè)計(jì)中可以做到更好
oDat = !iDat3 & (iDat1 | iDat2)
可以用以下方式實(shí)現(xiàn)
module setreset (
output reg oDat,
input iClk,
input iDat1, iDat2, iDat3);
always @(posedge iClk)
if(iDat3)
oDat <= 0;
else if(iDat1)
oDat <= 1;
else
oDat <= iDat2;
endmodule
其對(duì)應(yīng)RTL圖為
最后屯吊,總結(jié)起來一句話:在考慮設(shè)計(jì)的面積優(yōu)化時(shí)送巡,盡可能少的時(shí)候復(fù)位。