01、數(shù)據(jù)流式編程的思想
數(shù)據(jù)流式編程思想簡介
數(shù)據(jù)流式編程(Dataflow Programming)是一種存在已久的程序設(shè)計范式,可以追溯到19世紀60年代,由MIT的Jack Dennis教授開創(chuàng)。
圖1 信號處理領(lǐng)域的數(shù)據(jù)流框圖
數(shù)據(jù)流的前身是信號流,如上圖1所示,在信號與系統(tǒng)相關(guān)的領(lǐng)域中,使用信號流框圖來表示模擬信號在一個系統(tǒng)中的處理過程。隨著計算機技術(shù)的發(fā)展,越來越多的模擬系統(tǒng)被數(shù)字系統(tǒng)所取代,連續(xù)的信號也變?yōu)榱穗x散的數(shù)據(jù)。
軟件開發(fā)中的數(shù)據(jù)流式編程思想
在介紹硬件中的數(shù)據(jù)流式編程思想之前,先來看一下軟件中的數(shù)據(jù)流式編程,并了解數(shù)據(jù)流式編程中的幾個基本元素的概念。
圖2 一段LabView編寫的流式數(shù)據(jù)處理軟件程序
在軟件開發(fā)領(lǐng)域,數(shù)據(jù)流式程序的開發(fā)已經(jīng)得到了廣泛的應(yīng)用。其中頗具代表性的是NI公司的LabView軟件以及Mathworks公司的Simulink。如上圖2所示,該圖為LabView可視化開發(fā)環(huán)境中,采用圖形化編程的方式編寫的一個簡易數(shù)據(jù)處理程序。觀察圖2,可以得到數(shù)據(jù)流式程序具有的一些要素:
- Node:數(shù)據(jù)處理節(jié)點,代表著實際的數(shù)據(jù)處理步驟(如上圖中骰子節(jié)點、節(jié)拍器節(jié)點、4輸入加法器節(jié)點、除法節(jié)點等)
- Port:分為In和Out兩種類型,一個Node可以具有多個In和Out端口(如除法器就是2輸入1輸出)
- Source:數(shù)據(jù)源,待處理輸入通過數(shù)據(jù)源進入到程序中(例如上述的骰子節(jié)點,會生成隨機數(shù)輸出,即為一個可以輸出動態(tài)數(shù)據(jù)的Source,而圖中藍色的500、綠色的布爾節(jié)點則可以視為產(chǎn)生靜態(tài)常量的Source)。
- Sink:數(shù)據(jù)出口,經(jīng)過處理的數(shù)據(jù)經(jīng)過出口輸出(例如上述節(jié)拍器節(jié)點、右下角的紅色停止條件節(jié)點,它們都只有輸入沒有輸出,即接收最終數(shù)據(jù)結(jié)果)。
- Edge:連接Port與Port的邊,代表數(shù)據(jù)流通的管道
同樣,通過觀察圖2,還可以發(fā)現(xiàn)流式程序的執(zhí)行一些特點:
- 當一個Node的In端口滿足觸發(fā)條件(例如任意輸入端口有數(shù)據(jù),或所有端口都有數(shù)據(jù)),并且其Out端口不阻塞時(下游Node可以可以接收數(shù)據(jù)),該Node開始執(zhí)行自己的處理邏輯,并將輸出數(shù)據(jù)投遞到對應(yīng)的輸出端口。
- 反壓的思想貫穿了整個程序的執(zhí)行流程,當下游節(jié)點無法處理新數(shù)據(jù)時,上游節(jié)點的處理過程將被阻塞。
- 并發(fā)性:每個Node獨立處理自己的邏輯,Node之間是并發(fā)的
- 層次性:可以將多個Node組成的子系統(tǒng)看做一個黑箱,子系統(tǒng)的Sink和Source作為該黑箱的Port,這樣就可以把子系統(tǒng)看做上層系統(tǒng)的一個Node,從而形成層級結(jié)構(gòu)。
圖3 帶有循環(huán)、條件分支結(jié)構(gòu)的數(shù)據(jù)流式程序
近些年以來,為了實現(xiàn)敏捷化開發(fā)而逐步流行的圖形化拖拽編程、低代碼編程等敏捷開發(fā)方式中,通常都可以看到數(shù)據(jù)流式編程的影子。與DSP領(lǐng)域中的簡單數(shù)據(jù)流思想不同,在軟件開發(fā)領(lǐng)域的數(shù)據(jù)流程序中,通常可以看到條件分支結(jié)構(gòu)和循環(huán)執(zhí)行結(jié)構(gòu)。因此,數(shù)據(jù)流的編程模式是圖靈完備的,不僅可以用于表示簡單的數(shù)據(jù)計算流程,還可以表達復(fù)雜的控制流。
如上圖3所示即為一款兒童編程軟件的示意圖,讀者可以嘗試在上面的圖中找出Source、Sink、Node、Port、Edge這些概念。
軟件數(shù)據(jù)流 vs 硬件數(shù)據(jù)流
軟件實現(xiàn):
數(shù)據(jù)流系統(tǒng)都需要一套調(diào)度框架,所有節(jié)點之間的并行執(zhí)行都是軟件虛擬出來的,節(jié)點是否可以激活的判斷需要框架進行額外的計算,因此性能開銷不能忽略。
硬件實現(xiàn):
硬件天然具有并發(fā)的特性。端口、連線、層級結(jié)構(gòu)、反壓、并發(fā)執(zhí)行這些概念與硬件設(shè)計有著完美的對應(yīng)關(guān)系。事實上,Verilog、VHDL等語言本身也可以被算作是和LabView、Simulink一樣的數(shù)據(jù)流式程序的開發(fā)語言[1]。
數(shù)據(jù)流思想與狀態(tài)機思想的對比
在很多場景下,如果使用狀態(tài)機的思想對硬件行為進行建模,則需要考慮很多狀態(tài)之間的轉(zhuǎn)換關(guān)系,容易造成狀態(tài)爆炸。而是用數(shù)據(jù)流的編程思想,只需要考慮數(shù)據(jù)在兩個節(jié)點之間的傳輸關(guān)系以及各個節(jié)點自己要做的事情,每個節(jié)點各司其職,可以有效降低程序的復(fù)雜度。
下面以AXI4協(xié)議的寫通道握手為例,寫地址通道和寫數(shù)據(jù)通道之間沒有明確的先后依賴順序。
圖4 AXI握手協(xié)議的順序關(guān)系
圖5 一個最簡單的狀態(tài)機握手模型,忽略了其他狀態(tài)
如果以狀態(tài)機的思路來實現(xiàn),是上述圖5這樣的,它是一種順序化執(zhí)行的思維模式,依次進行各個通道上的握手,難以描述并行發(fā)生的事件。如果強行使用狀態(tài)機來描述并發(fā)場景下各種可能的先后順序,則容易導(dǎo)致狀態(tài)爆炸,維護成本高,不夠敏捷。
圖6 基于數(shù)據(jù)流思想的握手模型
如果以數(shù)據(jù)流的思想來實現(xiàn),則如上述圖6所示,4個環(huán)節(jié)之間各自獨立運行,各司其職,兩個輸入通道以及一個輸出通道是獨立模塊,各自可以并行的完成自己的握手,模塊與模塊之間通過Skid Buf(FIFO)進行解耦。可以靈活應(yīng)對各種握手信號到達的先后順序,可以寫出易于維護且吞吐量高的代碼。
02、Bluespec語言及其流式編程框架PAClib
Bluespec SystemVerilog(BSV)和PAClib簡介
Bluespec SystemVerilog是一種高級的函數(shù)式硬件描述語言,由MIT的Arvind教授開發(fā),最初以商業(yè)授權(quán)形式發(fā)布,并于2020年正式開源。
該語言將硬件行為抽象成多個可以并發(fā)運行的Rule,Rule由觸發(fā)邏輯來控制其運行時機,Rule之間、Module之間可以通過FIFO進行通信建模,編譯器會自動生成Module之間的握手和反壓信號(即Rule的觸發(fā)邏輯),從而降低了描述復(fù)雜并行系統(tǒng)的難度,使得它非常適合使用數(shù)據(jù)流的思想進行開發(fā)。
PAClib是Pipelined Architecture Composers library的縮寫,是BSV提供的一個官方庫,可以方便的開發(fā)出基于數(shù)據(jù)流思想設(shè)計的硬件邏輯。
與DSP領(lǐng)域的流式處理相比,PAClib提供了諸如If-Else-Then、While Loop、For Loop、Fork、Join等操作,因此PAClib所提供的數(shù)據(jù)流式編程框架是圖靈完備的,不僅可以用于對復(fù)雜數(shù)據(jù)流邏輯的描述,也可以用于對復(fù)雜控制流的描述。
PAClib中的基礎(chǔ)開發(fā)組件
PACLib中的基礎(chǔ)組件可以看做是流式編程中的各個節(jié)點,為了使用流式編程,需要首先了解各個節(jié)點的功能,然后才能將這些節(jié)點連接起來,從而形成完整的程序。下面首先介紹這些節(jié)點的通用形式,即PAClib中定義的PipeOut接口以及Pipe類型,其定義如下:
interface PipeOut #(type a);
method a first ();
method Action deq ();
method Bool notEmpty ();
endinterface
typedef (function Module #(PipeOut #(b)) mkPipeComponent (PipeOut #(a) ifc))
Pipe#(type a, type b);
上述PipeOut接口的形式類似于一個FIFO,即為一條數(shù)據(jù)通道的出口端。Pipe類型本質(zhì)是一個函數(shù),用來把一個PipeOut類型的輸入轉(zhuǎn)換為另一個PipeOut類型的輸出模塊,因此Pipe類型表示了一個連接并轉(zhuǎn)換的概念。
圖7 Pipe類型的圖形化示意
上述圖7可以幫助大家更形象的理解Pipe類型和PipeOut接口,其中整個節(jié)點可以看做是一個Pipe,數(shù)據(jù)從左側(cè)流入,從右側(cè)流出,并在流經(jīng)節(jié)點的過程中被處理(處理的過程在下文介紹)。其中右側(cè)突出的部分是用于和下一個節(jié)點(也就是下一段Pipe)連接的接口,即PipeOut。Pipe和PipeOut類型提供了數(shù)據(jù)流程序中各個節(jié)點的統(tǒng)一接口規(guī)范,所有基于PAClib開發(fā)的處理邏輯都要用這兩個概念進行包裝,從而使得各個節(jié)點之間具有一定的互換性,為架構(gòu)提供了很強的靈活性。
將函數(shù)包裝為Pipe
Pipe類型只是定義了數(shù)據(jù)流經(jīng)過一個節(jié)點前后的數(shù)據(jù)類型,因此還需要具體的邏輯來實現(xiàn)從類型A到類型B的轉(zhuǎn)換。由于函數(shù)的本質(zhì)就是做映射,因此使用函數(shù)來表達對數(shù)據(jù)的加工過程再合適不過了。mkFn_to_Pipe_buffered是一個工具函數(shù),提供了將一個函數(shù)封裝為一個Pipe的能力,其定義如下:
function Pipe #(ta, tb) mkFn_to_Pipe_buffered (Bool paramPre,
tb fn (ta x),
Bool paramPost);
圖8 mkFn_to_Pipe_buffered工具函數(shù)所生成的Pipe節(jié)點示意圖
這里的用法也體現(xiàn)出了BSV函數(shù)式編程語言的特性,函數(shù)是語言中的一等公民。事實上,PAClib中對數(shù)據(jù)流模式中各個節(jié)點之間的“連線”也是通過高階函數(shù)的嵌套來實現(xiàn)的。
兩個Bool類型的參數(shù)可以選擇是否在函數(shù)的前后加入FIFO隊列進行緩沖。如果前后兩個FIFO都不開啟,則該節(jié)點表現(xiàn)出組合邏輯的性質(zhì)。開啟FIFO后,則表現(xiàn)出了流水線的性質(zhì)。通過FIFO實現(xiàn)前后級節(jié)點之間的反壓邏輯,自動適應(yīng)不同的數(shù)據(jù)流速。
Map操作
map是函數(shù)式編程中必不可少的一個功能,PAClib中也提供了不同形式的map功能:
mkMap
用于將Vector中的每一個元素并行地進行處理。mkMap_indexed
則在mkMap
的基礎(chǔ)之上,額外提供了元素對應(yīng)的索引信息,從而使得用于map的函數(shù)可以獲得關(guān)于被處理數(shù)據(jù)的位置信息。這兩個函數(shù)的定義如下:
function Pipe #(Vector #(n, a),
Vector #(n, b))
mkMap (Pipe #(a, b) mkP);
function Pipe #(Vector #(n, a),
Vector #(n, b))
mkMap_indexed (Pipe #(Tuple2 #(a, UInt #(logn)), b) mkP);
圖9 mkMap節(jié)點的圖形化示意
如上圖9所示,展示了mkMap節(jié)點的示意圖,可以看到圖中用深淺兩種顏色表示了嵌套的Pipe類型,圖中不同Pipe類型的嵌套表示了Node之間的層級關(guān)系,本質(zhì)上是通過高階函數(shù)實現(xiàn)了簡單邏輯的組合。
由于BSV靜態(tài)展開的原因,如果Vector長度較大,則會產(chǎn)生出很多個mkP的實例(mkP實例的數(shù)量與輸入向量的長度相同),占用大量面積。為了減少面積占同,用時間換空間,則可以使用接下來要介紹的節(jié)點。
分批Map操作
PAClib中提供了mkMap_with_funnel_indexed
函數(shù)來創(chuàng)建支持分批Map操作的處理節(jié)點,從而可以在面積和吞吐率之間進行取舍,其定定義如下:
function Pipe #(Vector #(nm, a),
Vector #(nm, b))
mkMap_with_funnel_indexed (UInt #(m) dummy_m,
Pipe #(Tuple2 #(a, UInt #(lognm)), b) mkP,
Bool param_buf_unfunnel)
provisos (...);
圖10 mkMap_with_funnel_indexed工具函數(shù)所創(chuàng)建的處理流程
mkMap_with_funnel_indexed
函數(shù)實際上是一個工具函數(shù),該工具函數(shù)幫助用戶創(chuàng)建了3個Pipe
的組合,其中mkMap節(jié)點已經(jīng)介紹過了,另外兩個節(jié)點分別是mkFunnel_Indexed
和mkUnfunnel
,其定義如下:
function Pipe #(Vector #(nm, a),
Vector #(m, Tuple2 #(a, UInt #(lognm))))
mkFunnel_Indexed
provisos (...);
function Pipe #(Vector #(m, a),
Vector #(nm, a))
mkUnfunnel (Bool state_if_k_is_1)
provisos (...);
上述兩個節(jié)點的作用,第一個是將一個較長的向量輸入以指定的長度進行切分,從而產(chǎn)生多個批次的輸出。而第二個是接收多個批次較短的輸入,然后產(chǎn)生一個較長的輸出,即把之前切分的批次重新拼接回來。mkMap_with_funnel_indexed
函數(shù)通過上述3個節(jié)點的組合,其實現(xiàn)的最終功能為:
- 指定一個參數(shù)m,輸入的Vector會被自動切分為多段,每段的長度為m,然后以m長度的切片為單位進行map操作。
- 在實際設(shè)計中,只需要調(diào)整參數(shù)m的值就可以很方便的在面積和吞吐率之間進行平衡。串并轉(zhuǎn)換功能由框架自動完成,開發(fā)者只需要把精力放在核心的mkP 節(jié)點即可。
節(jié)點的串聯(lián)
上述的mkMap_with_funnel_indexed
函數(shù)將3個特定的節(jié)點進行了串聯(lián),從而實現(xiàn)了更加復(fù)雜的邏輯。將簡單節(jié)點進行串聯(lián)是流式編程中必不可少的操作,為了將任意節(jié)點進行串聯(lián),可以使用mkCompose
和mkLinearPipe
這兩個函數(shù)實現(xiàn),其定義如下:
function Pipe #(a, c) mkCompose (Pipe #(a, b) mkP,
Pipe #(b, c) mkQ);
圖11 mkCompose節(jié)點示意圖
function Pipe #(a, a)
mkLinearPipe_S (Integer n,
function Pipe #(a,a) mkStage (UInt #(logn) j));
圖12 mkLinearPipe節(jié)點示意圖
可以看到,mkCompose
的作用是將兩個不同的Pipe進行串接,多個mkCompose
嵌套使用則可以實現(xiàn)多級的串接。而mkLinearPipe_S
是將同一個函數(shù)進行多次調(diào)用,但每次調(diào)用時會傳入不同的參數(shù),從而使得這個函數(shù)可以在不同的調(diào)用位置上表現(xiàn)出不同的行為。
Fork和Join操作
上述介紹的節(jié)點都是對數(shù)據(jù)進行線性操作的節(jié)點,如果只有這樣的節(jié)點是無法實現(xiàn)圖靈完備的數(shù)據(jù)流式程序的,接下來介紹的幾個非線性操作節(jié)點可以使得程序變得圖靈完備。下面從mkFork
和mkJoin
兩個函數(shù)開始介紹:
module mkFork #(function Tuple2 #(b, c) fork_fn (a va),
PipeOut #(a) poa)
(Tuple2 #(PipeOut #(b), PipeOut #(c)));
module mkJoin #(function c join_fn (a va, b vb),
PipeOut #(a) poa,
PipeOut #(b) pob )
(PipeOut #(c));
圖13 mkFork與mkJoin節(jié)點的示意圖
mkFork
和mkJoin
可以創(chuàng)建簡單的并行結(jié)構(gòu),通過用戶自己提供的函數(shù)fork_fn和join_fn,由用戶決定如何將一個輸入數(shù)據(jù)流拆分成2個輸出數(shù)據(jù)流,以及如何把兩個輸入數(shù)據(jù)流合并為一個輸出數(shù)據(jù)流。
對于Join操作而言,要求兩個輸入端口上均存在數(shù)據(jù)時,該Node才可以執(zhí)行操作,如果兩條路徑上處理數(shù)據(jù)所需的時鐘周期數(shù)不一致,則Join節(jié)點會進行等待,直到兩個輸入都有數(shù)據(jù)為止。此外,PAClib中還提供了多種Fork節(jié)點的變種實現(xiàn),可以自行了解。
條件分支操作
使用mkIfThenElse
可以創(chuàng)建條件分支,其定義及示意圖如下:
module [Module] mkIfThenElse #(Integer latency,
Pipe #(a,b) pipeT,
Pipe #(a,b) pipeF,
PipeOut #(Tuple2 #(a, Bool)) poa)
(PipeOut #(b));
圖14 mkIfThenElse節(jié)點示意圖
mkIfThenElse
可以實現(xiàn)分支邏輯。輸入數(shù)據(jù)是一個Tuple2#(a, Bool)
類型,該節(jié)點會根據(jù)Bool類型的取值將數(shù)據(jù)包路由到pipeT
或者pipeF
子節(jié)點。其內(nèi)部具有一個FIFO結(jié)構(gòu)用于存儲Token,從而實現(xiàn)在pipeT和pipeF兩個節(jié)點所需處理周期不一致時起到保序的作用。
此外,PAClib中還提供了mkIfThenElse_unordered
變種實現(xiàn),當對輸入和輸出之間的數(shù)據(jù)沒有嚴格的順序要求時,可以使用這個變種來簡化設(shè)計。For循環(huán) 使用mkForLoop
可以創(chuàng)建條件分支,其定義及示意圖如下:
module [Module] mkForLoop #(Integer n_iters,
Pipe #(Tuple2 #(a, UInt #(wj)),
Tuple2 #(a, UInt #(wj))) mkLoopBody,
Pipe #(a,b) mkFinal,
PipeOut #(a) po_in)
(PipeOut #(b))
provisos (Bits #(a, sa));
圖15 mkForLoop節(jié)點示意圖
mkForLoop
需要通過n_iters
參數(shù)傳入循環(huán)次數(shù)。在實現(xiàn)邏輯上,Loop Control Logic內(nèi)部有一個共享隊列,外部進入的新數(shù)據(jù)和經(jīng)過循環(huán)體執(zhí)行過一次處理的數(shù)據(jù),都會被放到這個隊列中。
每個新進入的數(shù)據(jù)都會被打上一個Tag,Tag的值為循環(huán)次數(shù),這個Tag伴隨著數(shù)據(jù)在循環(huán)中流動,每流動一次,Tag減一,Loop Control Logic在從共享隊列中取出數(shù)據(jù)時,會根據(jù)Tag的計數(shù)值決定將其送入mkLoopBody
節(jié)點還是mkFinal
節(jié)點。內(nèi)部有兩條Rule,分別獨立負責(zé)把新數(shù)據(jù)和正在循環(huán)的數(shù)據(jù)放入FIFO中,為循環(huán)提供了內(nèi)在動力(圖中Pump標注路徑)。
注意,由于mkLoopBody
也可能是一個需要多拍才能完成的節(jié)點,因此可以有多個數(shù)據(jù)包同時在流水線中進行循環(huán)。
While循環(huán)
在理解了For循環(huán)的實現(xiàn)原理之后,理解While循環(huán)的實現(xiàn)就沒有太大困難了。其定義和示意圖如下:
module [Module] mkWhileLoop #(Pipe #(a,Tuple2 #(b, Bool)) mkPreTest,
Pipe #(b,a) mkPostTest,
Pipe #(b,c) mkFinal,
PipeOut #(a) po_in)
(PipeOut #(c))
provisos (Bits #(a, sa));
圖16 mkWhileLoop節(jié)點示意圖
while循環(huán)的實現(xiàn)需要用戶提供3個處理節(jié)點:
mkPreTest
子節(jié)點用于執(zhí)行循環(huán)終止條件的判斷mkPostTest
子節(jié)點是循環(huán)體要做的處理邏輯mkFinal
子節(jié)點是循環(huán)體退出前對數(shù)據(jù)再進行一次修改的機會 其實現(xiàn)邏輯與For循環(huán)類似,都是通過內(nèi)部共享一個隊列來實現(xiàn)的,同時通過兩條Rule來為循環(huán)提供動力。
03、IFFT應(yīng)用實例
需求背景
圖17 一個IFFT數(shù)據(jù)處理流程
以上述圖17中的IFFT變換為例,該流程中有很多平鋪重復(fù)的結(jié)構(gòu),對于不同的應(yīng)用場景,需要在面積、吞吐量之間進行平衡,例如:
- 組合邏輯實現(xiàn)?還是流水線實現(xiàn)?
- 幾個不同的stage之間是否可以fold以減少面積?
- 每個stage內(nèi)部的f_radix4實例是否可以減少?
代碼實現(xiàn)
使用PAClib提供的數(shù)據(jù)流式開發(fā)框架,只需要調(diào)整少量參數(shù)就可以快速調(diào)整系統(tǒng)的計算結(jié)構(gòu)。下面先給出整體代碼,再對其中的各個部分進行解析,可以看到,整體框架的代碼量小于40行(僅包含用于調(diào)度數(shù)據(jù)流的框架代碼,不包含具體的運算邏輯以及注釋):
module [Module] mkIFFT (Server#(IFFTData, IFFTData));
Bool param_linear_not_looped = False;
Integer jmax = 2;
FIFO#(IFFTData) tf < - mkFIFO;
function Tuple2#(IFFTData, UInt#(6)) stage_d(Tuple2#(IFFTData, UInt#(6)) a);
return a;
endfunction
let mkStage_D = mkFn_to_Pipe (stage_d);
let s < - mkPipe_to_Server
( param_linear_not_looped
? mkLinearPipe_S (3, mkStage_S)
: mkForLoop (jmax, mkStage_D, mkFn_to_Pipe (id)));
return s;
endmodule
function Pipe #(IFFTData, IFFTData) mkStage_S (UInt#(2) stagenum);
UInt#(1) param_dummy_m = ?;
Bool param_buf_permuter_output = True;
Bool param_buf_unfunnel = True;
function f_radix4(UInt#(2) stagenum,
Tuple2#(Vector#(4, ComplexSample), UInt#(4)) element);
return tpl_1(element);
endfunction
// ---- Group 64-vector into 16-vector of 4-vectors
let grouper = mkFn_to_Pipe (group);
// ---- Map f_radix4 over the 16-vec
let mapper = mkMap_fn_with_funnel_indexed ( param_dummy_m,
f_radix4 (stagenum),
param_buf_unfunnel);
// ---- Ungroup 16-vector of 4-vectors into a 64-vector
let ungrouper = mkFn_to_Pipe (ungroup);
// ---- Permute it
let permuter = mkFn_to_Pipe_Buffered (False, f_permute,
param_buf_permuter_output);
return mkCompose (grouper,
mkCompose (mapper,
mkCompose (ungrouper, permuter)));
endfunction
下面來依次解析代碼,首先下面看這一段:
let s < - mkPipe_to_Server
( param_linear_not_looped
? mkLinearPipe_S (3, mkStage_S)
: mkForLoop (jmax, mkStage_D, mkFn_to_Pipe (id)));
圖18 平鋪結(jié)構(gòu)的IFFT計算架構(gòu)
圖19 折疊結(jié)構(gòu)的IFFT計算架構(gòu)
如上圖18和19所示,只需要修改代碼中的param_linear_not_looped
變量,即可以在兩種截然不同的計算架構(gòu)之間進行切換。通過選擇mkLinerPipe_S
或者mkForLoop
來選擇stage之間是流水線平鋪的方式來實現(xiàn),還是通過Fold的方式來實現(xiàn)。
let mapper = mkMap_fn_with_funnel_indexed ( param_dummy_m,
f_radix4 (stagenum),
param_buf_unfunnel);
圖20 通過參數(shù)調(diào)整計算的并發(fā)度
如上述代碼片段和圖20所示,通過mkMap_fn_with_funnel_indexed
函數(shù)的第一個參數(shù)來決定實例化多少個f_radix4
計算單元,從而改變計算的并行度。
let permuter = mkFn_to_Pipe_Buffered (False, f_permute,
param_buf_permuter_output);
圖21 在不同位置插入流水線寄存器
如上述代碼片段和圖21所示,通過mkFn_to_Pipe_Buffered
函數(shù)的兩個輸入?yún)?shù)來控制是否加入FIFO,從而實現(xiàn)在流水線或組合邏輯之間的切換,使得程序可以在時序上做出簡單的調(diào)整。
return mkCompose (grouper,
mkCompose (mapper,
mkCompose (ungrouper, permuter)));
圖22 節(jié)點的串聯(lián)
如上述代碼片段和圖22所示,通過mkCompose
可以實現(xiàn)多個Node之間的順序連接。
最終效果
圖23 使用PAClib實現(xiàn)的各種IFFT計算架構(gòu)
在最終效果部分,直接引用Bluespec原始介紹PPT[2]中的一個頁面來進行說明,使用不超過100行代碼,僅需要調(diào)整4個參數(shù),即可實現(xiàn)在24種不同的計算架構(gòu)之間進行切換,這24中計算架構(gòu)在面積和功耗上的差異可以達到10倍以上,用戶可以根據(jù)自己的使用場景靈活的選擇實現(xiàn)方案。
04、寫在最后
從上述示例可以看到數(shù)據(jù)流式的編程思想可以為硬件設(shè)計引入極大的靈活性和可維護性。同時,由于分支節(jié)點、條件節(jié)點、循環(huán)節(jié)點的存在,這種數(shù)據(jù)流式的編程模式是圖靈完備的,因此數(shù)據(jù)流的開發(fā)思想可以用于簡化復(fù)雜的控制通路設(shè)計。
-
DSP技術(shù)
+關(guān)注
關(guān)注
2文章
58瀏覽量
28465 -
LabVIEW
+關(guān)注
關(guān)注
1998文章
3670瀏覽量
334948 -
接收機
+關(guān)注
關(guān)注
8文章
1222瀏覽量
54440 -
VHDL語言
+關(guān)注
關(guān)注
1文章
113瀏覽量
18629 -
狀態(tài)機
+關(guān)注
關(guān)注
2文章
493瀏覽量
28189
發(fā)布評論請先 登錄
labview數(shù)據(jù)流用法注意事項
LabVIEW中的數(shù)據(jù)流編程基礎(chǔ)
怎么將數(shù)據(jù)流式傳輸?shù)紼4438C
怎么通過USB將SPI數(shù)據(jù)流式傳輸?shù)絇C
通過Virtex5 FPGA上的SATA連接將數(shù)據(jù)流式傳輸?shù)紿DD或SSD的可行性
LabVIEW用NI-DAQmx高速數(shù)據(jù)流盤
多數(shù)據(jù)流采集處理中的軟硬件接口設(shè)計
數(shù)據(jù)流編程以及LabVIEW多核編程

基于大數(shù)據(jù)的流式計算

數(shù)據(jù)流編程模型優(yōu)化

基于實時車牌識別數(shù)據(jù)流的套牌車流式并行檢測方法

數(shù)據(jù)流是什么
使用Edison、Pi等將傳感器數(shù)據(jù)流式傳輸?shù)絆ctoblu

流式圖計算TuGraph-Analytics技術(shù)背后的故事和技術(shù)特性

評論