[otbn] Allow short names for operands in encoding docs

This is just aesthetic, but avoids gigantic names like "OFFSET_1" from
appearing in encoding tables. This improves the layout a bit, and
helps decouple the upper-case "field name" from the lower case
"operand name" in the reader's mind.

Signed-off-by: Rupert Swarbrick <rswarbrick@lowrisc.org>
diff --git a/hw/ip/otbn/data/base-insns.yml b/hw/ip/otbn/data/base-insns.yml
index 3b3fe66..2437fca 100644
--- a/hw/ip/otbn/data/base-insns.yml
+++ b/hw/ip/otbn/data/base-insns.yml
@@ -239,7 +239,11 @@
 - mnemonic: lw
   rv32i: true
   synopsis: Load Word
-  operands: [grd, offset, grs1]
+  operands:
+    - grd
+    - name: offset
+      abbrev: "off"
+    - grs1
   syntax: <grd>, <offset>(<grs1>)
   encoding:
     scheme: I
@@ -262,7 +266,11 @@
 - mnemonic: sw
   rv32i: true
   synopsis: Store Word
-  operands: [grs2, offset, grs1]
+  operands:
+    - grs2
+    - name: offset
+      abbrev: "off"
+    - grs1
   syntax: <grs2>, <offset>(<grs1>)
   encoding:
     scheme: S
@@ -289,6 +297,7 @@
     - grs2
     - &branch-offset-operand
       name: offset
+      abbrev: "off"
       pc-rel: true
       type: simm<<1
   straight-line: false
@@ -431,6 +440,7 @@
       doc: Name of the GPR containing the number of iterations
     - &bodysize-operand
       name: bodysize
+      abbrev: sz
       type: uimm+1
       doc: Number of instructions in the loop body
   straight-line: false
diff --git a/hw/ip/otbn/data/bignum-insns.yml b/hw/ip/otbn/data/bignum-insns.yml
index 02d2c3d..b8228d6 100644
--- a/hw/ip/otbn/data/bignum-insns.yml
+++ b/hw/ip/otbn/data/bignum-insns.yml
@@ -16,16 +16,19 @@
       doc: Name of the second source WDR
     - &bn-shift-type-operand
       name: shift_type
+      abbrev: st
       type: enum(<<, >>)
       doc: |
         The direction of an optional shift applied to `<wrs2>`.
     - &bn-shift-bits-operand
       name: shift_bits
+      abbrev: sb
       type: uimm5<<3
       doc: |
         Number of bits by which to shift `<wrs2>`. Defaults to 0.
     - &bn-flag-group-operand
       name: flag_group
+      abbrev: fg
       type: uimm1
       doc: Flag group to use. Defaults to 0.
   syntax: &bn-add-syntax |
@@ -143,6 +146,7 @@
   operands:
     - &mulqacc-zero-acc
       name: zero_acc
+      abbrev: za
       type: option(.Z)
       doc: Zero the accumulator before accumulating the multiply result.
     - &mulqacc-wrs1
@@ -150,6 +154,7 @@
       doc: First source WDR
     - &mulqacc-wrs1-qwsel
       name: wrs1_qwsel
+      abbrev: q1
       type: uimm2
       doc: |
         Quarter-word select for `<wrs1>`.
@@ -164,6 +169,7 @@
       doc: Second source WDR
     - &mulqacc-wrs2-qwsel
       name: wrs2_qwsel
+      abbrev: q2
       type: uimm2
       doc: |
         Quarter-word select for `<wrs2>`.
@@ -175,6 +181,7 @@
         - `3`: Select `wrs1[WLEN-1:WLEN/4*3]` (most significant quarter-word)
     - &mulqacc-acc-shift-imm
       name: acc_shift_imm
+      abbrev: shift
       type: uimm2<<6
       doc: |
         The number of bits to shift the `WLEN/2`-bit multiply result before accumulating.
@@ -264,6 +271,7 @@
     - *mulqacc-zero-acc
     - *mulqacc-wrd
     - name: wrd_hwsel
