gem5学习(25):用于异构SoC的片上网络模型——Garnet2.0

时间:2024-02-24 10:25:30

目录

一、Invocation

二、Configuration

三、Topology

四、Routing

五、Flow Control

六、Router Microarchitecture

七、Buffer Management

八、Lifecycle of a Network Traversal

九、Running Garnet2.0 with Synthetic Traffic


官网教程:gem5: Garnet 2.0

Garnet2.0是gem5内部的一个详细互连网络模型,是在2009年发表的原始Garnet模型的基础上进行构建的。目前正在开发中,并将定期推送具有更多功能的补丁到gem5中。

其他与Garnet相关的额外补丁和工具支持(不属于存储库),可以在佐治亚理工学院的Garnet页面查看:garnet | Synergy Lab

Garnet2.0是一个在芯片上网络路由器的周期精确微体系结构实现。它通过利用gem5的ruby内存系统模型提供的拓扑和路由基础设施来实现。默认情况下,Garnet2.0采用先进的1周期流水线作为路由器的设计。然而,通过在拓扑中指定任意数量的周期,可以在任何路由器中添加额外的延迟。

此外,Garnet2.0还可以通过在路由器和链路中设置适当的延迟来模拟片外互连网络。这使得Garnet2.0不仅适用于芯片内部的网络模拟,还可以扩展到模拟片外的互连网络。通过调整路由器和链路的延迟,可以模拟片外网络中的传输延迟和信号传播延迟等特性。

相关文件:

  • src/mem/ruby/network/Network.py
  • src/mem/ruby/network/garnet2.0/GarnetNetwork.py
  • src/mem/ruby/network/Topology.cc

一、Invocation

Garnet网络可以通过添加参数“--network=garnet2.0”来实现。

二、Configuration

Garnet2.0使用了Network.py中的通用网络参数:

  • number_of_virtual_networks: 这是最大虚拟网络的数量。实际活动虚拟网络的数量由协议确定。
  • control_msg_size: 控制消息的大小,以字节为单位。默认为8。
  • Network.cc中的m_data_msg_size设置为块大小(以字节为单位)+ control_msg_size。

其他参数在garnet2.0/GarnetNetwork.py中指定:

  • ni_flit_size: flit的大小,以字节为单位。Flit是从一个路由器发送到另一个路由器的信息的粒度。默认为16(=> 128位)。[1使用16作为默认值可以确保控制消息适应1个flit内,数据消息适应5个flits内]。Garnet要求ni_flit_size与network/BasicLink.py中的bandwidth_factor(带宽因子)相同,因为它不模拟网络内的可变带宽,这意味着ni_flit_size应与网络中的链路宽度相匹配。也可以通过命令行参数“--link-width-bits”来设置ni_flit_size的值。
  • vcs_per_vnet: 每个虚拟网络的虚拟通道(VC)数量。默认为4。也可以通过命令行参数“--vcs-per-vnet”进行设置。
  • buffers_per_data_vc: 数据消息类别中每个VC的flit缓冲区数量。由于数据消息占据5个flits,所以该值可以在1到5之间。默认为4,这意味着每个数据VC具有4个flit缓冲区。
  • buffers_per_ctrl_vc: 控制消息类别中每个VC的flit缓冲区数量。由于控制消息占据1个flit,并且一个VC一次只能容纳一条消息,所以该值必须为1。默认为1,这意味着每个控制VC具有4个flit缓冲区。
  • routing_algorithm:
    • 0: 基于权重的表(默认值);
    • 1: XY;
    • 2: 自定义。

三、Topology

Garnet2.0是建立在gem5的ruby内存系统模型之上的,利用其提供的拓扑基础设施。

Garnet2.0可以对任意异构的拓扑结构进行建模。

