diff options
Diffstat (limited to 'latex_utils.py')
-rw-r--r-- | latex_utils.py | 450 |
1 files changed, 450 insertions, 0 deletions
diff --git a/latex_utils.py b/latex_utils.py new file mode 100644 index 0000000..e421c6b --- /dev/null +++ b/latex_utils.py @@ -0,0 +1,450 @@ +import logging +import pprint +from typing import TextIO + +from constants import latex_fixed_fields, latex_inst_type, latex_mapping +from shared_utils import InstrDict, arg_lut, create_inst_dict + +pp = pprint.PrettyPrinter(indent=2) +logging.basicConfig(level=logging.INFO, format="%(levelname)s:: %(message)s") + + +def make_priv_latex_table(): + type_list = ["R-type", "I-type"] + system_instr = ["_h", "_s", "_system", "_svinval", "64_h", "_svinval_h"] + dataset_list = [(system_instr, "Trap-Return Instructions", ["sret", "mret"], False)] + dataset_list.append( + (system_instr, "Interrupt-Management Instructions", ["wfi"], False) + ) + dataset_list.append( + ( + system_instr, + "Supervisor Memory-Management Instructions", + ["sfence_vma"], + False, + ) + ) + dataset_list.append( + ( + system_instr, + "Hypervisor Memory-Management Instructions", + ["hfence_vvma", "hfence_gvma"], + False, + ) + ) + dataset_list.append( + ( + system_instr, + "Hypervisor Virtual-Machine Load and Store Instructions", + [ + "hlv_b", + "hlv_bu", + "hlv_h", + "hlv_hu", + "hlv_w", + "hlvx_hu", + "hlvx_wu", + "hsv_b", + "hsv_h", + "hsv_w", + ], + False, + ) + ) + dataset_list.append( + ( + system_instr, + "Hypervisor Virtual-Machine Load and Store Instructions, RV64 only", + ["hlv_wu", "hlv_d", "hsv_d"], + False, + ) + ) + dataset_list.append( + ( + system_instr, + "Svinval Memory-Management Instructions", + [ + "sinval_vma", + "sfence_w_inval", + "sfence_inval_ir", + "hinval_vvma", + "hinval_gvma", + ], + False, + ) + ) + caption = "\\caption{RISC-V Privileged Instructions}" + with open("priv-instr-table.tex", "w", encoding="utf-8") as latex_file: + make_ext_latex_table(type_list, dataset_list, latex_file, 32, caption) + + +def make_latex_table(): + """ + This function is mean to create the instr-table.tex that is meant to be used + by the riscv-isa-manual. This function basically creates a single latext + file of multiple tables with each table limited to a single page. Only the + last table is assigned a latex-caption. + + For each table we assign a type-list which capture the different instruction + types (R, I, B, etc) that will be required for the table. Then we select the + list of extensions ('_i, '32_i', etc) whose instructions are required to + populate the table. For each extension or collection of extension we can + assign Title, such that in the end they appear as subheadings within + the table (note these are inlined headings and not captions of the table). + + All of the above information is collected/created and sent to + make_ext_latex_table function to dump out the latex contents into a file. + + The last table only has to be given a caption - as per the policy of the + riscv-isa-manual. + """ + # open the file and use it as a pointer for all further dumps + with open("instr-table.tex", "w", encoding="utf-8") as latex_file: + + # create the rv32i table first. Here we set the caption to empty. We use the + # files rv_i and rv32_i to capture instructions relevant for rv32i + # configuration. The dataset is a list of 4-element tuples : + # (list_of_extensions, title, list_of_instructions, include_pseudo_ops). If list_of_instructions + # is empty then it indicates that all instructions of the all the extensions + # in list_of_extensions need to be dumped. If not empty, then only the + # instructions listed in list_of_instructions will be dumped into latex. + caption = "" + type_list = ["R-type", "I-type", "S-type", "B-type", "U-type", "J-type"] + dataset_list: list[tuple[list[str], str, list[str], bool]] = [ + (["_i", "32_i"], "RV32I Base Instruction Set", [], False) + ] + dataset_list.append((["_i"], "", ["fence_tso", "pause"], True)) + make_ext_latex_table(type_list, dataset_list, latex_file, 32, caption) + + type_list = ["R-type", "I-type", "S-type"] + dataset_list = [ + (["64_i"], "RV64I Base Instruction Set (in addition to RV32I)", [], False) + ] + dataset_list.append( + (["_zifencei"], "RV32/RV64 Zifencei Standard Extension", [], False) + ) + dataset_list.append( + (["_zicsr"], "RV32/RV64 Zicsr Standard Extension", [], False) + ) + dataset_list.append((["_m", "32_m"], "RV32M Standard Extension", [], False)) + dataset_list.append( + (["64_m"], "RV64M Standard Extension (in addition to RV32M)", [], False) + ) + make_ext_latex_table(type_list, dataset_list, latex_file, 32, caption) + + type_list = ["R-type"] + dataset_list = [(["_a"], "RV32A Standard Extension", [], False)] + dataset_list.append( + (["64_a"], "RV64A Standard Extension (in addition to RV32A)", [], False) + ) + make_ext_latex_table(type_list, dataset_list, latex_file, 32, caption) + + type_list = ["R-type", "R4-type", "I-type", "S-type"] + dataset_list = [(["_f"], "RV32F Standard Extension", [], False)] + dataset_list.append( + (["64_f"], "RV64F Standard Extension (in addition to RV32F)", [], False) + ) + make_ext_latex_table(type_list, dataset_list, latex_file, 32, caption) + + type_list = ["R-type", "R4-type", "I-type", "S-type"] + dataset_list = [(["_d"], "RV32D Standard Extension", [], False)] + dataset_list.append( + (["64_d"], "RV64D Standard Extension (in addition to RV32D)", [], False) + ) + make_ext_latex_table(type_list, dataset_list, latex_file, 32, caption) + + type_list = ["R-type", "R4-type", "I-type", "S-type"] + dataset_list = [(["_q"], "RV32Q Standard Extension", [], False)] + dataset_list.append( + (["64_q"], "RV64Q Standard Extension (in addition to RV32Q)", [], False) + ) + make_ext_latex_table(type_list, dataset_list, latex_file, 32, caption) + + caption = "\\caption{Instruction listing for RISC-V}" + type_list = ["R-type", "R4-type", "I-type", "S-type"] + dataset_list = [ + (["_zfh", "_d_zfh", "_q_zfh"], "RV32Zfh Standard Extension", [], False) + ] + dataset_list.append( + ( + ["64_zfh"], + "RV64Zfh Standard Extension (in addition to RV32Zfh)", + [], + False, + ) + ) + make_ext_latex_table(type_list, dataset_list, latex_file, 32, caption) + + ## The following is demo to show that Compressed instructions can also be + # dumped in the same manner as above + + # type_list = [''] + # dataset_list = [(['_c', '32_c', '32_c_f','_c_d'],'RV32C Standard Extension', [])] + # dataset_list.append((['64_c'],'RV64C Standard Extension (in addition to RV32C)', [])) + # make_ext_latex_table(type_list, dataset_list, latex_file, 16, caption) + + +def make_ext_latex_table( + type_list: "list[str]", + dataset: "list[tuple[list[str], str, list[str], bool]]", + latex_file: TextIO, + ilen: int, + caption: str, +): + """ + For a given collection of extensions this function dumps out a complete + latex table which includes the encodings of the instructions. + + The ilen input indicates the length of the instruction for which the table + is created. + + The caption input is used to create the latex-table caption. + + The type_list input is a list of instruction types (R, I, B, etc) that are + treated as header for each table. Each table will have its own requirements + and type_list must include all the instruction-types that the table needs. + Note, all elements of this list must be present in the latex_inst_type + dictionary defined in constants.py + + The latex_file is a file pointer to which the latex-table will dumped into + + The dataset is a list of 3-element tuples containing: + (list_of_extensions, title, list_of_instructions) + The list_of_extensions must contain all the set of extensions whose + instructions must be populated under a given title. If list_of_instructions + is not empty, then only those instructions mentioned in list_of_instructions + present in the extension will be dumped into the latex-table, other + instructions will be ignored. + + Once the above inputs are received then function first creates table entries + for the instruction types. To simplify things, we maintain a dictionary + called latex_inst_type in constants.py which is created in the same way the + instruction dictionary is created. This allows us to re-use the same logic + to create the instruction types table as well + + Once the header is created, we then parse through every entry in the + dataset. For each list dataset entry we use the create_inst_dict function to + create an exhaustive list of instructions associated with the respective + collection of the extension of that dataset. Then we apply the instruction + filter, if any, indicated by the list_of_instructions of that dataset. + Thereon, for each instruction we create a latex table entry. + + Latex table specification for ilen sized instructions: + Each table is created with ilen+1 columns - ilen columns for each bit of the + instruction and one column to hold the name of the instruction. + + For each argument of an instruction we use the arg_lut from constants.py + to identify its position in the encoding, and thus create a multicolumn + entry with the name of the argument as the data. For hardcoded bits, we + do the same where we capture a string of continuous 1s and 0s, identify + the position and assign the same string as the data of the + multicolumn entry in the table. + + """ + column_size = "".join(["p{0.002in}"] * (ilen + 1)) + + type_entries = ( + """ + \\multicolumn{3}{l}{31} & + \\multicolumn{2}{r}{27} & + \\multicolumn{1}{c}{26} & + \\multicolumn{1}{r}{25} & + \\multicolumn{3}{l}{24} & + \\multicolumn{2}{r}{20} & + \\multicolumn{3}{l}{19} & + \\multicolumn{2}{r}{15} & + \\multicolumn{2}{l}{14} & + \\multicolumn{1}{r}{12} & + \\multicolumn{4}{l}{11} & + \\multicolumn{1}{r}{7} & + \\multicolumn{6}{l}{6} & + \\multicolumn{1}{r}{0} \\\\ + \\cline{2-33}\n&\n\n +""" + if ilen == 32 + else """ + \\multicolumn{1}{c}{15} & + \\multicolumn{1}{c}{14} & + \\multicolumn{1}{c}{13} & + \\multicolumn{1}{c}{12} & + \\multicolumn{1}{c}{11} & + \\multicolumn{1}{c}{10} & + \\multicolumn{1}{c}{9} & + \\multicolumn{1}{c}{8} & + \\multicolumn{1}{c}{7} & + \\multicolumn{1}{c}{6} & + \\multicolumn{1}{c}{5} & + \\multicolumn{1}{c}{4} & + \\multicolumn{1}{c}{3} & + \\multicolumn{1}{c}{2} & + \\multicolumn{1}{c}{1} & + \\multicolumn{1}{c}{0} \\\\ + \\cline{2-17}\n&\n\n +""" + ) + + # depending on the type_list input we create a subset dictionary of + # latex_inst_type dictionary present in constants.py + type_dict = { + key: value for key, value in latex_inst_type.items() if key in type_list + } + + # iterate ovr each instruction type and create a table entry + for t in type_dict: + fields: list[tuple[int, int, str]] = [] + + # first capture all "arguments" of the type (funct3, funct7, rd, etc) + # and capture their positions using arg_lut. + for f in type_dict[t]["variable_fields"]: + (msb, lsb) = arg_lut[f] + name = f if f not in latex_mapping else latex_mapping[f] + fields.append((msb, lsb, name)) + + # iterate through the 32 bits, starting from the msb, and assign + # argument names to the relevant portions of the instructions. This + # information is stored as a 3-element tuple containing the msb, lsb + # position of the arugment and the name of the argument. + msb = ilen - 1 + y = "" + for r in range(0, ilen): + if y != "": + fields.append((msb, ilen - 1 - r + 1, y)) + y = "" + msb = ilen - 1 - r - 1 + if r == 31: + if y != "": + fields.append((msb, 0, y)) + y = "" + + # sort the arguments in decreasing order of msb position + fields.sort(key=lambda y: y[0], reverse=True) + + # for each argument/string of 1s or 0s, create a multicolumn latex table + # entry + entry = "" + for r, (msb, lsb, name) in enumerate(fields): + if r == len(fields) - 1: + entry += ( + f"\\multicolumn{{{msb - lsb + 1}}}{{|c|}}{{{name}}} & {t} \\\\\n" + ) + elif r == 0: + entry += f"\\multicolumn{{{msb - lsb + 1}}}{{|c|}}{{{name}}} &\n" + else: + entry += f"\\multicolumn{{{msb - lsb + 1}}}{{c|}}{{{name}}} &\n" + entry += f"\\cline{{2-{ilen+1}}}\n&\n\n" + type_entries += entry + + # for each entry in the dataset create a table + content = "" + for ext_list, title, filter_list, include_pseudo in dataset: + instr_dict: InstrDict = {} + + # for all extensions list in ext_list, create a dictionary of + # instructions associated with those extensions. + for e in ext_list: + instr_dict.update(create_inst_dict(["rv" + e], include_pseudo)) + + # if filter_list is not empty then use that as the official set of + # instructions that need to be dumped into the latex table + inst_list = list(instr_dict.keys()) if not filter_list else filter_list + + # for each instruction create an latex table entry just like how we did + # above with the instruction-type table. + instr_entries = "" + for inst in inst_list: + if inst not in instr_dict: + logging.error( + f"in make_ext_latex_table: Instruction: {inst} not found in instr_dict" + ) + raise SystemExit(1) + fields = [] + + # only if the argument is available in arg_lut we consume it, else + # throw error. + for f in instr_dict[inst]["variable_fields"]: + if f not in arg_lut: + logging.error( + f"Found variable {f} in instruction {inst} whose mapping is not available" + ) + raise SystemExit(1) + (msb, lsb) = arg_lut[f] + name = ( + f.replace("_", ".") if f not in latex_mapping else latex_mapping[f] + ) + fields.append((msb, lsb, name)) + + msb = ilen - 1 + y = "" + if ilen == 16: + encoding = instr_dict[inst]["encoding"][16:] + else: + encoding = instr_dict[inst]["encoding"] + for r in range(0, ilen): + x = encoding[r] + if (msb, ilen - 1 - r + 1) in latex_fixed_fields: + fields.append((msb, ilen - 1 - r + 1, y)) + msb = ilen - 1 - r + y = "" + if x == "-": + if y != "": + fields.append((msb, ilen - 1 - r + 1, y)) + y = "" + msb = ilen - 1 - r - 1 + else: + y += str(x) + if r == ilen - 1: + if y != "": + fields.append((msb, 0, y)) + y = "" + + fields.sort(key=lambda y: y[0], reverse=True) + entry = "" + for r, (msb, lsb, name) in enumerate(fields): + if r == len(fields) - 1: + entry += f'\\multicolumn{{{msb - lsb + 1}}}{{|c|}}{{{name}}} & {inst.upper().replace("_",".")} \\\\\n' + elif r == 0: + entry += f"\\multicolumn{{{msb - lsb + 1}}}{{|c|}}{{{name}}} &\n" + else: + entry += f"\\multicolumn{{{msb - lsb + 1}}}{{c|}}{{{name}}} &\n" + entry += f"\\cline{{2-{ilen+1}}}\n&\n\n" + instr_entries += entry + + # once an entry of the dataset is completed we create the whole table + # with the title of that dataset as sub-heading (sort-of) + if title != "": + content += f""" + +\\multicolumn{{{ilen}}}{{c}}{{}} & \\\\ +\\multicolumn{{{ilen}}}{{c}}{{\\bfseries {title} }} & \\\\ +\\cline{{2-{ilen+1}}} + + & +{instr_entries} +""" + else: + content += f""" +{instr_entries} +""" + + header = f""" +\\newpage + +\\begin{{table}}[p] +\\begin{{small}} +\\begin{{center}} + \\begin{{tabular}} {{{column_size}l}} + {" ".join(['&']*ilen)} \\\\ + + & +{type_entries} +""" + endtable = f""" + +\\end{{tabular}} +\\end{{center}} +\\end{{small}} +{caption} +\\end{{table}} +""" + # dump the contents and return + latex_file.write(header + content + endtable) |