/ test / frontend / test_rvc.py
test_rvc.py
  1  from parameterized import parameterized_class
  2  
  3  from amaranth import *
  4  from amaranth_types import ValueLike
  5  
  6  from coreblocks.frontend.decoder.rvc import InstrDecompress
  7  from coreblocks.arch import *
  8  from coreblocks.params import *
  9  from coreblocks.params.configurations import test_core_config
 10  
 11  from transactron.testing import TestCaseWithSimulator, TestbenchContext
 12  
 13  COMMON_TESTS = [
 14      # Illegal instruction
 15      (0x0000, IllegalInstr()),
 16      # c.addi4spn x15, 1020
 17      (0x1FFC, ITypeInstr(opcode=Opcode.OP_IMM, rd=Registers.X15, funct3=Funct3.ADD, rs1=Registers.SP, imm=1020)),
 18      # c.lw x13, 28(x11)
 19      (0x4DD4, ITypeInstr(opcode=Opcode.LOAD, rd=Registers.X13, funct3=Funct3.W, rs1=Registers.X11, imm=28)),
 20      # c.sw x10, 20(x8)
 21      (0xC848, STypeInstr(opcode=Opcode.STORE, imm=20, funct3=Funct3.W, rs1=Registers.X8, rs2=Registers.X10)),
 22      # c.nop
 23      (0x0001, ITypeInstr(opcode=Opcode.OP_IMM, rd=Registers.X0, funct3=Funct3.ADD, rs1=Registers.X0, imm=0)),
 24      # c.addi x2, -28
 25      (
 26          0x1111,
 27          ITypeInstr(opcode=Opcode.OP_IMM, rd=Registers.X2, funct3=Funct3.ADD, rs1=Registers.X2, imm=-28),
 28      ),
 29      # c.li x31, -7
 30      (
 31          0x5FE5,
 32          ITypeInstr(opcode=Opcode.OP_IMM, rd=Registers.X31, funct3=Funct3.ADD, rs1=Registers.ZERO, imm=-7),
 33      ),
 34      # c.addi16sp 496
 35      (0x617D, ITypeInstr(opcode=Opcode.OP_IMM, rd=Registers.SP, funct3=Funct3.ADD, rs1=Registers.SP, imm=496)),
 36      # c.lui x7, -3
 37      (0x73F5, UTypeInstr(opcode=Opcode.LUI, rd=Registers.X7, imm=Cat(C(0, 12), C(-3, 20)))),
 38      # c.srli x10, 3
 39      (
 40          0x810D,
 41          RTypeInstr(
 42              opcode=Opcode.OP_IMM,
 43              rd=Registers.X10,
 44              funct3=Funct3.SR,
 45              rs1=Registers.X10,
 46              rs2=Registers.X3,
 47              funct7=Funct7.SL,
 48          ),
 49      ),
 50      # c.srai x12, 8
 51      (
 52          0x8621,
 53          RTypeInstr(
 54              opcode=Opcode.OP_IMM,
 55              rd=Registers.X12,
 56              funct3=Funct3.SR,
 57              rs1=Registers.X12,
 58              rs2=Registers.X8,
 59              funct7=Funct7.SA,
 60          ),
 61      ),
 62      # c.andi x9, 17
 63      (0x88C5, ITypeInstr(opcode=Opcode.OP_IMM, rd=Registers.X9, funct3=Funct3.AND, rs1=Registers.X9, imm=17)),
 64      # c.sub x10, x15
 65      (
 66          0x8D1D,
 67          RTypeInstr(
 68              opcode=Opcode.OP,
 69              rd=Registers.X10,
 70              funct3=Funct3.SUB,
 71              rs1=Registers.X10,
 72              rs2=Registers.X15,
 73              funct7=Funct7.SUB,
 74          ),
 75      ),
 76      # c.xor x13, x8
 77      (
 78          0x8EA1,
 79          RTypeInstr(
 80              opcode=Opcode.OP,
 81              rd=Registers.X13,
 82              funct3=Funct3.XOR,
 83              rs1=Registers.X13,
 84              rs2=Registers.X8,
 85              funct7=Funct7.XOR,
 86          ),
 87      ),
 88      # c.or x15, x14
 89      (
 90          0x8FD9,
 91          RTypeInstr(
 92              opcode=Opcode.OP,
 93              rd=Registers.X15,
 94              funct3=Funct3.OR,
 95              rs1=Registers.X15,
 96              rs2=Registers.X14,
 97              funct7=Funct7.OR,
 98          ),
 99      ),
100      # c.and x9, x9
101      (
102          0x8CE5,
103          RTypeInstr(
104              opcode=Opcode.OP,
105              rd=Registers.X9,
106              funct3=Funct3.AND,
107              rs1=Registers.X9,
108              rs2=Registers.X9,
109              funct7=Funct7.AND,
110          ),
111      ),
112      # c.j 2012
113      (0xAFF1, JTypeInstr(opcode=Opcode.JAL, rd=Registers.ZERO, imm=2012)),
114      # c.beqz x8, -6
115      (
116          0xDC6D,
117          BTypeInstr(opcode=Opcode.BRANCH, imm=-6, funct3=Funct3.BEQ, rs1=Registers.X8, rs2=Registers.ZERO),
118      ),
119      # c.bnez x15, 20
120      (
121          0xEB91,
122          BTypeInstr(opcode=Opcode.BRANCH, imm=20, funct3=Funct3.BNE, rs1=Registers.X15, rs2=Registers.ZERO),
123      ),
124      # c.slli x13, 31
125      (
126          0x06FE,
127          RTypeInstr(
128              opcode=Opcode.OP_IMM,
129              rd=Registers.X13,
130              funct3=Funct3.SLL,
131              rs1=Registers.X13,
132              rs2=Registers.X31,
133              funct7=Funct7.SL,
134          ),
135      ),
136      # c.lwsp x2, 4
137      (0x4112, ITypeInstr(opcode=Opcode.LOAD, rd=Registers.X2, funct3=Funct3.W, rs1=Registers.SP, imm=4)),
138      # c.jr x30
139      (
140          0x8F02,
141          ITypeInstr(opcode=Opcode.JALR, rd=Registers.ZERO, funct3=Funct3.JALR, rs1=Registers.X30, imm=0),
142      ),
143      # c.mv x2, x26
144      (
145          0x816A,
146          RTypeInstr(
147              opcode=Opcode.OP,
148              rd=Registers.X2,
149              funct3=Funct3.ADD,
150              rs1=Registers.ZERO,
151              rs2=Registers.X26,
152              funct7=Funct7.ADD,
153          ),
154      ),
155      # c.ebreak
156      (0x9002, EBreakInstr()),
157      # c.add x14, x8
158      (
159          0x9722,
160          RTypeInstr(
161              opcode=Opcode.OP,
162              rd=Registers.X14,
163              funct3=Funct3.ADD,
164              rs1=Registers.X14,
165              rs2=Registers.X8,
166              funct7=Funct7.ADD,
167          ),
168      ),
169      # c.swsp x31, 20
170      (0xCA7E, STypeInstr(opcode=Opcode.STORE, imm=20, funct3=Funct3.W, rs1=Registers.SP, rs2=Registers.X31)),
171  ]
172  
173  RV32_TESTS = [
174      # c.ld x8, 8(x9)
175      (0x6480, IllegalInstr()),
176      # c.sd x14, 0(x13)
177      (0xE298, IllegalInstr()),
178      # c.jal 40
179      (0x2025, JTypeInstr(opcode=Opcode.JAL, rd=Registers.RA, imm=40)),
180      # c.jal -412
181      (0x3595, JTypeInstr(opcode=Opcode.JAL, rd=Registers.RA, imm=-412)),
182      # c.srli x10, 32
183      (0x9101, IllegalInstr()),
184      # c.srai x12, 40
185      (0x9621, IllegalInstr()),
186      # c.subw x10, x11
187      (0x9D0D, IllegalInstr()),
188      # c.addw x15, x8
189      (0x9FA1, IllegalInstr()),
190      # c.slli x13, 63
191      (0x16FE, IllegalInstr()),
192  ]
193  
194  RV64_TESTS = [
195      # c.ld x8, 8(x9)
196      (0x6480, ITypeInstr(opcode=Opcode.LOAD, rd=Registers.X8, funct3=Funct3.D, rs1=Registers.X9, imm=8)),
197      # c.sd x14, 0(x13)
198      (0xE298, STypeInstr(opcode=Opcode.STORE, imm=0, funct3=Funct3.D, rs1=Registers.X13, rs2=Registers.X14)),
199      # c.addiw x13, -12,
200      (
201          0x36D1,
202          ITypeInstr(opcode=Opcode.OP_IMM_32, rd=Registers.X13, funct3=Funct3.ADD, rs1=Registers.X13, imm=-12),
203      ),
204      # c.srli x10, 32
205      (
206          0x9101,
207          RTypeInstr(
208              opcode=Opcode.OP_IMM,
209              rd=Registers.X10,
210              funct3=Funct3.SR,
211              rs1=Registers.X10,
212              rs2=Registers.X0,
213              funct7=Funct7.SL | 1,
214          ),
215      ),
216      # c.srai x12, 40
217      (
218          0x9621,
219          RTypeInstr(
220              opcode=Opcode.OP_IMM,
221              rd=Registers.X12,
222              funct3=Funct3.SR,
223              rs1=Registers.X12,
224              rs2=Registers.X8,
225              funct7=Funct7.SA | 1,
226          ),
227      ),
228      # c.subw x10, x11
229      (
230          0x9D0D,
231          RTypeInstr(
232              opcode=Opcode.OP32,
233              rd=Registers.X10,
234              funct3=Funct3.SUB,
235              rs1=Registers.X10,
236              rs2=Registers.X11,
237              funct7=Funct3.SUB,
238          ),
239      ),
240      # c.addw x15, x8
241      (
242          0x9FA1,
243          RTypeInstr(
244              opcode=Opcode.OP32,
245              rd=Registers.X15,
246              funct3=Funct3.ADD,
247              rs1=Registers.X15,
248              rs2=Registers.X8,
249              funct7=Funct3.ADD,
250          ),
251      ),
252      # c.slli x13, 63
253      (
254          0x16FE,
255          RTypeInstr(
256              opcode=Opcode.OP_IMM,
257              rd=Registers.X13,
258              funct3=Funct3.SLL,
259              rs1=Registers.X13,
260              rs2=Registers.X31,
261              funct7=Funct7.SL | 1,
262          ),
263      ),
264      # c.ldsp x29, 40
265      (0x7EA2, ITypeInstr(opcode=Opcode.LOAD, rd=Registers.X29, funct3=Funct3.D, rs1=Registers.SP, imm=40)),
266      # c.sdsp x4, 8
267      (0xE412, STypeInstr(opcode=Opcode.STORE, imm=8, funct3=Funct3.D, rs1=Registers.SP, rs2=Registers.X4)),
268  ]
269  
270  
271  @parameterized_class(
272      ("name", "isa_xlen", "test_cases"),
273      [("rv32ic", 32, COMMON_TESTS + RV32_TESTS), ("rv64ic", 64, COMMON_TESTS + RV64_TESTS)],
274  )
275  class TestInstrDecompress(TestCaseWithSimulator):
276      isa_xlen: int
277      test_cases: list[tuple[int, ValueLike]]
278  
279      def test(self):
280          self.gen_params = GenParams(
281              test_core_config.replace(compressed=True, xlen=self.isa_xlen, fetch_block_bytes_log=3)
282          )
283          self.m = InstrDecompress(self.gen_params)
284  
285          async def process(sim: TestbenchContext):
286              illegal = Const.cast(IllegalInstr()).value
287  
288              for instr_in, instr_out in self.test_cases:
289                  sim.set(self.m.instr_in, instr_in)
290                  expected = Const.cast(instr_out).value
291  
292                  if expected == illegal:
293                      expected = instr_in  # for exception handling
294  
295                  assert sim.get(self.m.instr_out) == expected
296                  await sim.tick()
297  
298          with self.run_simulation(self.m) as sim:
299              sim.add_testbench(process)
300  
301  
302  ZCB_COMMON_TESTS = [
303      # c.lbu x13, 3(x11)
304      (0x81F4, ITypeInstr(opcode=Opcode.LOAD, rd=Registers.X13, funct3=Funct3.BU, rs1=Registers.X11, imm=3)),
305      # c.lhu x13, 2(x11)
306      (0x85B4, ITypeInstr(opcode=Opcode.LOAD, rd=Registers.X13, funct3=Funct3.HU, rs1=Registers.X11, imm=2)),
307      # c.lh x13, 2(x11)
308      (0x85F4, ITypeInstr(opcode=Opcode.LOAD, rd=Registers.X13, funct3=Funct3.H, rs1=Registers.X11, imm=2)),
309      # c.sb x13, 3(x11)
310      (0x89F4, STypeInstr(opcode=Opcode.STORE, imm=3, funct3=Funct3.B, rs1=Registers.X11, rs2=Registers.X13)),
311      # c.sh x13, 2(x11)
312      (0x8DB4, STypeInstr(opcode=Opcode.STORE, imm=2, funct3=Funct3.H, rs1=Registers.X11, rs2=Registers.X13)),
313      # c.zext.b x13
314      (0x9EE1, ITypeInstr(opcode=Opcode.OP_IMM, rd=Registers.X13, funct3=Funct3.AND, rs1=Registers.X13, imm=0xFF)),
315      # c.not x13
316      (0x9EF5, ITypeInstr(opcode=Opcode.OP_IMM, rd=Registers.X13, funct3=Funct3.XOR, rs1=Registers.X13, imm=-1)),
317      # c.sext.b x13
318      (
319          0x9EE5,
320          ITypeInstr(opcode=Opcode.OP_IMM, rd=Registers.X13, funct3=Funct3.SEXTB, rs1=Registers.X13, imm=Funct12.SEXTB),
321      ),
322      # c.zext.h x13
323      (
324          0x9EE9,
325          RTypeInstr(
326              opcode=Opcode.OP,
327              rd=Registers.X13,
328              funct3=Funct3.ZEXTH,
329              rs1=Registers.X13,
330              rs2=Registers.ZERO,
331              funct7=Funct7.ZEXTH,
332          ),
333      ),
334      # c.sext.h x13
335      (
336          0x9EED,
337          ITypeInstr(opcode=Opcode.OP_IMM, rd=Registers.X13, funct3=Funct3.SEXTH, rs1=Registers.X13, imm=Funct12.SEXTH),
338      ),
339      # c.mul x13, x11
340      (
341          0x9ECD,
342          RTypeInstr(
343              opcode=Opcode.OP,
344              rd=Registers.X13,
345              funct3=Funct3.MUL,
346              rs1=Registers.X13,
347              rs2=Registers.X11,
348              funct7=Funct7.MULDIV,
349          ),
350      ),
351  ]
352  
353  ZCB_RV64_ONLY_TESTS = [
354      # c.zext.w x13 (left illegal until add.uw is supported)
355      (0x9EF1, IllegalInstr()),
356  ]
357  
358  
359  @parameterized_class(
360      ("name", "isa_xlen", "test_cases"),
361      [("rv32imc_zcb_zbb", 32, ZCB_COMMON_TESTS), ("rv64imc_zcb_zbb", 64, ZCB_COMMON_TESTS + ZCB_RV64_ONLY_TESTS)],
362  )
363  class TestInstrDecompressZcb(TestCaseWithSimulator):
364      isa_xlen: int
365      test_cases: list[tuple[int, ValueLike]]
366  
367      def test(self):
368          self.gen_params = GenParams(
369              test_core_config.replace(
370                  xlen=self.isa_xlen,
371                  fetch_block_bytes_log=3,
372                  _implied_extensions=Extension.I | Extension.ZCA | Extension.ZCB | Extension.ZBB | Extension.M,
373              )
374          )
375          self.m = InstrDecompress(self.gen_params)
376  
377          async def process(sim: TestbenchContext):
378              illegal = Const.cast(IllegalInstr()).value
379  
380              for instr_in, instr_out in self.test_cases:
381                  sim.set(self.m.instr_in, instr_in)
382                  expected = Const.cast(instr_out).value
383  
384                  if expected == illegal:
385                      expected = instr_in
386  
387                  assert sim.get(self.m.instr_out) == expected
388                  await sim.tick()
389  
390          with self.run_simulation(self.m) as sim:
391              sim.add_testbench(process)