在拓扑文件中,每个路由器都可以被分配一个独立的延迟值,以覆盖默认值。此外,每个链路还有两个可选参数:src_outport和dst_inport,它们是源路由器和目标路由器的输出和输入端口的名称字符串。这些参数可以在garnet2.0内部用于实现自定义的路由算法(例如,在一个网格(Mesh)拓扑中,从西向东的链路的src_outport设置为"west",dst_inport设置为"east")。

以下是一些与网络组件相关的说明:

  • GarnetNetwork:这是顶层对象,用于实例化所有的网络接口(NetworkInterface)、路由器(Router)和链路(NetworkLink)。在Topology.cc中调用方法以添加"外部链路"(external links)连接网络接口和路由器,以及添加"内部链路"(internal links)连接路由器之间。
  • NetworkInterface:每个网络接口(NI)通过一侧的MsgBuffer接口与一个一致性控制器连接,另一侧连接到路由器。每个协议消息都被放入一个包含一个flit控制消息或多个(默认为5个)flit数据消息的缓冲区,并注入到路由器中。多个网络接口可以连接到同一个路由器,例如,在网格(Mesh)拓扑中,缓存和目录控制器通过各自的网络接口连接到同一个路由器。
  • Router:路由器负责输出链路的仲裁和路由器之间的流量控制。
  • NetworkLink:网络链路承载flit,可以分为三种类型:EXT_OUT_(路由器到网络接口),EXT_IN_(网络接口到路由器)和INT_(内部路由器到路由器)。
  • CreditLink:信用链路在路由器之间携带虚拟通道(VC)/缓冲区的信用,用于流量控制。

四、Routing

Garnet2.0是利用gem5的ruby内存系统模型提供路由基础设施的一个组件。

默认情况下,Garnet2.0使用基于确定性表格的最短路径路由算法作为默认的路由算法。该算法会根据预先定义的路由表来确定数据包应该沿着哪条路径进行转发。链路权重可以用于优先考虑某些链路,以便在路由选择时更加灵活。关于如何填充路由表的详细信息可以参考"src/mem/ruby/network/Topology.cc"文件。

自定义路由(Custom Routing):为了模拟自定义的路由算法,例如自适应路由,提供了一个框架来为每个链路命名源出口(src_outport)和目的入口(dst_inport),并在garnet内部使用它们来实现路由算法。例如,在网格(Mesh)拓扑结构中,可以通过将flit沿着“west”输出端口链路发送,直到flit不再具有任何X方向的跳数,然后随机选择剩余链路之一(或根据下一个路由器的VC可用性选择)(有关该实现方式的详细内容可以参考"src/mem/ruby/network/garnet2.0/RoutingUnit.cc"文件中的outportComputeXY()函数)。类似地,可以实现outportComputeCustom()函数,并通过在命令行中添加--routing-algorithm=2来调用。

多播消息(Multicast messages):需要注意的是,模拟的网络不支持硬件内部的多播消息。多播消息会在网络接口处被分解为多个单播消息进行传输和处理。

五、Flow Control

虚拟通道流量控制是在该设计中使用的一种技术。它将通信通道划分为多个虚拟通道,每个虚拟通道可以独立地容纳一个数据包。

在设计中,存在两种类型的虚拟通道,即控制通道和数据通道。控制通道用于传输控制信息,而数据通道用于传输实际的数据。

每个虚拟通道都有一个与之关联的缓冲区,用于存储待传输的数据包。这些缓冲区的深度可以在GarnetNetwork.py文件中进行独立的控制。默认情况下,控制通道的缓冲区深度为1个flit(流片片段),而数据通道的缓冲区深度为4个flit。

除了缓冲区深度之外,数据包的大小也是虚拟通道流量控制中的一个重要参数。在该设计中,控制包的默认大小为1个flit,而数据包的默认大小为5个flit。

六、Router Microarchitecture

