From 2fe7098fc662b7445768e4cb347f46adfa4a52fe Mon Sep 17 00:00:00 2001 From: shimingyu <115351423+shimingyu1026@users.noreply.github.com> Date: Thu, 14 Aug 2025 11:04:46 +0800 Subject: [PATCH] Pr smy ifu predecode new (#152) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * test * f3predecoder * predecode * predecode * update * 优化细节 * fix * fix * delete some files * update * final * update * Simplify and remove duplicate code * Revert: 撤回在 3a2d830 中的错误修改 * Revert: 撤回在 6a3fa2f 中的错误修改 * Update files * fix bugs of lambda * Fix errors in coverage collection without using lambda --------- Co-authored-by: mingyu shi --- .gitignore | 2 +- .../f3predecoder/agent/f3predecoder_agent.py | 5 + .../f3predecoder/test/f3predecoder_fixture.py | 108 +++++++- .../test/f3predecoder_instr_gen.py | 231 ++++++++++++++++++ .../f3predecoder/test/f3predecoder_test.py | 182 +++++++++++++- .../ifu/predecode/agent/predecode_agent.py | 15 +- .../ifu/predecode/test/predecode_fixture.py | 76 +++++- .../ifu/predecode/test/predecode_instr_gen.py | 168 +++++++++++++ .../ifu/predecode/test/predecode_test.py | 106 +++++++- 9 files changed, 881 insertions(+), 12 deletions(-) create mode 100644 ut_frontend/ifu/f3predecoder/test/f3predecoder_instr_gen.py create mode 100644 ut_frontend/ifu/predecode/test/predecode_instr_gen.py diff --git a/.gitignore b/.gitignore index d19094d..2bb9371 100644 --- a/.gitignore +++ b/.gitignore @@ -173,4 +173,4 @@ cython_debug/ # be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore # and can be added to the global gitignore or merged into this file. For a more nuclear # option (not recommended) you can uncomment the following to ignore the entire idea folder. -#.idea/ \ No newline at end of file +#.idea/ diff --git a/ut_frontend/ifu/f3predecoder/agent/f3predecoder_agent.py b/ut_frontend/ifu/f3predecoder/agent/f3predecoder_agent.py index be5b1b9..789bd0b 100644 --- a/ut_frontend/ifu/f3predecoder/agent/f3predecoder_agent.py +++ b/ut_frontend/ifu/f3predecoder/agent/f3predecoder_agent.py @@ -9,6 +9,10 @@ class F3PreDecodeData(): def __str__(self): return f"brTypes: {self.brTypes}\nisCalls: {self.isCalls}\nisRets: {self.isRets}" + def clear(self): + self.brTypes = [] + self.isCalls = [] + self.isRets = [] class F3PreDecoderAgent(Agent): def __init__(self, bundle:F3PreDecoderBundle): @@ -23,6 +27,7 @@ class F3PreDecoderAgent(Agent): await self.bundle.step() ret = F3PreDecodeData() + ret.clear() for i in range(16): ret.brTypes.append(getattr(self.bundle.io._out_pd, f"_{i}")._brType.value) ret.isCalls.append(getattr(self.bundle.io._out_pd, f"_{i}")._isCall.value) diff --git a/ut_frontend/ifu/f3predecoder/test/f3predecoder_fixture.py b/ut_frontend/ifu/f3predecoder/test/f3predecoder_fixture.py index 5d4de34..8f99120 100644 --- a/ut_frontend/ifu/f3predecoder/test/f3predecoder_fixture.py +++ b/ut_frontend/ifu/f3predecoder/test/f3predecoder_fixture.py @@ -2,11 +2,117 @@ import toffee_test from dut.F3Predecoder import DUTF3Predecoder from toffee import start_clock from ..env import F3PreDecoderEnv +import toffee.funcov as fc +from comm import UT_FCOV, module_name_with + + +grp = fc.CovGroup(UT_FCOV("../../CLASSIC")) + + +def check_function( + attr_name: str, expected_value: int, mask: int = 0xFFFFFFFF, shift: int = 0 +): + def checker(x): + return ((getattr(x, attr_name).value & mask) >> shift) == expected_value + + return checker + + +def init_cov(dut:DUTF3Predecoder, grp: fc.CovGroup): + for i in range(16): + grp.add_cover_point( + dut, + { + "instr is not cfi": check_function(f"io_out_pd_{i}_brType", 0), + "instr is branch": check_function(f"io_out_pd_{i}_brType", 1), + "instr is jal": check_function(f"io_out_pd_{i}_brType", 2), + "instr is jalr": check_function(f"io_out_pd_{i}_brType", 3), + }, + name=f"check_cfi_{i}", + ) + for i in range(16): + grp.add_cover_point( + dut, + { + "instr is RVI.JAL and is call": check_function( + f"io_out_pd_{i}_isCall", 1 + ) + and check_function(f"io_in_instr_{i}", 0b1101111, mask=0b1111111), + "instr is RVI.JAL and is not call or ret": check_function( + f"io_out_pd_{i}_isCall", 0 + ) + and check_function(f"io_out_pd_{i}_isRet", 0) + and check_function(f"io_in_instr_{i}", 0b1101111, mask=0b1111111), + "instr is RVC.JAL and is not call or ret": check_function( + f"io_out_pd_{i}_isCall", 0 + ) + and check_function(f"io_out_pd_{i}_isRet", 0) + and check_function(f"io_in_instr_{i}", 0b01, mask=0b11) + and check_function(f"io_in_instr_{i}", 0b101, shift=13), + }, + name=f"check_jal_{i}", + ) + + for i in range(16): + grp.add_cover_point( + dut, + { + "instr is RVI.JALR and is call": check_function( + f"io_out_pd_{i}_isCall", 1 + ) + and check_function(f"io_in_instr_{i}",0b1100111,mask = 0b1111111), + "instr is RVI.JALR and is ret": check_function( + f"io_out_pd_{i}_isRet", 1 + ) + and check_function(f"io_in_instr_{i}", 0b1100111, mask=0b1111111), + "instr is RVI.JALR and is not call or ret": check_function( + f"io_out_pd_{i}_isRet", 0 + ) + and check_function(f"io_out_pd_{i}_isCall", 0) + and check_function(f"io_in_instr_{i}", 0b1100111, mask=0b1111111), + "instr is RVC.JALR and is not call or ret": check_function( + f"io_out_pd_{i}_isRet", 0 + ) + and check_function(f"io_out_pd_{i}_isCall", 0) + and check_function(f"io_in_instr_{i}", 0b0000010, mask=0b1111111) + and check_function(f"io_in_instr_{i}", 0b1001, shift=12), + }, + name=f"check_jalr_{i}", + ) + + for i in range(16): + grp.add_cover_point( + dut, + { + "instr is RVC.JR and is ret": check_function(f"io_out_pd_{i}_isRet", 1) + and check_function(f"io_in_instr_{i}",0b0000010,mask = 0b1111111) + and check_function(f"io_in_instr_{i}",0b1000,shift= 12), + "instr is RVC.JR and is not call or ret": check_function( + f"io_out_pd_{i}_isRet", 0 + ) + and check_function(f"io_out_pd_{i}_isCall", 0) + and check_function(f"io_in_instr_{i}",0b0000010,mask = 0b1111111) + and check_function(f"io_in_instr_{i}",0b1000,shift=12), + }, + name=f"check_jr_{i}", + ) + + def _mark(name): + return module_name_with(name, "../f3predecoder_test") + + for i in range(16): + grp.mark_function(f'check_cfi_{i}',_mark(["test_cfi_checker_1_1","test_cfi_checker_1_2","test_cfi_checker_1_3"])) + grp.mark_function(f'check_jal_{i}',_mark(["test_cfi_checker_2_2_1_1","test_cfi_checker_2_2_1_2","test_cfi_checker_2_2_2"])) + grp.mark_function(f'check_jalr_{i}',_mark(["test_cfi_checker_2_3_2_1","test_cfi_checker_2_3_1_2","test_cfi_checker_2_3_1_3","test_cfi_checker_2_3_2_1"])) + grp.mark_function(f'check_jr_{i}',_mark(["test_cfi_checker_2_3_2_2_1","test_cfi_checker_2_3_2_2_2"])) + return grp + @toffee_test.fixture async def f3predecoder_env(toffee_request: toffee_test.ToffeeRequest): import asyncio dut = toffee_request.create_dut(DUTF3Predecoder) + toffee_request.add_cov_groups(init_cov(dut, grp)) start_clock(dut) predecode_env = F3PreDecoderEnv(dut) yield predecode_env @@ -18,4 +124,4 @@ async def f3predecoder_env(toffee_request: toffee_test.ToffeeRequest): try: await task except asyncio.CancelledError: - break \ No newline at end of file + break diff --git a/ut_frontend/ifu/f3predecoder/test/f3predecoder_instr_gen.py b/ut_frontend/ifu/f3predecoder/test/f3predecoder_instr_gen.py new file mode 100644 index 0000000..73e5521 --- /dev/null +++ b/ut_frontend/ifu/f3predecoder/test/f3predecoder_instr_gen.py @@ -0,0 +1,231 @@ +import random + +class F3PredecodeInstrGen(): + def __init__(self): + self.brTypes = [] + self.isCalls = [] + self.isRets = [] + self.instrs = [] + + def clear(self): + self.brTypes = [] + self.isCalls = [] + self.isRets = [] + self.instrs = [] + + def inst_gen(self, isa = 'random', type = 'not_cfi'): + if type == 'br': + #generate br type instructions + for _ in range(16): + #rvi + opcode = 0b1100011 + funct3 = random.choice([0b000,0b001,0b100,0b101,0b110,0b111]) + rs1 = random.getrandbits(5) + rs2 = random.getrandbits(5) + instr_rvi = (random.getrandbits(7) << 25) | rs1 << 20 |\ + rs2 << 15 | funct3 << 12 | (random.getrandbits(5) << 7) |\ + opcode + + #rvc + op = 0b01 + func3 = random.choice([0b110,0b111]) + instr_rvc = func3 << 13 | (random.getrandbits(11) << 2) | op + + self.brTypes.append(1) + if isa == 'random': + self.instrs.append(random.choice([instr_rvi, instr_rvc])) + elif isa == 'rvi': + self.instrs.append(instr_rvi) + elif isa == 'rvc': + self.instrs.append(instr_rvc) + + elif type == 'jal': + #generate jal type instructions + for _ in range(16): + #rvi + opcode = 0b1101111 + instr_rvi = (random.getrandbits(25) << 7) | opcode + + #rvc + func3 = 0b101 + op = 0b01 + instr_rvc = (func3 << 13) | (random.getrandbits(11) << 2) | op + + self.brTypes.append(2) + if isa == 'random': + self.instrs.append(random.choice([instr_rvi, instr_rvc])) + elif isa == 'rvi': + self.instrs.append(instr_rvi) + elif isa == 'rvc': + self.instrs.append(instr_rvc) + + elif type == 'jalr': + #generate jalr type instructions + for _ in range(16): + #rvi + funct3 = 0b000 + opcode = 0b1100111 + instr_rvi = (random.getrandbits(17) << 25) | funct3 << 12 |\ + (random.getrandbits(5) << 7) | opcode + + #rvc + rs1 = random.getrandbits(5) | 0b1 + head = random.choice([0b1001,0b1000]) + tail = 0b00000_10 + instr_rvc = (head << 12) | (rs1 << 7) | tail + + self.brTypes.append(3) + if isa == 'random': + self.instrs.append(random.choice([instr_rvi, instr_rvc])) + elif isa == 'rvi': + self.instrs.append(instr_rvi) + elif isa == 'rvc': + self.instrs.append(instr_rvc) + + elif type == 'not_cfi': + #generate not cfi type instructions + for _ in range(16): + #rvi + opcode = random.getrandbits(7) | 0b11 + funct3 = random.getrandbits(3) + + while True: + flag1 = (opcode == 0b1100011) and (funct3 in [0b000,0b001,0b010,0b011,0b100,0b101,0b110,0b111]) + flag2 = (opcode == 0b1101111) + flag3 = (opcode == 0b1100111) and (funct3 == 0b000) + if not flag3 and not flag2 and not flag1: + break + opcode = random.getrandbits(7) | 0b11 + funct3 = random.getrandbits(3) + instr_rvi = random.getrandbits(17) << 15 | funct3 << 12 | random.getrandbits(5) << 7 | opcode + + #rvc + c_ebreak = 0b100_1_00000_00000_10 + instr_rvc = random.getrandbits(16) + while True: + op = instr_rvc & 0b11 + func3 = instr_rvc >> 13 + flag1 = (op == 0b01) and (func3 in [0b110, 0b111, 0b101]) + + j1 = instr_rvc & 0b1111111 + j2 = instr_rvc >> 12 + flag2 = (j1 == 0b0000010) and (j2 in [0b1001, 0b1000]) + + flag3 = op == 0b11 + + if not flag1 and not flag2 and not flag3: + break + instr_rvc = random.getrandbits(16) + + self.brTypes.append(0) + self.instrs.append(random.choice([instr_rvc,instr_rvi,c_ebreak])) + + return self.instrs, self.brTypes + + def ret_call_checker(self,task = '2.1.1'): + if task =='2.1.1': + self.inst_gen(type = 'not_cfi') + self.isCalls = [0 for _ in range(16)] + self.isRets = [0 for _ in range(16)] + return self.instrs, self.isCalls, self.isRets + if task == '2.1.2': + self.inst_gen(type = 'br') + self.isCalls = [0 for _ in range(16)] + self.isRets = [0 for _ in range(16)] + return self.instrs, self.isCalls, self.isRets + if task == '2.2.1.1': + def rd(): + return random.choice([1, 5]) << 7 + mask = 0b1111_1111_1111_1111_1111_0000_0111_1111 + self.inst_gen(type = 'jal', isa = 'rvi') + self.instrs = [i & mask | rd() for i in self.instrs] + self.isCalls = [1 for _ in range(16)] + self.isRets = [0 for _ in range(16)] + return self.instrs, self.isCalls, self.isRets + if task == '2.2.1.2': + def rd(): + valid_numbers = [x for x in range(33) if x not in {1, 5}] + num = random.choice(valid_numbers) + return num << 7 + mask = 0b1111_1111_1111_1111_1111_0000_0111_1111 + self.inst_gen(type = 'jal', isa = 'rvi') + self.instrs = [i & mask | rd() for i in self.instrs] + self.isCalls = [0 for _ in range(16)] + self.isRets = [0 for _ in range(16)] + return self.instrs, self.isCalls, self.isRets + if task == '2.2.2': + self.isCalls = [0 for _ in range(16)] + self.isRets = [0 for _ in range(16)] + self.inst_gen(type = 'jal', isa = 'rvc') + return self.instrs, self.isCalls, self.isRets + if task == '2.3.1.1': + def rd(): + return random.choice([1, 5]) << 7 + mask = 0b1111_1111_1111_1111_1111_0000_0111_1111 + self.inst_gen(type = 'jalr', isa = 'rvi') + self.instrs = [i & mask | rd() for i in self.instrs] + self.isCalls = [1 for _ in range(16)] + self.isRets = [0 for _ in range(16)] + return self.instrs, self.isCalls, self.isRets + + if task == '2.3.1.2': + def rs(): + return random.choice([1,5]) << 15 + def rd(): + valid_numbers = [x for x in range(32) if x not in {1, 5}] + num = random.choice(valid_numbers) + return num << 7 + mask = 0b1111_1111_1111_0000_0111_0000_0111_1111 + self.inst_gen('jalr', 'rvi') + self.inst_gen(type = 'jalr', isa = 'rvi') + self.instrs = [i & mask | rd() | rs() for i in self.instrs] + self.isCalls = [0 for _ in range(16)] + self.isRets = [1 for _ in range(16)] + return self.instrs, self.isCalls, self.isRets + + if task == '2.3.1.3': + def rd(): + valid_numbers = [x for x in range(32) if x not in {1, 5}] + num = random.choice(valid_numbers) + return num << 7 + def rs(): + valid_numbers = [x for x in range(32) if x not in {1, 5}] + num = random.choice(valid_numbers) + return num << 15 + mask = 0b1111_1111_1111_0000_0111_0000_0111_1111 + self.inst_gen('jalr', 'rvi') + self.inst_gen(type = 'jalr', isa = 'rvi') + self.instrs = [i & mask | rd() | rs() for i in self.instrs] + self.isCalls = [0 for _ in range(16)] + self.isRets = [0 for _ in range(16)] + return self.instrs, self.isCalls, self.isRets + + if task == '2.3.2.1': + self.inst_gen(type = 'jalr', isa = 'rvc') + self.instrs = [i | 1 << 12 for i in self.instrs] + self.isCalls = [1 for _ in range(16)] + self.isRets = [0 for _ in range(16)] + return self.instrs, self.isCalls, self.isRets + + if task == '2.3.2.2.1': + def rs(): + return random.choice([1,5]) << 7 + mask = 0b1110000001111111 + self.inst_gen(type = 'jalr', isa = 'rvc') + self.instrs = [i & mask | rs() for i in self.instrs] + self.isCalls = [0 for _ in range(16)] + self.isRets = [1 for _ in range(16)] + return self.instrs, self.isCalls, self.isRets + + if task == '2.3.2.2.2': + def rs(): + valid_numbers = [x for x in range(32) if x not in {1, 5}] + num = random.choice(valid_numbers) + return num << 7 + mask = 0b1110000001111111 + self.inst_gen(type = 'jalr', isa = 'rvc') + self.instrs = [i & mask | rs() for i in self.instrs] + self.isCalls = [0 for _ in range(16)] + self.isRets = [0 for _ in range(16)] + return self.instrs, self.isCalls, self.isRets + diff --git a/ut_frontend/ifu/f3predecoder/test/f3predecoder_test.py b/ut_frontend/ifu/f3predecoder/test/f3predecoder_test.py index fe52d21..fccc22a 100644 --- a/ut_frontend/ifu/f3predecoder/test/f3predecoder_test.py +++ b/ut_frontend/ifu/f3predecoder/test/f3predecoder_test.py @@ -1,10 +1,184 @@ from .f3predecoder_fixture import f3predecoder_env from ..env import F3PreDecoderEnv import toffee_test +from .f3predecoder_instr_gen import F3PredecodeInstrGen + +instrGen = F3PredecodeInstrGen() @toffee_test.testcase -async def test_smoke(f3predecoder_env : F3PreDecoderEnv): - instrs = [483 for i in range(16)] +async def test_cfi_checker_1_1(f3predecoder_env : F3PreDecoderEnv): + print("test_cfi_checker_1_1") + for _ in range(100): + instrGen.clear() + instrs, brTypes = instrGen.inst_gen(type='not_cfi', isa='rvc') + res = await f3predecoder_env.agent.f3_predecode(instrs) + assert res.brTypes == brTypes + + instrGen.clear() + instrs, brTypes = instrGen.inst_gen(type='not_cfi', isa='rvi') + res = await f3predecoder_env.agent.f3_predecode(instrs) + assert res.brTypes == brTypes - print(await f3predecoder_env.agent.f3_predecode(instrs)) - \ No newline at end of file + +@toffee_test.testcase +async def test_cfi_checker_1_2(f3predecoder_env : F3PreDecoderEnv): + print("test_cfi_checker_1_2") + for _ in range(100): + instrGen.clear() + instrs, brTypes = instrGen.inst_gen(type='br', isa='rvi') + res = await f3predecoder_env.agent.f3_predecode(instrs) + assert res.brTypes == brTypes + + instrGen.clear() + instrs, brTypes = instrGen.inst_gen(type='br', isa='rvc') + res = await f3predecoder_env.agent.f3_predecode(instrs) + assert res.brTypes == brTypes + +@toffee_test.testcase +async def test_cfi_checker_1_3(f3predecoder_env : F3PreDecoderEnv): + print("test_cfi_checker_1_3") + for _ in range(100): + instrGen.clear() + instrs, brTypes = instrGen.inst_gen(type='jal', isa='rvi') + res = await f3predecoder_env.agent.f3_predecode(instrs) + assert res.brTypes == brTypes + + instrGen.clear() + instrs, brTypes = instrGen.inst_gen(type='jal', isa='rvc') + res = await f3predecoder_env.agent.f3_predecode(instrs) + assert res.brTypes == brTypes + +@toffee_test.testcase +async def test_cfi_checker_1_4(f3predecoder_env : F3PreDecoderEnv): + print("test_cfi_checker_1_4") + for _ in range(100): + instrGen.clear() + instrs, brTypes = instrGen.inst_gen(type='jalr', isa='rvi') + res = await f3predecoder_env.agent.f3_predecode(instrs) + assert res.brTypes == brTypes + + instrGen.clear() + instrs, brTypes = instrGen.inst_gen(type='jalr', isa='rvc') + res = await f3predecoder_env.agent.f3_predecode(instrs) + assert res.brTypes == brTypes + +@toffee_test.testcase +async def test_ret_call_checker_2_1_1(f3predecoder_env : F3PreDecoderEnv): + #check not cfi + print("test_ret_call_checker_2_1_1") + for _ in range(100): + instrGen.clear() + instrs, isCalls, isRets = instrGen.ret_call_checker(task = '2.1.1') + res = await f3predecoder_env.agent.f3_predecode(instrs) + assert res.isCalls == isCalls + assert res.isRets == isRets + +@toffee_test.testcase +async def test_ret_call_checker_2_1_2(f3predecoder_env : F3PreDecoderEnv): + #check br + print("test_ret_call_checker_2_1_2") + for _ in range(100): + instrGen.clear() + instrs, isCalls, isRets = instrGen.ret_call_checker(task = '2.1.2') + res = await f3predecoder_env.agent.f3_predecode(instrs) + assert res.isCalls == isCalls + assert res.isRets == isRets + +@toffee_test.testcase +async def test_ret_call_checker_2_2_1_1(f3predecoder_env : F3PreDecoderEnv): + #check RVI.JAL call + print("test_ret_call_checker_2_2_1_1") + for _ in range(100): + instrGen.clear() + instrs, isCalls, isRets = instrGen.ret_call_checker(task = '2.2.1.1') + res = await f3predecoder_env.agent.f3_predecode(instrs) + assert res.isCalls == isCalls + assert res.isRets == isRets + +@toffee_test.testcase +async def test_ret_call_checker_2_2_1_2(f3predecoder_env : F3PreDecoderEnv): + #check RVI.JAL not call and ret + print("test_ret_call_checker_2_2_1_2") + for _ in range(100): + instrGen.clear() + instrs, isCalls, isRets = instrGen.ret_call_checker(task = '2.2.1.2') + res = await f3predecoder_env.agent.f3_predecode(instrs) + assert res.isCalls == isCalls + assert res.isRets == isRets + +@toffee_test.testcase +async def test_ret_call_checker_2_2_2(f3predecoder_env : F3PreDecoderEnv): + #check RVC.JAL not call and ret + print("test_ret_call_checker_2_2_2") + for _ in range(100): + instrGen.clear() + instrs, isCalls, isRets = instrGen.ret_call_checker(task = '2.2.2') + res = await f3predecoder_env.agent.f3_predecode(instrs) + assert res.isCalls == isCalls + assert res.isRets == isRets + +@toffee_test.testcase +async def test_ret_call_checker_2_3_1_1(f3predecoder_env : F3PreDecoderEnv): + #check RVI.JALR call + print("test_ret_call_checker_2_3_1_1") + for _ in range(100): + instrGen.clear() + instrs, isCalls, isRets = instrGen.ret_call_checker(task = '2.3.1.1') + res = await f3predecoder_env.agent.f3_predecode(instrs) + assert res.isCalls == isCalls + assert res.isRets == isRets + +@toffee_test.testcase +async def test_ret_call_checker_2_3_1_2(f3predecoder_env : F3PreDecoderEnv): + #check RVI.JALR ret + print("test_ret_call_checker_2_3_1_2") + for _ in range(100): + instrGen.clear() + instrs, isCalls, isRets = instrGen.ret_call_checker(task = '2.3.1.2') + res = await f3predecoder_env.agent.f3_predecode(instrs) + assert res.isCalls == isCalls + assert res.isRets == isRets + +@toffee_test.testcase +async def test_ret_call_checker_2_3_1_3(f3predecoder_env : F3PreDecoderEnv): + #check RVI.JALR not call and ret + print("test_ret_call_checker_2_3_1_3") + for _ in range(100): + instrGen.clear() + instrs, isCalls, isRets = instrGen.ret_call_checker(task = '2.3.1.3') + res = await f3predecoder_env.agent.f3_predecode(instrs) + assert res.isCalls == isCalls + assert res.isRets == isRets + +@toffee_test.testcase +async def test_ret_call_checker_2_3_2_1(f3predecoder_env: F3PreDecoderEnv): + #check RVC.JALR call + print("test_ret_call_checker_2_3_2_1") + for _ in range(100): + instrGen.clear() + instrs, isCalls, isRets = instrGen.ret_call_checker(task = '2.3.2.1') + res = await f3predecoder_env.agent.f3_predecode(instrs) + assert res.isCalls == isCalls + assert res.isRets == isRets + +@toffee_test.testcase +async def test_ret_call_checker_2_3_2_2_1(f3predecoder_env: F3PreDecoderEnv): + #check RVC.JR ret + print("test_ret_call_checker_2_3_2_2_1") + for _ in range(100): + instrGen.clear() + instrs, isCalls, isRets = instrGen.ret_call_checker(task = '2.3.2.2.1') + res = await f3predecoder_env.agent.f3_predecode(instrs) + assert res.isCalls == isCalls + assert res.isRets == isRets + +@toffee_test.testcase +async def test_ret_call_checker_2_3_2_2_2(f3predecoder_env: F3PreDecoderEnv): + #check RVC.JR not ret + print("test_ret_call_checker_2_3_2_2_2") + for _ in range(100): + instrGen.clear() + instrs, isCalls, isRets = instrGen.ret_call_checker(task = '2.3.2.2.2') + res = await f3predecoder_env.agent.f3_predecode(instrs) + assert res.isCalls == isCalls + assert res.isRets == isRets diff --git a/ut_frontend/ifu/predecode/agent/predecode_agent.py b/ut_frontend/ifu/predecode/agent/predecode_agent.py index c5e7eaf..ad0159f 100644 --- a/ut_frontend/ifu/predecode/agent/predecode_agent.py +++ b/ut_frontend/ifu/predecode/agent/predecode_agent.py @@ -16,6 +16,16 @@ class PreDecodeDataDef(): res = f"new instrs: {self.new_instrs}\njump offsets: {self.jmp_offsets}\nrvcs: {self.rvcs}\nvalid_starts: {self.valid_starts}\nhalf_valid_starts: {self.half_valid_starts}\n" res += f"isRets:{self.isRets}\nisCalls:{self.isCalls}\nbyTypes:{self.brTypes}" return res + def clear(self): + self.new_instrs = [] + self.jmp_offsets = [] + self.rvcs = [] + self.valid_starts = [] + self.half_valid_starts = [] + + self.isRets = [] + self.isCalls = [] + self.brTypes = [] class PreDecodeAgent(Agent): @@ -29,10 +39,11 @@ class PreDecodeAgent(Agent): async def predecode(self, instrs: list[int]) -> PreDecodeDataDef: for i in range(17): getattr(self.bundle.io._in_bits_data, f"_{i}").value = instrs[i] - print("going to step") + #print("going to step") await self.bundle.step() - print("step over") + #print("step over") ret = PreDecodeDataDef() + ret.clear() for i in range(16): ret.new_instrs.append(getattr(self.bundle.io._out._instr, f"_{i}").value) diff --git a/ut_frontend/ifu/predecode/test/predecode_fixture.py b/ut_frontend/ifu/predecode/test/predecode_fixture.py index fd1be4f..1ac01c6 100644 --- a/ut_frontend/ifu/predecode/test/predecode_fixture.py +++ b/ut_frontend/ifu/predecode/test/predecode_fixture.py @@ -2,13 +2,85 @@ import toffee_test from dut.PreDecode import DUTPreDecode from ..env import PreDecodeEnv from toffee import start_clock +import toffee.funcov as fc +from comm import UT_FCOV, module_name_with + +grp = fc.CovGroup(UT_FCOV("../../CLASSIC")) + + +def check_isRVC(index: int, expected_value: int): + def checker(x): + attr_name = f"io_out_pd_{index}_isRVC" + return getattr(x, attr_name).value == expected_value + + return checker + + +def check_hasHalfValid(index: int, expected_value: int): + def checker(x): + attr_name = f"io_out_hasHalfValid_{index}" + return getattr(x, attr_name).value == expected_value + + return checker + + +def check_pdValid(index: int, expected_value: int): + def checker(x): + attr_name = f"io_out_pd_{index}_valid" + return getattr(x, attr_name).value == expected_value + + return checker + + +def init_cov(dut:DUTPreDecode, grp: fc.CovGroup): + for i in range(16): + grp.add_cover_point( + dut, + { + "instr is rvc:": check_isRVC(i, 1), + "instr is rvi:": check_isRVC(i, 0), + }, + name=f"check_rvc_rvi_{i}", + ) + + for i in range(2,16): + grp.add_cover_point( + dut, + { + "instr is half_valid_start:": check_hasHalfValid(i, 1), + "instr is not half_valid_start:": check_hasHalfValid(i, 0), + }, + name=f"check_half_valid_start_{i}", + ) + + for i in range(1,16): + grp.add_cover_point( + dut, + { + "instr is valid_starts:": check_pdValid(i, 1), + "instr is not valid_starts:": check_pdValid(i, 0), + }, + name=f"check_valid_start_{i}", + ) + + def _mark(name): + return module_name_with(name, "../predecode_test") + + for i in range(16): + grp.mark_function(f'check_rvc_rvi_{i}',_mark(["test_rvc_rvi_checker_2_1_2","test_rvc_rvi_checker_2_1_1"])) + for i in range(2,16): + grp.mark_function(f'check_half_valid_start_{i}',_mark(["test_precoding_checker_2_3_2"])) + for i in range(1,16): + grp.mark_function(f'check_valid_start_{i}',_mark(["test_precoding_checker_2_3_1"])) + + return grp @toffee_test.fixture async def predecode_env(toffee_request: toffee_test.ToffeeRequest): import asyncio - # version_check() dut = toffee_request.create_dut(DUTPreDecode) + toffee_request.add_cov_groups(init_cov(dut, grp)) start_clock(dut) predecode_env = PreDecodeEnv(dut) yield predecode_env @@ -20,4 +92,4 @@ async def predecode_env(toffee_request: toffee_test.ToffeeRequest): try: await task except asyncio.CancelledError: - break \ No newline at end of file + break diff --git a/ut_frontend/ifu/predecode/test/predecode_instr_gen.py b/ut_frontend/ifu/predecode/test/predecode_instr_gen.py new file mode 100644 index 0000000..b8eb7ac --- /dev/null +++ b/ut_frontend/ifu/predecode/test/predecode_instr_gen.py @@ -0,0 +1,168 @@ +import random + +class PreDecodeInstrGen(): + def __init__(self): + self.instrs = [] + self.new_instrs = [] + self.jmp_offsets = [] + self.rvcs = [] + self.valid_starts = [] + self.half_valid_starts = [] + + self.isRets = [] + self.isCalls = [] + self.brTypes = [] + + def clear(self): + self.instrs = [] + self.new_instrs = [] + self.jmp_offsets = [] + self.rvcs = [] + self.valid_starts = [] + self.half_valid_starts = [] + + self.isRets = [] + self.isCalls = [] + self.brTypes = [] + + def random_instrs(self): + #genetare random instructions + self.instrs = [random.getrandbits(16) for _ in range(17)] + self.new_instrs = [self.instrs[i+1] << 16 | self.instrs[i] for i in range(16)] + return self.instrs, self.new_instrs + def instr_gen(self, type='random', isa='rvi'): + if type == 'random': + instr_rvi = random.getrandbits(32) | 0b11 + instr_rvc = random.getrandbits(14) << 2 | random.choice([0b00,0b01,0b10]) + return instr_rvi if isa == 'rvi' else instr_rvc + if type == 'j': + opcode = 0b1101111 + instr_rvi = (random.getrandbits(25) << 7) | opcode + func3 = random.choice([0b101]) + op = 0b01 + instr_rvc = (func3) << 13 | (random.getrandbits(11) << 2) | op + + return instr_rvi if isa == 'rvi' else instr_rvc + + if type == 'br': + instr_rvi = 0 #instr_rvi is aborted + func3 = random.choice([0b110,0b111]) + op = 0b01 + instr_rvc = func3 << 13 | random.getrandbits(11) << 2 | op + return instr_rvi if isa == 'rvi' else instr_rvc + + if type == 'jalr': + instr_rvc = (0b1101) << 12 | random.getrandbits(5) << 7 | 0b0000010 + instr_rvi = random.getrandbits(1) << 15 | (0b000) << 12 | random.getrandbits(5) << 7 | 0b1100111 + return instr_rvi + if type == 'jal': + instr_rvi = random.getrandbits(9) << 7 | 0b1101111 + return instr_rvi + + + def precoding_checker(self, task = '2.1.1'): + if task == '2.1.1': + self.instrs = [self.instr_gen(type = 'random', isa = 'rvc') for _ in range(17)] + self.rvcs = [1 for _ in range(16)] + return self.instrs, self.rvcs + if task == '2.1.2': + self.instrs = [random.getrandbits(16) | 0b11 for _ in range(17)] + self.rvcs = [0 for _ in range(16)] + return self.instrs, self.rvcs + + if task == '2.2.1': + self.instrs = [self.instr_gen(type = 'j', isa = 'rvc') for _ in range(17)] + def offset(instr): + mask = 0b11111111111 + offset = (instr >> 2) & mask + o11 = (offset >> 10) & 1 + o4 = (offset >> 9) & 1 + o98 = (offset >> 7) & 0b11 + o10 = (offset >> 6) & 1 + o6 = (offset >> 5) & 1 + o7 = (offset >> 4) & 1 + o31 = (offset >> 1) & 0b111 + o5 = (offset >> 0) & 1 + offset = (o11 << 11) | (o4 << 4) | (o98 << 8) | (o10 << 10) | (o6 << 6) | (o7 << 7) | (o31 << 1) | (o5 << 5) + offset = offset if o11 == 0 else (offset | (0b1111) << 12) & 0xFFFF + return offset + self.jmp_offsets = [offset(i) for i in self.instrs] + self.jmp_offsets.pop() + return self.instrs, self.jmp_offsets + if task == '2.2.2': + self.instrs = [random.getrandbits(9) << 7 | 0b1101111 for _ in range(17)] + self.new_instrs = [self.instrs[i+1] << 16 | self.instrs[i] for i in range(16)] + def offset(instr): + offset = instr >> 12 + o20 = (offset >> 19) & 1 + o101 = (offset >> 9) & 0b1111111111 + o11 = (offset >> 8) & 1 + o1912 = offset & 0b11111111 + offset = (o20 << 20) | (o101 << 1) | (o11 << 11) | (o1912 << 12) + offset = offset & 0xFFFFFFFF if o20 == 0 else (offset | (0xFFFFFF) << 21) & 0xFFFFFFFF + return offset + self.jmp_offsets = [offset(i) for i in self.new_instrs] + return self.instrs, self.jmp_offsets + + if task == '2.2.3': + self.instrs = [self.instr_gen(type = 'br', isa = 'rvc') for _ in range(17)] + def offset(instr): + o5 = (instr >> 2) & 1 + o21 = (instr >> 3) & 0b11 + o76 = (instr >> 5) & 0b11 + o43 = (instr >> 10) & 0b11 + o8 = (instr >> 12) & 1 + offset = o5 << 5 | o21 << 1 | o76 << 6 | o43 << 3 | o8 << 8 + offset = offset if o8 == 0 else (offset | 0xFF << 9) & 0xFFFF + return offset + self.jmp_offsets = [offset(i) for i in self.instrs] + self.jmp_offsets.pop() + return self.instrs, self.jmp_offsets + if task == '2.2.4': + def offset(instr): + o12 = (instr >> 31) & 1 + o105 = (instr >> 25) & 0b111111 + o11 = (instr >> 7) & 1 + o41 = (instr >> 8) & 0b1111 + offset = o12 << 12 | o105 << 5 | o11 << 11 | o41 << 1 + offset = offset if o12 == 0 else (offset | 0xFFFFFFF << 13) & 0xFFFFFFFF + return offset + self.instrs = [random.getrandbits(5) << 7 | random.getrandbits(1) << 15 | 0b1100011 | random.choice([0b000,0b001,0b100,0b101,0b110,0b111]) << 12 for _ in range(17)] + self.new_instrs = [self.instrs[i+1] << 16 | self.instrs[i] for i in range(16)] + self.jmp_offsets = [offset(i) for i in self.new_instrs] + return self.instrs, self.jmp_offsets + + if task == '3.1': + self.instrs = [random.getrandbits(16) for _ in range(17)] + self.new_instrs = [self.instrs[i+1] << 16 | self.instrs[i] for i in range(16)] + mask = 0b11 + flag = (self.new_instrs[0] & mask) == 0b11 + self.valid_starts.append(1) + + for i in range(1, 16): + if flag: + self.valid_starts.append(0) + flag = False + else: + self.valid_starts.append(1) + flag = (self.new_instrs[i] & mask) == 0b11 + return self.instrs, self.valid_starts + + if task == '3.2': + self.instrs = [random.getrandbits(16) for _ in range(17)] + self.new_instrs = [self.instrs[i+1] << 16 | self.instrs[i] for i in range(16)] + mask = 0b11 + self.half_valid_starts.append(0) # i == 0 + self.half_valid_starts.append(1) # i == 1 + flag = (self.new_instrs[1] & mask) == 0b11 + + for i in range(2, 16): + if flag: + self.half_valid_starts.append(0) + flag = False + else: + self.half_valid_starts.append(1) + flag = (self.new_instrs[i] & mask) == 0b11 + return self.instrs, self.half_valid_starts + + diff --git a/ut_frontend/ifu/predecode/test/predecode_test.py b/ut_frontend/ifu/predecode/test/predecode_test.py index 69ae09c..51b898d 100644 --- a/ut_frontend/ifu/predecode/test/predecode_test.py +++ b/ut_frontend/ifu/predecode/test/predecode_test.py @@ -1,10 +1,112 @@ import toffee_test from .predecode_fixture import predecode_env from ..env import PreDecodeEnv +from .predecode_instr_gen import PreDecodeInstrGen + +instrGen = PreDecodeInstrGen() + @toffee_test.testcase -async def test_smoke(predecode_env : PreDecodeEnv): +async def test_smoke(predecode_env: PreDecodeEnv): fake_instrs = [54541 for i in range(17)] res = await predecode_env.agent.predecode(fake_instrs) - print(res) + + +@toffee_test.testcase +async def test_splice_checker(predecode_env: PreDecodeEnv): + # 指令拼接测试 + print("test_splice_checker") + for _ in range(100): + instrGen.clear() + instrs, new_instrs = instrGen.random_instrs() + res = await predecode_env.agent.predecode(instrs) + assert res.new_instrs == new_instrs + + +@toffee_test.testcase +async def test_precoding_checker_2_1_1(predecode_env: PreDecodeEnv): + ##RVC判定 传入RVC指令 + print("test_precoding_checker_2_1_1") + for _ in range(100): + instrGen.clear() + instrs, rvcs = instrGen.precoding_checker(task="2.1.1") + res = await predecode_env.agent.predecode(instrs) + assert res.rvcs == rvcs + + +@toffee_test.testcase +async def test_precoding_checker_2_1_2(predecode_env: PreDecodeEnv): + # RVI判定 传入RVI指令 + print("test_precoding_checker_2_1_2") + for _ in range(100): + instrGen.clear() + instrs, rvcs = instrGen.precoding_checker(task="2.1.2") + res = await predecode_env.agent.predecode(instrs) + assert res.rvcs == rvcs + + +@toffee_test.testcase +async def test_precoding_checker_2_2_1(predecode_env: PreDecodeEnv): + ##RVC.J计算 offset + print("test_precoding_checker_2_2_1") + for _ in range(100): + instrGen.clear() + instrs, jmp_offsets = instrGen.precoding_checker(task="2.2.1") + res = await predecode_env.agent.predecode(instrs) + assert [i & 0xFFFF for i in res.jmp_offsets] == jmp_offsets + + +@toffee_test.testcase +async def test_precoding_checker_2_2_2(predecode_env: PreDecodeEnv): + ##RVI.J计算 offset + print("test_precoding_checker_2_2_2") + for _ in range(100): + instrGen.clear() + instrs, jmp_offsets = instrGen.precoding_checker(task="2.2.2") + res = await predecode_env.agent.predecode(instrs) + assert [i & 0xFFFFFFFF for i in res.jmp_offsets] == jmp_offsets + + +@toffee_test.testcase +async def test_precoding_checker_2_2_3(predecode_env: PreDecodeEnv): + # RVC.BR计算 offset + print("test_precoding_checker_2_2_3") + for _ in range(100): + instrGen.clear() + instrs, jmp_offsets = instrGen.precoding_checker(task="2.2.3") + res = await predecode_env.agent.predecode(instrs) + assert [i & 0xFFFF for i in res.jmp_offsets] == jmp_offsets + + +@toffee_test.testcase +async def test_precoding_checker_2_2_4(predecode_env: PreDecodeEnv): + # RVI.BR计算 offset + print("test_precoding_checker_2_2_4") + for _ in range(100): + instrGen.clear() + instrs, jmp_offsets = instrGen.precoding_checker(task="2.2.4") + res = await predecode_env.agent.predecode(instrs) + assert [i & 0xFFFFFFFF for i in res.jmp_offsets] == jmp_offsets + + +@toffee_test.testcase +async def test_precoding_checker_3_1(predecode_env: PreDecodeEnv): + # 有效指令开始向量计算1 + print("test_precoding_checker_3_1") + for _ in range(100): + instrGen.clear() + instrs, valid_starts = instrGen.precoding_checker(task="3.1") + res = await predecode_env.agent.predecode(instrs) + assert res.valid_starts == valid_starts + + +@toffee_test.testcase +async def test_precoding_checker_3_2(predecode_env: PreDecodeEnv): + # 有效指令开始向量计算2 + print("test_precoding_checker_3_2") + for _ in range(100): + instrGen.clear() + instrs, half_valid_starts = instrGen.precoding_checker(task="3.2") + res = await predecode_env.agent.predecode(instrs) + assert res.half_valid_starts == half_valid_starts