續接上文,這一次我們來詳細了解下一個簡單的pipeline是如何構建起來的
》最簡單的流水線
書接上文,一個最簡單的流水線例子,這里對data_in打兩拍做輸出:
我們逐行分析pipeline里每一行代碼都干了什么。
》Stage分析
在第八行我們聲明了一個Stageable變量,如前文所述,Stageable變量并不會立即產生電路對象。
代碼第9行,我們創建一個Stage,Stage中的下面的代碼會通過調用Pipeline中的addStage函數向Pipeline中的StageSet添加當前Stage:
if(_pip != null) { _pip.addStage(this) }
在stage0中,第11行用io.data_in.valid驅動stage0中的internals.input.valid。而在第12行,從左向右看,this(payload)函數會調用Stage的apply函數:
def apply[T <: Data](key : Stageable[T]) : T = { ????apply(StageableKey(key.asInstanceOf[Stageable[Data]], null)).asInstanceOf[T] }
其進一步調用:
defapply(key : StageableKey):Data = { internals.stageableToData.getOrElseUpdate(key, ContextSwapper.outsideCondScope{ key.stageable()//.setCompositeName(this, s"${key}") }) }
可以看到,這里的主要作用是一StageableKey作為Key查詢internals.stageableToData中是否包含該key,如果有則返回其value,否則創建該key-value,并將value返回。
最終這里會返回一個UInt(8 bits)電路對象。并將io.data_in.payload賦值給該電路對象。此時,stage0中的internals.stageableToData中包含一個元素。
在第14行,我們創建了stage1,其例化時傳入了Connection,其會調用:
if(_pip != null) { _pip.addStage(this) } def chainConnect(connection: ConnectionLogic): Unit ={ _pip.connect(_pip.stagesSet.takeWhile(_ != this).last, this)(connection) } def this(connection: ConnectionLogic)(implicit_pip: Pipeline) { this() chainConnect(connection) }
這里的的調用關系是:
當前Pipeline的StageSet添加當前Stage元素
通過調用pipeline的connect函數和StageSet中的最后一個元素建立連接關系:
defconnect(m : Stage, s : Stage)(logics : ConnectionLogic*) = { val c = new ConnectionModel connections += c c.m = m c.s = s c.logics ++= logics c }
在connection里,創建了一個連接關系ConnectionModel,用于連接stage0中的output接口和stage1的input接口,采用M2S()方式進行連接。
同樣,15行同樣stage2的創建亦如此。
代碼第16行則是用stage2的output.valid驅動data_out的valid輸出。而代碼第17行通過同樣的方式創建了一個UInt(8 bits)電路對象,用來驅動data_out.payload。此時stage2中的internals.stageableToData中包含一個元素。
最后調用pipeline的build函數進行流水線的搭建。
》build分析
我們按片段來分析build中的代碼:
首先,上面第一行代碼將connection中的stage添加到stageSet。在執行該行之前,stageSet中包含了stage0、stage1、stage2三個元素,而connection按照描述進行map后得到兩個元素:(stage0,stage1),(stage1,stage2)。由于stageSet為LinkedHashSet,故執行完后依然是(stage0,stage1,stage2)。
第二行代碼則是獲取帶有slave驅動的stage,這里即stage0(驅動stage1)、stage1(驅動stage2)。
第三行則是獲取沒有slave驅動的stage,這里只有stage2。
第7~8行則是分別建立每個stage都是由誰驅動的映射存儲至stageMaster、而stage input的驅動邏輯則存儲至stageDriver。同時這里也限制了每個stage,最多只能由一個驅動邏輯。處理完后,stageMaster、stageDriver中存儲的元素分別為:
stageMaster: stage0->ArrayBuffer() stage1->ArrayBuffer(stage0) stage2->ArrayBuffer(stage2) stageDriver: stage1->Connection(m=stage0,s=stage1,logic=M2S) stage2->Connection(m=stage1,s=stage2,logic=M2S)
代碼13~16行由于我們并沒有使用到stageableResultingToData,這里可以暫時忽略,等后續章節再進行討論。
val clFlush = mutable.LinkedHashMap[ConnectionLogic, Bool]() val clFlushNext = mutable.LinkedHashMap[ConnectionLogic, Bool]() val clFlushNextHit = mutable.LinkedHashMap[ConnectionLogic, Bool]() val clThrowOne = mutable.LinkedHashMap[ConnectionLogic, Bool]() val clThrowOneHit = mutable.LinkedHashMap[ConnectionLogic, Bool]()
這里聲明的變量我們這里都是用不上,可以暫時先不進行關注。
def propagateData(key : StageableKey, stage : Stage): Boolean ={ if(stage.internals.stageableTerminal.contains(key)) returnfalse stage.stageableToData.get(key) match { caseNone => { val hits = ArrayBuffer[Stage]() for(m <- stageMasters(stage)){ ????????if(propagateData(key, m)){ ??????????stage.apply(key) //Force creation ??????????hits += m ????????} ??????} ??????hits.size match { ????????case?0?=> false case1=> true case2=> PendingError(s"$key at $stage has multiple drivers : ${hits.mkString(",")}"); false } } caseSome(x) => true } } for(stage <- stagesSet){ ??for(key <- stage.stageableToData.keys){ ????for(m <- stageMasters(stage)) { ??????propagateData(key, m); ????} ??} }
這里就有點兒意思了對于StageSet中的每個stage中stageableToData中的每個元素,都會調用propgateData函數進行處理。我們不妨先來看看此時各stage中的stageableToData中的元素:
stage0: StageableKey(payload,null)->UInt(8) stage1: null stage2: StageableKey(payload,null)->UInt(8)
由于stage0沒有master,無需考慮,stage1中stageableToData為空,也可以跳過。那么來看stage2,此時調用propagateData傳入的參數為:
key=StageableKey(payload,null)->UInt(8) m=stage1
進入propagateData函數,由于stageableTerminal我們并未使用,繼續往下看,由于stage1中的stageableToData為空,故這里match匹配為None,此時會看stage1的master端口,此時嵌套調用propagateData,傳入參數為:
key=StageableKey(payload,null)->UInt(8) m=stage0
由于stage0中的stageableToData包含該key,那么此時返回true退出,再次回到頭次調用的處理上。
由于嵌套返回true,那么此時會在stage1上調用apply函數,為stage1插入一個stageableKey。最終,各stage中stageableToData的結果為:
stage0: StageableKey(payload,null)->UInt(8) stage1: StageableKey(payload,null)->UInt(8) stage2: StageableKey(payload,null)->UInt(8)
看到這里,是不是明白了一些門道了呢?pipeline自動幫我們補齊了stage1中所依賴的元素,完成了payload從stage0到stage2中的傳輸元素補齊。
對于propagateRequirements函數,這里我們并用不上,這里我們先無需關注。
接下來看時internal connections:
這里我們近會用到s.output.valid:=s.input.valid,即將每個stage中的internal.output.valid由internal.input.valid進行驅動。
剩下的就是stage之間的連接了:
for(c<- connections){ ??val stageables = (c.m.stageableToData.keys).filter(key => c.s.stageableToData.contains(key) && !c.m.stageableTerminal.contains(key)) var m= ConnectionPoint(c.m.output.valid, c.m.output.ready, stageables.map(c.m.outputOf(_)).toList) for((l, id) <- c.logics.zipWithIndex){ ????val s = if(l?== c.logics.last) ??????ConnectionPoint(c.s.input.valid, c.s.input.ready, stageables.map(c.s.stageableToData(_)).toList) ????else?{ ??????ConnectionPoint(Bool(), (m.ready != null) generate Bool(), stageables.map(_.stageable.craft()).toList) ????} ????val area = l.on(m, s, clFlush(l), clFlushNext(l), clFlushNextHit(l), clThrowOne(l), clThrowOneHit(l)) ????if(c.logics.size != 1) ??????area.setCompositeName(c, s"level_$id", true) ????else ??????area.setCompositeName(c, true) ????m?= s ??} }
對于每個connection,首先是將master端和slave端stage共有的stageableToData給篩選到stageables中去,這里對應的為:
Connection(m=stage0,s=stage1,logic=M2S): StageableKey(payload,null) Connection(m=stage1,s=stage2,logic=M2S): StageableKey(payload,null)
接著創建ConnectionPoint,對應的paylaod即為StageabelKey所對應的電路對象,也就意味著:
Connection(m=stage0,s=stage1,logic=M2S): m=ConenctionPoint(stage0.out.valid,stage0.out.ready,stage0.stageableToData(StageableKey(payload,null))) s=ConenctionPoint(stage1.in.valid,stage1.in.ready,stage1.stageableToData(StageableKey(payload,null))) Connection(m=stage1,s=stage2,logic=M2S): m=ConenctionPoint(stage1.out.valid,stage1.out.ready,stage1.stageableToData(StageableKey(payload,null))) s=ConenctionPoint(stage2.in.valid,stage2.in.ready,stage2.stageableToData(StageableKey(payload,null)))
最終,調用M2S.on創建stage之間的連接關系:
這里我們只用到6~7行,建立起各stage之間的連接關系。
至此!完成整個流水線的創建。
》寫在最后
通過本篇,分析了一個簡單的流水線在Pipeline中的創建實現,后續將陸續進行更加復雜流水線的Demo及其背后自動實現原理。
審核編輯:劉清
-
處理器
+關注
關注
68文章
19409瀏覽量
231207 -
驅動器
+關注
關注
53文章
8272瀏覽量
147075 -
存儲器
+關注
關注
38文章
7528瀏覽量
164351 -
連接器
+關注
關注
98文章
14669瀏覽量
137248 -
Pipeline
+關注
關注
0文章
28瀏覽量
9384
原文標題:pipeline高端玩法(三)——一個pipeline是如何建立起來的
文章出處:【微信號:Spinal FPGA,微信公眾號:Spinal FPGA】歡迎添加關注!文章轉載請注明出處。
發布評論請先 登錄
相關推薦
評論