在Garnet2.0路由器中,传入的flit经历以下操作:

  1. 缓冲写入(Buffer Write,BW):传入的flit被存储在其所属的虚拟通道(VC)中进行缓冲。

  2. 路由计算(Route Compute,RC):缓冲的flit计算出它应该发送到的输出端口,并将这个信息存储在其VC中。

  3. 交换机分配(Switch Allocation,SA):所有缓冲的flit尝试在下一个周期中为交换机端口预留位置。这个分配过程是可分离的,首先每个输入端使用输入仲裁器选择一个输入VC,并发出一个交换机请求。然后,每个输出端通过输出仲裁器解决冲突,确定哪些请求可以成功分配。在有序虚拟网络中,所有的仲裁器都以队列方式工作,以维持点对点的顺序。对于其他类型的网络,仲裁器采用轮询方式。

  4. VC选择(VC Selection,VS):在交换机分配过程中胜出的flit从其所属的输出端口中选择一个空闲的虚拟通道(VC),如果该flit是一个头部(HEAD)或头尾(HEAD_TAIL)flit。

  5. 交换机遍历(Switch Traversal,ST):在交换机分配成功后,胜出的flit通过交换机进行遍历,从输入端口转发到输出端口。

  6. 链路遍历(Link Traversal,LT):经过交换机遍历后,flit通过链路传输到达下一个路由器。

在默认设计中,缓冲写入(BW)、路由计算(RC)、交换机分配(SA)、VC选择(VS)和交换机遍历(ST)都在同一个周期内完成。链路遍历(LT)则在下一个周期中进行。

注意:如果需要模拟多周期路由器(Multi-cycle Router),可以在拓扑文件中指定每个路由器的延迟,或者修改src/mem/ruby/network/BasicRouter.py中的默认路由器延迟。这样做会使缓冲的flit在路由器中等待(延迟-1)个周期,然后才能参与交换机分配过程。

七、Buffer Management

在每个路由器的输入端口中,存在number_of_virtual_networks个虚拟网络(Vnets),每个虚拟网络包含vcs_per_vnet个虚拟通道(VC)。虚拟通道(VC)可以被认为是在同一输入端口上的不同逻辑通道,用于在路由器内部缓存和传输数据。

对于控制虚拟网络,每个VC具有buffers_per_ctrl_vc(默认为1)个缓冲深度。这意味着每个VC可以同时缓存1个控制消息。

对于数据虚拟网络,每个VC具有buffers_per_data_vc(默认为4)个缓冲深度。这意味着每个VC可以同时缓存4个数据包。

为了维护关于可用VC和每个VC内缓冲区数量的信息,使用信用(Credits)进行传递。Credits是一种表示可用资源的计数器,用于指示虚拟通道中可供使用的空闲VC数量以及每个VC中剩余的缓冲区数量。路由器通过在传输过程中更新Credits来跟踪可用资源的状态,并根据Credits的值进行决策,例如选择可用的VC进行传输。这样可以避免拥塞和资源竞争,以提高网络性能和可靠性。

八、Lifecycle of a Network Traversal

