[otbn] Avoid getting stuck in otbn-rig
This avoids painting ourselves into a corner with straight-line
instructions. Once we start generating jumps, there's a possibility of
ending up with e.g. a current PC of 100 and some other instructions
already defined at address 104. Then we *have* to generate a jump or
an ECALL, or we'll crash into stuff that's already defined (causing
infinite loops and other badness when you try to run the test!).
Signed-off-by: Rupert Swarbrick <rswarbrick@lowrisc.org>
diff --git a/hw/ip/otbn/util/rig/gens/straight_line_insn.py b/hw/ip/otbn/util/rig/gens/straight_line_insn.py
index 640ae09..39a54b4 100644
--- a/hw/ip/otbn/util/rig/gens/straight_line_insn.py
+++ b/hw/ip/otbn/util/rig/gens/straight_line_insn.py
@@ -42,6 +42,15 @@
model: Model,
program: Program) -> Optional[Tuple[Snippet, bool, int]]:
+ # Return None if this is the last instruction in the current gap
+ # because we need to either jump or do an ECALL to avoid getting stuck.
+ #
+ # Note that we could do this by defining pick_weight, but we don't
+ # expect it to happen very often so it's probably best (and cheaper)
+ # just to disable ourselves on the rare occasions when it does.
+ if program.get_blank_insns_above(model.pc) <= 1:
+ return None
+
# Pick a (YAML) instruction at random. We'll probably do some clever
# weighting here later on but, for now, we'll pick uniformly at the
# start.
diff --git a/hw/ip/otbn/util/rig/program.py b/hw/ip/otbn/util/rig/program.py
index 3ea5dc9..3fa2191 100644
--- a/hw/ip/otbn/util/rig/program.py
+++ b/hw/ip/otbn/util/rig/program.py
@@ -299,3 +299,24 @@
assert len(tgts) == 1
return tgts[0]
+
+ def get_blank_insns_above(self, addr: int) -> int:
+ '''Return the number of instructions that fit above addr'''
+ bytes_above = self.imem_size - addr
+ if bytes_above <= 0:
+ return 0
+
+ for sec_base, sec_insns in self._sections.items():
+ sec_top = sec_base + 4 * len(sec_insns)
+ if addr < sec_top:
+ bytes_above = min(bytes_above, sec_base - addr)
+ if bytes_above <= 0:
+ return 0
+
+ if self._cur_section is not None:
+ sec_base, open_section = self._cur_section
+ sec_top = sec_base + 4 * len(open_section.insns)
+ if addr < sec_top:
+ bytes_above = min(bytes_above, sec_base - addr)
+
+ return max(0, bytes_above // 4)