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 <github43132@proton.me>
This commit is contained in:
Ronald Caesar
2025-12-09 12:44:21 -04:00
parent e0c76b5361
commit 2bea3a817f
3 changed files with 99 additions and 37 deletions

View File

@@ -1,3 +1,8 @@
/*
*GENERATED FILE - DO NOT EDIT
*Generated with tools/generate_a64_table.py
*/
/* Generated 2807 instructions */
#include "decoder_table_gen.h"

View File

@@ -1,4 +1,8 @@
/* Generated header file */
/*
*GENERATED FILE - DO NOT EDIT
*Generated with tools/generate_a64_table.py
*/
#include "decoder.h"
#include <stdint.h>

View File

@@ -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 <stdint.h>\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}")