组件之间的交互过程:

  • NetworkInterface.cc::wakeup()
    • 每个网络接口(NI)一端连接着一个一致性协议控制器,另一端连接着一个路由器。
    • 接收来自虚拟网络的一致性协议缓冲区的消息,并将其转换为网络数据包发送到网络中【Garnet2.0在此时添加了捕获网络跟踪的功能(正在开发中)】。
    • 从网络接收flit,提取协议消息,并将其发送到相应虚拟网络的一致性协议缓冲区。
    • 与所连接的路由器一起管理流量控制(即信用)。
    • NI的消费flit/credit输出链路将在全局事件队列中放置,并设置时间戳为下一个周期。事件队列调用消费者的wakeup函数。
  • NetworkLink.cc::wakeup()
    • 从NI/路由器接收flit,并在m_latency个周期的延迟后将其发送到NI/路由器。
    • 每个链路的默认延迟值可以从命令行设置(参见configs/network/Network.py)。
    • 可以在拓扑文件中覆盖每个链路的延迟值。
    • 链路的消费者(NI/路由器)将在全局事件队列中放置,并设置时间戳为m_latency个周期后。事件队列调用消费者的wakeup函数。
  • Router.cc::wakeup()
    • 循环遍历所有的输入单元(InputUnit)并调用它们的wakeup函数。
    • 循环遍历所有的输出单元(OutputUnit)并调用它们的wakeup函数。
    • 调用SwitchAllocator的wakeup函数。
    • 调用CrossbarSwitch的wakeup函数。
    • 当路由器的任何模块【InputUnit(输入单元)、OutputUnit(输出单元)、SwitchAllocator(交换分配器)、CrossbarSwitch(交叉开关)】在本周期内有准备好的flit/credit时,会调用路由器的wakeup函数。
  • InputUnit.cc::wakeup()
    • 如果来自上游路由器的输入flit在本周期准备好,则读取它。
    • 对于头部(HEAD)/头尾(HEAD_TAIL)flit,执行路由计算,并更新虚拟通道(VC)中的路由。
    • 将flit缓冲m_latency-1个周期,并标记为从那个周期起对SwitchAllocation有效。
      • 每个路由器的默认延迟可以从命令行设置(参见configs/network/Network.py)。
      • 可以在拓扑文件中设置每个路由器的延迟(即流水线阶段数)。
  • OutputUnit.cc::wakeup()
    • 如果来自下游路由器的输入credit在本周期准备好,则读取它。
    • 增加相应输出VC状态中的credit。
    • 如果credit携带is_free_signal为true,则将输出VC标记为空闲状态。
  • SwitchAllocator.cc::wakeup()
    • 注意:SwitchAllocator执行VC仲裁和选择。
    • SA-I(或SA-i):循环遍历每个输入端口的所有输入VC,并以轮换的方式选择一个。
      • 对于头部(HEAD)/头尾(HEAD_TAIL)flit,选择一个输入虚拟通道(VC),该输入VC连接的输出端口至少有一个空闲的输出虚拟通道(VC)。
      • 对于BODY/TAIL flit,只选择其输出VC中有credit的输入VC。
    • 为该VC放置一个对输出端口的请求。
    • SA-II(或SA-o):循环遍历所有输出端口,并以轮换的方式,在每个输出端口上选择一个合适的输入VC,并将该VC标记为获胜者。
      • 对于头部(HEAD)/头尾(HEAD_TAIL)flit,执行输出VC分配(outvc allocation),即从输出端口选择一个空闲VC。
      • 对于BODY/TAIL flit,在输出VC中减少一个credit。
    • 从输入VC读取flit,并将其发送到CrossbarSwitch中。
    • 在网络通信中,路由器之间通过发送信号来管理虚拟通道(VC)的流量控制。增加credit的信号是一种用于向上游路由器发送的信号,用于表示该输入虚拟通道(VC)中的可用资源。
      • 对于头尾(HEAD_TAIL)/尾部(TAIL)flit,在credit中标记is_free_signal为true。
      • 输入单元将credit通过credit链路发送给上游路由器。
  • CrossbarSwitch.cc::wakeup()
    • 循环遍历所有输入端口,并将获胜的flit发送到其输出端口的输出链路上。
    • 路由器的消费flit输出链路将在全局事件队列中放置,并设置时间戳为下一个周期。事件队列调用消费者的wakeup函数。
  • NetworkLink.cc::wakeup()
    • 从NI/路由器接收flit,并在m_latency个周期的延迟后将其发送到NI/路由器。
    • 每个链路的默认延迟值可以从命令行设置(参见configs/network/Network.py)。
    • 可以在拓扑文件中覆盖每个链路的延迟值。
    • 链路的消费者(NI/路由器)将在全局事件队列中放置,并设置时间戳为m_latency个周期后。事件队列调用消费者的wakeup函数。

九、Running Garnet2.0 with Synthetic Traffic

(使用合成流量运行Garnet2.0)

Garnet2.0可以独立运行,并通过合成流量进行测试:Garnet合成流量