diff options
Diffstat (limited to 'sim/ppc/dgen.py')
-rwxr-xr-x | sim/ppc/dgen.py | 305 |
1 files changed, 305 insertions, 0 deletions
diff --git a/sim/ppc/dgen.py b/sim/ppc/dgen.py new file mode 100755 index 0000000..14dbb25 --- /dev/null +++ b/sim/ppc/dgen.py @@ -0,0 +1,305 @@ +#!/usr/bin/env python3 +# Copyright (C) 1994-1995 Andrew Cagney <cagney@highland.com.au> +# Copyright (C) 1996-2022 Free Software Foundation, Inc. +# +# This file is part of the GNU simulators. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. + +"""Helper to generate spreg.[ch] files.""" + +import argparse +from pathlib import Path +import sys +from typing import NamedTuple, TextIO + + +FILE = Path(__file__).resolve() +DIR = FILE.parent + + +NR_OF_SPRS = 1024 + +SPREG_ATTRIBUTES = ( + "is_valid", + "is_readonly", + "name", + "index", + "length", +) + + +class Spreg(NamedTuple): + """A single spreg entry.""" + + name: str + reg_nr: int + is_readonly: int + length: int + + +def load_table(source: Path) -> list[Spreg]: + """Load the spreg table & return all entries in it.""" + ret = [] + + with source.open("r", encoding="utf-8") as fp: + for i, line in enumerate(fp): + line = line.split("#", 1)[0].strip() + if not line: + # Skip blank & comment lines. + continue + + fields = line.split(":") + assert len(fields) == 4, f"{source}:{i}: bad line: {line}" + spreg = Spreg( + name=fields[0].lower(), + reg_nr=int(fields[1]), + is_readonly=int(fields[2]), + length=int(fields[3]), + ) + ret.append(spreg) + + return sorted(ret, key=lambda x: x[1]) + + +def print_copyleft(fp: TextIO) -> None: + """Write out the standard copyright & license file block.""" + fp.write( + f"""\ +/* DO NOT EDIT: GENERATED BY {FILE.name}. + + Copyright (C) 1994-1995 Andrew Cagney <cagney@highland.com.au> + Copyright (C) 1996-2022 Free Software Foundation, Inc. + + This file is part of the GNU simulators. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. */ +""" + ) + + +def gen_header(table: list[Spreg], output: Path) -> None: + """Write header to |output| from spreg |table|.""" + with output.open("w", encoding="utf-8") as fp: + print_copyleft(fp) + fp.write( + """ +#ifndef _SPREG_H_ +#define _SPREG_H_ + +typedef unsigned_word spreg; + +typedef enum { +""" + ) + + for spreg in table: + fp.write(f" spr_{spreg.name} = {spreg.reg_nr},\n") + + fp.write( + f"""\ + nr_of_sprs = {NR_OF_SPRS} +}} sprs; + +""" + ) + + for attr in SPREG_ATTRIBUTES: + ret_type = "const char *" if attr == "name" else "int" + fp.write(f"INLINE_SPREG({ret_type}) spr_{attr}(sprs spr);\n") + + fp.write( + """ +#endif /* _SPREG_H_ */ +""" + ) + + +def gen_switch_is_valid(table: list[Spreg], fp: TextIO) -> None: + """Generate switch table for is_valid property.""" + fp.write(" switch (spr) {\n") + # Output all the known registers. We'll return 1 for them. + for spreg in table: + fp.write(f" case {spreg.reg_nr}:\n") + # All other registers return 0. + fp.write( + """\ + return 1; + } + return 0; +""" + ) + + +def gen_switch_is_readonly(table: list[Spreg], fp: TextIO) -> None: + """Generate switch table for is_readonly property.""" + # Find any readonly registers and output a switch for them. + # If there aren't any, we can optimize this away to a single return. + output_switch = False + has_readonly = False + for spreg in table: + if spreg.is_readonly: + if not output_switch: + fp.write(" switch (spr) {\n") + output_switch = True + has_readonly = True + fp.write(f" case {spreg.reg_nr}:\n") + if has_readonly: + fp.write(" return 1;\n") + + if output_switch: + fp.write(" }\n") + fp.write(" return 0;\n") + + +def gen_switch_length(table: list[Spreg], fp: TextIO) -> None: + """Generate switch table for length property.""" + # Find any registers with a length property and output a switch for them. + # If there aren't any, we can optimize this away to a single return. + output_switch = False + for spreg in table: + if spreg.length: + if not output_switch: + fp.write(" switch (spr) {\n") + output_switch = True + fp.write(f" case {spreg.reg_nr}:\n") + fp.write(f" return {spreg.length};\n") + + if output_switch: + fp.write(" }\n") + fp.write(" return 0;\n") + + +def gen_source(table: list[Spreg], output: Path) -> None: + """Write header to |output| from spreg |table|.""" + with output.open("w", encoding="utf-8") as fp: + print_copyleft(fp) + fp.write( + """ +#ifndef _SPREG_C_ +#define _SPREG_C_ + +#include "basics.h" +#include "spreg.h" + +typedef struct _spreg_info { + const char *name; + int is_valid; + int length; + int is_readonly; + int index; +} spreg_info; + +static const spreg_info spr_info[nr_of_sprs+1] = { +""" + ) + + entries = iter(table) + entry = next(entries) + for spreg_nr in range(0, NR_OF_SPRS + 1): + if entry is None or spreg_nr < entry.reg_nr: + fp.write(f" {{ 0, 0, 0, 0, {spreg_nr} }},\n") + else: + fp.write( + f' {{ "{entry.name}", 1, {entry.length}, {entry.is_readonly}, spr_{entry.name} /*{spreg_nr}*/ }},\n' + ) + entry = next(entries, None) + fp.write("};\n") + + for attr in SPREG_ATTRIBUTES: + ret_type = "const char *" if attr == "name" else "int" + fp.write( + f""" +INLINE_SPREG({ret_type}) spr_{attr}(sprs spr) +{{ +""" + ) + + if attr not in ("index", "name"): + fp.write( + """\ +#ifdef WITH_SPREG_SWITCH_TABLE +""" + ) + if attr == "is_valid": + gen_switch_is_valid(table, fp) + elif attr == "is_readonly": + gen_switch_is_readonly(table, fp) + elif attr == "length": + gen_switch_length(table, fp) + else: + assert False, f"{attr}: Unknown attribute" + fp.write("#else\n") + fp.write(f" return spr_info[spr].{attr};\n") + if attr not in ("index", "name"): + fp.write("#endif\n") + fp.write("}\n") + + fp.write("\n#endif /* _SPREG_C_ */\n") + + +def get_parser() -> argparse.ArgumentParser: + """Get CLI parser.""" + parser = argparse.ArgumentParser( + description=__doc__, + formatter_class=argparse.RawDescriptionHelpFormatter, + ) + parser.add_argument( + "--table", + type=Path, + default=DIR / "ppc-spr-table", + help="path to source table", + ) + parser.add_argument("--header", type=Path, help="path to header (.h) file") + parser.add_argument("--source", type=Path, help="path to source (.c) file") + return parser + + +def parse_args(argv: list[str]) -> argparse.Namespace: + """Process the command line & default options.""" + parser = get_parser() + opts = parser.parse_args(argv) + + if not opts.header and not opts.source: + opts.header = DIR / "spreg.h" + opts.source = DIR / "spreg.c" + + return opts + + +def main(argv: list[str]) -> int: + """The main entry point for scripts.""" + opts = parse_args(argv) + + table = load_table(opts.table) + if opts.header: + gen_header(table, opts.header) + if opts.source: + gen_source(table, opts.source) + return 0 + + +if __name__ == "__main__": + sys.exit(main(sys.argv[1:])) |