From 2bea3a817f41d19db9198b12c53cfb1711e9cc5e Mon Sep 17 00:00:00 2001 From: Ronald Caesar Date: Tue, 9 Dec 2025 12:44:21 -0400 Subject: [PATCH] decoder/tools: Made python script more user-friendly - Added default values for input/output directories - Improve error handling for missing cli arguments Signed-off-by: Ronald Caesar --- src/decoder_table_gen.c | 5 ++ src/decoder_table_gen.h | 6 +- tools/generate_a64_table.py | 125 +++++++++++++++++++++++++----------- 3 files changed, 99 insertions(+), 37 deletions(-) diff --git a/src/decoder_table_gen.c b/src/decoder_table_gen.c index b3cee18..639ecdb 100644 --- a/src/decoder_table_gen.c +++ b/src/decoder_table_gen.c @@ -1,3 +1,8 @@ +/* + *GENERATED FILE - DO NOT EDIT + *Generated with tools/generate_a64_table.py + */ + /* Generated 2807 instructions */ #include "decoder_table_gen.h" diff --git a/src/decoder_table_gen.h b/src/decoder_table_gen.h index ba6514d..e402862 100644 --- a/src/decoder_table_gen.h +++ b/src/decoder_table_gen.h @@ -1,4 +1,8 @@ -/* Generated header file */ +/* + *GENERATED FILE - DO NOT EDIT + *Generated with tools/generate_a64_table.py + */ + #include "decoder.h" #include diff --git a/tools/generate_a64_table.py b/tools/generate_a64_table.py index f5a0aae..f845ba2 100644 --- a/tools/generate_a64_table.py +++ b/tools/generate_a64_table.py @@ -1,3 +1,15 @@ +""" +If you're in the same directory where this script is located you will be able +to run it without passing any parameters: + +$ python3 generate_a64_table.py +Using default XML directory: ../spec/arm64_xml/ +Using default output directory: ../src/ +Found 2038 XML files +Generated ARM decoder table header file -> ../src/decoder_table_gen.h +Generated ARM decoder table source file -> ../src/decoder_table_gen.c +""" + import os import sys import glob @@ -6,10 +18,21 @@ import xml.etree.ElementTree as ET from dataclasses import dataclass from typing import List, Tuple, Optional -DEFAULT_XML_DIRECTORY_PATH = "../spec/arm64_xml" -DEFAULT_GENERATED_TABLE_C_PATH = "../src/decoder_table_gen.c" -DEFAULT_GENERATED_TABLE_H_PATH = "../src/decoder_table_gen.h" +DEFAULT_DECODER_GENERATED_HEADER_NAME = "decoder_table_gen.h" +DEFAULT_DECODER_GENERATED_SOURCE_NAME = "decoder_table_gen.c" +DEFAULT_OUTPUT_DIRECTORY = "../src/" +DEFAULT_XML_DIRECTORY_PATH = "../spec/arm64_xml/" + +DECODER_HEADER_NAME = "decoder.h" +DECODER_ARM64_INSTRUCTIONS_SIZE_NAME = "BAL_DECODER_ARM64_INSTRUCTIONS_SIZE" +DECODER_ARM64_GLOBAL_INSTRUCTIONS_ARRAY_NAME = "g_bal_decoder_arm64_instructions" +DECODER_STRUCT_NAME = "bal_decoder_instruction_metadata_t" + +GENERATED_FILE_WARNING = """/* + *GENERATED FILE - DO NOT EDIT + *Generated with tools/generate_a64_table.py + */""" @dataclass class A64Instruction: @@ -28,7 +51,7 @@ def process_box( hibit = int(box.attrib.get("hibit")) # The width of this bitfield. width = int(box.attrib.get("width", "1")) - except ValueError: + except TypeError: return (current_mask, current_value) if hibit >= 32: @@ -100,6 +123,7 @@ def parse_xml_file(filepath: str) -> List[A64Instruction]: # Extract mask and value. for iclass in root.findall(".//iclass"): + # The diagram box contains the bit definitions. box_diagram = iclass.find("regdiagram") if box_diagram is None: @@ -169,29 +193,60 @@ def parse_xml_file(filepath: str) -> List[A64Instruction]: if __name__ == "__main__": parser = argparse.ArgumentParser(description="Generate ARM64 Decoder Tables") - parser.add_argument("--directory") - parser.add_argument("--output_header") - parser.add_argument("--output_source") + parser.add_argument("--xml-directory", help="Directory containing the ARM XML files") + parser.add_argument("--output-directory", help="Directory to store the generated files") + parser.add_argument("--output-header", help="Name of the generated header file") + parser.add_argument("--output-source", help="Name of the generated source file") + # ------------------------------------------------------------------------- + # CLI Arg Parsing + # ------------------------------------------------------------------------- args = parser.parse_args() xml_directory: str = DEFAULT_XML_DIRECTORY_PATH - if args.directory is not None: - xml_directory = args.directory + if args.xml_directory is not None: + xml_directory = args.xml_directory else: - print( - f"XML directory not found, using default directory: {DEFAULT_XML_DIRECTORY_PATH}" - ) + print(f"Using default XML directory: {DEFAULT_XML_DIRECTORY_PATH}") if not os.path.exists(xml_directory): - print(f"XML path not found at {xml_directory}", file=sys.stderr) + print(f"XML directory does not exist: {xml_directory}", file=sys.stderr) sys.exit(1) + output_directory: str = DEFAULT_OUTPUT_DIRECTORY + if args.output_directory is not None: + output_directory = args.output_directory + else: + print(f"Using default output directory: {DEFAULT_OUTPUT_DIRECTORY}") + + if not os.path.exists(output_directory): + print(f"Output directory does not exit: {output_directory}", file=sys.stderr) + sys.exit(1) + + + output_header_path: str = os.path.join(DEFAULT_OUTPUT_DIRECTORY + DEFAULT_DECODER_GENERATED_HEADER_NAME) + output_source_path: str = os.path.join(DEFAULT_OUTPUT_DIRECTORY + DEFAULT_DECODER_GENERATED_SOURCE_NAME) + if output_directory != DEFAULT_OUTPUT_DIRECTORY: + output_header_path = os.path.join(output_directory + DEFAULT_DECODER_GENERATED_HEADER_NAME) + output_source_path = os.path.join(output_directory + DEFAULT_DECODER_GENERATED_SOURCE_NAME) + + if args.output_header is not None: + output_header_path = os.path.join(output_directory + args.output_header) + + if args.output_source is not None: + output_source_path = os.path.join(output_directory + args.output_source) + + # ------------------------------------------------------------------------- + # Process XML Files + # ------------------------------------------------------------------------- all_instructions = [] files = glob.glob(os.path.join(xml_directory, "*.xml")) + if len(files) < 1: + print(f"No XML files found in {xml_directory}") + sys.exit(1) print(f"Found {len(files)} XML files") - files_to_ignore: List[str] = [xml_directory + "/onebigfile.xml"] + files_to_ignore: List[str] = [os.path.join(xml_directory + "onebigfile.xml")] for f in files: # Skip index and shared pseudo-code files. if "index" in f or "shared" in f: @@ -205,38 +260,36 @@ if __name__ == "__main__": # Sort by priority all_instructions.sort(key=lambda x: x.priority, reverse=True) - # Generate Header - out_file_header = DEFAULT_GENERATED_TABLE_H_PATH - if args.output_header is not None: - out_file_header = args.output_header - - arm64_instructions_size_name: str = "BAL_DECODER_ARM64_INSTRUCTIONS_SIZE" - arm64_global_instructions_array_name: str = "g_bal_decoder_arm64_instructions" - - with open(out_file_header, "w") as f: - f.write(f"/* Generated header file */\n") - f.write(f'#include "decoder.h"\n') + # ------------------------------------------------------------------------- + # Generate Header File + # ------------------------------------------------------------------------- + with open(output_header_path, "w") as f: + f.write(f"{GENERATED_FILE_WARNING}\n\n") + f.write(f'#include "{DECODER_HEADER_NAME}"\n') f.write("#include \n\n") - f.write(f"#define {arm64_instructions_size_name} {len(all_instructions)}\n\n") + f.write(f"#define {DECODER_ARM64_INSTRUCTIONS_SIZE_NAME} {len(all_instructions)}\n\n") f.write( - f"extern const bal_decoder_instruction_metadata_t {arm64_global_instructions_array_name}[{arm64_instructions_size_name}];\n" + f"extern const bal_decoder_instruction_metadata_t {DECODER_ARM64_GLOBAL_INSTRUCTIONS_ARRAY_NAME}[{DECODER_ARM64_INSTRUCTIONS_SIZE_NAME}];\n" ) - print(f"Generated ARM decoder table header file -> {out_file_header}") + print(f"Generated ARM decoder table header file -> {output_header_path}") - # Generate Source - out_file_source = DEFAULT_GENERATED_TABLE_C_PATH - if args.output_source is not None: - out_file_source = args.output_source + # ------------------------------------------------------------------------- + # Generate Source File + # ------------------------------------------------------------------------- + decoder_generated_header_name: str = DEFAULT_DECODER_GENERATED_HEADER_NAME + if args.output_header is not None: + decoder_generated_header_name = args.output_header - with open(out_file_source, "w") as f: + with open(output_source_path, "w") as f: + f.write(f"{GENERATED_FILE_WARNING}\n\n") f.write(f"/* Generated {len(all_instructions)} instructions */\n") - f.write(f'#include "decoder_table_gen.h"\n\n') + f.write(f'#include "{decoder_generated_header_name}"\n\n') f.write( - f"const bal_decoder_instruction_metadata_t {arm64_global_instructions_array_name}[{arm64_instructions_size_name}] = {{\n" + f"const bal_decoder_instruction_metadata_t {DECODER_ARM64_GLOBAL_INSTRUCTIONS_ARRAY_NAME}[{DECODER_ARM64_INSTRUCTIONS_SIZE_NAME}] = {{\n" ) for inst in all_instructions: f.write( f' {{ "{inst.mnemonic}", 0x{inst.mask:08X}, 0x{inst.value:08X} }}, \n' ) f.write("};") - print(f"Generated ARM decoder table source file -> {out_file_source}") + print(f"Generated ARM decoder table source file -> {output_source_path}")