新闻中心

EEPW首页 > 网络与存储 > 设计应用 > Vivado HLS推动协议处理系统蓬勃发展(下)

Vivado HLS推动协议处理系统蓬勃发展(下)

作者:KimonKarras JamesHrica时间:2015-04-29来源:电子产品世界收藏

  5 流分割和合并

本文引用地址:http://www.eepw.com.cn/article/273295.htm

  在协议处理中,根据协议栈特定字段转发数据包给不同模块,然后在发送前将不同的流重新组合,是一项关键功能。 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”。该编译指令能让 HLS了解为了提高吞吐量,是否需要把一个阵列拆分为多个子阵列。如果未加设定, HLS会使用双端口B来访问阵列。如果要在一个时钟周期中访问阵列两次以上,如果不适当地提高初始化间隔(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++教程




关键词: Vivado FIFO 存储器 RAM C/C++

评论


相关推荐

技术专区

关闭