| #!/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 | 
 |  | 
 | # 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. | 
 | _RIG_DIR = os.path.dirname(__file__) | 
 | _OTBN_DIR = os.path.normpath(os.path.join(_RIG_DIR, '../..')) | 
 | _OTBN_UTIL_DIR = os.path.join(_OTBN_DIR, 'util') | 
 | sys.path.append(_OTBN_UTIL_DIR) | 
 |  | 
 | from shared.insn_yaml import InsnsFile, load_insns_yaml  # noqa: E402 | 
 |  | 
 | from rig.config import Config  # noqa: E402 | 
 | from rig.init_data import InitData  # noqa: E402 | 
 | from rig.rig import gen_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 get_named_config(name: str) -> Optional[Config]: | 
 |     cfg_dir = os.path.join(_RIG_DIR, 'rig/configs') | 
 |     try: | 
 |         return Config.load(cfg_dir, name) | 
 |     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 | 
 |  | 
 |     config = get_named_config(args.config) | 
 |     if config is None: | 
 |         return 1 | 
 |  | 
 |     # Run the generator | 
 |     try: | 
 |         init_data, snippet, end_addr = gen_program(config, | 
 |                                                    args.size, | 
 |                                                    insns_file) | 
 |     except RuntimeError as err: | 
 |         print(err, file=sys.stderr) | 
 |         return 1 | 
 |  | 
 |     # Write out the data and snippets to a JSON file | 
 |     ser_data = init_data.as_json() | 
 |     ser_snippet = snippet.to_json() | 
 |     ser = [ser_data, ser_snippet, end_addr] | 
 |     json.dump(ser, args.output) | 
 |     # Add a newline at end of output: json.dump doesn't, and it makes a | 
 |     # bit of a mess of some consoles. | 
 |     args.output.write('\n') | 
 |  | 
 |     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 JSON | 
 |     try: | 
 |         json_data = 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 init data and snippet objects. | 
 |     try: | 
 |         if not (isinstance(json_data, list) and len(json_data) == 3): | 
 |             raise ValueError('Top-level structure should be a length 3 list.') | 
 |  | 
 |         json_init_data, json_snippet, json_end_addr = json_data | 
 |         init_data = InitData.read(json_init_data) | 
 |         snippet = Snippet.from_json(insns_file, [], json_snippet) | 
 |         if not isinstance(json_end_addr, int) or json_end_addr < 0: | 
 |             raise ValueError('end_addr should be an integer.') | 
 |         end_addr = json_end_addr | 
 |     except ValueError as err: | 
 |         print('Failed to parse snippets from {!r}: {}' | 
 |               .format(args.snippets.name, err), | 
 |               file=sys.stderr) | 
 |         return 1 | 
 |  | 
 |     program = snippet.to_program() | 
 |     dsegs = init_data.as_segs() | 
 |  | 
 |     # Dump the assembly output, and the linker script too if we're writing to | 
 |     # something other than stdout. | 
 |     if args.output is None or args.output == '-': | 
 |         program.dump_asm(sys.stdout, dsegs) | 
 |     else: | 
 |         try: | 
 |             asm_path = args.output + '.s' | 
 |             with open(asm_path, 'w') as out_file: | 
 |                 program.dump_asm(out_file, dsegs) | 
 |         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, dsegs, end_addr) | 
 |         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(dest='cmd') | 
 |     subparsers.required = True | 
 |  | 
 |     gen = subparsers.add_parser('gen', help='Generate a random program') | 
 |     asm = subparsers.add_parser('asm', help='Convert snippets to assembly') | 
 |  | 
 |     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('--config', type=str, default='default', | 
 |                      help='Configuration to use') | 
 |     gen.add_argument('--output', '-o', | 
 |                      metavar='out', | 
 |                      type=argparse.FileType('w', encoding='UTF-8'), | 
 |                      default=sys.stdout, | 
 |                      help='Output filename') | 
 |     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()) |