Vivado HLS推动协议处理系统蓬勃发展(下)
5 流分割和合并
本文引用地址:https://www.eepw.com.cn/article/273295.htm在协议处理中,根据协议栈特定字段转发数据包给不同模块,然后在发送前将不同的流重新组合,是一项关键功能。Vivado HLS允许使用高级架构来推动这一转发过程,具体如例4中所示的流合并。
例4:简单的流合并情况
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 }
本例体现的是模块合并功能的使用,其中一个流阵列作为输入(inData),一个单流作为输出(outData)。这个模块的功能是以无区别的方式从输入流读取数据,然后将读取的数据输出给输出流。该模块采用双级FSM实现,其结构与前文介绍的结构一致。
FSM的第一个状态用于确保选择输入流的无区别性(fairness)。实现的方法是使用循环算法检查队列。该算法在完成上一队列的访问之后,即从下一队列起查找新的数据。第17到19行的代码采用的即是此循环算法。常量NUM_MERGE_STREAMS用于设定待合并的流的数量。接下来的第20行负责测试当前的流,其内容用tempCntr变量标示。如果当前流非空,则将其设置为活跃流(第21行)。然后从该流中读取数据(第22行)。如果读取的数据字不是最后一个数据字(由第24行负责检查),则状态机进入M_STREAM状态,然后输出来自该流的剩余数据字。在处理完成最后一个数据字后,FSM返回M_IDLE状态,然后重复上述过程。
这个模块引入了一个新的编译指令,称为“array_partition”。该编译指令能让Vivado HLS了解为了提高吞吐量,是否需要把一个阵列拆分为多个子阵列。如果未加设定,Vivado HLS会使用双端口BRAM来访问阵列。如果要在一个时钟周期中访问阵列两次以上,如果不适当地提高初始化间隔(II)的值,该工具将无法调度这些访问。在本例中,略去array_partition编译指令,将NUN_MERGE_STREAMS值设为8,就可以让II=4。但因为想能够在每个时钟周期内访问steamEmpty阵列的所有元素,让目标II=1,我们需要对这个阵列进行充分分区。在本例中,该阵列实现为一组基于触发器的寄存器。
拆分输入流的过程耳熟能详,把来自一个流的数据字正确地路由到一个流阵列即可。
6 抽取字段和重新对齐字段
在包处理中,抽取字段和重新对齐字段是最基本的操作之一。由于数据包一般是经过多个时钟周期内通过总线到达模块的,常见的情况是需要的字段要么在它们抵达的数据字中未能对齐,要么分散在多个数据字中(往往两种情况都有)。因此要处理这些字段,必须将它们从数据流中抽取出来,存入缓存然后重新对齐以便处理。
例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是一个非常简单的字段抽取和再对齐示例。这个示例从以太网报头中抽取源MAC地址。数据通过称为“inData”的64位流抵达。在每个时钟周期读入数据(第2行)。随后根据读取的数据字执行合适的语句。因此在第5行中源MAC地址的头16位被抽取出来,并移位到MAC_SRC变量的起始部分。在下一时钟周期中,MAC地址的其余32位抵达总线,然后存入MAC_SRC变量的32位更高位中。
c++相关文章:c++教程
评论