IntervalZero RTX64 4.x 帮助文档

⌘K
  1. 主页
  2. 文档
  3. IntervalZero RTX64 4.x 帮助...
  4. RTX64 实时网络
  5. RTX64 网络抽象层(NAL)

RTX64 网络抽象层(NAL)

现代以太网适配器包含传统 TCP/IP 栈无法提供的功能。例如,Intel i210 支持第 2 层协议,比如 IEEE 802.1AS(时间同步)和 802.1Qav(音频视频桥接),以及硬件支持 IEEE 802.1Qat(流预留协议)和 IEEE 1588/802.1AS(时间戳)。很多以太网适配器还支持多个优先级队列,使得在硬件层,就可以将时间敏感的流量与其他流量隔离。

越来越多的嵌入式应用程序可以从这些功能中受益。音频-视频流,低延迟数据共享,以及优先级隔离只是其中的一部分。对大部分此类应用程序来说,最关键的是 LAN 上计算机之间的时间同步。通过 IEEE 802.1AS 时间同步,LAN 上得任何两个节点都能创建精确到 ±500ns 内的公共时钟。这些功能需要一个新的实时界面来访问。

利用这些硬件功能,以及更快的以太网第 2 层接口,IntervalZero 开发了 RTX64 网络抽象层 (NAL)。RTX64 NAL 是一个网络层,它从上层协议栈中抽象出网络硬件和驱动功能,并为上层提供管理接口来查询和使用可用的网络资源。NAL 是一个独立于 TCP/IP 栈的协议层。使用 NAL,可以更高效的使用 EtherCAT,TSN(时间敏感网络)和 PTP(精确时间协议)等网络功能。

例如,用户可以调用驱动程序中的发送功能,一次发送多个数据包。这极大地提高了性能,并允许以接近线速的速度传输小数据包。驱动程序将引导数据包通过指示的优先级队列传输,并使用扩展信息(如数据包的实际传输时间)回调调用者。


网络抽象层(NAL)架构

网络抽象层(NAL)是从上层协议栈中抽象出网络硬件和驱动的网络层。 它为上层提供管理接口来查询和使用可用的网络资源,并借鉴了 EtherCAT,TSN(时间敏感网络)和 PTP(精确时间协议)等网络功能。NAL 是独立于 RT-TCP/IP 栈(RT-TCP/IP Stack)的协议层。

下图展示了 NAL 的架构及其应用示意:


主要组成部分

网络抽象层 NAL

RTX64 网络抽象层具有双重用途。首先,它从上层应用程序中抽象出 NIC 设备的详细信息。NAL 通过为实时应用程序提供了 RtNal 接口,来查询哪些 NIC 设备和队列可用。

基于此信息,应用程序可以选择最能满足其需求的网络资源。选定之后,应用程序就可以使用 RtNal 接口保留该设备,并适时连接。

NAL 的第二个用途,是用来托管实际的网络接口驱动(NIC 驱动程序)。 由于硬件驱动程序与用户应用程序完全分离,最大限度的提升了系统的稳定性。

作为一个抽象层,NAL 实际上也是一个实时进程,它在启动时就会加载所有已配置的驱动程序,并通过 RtNal 接口向实时应用程序提供相关信息。

RT-TCP/IP 栈作为 NAL 客户端应用程序运行,占用接口的一个接收队列和一个发送队列。协议栈的接收队列和发送队列默认使用相同的编号。剩余的接口队列可供其他 NAL 客户端应用程序使用。

注意:如果 RT-TCP/IP 栈的一个或两个队列需要的接口被另一个 NAL 应用程序使用,则协议栈将无法启动,并返回错误代码 ERROR_ACCESS_DENIED

网络接口驱动程序

网络接口驱动程序是由 NAL 加载和初始化的 RTDLL。驱动程序函数(RTN 和 RTND API)在 NAL 以及附加到 NAL 的实时应用中执行。


理解调用顺序

RtNAL NIC 驱动程序通过 RTX64 NAL 进程加载,在 NAL 和驱动程序之间有一个已定义好的接口。其初始化,发送和接收的顺序如下所示。以下示例使用 RtNalIGB.rtdll 驱动程序。

注意:在下图中,红色表示 NAL 上下文灰色表示 NAL 应用程序的上下文

初始化

RTX64 NAL 进程加载 NIC 卡对应的 RTDLL(驱动程序)。在下图中,RtNal 加载并初始化 RtNalIGB.rtdll

  1. 从调用 RtndInitializeInterface 开始,NAL 必须对 NIC 驱动程序进行正确的调用,以正常初始化 NIC 硬件;
  2. 当驱动程序收到此调用时,它必须找到 PCI/PCIe 总线上的 NIC设备。为此,驱动程序将在 RtndInitializeInterface 或 RTX64 NAL 进程的上下文中,调用实时网络库函数 RtnEnumPciCardsRtndInitializeInterface 将驱动程序的配置信息返回给 NAL。NAL 应用程序再通过调用RtNalEnumInterfaceInfo 来查询实际的接口配置;
  3. 接下来,RTX64 NAL进程调用 RtndConfigure 对硬件进行初始化和配置;

下图的重点在网络应用程序(Network Application)上。它展示了 RTX64 中最简单的网络应用。网络应用程序将查询 NAL 队列,并至少选择一个使用。然后,网络应用程序在处理完这些队列后,将它们释放。注意,当网络应用程序终止时,NAL 会释放尚未释放的队列。下图描述了网络应用与 NAL(最终指定网卡关联的队列)的交互。

