From cd17c65ce70dc2d08feab89d56927e08d2eb4a88 Mon Sep 17 00:00:00 2001 From: Anton Lydike Date: Mon, 19 Apr 2021 12:51:43 +0200 Subject: [PATCH] Added instruction set selector to CLI --- README.md | 11 +++++++++-- riscemu/__main__.py | 22 ++++++++++++++++++---- riscemu/instructions/__init__.py | 4 ++++ 3 files changed, 31 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 7add538..d0c8d3a 100644 --- a/README.md +++ b/README.md @@ -35,7 +35,9 @@ Basic IO should work, as open, read, write and close are supported for stdin/std This is how the interface is used: ``` -riscemu [--options OPTIONS] [--syscall-option SYSCALL_OPTIONS] [--default_stack_size] file.asm [file.asm ...] +usage: riscemu [-h] [--options OPTIONS] [--syscall-opts SYSCALL_OPTS] [--instruction-sets INSTRUCTION_SETS] [--default_stack_size [default-stack-size]] file.asm [file.asm ...] + + OPTIONS and SYSCALL_OPTIONS is a list of comma-separated flags that will be enabled @@ -44,9 +46,14 @@ disable_debug Disable the ebreak and sbreak instructions no_syscall_symbols Don't make syscall symbols globally available fail_on_ex Do not launch an interactive debugger when the CPU loop catches an exception - SYSCALL_OPTIONS: + SYSCALL_OPTS: Options to control syscall behaviour fs_access Allow access to the filesystem disable_io Disallow reading/writing from stdin/stdout/stderr + + INSTRUCTION_SETS: A list of comma separated instruction sets you want to load: + Currently implemented: RV32I, RV32M + + ``` If multiple files are specified, all are loaded into memeory, but only the last one is executed. This might be improved diff --git a/riscemu/__main__.py b/riscemu/__main__.py index a03a0f9..bd8d8c8 100644 --- a/riscemu/__main__.py +++ b/riscemu/__main__.py @@ -1,13 +1,14 @@ if __name__ == '__main__': from . import * from .helpers import * - from .instructions import RV32I, RV32M + from .instructions import InstructionSetDict import argparse import sys + all_ins_names = list(InstructionSetDict.keys()) class OptionStringAction(argparse.Action): - def __init__(self, option_strings, dest, keys=None, **kwargs): + def __init__(self, option_strings, dest, keys=None, omit_empty=False, **kwargs): if keys is None: raise ValueError('must define "keys" argument') if isinstance(keys, dict): @@ -24,13 +25,17 @@ if __name__ == '__main__': keys_d = dict() super().__init__(option_strings, dest, default=keys_d, **kwargs) self.keys = keys_d + self.omit_empty = omit_empty def __call__(self, parser, namespace, values, option_string=None): d = {} - d.update(self.keys) + if not self.omit_empty: + d.update(self.keys) for x in values.split(','): if x in self.keys: d[x] = True + else: + raise ValueError('Invalid parameter supplied: ' + x) setattr(namespace, self.dest, d) @@ -44,6 +49,10 @@ if __name__ == '__main__': parser.add_argument('--syscall-opts', '-so', action=OptionStringAction, keys=('fs_access', 'disable_input')) + parser.add_argument('--instruction-sets', '-is', action=OptionStringAction, help="Instruction sets to load, available are: {}. " + "All are enabled by default" + .format(", ".join(all_ins_names)), keys={k: True for k in all_ins_names}, omit_empty=True) + parser.add_argument('--default_stack_size', type=int, help='Default stack size of loaded programs', default=None, metavar='default-stack-size', nargs='?') @@ -60,8 +69,13 @@ if __name__ == '__main__': FMT_PRINT = FMT_BOLD + FMT_MAGENTA + # parse required instruction sets + ins_to_load = [ + InstructionSetDict[name] for name, b in args.ins.items() if b + ] + try: - cpu = CPU(cfg, [RV32I, RV32M]) + cpu = CPU(cfg, ins_to_load) loaded_exe = None for file in args.files: tk = cpu.get_tokenizer(RiscVInput.from_file(file)) diff --git a/riscemu/instructions/__init__.py b/riscemu/instructions/__init__.py index 819ba1c..095ef56 100644 --- a/riscemu/instructions/__init__.py +++ b/riscemu/instructions/__init__.py @@ -1,3 +1,7 @@ from .InstructionSet import InstructionSet from .RV32M import RV32M from .RV32I import RV32I + +InstructionSetDict = { + v.__name__: v for v in [RV32I, RV32M] +}