[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/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.