Vivado HLS推動(dòng)協(xié)議處理系統(tǒng)蓬勃發(fā)展(下)
5 流分割和合并
本文引用地址:http://cafeforensic.com/article/273295.htm在協(xié)議處理中,根據(jù)協(xié)議棧特定字段轉(zhuǎn)發(fā)數(shù)據(jù)包給不同模塊,然后在發(fā)送前將不同的流重新組合,是一項(xiàng)關(guān)鍵功能。Vivado HLS允許使用高級(jí)架構(gòu)來(lái)推動(dòng)這一轉(zhuǎn)發(fā)過(guò)程,具體如例4中所示的流合并。
例4:簡(jiǎn)單的流合并情況
1 void merge(stream inData[NUM_MERGE_
STREAMS], stream&outData) {
2 #pragma HLS INLINE off
3 #pragma HLS pipeline II=1 enable_flush
4
5 static enum mState{M_IDLE = 0, M_STREAM}
mergeState;
6 static ap_uint
rrCtr = 0;
7 static ap_uint
streamSource = 0;
8 axiWord inputWord = {0, 0, 0, 0};
9
10 switch(mergeState) {
11 case M_IDLE:
12 bool streamEmpty[NUM_MERGE_STREAMS];
13 #pragma HLS ARRAY_PARTITION variable=stream-
Empty complete
14 for (uint8_t i=0;i
15 streamEmpty[i] = inData[i].empty();
16 for (uint8_t i=0;i
17 uint8_t tempCtr = streamSource + 1 + i;
18 if (tempCtr >= NUM_MERGE_STREAMS)
19 tempCtr -= NUM_MERGE_STREAMS;
20 if(!streamEmpty[tempCtr]) {
21 streamSource = tempCtr;
22 inputWord = inData[streamSource].
read();
23 outData.write(inputWord);
24 if (inputWord.last == 0)
25 mergeState = M_STREAM;
26 break;
27 }
28 }
29 break;
30 case M_STREAM:
31 if (!inData[streamSource].empty()) {
32 inData[streamSource].read(inputWord);
33 outData.write(inputWord);
34 if (inputWord.last == 1)
35 mergeState = M_IDLE;
36 }
37 break;
38 }
39 }
本例體現(xiàn)的是模塊合并功能的使用,其中一個(gè)流陣列作為輸入(inData),一個(gè)單流作為輸出(outData)。這個(gè)模塊的功能是以無(wú)區(qū)別的方式從輸入流讀取數(shù)據(jù),然后將讀取的數(shù)據(jù)輸出給輸出流。該模塊采用雙級(jí)FSM實(shí)現(xiàn),其結(jié)構(gòu)與前文介紹的結(jié)構(gòu)一致。
FSM的第一個(gè)狀態(tài)用于確保選擇輸入流的無(wú)區(qū)別性(fairness)。實(shí)現(xiàn)的方法是使用循環(huán)算法檢查隊(duì)列。該算法在完成上一隊(duì)列的訪問(wèn)之后,即從下一隊(duì)列起查找新的數(shù)據(jù)。第17到19行的代碼采用的即是此循環(huán)算法。常量NUM_MERGE_STREAMS用于設(shè)定待合并的流的數(shù)量。接下來(lái)的第20行負(fù)責(zé)測(cè)試當(dāng)前的流,其內(nèi)容用tempCntr變量標(biāo)示。如果當(dāng)前流非空,則將其設(shè)置為活躍流(第21行)。然后從該流中讀取數(shù)據(jù)(第22行)。如果讀取的數(shù)據(jù)字不是最后一個(gè)數(shù)據(jù)字(由第24行負(fù)責(zé)檢查),則狀態(tài)機(jī)進(jìn)入M_STREAM狀態(tài),然后輸出來(lái)自該流的剩余數(shù)據(jù)字。在處理完成最后一個(gè)數(shù)據(jù)字后,F(xiàn)SM返回M_IDLE狀態(tài),然后重復(fù)上述過(guò)程。
這個(gè)模塊引入了一個(gè)新的編譯指令,稱為“array_partition”。該編譯指令能讓Vivado HLS了解為了提高吞吐量,是否需要把一個(gè)陣列拆分為多個(gè)子陣列。如果未加設(shè)定,Vivado HLS會(huì)使用雙端口BRAM來(lái)訪問(wèn)陣列。如果要在一個(gè)時(shí)鐘周期中訪問(wèn)陣列兩次以上,如果不適當(dāng)?shù)靥岣叱跏蓟g隔(II)的值,該工具將無(wú)法調(diào)度這些訪問(wèn)。在本例中,略去array_partition編譯指令,將NUN_MERGE_STREAMS值設(shè)為8,就可以讓II=4。但因?yàn)橄肽軌蛟诿總€(gè)時(shí)鐘周期內(nèi)訪問(wèn)steamEmpty陣列的所有元素,讓目標(biāo)II=1,我們需要對(duì)這個(gè)陣列進(jìn)行充分分區(qū)。在本例中,該陣列實(shí)現(xiàn)為一組基于觸發(fā)器的寄存器。
拆分輸入流的過(guò)程耳熟能詳,把來(lái)自一個(gè)流的數(shù)據(jù)字正確地路由到一個(gè)流陣列即可。
6 抽取字段和重新對(duì)齊字段
在包處理中,抽取字段和重新對(duì)齊字段是最基本的操作之一。由于數(shù)據(jù)包一般是經(jīng)過(guò)多個(gè)時(shí)鐘周期內(nèi)通過(guò)總線到達(dá)模塊的,常見(jiàn)的情況是需要的字段要么在它們抵達(dá)的數(shù)據(jù)字中未能對(duì)齊,要么分散在多個(gè)數(shù)據(jù)字中(往往兩種情況都有)。因此要處理這些字段,必須將它們從數(shù)據(jù)流中抽取出來(lái),存入緩存然后重新對(duì)齊以便處理。
例5:源MAC地址抽取示例
1 if (!inData.empty()) {
2 inData.read(currWord);
3 switch(wordCount) {
4 case 0:
5 MAC_DST = currWord.data.range(47, 0);
6 MAC_SRC.range(15, 0) = currWord.data.
range(63, 48);
7 break;
8 case 1:
9 MAC_SRC.range(47 ,16) = currWord.
data.range(31, 0);
10 break;
11 case 2:
12 ……
例5是一個(gè)非常簡(jiǎn)單的字段抽取和再對(duì)齊示例。這個(gè)示例從以太網(wǎng)報(bào)頭中抽取源MAC地址。數(shù)據(jù)通過(guò)稱為“inData”的64位流抵達(dá)。在每個(gè)時(shí)鐘周期讀入數(shù)據(jù)(第2行)。隨后根據(jù)讀取的數(shù)據(jù)字執(zhí)行合適的語(yǔ)句。因此在第5行中源MAC地址的頭16位被抽取出來(lái),并移位到MAC_SRC變量的起始部分。在下一時(shí)鐘周期中,MAC地址的其余32位抵達(dá)總線,然后存入MAC_SRC變量的32位更高位中。
c++相關(guān)文章:c++教程
評(píng)論