跳转至

进阶功能提示

给分

进阶功能的实现数量和难度没有统一的评价标准。请不要提问诸如“写多少功能可以拿多少分”、“这个拓展功能值多少分”这种问题。类似问题我们会一律忽略不予回答。可以确定的给分规则已在课堂 ppt 中做了说明,请自行查看。

下面只列出一些单项的进阶功能。

将下面的这些进阶功能组合到一起,也许可以实现一些非常有意思的东西,期待你的创意。

运行速度类

最基本的要求:需要在检查时能够展现出性能差距。记得控制变量,留存未实现功能的版本(可以在 git 仓库里面打 tag 方便检查)。

分支预测

  • 参考 ppt2-8 - 控制冲突和异常
  • 设计你的 BTB 表项,确认查询方式,即如何将 PC 对应到一个或多个表项上(全连接?直接映射?组相联?)
  • PC_MUX 增加新的来自于 BTB 的 next_pc(优先级?)
  • 设计 BTB 内信息(如饱和计数器,跳转目标 PC)的更新方式
  • 修改指令 flush 的条件

指令缓存

  • 参考 ppt3-2 和 ppt3-3 高速缓冲存储器的有关内容
  • 设计你的缓存项,确认查询方式,即如何将 PC 对应到一个或多个表项上(全连接?直接映射?组相联?)
  • 编写逻辑,在 IF 阶段先查询缓存,未命中则再去访问 Wishbone 总线
  • 完成失配逻辑编写
  • 实现 FENCE.I 指令,一种简单的实现方式是清空 icache。注意在清空 icache 之后,后面的指令可能需要重新取指,保证 FENCE.I 指令执行的正确性。
  • 修改监控程序的编译选项,激活 FENCE.I 指令

TLB

  • 请先完成 Page Table Walker 即监控程序 Ver.3 的相关内容
  • 参考 ppt3-4 有关转换旁路缓冲的内容
  • 设计 TLB 表项,确认查询方式,即如何将一个虚地址对应到一个或多个表项上(全连接?直接映射?组相联?)
  • 在内存中查询页表之前,先查 TLB
  • 失配时更新表项
  • SFENCE.VMA 指令,参考:RISC-V Privileged, 4.2.1 Supervisor Memory-Management Fence Instruction, 可以简单实现为清空 TLB

数据缓存

  • 参考 ppt3-2 和 ppt3-3 高速缓冲存储器的有关内容
  • 先想清楚写分配?Write-back 还是 Write-through?
  • 设计缓存项,建立地址和缓存项的对应关系(全连接?直接映射?组相联?)
  • 完成查询和失配逻辑编写
  • 所有地址的数据都能被缓存吗?参考:RISC-V Privileged, 3.6.1 Main Memory versus I/O versus Empty Regions
  • 理解之后,请参考:RISC-V Privileged, 3.6.5 Coherence and Cacheability PMAs 中有关 Cacheability 的内容,设计 PMA Checker 阻止某些地址进入数据缓存。
  • (进阶)如果实现为写回缓存 writeback cache,则需要实现 FENCE.I 指令和 SFENCE.VMA 指令。

    • 如果监控程序修改的是代码部分的内容(例如装载器装入代码),则修改完成之后需要调用 FENCE.I 指令,使得修改部分的内容可以被 IF master(即指令取指阶段的总线 master)所获知。一种简单的实现方式是将 cache 中 dirty 的部分写到 SRAM 当中。
    • 如果监控程序修改了页表,则修改完成之后需要调用 SFENCE.VMA 指令,使得修改部分的内容可以及时被程序所使用。
    • FENCE.I 的一种实现是在写回数据缓存 dcache 的所有 dirty 项之后清空代码缓存 icache,并且从内存中获取更新之后的代码,因此在此之前要保证内存中的数据是最新的。
    • 如果没有 icache,则只清空 dcache 即可。但注意在 FENCE.I 指令之后的其他指令可能需要重新取指,保证 FENCE.I 指令执行的正确性。
    • SFENCE.VMA 的一种实现方式是清空 tlb 缓存,这样 page table walker(在处理器内部查找页表部分的逻辑)就会去内存获得最新部分的页表,同样也需要在此之前要保证内存中的数据是最新的。
    • 注意清空的顺序。应该是先清空 dcache,再清空 icache 和 TLB。
    • 修改监控程序的编译选项,激活 FENCE.I 指令
    • 注意这可能导致部分功能测试不正确,这个是正常现象。但你需要留存一个能够通过功能测试和额外指令测试的版本供实验考试使用。
    为什么 writeback dcache 会导致通过不了测试?

    考虑下面这种情况:

    1. 监控程序 A 指令写入 0x80100000 地址,通过 sw 指令实现。
    2. 写入的指令数据缓存在 dcache 中,没有被 flush 到 SRAM 上。
    3. G 指令跳转到 0x80100000
    4. IF 读取 SRAM,获得写入之前在地址 0x80100000 的数据
    5. 指令执行错误

    这种情况在不使用 FENCE.I 指令时是无法避免的。只有在 A 指令写入 dcache 之后,使用 FENCE.I 指令将 dcache 中内容强行写到 SRAM 中才能解决。

