| #!/usr/bin/env python3 | 
 | # Copyright lowRISC contributors. | 
 | # Licensed under the Apache License, Version 2.0, see LICENSE for details. | 
 | # SPDX-License-Identifier: Apache-2.0 | 
 |  | 
 | '''Wrapper script to run the OTBN random instruction generator''' | 
 |  | 
 | import argparse | 
 | import json | 
 | import os | 
 | import random | 
 | import sys | 
 | from typing import Optional, cast | 
 |  | 
 | from shared.insn_yaml import InsnsFile, load_insns_yaml | 
 |  | 
 | # Ensure that the OTBN utils directory is on sys.path. This means that RIG code | 
 | # can import modules like "shared.foo" and get the OTBN shared code. | 
 | sys.path.append(os.path.dirname(__file__)) | 
 |  | 
 | from rig.rig import gen_program, snippets_to_program  # noqa: E402 | 
 | from rig.snippet import Snippet  # noqa: E402 | 
 |  | 
 |  | 
 | def get_insns_file() -> Optional[InsnsFile]: | 
 |     '''Load up insns.yml''' | 
 |     try: | 
 |         return load_insns_yaml() | 
 |     except RuntimeError as err: | 
 |         print(err, file=sys.stderr) | 
 |         return None | 
 |  | 
 |  | 
 | def gen_main(args: argparse.Namespace) -> int: | 
 |     '''Entry point for the gen subcommand''' | 
 |     random.seed(args.seed) | 
 |  | 
 |     insns_file = get_insns_file() | 
 |     if insns_file is None: | 
 |         return 1 | 
 |  | 
 |     # Run the generator | 
 |     snippets, program = gen_program(args.start_addr, args.size, insns_file) | 
 |  | 
 |     # Write out the snippets to a JSON file | 
 |     ser_snippets = [snippet.to_json() for snippet in snippets] | 
 |     try: | 
 |         if args.output == '-': | 
 |             json.dump(ser_snippets, sys.stdout) | 
 |             # Add a newline at end of output: json.dump doesn't, and it makes a | 
 |             # bit of a mess of some consoles. | 
 |             sys.stdout.write('\n') | 
 |         else: | 
 |             with open(args.output, 'w') as out_file: | 
 |                 json.dump(ser_snippets, out_file) | 
 |     except OSError as err: | 
 |         print('Failed to open json output file {!r}: {}.' | 
 |               .format(args.output, err), | 
 |               file=sys.stderr) | 
 |         return 1 | 
 |  | 
 |     return 0 | 
 |  | 
 |  | 
 | def asm_main(args: argparse.Namespace) -> int: | 
 |     '''Entry point for the asm subcommand''' | 
 |  | 
 |     insns_file = get_insns_file() | 
 |     if insns_file is None: | 
 |         return 1 | 
 |  | 
 |     # Load snippets JSON | 
 |     try: | 
 |         snippets_json = json.load(args.snippets) | 
 |     except json.JSONDecodeError as err: | 
 |         print('Snippets file at {!r} is not valid JSON: {}.' | 
 |               .format(args.snippets.name, err), | 
 |               file=sys.stderr) | 
 |         return 1 | 
 |  | 
 |     # Parse these to proper snippet objects and then make a program by | 
 |     # combining them | 
 |     try: | 
 |         if not isinstance(snippets_json, list): | 
 |             raise ValueError('Top-level structure should be a list.') | 
 |         snippets = [Snippet.from_json(insns_file, idx, x) | 
 |                     for idx, x in enumerate(snippets_json)] | 
 |     except ValueError as err: | 
 |         print('Failed to parse snippets from {!r}: {}' | 
 |               .format(args.snippets, err), | 
 |               file=sys.stderr) | 
 |         return 1 | 
 |  | 
 |     program = snippets_to_program(snippets) | 
 |  | 
 |     # Dump the assembly output. | 
 |     if args.output is None or args.output == '-': | 
 |         program.dump_asm(sys.stdout) | 
 |     else: | 
 |         try: | 
 |             asm_path = args.output + '.S' | 
 |             with open(asm_path, 'w') as out_file: | 
 |                 program.dump_asm(out_file) | 
 |         except OSError as err: | 
 |             print('Failed to open asm output file {!r}: {}.' | 
 |                   .format(args.output, err), | 
 |                   file=sys.stderr) | 
 |             return 1 | 
 |  | 
 |         try: | 
 |             ld_path = args.output + '.ld' | 
 |             with open(ld_path, 'w') as out_file: | 
 |                 program.dump_linker_script(out_file) | 
 |         except OSError as err: | 
 |             print('Failed to open ld script output file {!r}: {}.' | 
 |                   .format(ld_path, err), | 
 |                   file=sys.stderr) | 
 |             return 1 | 
 |  | 
 |     return 0 | 
 |  | 
 |  | 
 | def main() -> int: | 
 |     parser = argparse.ArgumentParser() | 
 |     subparsers = parser.add_subparsers() | 
 |  | 
 |     gen = subparsers.add_parser('gen', help='Generate a random program') | 
 |     asm = subparsers.add_parser('asm', help='Convert snippets to assembly') | 
 |  | 
 |     gen.add_argument('--output', '-o', | 
 |                      default='-', | 
 |                      help=("Path for JSON output of generated snippets. The " | 
 |                            "special path '-' (which is the default) means to " | 
 |                            "write to stdout")) | 
 |     gen.add_argument('--seed', type=int, default=0, | 
 |                      help='Random seed. Defaults to 0.') | 
 |     gen.add_argument('--size', type=int, default=100, | 
 |                      help=('Max number of instructions in stream. ' | 
 |                            'Defaults to 100.')) | 
 |     gen.add_argument('--start-addr', type=int, default=0, | 
 |                      help='Reset address. Defaults to 0.') | 
 |     gen.set_defaults(func=gen_main) | 
 |  | 
 |     asm.add_argument('--output', '-o', | 
 |                      metavar='out', | 
 |                      help=('Base path for output filenames. Will generate ' | 
 |                            'out.S with an assembly listing and out.ld with ' | 
 |                            'a linker script. If this is not supplied, the ' | 
 |                            'assembly (but no linker script) will be dumped ' | 
 |                            'to stdout.')) | 
 |     asm.add_argument('snippets', metavar='path.json', | 
 |                      type=argparse.FileType('r'), nargs='?', default=sys.stdin, | 
 |                      help=('A JSON file of snippets, as generated by ' | 
 |                            'otbn-rig gen.')) | 
 |     asm.set_defaults(func=asm_main) | 
 |  | 
 |     args = parser.parse_args() | 
 |     return cast(int, args.func(args)) | 
 |  | 
 |  | 
 | if __name__ == '__main__': | 
 |     sys.exit(main()) |