+      abbrev: dh
       type: enum(L,U)
       doc: |
         Half-word select for `<wrd>`.
@@ -698,16 +706,19 @@
         Name of the GPR containing the memory byte address.
         The value contained in the referenced GPR must be WLEN-aligned.
     - name: offset
+      abbrev: "off"
       doc: |
         Offset value.
         Must be WLEN-aligned.
       type: simm<<5
     - name: grs1_inc
+      abbrev: inc1
       type: option(++)
       doc: |
         Increment the value in `<grs1>` by WLEN/8 (one word).
         Cannot be specified together with `grd_inc`.
     - name: grd_inc
+      abbrev: incd
       type: option(++)
       doc: |
         Increment the value in `<grd>` by one.
@@ -768,17 +779,20 @@
     - name: grs2
       doc: Name of the GPR referencing the source WDR.
     - name: offset
+      abbrev: "off"
       doc: |
         Offset value.
         Must be WLEN-aligned.
       type: simm<<5
     - name: grs1_inc
       type: option(++)
+      abbrev: inc1
       doc: |
         Increment the value in `<grs1>` by WLEN/8 (one word).
         Cannot be specified together with `grs2_inc`.
     - name: grs2_inc
       type: option(++)
+      abbrev: inc2
       doc: |
         Increment the value in `<grs2>` by one.
         Cannot be specified together with `grs1_inc`.
diff --git a/hw/ip/otbn/data/insns.yml b/hw/ip/otbn/data/insns.yml
index c0ed391..8fff402 100644
--- a/hw/ip/otbn/data/insns.yml
+++ b/hw/ip/otbn/data/insns.yml
@@ -92,6 +92,10 @@
 #
 #  name:      The name of the operand. Required and must be unique.
 #
+#  abbrev:    An optional shortened name for the operand. If given, it
+#             must not match the short name or full name of any other
+#             operand.
+#
 #  type:      The type of the operand. A string. If this can be figured out
 #             from the operand name, it is optional. See below for a list of
 #             possible operand types and the rules for recognising them
diff --git a/hw/ip/otbn/util/shared/insn_yaml.py b/hw/ip/otbn/util/shared/insn_yaml.py
index 7029a45..d9ebaee 100644
--- a/hw/ip/otbn/util/shared/insn_yaml.py
+++ b/hw/ip/otbn/util/shared/insn_yaml.py
@@ -53,6 +53,19 @@
                                           self.operands,
                                           lambda op: op.name)
 
+        # The call to index_list has checked that operand names are distinct.
+        # We also need to check that no operand abbreviation clashes with
+        # anything else.
+        operand_names = set(self.name_to_operand.keys())
+        for op in self.operands:
+            if op.abbrev is not None:
+                if op.abbrev in operand_names:
+                    raise ValueError('The name {!r} appears as an operand or '
+                                     'abbreviation more than once for '
+                                     'instruction {!r}.'
+                                     .format(op.abbrev, self.mnemonic))
+                operand_names.add(op.abbrev)
+
         if self.encoding is not None:
             # If we have an encoding, we passed it to the Operand constructors
             # above. This ensured that each operand has a field. However, it's
diff --git a/hw/ip/otbn/util/shared/operand.py b/hw/ip/otbn/util/shared/operand.py
index 1b0c0be..033bf96 100644
--- a/hw/ip/otbn/util/shared/operand.py
+++ b/hw/ip/otbn/util/shared/operand.py
@@ -757,16 +757,22 @@
         # dict.
         what = 'operand for {!r} instruction'.format(mnemonic)
         if isinstance(yml, str):
-            name = yml
+            name = yml.lower()
+            abbrev = None
             op_type = None
             doc = None
             pc_rel = False
             op_what = '{!r} {}'.format(name, what)
         elif isinstance(yml, dict):
-            yd = check_keys(yml, what, ['name'], ['type', 'pc-rel', 'doc'])
-            name = check_str(yd['name'], 'name of ' + what)
+            yd = check_keys(yml, what,
+                            ['name'],
+                            ['type', 'pc-rel', 'doc', 'abbrev'])
+            name = check_str(yd['name'], 'name of ' + what).lower()
 
             op_what = '{!r} {}'.format(name, what)