注意:上述的 4 个单元不必实现成复杂的 block ram, 实现成少量寄存器(如 16 项) 已经能够获得明显的速度提升了。

更多流水线段数

  • 将上述的部分功能分割成单独的流水线段,提高最优理论 IPC
  • 更复杂的 stall 和 flush 逻辑
  • 提高处理器主频

外设类

最基本的要求:要能够编写汇编程序操作这些外设。

VGA

  • 参考给的示例 VGA 逻辑,想清楚 VGA 的时序
  • 需要额外的显存(不能使用寄存器,请尝试 Block RAM, xpm memory 或 inferred RAM),并实现 Wishbone 显存控制器接入到 CPU 中
  • 注意 VGA 需要 50M 时钟,这可能与 CPU 的主频不一致。Block RAM 和 xpm memory 的不同端口 可以 跨越时钟域
  • 编写汇编程序,修改显存,尝试显示画面

Flash

  • flash 的型号已经在 thinpad_top.sv 中给出,请自行查阅器件手册
  • 写时序相当复杂,建议只实现读时序
  • 通过云平台写入,当作只读存储器使用
  • 实现只读的 Wishbone Flash 控制器
  • 编写汇编程序访问 flash 获取数据

GPIO

  • 尝试将板子上的按钮,拨码开关,数码管等 I/O 接入到你的 CPU 中
  • 地址映射方式可能类似于串口(这些都是可变的 "寄存器"),需要实现 Wishbone GPIO 控制器
  • (进阶)轮询的方式对于部分演示功能来说可能不合适。中断?修改监控程序和 CPU 支持外部中断
  • (进阶)由于 RISC-V 特权态标准中只有 MIP.MEIP 一位外部中断位,为了将更多输入接入到 CPU 中,可能需要额外实现中断控制器来统一这些信号
    • 指明中断的来源
    • 指明 GPIO 的状态变化
    • 与软件(汇编代码)进行配合,完成 GPIO 的读写功能
  • 编写汇编程序从 GPIO 获取数据,或修改 GPIO,实现对应功能

功能扩展类

最基本的要求:自行编写使用到这些新实现的功能的汇编代码进行展示。

中断和异常

页表和虚拟地址

  • 监控程序 Ver. 3
  • 参考监控程序文档和 页表实现提示 查看有关要求
  • 参考 ppt3-4 有关虚拟地址的相关内容

RISC-V 指令集扩展

  • M/F/A 扩展等
  • 阅读文档,了解扩展对应的指令
  • 运算部件 IP 核的使用,参考 运算部件 一节进行学习
  • 当然也可以根据上课讲述的内容自行实现运算部件加入到 CPU 当中
  • 单纯增加支持的指令数量的意义不大,需要设计一些用到这些新指令的功能 (更改监控程序,编写汇编程序等等)

特权态功能补充

  • 支持更多异常和中断类型
  • PMP
  • S 态
  • uCore
  • 阅读 RISC-V Privileged 文档的相关章节,实现对应的特权态功能
  • 编写汇编程序进行演示

最后更新: 2023年11月30日
作者:Jiajie Chen (3.17%), cuibst (48.41%), 崔轶锴 (48.41%)