跳转至

实事求是——简单片上系统的“落实”

在这个方向中,我们提供了将本课程所使用的处理器进行上板实验的方法。我们需要大家通过串口来观察处理器的运行情况,并最终将一个仅支持串口外设的处理器上板实验。

想要完整完成这一部分的难度是比较高的,下面推荐的一系列工作仅仅是推荐实现的方式,且在板上资源允许的情况下,不必要完全按照推荐的方式来实现。

本方向需要使用开发板

如果你需要使用开发板且没有开发板,请联系助教。

1 消除DPI-C机制

我们在通用寄存器堆和控制状态寄存器堆中,分别使用了两个DPI-C机制来监测处理器行为,以此来实现Difftest。但是,这种机制的代码是不可综合的。因此,我们需要将这两个机制消除。

  • 首先确保你的处理器可以通过Difftest启动我们提供的操作系统。如果不能启动,那么请先修复这个问题。

  • 删除两个寄存器堆中的DPI-C机制代码。

2 裁剪操作系统

我们的操作系统可以加载很多图形化的应用程序,但实现VGA显示会较为困难。我们建议大家修改操作系统,最低要求是使其能够在启动后输出一个字符串。如果你想要实现更多的功能,可以参考我们提供的操作系统的代码,但是请注意,不要使用任何需要VGA显示的功能

在这个基础上,你可以创建一个简单的内核应用程序,通过串口来输入命令。你也可以实现Linux中最基本的命令whoami,来输出当前的用户名。

3 实现串口外设

串口是最简单、最基本的外设。如果想要实现串口外设,你需要完成以下工作:

  • 参考数字电路提高班实验讲义,完成一个简单的串口外设。

  • 在电脑上安装串口软件,如使用Linux,可以使用功能minicom;使用Windows,可以使用MobaXterm。

  • 我们访问串口使用的是AXI总线协议,因此你需要参照仿真环境中的pmem_write函数,使串口单元可以对AXI总线进行响应。

  • 在Vivado中对串口进行单独调试,使其可以正常与电脑端通信。

4 实现访存分配器

在课内的实验中,我们仅仅一个AXI总线的仲裁转接桥,这个转接桥将所有访存请求集中到一起,发送给了存储单元。但是,在我们的约定中,地址最高4位是0xa的请求应该访问外设,因此,在转接桥的外面,仍然需要一个访存分配器,来将外设的访存请求分配到外设上。

这个访存分配器的实现非常简单,你只需要辨别当前发起访存的地址是否是0xa开头,如果是,那么就将所有AXI总线的访存请求发送到串口上,否则,就将访存请求发送到存储单元上。(你甚至不需要使用状态机就可以完成这个工作)

5 使用顶层模块封装CPU、访存分配器、串口和主存

在完成了上述工作之后,你需要将这些模块进行封装,使其可以在开发板上运行。你需要完成以下工作:

  • 将CPU、访存分配器、串口和主存封装到一个顶层模块中。
  • 将串口所需的RXD、TXD信号接入限制文件中。
  • 全部实现后,你的顶层模块应当具有如下IO信号:
    • clk:时钟信号
    • rstn:复位信号
    • RXD:串口接收信号
    • TXD:串口发送信号

image-20231109133519642

对于主存,我们需要使用Xilinx官方提供的IP核。这个IP核可以按照如下方法获得:

  • 打开Vivado,找到IP Catalog栏目:

image-20231107101228315

  • 双击打开,将Basic栏目中的Interface Type改为AXI4。点击左侧的AXI_SLAVE_S_AXI,打开接口列表,你应该能看到如下内容:

image-20231107101411072

  • 把IP核的名字改为你喜欢的名字(这里我修改为了main_mem),打开Port A Options,将Depth设置的足够大(这里我设置为65536,单行宽度为4字节,也就是一共有18位地址空间):

image-20231107101620967

请注意,这个容量需要和你手中的开发板适配,不管如何,这个大小至少要能够容纳你的操作系统。

  • 点击OK后Generate即可,这就是一个符合AXI风格的存储器。需要注意的是,这个AXI存储器的接口名我们已经在课内讲解过,有些接口(例如id)虽然我们在仿真中不需要,但实际上板是需要的,将其固定为0即可。在这里,助教已经帮你整理好了CPU顶层应该具有的接口:

    input  logic [ 0:0] clk,
    input  logic [ 0:0] rstn,
    // AR
    output logic [31:0] araddr,
    output logic [ 1:0] arburst,
    output logic [ 3:0] arid,
    output logic [ 7:0] arlen,
    input  logic [ 0:0] arready,
    output logic [ 2:0] arsize,
    output logic [ 0:0] arvalid,
    // AW
    output logic [31:0] awaddr,
    output logic [ 1:0] awburst,
    output logic [ 3:0] awid,
    output logic [ 7:0] awlen,
    input  logic [ 0:0] awready,
    output logic [ 2:0] awsize,
    output logic [ 0:0] awvalid,
    // B
    input  logic [ 3:0] bid,
    output logic [ 0:0] bready,
    input  logic [ 1:0] bresp,
    input  logic [ 0:0] bvalid,
    // R
    input  logic [31:0] rdata,
    input  logic [ 3:0] rid,
    input  logic [ 0:0] rlast,
    output logic [ 0:0] rready,
    input  logic [ 1:0] rresp,
    input  logic [ 0:0] rvalid,
    // W
    output logic [31:0] wdata,
    output logic [ 0:0] wlast,
    input  logic [ 0:0] wready,
    output logic [ 3:0] wstrb,
    output logic [ 0:0] wvalid
    

6 其他选项

这真的只是选做

如果你成功完成了上述所有内容,那么你已经达到了这门课程的终极目标——上板启动SOC。下面这些内容,只是一些额外的工作,如果你有兴趣,可以尝试完成。

6.1 乘法器和除法器的改造

我们的处理器使用了乘号和除号完成了乘法和除法的功能,这样的实现是很耗费资源的。如果你有兴趣,可以尝试使用乘法器和除法器来完成这些功能,你可以参照这份讲义完成对应的设计。

需要注意的是,乘法器可以实现完全的流水化,而除法器则不得不停顿整个流水线。因此在处理除法操作时,你需要停顿整个流水线,直到除法操作完成。当然,你也可以仅仅实现一个高性能乘法器。

6.2 分支预测器

分支指令一直是处理器需要重点处理的指令。如果我们能提前预知分支指令的跳转情况,那么我们就可以在分支指令执行之前,就将正确的指令加载到流水线中,从而提高处理器的性能。如果你希望实现这部分内容,可以参照这份讲义完成对应的设计。