[otbn] Always generate an example syntax for an instruction
Before, we stored an insn.syntax of None if the syntax was the normal
bog-standard one (mnemonic op0, op1, op2). But that means you need
special cases later. This patch changes things so that we just
generate the bog-standard syntax as we go.
Signed-off-by: Rupert Swarbrick <rswarbrick@lowrisc.org>
diff --git a/hw/ip/otbn/util/insn_yaml.py b/hw/ip/otbn/util/insn_yaml.py
index 63cf7bb..373bd52 100644
--- a/hw/ip/otbn/util/insn_yaml.py
+++ b/hw/ip/otbn/util/insn_yaml.py
@@ -752,31 +752,32 @@
class InsnSyntax:
- def __init__(self, raw: str) -> None:
- # The raw syntax looks something like "<foo> + <bar> (baz <qux>)". We
- # need to check that each <..> holds an operand name. We want to
- # tokenize the string to split out the operands. The easiest way to
- # encode this in the types is as a string followed by zero or more
- # pairs, (operand, string).
- #
- # Conveniently, re.split does exactly what we need, always yielding an
- # odd number of parts and starting with an empty string if there's a
- # match at the start.
- parts = re.split(r'<([^>]+)>', raw)
- self.prefix = parts[0]
- self.pairs = list(zip(parts[1::2], parts[2::2]))
+ '''A class representing the syntax of an instruction
- assert len(parts) == 1 + 2 * len(self.pairs)
+ The pairs attribute gives the operands to the instruction, together with
+ the text between them. For example, if the instruction had the following
+ form:
- # Collect up the named operands that we've seen, checking for
- # duplicates
- self.operands = set() # type: Set[str]
- for operand, _ in self.pairs:
- if operand in self.operands:
- raise ValueError('Instruction syntax ({!r}) has duplicate '
- 'occurrence of the {!r} operand.'
- .format(raw, operand))
- self.operands.add(operand)
+ my_mnemonic (<op_0>, <op_1>), <op_2>
+
+ then it would be represented as:
+
+ prefix = '('
+ pairs = [('op_0', ', '),
+ ('op_1', '), '),
+ ('op_2', '')]
+
+ The operands attribute is just the set of operand names (in the example, it
+ would be {'op_0', 'op_1', 'op_2'}).
+
+ '''
+ def __init__(self,
+ prefix: str,
+ pairs: List[Tuple[str, str]],
+ operands: Set[str]) -> None:
+ self.prefix = prefix
+ self.pairs = pairs
+ self.operands = operands
def raw_string(self) -> str:
'''Return the raw string of the syntax'''
@@ -786,6 +787,39 @@
parts.append(suffix)
return ''.join(parts)
+ @staticmethod
+ def from_list(operands: List[str]) -> 'InsnSyntax':
+ op_set = set(operands)
+ assert len(op_set) == len(operands)
+ return InsnSyntax('', [(op, ', ') for op in operands], op_set)
+
+ @staticmethod
+ def from_yaml(raw: str) -> 'InsnSyntax':
+ '''Parse the syntax in the YAML file'''
+
+ # The raw syntax looks something like "<foo> + <bar> (baz <qux>)". We
+ # need to check that each <..> holds an operand name. Conveniently,
+ # re.split does exactly what we need, always yielding an odd number of
+ # parts and starting with an empty string if there's a match at the
+ # start.
+ parts = re.split(r'<([^>]+)>', raw)
+ prefix = parts[0]
+ pairs = list(zip(parts[1::2], parts[2::2]))
+
+ assert len(parts) == 1 + 2 * len(pairs)
+
+ # Collect up the named operands that we've seen, checking for
+ # duplicates
+ operands = set() # type: Set[str]
+ for operand, _ in pairs:
+ if operand in operands:
+ raise ValueError('Instruction syntax ({!r}) has duplicate '
+ 'occurrence of the {!r} operand.'
+ .format(raw, operand))
+ operands.add(operand)
+
+ return InsnSyntax(prefix, pairs, operands)
+
class EncodingField:
'''A single element of an encoding's mapping'''
@@ -962,19 +996,21 @@
self.operation = get_optional_str(yd, 'operation', what)
raw_syntax = get_optional_str(yd, 'syntax', what)
- self.syntax = None # type: Optional[InsnSyntax]
if raw_syntax is not None:
- self.syntax = InsnSyntax(raw_syntax)
+ self.syntax = InsnSyntax.from_yaml(raw_syntax)
+ else:
+ op_name_list = [op.name for op in self.operands]
+ self.syntax = InsnSyntax.from_list(op_name_list)
- # Make sure we have exactly the operands we expect.
- if set(self.name_to_operand.keys()) != self.syntax.operands:
- raise ValueError("Operand syntax for {!r} doesn't have the "
- "same list of operands as given in the "
- "operand list. The syntax uses {}, "
- "but the list of operands gives {}."
- .format(self.mnemonic,
- list(sorted(self.syntax.operands)),
- list(sorted(self.name_to_operand))))
+ # Make sure we have exactly the operands we expect.
+ if set(self.name_to_operand.keys()) != self.syntax.operands:
+ raise ValueError("Operand syntax for {!r} doesn't have the "
+ "same list of operands as given in the "
+ "operand list. The syntax uses {}, "
+ "but the list of operands gives {}."
+ .format(self.mnemonic,
+ list(sorted(self.syntax.operands)),
+ list(sorted(self.name_to_operand))))
encoding_yml = yd.get('encoding')
self.encoding = None
diff --git a/hw/ip/otbn/util/yaml_to_doc.py b/hw/ip/otbn/util/yaml_to_doc.py
index 10b98f6..818119a 100755
--- a/hw/ip/otbn/util/yaml_to_doc.py
+++ b/hw/ip/otbn/util/yaml_to_doc.py
@@ -170,10 +170,7 @@
# Syntax example: either given explicitly or figured out from operands
parts.append("```\n")
parts.append(insn.mnemonic.upper() + ('' if insn.glued_ops else ' '))
- if insn.syntax is not None:
- parts.append(insn.syntax.raw_string())
- else:
- parts.append(', '.join('<{}>'.format(op.name) for op in insn.operands))
+ parts.append(insn.syntax.raw_string())
parts.append("\n```\n\n")
# If this came from the RV32I instruction set, say so.