[otbn] Spot invalid increments in BN.MOVR/BN.LID/BN.SID in ISS
Signed-off-by: Rupert Swarbrick <rswarbrick@lowrisc.org>
diff --git a/hw/ip/otbn/dv/otbnsim/sim/decode.py b/hw/ip/otbn/dv/otbnsim/sim/decode.py
index 5383246..60eabef 100644
--- a/hw/ip/otbn/dv/otbnsim/sim/decode.py
+++ b/hw/ip/otbn/dv/otbnsim/sim/decode.py
@@ -7,7 +7,8 @@
import struct
from typing import List, Optional, Tuple, Type
-from .isa import OTBNInsn
+from .alert import Alert, ERR_CODE_ILLEGAL_INSN
+from .isa import DecodeError, OTBNInsn
from .insn import INSN_CLASSES
from .state import OTBNState
@@ -17,6 +18,18 @@
_MaskTuple = Tuple[int, int, Type[OTBNInsn]]
+class IllegalInsnError(Alert):
+ '''Raised on a bad instruction'''
+ err_code = ERR_CODE_ILLEGAL_INSN
+
+ def __init__(self, word: int, msg: str):
+ self.word = word
+ self.msg = msg
+
+ def __str__(self) -> str:
+ return ('Illegal instruction {:#010x}: {}'.format(self.word, self.msg))
+
+
class IllegalInsn(OTBNInsn):
'''A catch-all subclass of Instruction for bad data
@@ -29,12 +42,16 @@
we know this doesn't match any real instruction.
'''
- def __init__(self, word: int) -> None:
- self.word = word
+ def __init__(self, pc: int, raw: int, msg: str) -> None:
+ super().__init__(raw, {})
+ self.msg = msg
+
+ # Override the memoized disassembly for the instruction, avoiding us
+ # disassembling the underlying DummyInsn.
+ self._disasm = (pc, '?? 0x{:08x}'.format(raw))
def execute(self, state: OTBNState) -> None:
- raise RuntimeError('Illegal instruction at {:#x}: encoding {:#010x}.'
- .format(int(state.pc), self.word))
+ raise IllegalInsnError(self.raw, self.msg)
MASK_TUPLES = None # type: Optional[List[_MaskTuple]]
@@ -68,7 +85,7 @@
return MASK_TUPLES
-def _decode_word(word_off: int, word: int) -> OTBNInsn:
+def _decode_word(pc: int, word: int) -> OTBNInsn:
found_cls = None
for m0, m1, cls in get_insn_masks():
# If any bit is set that should be zero or if any bit is clear that
@@ -80,7 +97,7 @@
break
if found_cls is None:
- return IllegalInsn(word)
+ return IllegalInsn(pc, word, 'No legal decoding')
# Decode the instruction. We know that we have an encoding (we checked in
# get_insn_masks).
@@ -89,18 +106,24 @@
# Make sense of these encoded values as "operand values" (doing any
# shifting, sign interpretation etc.)
- op_vals = cls.insn.enc_vals_to_op_vals(4 * word_off, enc_vals)
+ op_vals = cls.insn.enc_vals_to_op_vals(pc, enc_vals)
- return cls(word, op_vals)
+ # Catch any decode errors raised by the instruction constructor. This lets
+ # us generate errors if an instruction encoding has extra constraints that
+ # can't be captured by the logic in the Encoding class.
+ try:
+ return cls(word, op_vals)
+ except DecodeError as err:
+ return IllegalInsn(pc, word, str(err))
-def decode_bytes(data: bytes) -> List[OTBNInsn]:
+def decode_bytes(base_addr: int, data: bytes) -> List[OTBNInsn]:
'''Decode instruction bytes as instructions'''
assert len(data) & 3 == 0
- return [_decode_word(offset, int_val[0])
+ return [_decode_word(base_addr + 4 * offset, int_val[0])
for offset, int_val in enumerate(struct.iter_unpack('<I', data))]
-def decode_file(path: str) -> List[OTBNInsn]:
+def decode_file(base_addr: int, path: str) -> List[OTBNInsn]:
with open(path, 'rb') as handle:
- return decode_bytes(handle.read())
+ return decode_bytes(base_addr, handle.read())
diff --git a/hw/ip/otbn/dv/otbnsim/sim/elf.py b/hw/ip/otbn/dv/otbnsim/sim/elf.py
index f293f73..32eec53 100644
--- a/hw/ip/otbn/dv/otbnsim/sim/elf.py
+++ b/hw/ip/otbn/dv/otbnsim/sim/elf.py
@@ -133,7 +133,7 @@
'not a multiple of 4.'
.format(path, len(imem_bytes)))
- imem_insns = decode_bytes(imem_bytes)
+ imem_insns = decode_bytes(0, imem_bytes)
sim.load_program(imem_insns)
sim.load_data(dmem_bytes)
diff --git a/hw/ip/otbn/dv/otbnsim/sim/insn.py b/hw/ip/otbn/dv/otbnsim/sim/insn.py
index a125c7a..594abee 100644
--- a/hw/ip/otbn/dv/otbnsim/sim/insn.py
+++ b/hw/ip/otbn/dv/otbnsim/sim/insn.py
@@ -6,7 +6,7 @@
from .alert import LoopError
from .flags import FlagReg
-from .isa import (OTBNInsn, RV32RegReg, RV32RegImm, RV32ImmShift,
+from .isa import (DecodeError, OTBNInsn, RV32RegReg, RV32RegImm, RV32ImmShift,
insn_for_mnemonic, logical_byte_shift)
from .state import OTBNState
@@ -804,6 +804,9 @@
self.grs1 = op_vals['grs1']
self.grs1_inc = op_vals['grs1_inc']
+ if self.grd_inc and self.grs1_inc:
+ raise DecodeError('grd_inc and grs1_inc both set')
+
def execute(self, state: OTBNState) -> None:
grs1_val = state.gprs.get_reg(self.grs1).read_unsigned()
addr = (grs1_val + self.offset) & ((1 << 32) - 1)
@@ -833,6 +836,9 @@
self.grs1 = op_vals['grs1']
self.grs1_inc = op_vals['grs1_inc']
+ if self.grs1_inc and self.grs2_inc:
+ raise DecodeError('grs1_inc and grs2_inc both set')
+
def execute(self, state: OTBNState) -> None:
grs1_val = state.gprs.get_reg(self.grs1).read_unsigned()
addr = (grs1_val + self.offset) & ((1 << 32) - 1)
@@ -875,6 +881,9 @@
self.grs = op_vals['grs']
self.grs_inc = op_vals['grs_inc']
+ if self.grd_inc and self.grs_inc:
+ raise DecodeError('grd_inc and grs_inc both set')
+
def execute(self, state: OTBNState) -> None:
grd_val = state.gprs.get_reg(self.grd).read_unsigned()
grs_val = state.gprs.get_reg(self.grs).read_unsigned()
diff --git a/hw/ip/otbn/dv/otbnsim/sim/isa.py b/hw/ip/otbn/dv/otbnsim/sim/isa.py
index cc9772e..46a9f7c 100644
--- a/hw/ip/otbn/dv/otbnsim/sim/isa.py
+++ b/hw/ip/otbn/dv/otbnsim/sim/isa.py
@@ -21,6 +21,15 @@
sys.exit(1)
+class DecodeError(Exception):
+ '''An error raised when trying to decode malformed instruction bits'''
+ def __init__(self, msg: str):
+ self.msg = msg
+
+ def __str__(self) -> str:
+ return 'DecodeError: {}'.format(self.msg)
+
+
class DummyInsn(Insn):
'''A dummy instruction that will never be decoded. Used for the insn class
variable in the OTBNInsn base class.
diff --git a/hw/ip/otbn/dv/otbnsim/stepped.py b/hw/ip/otbn/dv/otbnsim/stepped.py
index 8fb63df..bda5be1 100755
--- a/hw/ip/otbn/dv/otbnsim/stepped.py
+++ b/hw/ip/otbn/dv/otbnsim/stepped.py
@@ -144,7 +144,7 @@
path = args[0]
print('LOAD_I {!r}'.format(path))
- sim.load_program(decode_file(path))
+ sim.load_program(decode_file(0, path))
def on_dump_d(sim: OTBNSim, args: List[str]) -> None: