Report: AI+X Hardware NoC Acceleration (zh-cn)

1 NoC的整体架构介绍1.1 网络设计总览本组采用的片上网络(NoC)采用的是二维网格式架构,每个节点与其上、下、左、右的节点可以进行通讯,同时也有本地的数据收发口。节点与节点之间的通讯容量为34位数据线+vld信号+rdy信号,总宽度为36位;采用的网络规模为2*2的网络,即一共有4个节点,但由于各个节点均严格遵守握手协议(将在后文介绍),我们也可以很方便地拓展这个网络。

1 NoC的整体架构介绍

1.1 网络设计总览

本组采用的片上网络(NoC)采用的是二维网格式架构,每个节点与其上、下、左、右的节点可以进行通讯,同时也有本地的数据收发口。节点与节点之间的通讯容量为34位数据线+vld信号+rdy信号,总宽度为36位;采用的网络规模为2*2的网络,即一共有4个节点,但由于各个节点均严格遵守握手协议(将在后文介绍),我们也可以很方便地拓展这个网络。

image-20220909004657357

1.2 数据传输协议

我们称每个周期传输的34位数据为一个微片,多位数据的传输则通过包含多个连续微片的数据包的一个传输任务来完成的。在网络中,我们总是保证同一个传输任务的多个微片是连续传输的,因此我们需要各种微片来完成以下的功能:

  • 头微片(head):每个传输任务的第一个微片,其中包含数据地址、目标节点等metadata。
  • 中间微片(body):用于传输数据
  • 尾微片(tail):用于传输最后一份数据,并提示传输任务已经完成

这就要求我们对微片的格式做出约定:

image-20220909004615530

而在各个节点,需要我们针对不同的微片做出不同的反应:

  • 头微片:计算路由,决定是否开放目标方向
  • 中间微片、尾微片:将输入方向直接接通到上一周期开放的方向

1.3 传输握手协议

valid信号(简称vld)随数据一起发送,指示当前数据是否有效;ready信号(简称rdy)向回发送,指示后续模块是否准备好接受前面模块下一个周期的输入。若ready信号为0时,下一周期传输的数据应当保持不变;ready信号为1时,下一周期应当传输新的数据。

2 节点设计

为了实现上面提到的功能,我们将单个节点进行如下的设计:

2.1 路由计算模块

在每个节点,面对五个输入方向各有一个路由计算模块。这一模块由纯组合逻辑电路实现,输入为对应方向上接收到的微片,输出为指示输出方向的5位hot bit。例如,dst[n]指示路由结果是否为北方,以此类推。

在这一模块中,我们首先分析进入微片的类型,若为中间或尾微片则保持上一周期的输出,若为头微片则解析出其目标节点的XY坐标,使用先比较X坐标后比较Y坐标的方法计算出期望的输出方向。

if (X_target < X_current)
   direction = west;
else if (X_target > X_current)
   direction = east;
else // X_target == X_current
if (Y_target < Y_current)
    direction = north;
else if (Y_target > Y_current)
   direction = south;
else // (X,Y)_target == (X,Y)_current
   direction = local;
end

image-20220909002058473

2.2 仲裁模块

在每个节点,面对五个输出方向各有一个仲裁模块。这一模块也由纯组合逻辑电路实现,输入为五个输入方向的路由计算结果,各个方向指示微片类型的位,和输入微片的有效性,而输出则指示这一方向应满足哪个方向输入的仲裁结果,同样由5位的hot bit组成。例如,sel[e]表示当前仲裁器对应的输出方向是否选择北方作为其输入方向。

具体的仲裁算法如下:

  1. 当某个方向输入微片类型为body或tail,输入有效且路由计算结果支持当前输出方向时,无条件对其开放;
  2. 若1中未开放,从最优先方向priority开始,依次判断是否为有效的head微片且路由结果是否为当前的输出方向,若都是则对其开放;
  3. 更新最优先方向。

其中优先方向的判断顺序和更新顺序均为north-east-south-west-local循环,这样可以避免某个方向的请求始终得不到满足。

image-20220909002602294

2.3 交叉矩阵模块

这一模块根据仲裁模块给出的选择信号,将对应的输入输出方向接通。其中既包含数据的接通,也包含正向vld信号的接通以及反向rdy信号的接通。其中,sel是集合了来自五个仲裁器的信号,例如sel_n[e]就指示(输出方向)北面是否选择东面作为其输入方向。

image-20220909002856510

各个输出信号的具体计算如下:

