一:定義
脈動(dòng)陣列:數(shù)據(jù)流同步流過相鄰的二維陣列單元的處理器結(jié)構(gòu),一般不同方向流過不同數(shù)據(jù)。如下圖:
二維不同數(shù)據(jù)在同一時(shí)鐘下依次輸入每個(gè)處理單元,而后完成乘法并存在其寄存器中。
二:舉例
三:設(shè)計(jì)
結(jié)構(gòu):
單個(gè)PE的代碼
module pe(clk, reset, coeff, in_x, in_y, out_x, out_y);
parameter size = 8;
input?clk, reset;
input?[size-1:0]?in_x, coeff;
input?[size+size-1:0]?in_y;
output?[size-1:0]?out_x;
output?[size+size-1:0]?out_y;
reg?[size+size-1:0]?out_y;
reg?[size-1:0]?out_x;
always@(posedge clk)
begin
?if(reset) begin
?out_x <= 0;
?out_y <= 0;
?end
?else begin
?out_y <= in_y + (in_x * coeff);
?out_x <= in_x;
?end
end
endmodule
?
四個(gè)PE,其余類推
//***** main ****************************
module systolic(clk, reset, input_x, output_y);
parameter?size = 8;
input?clk, reset;
input?[size-1:0]?input_x;
output [size+size-1:0]?output_y;
wire?[size-1:0]?pe0_x, pe1_x, pe2_x, pe3_x;
wire?[size+size-1:0]?pe1_y, pe2_y, pe3_y;
wire?[size-1:0]?h0 = 8'h01;
wire?[size-1:0]?h1 = 8'h01;
wire?[size-1:0]?h2 = 8'h01;
wire?[size-1:0]?h3 = 8'h01;
wire?[size+size-1:0]?pe4_y = 16'h0000;
pe pe_0(clk, reset, h0, input_x, pe1_y, pe0_x, output_y);
pe pe_1(clk, reset, h1, pe0_x, pe2_y, pe1_x, pe1_y);
pe pe_2(clk, reset, h2, pe1_x, pe3_y, pe2_x, pe2_y);
pe pe_3(clk, reset, h3, pe2_x, pe4_y, pe3_x, pe3_y);
endmodule
=========================================================================================================
脈動(dòng)陣列(Systolic Array)計(jì)算矩陣乘法(Array Multiplication)
下一個(gè)目標(biāo)是實(shí)現(xiàn)流水線輸出,提升硬件資源的利用率。
脈動(dòng)陣列(Systolic Array):數(shù)據(jù)流同步流過相鄰的二維陣列單元的處理器結(jié)構(gòu),一般不同方向流過不同數(shù)據(jù)。
結(jié)構(gòu):
矩陣計(jì)算:
C語言描述每個(gè)輸出矩陣中的值:
For I = 1 to N
?For J = 1 to N
?For K = 1 to N
?C[I,J] = C[I,J] + A[J,K] * B[K,J];
運(yùn)用N x N processing units,輸入數(shù)據(jù)呈批次輸入:
二維不同數(shù)據(jù)在同一時(shí)鐘下依次輸入每個(gè)處理單元,而后完成乘法并存在其寄存器中。
其中每個(gè)PE(處理單元)結(jié)構(gòu)如下:
是一個(gè)乘加單元?c=c+(a*b)
例子:計(jì)算兩個(gè)3×3矩陣的乘積
結(jié)構(gòu):
在CLK驅(qū)動(dòng)下的每一個(gè)步驟如下:
Clk1:
?
Clk2:
Clk3:
Clk4:
Clk5:
Clk6:
Clk7:
Clk8:輸出
功能仿真圖:
在start?上升沿到來后的第一個(gè)CLK上升沿開始計(jì)數(shù)
Count_start高電平期間
Cout=1時(shí),準(zhǔn)備a11和b11;
Cout=2時(shí),將數(shù)據(jù)打入寄存器,并計(jì)數(shù)出a11*b11;
Cout=3時(shí),計(jì)數(shù)a11*b11+a12*b21
Cout=4時(shí),計(jì)數(shù)a11*b11+a12*b21+a13*b31
Cout=5時(shí),用寄存器打一拍輸出Y11。
其他類似。
時(shí)序仿真圖:
連續(xù)運(yùn)算,中間忘了將乘加單元寄存器清零的情況,功能仿真:
每次計(jì)算出結(jié)果后清零寄存器,修改后的功能仿真圖:
數(shù)據(jù)在送入運(yùn)算單元之前,采用寄存器打一拍,功能仿真圖:
狀態(tài)機(jī)便于實(shí)現(xiàn)控制。
狀態(tài)機(jī)控制:功能仿真
時(shí)序仿真圖:
二維流水線結(jié)構(gòu)矩陣乘法(Array Multiplication)
上一篇文中建立了矩陣乘法運(yùn)算的數(shù)據(jù)路徑,從仿真結(jié)構(gòu)中可以看出整個(gè)計(jì)算方案的可行性,但是存在一個(gè)問題,就是硬件運(yùn)算單元的資源利用率不高。這是什么意思呢?就是說,在每次計(jì)算兩個(gè)3*3矩陣的乘法之前,需要將整個(gè)運(yùn)算單元中的每個(gè)寄存器都清零,但是9個(gè)輸出結(jié)果不是在同一個(gè)時(shí)刻輸出,有先有后。當(dāng)最先出結(jié)果的P11計(jì)算出第一個(gè)結(jié)果之后,它就不再輸出新值了。其實(shí)這時(shí)硬件電路是存在的,而且是在不斷計(jì)算出新值,只是這些值不是我們需要的有效值。那么如果將這些硬件資源完全被利用起來,讓它們?cè)谧疃痰臅r(shí)間間隔里都有有效正確數(shù)據(jù)輸出呢。
其實(shí)要說的就是流水線設(shè)計(jì)思想,我們只需要當(dāng)P11單元計(jì)算出新的值,下個(gè)CLK將其計(jì)算結(jié)果輸出(只要有另一個(gè)機(jī)制接收這些值),然后將其清零(如果不清零,那個(gè)會(huì)累加了上次的計(jì)算結(jié)果),再然后就可以將下個(gè)要計(jì)算的兩個(gè)3*3矩陣對(duì)應(yīng)位置上面的新值輸入給P11單元,讓其計(jì)算兩個(gè)數(shù)的乘積。
其他單元類似,思想就是在輸出結(jié)果后立即做清零和賦新值,不必等整個(gè)計(jì)算單元都出結(jié)果之后才開始進(jìn)行下個(gè)矩陣乘法的計(jì)算。
那么從大局來看,就出現(xiàn)個(gè)這樣的情況:在上個(gè)矩陣計(jì)算還沒有完全計(jì)算出9個(gè)值的時(shí)候(右下角部分還在計(jì)算),左上角的單元就已經(jīng)開始下個(gè)矩陣乘法的計(jì)算。關(guān)于輸出就是,每個(gè)CLK都有有效的數(shù)據(jù)輸出,產(chǎn)生了流水輸出。而這些輸出是從這整個(gè)二維矩陣計(jì)算單元的某些地方同時(shí)輸出的,就像水流即向下流也向右流,只是在這里水流對(duì)應(yīng)了數(shù)據(jù)輸出。
回顧一下這樣做的目的是什么:為了增加整個(gè)運(yùn)算單元的利用率。做了改進(jìn),我們可以看出,每個(gè)CLK到來時(shí),每個(gè)計(jì)算單元中的乘法和加法器的運(yùn)算都是有效的計(jì)算。我們一定要記住FPGA做運(yùn)算和CPU做計(jì)算的不同點(diǎn)。FPGA做運(yùn)算,那些設(shè)計(jì)好的運(yùn)算單元,不管你對(duì)這些運(yùn)算單元操作或者不操作,它們都已經(jīng)以硬件電路的形式存在了,也就是說,每個(gè)CLK到來時(shí),它們都進(jìn)行了一次計(jì)算。那么如何有效的利用這些運(yùn)算單元,就是不要讓它們做無效的計(jì)算,讓它們每一次的計(jì)算都對(duì)我們來說是有效的,是一個(gè)對(duì)整個(gè)單元輸出有影響的計(jì)算。而想實(shí)現(xiàn)這樣的功能流水線設(shè)計(jì)方法是必須要采用的。只是有時(shí)候單向流水(一維)更容易被我們所理解。其實(shí)這二維運(yùn)算單元也是如此,也可以產(chǎn)生流水。
相對(duì)于不采用流水線操作,數(shù)據(jù)路徑不必做太多修改,主要是控制單元更為復(fù)雜,要考慮何時(shí)對(duì)某個(gè)單元清零,何時(shí)再對(duì)其賦值,何時(shí)把數(shù)據(jù)讀走等。這些需要比較精準(zhǔn)的調(diào)度。有人會(huì)想,那么這樣是不是需要更多狀態(tài)機(jī)狀態(tài)?答案是肯定的。那么是不是需要更多的硬件資源的開銷呢?答案也是肯定的。那么這種流水線的優(yōu)勢(shì)在哪呢?其實(shí)是速度上面的優(yōu)勢(shì),有時(shí)候在不能提高整個(gè)系統(tǒng)的工作頻率的情況下,再消耗一點(diǎn)資源來提高其它部分(運(yùn)算單元)的利用率也是一個(gè)很好的提高運(yùn)算速度的辦法。(注:這并不等同于面積與速度的法則)其實(shí)這也是一個(gè)比較好的,硬件算法優(yōu)化方案。我們可以將其歸納為以優(yōu)化算法路徑的算法優(yōu)化方法。
下面是仿真結(jié)果:
?
矩陣還是之前的矩陣??梢钥闯雒總€(gè)CLK都有2-3個(gè)有效輸出。
增加了Valid信號(hào),當(dāng)其為高電平時(shí),說明對(duì)應(yīng)運(yùn)算單元輸出是有效值。其實(shí)在算法成熟之后,這些Valid信號(hào)是可以撤銷的,因?yàn)槲覀兺耆懒?,輸出的?guī)律,只需要在特定的時(shí)間讀走數(shù)據(jù)即可。而且我們可以將這些數(shù)據(jù)合并到幾根連續(xù)有效的總線上面。讓每個(gè)總線上面每個(gè)CLK都是有效值。(筆者將按照此方法,繼續(xù)優(yōu)化輸出總線)
下面是對(duì)輸入最大值時(shí)的輸出仿真,主要是看數(shù)據(jù)會(huì)不會(huì)益處:
測(cè)試8bits輸入最大值255對(duì)應(yīng)的輸出值:
時(shí)序仿真:
255*255+255*255+255*255=195075
255*254+255*254+255*254=194310
編輯:黃飛
?
評(píng)論