Skip to content

Commit dde6ef2

Browse files
Completeness check
Refactor `InstructionChecker` to subclass `InstructionCheckerBase`. Add `CompleteIsaChecker` which also subclasses the same, with adjustments to perform ISA completeness checking. `CompleteIsaChecker` takes two lists, one for valid opcodes (compared against `rvfi.insn[6:0]`), and one for arbitrary (combinatorial) validity checks (tested NERV under `base_isa` with a skip for `if key == "lb"`, included commented out here so I don't forget how I did it). Refactor submodule `__init__.py` files to import instead of assign (which works better with VSCode for code following). Move base rvfi observers into `base_observers` function in `rvfi` submodule. Also split `defined_checks` into a method call.
1 parent 9593cc3 commit dde6ef2

File tree

8 files changed

+164
-82
lines changed

8 files changed

+164
-82
lines changed

src/checks/__init__.py

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1-
from . import generic_checker
1+
from .generic_checker import GenericChecker
22

3-
GenericChecker = generic_checker.GenericChecker
3+
from .instruction_checker import (
4+
InstructionChecker,
5+
CompleteISAChecker,
6+
)

src/checks/base_isa.py

Lines changed: 31 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -4,48 +4,15 @@
44

55
import click
66

7-
from .instruction_checker import InstructionChecker
7+
from . import InstructionChecker, CompleteISAChecker
88
from ..insns import Instruction, builtins
99
from ..rvfi import (
10-
Observer,
11-
SpeculativeObserver,
12-
ZeroedObserver,
10+
base_observers,
1311
SpeculativeEvaluation,
1412
)
1513

16-
def dump_isa(
17-
name: str,
18-
insns: dict[str, Instruction],
19-
xlen: int,
20-
format: str,
21-
channel: Optional[int] = None,
22-
channelized: bool = True,
23-
) -> str:
24-
rvfi: dict[str, Observer] = {o.name: o for o in [
25-
SpeculativeObserver("valid", "1"),
26-
Observer("order", "64"),
27-
Observer("insn", "`RISCV_FORMAL_ILEN"),
28-
SpeculativeObserver("trap", "1"),
29-
Observer("halt", "1"),
30-
Observer("intr", "1"),
31-
Observer("mode", "2"),
32-
Observer("ixl", "2"),
33-
SpeculativeObserver("rs1_addr", "5", "rvfi.rs1_addr"),
34-
SpeculativeObserver("rs2_addr", "5", "rvfi.rs2_addr"),
35-
ZeroedObserver("rs1_rdata", "`RISCV_FORMAL_XLEN", "spec_rs1_addr == 0"),
36-
ZeroedObserver("rs2_rdata", "`RISCV_FORMAL_XLEN", "spec_rs2_addr == 0"),
37-
SpeculativeObserver("rd_addr", "5"),
38-
SpeculativeObserver("rd_wdata", "`RISCV_FORMAL_XLEN"),
39-
Observer("pc_rdata", "`RISCV_FORMAL_XLEN"),
40-
SpeculativeObserver("pc_wdata", "`RISCV_FORMAL_XLEN", "rvfi.pc_rdata + 4"),
41-
SpeculativeObserver("mem_addr", "`RISCV_FORMAL_XLEN"),
42-
SpeculativeObserver("mem_rmask", "`RISCV_FORMAL_XLEN/8"),
43-
SpeculativeObserver("mem_wmask", "`RISCV_FORMAL_XLEN/8"),
44-
Observer("mem_rdata", "`RISCV_FORMAL_XLEN"),
45-
SpeculativeObserver("mem_wdata", "`RISCV_FORMAL_XLEN"),
46-
]}
47-
48-
defined_checks: list[SpeculativeEvaluation] = [
14+
def base_checks() -> list[SpeculativeEvaluation]:
15+
return [
4916
SpeculativeEvaluation(
5017
"if (rvfi.rs1_addr == 0) assert(rvfi.rs1_rdata == 0);",
5118
ignore_trap = True,
@@ -81,13 +48,39 @@ def dump_isa(
8148
),
8249
]
8350

51+
def dump_isa(
52+
name: str,
53+
insns: dict[str, Instruction],
54+
xlen: int,
55+
format: str,
56+
channel: Optional[int] = None,
57+
channelized: bool = True,
58+
) -> str:
59+
rvfi = base_observers()
60+
defined_checks = base_checks()
61+
62+
# checks base_isa with a missing lb insn check
63+
# isa_checker = CompleteISAChecker(
64+
# name = name,
65+
# instructions = insns,
66+
# observers = rvfi,
67+
# channel = channel,
68+
# channelized = channelized if channelized is not None else channel is not None,
69+
# valid_opcodes = [
70+
# "1110011", # SYSTEM
71+
# ],
72+
# extra_valid_checks = [
73+
# "rvfi.insn[14:12] == 3'b 000 && rvfi.insn[6:0] == 7'b 0000011", # lb
74+
# ]
75+
# )
76+
8477
isa_checker = InstructionChecker(
8578
name = name,
8679
instructions = insns,
8780
observers = rvfi,
8881
defined_checks = defined_checks,
8982
channel = channel,
90-
channelized = channelized if channelized is not None else channel is not None
83+
channelized = channelized if channelized is not None else channel is not None,
9184
)
9285

9386
isa_checker.configure_io()

src/checks/instruction_checker.py

Lines changed: 80 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -11,42 +11,26 @@
1111
SpeculativeEvaluation,
1212
)
1313

14+
1415
@dataclass(kw_only=True)
15-
class InstructionChecker(GenericChecker):
16+
class InstructionCheckerBase(GenericChecker):
1617
instructions: dict[str, Instruction] = field(default_factory=dict)
1718
observers: dict[str, Observer] = field(default_factory=dict)
1819

19-
defined_checks: list[SpeculativeEvaluation] = field(default_factory=list)
20-
21-
registered_checks: ClassVar[list[SpeculativeEvaluation]] = []
22-
registered_verilog: ClassVar[list[str]] = []
2320
registered_speculators: ClassVar[dict[str, tuple[SpeculativeObserver, Callable[[Instruction], Optional[str]]]]] = {}
24-
registered_hw_traps: ClassVar[dict[str, Callable[[dict[str, Observer]], str]]] = {}
2521

2622
def configure_io(self):
2723
for insn in self.instructions.values():
2824
insn.select_inputs(self.observers.values())
2925
insn.select_outputs([o for o in self.observers.values() if isinstance(o, SpeculativeObserver)])
3026

31-
@classmethod
32-
def register_check(cls, check: SpeculativeEvaluation):
33-
cls.registered_checks.append(check)
34-
35-
@classmethod
36-
def register_verilog(cls, verilog: str):
37-
cls.registered_verilog.append(verilog)
38-
3927
@classmethod
4028
def register_speculator(cls,
4129
spec_obs: SpeculativeObserver,
4230
speculator: Callable[[Instruction], Optional[str]],
4331
):
4432
cls.registered_speculators[spec_obs.name] = (spec_obs, speculator)
4533

46-
@classmethod
47-
def register_hw_trap(cls, condition: str, handler: Callable[[dict[str, Observer]], str]):
48-
cls.registered_hw_traps[condition] = handler
49-
5034
def _v_io(self) -> str:
5135
# macro defined RVFI inputs
5236
return dedent("""\
@@ -124,6 +108,47 @@ def _v_instantiation(self) -> str:
124108
v_str += " end"
125109
v_str += "\nend\n"
126110

111+
return v_str
112+
113+
def _v_spec_check(self) -> str:
114+
raise NotImplementedError()
115+
116+
def _v_body(self) -> str:
117+
v_str = self._v_format_block(self._v_rvfi_channel())
118+
v_str += self._v_format_block(self._v_instantiation())
119+
v_str += self._v_format_block(self._v_spec_check())
120+
return v_str
121+
122+
def to_verilog(self, xlen: int):
123+
v_str = ""
124+
for insn in self.instructions.values():
125+
v_str += insn.to_verilog(xlen) + '\n\n'
126+
return v_str + super().to_verilog()
127+
128+
129+
@dataclass(kw_only=True)
130+
class InstructionChecker(InstructionCheckerBase):
131+
defined_checks: list[SpeculativeEvaluation] = field(default_factory=list)
132+
133+
registered_checks: ClassVar[list[SpeculativeEvaluation]] = []
134+
registered_verilog: ClassVar[list[str]] = []
135+
registered_hw_traps: ClassVar[dict[str, Callable[[dict[str, Observer]], str]]] = {}
136+
137+
@classmethod
138+
def register_check(cls, check: SpeculativeEvaluation):
139+
cls.registered_checks.append(check)
140+
141+
@classmethod
142+
def register_verilog(cls, verilog: str):
143+
cls.registered_verilog.append(verilog)
144+
145+
@classmethod
146+
def register_hw_trap(cls, condition: str, handler: Callable[[dict[str, Observer]], str]):
147+
cls.registered_hw_traps[condition] = handler
148+
149+
def _v_instantiation(self) -> str:
150+
v_str = super()._v_instantiation()
151+
127152
if self.registered_verilog:
128153
v_str += "\n"
129154
v_str += "\n\n".join(self.registered_verilog)
@@ -180,14 +205,42 @@ def _v_spec_check(self) -> str:
180205
)
181206
return v_str
182207

183-
def _v_body(self) -> str:
184-
v_str = self._v_format_block(self._v_rvfi_channel())
185-
v_str += self._v_format_block(self._v_instantiation())
186-
v_str += self._v_format_block(self._v_spec_check())
187-
return v_str
188208

189-
def to_verilog(self, xlen: int):
209+
@dataclass(kw_only=True)
210+
class CompleteISAChecker(InstructionCheckerBase):
211+
valid_opcodes: list[str] = field(default_factory=list)
212+
extra_valid_checks: list[str] = field(default_factory=list)
213+
214+
def _v_spec_check(self) -> str:
190215
v_str = ""
191-
for insn in self.instructions.values():
192-
v_str += insn.to_verilog(xlen) + '\n\n'
193-
return v_str + super().to_verilog()
216+
217+
v_str += "wire opcode_valid = "
218+
if self.valid_opcodes:
219+
op_checks: list[str] = []
220+
for op in self.valid_opcodes:
221+
if len(op) == 7:
222+
try:
223+
int(op, 2)
224+
except ValueError:
225+
# not valid binary
226+
pass
227+
else:
228+
op = f"7'b {op}"
229+
op_checks.append(f"(rvfi.insn[6:0] == {op})")
230+
v_str += "\n || ".join(op_checks)
231+
else:
232+
v_str += "0"
233+
v_str += ";\n\n"
234+
235+
v_str += "wire extra_valid = "
236+
v_str += "\n || ".join(f"({check})" for check in self.extra_valid_checks) or "0"
237+
v_str += ";\n\n"
238+
239+
v_str +=dedent("""\
240+
always @* begin
241+
if (!reset && rvfi.valid && !rvfi.trap && !opcode_valid && !extra_valid) begin
242+
assert(spec_valid && !spec_trap);
243+
end
244+
end""")
245+
246+
return v_str

src/insns/__init__.py

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
1-
from . import model, wrapped_model, builtins
1+
from .model import (
2+
Instruction,
3+
MemoryInstruction,
4+
AltopsInstruction,
5+
)
26

3-
Instruction = model.Instruction
4-
MemoryInstruction = model.MemoryInstruction
5-
AltopsInstruction = model.AltopsInstruction
6-
WrappedInstruction = wrapped_model.WrappedInstruction
7-
builtins = builtins.builtins
7+
from .wrapped_model import WrappedInstruction
8+
9+
from .builtins import builtins

src/rvfi/__init__.py

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
1-
from . import observer, speculative_evaluation
1+
from .observer import (
2+
Observer,
3+
SpeculativeObserver,
4+
ZeroedObserver,
5+
)
26

3-
Observer = observer.Observer
4-
SpeculativeObserver = observer.SpeculativeObserver
5-
ZeroedObserver = observer.ZeroedObserver
7+
from .speculative_evaluation import SpeculativeEvaluation
68

7-
SpeculativeEvaluation = speculative_evaluation.SpeculativeEvaluation
9+
from .base import base_observers

src/rvfi/base.py

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
from .observer import Observer, SpeculativeObserver, ZeroedObserver
2+
3+
def base_observers() -> dict[str, Observer]:
4+
return {o.name: o for o in [
5+
SpeculativeObserver("valid", "1"),
6+
Observer("order", "64"),
7+
Observer("insn", "`RISCV_FORMAL_ILEN"),
8+
SpeculativeObserver("trap", "1"),
9+
Observer("halt", "1"),
10+
Observer("intr", "1"),
11+
Observer("mode", "2"),
12+
Observer("ixl", "2"),
13+
SpeculativeObserver("rs1_addr", "5", "rvfi.rs1_addr"),
14+
SpeculativeObserver("rs2_addr", "5", "rvfi.rs2_addr"),
15+
ZeroedObserver("rs1_rdata", "`RISCV_FORMAL_XLEN", "spec_rs1_addr == 0"),
16+
ZeroedObserver("rs2_rdata", "`RISCV_FORMAL_XLEN", "spec_rs2_addr == 0"),
17+
SpeculativeObserver("rd_addr", "5"),
18+
SpeculativeObserver("rd_wdata", "`RISCV_FORMAL_XLEN"),
19+
Observer("pc_rdata", "`RISCV_FORMAL_XLEN"),
20+
SpeculativeObserver("pc_wdata", "`RISCV_FORMAL_XLEN", "rvfi.pc_rdata + 4"),
21+
SpeculativeObserver("mem_addr", "`RISCV_FORMAL_XLEN"),
22+
SpeculativeObserver("mem_rmask", "`RISCV_FORMAL_XLEN/8"),
23+
SpeculativeObserver("mem_wmask", "`RISCV_FORMAL_XLEN/8"),
24+
Observer("mem_rdata", "`RISCV_FORMAL_XLEN"),
25+
SpeculativeObserver("mem_wdata", "`RISCV_FORMAL_XLEN"),
26+
]}

src/rvfi/mem_fault.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
from textwrap import dedent
22

3-
from ..checks.instruction_checker import InstructionChecker
3+
from ..checks import InstructionChecker
44
from .observer import Observer
55

66
def mem_fault_handler(observers: dict[str, Observer]) -> str:

src/rvfi/misa_fault.py

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,13 @@
11
from textwrap import dedent
22
from typing import Optional
33

4-
from ..checks.instruction_checker import InstructionChecker
4+
from ..checks import InstructionChecker
55
from ..insns import Instruction
6-
from .observer import Observer, SpeculativeObserver
7-
from . import SpeculativeEvaluation
6+
from . import (
7+
Observer,
8+
SpeculativeObserver,
9+
SpeculativeEvaluation,
10+
)
811

912
MISA_MAP = {
1013
"A": 1 << 0, # Atomic

0 commit comments

Comments
 (0)