[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)