diff --git a/doc/ug/otbn_sw.md b/doc/ug/otbn_sw.md index 6a17c88bab0b4..8e51144f45e28 100644 --- a/doc/ug/otbn_sw.md +++ b/doc/ug/otbn_sw.md @@ -23,14 +23,14 @@ For specific formatting and secure coding guidelines, see the [OTBN style guide] ### Assembler -The OTBN assembler is called `otbn-as` and can be found at `hw/ip/otbn/util/otbn-as`. +The OTBN assembler is called `otbn_as.py` and can be found at `hw/ip/otbn/util/otbn_as.py`. This has the same command line interface as `riscv32-unknown-elf-as` (indeed, it's a wrapper around that program). -The only difference in default flags is that `otbn-as` passes `-mno-relax`, telling the assembler not to request linker relaxation. +The only difference in default flags is that `otbn_as.py` passes `-mno-relax`, telling the assembler not to request linker relaxation. This is needed because one of these relaxations generates GP-relative loads, which assume `x3` is treated as a global pointer (not true for OTBN code). To assemble some code in `foo.s` to an ELF object called `foo.o`, run: ```shell -hw/ip/otbn/util/otbn-as -o foo.o foo.s +hw/ip/otbn/util/otbn_as.py -o foo.o foo.s ``` ### Linker diff --git a/hw/ip/otbn/README.md b/hw/ip/otbn/README.md index 6bba83e1b8d6b..c3e0d6ec39bd6 100644 --- a/hw/ip/otbn/README.md +++ b/hw/ip/otbn/README.md @@ -22,11 +22,11 @@ toolchain](https://docs.opentitan.org/doc/ug/install_instructions/#software-deve For more details about the toolchain, see the [user guide](https://docs.opentitan.org/doc/ug/otbn_sw)). -`otbn-as` and `otbn_ld.py` can be used to build .elf files for use with +`otbn_as.py` and `otbn_ld.py` can be used to build .elf files for use with simulations. They work work similarly to binutils programs they wrap. ``` -hw/ip/otbn/util/otbn-as -o prog_bin/prog.o prog.s +hw/ip/otbn/util/otbn_as.py -o prog_bin/prog.o prog.s hw/ip/otbn/util/otbn_ld.py -o prog_bin/prog.elf prog_bin/prog.o ``` @@ -50,7 +50,7 @@ users include: ### Run the Python simulator The quickest way to run an OTBN-only program is to use the Python simulator. First, generate a `.elf.` file either using the usual build process or by -manually running `otbn-as` and `otbn_ld.py` as shown above. Then, from `$REPO_TOP`: +manually running `otbn_as.py` and `otbn_ld.py` as shown above. Then, from `$REPO_TOP`: ```console $ hw/ip/otbn/dv/otbnsim/standalone.py -t path/to/prog.elf ``` diff --git a/hw/ip/otbn/doc/_index.md b/hw/ip/otbn/doc/_index.md index 7409d6e5ba22d..dcb0d862224fb 100644 --- a/hw/ip/otbn/doc/_index.md +++ b/hw/ip/otbn/doc/_index.md @@ -1109,7 +1109,7 @@ OTBN comes with a toolchain consisting of an assembler, a linker, and helper too The toolchain wraps a RV32 GCC toolchain and supports many of its features. The following tools are available: -* `otbn-as`: The OTBN assembler. +* `otbn_as.py`: The OTBN assembler. * `otbn_ld.py`: The OTBN linker. * `otbn-objdump`: objdump for OTBN. diff --git a/hw/ip/otbn/dv/otbnsim/test/simple/insns/addi.s b/hw/ip/otbn/dv/otbnsim/test/simple/insns/addi.s index 5e83f310184a5..4fc545edce3d3 100644 --- a/hw/ip/otbn/dv/otbnsim/test/simple/insns/addi.s +++ b/hw/ip/otbn/dv/otbnsim/test/simple/insns/addi.s @@ -6,7 +6,7 @@ Simple tests of the addi instruction This test also uses a comment syntax that broke the parser in a - previous version of otbn-as: if editing it, keep the block + previous version of otbn_as.py: if editing it, keep the block comments on the same lines as instructions to keep that test in place. diff --git a/hw/ip/otbn/dv/otbnsim/test/testutil.py b/hw/ip/otbn/dv/otbnsim/test/testutil.py index a25170c020f5d..47ca999d797d2 100644 --- a/hw/ip/otbn/dv/otbnsim/test/testutil.py +++ b/hw/ip/otbn/dv/otbnsim/test/testutil.py @@ -21,7 +21,7 @@ def asm_and_link_one_file(asm_path: str, work_dir: py.path.local) -> str: Returns the path to the resulting ELF ''' - otbn_as = os.path.join(UTIL_DIR, 'otbn-as') + otbn_as = os.path.join(UTIL_DIR, 'otbn_as.py') otbn_ld = os.path.join(UTIL_DIR, 'otbn_ld.py') obj_path = os.path.join(work_dir, 'tst.o') elf_path = os.path.join(work_dir, 'tst') diff --git a/hw/ip/otbn/dv/rig/README.md b/hw/ip/otbn/dv/rig/README.md index 2bae86ab2b078..1def062a03ec0 100644 --- a/hw/ip/otbn/dv/rig/README.md +++ b/hw/ip/otbn/dv/rig/README.md @@ -86,7 +86,7 @@ are an assembly listing and a linker script, respectively. To assemble and link, use commands like: ``` -hw/ip/otbn/util/otbn-as -o foo.o foo.s +hw/ip/otbn/util/otbn_as.py -o foo.o foo.s hw/ip/otbn/util/otbn_ld.py -o foo.elf -T foo.ld foo.o ``` This is automated in the `gen-binaries.py` wrapper described above. diff --git a/hw/ip/otbn/dv/smoke/run_smoke.sh b/hw/ip/otbn/dv/smoke/run_smoke.sh index 3fa5ab19ea83f..eb5c78007e053 100755 --- a/hw/ip/otbn/dv/smoke/run_smoke.sh +++ b/hw/ip/otbn/dv/smoke/run_smoke.sh @@ -27,7 +27,7 @@ mkdir -p $SMOKE_BIN_DIR OTBN_UTIL=$REPO_TOP/hw/ip/otbn/util -$OTBN_UTIL/otbn-as -o $SMOKE_BIN_DIR/smoke_test.o $SMOKE_SRC_DIR/smoke_test.s || \ +$OTBN_UTIL/otbn_as.py -o $SMOKE_BIN_DIR/smoke_test.o $SMOKE_SRC_DIR/smoke_test.s || \ fail "Failed to assemble smoke_test.s" $OTBN_UTIL/otbn_ld.py -o $SMOKE_BIN_DIR/smoke.elf $SMOKE_BIN_DIR/smoke_test.o || \ fail "Failed to link smoke_test.o" diff --git a/hw/ip/otbn/util/Makefile b/hw/ip/otbn/util/Makefile index 43fee3ba62e6c..eba33a9442f60 100644 --- a/hw/ip/otbn/util/Makefile +++ b/hw/ip/otbn/util/Makefile @@ -16,7 +16,7 @@ $(build-dir) $(lint-build-dir): mkdir -p $@ pylibs := $(wildcard shared/*.py docs/*.py) -pyscripts := yaml_to_doc.py otbn-as otbn_ld.py otbn-objdump +pyscripts := yaml_to_doc.py otbn_as.py otbn_ld.py otbn-objdump lint-stamps := $(foreach s,$(pyscripts),$(lint-build-dir)/$(s).stamp) $(lint-build-dir)/%.stamp: % $(pylibs) | $(lint-build-dir) diff --git a/hw/ip/otbn/util/otbn-as b/hw/ip/otbn/util/otbn_as.py similarity index 85% rename from hw/ip/otbn/util/otbn-as rename to hw/ip/otbn/util/otbn_as.py index 523fa4cbce0b5..3cf505add39ff 100755 --- a/hw/ip/otbn/util/otbn-as +++ b/hw/ip/otbn/util/otbn_as.py @@ -2,7 +2,6 @@ # Copyright lowRISC contributors. # Licensed under the Apache License, Version 2.0, see LICENSE for details. # SPDX-License-Identifier: Apache-2.0 - '''A wrapper around riscv32-unknown-elf-as for OTBN Partial support: @@ -27,7 +26,7 @@ from shared.bit_ranges import BitRanges from shared.encoding import Encoding from shared.insn_yaml import Insn, InsnsFile, load_insns_yaml -from shared.operand import ImmOperandType, RegOperandType, Operand +from shared.operand import ImmOperandType, Operand, RegOperandType from shared.toolchain import find_tool @@ -66,9 +65,8 @@ class RVFmt: suffix. ''' - def __init__(self, - name: str, - bitfields: List[str], + + def __init__(self, name: str, bitfields: List[str], syntax: List[str]) -> None: self.name = name self.operands = syntax @@ -134,8 +132,7 @@ def __init__(self, assert operand not in self.op_data assert op_fmt is not None - self.op_data[operand] = (op_fmt, - op_shift, + self.op_data[operand] = (op_fmt, op_shift, BitRanges.from_list(op_ranges)) assert len(b2o_dict) == 32 @@ -174,11 +171,9 @@ class RVEncoding: that has the given encoding as a .insn line (with the given RVFmt). ''' - def __init__(self, - encoding: Encoding, - rvfmt: RVFmt, - rv_masks: Dict[str, int], - rv_to_full_op: Dict[str, str], + + def __init__(self, encoding: Encoding, rvfmt: RVFmt, + rv_masks: Dict[str, int], rv_to_full_op: Dict[str, str], part_field_to_rv: Dict[str, _PartFieldEncoding]) -> None: self.encoding = encoding self.rvfmt = rvfmt @@ -188,23 +183,20 @@ def __init__(self, RISCV_FORMATS = [ - RVFmt('r', - ['func7:f7', 'rs2:r', 'rs1:r', 'func3:f3', 'rd:r', 'opcode:f7'], + RVFmt('r', ['func7:f7', 'rs2:r', 'rs1:r', 'func3:f3', 'rd:r', 'opcode:f7'], ['opcode', 'func3', 'func7', 'rd', 'rs1', 'rs2']), - RVFmt('r4', - ['rs3:r', 'func2:f2', - 'rs2:r', 'rs1:r', 'func3:f3', 'rd:r', 'opcode:f7'], - ['opcode', 'func3', 'func2', 'rd', 'rs1', 'rs2', 'rs3']), - RVFmt('i', - ['simm12:i12', 'rs1:r', 'func3:f3', 'rd:r', 'opcode:f7'], + RVFmt('r4', [ + 'rs3:r', 'func2:f2', 'rs2:r', 'rs1:r', 'func3:f3', 'rd:r', 'opcode:f7' + ], ['opcode', 'func3', 'func2', 'rd', 'rs1', 'rs2', 'rs3']), + RVFmt('i', ['simm12:i12', 'rs1:r', 'func3:f3', 'rd:r', 'opcode:f7'], ['opcode', 'func3', 'rd', 'rs1', 'simm12']), RVFmt('s', ['imm0:i7', 'rs2:r', 'rs1:r', 'func3:f3', 'imm1:i5', 'opcode:f7'], ['opcode', 'func3', 'rs2', 'rs1', 'imm0|imm1']), - RVFmt('sb', - ['imm12:i1', 'imm105:i6', 'rs2:r', 'rs1:r', 'func3:f3', - 'imm41:i4', 'imm11:i1', 'opcode:f7'], - ['opcode', 'func3', 'rs2', 'rs1', 'imm12|imm11|imm105|imm41<<1']), + RVFmt('sb', [ + 'imm12:i1', 'imm105:i6', 'rs2:r', 'rs1:r', 'func3:f3', 'imm41:i4', + 'imm11:i1', 'opcode:f7' + ], ['opcode', 'func3', 'rs2', 'rs1', 'imm12|imm11|imm105|imm41<<1']), # The "U" format is used for LUI. The immediate operand gives imm[31:12] # for some imm. Confusingly, the spec implies that this is a signed @@ -212,18 +204,14 @@ def __init__(self, # architecture. For a 32-bit architecture, the top bit is the sign, so # there is no sign extension to be done. The binutils assembler rejects # things like "lui x1, -1", treating the immediate as unsigned. - RVFmt('u', - ['imm20:u20', 'rd:r', 'opcode:f7'], - ['opcode', 'rd', 'imm20']), - + RVFmt('u', ['imm20:u20', 'rd:r', 'opcode:f7'], ['opcode', 'rd', 'imm20']), RVFmt('j', ['i20:i1', 'i101:i10', 'i11:i1', 'i1912:i8', 'rd:r', 'opcode:f7'], ['opcode', 'rd', 'i20|i1912|i11|i101<<1']) ] -def find_rv_encoding(enc: Encoding, - name_to_operand: Dict[str, Operand], +def find_rv_encoding(enc: Encoding, name_to_operand: Dict[str, Operand], rvfmt: RVFmt) -> Optional[RVEncoding]: '''Try to find an RVEncoding that expresses enc with rvfmt''' @@ -378,8 +366,8 @@ def find_rv_encoding(enc: Encoding, rv_bit = field_bit - field_lsb + rv_lsb rv_masks[rv_name] |= 1 << rv_bit - return RVEncoding(enc, rvfmt, - rv_masks, rv_field_to_op, part_field_to_rv_field) + return RVEncoding(enc, rvfmt, rv_masks, rv_field_to_op, + part_field_to_rv_field) def find_insn_schemes(mnem_to_insn: Dict[str, Insn]) -> Dict[str, RVEncoding]: @@ -398,7 +386,8 @@ def find_insn_schemes(mnem_to_insn: Dict[str, Insn]) -> Dict[str, RVEncoding]: return ret -def parse_positionals(argv: List[str]) -> Tuple[List[str], List[str], Set[str]]: +def parse_positionals( + argv: List[str]) -> Tuple[List[str], List[str], Set[str]]: '''A partial argument parser that extracts positional arguments''' # The only arguments we actually need to parse from as are the input files: @@ -411,10 +400,7 @@ def parse_positionals(argv: List[str]) -> Tuple[List[str], List[str], Set[str]]: # The switches listed in the as manual that can be specified as "--foo bar" # (we need to know them so that we don't think that "bar" is a positional # argument). - space_args = ['--debug-prefix-map', - '--defsym', - '-I', - '-o'] + space_args = ['--debug-prefix-map', '--defsym', '-I', '-o'] # OTBN-specific flags otbn_flags = ['--otbn-translate'] @@ -443,7 +429,7 @@ def parse_positionals(argv: List[str]) -> Tuple[List[str], List[str], Set[str]]: positionals.append(arg) if '-h' in others or '--help' in others: - print('otbn-as:\n\n' + print('otbn_as.py:\n\n' 'A wrapper around riscv32-unknown-elf-as for OTBN.\n' 'Most arguments are passed through: see "man as" ' 'for more information.\n' @@ -455,8 +441,7 @@ def parse_positionals(argv: List[str]) -> Tuple[List[str], List[str], Set[str]]: return (positionals, others, flags) -def _unpack_lx(where: str, - mnemonic: str, +def _unpack_lx(where: str, mnemonic: str, op_to_expr: Dict[str, Optional[str]]) -> Tuple[str, str]: '''Unpack the arguments to li or la''' if set(op_to_expr.keys()) != {'grd', 'imm'}: @@ -464,7 +449,7 @@ def _unpack_lx(where: str, keys_list = list(op_to_expr.keys()) raise RuntimeError(f'When expanding {umnem}, got wrong op_to_expr ' f'keys ({keys_list}). This is a mismatch between ' - f'expand_{mnemonic} in otbn-as and the operands ' + f'expand_{mnemonic} in otbn_as.py and the operands ' f'for {umnem} in insns.yml.') grd = op_to_expr['grd'] @@ -484,8 +469,7 @@ def _unpack_lx(where: str, assert grd_op_val is not None except ValueError as err: raise RuntimeError('{}: When parsing LI instruction, ' - 'operand is wrong: {}' - .format(where, err)) + 'operand is wrong: {}'.format(where, err)) grd_txt = gpr_type.op_val_to_str(grd_op_val, None) return (grd_txt, imm) @@ -504,15 +488,14 @@ def expand_li(where: str, op_to_expr: Dict[str, Optional[str]]) -> List[str]: imm_int = int(imm, 0) except ValueError: raise RuntimeError('{}: Cannot parse {!r}, the immediate for an LI ' - 'instruction, as an integer.' - .format(where, imm)) + 'instruction, as an integer.'.format(where, imm)) # We allow immediates in the range [-2^31, 2^31-1] (the i32 range), plus # [2^31, 2^32-1] (to allow any u32 constant). if not (-(1 << 31) <= imm_int <= (1 << 32) - 1): raise RuntimeError('{}: The immediate for an LI instruction is {}, ' - 'which does not fit in a 32-bit integer.' - .format(where, imm)) + 'which does not fit in a 32-bit integer.'.format( + where, imm)) # Convert any large positive constants to negative ones, so imm_int ends up # in the range [-2^31, 2^31-1]. @@ -542,13 +525,17 @@ def expand_li(where: str, op_to_expr: Dict[str, Optional[str]]) -> List[str]: if imm_12 > 0: # LUI; ADDI with a positive constant - return ['lui {}, {:#x}'.format(grd_txt, imm_uint >> 12), - 'addi {}, {}, {:#x}'.format(grd_txt, grd_txt, imm_12)] + return [ + 'lui {}, {:#x}'.format(grd_txt, imm_uint >> 12), + 'addi {}, {}, {:#x}'.format(grd_txt, grd_txt, imm_12) + ] # LUI; ADDI with a negative constant. Add 1 to the upper immediate to # subtract from it. - return ['lui {}, {:#x}'.format(grd_txt, 1 + (imm_uint >> 12)), - 'addi {}, {}, {}'.format(grd_txt, grd_txt, imm_12)] + return [ + 'lui {}, {:#x}'.format(grd_txt, 1 + (imm_uint >> 12)), + 'addi {}, {}, {}'.format(grd_txt, grd_txt, imm_12) + ] def expand_la(where: str, op_to_expr: Dict[str, Optional[str]]) -> List[str]: @@ -568,14 +555,13 @@ def expand_la(where: str, op_to_expr: Dict[str, Optional[str]]) -> List[str]: # # Much easier! grd_txt, imm = _unpack_lx(where, 'la', op_to_expr) - return ['lui {}, %hi({})'.format(grd_txt, imm), - 'addi {}, {}, %lo({})'.format(grd_txt, grd_txt, imm)] + return [ + 'lui {}, %hi({})'.format(grd_txt, imm), + 'addi {}, {}, %lo({})'.format(grd_txt, grd_txt, imm) + ] -_PSEUDO_OP_ASSEMBLERS = { - 'li': expand_li, - 'la': expand_la -} +_PSEUDO_OP_ASSEMBLERS = {'li': expand_li, 'la': expand_la} class Transformer: @@ -627,10 +613,8 @@ class Transformer: need to spot them in order to find the key-sym. ''' - def __init__(self, - out_handle: TextIO, - in_path: str, - insns_file: InsnsFile, + + def __init__(self, out_handle: TextIO, in_path: str, insns_file: InsnsFile, glued_insns_dec_len: List[Insn], mnem_to_rve: Dict[str, RVEncoding]) -> None: self.out_handle = out_handle @@ -660,9 +644,8 @@ def __init__(self, # came from. out_handle.write('.file "{}"\n.line 1\n'.format(in_path)) - def mk_raw_line(self, - insn: Insn, - op_to_expr: Dict[str, Optional[str]]) -> str: + def mk_raw_line(self, insn: Insn, op_to_expr: Dict[str, + Optional[str]]) -> str: '''Generate a .word-style raw line insn must have an encoding and op_to_expr should map the operand names @@ -678,48 +661,47 @@ def mk_raw_line(self, for op_name, expr in op_to_expr.items(): op_type = insn.name_to_operand[op_name].op_type try: - op_val = (0 if expr is None - else op_type.str_to_op_val(expr.strip())) + op_val = (0 if expr is None else op_type.str_to_op_val( + expr.strip())) except ValueError as err: - raise RuntimeError('{}:{}: {}' - .format(self.in_path, - self.line_number, err)) from None + raise RuntimeError('{}:{}: {}'.format(self.in_path, + self.line_number, + err)) from None if op_val is None: raise RuntimeError('{}:{}: Cannot resolve operand expression ' '{!r} to an index and the instruction {!r} ' 'has an encoding incompatible with rv32i ' - '.insn lines.' - .format(self.in_path, self.line_number, - expr, insn.mnemonic)) from None + '.insn lines.'.format( + self.in_path, self.line_number, expr, + insn.mnemonic)) from None try: enc_val = op_type.op_val_to_enc_val(op_val, None) except ValueError as err: - raise RuntimeError('{}:{}: {}' - .format(self.in_path, - self.line_number, err)) from None + raise RuntimeError('{}:{}: {}'.format(self.in_path, + self.line_number, + err)) from None if enc_val is None: - raise RuntimeError('{}:{}: Cannot encode {!r} operand for ' - '{!r} instruction without a current PC ' - '(which is not known to otbn-as).' - .format(self.in_path, self.line_number, - expr, insn.mnemonic)) from None + raise RuntimeError( + '{}:{}: Cannot encode {!r} operand for ' + '{!r} instruction without a current PC ' + '(which is not known to otbn_as.py).'.format( + self.in_path, self.line_number, expr, + insn.mnemonic)) from None op_to_idx[op_name] = enc_val try: word_val = insn.encoding.assemble(op_to_idx) except ValueError as err: - raise RuntimeError('{}:{}: {}' - .format(self.in_path, - self.line_number, err)) from None + raise RuntimeError('{}:{}: {}'.format(self.in_path, + self.line_number, + err)) from None return '.word {:#010x}'.format(word_val) - def mk_rve_line(self, - insn: Insn, - rve: RVEncoding, + def mk_rve_line(self, insn: Insn, rve: RVEncoding, op_to_expr: Dict[str, Optional[str]]) -> str: # Take a copy of the fixed fields @@ -742,9 +724,9 @@ def mk_rve_line(self, enc_val = op_type.op_val_to_enc_val(op_val, None) assert enc_val is not None except ValueError as err: - raise RuntimeError('{}:{}: {}' - .format(self.in_path, - self.line_number, err)) from None + raise RuntimeError('{}:{}: {}'.format(self.in_path, + self.line_number, + err)) from None # read_index should always return a non-None result if # syntax_determines_value() returned false. @@ -782,9 +764,8 @@ def mk_rve_line(self, return '.insn {} {}'.format(rve.rvfmt.name, ', '.join(rv_str_list)) - def gen_line(self, - insn: Insn, - op_to_expr: Dict[str, Optional[str]]) -> None: + def gen_line(self, insn: Insn, op_to_expr: Dict[str, + Optional[str]]) -> None: '''Build and write out a line for this instruction''' assert self.key_sym is not None @@ -807,26 +788,26 @@ def gen_line(self, assert '\n' not in reconstructed if expansion is not None: - self.out_handle.write('# pseudo-expansion for: {}\n' - .format(reconstructed)) + self.out_handle.write( + '# pseudo-expansion for: {}\n'.format(reconstructed)) for entry in expansion: - self.out_handle.write('.line {}\n{}\n' - .format(self.line_number - 1, entry)) + self.out_handle.write('.line {}\n{}\n'.format( + self.line_number - 1, entry)) return # If this instruction comes from the rv32i instruction set, we can just # pass it straight through. if insn.rv32i: - self.out_handle.write('.line {}\n{}\n' - .format(self.line_number - 1, reconstructed)) + self.out_handle.write('.line {}\n{}\n'.format( + self.line_number - 1, reconstructed)) return # If we don't know an encoding for this instruction, we're not going to # have much chance. if insn.encoding is None: - raise RuntimeError('{}:{}: Instruction {!r} has no encoding.' - .format(self.in_path, self.line_number, - insn.mnemonic)) + raise RuntimeError( + '{}:{}: Instruction {!r} has no encoding.'.format( + self.in_path, self.line_number, insn.mnemonic)) # A custom instruction. We have two possible approaches. # @@ -850,10 +831,8 @@ def gen_line(self, else: line = self.mk_raw_line(insn, op_to_expr) - self.out_handle.write('# {}\n.line {}\n{}\n' - .format(reconstructed, - self.line_number - 1, - line)) + self.out_handle.write('# {}\n.line {}\n{}\n'.format( + reconstructed, self.line_number - 1, line)) def _continue_block_comment(self, line: str, pos: int) -> int: '''Continue whitespace matching in a block comment @@ -991,9 +970,8 @@ def _insn_for_keysym(self) -> Insn: if low_key_sym.startswith(insn.mnemonic): return insn - raise RuntimeError('{}:{}: Unknown mnemonic: {!r}.' - .format(self.in_path, - self.line_number, self.key_sym)) + raise RuntimeError('{}:{}: Unknown mnemonic: {!r}.'.format( + self.in_path, self.line_number, self.key_sym)) def _end_stmt_line(self) -> None: '''Called at end of a stmt line to deal with any completed statement''' @@ -1024,9 +1002,10 @@ def _end_stmt_line(self) -> None: match = insn.asm_pattern.match(operands_str.rstrip()) if match is None: - raise RuntimeError('{}:{}: Cannot match syntax for {!r} ({!r}).' - .format(self.in_path, self.line_number, - self.key_sym, insn.syntax.render_doc())) + raise RuntimeError( + '{}:{}: Cannot match syntax for {!r} ({!r}).'.format( + self.in_path, self.line_number, self.key_sym, + insn.syntax.render_doc())) op_to_val = {} # type: Dict[str, Optional[str]] for op, grp in insn.pattern_op_to_grp.items(): @@ -1058,9 +1037,9 @@ def _take_stmt_body(self, line: str, pos: int) -> None: match = re.match(r'[0-9a-zA-Z_$.]+', line[pos:]) if match is None: - raise RuntimeError('{}:{}:{}: Expected key symbol, but found {!r}.' - .format(self.in_path, self.line_number, pos, - line[pos:])) + raise RuntimeError( + '{}:{}:{}: Expected key symbol, but found {!r}.'.format( + self.in_path, self.line_number, pos, line[pos:])) self.key_sym = match.group(0) self.state = 1 @@ -1137,23 +1116,18 @@ def at_eof(self) -> None: raise RuntimeError('Reached EOF while still in a string.') -def transform_input(out_handle: TextIO, - in_path: str, - in_handle: TextIO, - insns_file: InsnsFile, - glued_insns_dec_len: List[Insn], +def transform_input(out_handle: TextIO, in_path: str, in_handle: TextIO, + insns_file: InsnsFile, glued_insns_dec_len: List[Insn], mnem_to_rve: Dict[str, RVEncoding]) -> None: '''Transform an input file to make it suitable for riscv as''' - transformer = Transformer(out_handle, in_path, - insns_file, glued_insns_dec_len, mnem_to_rve) + transformer = Transformer(out_handle, in_path, insns_file, + glued_insns_dec_len, mnem_to_rve) for line in in_handle: transformer.take_line(line) transformer.at_eof() -def transform_inputs(out_dir: str, - inputs: List[str], - insns_file: InsnsFile, +def transform_inputs(out_dir: str, inputs: List[str], insns_file: InsnsFile, mnem_to_rve: Dict[str, RVEncoding], glued_insns_dec_len: List[Insn], just_translate: bool) -> List[str]: @@ -1174,8 +1148,8 @@ def transform_inputs(out_dir: str, if not just_translate: out_handle = open(out_path, 'w') - transform_input(out_handle, pretty_in_path, in_handle, - insns_file, glued_insns_dec_len, mnem_to_rve) + transform_input(out_handle, pretty_in_path, in_handle, insns_file, + glued_insns_dec_len, mnem_to_rve) finally: if in_handle is not sys.stdin and in_handle is not None: @@ -1209,8 +1183,7 @@ def run_binutils_as(other_args: List[str], inputs: List[str]) -> int: try: return subprocess.run(cmd).returncode except FileNotFoundError: - sys.stderr.write('Unknown command: {!r}.\n' - .format(as_name)) + sys.stderr.write('Unknown command: {!r}.\n'.format(as_name)) return 127 @@ -1245,10 +1218,10 @@ def main() -> int: for insn in insns_file.insns: if insn.python_pseudo_op: if insn.mnemonic not in _PSEUDO_OP_ASSEMBLERS: - sys.stderr.write("Instruction {!r} has python-pseudo-op true, " - "but otbn-as doesn't have a custom assembler " - "for it.\n" - .format(insn.mnemonic)) + sys.stderr.write( + "Instruction {!r} has python-pseudo-op true, " + "but otbn_as.py doesn't have a custom assembler " + "for it.\n".format(insn.mnemonic)) return 1 # Try to match up OTBN instruction encodings with .insn schemes (as stored @@ -1257,9 +1230,8 @@ def main() -> int: with tempfile.TemporaryDirectory(suffix='.otbn-as') as tmpdir: try: - transformed = transform_inputs(tmpdir, files, - insns_file, mnem_to_rve, - glued_insns_dec_len, + transformed = transform_inputs(tmpdir, files, insns_file, + mnem_to_rve, glued_insns_dec_len, just_translate) except RuntimeError as err: sys.stderr.write('{}\n'.format(err)) diff --git a/hw/ip/otbn/util/shared/operand.py b/hw/ip/otbn/util/shared/operand.py index e95e1fe684fbf..86a0529b5109a 100644 --- a/hw/ip/otbn/util/shared/operand.py +++ b/hw/ip/otbn/util/shared/operand.py @@ -5,8 +5,8 @@ import re from typing import List, Optional, Tuple -from serialize.parse_helpers import (check_keys, check_bool, - check_str, get_optional_str) +from serialize.parse_helpers import (check_bool, check_keys, check_str, + get_optional_str) from .encoding import Encoding from .encoding_scheme import EncSchemeField @@ -22,7 +22,7 @@ class OperandType: - Encoded value The string representation is the string that you expect to see in an - assembly listing (either fed into otbn-as, or generated by otbn-objdump). + assembly listing (either fed into otbn_as.py, or generated by otbn-objdump). The encoded value is the non-negative integer value that is encoded in the bits of the instruction. @@ -40,6 +40,7 @@ class OperandType: The interesting conversion is between operand value and encoded value. ''' + def __init__(self, width: Optional[int]) -> None: assert width is None or width > 0 self.width = width @@ -97,13 +98,12 @@ def str_to_op_val(self, as_str: str) -> Optional[int]: ''' raise NotImplementedError() - def op_val_to_enc_val(self, - op_val: int, + def op_val_to_enc_val(self, op_val: int, cur_pc: Optional[int]) -> Optional[int]: '''Convert the operand value to an encoded value This expects a current PC in cur_pc. If we don't know that (because - this is otbn-as, and we don't know our eventual current address), a + this is otbn_as.py, and we don't know our eventual current address), a pc_rel immediate will return None. Similarly, if we don't know our width, we should return None. @@ -122,12 +122,10 @@ def op_val_to_enc_val(self, if op_val < 0: raise ValueError('Negative operand value {} for a basic operand ' 'that expects to encode an unsigned value in ' - '{} bits.' - .format(op_val, self.width)) + '{} bits.'.format(op_val, self.width)) if op_val >> self.width: raise ValueError('Operand value {} is too large for a basic ' - 'operand of {} bits.' - .format(op_val, self.width)) + 'operand of {} bits.'.format(op_val, self.width)) return op_val @@ -202,10 +200,7 @@ def __init__(self, reg_type: str, is_src: bool, is_dest: bool) -> None: self._is_dest = is_dest @staticmethod - def make(reg_type: str, - is_src: bool, - is_dest: bool, - what: str, + def make(reg_type: str, is_src: bool, is_dest: bool, what: str, scheme_field: Optional[EncSchemeField]) -> 'RegOperandType': if scheme_field is not None: fmt = RegOperandType.TYPE_FMTS.get(reg_type) @@ -213,11 +208,11 @@ def make(reg_type: str, width, _ = fmt if scheme_field.bits.width != width: - raise ValueError('In {}, there is an encoding scheme that ' - 'allocates {} bits, but the operand has ' - 'register type {!r}, which expects {} bits.' - .format(what, scheme_field.bits.width, - reg_type, width)) + raise ValueError( + 'In {}, there is an encoding scheme that ' + 'allocates {} bits, but the operand has ' + 'register type {!r}, which expects {} bits.'.format( + what, scheme_field.bits.width, reg_type, width)) return RegOperandType(reg_type, is_src, is_dest) @@ -230,14 +225,14 @@ def str_to_op_val(self, as_str: str) -> int: re_pfx = '' if pfx is None else re.escape(pfx) match = re.match(re_pfx + '([0-9]+)$', as_str) if match is None: - raise ValueError("Expression {!r} can't be parsed as a {}." - .format(as_str, self.reg_type)) + raise ValueError("Expression {!r} can't be parsed as a {}.".format( + as_str, self.reg_type)) idx = int(match.group(1)) assert 0 <= idx if idx >> width: - raise ValueError("Invalid register of type {}: {!r}." - .format(self.reg_type, as_str)) + raise ValueError("Invalid register of type {}: {!r}.".format( + self.reg_type, as_str)) return idx @@ -262,12 +257,9 @@ def is_dest(self) -> bool: class ImmOperandType(OperandType): '''A class representing an immediate operand type''' - def __init__(self, - width: Optional[int], - enc_offset: int, - shift: int, - signed: bool, - pc_rel: bool) -> None: + + def __init__(self, width: Optional[int], enc_offset: int, shift: int, + signed: bool, pc_rel: bool) -> None: assert shift >= 0 super().__init__(width) @@ -277,12 +269,8 @@ def __init__(self, self.pc_rel = pc_rel @staticmethod - def make(width: Optional[int], - enc_offset: int, - shift: int, - signed: bool, - pc_rel: bool, - what: str, + def make(width: Optional[int], enc_offset: int, shift: int, signed: bool, + pc_rel: bool, what: str, scheme_field: Optional[EncSchemeField]) -> 'ImmOperandType': if scheme_field is not None: # If there is an encoding scheme, check its width is compatible @@ -291,10 +279,11 @@ def make(width: Optional[int], if width is None: width = scheme_field.bits.width if scheme_field.bits.width != width: - raise ValueError('In {}, there is an encoding scheme that ' - 'allocates {} bits to the immediate operand ' - 'but the operand claims to have width {}.' - .format(what, scheme_field.bits.width, width)) + raise ValueError( + 'In {}, there is an encoding scheme that ' + 'allocates {} bits to the immediate operand ' + 'but the operand claims to have width {}.'.format( + what, scheme_field.bits.width, width)) return ImmOperandType(width, enc_offset, shift, signed, pc_rel) @@ -325,12 +314,12 @@ def markdown_doc(self) -> Optional[str]: rel_suffix = (' This is encoded PC-relative but appears ' 'as an absolute value in assembly. To write a raw ' 'value in an assembly file, write something in the ' - 'range `{}` to `{}`.' - .format(ImmOperandType._doc_rel_to_abs(lo), - ImmOperandType._doc_rel_to_abs(hi))) + 'range `{}` to `{}`.'.format( + ImmOperandType._doc_rel_to_abs(lo), + ImmOperandType._doc_rel_to_abs(hi))) - return ('Valid range: `{}` to `{}`{}.{}' - .format(lo, hi, stp_msg, rel_suffix)) + return ('Valid range: `{}` to `{}`{}.{}'.format( + lo, hi, stp_msg, rel_suffix)) def describe_decode(self, bits_lst: List[str]) -> str: # The "most general" result is something like this: @@ -395,8 +384,7 @@ def op_val_to_str(self, op_val: int, cur_pc: Optional[int]) -> str: else: return str(op_val) - def op_val_to_enc_val(self, - op_val: int, + def op_val_to_enc_val(self, op_val: int, cur_pc: Optional[int]) -> Optional[int]: # Give up immediately if we don't know our width if self.width is None: @@ -417,10 +405,9 @@ def op_val_to_enc_val(self, if pc_relative_val & shift_mask: raise ValueError('Cannot encode the value {}: the operand has a ' 'shift of {}, but that would clobber some bits ' - '(because {} & {} = {}, not zero).' - .format(pc_relative_val, self.shift, - pc_relative_val, shift_mask, - pc_relative_val & shift_mask)) + '(because {} & {} = {}, not zero).'.format( + pc_relative_val, self.shift, pc_relative_val, + shift_mask, pc_relative_val & shift_mask)) # Compute offset encoded value by applying shift and enc_offset shifted = pc_relative_val >> self.shift @@ -440,15 +427,13 @@ def op_val_to_enc_val(self, encoded_msg = (', which encodes to {},'.format(offset_val) if self.shift != 0 or self.enc_offset != 0 else '') raise ValueError('Cannot encode the value {}{} as a {}-bit ' - '{}signed value. Possible range: {}..{}.' - .format(pc_relative_val, encoded_msg, - self.width, - '' if self.signed else 'un', - doc_lo, doc_hi)) + '{}signed value. Possible range: {}..{}.'.format( + pc_relative_val, encoded_msg, self.width, + '' if self.signed else 'un', doc_lo, doc_hi)) if self.signed: - encoded = ((1 << self.width) + offset_val - if offset_val < 0 else offset_val) + encoded = ((1 << self.width) + + offset_val if offset_val < 0 else offset_val) else: assert offset_val >= 0 encoded = offset_val @@ -530,9 +515,8 @@ class EnumOperandType(OperandType): are matched with case-folding in read_index. ''' - def __init__(self, - items: List[str], - what: str, + + def __init__(self, items: List[str], what: str, scheme_field: Optional[EncSchemeField]) -> None: assert items @@ -547,9 +531,9 @@ def __init__(self, raise ValueError('In {}, there is an encoding scheme that ' 'assigns {} bits to the field. But this ' 'field is an enum with {} items, so needs ' - 'at least {} bits.' - .format(what, scheme_field.bits.width, - len(items), min_width)) + 'at least {} bits.'.format( + what, scheme_field.bits.width, len(items), + min_width)) width = scheme_field.bits.width super().__init__(width) @@ -557,11 +541,12 @@ def __init__(self, def markdown_doc(self) -> Optional[str]: # Override from OperandType base class - parts = ['| Assembly Syntax | Value |\n' - '|-----------------|-------|\n'] + parts = [ + '| Assembly Syntax | Value |\n' + '|-----------------|-------|\n' + ] for idx, item in enumerate(self.items): - parts.append('| `{}` | `{}` |\n' - .format(item, idx)) + parts.append('| `{}` | `{}` |\n'.format(item, idx)) return ''.join(parts) def syntax_determines_value(self) -> bool: @@ -575,8 +560,7 @@ def str_to_op_val(self, as_str: str) -> int: known_vals = ', '.join(repr(item) for item in self.items) raise ValueError('Invalid enum value, {!r}. ' - 'Supported values: {}.' - .format(as_str, known_vals)) + 'Supported values: {}.'.format(as_str, known_vals)) def op_val_to_str(self, op_val: int, cur_pc: Optional[int]) -> str: # On a bad value, we have to return *something*. Since this is just @@ -601,9 +585,8 @@ class OptionOperandType(OperandType): and is matched with case-folding in read_index. ''' - def __init__(self, - option: str, - what: str, + + def __init__(self, option: str, what: str, scheme_field: Optional[EncSchemeField]) -> None: width = 1 @@ -626,8 +609,8 @@ def str_to_op_val(self, as_str: str) -> int: return 1 raise ValueError('Invalid option value, {!r}. ' - 'If specified, it should have been {!r}.' - .format(as_str, self.option)) + 'If specified, it should have been {!r}.'.format( + as_str, self.option)) def op_val_to_str(self, op_val: int, cur_pc: Optional[int]) -> str: assert op_val in [0, 1] @@ -637,9 +620,7 @@ def get_max_enc_val(self) -> int: return 1 -def parse_operand_type(fmt: str, - pc_rel: bool, - what: str, +def parse_operand_type(fmt: str, pc_rel: bool, what: str, scheme_field: Optional[EncSchemeField]) -> OperandType: '''Make sense of the operand type syntax''' # Registers @@ -653,25 +634,22 @@ def parse_operand_type(fmt: str, reg_match = reg_fmts.get(fmt) if reg_match is not None: if pc_rel: - raise ValueError('In {}, the operand has type {!r} which is a ' - 'type of register operand. It also has pc_rel ' - 'set, which is only allowed for immediates.' - .format(what, fmt)) + raise ValueError( + 'In {}, the operand has type {!r} which is a ' + 'type of register operand. It also has pc_rel ' + 'set, which is only allowed for immediates.'.format(what, fmt)) reg_type, is_src, is_dest = reg_match - return RegOperandType.make(reg_type, is_src, is_dest, - what, scheme_field) + return RegOperandType.make(reg_type, is_src, 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_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) + return ImmOperandType.make(xsr_match, 0, 0, False, False, what, + scheme_field) # Immediates for base, signed in [('simm', True), ('uimm', False)]: @@ -688,38 +666,33 @@ def parse_operand_type(fmt: str, width = int(m.group(1)) if m.group(1) is not None else None enc_offset = int(m.group(2)) if m.group(2) is not None else 0 shift = int(m.group(3)) if m.group(3) is not None else 0 - return ImmOperandType.make(width, enc_offset, shift, - signed, pc_rel, what, scheme_field) + return ImmOperandType.make(width, enc_offset, shift, signed, + pc_rel, what, scheme_field) m = re.match(r'enum\(([^\)]+)\)$', fmt) if m: if pc_rel: raise ValueError('In {}, the operand is an enumeration, but also ' 'has pc_rel set, which is only allowed for bare ' - 'immediates.' - .format(what)) + 'immediates.'.format(what)) - return EnumOperandType([item.strip() - for item in m.group(1).split(',')], - what, scheme_field) + return EnumOperandType( + [item.strip() for item in m.group(1).split(',')], what, + scheme_field) m = re.match(r'option\(([^\)]+)\)$', fmt) if m: if pc_rel: raise ValueError('In {}, the operand is an option, but also ' 'has pc_rel set, which is only allowed for bare ' - 'immediates.' - .format(what)) + 'immediates.'.format(what)) return OptionOperandType(m.group(1).strip(), what, scheme_field) raise ValueError("In {}, operand type description {!r} " - "didn't match any recognised format." - .format(what, fmt)) + "didn't match any recognised format.".format(what, fmt)) -def infer_operand_type(name: str, - pc_rel: bool, - what: str, +def infer_operand_type(name: str, pc_rel: bool, what: str, scheme_field: Optional[EncSchemeField]) -> OperandType: '''Try to guess an operand's type from its name''' @@ -734,17 +707,15 @@ def infer_operand_type(name: str, op_type_name = 'simm' if op_type_name is None: - raise ValueError("Operand name {!r} doesn't imply an operand type: " - "you'll have to set the type explicitly." - .format(name)) + raise ValueError( + "Operand name {!r} doesn't imply an operand type: " + "you'll have to set the type explicitly.".format(name)) return parse_operand_type(op_type_name, pc_rel, what, scheme_field) -def make_operand_type(op_type_name: Optional[str], - pc_rel: bool, - operand_name: str, - mnemonic: str, +def make_operand_type(op_type_name: Optional[str], pc_rel: bool, + operand_name: str, mnemonic: str, scheme_field: Optional[EncSchemeField]) -> OperandType: '''Construct a type for an operand @@ -753,17 +724,16 @@ def make_operand_type(op_type_name: Optional[str], that will be used. ''' - what = ('the type for the {!r} operand of instruction {!r}' - .format(operand_name, mnemonic)) + what = ('the type for the {!r} operand of instruction {!r}'.format( + operand_name, mnemonic)) return (parse_operand_type(op_type_name, pc_rel, what, scheme_field) - if op_type_name is not None - else infer_operand_type(operand_name, pc_rel, what, scheme_field)) + if op_type_name is not None else infer_operand_type( + operand_name, pc_rel, what, scheme_field)) class Operand: - def __init__(self, - yml: object, - mnemonic: str, + + def __init__(self, yml: object, mnemonic: str, insn_encoding: Optional[Encoding]) -> None: # The YAML representation should be a string (a bare operand name) or a # dict. @@ -776,8 +746,7 @@ def __init__(self, pc_rel = False op_what = '{!r} {}'.format(name, what) elif isinstance(yml, dict): - yd = check_keys(yml, what, - ['name'], + yd = check_keys(yml, what, ['name'], ['type', 'pc-rel', 'doc', 'abbrev']) name = check_str(yd['name'], 'name of ' + what).lower() @@ -798,18 +767,17 @@ def __init__(self, if field_name is None: raise ValueError('The {!r} instruction has an operand called ' '{!r}, but the associated encoding has no ' - 'field that encodes it.' - .format(mnemonic, name)) + 'field that encodes it.'.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)) + '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.op_type = make_operand_type(op_type, pc_rel, name, mnemonic, + enc_scheme_field) self.doc = doc diff --git a/meson.build b/meson.build index 5f9ca4bc05bbb..0c2d8af8cc9f2 100644 --- a/meson.build +++ b/meson.build @@ -209,7 +209,7 @@ prog_as = find_program('as') prog_objdump = find_program('objdump') prog_objcopy = find_program('objcopy') -prog_otbn_as = meson.project_source_root() / 'hw/ip/otbn/util/otbn-as' +prog_otbn_as = meson.project_source_root() / 'hw/ip/otbn/util/otbn_as.py' prog_otbn_ld = meson.project_source_root() / 'hw/ip/otbn/util/otbn_ld.py' # Hardware register headers. These are generated from HJSON files, and accesible diff --git a/rules/otbn.bzl b/rules/otbn.bzl index 07f95f273e1a9..e0646a337305e 100644 --- a/rules/otbn.bzl +++ b/rules/otbn.bzl @@ -10,13 +10,13 @@ def _get_assembler(cc_toolchain): # Note: the toolchain config doesn"t appear to have a good way to get # access to the assembler. We should be able to access it via the - # the compiler, but I had trouble with //hw/ip/otbn/util/otbn-as invoking + # the compiler, but I had trouble with //hw/ip/otbn/util/otbn_as.py invoking # the compiler as assembler. return [f for f in cc_toolchain.all_files.to_list() if f.basename.endswith("as")][0] def _otbn_assemble_sources(ctx): """Helper function that, for each source file in the provided context, adds - an action to the context that invokes the otbn assember (otbn-as), + an action to the context that invokes the otbn assember (otbn_as.py), producing a corresponding object file. Returns a list of all object files that will be generated by these actions. """ @@ -153,7 +153,7 @@ otbn_library = rv_rule( attrs = { "srcs": attr.label_list(allow_files = True), "_cc_toolchain": attr.label(default = Label("@bazel_tools//tools/cpp:current_cc_toolchain")), - "_otbn_as": attr.label(default = "//hw/ip/otbn/util:otbn-as", allow_single_file = True), + "_otbn_as": attr.label(default = "//hw/ip/otbn/util:otbn_as.py", allow_single_file = True), }, fragments = ["cpp"], toolchains = ["@rules_cc//cc:toolchain_type"], @@ -166,7 +166,7 @@ otbn_binary = rv_rule( "srcs": attr.label_list(allow_files = True), "deps": attr.label_list(providers = [DefaultInfo]), "_cc_toolchain": attr.label(default = Label("@bazel_tools//tools/cpp:current_cc_toolchain")), - "_otbn_as": attr.label(default = "//hw/ip/otbn/util:otbn-as", allow_single_file = True), + "_otbn_as": attr.label(default = "//hw/ip/otbn/util:otbn_as.py", allow_single_file = True), "_otbn_ld": attr.label(default = "//hw/ip/otbn/util:otbn_ld.py", allow_single_file = True), "_otbn_data": attr.label(default = "//hw/ip/otbn/data:all_files", allow_files = True), "_wrapper": attr.label(default = "//util:otbn_build.py", allow_single_file = True), diff --git a/util/otbn_build.py b/util/otbn_build.py index bf109a038fed1..4453095960cb5 100755 --- a/util/otbn_build.py +++ b/util/otbn_build.py @@ -4,9 +4,9 @@ # SPDX-License-Identifier: Apache-2.0 """Build software running on OTBN -Each assembly source file is first assembled with otbn-as. All resulting objects -are then linked with otbn_ld.py. The resulting ELF file is converted into an -embeddable RV32 object file using objcopy. In this object, all symbols +Each assembly source file is first assembled with otbn_as.py. All resulting +objects are then linked with otbn_ld.py. The resulting ELF file is converted +into an embeddable RV32 object file using objcopy. In this object, all symbols are prefixed with `_otbn_app__` (only global symbols are included). environment variables: @@ -15,7 +15,7 @@ sensible default values are provided (tools are generally expected to be in the $PATH). - OTBN_AS path to otbn-as, the OTBN assembler + OTBN_AS path to otbn_as.py, the OTBN assembler OTBN_LD path to otbn_ld.py, the OTBN linker RV32_TOOL_LD path to RV32 ld RV32_TOOL_AS path to RV32 as @@ -23,7 +23,7 @@ RV32_TOOL_OBJCOPY path to RV32 objcopy The RV32* environment variables are used by both this script and the OTBN - wrappers (otbn-as and otbn_ld.py) to find tools in a RV32 toolchain. + wrappers (otbn_as.py and otbn_ld.py) to find tools in a RV32 toolchain. outputs: The build process produces multiple files inside the output directory. @@ -102,7 +102,7 @@ def run_tool(tool: str, out_file: Path, args) -> None: def call_otbn_as(src_file: Path, out_file: Path): otbn_as_cmd = os.environ.get('OTBN_AS', - str(REPO_TOP / 'hw/ip/otbn/util/otbn-as')) + str(REPO_TOP / 'hw/ip/otbn/util/otbn_as.py')) run_tool(otbn_as_cmd, out_file, [src_file])