发送单个数据包

网络应用程序在数据链路层发送以太网数据包。数据链路层,或称为第 2 层,是指计算机网络七层 OSI 模型中的第二层。

NAL 应用可以按照自己的格式为数据包分配内存。这些内存中除了数据包缓冲区之外,还可以存储其他内部数据。NAL 应用可以将指向该数据包内存的指针传递给 RtNalTransmit 调用,该调用来自驱动程序的 RtndTransmit API。接着,驱动程序调用 RtnDecodePacket,待数据包解码完成后,再回调 fpRtnDecodePacket,获取指向要发送的以太网数据包的数据缓冲区(及其大小)指针。fpRtnDecodePacket 通过 RtNalConfigureQueue API 配置。然后驱动程序将数据复制到之前为 DMA 事务分配的缓冲区。最后将新数据传送到 NIC 并返回。

发送多个数据包

RtNalTransmitEx 函数可以在单个调用一次发送多个以太网帧。应用程序必须通过调用 RtNalAllocateFrame 预分配 NAL 帧,并将指向 NAL 帧的指针数组传递给 RtNalTransmitExRtNalAllocateFrame 返回指向 RTNAL_FRAME 结构体的指针,该结构体中包含有以太网帧的虚拟地址和物理地址。应用程序不能更改这些地址。应用程序还可以提供回调,便于发送完成线程中的多个或单个帧调用。该回调指针将被写入RTNAL_FRAME 结构体中。

注意:要使用发送完成回调,需要启用队列的发送完成通知。 RtNalTransmitEx 调用驱动程序中的 RtndTransmitEx 将帧提交给驱动程序。

NAL 拥有提交至驱动程序的全部帧的所有权。在发送完成线程中完成发送后,NAL 将释放帧的所有权。应用程序应当调用 RtNalIsApplicationFrame 函数来查询帧的所有权。

注意:当 NAL 帧属于 NAL 自有时,应用程序不得修改 RTNAL_FRAME 结构体或帧数据缓冲区。

当队列获取到发送通知时,NAL 将会为每个队列运行一个额外的传输完成线程。该线程在发送完成的理想处理器上运行,同时为接口配置了传输完成优先级。该线程调用驱动程序导出的 RtndServiceTransmitQueue API。

函数 RtndServiceTransmitQueue 调用 RtnTransmitComplete 的回调,将由 RtNalTransmitEx 传输的数据包的所有权转移回应用程序。最后调用应用程序传输完成回调。

如果应用程序没有为队列配置发送完成通知,那么发送的数据包将在驱动程序的发送完成线程中处理。该线程还会将 RtNalTransmitEx 发送的数据包的所有权传输给应用程序,但并不调用应用程序回调。

注意:在下图中,红色表示 NAL 上下文灰色表示 NAL 应用程序的上下文

接收数据包

网络应用程序还可以在数据链路层(第 2 层)接收以太网数据包。要实现该操作,需要创建一个线程,用来等待收到多个数据包的信号。当 NIC 生成收到多个数据包的中断指示时,驱动程序的中断服务线程 (IST:Interrupt Service Thread) 通过调用 RtnNotifyRecvQueue 通知正在等待信号的网络应用程序。应用程序的接收线程应调用 RtNalGetReceivePacketCount,以获取有多少数据包正在等待,然后循环调用 RtNalReceivePacketRtNalReceivePacket 调用驱动程序的 RtndReceive 函数来检索等待的数据包。驱动程序调用 RtnGetPacket,后者又调用应用程序已注册的获取数据包回调,以便从应用程序检索数据包。然后,驱动程序调用 RtnDecodePacket,后者又调用应用程序已注册的解码数据包回调,以获取指向应用程序数据区域的指针。最后,驱动程序将数据从 DMA 缓冲区复制到应用程序数据缓冲区并返回。

指向应用程序中获取数据包和解码数据包回调的指针,通过 RtNalConfigureQueue 函数的功能结构体中提供。

NAL 应用程序可以循环调用 RtNalGetReceivePacket,直到失败并出现错误代码 ERROR_NO_DATA ,而不是使用 RtNalGetReceivePacketCount。RT-TCP/IP 使用这种方法。

在回调中接收数据包

指向应用程序接收数据包回调的指针,可以传递给 RtNalConfigureQueue 的结构体 RTNAL_QUEUE_CAPABILITIES,再由 fpRtnReceiveCallback 传递给驱动程序。如果应用程序使用 RtNalReceiveWithCallback 或接收轮询计时器,则必须向回调函数提供一个非零指针。

应用程序接收数据包回调函数直接从驱动程序中的 RtndReceiveWithCallback 调用。RtNalReceiveWithCallback 不访问应用程序定义的数据包结构,也不复制数据。应用程序必须在接收数据包回调中处理该数据包。

注意:在下图中,红色表示 NAL 上下文灰色表示 NAL 应用程序的上下文

如果应用程序使用轮询定时器,则必须在没有接收通知的情况下获取接收队列,并且不运行接收线程。轮询定时器可以通过函数 RtNalEnableReceivePolling 启动,并通过函数 RtNalDisableReceivePolling 停止。轮询定时器处理程序调用驱动程序的函数 RtndReceiveWithCallback