跳转至

第五阶段:软硬件接口设计

CPU 系统集成

联合实验进展到此时,实验者应当已经基本完成 CPU 的设计与实现,CPU 应当可以执行指令(包括运行监控程序)并往串口打印一些内容了。接下来,实验者需要将完成的 CPU 移植并集成到本实验的实验框架中。考虑到《计算机组成原理》实验仅要求实现 RV32I 中的 19 条指令,若实验者后续进行软件开发时需要使用 C、C++ 或 Rust 等高级语言,为了使得 CPU 能够顺利执行编译器生成的指令,实验者还需要将 CPU 支持的指令集扩充至基本完整的 RV32I(其中,fenceecall 以及 ebreak 是不需要的),共 37 条。

软件配置 MAC 地址及 IP 地址

为了方便使用,路由器不能将地址的配置,特别包括 IP 地址的配置,固定在硬件中。因此,在本节中,实验者需要设计一个简单的软硬件接口,使得软件能够编程修改路由器各个接口地址的配置。此处建议实验者采用普通的 MMIO 方案即可,如下一个小节所述。

软件收发以太网帧

本节中,实验者需要设计一套软硬件接口,使得运行在 CPU 上的软件能够接收转发引擎传递的需要软件处理的以太网帧,以及能够向转发引擎传递需要发送的以太网帧。下文以接收以太网帧为例进行讨论,发送以太网帧是类似的。

软硬件接口常见的设计方案有如下几种:

  1. 普通 MMIO(Memory-Mapped I/O):与《计算机组成原理》实验中的串口非常类似,软件通过统一的内存地址访问硬件接口,执行一次寄存器读操作就读取以太网帧的一个字(若干个字节)。同时,硬件接口提供状态和长度等寄存器。
  2. DMA(Direct Memory Access):软件在内存中提供一块缓冲区,然后通过 MMIO 的方式将缓冲区地址和大小写入硬件接口提供的寄存器,硬件此时开始自动接收以太网帧,并将收到的内容写入上述缓冲区。当一个以太网帧接收完成后,硬件在状态寄存器中提供一个标志位或向 CPU 发送一个中断来通知这一事件,然后 CPU 轮询这一标志位或处理相应的中断。同时,硬件接口还可以提供一些寄存器,用于保存上次接收的以太网帧的长度。
  3. 共享内存:CPU 上的软件和硬件共享一块缓冲区,然后同时访问某种数据结构(如循环队列)。

值得注意的是,在接收承载 IP 分组的以太网帧时,若在地址按 4 字节对齐的缓冲区开始接收,那么考虑到以太网帧的 14 字节头部,IP 分组头部开始于第 14 字节,其地址不是 4 字节对齐的(模 4 余 2),不方便软件后续对 IP 分组的处理。对此,实验者可以从模 4 余 2 的地址处开始接收。

转发引擎部分需要按照 IP 分组头部的目标 IP 地址决定是否将此 IP 分组传递给 CPU 上的软件进行处理。需要软件处理的 IP 分组主要包括 RIP 报文(本实验需要实验者使用软件实现 RIP 协议,并设计软硬件接口)。

软件部分需要不断地接收以太网帧并进行处理,还可以通过串口输出帧的信息。实现软件部分的过程中,实验者需要将高级语言编写的程序编译为可以直接在实验者的 CPU 上执行的机器码,这需要借助链接器脚本、若干编译选项以及一些工具来实现。本实验的实验框架中给出了示例代码。

本节实验的过程中,实验者可能会发现其 CPU 实现存在若干个问题,或者不支持某些新指令。前者一般可以通过仔细阅读代码找到并修复,而后者一般可以通过修改编译选项解决。

思考

  1. 哪种软硬件接口设计方案性能较好?
  2. 如何从 C 源代码生成可以直接在实验者的 CPU 上运行的机器码?
  3. 链接器脚本起到什么作用?为什么需要链接器脚本?

转发表软硬件接口

在介绍转发表的软硬件接口前,本节首先区分路由表以及转发表的概念。路由表(Routing Table,也称作 RIB,Routing Information Base)主要供(软件的)路由协议进行路由信息交换以及路由选择。软件处理路由信息并选择得到最佳路由后,会生成转发表(Forwarding Table,也称作 FIB,Forwarding Information Base)供(硬件的)转发引擎进行高效转发。此外,路由表相比转发表多了许多路由协议相关的字段,以 RIPng 路由协议为例,其路由表比转发表多了 metric 等字段,硬件事实上不需要这些额外的字段即可进行转发。请注意,在本实验中,实验者可以不必严格区分这两个表,若在实现上方便且高效,它们可以实现为同一个表,但本节后文的描述仍然会对其进行概念上的区分。

本实验中,软件实现的 RIPng 路由协议交换路由表信息,并在路由选择后生成转发表。为此,实验者需要设计并实现转发表的软硬件接口,使得软件能够读写硬件的转发表(事实上,最少只需要支持写操作)。例如,当路由表被 RIPng 路由协议更新后,软件需要生成新的转发表并更新至硬件转发引擎的转发表中。若转发表的接口支持读操作,软件就可以读取并验证转发表是否正确。

如果转发表基于 FPGA 片内的 RAM 实现,实验者可以直接为 CPU 提供该 RAM 的访问接口(类似共享内存),这样软件即可较为简单地访问转发表并操作数据结构了。此外,实验者的设计方案中,软件路由协议和硬件转发引擎可能会同时对转发表进行写和读。此时,在软件结束写操作前,转发表中的某些数据可能不正确,转发可能出错。若实验者希望进行“无缝”的转发表更新,可以实现两份转发表:转发引擎使用一份,而软件进行写操作时使用另一份。当软件写操作结束后,其即可原子地交换这两个转发表(的指针)。然而,容易发现,这种双转发表的设计会使用更多的存储空间。相对简单地,实验者还可以确保查询转发表的状态机或流水线在转发表某些数据不正确时依然能够给出某些结果(garbage in, garbage out),在牺牲几个 IP 分组转发正确性的情况下绕过该问题。

RIPng 路由协议实现

实验者实现上述软硬件接口后,可以开始实现 RIPng 路由协议,同时通过设计好的软硬件接口来收发 RIPng 报文以及更新转发表。若实验者选择软件实现 ND 协议处理,那么实验者还需要操作邻居缓存。此时,建议实验者参考软件 RIPng 路由器实验文档(左侧菜单的“软件实验”—“第二阶段:个人实验”—“RIPng”)中的相关内容。

其中,相比于软件 RIPng 路由器,本实验需要实现的功能略有区别。总结而言,以下的功能对于本实验是必需的:

  1. 系统启动时初始化地址配置以及直连路由。
  2. 周期性地向所有接口发送组播 RIPng Response。
  3. 对收到的 RIPng Request 生成单播 RIPng Response 进行回复。
  4. 发送 RIPng Response 时,实现水平分割(Split Horizon)和反向投毒(Poison Reverse)。
  5. 发送 RIPng Response 时,若路由条数很多,需要根据 MTU 切分 RIPng 报文。
  6. 接收 RIPng Response 后,对路由表和转发表进行维护,处理 metric 为 16 的情况。
  7. 实现对于每条路由表项的超时(timeout)定时器。请注意,这项功能在软件 RIPng 路由器中是可选的,但在本实验中是必需的。

另请参见:RFC 2080: RIPng for IPv6


最后更新: 2023年11月16日
作者:Jiajie Chen (6.9%), Wende Tan (62.07%), Harry Chen (24.14%), Bruce (6.9%)

评论