[otbn, dv] Generate instructions with bad CSR / WSR addresses.
Following additions/ modifications are done in this commit:
1.bad_ispr.py: A new snippet generator that picks up instructions only
related to CSR/ WSR registers and replaces the good register address
with bad address.
2.known_mem.py: A method called pick_bad_addr is added to pick an
address from the gaps in known memory.
Signed-off-by: Prajwala Puttappa <prajwalaputtappa@lowrisc.org>
diff --git a/hw/ip/otbn/dv/rig/rig/configs/base.yml b/hw/ip/otbn/dv/rig/rig/configs/base.yml
index 3fece62..3ffe4f2 100644
--- a/hw/ip/otbn/dv/rig/rig/configs/base.yml
+++ b/hw/ip/otbn/dv/rig/rig/configs/base.yml
@@ -27,3 +27,4 @@
BadGiantLoop: 1
BadZeroLoop: 1
MisalignedLoadStore: 1
+ BadIspr: 1
diff --git a/hw/ip/otbn/dv/rig/rig/gens/bad_ispr.py b/hw/ip/otbn/dv/rig/rig/gens/bad_ispr.py
new file mode 100644
index 0000000..d22dc10
--- /dev/null
+++ b/hw/ip/otbn/dv/rig/rig/gens/bad_ispr.py
@@ -0,0 +1,97 @@
+# Copyright lowRISC contributors.
+# Licensed under the Apache License, Version 2.0, see LICENSE for details.
+# SPDX-License-Identifier: Apache-2.0
+
+import random
+from typing import Optional, Tuple
+
+from shared.insn_yaml import InsnsFile
+
+from ..config import Config
+from ..program import ProgInsn, Program
+from ..model import Model
+from ..snippet import ProgSnippet
+from ..snippet_gen import GenCont, GenRet, SnippetGen
+from .straight_line_insn import StraightLineInsn
+
+
+class BadIspr(SnippetGen):
+ ''' A snippet to generate load and store instructions to bad CSR/ WSR adresses '''
+
+ ends_program = True
+
+ def __init__(self, cfg: Config, insns_file: InsnsFile) -> None:
+ super().__init__()
+
+ self.insns = []
+ self.weights = []
+ self.straightgen = StraightLineInsn(cfg, insns_file)
+
+ for insn in insns_file.insns:
+ # Pick only the instructions pertaining to CSR and WSR.
+ if insn.mnemonic not in ['csrrw', 'csrrs', 'wsrs', 'wsrw']:
+ continue
+
+ assert not (insn.python_pseudo_op or insn.literal_pseudo_op)
+
+ # The instructions we're interested in are all straight-line
+ assert insn.straight_line
+
+ weight = cfg.insn_weights.get(insn.mnemonic)
+ if weight > 0:
+ self.insns.append(insn)
+ self.weights.append(weight)
+
+ # Disable the generator if no instruction has a positive weight
+ assert len(self.insns) == len(self.weights)
+ if not self.insns:
+ self.disabled = True
+
+ def gen(self,
+ cont: GenCont,
+ model: Model,
+ program: Program) -> Optional[GenRet]:
+ pc = model.pc
+ ret = self._gen(model, program)
+ if ret is None:
+ return None
+
+ prog_insn, model = ret
+ snippet = ProgSnippet(pc, [prog_insn])
+ snippet.insert_into_program(program)
+
+ return (snippet, True, model)
+
+ def _gen(self,
+ model: Model,
+ program: Program) -> Optional[Tuple[ProgInsn, Model]]:
+
+ weights = self.weights
+ prog_insn = None
+ while prog_insn is None:
+ idx = random.choices(range(len(self.insns)), weights=weights)[0]
+
+ # weights[idx] will be positive so long as at least one instruction has positive weight.
+ # If it's zero, it means that every weight is zero and we should give up.
+ if weights[idx] < 0:
+ return None
+
+ # Try to fill out the instruction. On failure, clear the weight for
+ # this index and go around again. We take the copy here, rather
+ # than outside the loop, because we don't expect this to happen
+ # very often.
+ prog_insn = self.straightgen.fill_insn(self.insns[idx], model)
+
+ if prog_insn is None:
+ weights = self.weights.copy()
+ weights[idx] = 0
+ continue
+
+ assert prog_insn.lsu_info is not None
+ mem_type, tgt_addr = prog_insn.lsu_info
+ # Replace good CSR / WSR address with bad address.
+ bad_addr = model.pick_bad_addr(mem_type)
+ assert bad_addr is not None
+ prog_insn.lsu_info = mem_type, bad_addr
+
+ return (prog_insn, model)
diff --git a/hw/ip/otbn/dv/rig/rig/known_mem.py b/hw/ip/otbn/dv/rig/rig/known_mem.py
index e370c4f..c0338c3 100644
--- a/hw/ip/otbn/dv/rig/rig/known_mem.py
+++ b/hw/ip/otbn/dv/rig/rig/known_mem.py
@@ -360,3 +360,25 @@
assert addr == ibase_addr + offset
return addr, offset
+
+ def pick_bad_addr(self) -> Optional[int]:
+ '''Pick bad addresses from gaps present in known addresses.'''
+
+ gap_list = []
+ gap_vma = 0
+ for low, high in self.known_ranges:
+ assert gap_vma <= low
+ if gap_vma < low:
+ gap_list.append((gap_vma, low - 1))
+ gap_vma = high + 1
+
+ if gap_vma <= self.top_addr:
+ gap_list.append((gap_vma, self.top_addr))
+
+ if not gap_list:
+ return None
+
+ gap_len = [1 + hi - lo for lo, hi in gap_list]
+ bad_addr_lo, bad_addr_hi = random.choices(gap_list, weights=gap_len)[0]
+
+ return random.randint(bad_addr_lo, bad_addr_hi)
diff --git a/hw/ip/otbn/dv/rig/rig/model.py b/hw/ip/otbn/dv/rig/rig/model.py
index 33644d1..ccda388 100644
--- a/hw/ip/otbn/dv/rig/rig/model.py
+++ b/hw/ip/otbn/dv/rig/rig/model.py
@@ -1055,3 +1055,6 @@
self._generic_update_for_insn(prog_insn)
self.consume_fuel()
+
+ def pick_bad_addr(self, mem_type: str) -> Optional[int]:
+ return self._known_mem[mem_type].pick_bad_addr()
diff --git a/hw/ip/otbn/dv/rig/rig/snippet_gens.py b/hw/ip/otbn/dv/rig/rig/snippet_gens.py
index 755065e..21da372 100644
--- a/hw/ip/otbn/dv/rig/rig/snippet_gens.py
+++ b/hw/ip/otbn/dv/rig/rig/snippet_gens.py
@@ -32,6 +32,7 @@
from .gens.bad_giant_loop import BadGiantLoop
from .gens.bad_load_store import BadLoadStore
from .gens.bad_zero_loop import BadZeroLoop
+from .gens.bad_ispr import BadIspr
from .gens.misaligned_load_store import MisalignedLoadStore
from .gens.untaken_branch import UntakenBranch
@@ -60,6 +61,7 @@
BadGiantLoop,
BadLoadStore,
BadZeroLoop,
+ BadIspr,
MisalignedLoadStore
]