#!/usr/bin/env python3 # Copyright (c) 2023-2024, ARM Limited. # SPDX-License-Identifier: Apache-2.0 import os import tosa class TOSASpecAsciidocGenerator: def __init__(self, spec): self.spec = spec def generate_enum(self, enum, file): file.write(f"\n=== {enum.name}\n") file.write(f"{enum.description}\n") file.write("|===\n") file.write("|Name|Value|Description\n\n") for val in enum.values: file.write(f"|{val[0]}|{val[1]}|{val[2]}\n") file.write("|===\n") def generate_operator(self, op, file): file.write("\n*Arguments:*\n") file.write("[cols='3,3,2,2,4,8']") file.write("\n|===\n") file.write("|Argument|Type|Name|Shape|Rank|Description\n\n") for arg in op.arguments: # Argument cats = arg.categories if len(cats) > 1: cattext = "" sep = "" for cat in cats: proflist = "/".join(cat.profiles) profcaption = "profiles" if len(cat.profiles) > 1 else "profile" cattext += sep + cat.name.title() + f" ({proflist} {profcaption})" sep = " " else: cattext = cats[0].name.title() # Type if arg.type == "tensor_t": argtype = f"T<{arg.tensor_element_type}>" elif arg.type == "tensor_list_t": if arg.tensor_element_type == "-": argtype = "tensor_list_t" else: argtype = f"tensor_list_t>" elif arg.type == "shape_t": if arg.shape != "-": argtype = f"shape_t<{arg.shape}>" else: argtype = "shape_t<>" else: argtype = arg.type # Rank if len(arg.rank) > 0: if arg.rank[0] == arg.rank[1]: rank = f"{arg.rank[0]}" else: rank = f"{arg.rank[0]} to {arg.rank[1]}" else: rank = "" # Format and write line file.write( f"|{cattext}|{argtype}|{arg.name}|{arg.shape}" f"|{rank}|{arg.description}\n" ) file.write("|===\n") if op.typesupports: file.write("\n*Supported Data Types:*\n\n") file.write("|===\n") header = "|Profile|Mode" for ty in op.types: header += f"|{ty}" file.write(header) file.write("\n\n") for tysup in op.typesupports: profile = ", ".join(tysup.profiles) if tysup.profiles else "Any" entry = f"|{profile}|{tysup.mode}" for ty in op.types: entry += f"|{tysup.tymap[ty]}" entry += "\n" file.write(entry) file.write("|===\n") file.write("\n*Operation Function:*\n\n") leveltext = "" for arg in op.arguments: if len(arg.levellimits) > 0: for limit in arg.levellimits: leveltext += "LEVEL_CHECK(" + limit[0] + " <= " + limit[1] + ");\n" if len(leveltext) > 0: file.write(f"[source,c++]\n----\n{leveltext}\n----\n") def generate(self, outdir): os.makedirs(outdir, exist_ok=True) # Generate version information major = self.spec.version_major minor = self.spec.version_minor patch = self.spec.version_patch with open(os.path.join(outdir, "version.adoc"), "w") as f: f.write(":tosa-version-string: {}.{}.{}".format(major, minor, patch)) if self.spec.version_is_draft: f.write(" draft") f.write("\n") # Generate level maximums table with open(os.path.join(outdir, "levels.adoc"), "w") as f: f.write("|===\n") f.write("|tosa_level_t") for level in self.spec.levels: f.write("|tosa_level_{}".format(level.name)) f.write("\n") f.write("|Description") for level in self.spec.levels: f.write("|{}".format(level.desc)) f.write("\n") for param in self.spec.levels[0].maximums: f.write("|{}".format(param)) for level in self.spec.levels: f.write("|{}".format(level.maximums[param])) f.write("\n") f.write("|===\n") # Generator operators opdir = os.path.join(outdir, "operators") os.makedirs(opdir, exist_ok=True) for group in self.spec.operatorgroups: for op in group.operators: with open(os.path.join(opdir, op.name + ".adoc"), "w") as f: self.generate_operator(op, f) with open(os.path.join(outdir, "enums.adoc"), "w") as f: for enum in self.spec.enums: self.generate_enum(enum, f) if __name__ == "__main__": import argparse parser = argparse.ArgumentParser() parser.add_argument("--xml", required=True, help="Path to specification XML") parser.add_argument("--outdir", required=True, help="Output directory") args = parser.parse_args() try: spec = tosa.TOSASpec(args.xml) except RuntimeError as e: print(f"Failure reading/validating XML spec: {str(e)}") exit(1) generator = TOSASpecAsciidocGenerator(spec) generator.generate(args.outdir)