test_instr_decoder.py
1 from amaranth.sim import * 2 3 from transactron.testing import TestCaseWithSimulator, TestbenchContext 4 5 from coreblocks.params import * 6 from coreblocks.params.configurations import test_core_config 7 from coreblocks.frontend.decoder.instr_decoder import InstrDecoder, Encoding, instructions_by_optype 8 from coreblocks.arch import * 9 from unittest import TestCase 10 from typing import Optional, TypeAlias 11 12 13 class TestDecoder(TestCaseWithSimulator): 14 class InstrTest: 15 def __init__( 16 self, 17 encoding, 18 opcode, 19 funct3=None, 20 funct7=None, 21 funct12=None, 22 rd=None, 23 rs1=None, 24 rs2=None, 25 imm=None, 26 succ=None, 27 pred=None, 28 fm=None, 29 csr=None, 30 op=None, 31 illegal=0, 32 ): 33 self.encoding = encoding 34 self.opcode = opcode 35 self.funct3 = funct3 36 self.funct7 = funct7 37 self.funct12 = funct12 38 self.rd = rd 39 self.rs1 = rs1 40 self.rs2 = rs2 41 self.imm = imm 42 self.succ = succ 43 self.pred = pred 44 self.fm = fm 45 self.csr = csr 46 self.op = op 47 self.illegal = illegal 48 49 DECODER_TESTS_I = [ 50 # Arithmetic 51 InstrTest(0x02A28213, Opcode.OP_IMM, Funct3.ADD, rd=4, rs1=5, imm=42, op=OpType.ARITHMETIC), 52 InstrTest(0x003100B3, Opcode.OP, Funct3.ADD, Funct7.ADD, rd=1, rs1=2, rs2=3, op=OpType.ARITHMETIC), 53 InstrTest(0x40418133, Opcode.OP, Funct3.ADD, Funct7.SUB, rd=2, rs1=3, rs2=4, op=OpType.ARITHMETIC), 54 InstrTest(0x001230B7, Opcode.OP_IMM, Funct3.ADD, rd=1, rs1=0, imm=0x123 << 12, op=OpType.ARITHMETIC), 55 # Compare 56 InstrTest(0x07BF2A13, Opcode.OP_IMM, Funct3.SLT, rd=20, rs1=30, imm=123, op=OpType.COMPARE), 57 InstrTest(0x0FFFBA93, Opcode.OP_IMM, Funct3.SLTU, rd=21, rs1=31, imm=0xFF, op=OpType.COMPARE), 58 InstrTest(0x00C5A533, Opcode.OP, Funct3.SLT, Funct7.SLT, rd=10, rs1=11, rs2=12, op=OpType.COMPARE), 59 InstrTest(0x00C5B533, Opcode.OP, Funct3.SLTU, Funct7.SLT, rd=10, rs1=11, rs2=12, op=OpType.COMPARE), 60 # Logic 61 InstrTest(0xFFF04013, Opcode.OP_IMM, Funct3.XOR, rd=0, rs1=0, imm=-1, op=OpType.LOGIC), 62 InstrTest(0x3FF0E093, Opcode.OP_IMM, Funct3.OR, rd=1, rs1=1, imm=0x3FF, op=OpType.LOGIC), 63 InstrTest(0x000FFF13, Opcode.OP_IMM, Funct3.AND, rd=30, rs1=31, imm=0, op=OpType.LOGIC), 64 InstrTest(0x003140B3, Opcode.OP, Funct3.XOR, Funct7.XOR, rd=1, rs1=2, rs2=3, op=OpType.LOGIC), 65 InstrTest(0x003160B3, Opcode.OP, Funct3.OR, Funct7.OR, rd=1, rs1=2, rs2=3, op=OpType.LOGIC), 66 InstrTest(0x003170B3, Opcode.OP, Funct3.AND, Funct7.AND, rd=1, rs1=2, rs2=3, op=OpType.LOGIC), 67 # Shift 68 InstrTest(0x00029793, Opcode.OP_IMM, Funct3.SLL, Funct7.SL, rd=15, rs1=5, imm=0, op=OpType.SHIFT), 69 InstrTest(0x00F2D793, Opcode.OP_IMM, Funct3.SR, Funct7.SL, rd=15, rs1=5, imm=15, op=OpType.SHIFT), 70 InstrTest(0x41F2D793, Opcode.OP_IMM, Funct3.SR, Funct7.SA, rd=15, rs1=5, imm=31, op=OpType.SHIFT), 71 InstrTest(0x019297B3, Opcode.OP, Funct3.SLL, Funct7.SL, rd=15, rs1=5, rs2=25, op=OpType.SHIFT), 72 InstrTest(0x0192D7B3, Opcode.OP, Funct3.SR, Funct7.SL, rd=15, rs1=5, rs2=25, op=OpType.SHIFT), 73 InstrTest(0x4192D7B3, Opcode.OP, Funct3.SR, Funct7.SA, rd=15, rs1=5, rs2=25, op=OpType.SHIFT), 74 # AUIPC 75 InstrTest(0x00777F17, Opcode.AUIPC, rd=30, imm=0x777 << 12, op=OpType.AUIPC), 76 # Jumps 77 InstrTest(0x000000EF, Opcode.JAL, rd=1, imm=0, op=OpType.JAL), 78 InstrTest(0xFFE100E7, Opcode.JALR, Funct3.JALR, rd=1, rs1=2, imm=-2, op=OpType.JALR), 79 # Branch 80 InstrTest(0x00209463, Opcode.BRANCH, Funct3.BNE, rs1=1, rs2=2, imm=4 << 1, op=OpType.BRANCH), 81 InstrTest(0x00310463, Opcode.BRANCH, Funct3.BEQ, rs1=2, rs2=3, imm=4 << 1, op=OpType.BRANCH), 82 InstrTest(0x0041D463, Opcode.BRANCH, Funct3.BGE, rs1=3, rs2=4, imm=4 << 1, op=OpType.BRANCH), 83 InstrTest(0x00524463, Opcode.BRANCH, Funct3.BLT, rs1=4, rs2=5, imm=4 << 1, op=OpType.BRANCH), 84 InstrTest(0x0062F463, Opcode.BRANCH, Funct3.BGEU, rs1=5, rs2=6, imm=4 << 1, op=OpType.BRANCH), 85 InstrTest(0x00736463, Opcode.BRANCH, Funct3.BLTU, rs1=6, rs2=7, imm=4 << 1, op=OpType.BRANCH), 86 # Load 87 InstrTest(0x00B48403, Opcode.LOAD, Funct3.B, rd=8, rs1=9, imm=11, op=OpType.LOAD), 88 InstrTest(0x00C54483, Opcode.LOAD, Funct3.BU, rd=9, rs1=10, imm=12, op=OpType.LOAD), 89 InstrTest(0x00D59503, Opcode.LOAD, Funct3.H, rd=10, rs1=11, imm=13, op=OpType.LOAD), 90 InstrTest(0x00E65583, Opcode.LOAD, Funct3.HU, rd=11, rs1=12, imm=14, op=OpType.LOAD), 91 InstrTest(0x00F6A603, Opcode.LOAD, Funct3.W, rd=12, rs1=13, imm=15, op=OpType.LOAD), 92 InstrTest(0xFFA09703, Opcode.LOAD, Funct3.H, rd=14, rs1=1, imm=-6, op=OpType.LOAD), 93 # Store 94 InstrTest(0x00D70823, Opcode.STORE, Funct3.B, rs1=14, rs2=13, imm=16, op=OpType.STORE), 95 InstrTest(0x00E798A3, Opcode.STORE, Funct3.H, rs1=15, rs2=14, imm=17, op=OpType.STORE), 96 InstrTest(0x00F82923, Opcode.STORE, Funct3.W, rs1=16, rs2=15, imm=18, op=OpType.STORE), 97 # Fence 98 InstrTest( 99 0x0320000F, 100 Opcode.MISC_MEM, 101 Funct3.FENCE, 102 rd=0, 103 rs1=0, 104 succ=FenceTarget.MEM_R, 105 pred=(FenceTarget.MEM_R | FenceTarget.MEM_W), 106 fm=FenceFm.NONE, 107 op=OpType.FENCE, 108 ), 109 # ECALL 110 InstrTest(0x00000073, Opcode.SYSTEM, Funct3.PRIV, funct12=Funct12.ECALL, op=OpType.ECALL), 111 # EBREAK 112 InstrTest(0x00100073, Opcode.SYSTEM, Funct3.PRIV, funct12=Funct12.EBREAK, op=OpType.EBREAK), 113 ] 114 DECODER_TESTS_ZIFENCEI = [ 115 InstrTest(0x0000100F, Opcode.MISC_MEM, Funct3.FENCEI, rd=0, rs1=0, imm=0, op=OpType.FENCEI), 116 ] 117 DECODER_TESTS_ZICSR = [ 118 InstrTest(0x001A9A73, Opcode.SYSTEM, Funct3.CSRRW, rd=20, rs1=21, csr=0x01, op=OpType.CSR_REG), 119 InstrTest(0x002B2AF3, Opcode.SYSTEM, Funct3.CSRRS, rd=21, rs1=22, csr=0x02, op=OpType.CSR_REG), 120 InstrTest(0x004BBB73, Opcode.SYSTEM, Funct3.CSRRC, rd=22, rs1=23, csr=0x04, op=OpType.CSR_REG), 121 InstrTest(0x001FDA73, Opcode.SYSTEM, Funct3.CSRRWI, rd=20, imm=0x1F, csr=0x01, op=OpType.CSR_IMM), 122 InstrTest(0x0027EAF3, Opcode.SYSTEM, Funct3.CSRRSI, rd=21, imm=0xF, csr=0x02, op=OpType.CSR_IMM), 123 InstrTest(0x00407B73, Opcode.SYSTEM, Funct3.CSRRCI, rd=22, imm=0x0, csr=0x04, op=OpType.CSR_IMM), 124 ] 125 DECODER_TESTS_ILLEGAL = [ 126 InstrTest(0xFFFFFFFF, Opcode.OP_IMM, illegal=1), 127 InstrTest(0x003160FF, Opcode.OP, Funct3.OR, Funct7.OR, rd=1, rs1=2, rs2=3, op=OpType.LOGIC, illegal=1), 128 InstrTest(0x000000F3, Opcode.SYSTEM, Funct3.PRIV, funct12=Funct12.ECALL, op=OpType.ECALL, illegal=1), 129 ] 130 DECODER_TESTS_M = [ 131 InstrTest(0x02310133, Opcode.OP, Funct3.MUL, Funct7.MULDIV, rd=2, rs1=2, rs2=3, op=OpType.MUL), 132 InstrTest(0x02341133, Opcode.OP, Funct3.MULH, Funct7.MULDIV, rd=2, rs1=8, rs2=3, op=OpType.MUL), 133 InstrTest(0x02A12233, Opcode.OP, Funct3.MULHSU, Funct7.MULDIV, rd=4, rs1=2, rs2=10, op=OpType.MUL), 134 InstrTest(0x02A43233, Opcode.OP, Funct3.MULHU, Funct7.MULDIV, rd=4, rs1=8, rs2=10, op=OpType.MUL), 135 InstrTest(0x02314133, Opcode.OP, Funct3.DIV, Funct7.MULDIV, rd=2, rs1=2, rs2=3, op=OpType.DIV_REM), 136 InstrTest(0x02745133, Opcode.OP, Funct3.DIVU, Funct7.MULDIV, rd=2, rs1=8, rs2=7, op=OpType.DIV_REM), 137 InstrTest(0x02716233, Opcode.OP, Funct3.REM, Funct7.MULDIV, rd=4, rs1=2, rs2=7, op=OpType.DIV_REM), 138 InstrTest(0x02A47233, Opcode.OP, Funct3.REMU, Funct7.MULDIV, rd=4, rs1=8, rs2=10, op=OpType.DIV_REM), 139 ] 140 DECODER_TESTS_XINTMACHINEMODE = [ 141 # MRET 142 InstrTest(0x30200073, Opcode.SYSTEM, Funct3.PRIV, funct12=Funct12.MRET, op=OpType.MRET), 143 # WFI 144 # InstrTest(0x10500073, Opcode.SYSTEM, Funct3.PRIV, funct12=Funct12.WFI, op=OpType.WFI), 145 ] 146 DECODER_TESTS_XINTSUPERVISOR = [ 147 # SRET 148 InstrTest(0x10200073, Opcode.SYSTEM, Funct3.PRIV, funct12=Funct12.SRET, op=OpType.SRET), 149 # SFENCE.VMA 150 InstrTest(0x12208073, Opcode.SYSTEM, Funct3.PRIV, Funct7.SFENCEVMA, rs1=1, rs2=2, op=OpType.SFENCEVMA), 151 ] 152 DECODER_TESTS_ZBB = [ 153 InstrTest( 154 0x60201013, 155 Opcode.OP_IMM, 156 Funct3.CPOP, 157 funct12=Funct12.CPOP, 158 rd=0, 159 rs1=0, 160 op=OpType.UNARY_BIT_MANIPULATION_5, 161 ), 162 InstrTest(0x40007033, Opcode.OP, Funct3.ANDN, Funct7.ANDN, rd=0, rs1=0, rs2=0, op=OpType.BIT_MANIPULATION), 163 InstrTest( 164 0x60411093, 165 Opcode.OP_IMM, 166 Funct3.SEXTB, 167 funct12=Funct12.SEXTB, 168 rd=1, 169 rs1=2, 170 op=OpType.UNARY_BIT_MANIPULATION_1, 171 ), 172 ] 173 DECODER_TESTS_ZICOND = [ 174 # CZERO RS2 RS1 EQZ RD OP 175 # nez 0b0000111 00000 00000 111 00000 0110011 176 # eqz 0b0000111 00000 00000 101 00000 0110011 177 # CZERO.NEZ 178 InstrTest(0x0E007033, Opcode.OP, Funct3.CZERONEZ, Funct7.CZERO, rd=0, rs1=0, rs2=0, op=OpType.CZERO), 179 # CZERO.EQZ 180 InstrTest(0x0E005033, Opcode.OP, Funct3.CZEROEQZ, Funct7.CZERO, rd=0, rs1=0, rs2=0, op=OpType.CZERO), 181 ] 182 183 DECODER_TESTS_ZBKX = [ 184 # XPERM4 185 InstrTest( 186 0x28002033, Opcode.OP, Funct3.XPERM4, Funct7.XPERM, rd=0, rs1=0, rs2=0, op=OpType.CROSSBAR_PERMUTATION 187 ), 188 # XPERM8 189 InstrTest( 190 0x28004033, Opcode.OP, Funct3.XPERM8, Funct7.XPERM, rd=0, rs1=0, rs2=0, op=OpType.CROSSBAR_PERMUTATION 191 ), 192 ] 193 194 DECODER_TESTS_A = [ 195 InstrTest(0x0821A22F, Opcode.AMO, Funct3.W, Funct7.AMOSWAP, rd=4, rs2=2, rs1=3, op=OpType.ATOMIC_MEMORY_OP), 196 InstrTest( 197 0x0C21A22F, Opcode.AMO, Funct3.W, Funct7.AMOSWAP | 0x2, rd=4, rs2=2, rs1=3, op=OpType.ATOMIC_MEMORY_OP 198 ), 199 InstrTest(0x1812A1AF, Opcode.AMO, Funct3.W, Funct7.SC, rd=3, rs2=1, rs1=5, op=OpType.ATOMIC_LR_SC), 200 ] 201 202 def setup_method(self): 203 self.gen_params = GenParams( 204 test_core_config.replace( 205 _implied_extensions=Extension.G 206 | Extension.XINTMACHINEMODE 207 | Extension.XINTSUPERVISOR 208 | Extension.ZBB 209 | Extension.ZBKX 210 | Extension.ZICOND 211 ) 212 ) 213 self.decoder = InstrDecoder(self.gen_params) 214 self.cnt = 1 215 216 def do_test(self, tests: list[InstrTest]): 217 async def process(sim: TestbenchContext): 218 for test in tests: 219 sim.set(self.decoder.instr, test.encoding) 220 221 assert sim.get(self.decoder.illegal) == test.illegal 222 if test.illegal: 223 return 224 225 assert sim.get(self.decoder.opcode) == test.opcode 226 227 if test.funct3 is not None: 228 assert sim.get(self.decoder.funct3) == test.funct3 229 assert sim.get(self.decoder.funct3_v) == (test.funct3 is not None) 230 231 if test.funct7 is not None: 232 assert sim.get(self.decoder.funct7) == test.funct7 233 assert sim.get(self.decoder.funct7_v) == (test.funct7 is not None) 234 235 if test.funct12 is not None: 236 assert sim.get(self.decoder.funct12) == test.funct12 237 assert sim.get(self.decoder.funct12_v) == (test.funct12 is not None) 238 239 if test.rd is not None: 240 assert sim.get(self.decoder.rd) == test.rd 241 assert sim.get(self.decoder.rd_v) == (test.rd is not None) 242 243 if test.rs1 is not None: 244 assert sim.get(self.decoder.rs1) == test.rs1 245 assert sim.get(self.decoder.rs1_v) == (test.rs1 is not None) 246 247 if test.rs2 is not None: 248 assert sim.get(self.decoder.rs2) == test.rs2 249 assert sim.get(self.decoder.rs2_v) == (test.rs2 is not None) 250 251 if test.imm is not None: 252 if test.csr is not None: 253 # in CSR instruction additional fields are passed in unused bits of imm field 254 assert sim.get(self.decoder.imm.as_signed() & ((2**5) - 1)) == test.imm 255 else: 256 assert sim.get(self.decoder.imm.as_signed()) == test.imm 257 258 if test.succ is not None: 259 assert sim.get(self.decoder.succ) == test.succ 260 261 if test.pred is not None: 262 assert sim.get(self.decoder.pred) == test.pred 263 264 if test.fm is not None: 265 assert sim.get(self.decoder.fm) == test.fm 266 267 if test.csr is not None: 268 assert sim.get(self.decoder.csr) == test.csr 269 270 assert sim.get(self.decoder.optype) == test.op 271 272 with self.run_simulation(self.decoder) as sim: 273 sim.add_testbench(process) 274 275 def test_i(self): 276 self.do_test(self.DECODER_TESTS_I) 277 278 def test_zifencei(self): 279 self.do_test(self.DECODER_TESTS_ZIFENCEI) 280 281 def test_zicsr(self): 282 self.do_test(self.DECODER_TESTS_ZICSR) 283 284 def test_m(self): 285 self.do_test(self.DECODER_TESTS_M) 286 287 def test_illegal(self): 288 self.do_test(self.DECODER_TESTS_ILLEGAL) 289 290 def test_xintmachinemode(self): 291 self.do_test(self.DECODER_TESTS_XINTMACHINEMODE) 292 293 def test_xintsupervisor(self): 294 self.do_test(self.DECODER_TESTS_XINTSUPERVISOR) 295 296 def test_zbb(self): 297 self.do_test(self.DECODER_TESTS_ZBB) 298 299 def test_zicond(self): 300 self.do_test(self.DECODER_TESTS_ZICOND) 301 302 def test_zbkx(self): 303 self.do_test(self.DECODER_TESTS_ZBKX) 304 305 def test_a(self): 306 self.do_test(self.DECODER_TESTS_A) 307 308 309 class TestDecoderEExtLegal(TestCaseWithSimulator): 310 E_TEST = [ 311 (0x00000033, False), # add x0, x0, x0 312 (0x00F787B3, False), # add x15, x15, x15 313 (0x00F807B3, True), # add x15, x16, x15 314 (0xFFF78793, False), # addi x15, x15, -1 315 (0xFFF78813, True), # addi x16, x15, -1 316 (0xFFFFF06F, False), # jal x0, -2 317 (0xFFFFFF6F, True), # jal x30, -2 318 ] 319 320 def test_e(self): 321 self.gen_params = GenParams(test_core_config.replace(embedded=True, _implied_extensions=Extension.E)) 322 self.decoder = InstrDecoder(self.gen_params) 323 324 async def process(sim: TestbenchContext): 325 for encoding, illegal in self.E_TEST: 326 sim.set(self.decoder.instr, encoding) 327 assert sim.get(self.decoder.illegal) == illegal 328 329 with self.run_simulation(self.decoder) as sim: 330 sim.add_testbench(process) 331 332 333 code_type: TypeAlias = tuple[Optional[int], Optional[int], Optional[int], Optional[int]] 334 funct_code_type: TypeAlias = tuple[Optional[int], Optional[int]] 335 336 337 class TestEncodingUniqueness(TestCase): 338 def test_encoding_uniqueness(self): 339 def instruction_code(instr: Encoding) -> code_type: 340 op_code = int(instr.opcode) 341 funct3 = int(instr.funct3) if instr.funct3 is not None else None 342 funct7 = int(instr.funct7) if instr.funct7 is not None else None 343 funct12_5bits = None 344 345 if instr.funct12 is not None: 346 funct7 = (int(instr.funct12) & 0xFE0) >> 5 347 funct12_5bits = int(instr.funct12) & 0x1F 348 349 return (op_code, funct3, funct7, funct12_5bits) 350 351 # prefixes of encoding 352 def code_prefixes(code: code_type) -> list[code_type]: 353 prefixes = [] 354 355 for i in range(3, -1, -1): 356 if code[i] is not None: 357 nt = tuple(list(code[:i]) + [None] * (4 - i)) 358 359 prefixes.append(nt) 360 361 return prefixes 362 363 # known_codes store insformation about already read encodings 364 # if value is Encoding -> there is instruction with given code 365 # if value is None -> there is an instruction with prefix equal to this code 366 known_codes: dict[code_type, Optional[Encoding]] = dict() 367 368 for instructions in instructions_by_optype.values(): 369 for instruction in instructions: 370 code = instruction_code(instruction) 371 prefixes = code_prefixes(code) 372 373 for prefix in prefixes: 374 if prefix in known_codes: 375 encoding = known_codes[prefix] 376 377 # prefix of instruction can not be equal to code of any other instruction 378 assert encoding is None, f"Instruction is not unique: I1 = {encoding} I2 = {instruction}" 379 380 known_codes[prefix] = None 381 382 # current instruction can not be prefix of other instruction 383 self.assertNotIn(code, known_codes, f"Instruction is not unique: I = {instruction}") 384 385 known_codes[code] = instruction 386 387 def test_decoded_distinguishable(self): 388 collisions: dict[OpType, set[Encoding]] = { 389 OpType.ARITHMETIC: { 390 Encoding(Opcode.OP_IMM, Funct3.ADD), 391 Encoding(Opcode.LUI), 392 }, 393 OpType.SHIFT: { 394 Encoding(Opcode.OP_IMM, Funct3.SLL, Funct7.SL), 395 Encoding(Opcode.OP_IMM, Funct3.SR, Funct7.SL), 396 Encoding(Opcode.OP_IMM, Funct3.SR, Funct7.SA), 397 }, 398 OpType.LOGIC: { 399 Encoding(Opcode.OP_IMM, Funct3.XOR), 400 Encoding(Opcode.OP_IMM, Funct3.OR), 401 Encoding(Opcode.OP_IMM, Funct3.AND), 402 }, 403 OpType.COMPARE: { 404 Encoding(Opcode.OP_IMM, Funct3.SLT), 405 Encoding(Opcode.OP_IMM, Funct3.SLTU), 406 }, 407 OpType.SINGLE_BIT_MANIPULATION: { 408 Encoding(Opcode.OP_IMM, Funct3.BCLR, Funct7.BCLR), 409 Encoding(Opcode.OP_IMM, Funct3.BEXT, Funct7.BEXT), 410 Encoding(Opcode.OP_IMM, Funct3.BSET, Funct7.BSET), 411 Encoding(Opcode.OP_IMM, Funct3.BINV, Funct7.BINV), 412 }, 413 OpType.BIT_ROTATION: { 414 Encoding(Opcode.OP_IMM, Funct3.ROR, Funct7.ROR), 415 }, 416 } 417 418 def instruction_code(instr: Encoding) -> funct_code_type: 419 funct3 = int(instr.funct3) if instr.funct3 is not None else 0 420 funct7 = int(instr.funct7) if instr.funct7 is not None else 0 421 422 if instr.funct12 is not None: 423 funct7 = (int(instr.funct12) & 0xFE0) >> 5 424 425 return (funct3, funct7) 426 427 for ext, instructions in instructions_by_optype.items(): 428 known_codes: set[funct_code_type] = set() 429 ext_collisions = collisions[ext] if ext in collisions else set() 430 431 for instruction in instructions: 432 if instruction in ext_collisions: 433 continue 434 435 code = instruction_code(instruction) 436 437 self.assertNotIn( 438 code, known_codes, f"Instruction is not unique within OpType: OpType={ext} I={instruction}" 439 ) 440 441 known_codes.add(code) 442 443 for instruction in ext_collisions: 444 code = instruction_code(instruction) 445 assert code in known_codes, f"Instruction is not colliding: OpType={ext} I={instruction}"