case (sel_n)
  north: begin dout_n <= din_n; out_vld_n <= in_vld_n; end // north = 00001
  east:  begin dout_n <= din_e; out_vld_n <= in_vld_e; end // east  = 00010
  south: begin dout_n <= din_s; out_vld_n <= in_vld_s; end // south = 00100
  west:  begin dout_n <= din_w; out_vld_n <= in_vld_w; end // west  = 01000
  local: begin dout_n <= din_l; out_vld_n <= in_vld_l; end // local = 10000
  default:  begin  dout_n <= dout_n; out_vld_n <= out_vld_n; end
endcase
assign in_rdy_n = (~in_vld_n) | ( (out_rdy_n | ~(sel_n[n])) & (out_rdy_e | ~(sel_n[n])) & ... & (out_rdy_l | ~(sel_l[n]) );  // 若某个方向接受了n方向的有效数据,且输出阻塞,或者n方向输入有效但没有输出方向接受的话,则要阻塞n方向的输入

2.4 输入缓存

在每个节点的输入输出方向,我们还加入了一个输入缓存模块,主要是基于以下几点原因考虑:

  1. 如果没有输入缓存模块,则在节点中,输入阻塞信号in_rdy由一个以各个方向输出阻塞信号out_rdy作为输入的纯组合逻辑电路所计算,在环形网络中这会形成组合逻辑循环(combinational loop)。为此,应该考虑由时序的方法计算in_rdy信号,在每个节点的反压应当有1个周期的延迟,这就要求我们不得不引入输入缓存模块。
  2. 有了输入缓存模块,数据在网络中的传输也可以做到“走一步等一步”,而不必等整条路径都开通后再进行传输,更好地增进网络的并行度。

为此,我们对于输入缓存的初步设计是使用一个FIFO,具体接线如下(时钟信号和复位信号未画出):

image-20220909133300847

然而,我们在实际调试中发现,调用的FIFO IP核因为未知原因会出现刚开始时无法写入数据的问题,在本报告第5.1部分我们将详细介绍这一问题。

最终出于无奈,我们选择了使用一个寄存器作为替代缓存,用以完成将rdy信号延迟一周期的任务,而对外接口均不变。这样的设计对于网络的容量、并行度等指标都有着一定限制,但好在2*2的mesh网络对于网络并行度的要求并不高,如果在更大的网络中这一问题将更为突出。

2.5 总结

将上述模块拼接起来,我们就得到了一个节点的基本架构:(这里对外的输入输出端口都应该分五个方向,没能一一标出)

image-20220909143456363

此外,再加入初始化端口用以输入并存储当前节点的坐标,我们就完成了整个节点的搭建。经过验证这一架构可以在100MHz的时钟频率下运行。

2.6 串-并行转换器

截止于此,经过Vivado分析我们的网络部分可以满足100MHz的运行频率,远高于乘法器模块的频率。但是这样的设计中每周期只能传32位的数据,如果运行频率向下适配的话,传输带宽会形成一个瓶颈。

同时,网络内部的数据微片格式特殊,需要一个模块将输入进来的数据按规范进行封装,在传输完成后再将其复原回原来的形式。

为此,我们引入了并行-串行转换器serializer和串行-并行转换器deserializer,将这两个模块分别安装在每个节点的本地输入输出端口上。前者用于将低频时钟域下的并行数据(带宽可能达到几百位)按照传输协议封装成微片,形成高频时钟域下的串行数据;而后者功能相反,按照传输协议将接收到的微片转译成并行数据,用于后续处理。这样的设计可以让我们的网络有更加简明的对外接口,也能适配不同类型的传输任务。

考虑到卷积计算任务对网络的要求,我们最终将各项指标选择如下:

  • 并行输入位数:固定为768位(即32个int24类型数据)
  • 时钟频率比值:可支持26倍以上的整数倍,需要手动配置;在测试中我们采取的是50倍的频率比,即网络内高频部分100MHz,对外低频部分为2MHz。

image-20220909143155167

image-20220909143301736

最终单个节点的架构如下,上下左右方向均连接至其他节点,左上为本地输入,右下为本地输出。

image-20220909150051147

3 仿真结果与波形解读

3.1 单个节点测试

首先,我们对于单个节点对于数据的处理、收发能力进行了测试。在第一个测试中,我们让五个方向同时传入数据,但目标是不同的方向,以检查节点是否能正确确定路由、并满足多个方向的并行发送。处理结果完美符合预期,每个方向上的数据均在两个周期的延迟(由输入缓存造成)后正确到达对应输出端。

image-20220909152306885

第二个测试中,我们让四个方向的输入均指向同一个方向输出,以测试输入缓存是否正常工作,以及仲裁模块是否能正确处理多个方向排队的情况。经验证,结果也符合预期。

image-20220909153124803

3.2 串-并行转换测试

接下来,我们对串并行转换模块也做了调试。由于这两个模块涉及到两个不同频率的时钟,这样的异步时序模块需要更加仔细的调试。首先是并行-串行转换器serializer:

image-20220909160208300

接下来,将之前产生的串行数据再输入至串行-并行转换器deserializer中,可见这两个模块顺利配合,成功在终点将发送的数据恢复出来:

image-20220909160659625

3.3 网络整体测试

最后,我们对整个网络进行了测试。如下面两图所示,每个节点生成了31份768位的数据以及相应的地址,每一份数据发送至其他的随机节点。若当前输入通道被阻塞,则会一直等待直到通道畅通再发送。

在每个节点的本地输出端口,我们加上了一个验证模块,该模块会检验收到数据的正确性,数据终点是否正确,若均正确则将correct信号的对应位置于1;同时他也会输出表示收到的数据是否为有效的标志,综合来看若correct信号与valid信号始终保持一致则说明所有数据均正确传输;另外,验证模块也内置了一个计数器,计算其收到的正确数据的数量。对每个验证模块的计数器求和,就能直观看到有多少数据已经完成了传输。

第一张图可以看出,各个节点都从0.25微秒的时间开始向外发送数据,已完成的计数初始时为零,并随着时间推进而逐渐增长。

image-20220909162718921

第二张图则是完成时的情况。第四到七行指示的是每个节点已经成功发送出去的数据个数。可以看到虽然并不同步增长,但各个节点的发送进度是大致一致的,这表明网络中不会出现某个节点的数据长时间得不到传输的情形。

在25.25微秒的时刻,可以看到计数器cnt增长到了124,这就说明所有数据均已经正确完成了传输。如果以对外的低频周期(即2MHz,0.5微秒)为单位的话,传输任务共消耗了50个周期,可见在高频网络的帮助下我们可以高效地完成传输任务。

image-20220909163356209

4 资源与性能分析

4.1 资源利用报告

注:BRAM的占用主要是因为测试数据的存储,网络模块实际利用的BRAM应该小于1%。

image-20220909164908366

4.2 性能指标

封装前的内部理论性能:

  • 节点数:4(可扩展)

  • 时钟频率:100MHz

  • 低负载传输延迟:2cycle/hop(s)

  • 数据最大接收率:单个节点32bit/cycle,400MB/s

经封装后对外接口的实际性能:

  • 节点数:4(可拓展)
  • 时钟频率:2MHz
  • 低负载传输延迟:2cycle(固定值)
  • 数据最大接收率:单个节点768bit/cycle,192MB/s

4.3 优势与不足

优点:

  • 传输协议中“微片”的设计让网络可以适配不同大小的传输任务;
  • 节点之间依传输握手协议解耦良好,可以方便地扩展;
  • 节点内各模块分工清晰,易于调试;
  • 实现了串并行转换,这样面对不同的外部输入频率和带宽,特别是外部模块以高并行度、低频率发送数据时,网络依然能保持相对稳定的数据传输速度,从而降低了对于外部输入的要求。

缺点:

  • 由于最终没有使用FIFO作为输入缓存,实际的输入缓存只能存储一份数据,不能很好地发挥网络的性能;
  • 串并行转换器对于高低频率比有较高的要求(一般要求是整数倍),但在写约束文件时我们一般约束的是时钟的周期,需要精确控制频率为整数倍并不容易
  • 网络部分频率仅达到了100MHz,还有继续提升的潜力。

5 Debug过程中遇到的问题

5.1 FIFO初始化后无法写入数据

在调试过程中,我们发现FIFO在初始化后一段时间无法写入数据,会造成传输丢失数据的问题。具体来说,将复位信号置于低电平,写使能信号wr_enin_vld置于高电平,不断写入数据的过程中,FIFO的输出empty信号始终为高,其表现就像是没有写入数据,在后续读取时也无法正常读取数据。

以下是behavior simulation的波形,可以看到45ns时out_rdy信号置于1,此后一个周期FIFO开始输出此前收到的数据,这时一切正常。

image-20220909135540861

但在post synthesis simulation的波形中,可以看到虽然读入了0001和0002两个数据,但是FIFO的empty信号始终置于高位,此后的输出中也表明根本没有读到0001和0002这两个数据。

image-20220909140210777

这是此后的波形,可见115ns后FIFO输出的数据始终为0000000a,也不再输出其他接收到的数据。

image-20220909140652684

这一故障一直无法排除,最终只好放弃使用FIFO作为输入缓存。

6 参考资料

[1]蔡升. 一种二维片上网络路由器的设计实现[J]. 计算机测量与控制, 2019, 27(09): 209-212+217. DOI: 10.16526/j.cnki.11-4762/tp.2019.09.044.