指令拓展
在开始本实验之前,请备份 Lab4-1 与 Lab4-2 的所有工程,包括你使用的 IP Core。
在本门课的实验中,你不需要考虑 lh lhu sh lw sw
会跨越字的情况。即我们保证使用的 load/store 类指令操作的数据一定在当前地址的字范围内。
模块实现
在之前的实验中,我们完成了基本的指令,本节实验要在 Lab4-1 & Lab4-2 的基础之上拓展指令。
在本节实验中,你需要实现以下指令:
- R-Type: add, sub, and, or, xor, slt, srl, sll, sra, sltu
- I-Type: addi, andi, ori, xori, srli, slti, slli, srai, sltiu, lb, lh, lw, lbu, lhu, jalr
- S-Type: sb, sh, sw
- B-Type: beq, bne, blt, bge, bltu, bgeu
- J-Type: jal
- U-Type: lui, auipc
即 RISC-V 32I 中除了 ecall, ebreak
外的所有指令,黑体标出的指令为新添加的指令。在开始实验之前,请保证你理解了所有的指令含义与类型。
你可以在 venus 上使用以下代码来理解 sb, sh
的含义:
验收时,你需要提供自己绘制的 CPU datapath 图。(笔者建议在开始本次实验之前就开始绘制,以便更好地理解单周期 CPU 的数据流)你可以参考理论课 PPT 的 datapath 或者其他人的图,但每个人的实现不尽相同,请务必动手绘制属于自己的 datapath。这里以理论课 PPT 的 datapath 为例:

你可以使用纸笔绘制(不推荐)但请保证验收时提供一张线条清晰较少涂改的原理图,也可以使用 Microsoft 的 Visio 或 PowerPoint、在线平台如 ProcessOn、 drawio 或平板软件如 Goodnotes、Notability 绘制。
Tip
- 如果你是基于 Lab4-2 的基础进行实验,你可能需要将 SCPU_ctrl 中输出的控制信号进行修改(如增加位宽、指示当前指令是否为无符号,当前指令是否按字节、半字、字访问),以支持更多的指令,同理 ImmGen 生成立即数的方式可能也需要增加。
- 为了支持 sb、sh 等按字节寻址的指令,你可能需要修改你的存储器 IP 核。对于 RAM 核,需要勾选 Byte Write Enable。同时将 Write Width 改为 32,这样 RAM 的 wea 就会变为 4 位,每一位代表一个字节的写使能。
- 你可以直接将生成的 4 位 wea 信号与 MIO_BUS 产生的信号(MI0_BUS 产生的信号是 1 位的,你需要将其扩充为 4 位)按位与起来,随后送入存储器 IP 核的 wea 端口。
- 在生成 RAM 核时,我们需要取消勾选 Primitives Output Register, 否则每次读取数据需要两个周期的时间,而取消勾选后每次读取数据则只需要一周期的时间。
- 修改 IMem.coe 或 DMem.coe 内容后,需要重新生成对应的存储器 IP 核。
波形仿真
你需要自行编写 SCPU、CtrlUnit 等重要模块的仿真代码,并进行仿真。
SCPU 的仿真与上一节类似,请修改上一节提供的 testbench,使之能够对指令扩展后的 SCPU 进行仿真验证。你可能需要将之前的 wea
信号从 1 位扩展为 4 位或 3 位 以支持扩展指令中的 unsigned, byte, half-word
的要求。我们要求 SCPU 的仿真测试覆盖各类指令。
下板验证
验收时,你需要使用以下验收代码。给出的验收代码是完成验收的最低标准,我们很欢迎对验收代码的补充完善,优秀的测试代码将获得本实验的加分。
验收代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 |
|
memory_initialization_radix=16;
memory_initialization_vector=
00000097,0240006F,00000013,00000013,00000013,00000013,00000013,00000013,00000013,FE5FF06F,FE0090E3,00000863,00000F93,00000F17,FD1FF06F,00100F93,FC0014E3,FC0062E3,FFF00093,0010C193,003181B3,003181B3,003181B3,003181B3,003181B3,003181B3,003181B3,003181B3,003181B3,003181B3,003181B3,003181B3,003181B3,003181B3,003181B3,003181B3,003181B3,003181B3,003181B3,003181B3,003181B3,003181B3,003181B3,003181B3,003181B3,003182B3,005281B3,00318233,00420333,006303B3,00106413,01F06E13,01C3DEB3,00000F17,F3D418E3,00000F17,F27444E3,41C3DEB3,003EFEB3,00000F17,F1D19CE3,00040E93,007EE663,00000F17,F09FF06F,00000013,00200F93,407301B3,40338233,00102493,0041A533,00322533,00000F17,EEA482E3,01E1DE93,009E8663,00000F17,ED5FF06F,00000013,00300F93,0030A513,0012A5B3,0030A633,0FF57513,00B57533,00C57533,00000F17,EA0506E3,0080B533,00000F17,EA0510E3,00343533,00000F17,E8050AE3,0030B513,00000F17,E80514E3,00100593,00B51663,00000F17,E79FF06F,00000013,00400F93,0033E5B3,00658663,00000F17,E61FF06F,00000013,00500F93,02000913,00592023,00492223,00092D83,005DCDB3,00692023,00092E03,01C34DB3,00000F17,E20D98E3,A0000A37,01492423,FEDCBDB7,40CDDD93,00800E13,01CD9DB3,0FFDED93,00B90E83,01DDFDB3,01B92423,00895D83,FFFF0A37,01BA7A33,00000F17,DE0A1AE3,00600F93,00A94E03,00B94E83,008E9E93,01CEEEB3,010E9E93,01DDEEB3,00892E03,00000F17,DDDE16E3,00092023,01B91023,0D000E13,01C90123,00092E03,00D0DEB7,BA0E8E93,00000F17,DBDE14E3,00291D83,0D000E13,00000F17,D9CD9CE3,00700F93,00000F17,D800D6E3,00145663,00000F17,D81FF06F,00000F17,D6107CE3,00000F17,D61478E3,00000A17,2AC00AE7,00000F17,D61FF06F,008A0A13,00000F17,D55A1AE3,66600F93,D4DFF06F;
代码运行与上节验收代码描述基本一致。一切顺利的话,代码将进入 dummy
死循环,同时 x31
寄存器的值为 0x666
;如果出现问题,你可以用 x31
确定指令发生在什么位置之后,同时 0x30
存储了跳转进 dummy
的跳转指令前一个指令的 PC 值,你可以利用这些信息进行错误筛查。
实验要求
- 绘制自己的 CPU datapath 图。
- 根据自己的 datapath实现指令拓展,得到能够支持 RISC-V 32I 中除了
ecall, ebreak
外的所有指令的 CPU。 - 对实现的 CPU 进行仿真验证、下板验证、验收。
- 实验报告里需要包含的内容:
- datapath 图。
- CPU 代码并简要分析(重点部分结合 datapath 和 Verilog 代码解释)。
- 仿真代码与波形截图(注意缩放比例、数制选择合适),并分析仿真波形。