实事求是——简单片上系统的“落实”
在这个方向中,我们提供了将本课程所使用的处理器进行上板实验的方法。我们需要大家通过串口来观察处理器的运行情况,并最终将一个仅支持串口外设的处理器上板实验。
想要完整完成这一部分的难度是比较高的,下面推荐的一系列工作仅仅是推荐实现的方式,且在板上资源允许的情况下,不必要完全按照推荐的方式来实现。
本方向需要使用开发板
如果你需要使用开发板且没有开发板,请联系助教。
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:串口发送信号
对于主存,我们需要使用Xilinx官方提供的IP核。这个IP核可以按照如下方法获得:
- 打开Vivado,找到IP Catalog栏目:
- 双击打开,将Basic栏目中的Interface Type改为AXI4。点击左侧的AXI_SLAVE_S_AXI,打开接口列表,你应该能看到如下内容:
- 把IP核的名字改为你喜欢的名字(这里我修改为了main_mem),打开Port A Options,将Depth设置的足够大(这里我设置为65536,单行宽度为4字节,也就是一共有18位地址空间):
请注意,这个容量需要和你手中的开发板适配,不管如何,这个大小至少要能够容纳你的操作系统。
-
点击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 分支预测器
分支指令一直是处理器需要重点处理的指令。如果我们能提前预知分支指令的跳转情况,那么我们就可以在分支指令执行之前,就将正确的指令加载到流水线中,从而提高处理器的性能。如果你希望实现这部分内容,可以参照这份讲义完成对应的设计。