[otbn] Add support for CSR/WSR instructions to RIG

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 5434f18..1d8e838 100644
--- a/hw/ip/otbn/util/rig/gens/straight_line_insn.py
+++ b/hw/ip/otbn/util/rig/gens/straight_line_insn.py
@@ -379,12 +379,16 @@
         # Ask the model to try to find a target we can use. If this is a load
         # or a CSR operation, it will have to be an address that already has an
         # architectural value. If a store, it can be any address in range.
+        #
+        # We cheat a bit for WSR stores. These don't actually load a value, but
+        # we still want to make sure we pick a valid WSR index, so we claim we
+        # loaded one anyway.
         lsu_type_to_info = {
             'mem-load': ('dmem', True),
             'mem-store': ('dmem', False),
             'csr': ('csr', True),
             'wsr-load': ('wsr', True),
-            'wsr-store': ('wsr', False)
+            'wsr-store': ('wsr', True)
         }
         assert set(lsu_type_to_info.keys()) == set(LSUDesc.TYPES)
         mem_type, loads_value = lsu_type_to_info[insn.lsu.lsu_type]
diff --git a/hw/ip/otbn/util/rig/model.py b/hw/ip/otbn/util/rig/model.py
index 05797a9..2d542b7 100644
--- a/hw/ip/otbn/util/rig/model.py
+++ b/hw/ip/otbn/util/rig/model.py
@@ -6,7 +6,6 @@
 import random
 from typing import Dict, List, Optional, Set, Tuple
 
-from shared.insn_yaml import Insn
 from shared.operand import (OperandType,
                             ImmOperandType, OptionOperandType, RegOperandType)
 
@@ -49,7 +48,7 @@
         self.known_ranges = []  # type: List[Tuple[int, int]]
 
     def touch_range(self, base: int, width: int) -> None:
-        '''Mark {base .. base+width} as known'''
+        '''Mark {base .. base + width - 1} as known'''
         assert 0 <= width
         assert 0 <= base <= self.top_addr - width
         for off in range(width):
@@ -349,14 +348,25 @@
         self._call_stack = []  # type: List[Optional[int]]
 
         # Known values for memory, keyed by memory type ('dmem', 'csr', 'wsr').
+        csrs = KnownMem(4096)
+        wsrs = KnownMem(4096)
         self._known_mem = {
             'dmem': KnownMem(dmem_size),
-            # TODO: How many CSRs/WSRs? Is that written down somewhere we can
-            # extract?
-            'csr': KnownMem(4096),
-            'wsr': KnownMem(4096)
+            'csr': csrs,
+            'wsr': wsrs
         }
 
+        # Valid CSRs and WSRs
+        csrs.touch_addr(0x7c0)      # FG0
+        csrs.touch_addr(0x7c1)      # FG1
+        csrs.touch_addr(0x7c8)      # FLAGS
+        csrs.touch_range(0x7d0, 8)  # MOD0 - MOD7
+        csrs.touch_addr(0xfc0)      # RND
+
+        wsrs.touch_addr(0x0)        # MOD
+        wsrs.touch_addr(0x1)        # RND
+        wsrs.touch_addr(0x2)        # ACC
+
         # The current PC (the address of the next instruction that needs
         # generating)
         self.pc = reset_addr
diff --git a/hw/ip/otbn/util/shared/operand.py b/hw/ip/otbn/util/shared/operand.py
index ef914b8..dc6dd7c 100644
--- a/hw/ip/otbn/util/shared/operand.py
+++ b/hw/ip/otbn/util/shared/operand.py
@@ -168,8 +168,6 @@
     TYPE_FMTS = {
         'gpr': (5, 'x'),
         'wdr': (5, 'w'),
-        'csr': (12, None),
-        'wsr': (8, None)
     }
 
     def __init__(self, reg_type: str, is_dest: bool) -> None:
@@ -569,8 +567,6 @@
         'grd': ('gpr', True),
         'wrs': ('wdr', False),
         'wrd': ('wdr', True),
-        'csr': ('csr', True),
-        'wsr': ('wsr', True)
     }
     reg_match = reg_fmts.get(fmt)
     if reg_match is not None:
@@ -582,6 +578,18 @@
         reg_type, is_dest = reg_match
         return RegOperandType.make(reg_type, is_dest, what, scheme_field)
 
+    # CSR and WSR indices. These are treated like unsigned immediates, with
+    # width 12 and 8, respectively.
+    xsr_fmts = {
+        'csr': 12,
+        'wsr': 8
+    }
+    xsr_match = xsr_fmts.get(fmt)
+    if xsr_match is not None:
+        assert not pc_rel
+        return ImmOperandType.make(xsr_match, 0, 0, False, False,
+                                   what, scheme_field)
+
     # Immediates
     for base, signed in [('simm', True), ('uimm', False)]:
         # The type of an immediate operand is encoded as