+            abbrev = get_optional_str(yd, 'abbrev', op_what)
+            if abbrev is not None:
+                abbrev = abbrev.lower()
             op_type = get_optional_str(yd, 'type', op_what)
             pc_rel = check_bool(yd.get('pc-rel', False),
                                 'pc-rel field of ' + op_what)
@@ -784,7 +790,14 @@
                                  .format(mnemonic, name))
             enc_scheme_field = insn_encoding.fields[field_name].scheme_field
 
+        if abbrev is not None:
+            if name == abbrev:
+                raise ValueError('Operand {!r} of the {!r} instruction has '
+                                 'an abbreviated name the same as its '
+                                 'actual name.'
+                                 .format(name, mnemonic))
         self.name = name
+        self.abbrev = abbrev
         self.op_type = make_operand_type(op_type, pc_rel, name,
                                          mnemonic, enc_scheme_field)
         self.doc = doc
diff --git a/hw/ip/otbn/util/yaml_to_doc.py b/hw/ip/otbn/util/yaml_to_doc.py
index 3dbd2a1..8e698f3 100755
--- a/hw/ip/otbn/util/yaml_to_doc.py
+++ b/hw/ip/otbn/util/yaml_to_doc.py
@@ -156,14 +156,16 @@
     return ''.join(parts)
 
 
-def name_op_enc_fields(encoding: Encoding) -> _O2EDicts:
+def name_op_enc_fields(name_to_operand: Dict[str, Operand],
+                       encoding: Encoding) -> _O2EDicts:
     '''Name the encoding fields corresponding to operators
 
     In the generated documentation, we name encoding fields based on the
     operand that the encode. For example, if the operand "foo" is encoded in a
     field, the field will be labelled "FOO" in the table. If the field is split
     over multiple bit ranges, they will be labelled like "FOO_0", "FOO_1" etc,
-    counting from the LSB.
+    counting from the LSB. If an operand has an abbreviated name, this will be
+    used for the field instead of the full operand name.
 
     Returns a pair of dicts: (o2e, e2o). o2e maps an operand name to the list
     of (our names for) encoding fields that contribute to it, MSB first. e2o
@@ -190,6 +192,14 @@
         # An encoding should never use an operand more than once
         assert operand_name not in o2e
 
+        # Get the base name to use for fields. This is either an upper-case
+        # version of the operand name, or uses the operand's abbreviated name
+        # if available.
+        operand = name_to_operand.get(operand_name)
+        assert operand is not None
+        basename = operand_name if operand.abbrev is None else operand.abbrev
+        basename = basename.upper()
+
         # There should always be at least one bit range for the field
         scheme_field = field.scheme_field
         assert scheme_field.bits.ranges
@@ -199,7 +209,7 @@
         if len(scheme_field.bits.ranges) == 1:
             msb = scheme_field.bits.ranges[0][0]
             assert msb not in e2o
-            range_name = operand_name.upper()
+            range_name = basename
             o2e[operand_name] = [range_name]
             e2o[msb] = range_name
             continue
@@ -207,9 +217,8 @@
         # Otherwise, we need to label the operands. Sorting ranges ensures that
         # they appear LSB-first.
         o2e_list = []
-        range_basename = operand_name.upper()
         for idx, (msb, lsb) in enumerate(sorted(scheme_field.bits.ranges)):
-            range_name = '{}_{}'.format(range_basename, idx)
+            range_name = '{}_{}'.format(basename, idx)
             o2e_list.append(range_name)
             assert msb not in e2o
             e2o[msb] = range_name
@@ -282,7 +291,7 @@
         o2e = None
         e2o = None
     else:
-        o2e, e2o = name_op_enc_fields(insn.encoding)
+        o2e, e2o = name_op_enc_fields(insn.name_to_operand, insn.encoding)
 
     # Show the operand table if there is at least one operand and this isn't a
     # pseudo-op.