diff --git a/c_utils.py b/c_utils.py index 4143625f..ee25bc24 100644 --- a/c_utils.py +++ b/c_utils.py @@ -1,50 +1,60 @@ -import re +import collections import glob +import logging import os import pprint -import logging -import collections -import yaml +import re import sys + +import yaml + # from shared_utils import overlaps, overlap_allowed, extension_overlap_allowed, instruction_overlap_allowed, process_enc_line, same_base_isa, add_segmented_vls_insn, expand_nf_field from shared_utils import * pp = pprint.PrettyPrinter(indent=2) -logging.basicConfig(level=logging.INFO, format='%(levelname)s:: %(message)s') +logging.basicConfig(level=logging.INFO, format="%(levelname)s:: %(message)s") + def make_c(instr_dict): - mask_match_str = '' - declare_insn_str = '' + mask_match_str = "" + declare_insn_str = "" for i in instr_dict: - mask_match_str += f'#define MATCH_{i.upper().replace(".","_")} {instr_dict[i]["match"]}\n' - mask_match_str += f'#define MASK_{i.upper().replace(".","_")} {instr_dict[i]["mask"]}\n' + mask_match_str += ( + f'#define MATCH_{i.upper().replace(".","_")} {instr_dict[i]["match"]}\n' + ) + mask_match_str += ( + f'#define MASK_{i.upper().replace(".","_")} {instr_dict[i]["mask"]}\n' + ) declare_insn_str += f'DECLARE_INSN({i.replace(".","_")}, MATCH_{i.upper().replace(".","_")}, MASK_{i.upper().replace(".","_")})\n' - csr_names_str = '' - declare_csr_str = '' - for num, name in csrs+csrs32: - csr_names_str += f'#define CSR_{name.upper()} {hex(num)}\n' - declare_csr_str += f'DECLARE_CSR({name}, CSR_{name.upper()})\n' + csr_names_str = "" + declare_csr_str = "" + for num, name in csrs + csrs32: + csr_names_str += f"#define CSR_{name.upper()} {hex(num)}\n" + declare_csr_str += f"DECLARE_CSR({name}, CSR_{name.upper()})\n" - causes_str= '' - declare_cause_str = '' + causes_str = "" + declare_cause_str = "" for num, name in causes: causes_str += f"#define CAUSE_{name.upper().replace(' ', '_')} {hex(num)}\n" - declare_cause_str += f"DECLARE_CAUSE(\"{name}\", CAUSE_{name.upper().replace(' ','_')})\n" + declare_cause_str += ( + f"DECLARE_CAUSE(\"{name}\", CAUSE_{name.upper().replace(' ','_')})\n" + ) - arg_str = '' + arg_str = "" for name, rng in arg_lut.items(): begin = rng[1] - end = rng[0] + end = rng[0] mask = ((1 << (end - begin + 1)) - 1) << begin arg_str += f"#define INSN_FIELD_{name.upper().replace(' ', '_')} {hex(mask)}\n" - with open(f'{os.path.dirname(__file__)}/encoding.h', 'r') as file: + with open(f"{os.path.dirname(__file__)}/encoding.h", "r") as file: enc_header = file.read() commit = os.popen('git log -1 --format="format:%h"').read() - enc_file = open('encoding.out.h','w') - enc_file.write(f'''/* SPDX-License-Identifier: BSD-3-Clause */ + enc_file = open("encoding.out.h", "w") + enc_file.write( + f"""/* SPDX-License-Identifier: BSD-3-Clause */ /* Copyright (c) 2023 RISC-V International */ @@ -67,5 +77,6 @@ def make_c(instr_dict): {declare_csr_str}#endif #ifdef DECLARE_CAUSE {declare_cause_str}#endif -''') +""" + ) enc_file.close() diff --git a/chisel_utils.py b/chisel_utils.py index 061eef4b..957e4f8a 100644 --- a/chisel_utils.py +++ b/chisel_utils.py @@ -1,24 +1,28 @@ -from constants import * +import collections import copy -import re import glob +import logging import os import pprint -import logging -import collections -import yaml +import re import sys + +import yaml + +from constants import * + # from shared_utils import overlaps, overlap_allowed, extension_overlap_allowed, instruction_overlap_allowed, process_enc_line, same_base_isa, add_segmented_vls_insn, expand_nf_field from shared_utils import * pp = pprint.PrettyPrinter(indent=2) -logging.basicConfig(level=logging.INFO, format='%(levelname)s:: %(message)s') +logging.basicConfig(level=logging.INFO, format="%(levelname)s:: %(message)s") + def make_chisel(instr_dict, spinal_hdl=False): - chisel_names='' - cause_names_str='' - csr_names_str = '' + chisel_names = "" + cause_names_str = "" + csr_names_str = "" for i in instr_dict: if spinal_hdl: chisel_names += f' def {i.upper().replace(".","_"):<18s} = M"b{instr_dict[i]["encoding"].replace("-","-")}"\n' @@ -27,7 +31,7 @@ def make_chisel(instr_dict, spinal_hdl=False): if not spinal_hdl: extensions = instr_dict_2_extensions(instr_dict) for e in extensions: - e_instrs = filter(lambda i: instr_dict[i]['extension'][0] == e, instr_dict) + e_instrs = filter(lambda i: instr_dict[i]["extension"][0] == e, instr_dict) if "rv64_" in e: e_format = e.replace("rv64_", "").upper() + "64" elif "rv32_" in e: @@ -38,42 +42,43 @@ def make_chisel(instr_dict, spinal_hdl=False): e_format = e.upper chisel_names += f' val {e_format+"Type"} = Map(\n' for instr in e_instrs: - tmp_instr_name = '"'+instr.upper().replace(".","_")+'"' + tmp_instr_name = '"' + instr.upper().replace(".", "_") + '"' chisel_names += f' {tmp_instr_name:<18s} -> BitPat("b{instr_dict[instr]["encoding"].replace("-","?")}"),\n' - chisel_names += f' )\n' + chisel_names += f" )\n" for num, name in causes: cause_names_str += f' val {name.lower().replace(" ","_")} = {hex(num)}\n' - cause_names_str += ''' val all = { + cause_names_str += """ val all = { val res = collection.mutable.ArrayBuffer[Int]() -''' +""" for num, name in causes: cause_names_str += f' res += {name.lower().replace(" ","_")}\n' - cause_names_str += ''' res.toArray - }''' + cause_names_str += """ res.toArray + }""" - for num, name in csrs+csrs32: - csr_names_str += f' val {name} = {hex(num)}\n' - csr_names_str += ''' val all = { + for num, name in csrs + csrs32: + csr_names_str += f" val {name} = {hex(num)}\n" + csr_names_str += """ val all = { val res = collection.mutable.ArrayBuffer[Int]() -''' +""" for num, name in csrs: - csr_names_str += f''' res += {name}\n''' - csr_names_str += ''' res.toArray + csr_names_str += f""" res += {name}\n""" + csr_names_str += """ res.toArray } val all32 = { val res = collection.mutable.ArrayBuffer(all:_*) -''' +""" for num, name in csrs32: - csr_names_str += f''' res += {name}\n''' - csr_names_str += ''' res.toArray - }''' + csr_names_str += f""" res += {name}\n""" + csr_names_str += """ res.toArray + }""" if spinal_hdl: - chisel_file = open('inst.spinalhdl','w') + chisel_file = open("inst.spinalhdl", "w") else: - chisel_file = open('inst.chisel','w') - chisel_file.write(f''' + chisel_file = open("inst.chisel", "w") + chisel_file.write( + f""" /* Automatically generated by parse_opcodes */ object Instructions {{ {chisel_names} @@ -84,5 +89,6 @@ def make_chisel(instr_dict, spinal_hdl=False): object CSRs {{ {csr_names_str} }} -''') +""" + ) chisel_file.close() diff --git a/go_utils.py b/go_utils.py index 9c5ef2b1..1f4c94bb 100644 --- a/go_utils.py +++ b/go_utils.py @@ -1,23 +1,26 @@ -import re +import collections import glob +import logging import os import pprint -import logging -import collections -import yaml +import re import sys + +import yaml + # from shared_utils import overlaps, overlap_allowed, extension_overlap_allowed, instruction_overlap_allowed, process_enc_line, same_base_isa, add_segmented_vls_insn, expand_nf_field from shared_utils import * pp = pprint.PrettyPrinter(indent=2) -logging.basicConfig(level=logging.INFO, format='%(levelname)s:: %(message)s') +logging.basicConfig(level=logging.INFO, format="%(levelname)s:: %(message)s") + def make_go(instr_dict): args = " ".join(sys.argv) - prelude = f'''// Code generated by {args}; DO NOT EDIT.''' + prelude = f"""// Code generated by {args}; DO NOT EDIT.""" - prelude += ''' + prelude += """ package riscv import "cmd/internal/obj" @@ -33,33 +36,34 @@ def make_go(instr_dict): func encode(a obj.As) *inst { switch a { -''' +""" - endoffile = ''' } + endoffile = """ } return nil } -''' +""" - instr_str = '' + instr_str = "" for i in instr_dict: - enc_match = int(instr_dict[i]['match'],0) - opcode = (enc_match >> 0) & ((1<<7)-1) - funct3 = (enc_match >> 12) & ((1<<3)-1) - rs1 = (enc_match >> 15) & ((1<<5)-1) - rs2 = (enc_match >> 20) & ((1<<5)-1) - csr = (enc_match >> 20) & ((1<<12)-1) - funct7 = (enc_match >> 25) & ((1<<7)-1) - instr_str += f''' case A{i.upper().replace("_","")}: + enc_match = int(instr_dict[i]["match"], 0) + opcode = (enc_match >> 0) & ((1 << 7) - 1) + funct3 = (enc_match >> 12) & ((1 << 3) - 1) + rs1 = (enc_match >> 15) & ((1 << 5) - 1) + rs2 = (enc_match >> 20) & ((1 << 5) - 1) + csr = (enc_match >> 20) & ((1 << 12) - 1) + funct7 = (enc_match >> 25) & ((1 << 7) - 1) + instr_str += f""" case A{i.upper().replace("_","")}: return &inst{{ {hex(opcode)}, {hex(funct3)}, {hex(rs1)}, {hex(rs2)}, {signed(csr,12)}, {hex(funct7)} }} -''' - - with open('inst.go','w') as file: +""" + + with open("inst.go", "w") as file: file.write(prelude) file.write(instr_str) file.write(endoffile) try: import subprocess + subprocess.run(["go", "fmt", "inst.go"]) except: - pass \ No newline at end of file + pass diff --git a/latex_utils.py b/latex_utils.py index 59a8bb52..ee3a7232 100644 --- a/latex_utils.py +++ b/latex_utils.py @@ -1,10 +1,11 @@ #!/usr/bin/env python3 -import pprint import logging +import pprint + from constants import * from shared_utils import create_inst_dict -LOG_FORMAT = '%(levelname)s:: %(message)s' +LOG_FORMAT = "%(levelname)s:: %(message)s" LOG_LEVEL = logging.INFO pretty_printer = pprint.PrettyPrinter(indent=2) @@ -13,37 +14,78 @@ def create_priv_instr_dataset(): """Create dataset list for privileged instructions.""" - system_instr = ['_h', '_s', '_system', '_svinval', '64_h'] + system_instr = ["_h", "_s", "_system", "_svinval", "64_h"] return [ - (system_instr, 'Trap-Return Instructions', ['sret', 'mret'], False), - (system_instr, 'Interrupt-Management Instructions', ['wfi'], False), - (system_instr, 'Supervisor Memory-Management Instructions', ['sfence_vma'], False), - (system_instr, 'Hypervisor Memory-Management Instructions', ['hfence_vvma', 'hfence_gvma'], False), - (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), - (system_instr, 'Hypervisor Virtual-Machine Load and Store Instructions, RV64 only', ['hlv_wu', 'hlv_d', 'hsv_d'], False), - (system_instr, 'Svinval Memory-Management Instructions', ['sinval_vma', 'sfence_w_inval', 'sfence_inval_ir', 'hinval_vvma', 'hinval_gvma'], False) + (system_instr, "Trap-Return Instructions", ["sret", "mret"], False), + (system_instr, "Interrupt-Management Instructions", ["wfi"], False), + ( + system_instr, + "Supervisor Memory-Management Instructions", + ["sfence_vma"], + False, + ), + ( + system_instr, + "Hypervisor Memory-Management Instructions", + ["hfence_vvma", "hfence_gvma"], + False, + ), + ( + 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, + ), + ( + system_instr, + "Hypervisor Virtual-Machine Load and Store Instructions, RV64 only", + ["hlv_wu", "hlv_d", "hsv_d"], + False, + ), + ( + system_instr, + "Svinval Memory-Management Instructions", + [ + "sinval_vma", + "sfence_w_inval", + "sfence_inval_ir", + "hinval_vvma", + "hinval_gvma", + ], + False, + ), ] def make_priv_latex_table(): """Generate and write the LaTeX table for privileged instructions.""" - type_list = ['R-type', 'I-type'] + type_list = ["R-type", "I-type"] dataset_list = create_priv_instr_dataset() - caption = '\\caption{RISC-V Privileged Instructions}' + caption = "\\caption{RISC-V Privileged Instructions}" - with open('priv-instr-table.tex', 'w') as latex_file: + with open("priv-instr-table.tex", "w") 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. 1. creates a single latex file of multiple table 2. Each table limited to a single page 3. Only the last table is assigned a latex-caption. - + - For each table, we assign a type-list that captures the different instruction types (R, I, B, etc.) required for that table. 1. Specify the type-list to capture various instruction types (e.g., R-type, I-type, B-type). 2. Select a list of extensions (e.g., _i, 32_i) whose instructions are necessary to populate the table. @@ -54,113 +96,161 @@ def make_latex_table(): * The last table only has to be given a caption - as per the policy of the riscv-isa-manual. - ''' + """ # File for writing LaTeX content - with open('instr-table.tex', 'w') as latex_file: + with open("instr-table.tex", "w") as latex_file: # Prepare table configurations with type list, datasets, word size & caption table_configurations = get_table_configurations() - + # Map each configuration from above with variables to pass as argumnet for config in table_configurations: # Unpack configuration dictionary into arguments for make_ext_latex_table - type_list = config['type_list'] - datasets = config['datasets'] - word_size = config['word_size'] - caption = config['caption'] - + type_list = config["type_list"] + datasets = config["datasets"] + word_size = config["word_size"] + caption = config["caption"] + # LaTeX table generation function - make_ext_latex_table( - type_list, - datasets, - latex_file, - word_size, - caption - ) + make_ext_latex_table(type_list, datasets, latex_file, word_size, caption) def get_table_configurations(): - ''' + """ Returns a list of table configurations, each specifying the type list, datasets, word size, and caption for LaTeX table generation. - + Returns: list: A list of dictionaries, each representing a table's configuration. - ''' + """ return [ create_table_configuration( - type_list=['R-type', 'I-type', 'S-type', 'B-type', 'U-type', 'J-type'], + type_list=["R-type", "I-type", "S-type", "B-type", "U-type", "J-type"], datasets=[ - create_dataset(['_i', '32_i'], 'RV32I Base Instruction Set', [], False), - create_dataset(['_i'], '', ['fence_tso', 'pause'], True) + create_dataset(["_i", "32_i"], "RV32I Base Instruction Set", [], False), + create_dataset(["_i"], "", ["fence_tso", "pause"], True), ], - word_size=32 + word_size=32, ), create_table_configuration( - type_list=['R-type', 'I-type', 'S-type'], + type_list=["R-type", "I-type", "S-type"], datasets=[ - create_dataset(['64_i'], 'RV64I Base Instruction Set (in addition to RV32I)', [], False), - create_dataset(['_zifencei'], 'RV32/RV64 Zifencei Standard Extension', [], False), - create_dataset(['_zicsr'], 'RV32/RV64 Zicsr Standard Extension', [], False), - create_dataset(['_m', '32_m'], 'RV32M Standard Extension', [], False), - create_dataset(['64_m'], 'RV64M Standard Extension (in addition to RV32M)', [], False) + create_dataset( + ["64_i"], + "RV64I Base Instruction Set (in addition to RV32I)", + [], + False, + ), + create_dataset( + ["_zifencei"], "RV32/RV64 Zifencei Standard Extension", [], False + ), + create_dataset( + ["_zicsr"], "RV32/RV64 Zicsr Standard Extension", [], False + ), + create_dataset(["_m", "32_m"], "RV32M Standard Extension", [], False), + create_dataset( + ["64_m"], + "RV64M Standard Extension (in addition to RV32M)", + [], + False, + ), ], - word_size=32 + word_size=32, ), create_table_configuration( - type_list=['R-type'], + type_list=["R-type"], datasets=[ - create_dataset(['_a'], 'RV32A Standard Extension', [], False), - create_dataset(['64_a'], 'RV64A Standard Extension (in addition to RV32A)', [], False) + create_dataset(["_a"], "RV32A Standard Extension", [], False), + create_dataset( + ["64_a"], + "RV64A Standard Extension (in addition to RV32A)", + [], + False, + ), ], - word_size=32 + word_size=32, ), create_table_configuration( - type_list=['R-type', 'R4-type', 'I-type', 'S-type'], + type_list=["R-type", "R4-type", "I-type", "S-type"], datasets=[ - create_dataset(['_f'], 'RV32F Standard Extension', [], False), - create_dataset(['64_f'], 'RV64F Standard Extension (in addition to RV32F)', [], False) + create_dataset(["_f"], "RV32F Standard Extension", [], False), + create_dataset( + ["64_f"], + "RV64F Standard Extension (in addition to RV32F)", + [], + False, + ), ], - word_size=32 + word_size=32, ), create_table_configuration( - type_list=['R-type', 'R4-type', 'I-type', 'S-type'], + type_list=["R-type", "R4-type", "I-type", "S-type"], datasets=[ - create_dataset(['_d'], 'RV32D Standard Extension', [], False), - create_dataset(['64_d'], 'RV64D Standard Extension (in addition to RV32D)', [], False) + create_dataset(["_d"], "RV32D Standard Extension", [], False), + create_dataset( + ["64_d"], + "RV64D Standard Extension (in addition to RV32D)", + [], + False, + ), ], - word_size=32 + word_size=32, ), create_table_configuration( - type_list=['R-type', 'R4-type', 'I-type', 'S-type'], + type_list=["R-type", "R4-type", "I-type", "S-type"], datasets=[ - create_dataset(['_q'], 'RV32Q Standard Extension', [], False), - create_dataset(['64_q'], 'RV64Q Standard Extension (in addition to RV32Q)', [], False) + create_dataset(["_q"], "RV32Q Standard Extension", [], False), + create_dataset( + ["64_q"], + "RV64Q Standard Extension (in addition to RV32Q)", + [], + False, + ), ], - word_size=32 + word_size=32, ), create_table_configuration( - type_list=['R-type', 'R4-type', 'I-type', 'S-type'], + type_list=["R-type", "R4-type", "I-type", "S-type"], datasets=[ - create_dataset(['_zfh', '_d_zfh', '_q_zfh'], 'RV32Zfh Standard Extension', [], False), - create_dataset(['64_zfh'], 'RV64Zfh Standard Extension (in addition to RV32Zfh)', [], False) + create_dataset( + ["_zfh", "_d_zfh", "_q_zfh"], + "RV32Zfh Standard Extension", + [], + False, + ), + create_dataset( + ["64_zfh"], + "RV64Zfh Standard Extension (in addition to RV32Zfh)", + [], + False, + ), ], word_size=32, - caption='\\caption{Instruction listing for RISC-V}' + caption="\\caption{Instruction listing for RISC-V}", ), create_table_configuration( - type_list=[''], + type_list=[""], datasets=[ - create_dataset(['_c', '32_c', '32_c_f', '_c_d'], 'RV32C Standard Extension', [], False), - create_dataset(['64_c'], 'RV64C Standard Extension (in addition to RV32C)', [], False) + create_dataset( + ["_c", "32_c", "32_c_f", "_c_d"], + "RV32C Standard Extension", + [], + False, + ), + create_dataset( + ["64_c"], + "RV64C Standard Extension (in addition to RV32C)", + [], + False, + ), ], word_size=16, - caption='' - ) + caption="", + ), ] -def create_table_configuration(type_list, datasets, word_size, caption=''): - ''' +def create_table_configuration(type_list, datasets, word_size, caption=""): + """ Creates a table configuration dictionary with the provided parameters. Parameters: @@ -175,17 +265,17 @@ def create_table_configuration(type_list, datasets, word_size, caption=''): Returns: dict: A dictionary representing the table configuration. - ''' + """ return { "type_list": type_list, "datasets": datasets, "word_size": word_size, - "caption": caption + "caption": caption, } def create_dataset(extensions, title, instructions, include_pseudo_ops): - ''' + """ Creates a dataset tuple for table configuration. Parameters: @@ -196,53 +286,53 @@ def create_dataset(extensions, title, instructions, include_pseudo_ops): Returns: tuple: A tuple representing the dataset configuration. - ''' + """ return (extensions, title, instructions, include_pseudo_ops) def make_ext_latex_table(type_list, dataset, latex_file, ilen, caption): - ''' + """ For a given collection of extensions this function dumps out a complete latex table which includes the encodings of the instructions. Args: - - type_list (list of str): - 1. A list of instruction types (R, I, B, etc) that are treated as header for each table. + - type_list (list of str): + 1. A list of instruction types (R, I, B, etc) that are treated as header for each table. 2. Each table will have its own requirements and type_list must include all the instruction-types that the table needs. 3. All elements of this list must be present in the latex_inst_type dictionary defined in constants.py - - + + - dataset (list of tuples): A list of 3-element tuples where each tuple consists of: 1. list_of_extensions (list): A list of extensions whose instructions will be populated under the respective title. 2. title (str): A title associated with the respective table. - 3. list_of_instructions (list): If not empty, only these instructions present in the corresponding extension + 3. list_of_instructions (list): If not empty, only these instructions present in the corresponding extension will be included in the table, while others will be ignored. - latex_file (file pointer): A file pointer to the LaTeX file where the generated table will be written. - + - ilen (int): The ilen input indicates the length of the instruction for which the table is created. - + - caption (str): The caption for the LaTeX table. Returns: - None: The function writes the generated LaTeX table directly to the provided `latex_file`. Process: - 1. Creates table headers based on the instruction types in `type_list` using the `latex_inst_type` dictionary + 1. Creates table headers based on the instruction types in `type_list` using the `latex_inst_type` dictionary from constants.py. - + 2. Iterates through each entry in the dataset to: - Generate an exhaustive list of instructions for each dataset using `create_inst_dict`. - Apply any instruction filters based on `list_of_instructions` to select only relevant instructions. - + 3. For each instruction, generates LaTeX table entries. - - Uses `arg_lut` from constants.py to determine the position of arguments in the encoding, and creates multicolumn + - Uses `arg_lut` from constants.py to determine the position of arguments in the encoding, and creates multicolumn LaTeX entries for these arguments. - - Handles hardcoded bits (e.g., strings of 1s and 0s) similarly, creating multicolumn entries for continuous + - Handles hardcoded bits (e.g., strings of 1s and 0s) similarly, creating multicolumn entries for continuous strings of bits. 4. Writes the LaTeX table to `latex_file` with a specific format suitable for instructions of size `ilen`. - ''' + """ column_size = get_column_size(ilen) type_entries = generate_type_entries(ilen) @@ -264,13 +354,13 @@ def make_ext_latex_table(type_list, dataset, latex_file, ilen, caption): def get_column_size(ilen): """Generate the column size string based on instruction length (ilen).""" - return "".join(['p{0.002in}'] * (ilen + 1)) + return "".join(["p{0.002in}"] * (ilen + 1)) def generate_type_entries(ilen): """Generate the type entries section of the LaTeX table.""" if ilen == 32: - return ''' + return """ \\multicolumn{3}{l}{31} & \\multicolumn{2}{r}{27} & \\multicolumn{1}{c}{26} & @@ -285,9 +375,9 @@ def generate_type_entries(ilen): \\multicolumn{1}{r}{7} & \\multicolumn{6}{l}{6} & \\multicolumn{1}{r}{0} \\\\ - \\cline{2-33}\n&\n\n''' + \\cline{2-33}\n&\n\n""" else: - return ''' + return """ \\multicolumn{1}{c}{15} & \\multicolumn{1}{c}{14} & \\multicolumn{1}{c}{13} & @@ -304,7 +394,7 @@ def generate_type_entries(ilen): \\multicolumn{1}{c}{2} & \\multicolumn{1}{c}{1} & \\multicolumn{1}{c}{0} \\\\ - \\cline{2-17}\n&\n\n''' + \\cline{2-17}\n&\n\n""" def get_type_dict(type_list): @@ -315,7 +405,7 @@ def get_type_dict(type_list): def build_instruction_type_entry(inst_type, fields, ilen): """Build a LaTeX table entry for each instruction type.""" entries = [] - for field in fields['variable_fields']: + for field in fields["variable_fields"]: (msb, lsb) = arg_lut[field] name = latex_mapping.get(field, field) entries.append((msb, lsb, name)) @@ -326,21 +416,23 @@ def build_instruction_type_entry(inst_type, fields, ilen): def format_table_entry(fields, entry_type, ilen): """Generate formatted LaTeX table entry.""" fields.sort(key=lambda f: f[0], reverse=True) - entry = '' + entry = "" for i, (msb, lsb, name) in enumerate(fields): col_size = msb - lsb + 1 if i == len(fields) - 1: - entry += f'\\multicolumn{{{col_size}}}{{|c|}}{{{name}}} & {entry_type} \\\\\n' + entry += ( + f"\\multicolumn{{{col_size}}}{{|c|}}{{{name}}} & {entry_type} \\\\\n" + ) elif i == 0: - entry += f'\\multicolumn{{{col_size}}}{{|c|}}{{{name}}} &\n' + entry += f"\\multicolumn{{{col_size}}}{{|c|}}{{{name}}} &\n" else: - entry += f'\\multicolumn{{{col_size}}}{{c|}}{{{name}}} &\n' - return entry + f'\\cline{{2-{ilen+1}}}\n&\n\n' + entry += f"\\multicolumn{{{col_size}}}{{c|}}{{{name}}} &\n" + return entry + f"\\cline{{2-{ilen+1}}}\n&\n\n" def generate_dataset_content(dataset, ilen): """Generate LaTeX content for each dataset entry.""" - content = '' + content = "" for ext_list, title, filter_list, include_pseudo in dataset: instr_dict = get_instruction_dict(ext_list, include_pseudo) filtered_list = filter_list if filter_list else list(instr_dict.keys()) @@ -357,20 +449,22 @@ def get_instruction_dict(ext_list, include_pseudo): """Create a dictionary of instructions for given extensions.""" instr_dict = {} for ext in ext_list: - instr_dict.update(create_inst_dict([f'rv{ext}'], include_pseudo)) + instr_dict.update(create_inst_dict([f"rv{ext}"], include_pseudo)) return instr_dict def generate_instruction_entries(instr_dict, inst_list, ilen): """Generate LaTeX entries for each instruction in the list.""" - instr_entries = '' + instr_entries = "" for inst in inst_list: if inst not in instr_dict: - logging.error(f'Instruction {inst} not found in instr_dict') + logging.error(f"Instruction {inst} not found in instr_dict") raise SystemExit(1) fields = parse_instruction_fields(instr_dict[inst], ilen) - instr_entries += format_table_entry(fields, inst.upper().replace("_", "."), ilen) + instr_entries += format_table_entry( + fields, inst.upper().replace("_", "."), ilen + ) return instr_entries @@ -378,16 +472,16 @@ def generate_instruction_entries(instr_dict, inst_list, ilen): def parse_instruction_fields(inst_data, ilen): """Parse and extract fields from instruction data.""" fields = [] - encoding = inst_data['encoding'][16:] if ilen == 16 else inst_data['encoding'] + encoding = inst_data["encoding"][16:] if ilen == 16 else inst_data["encoding"] msb = ilen - 1 - y = '' + y = "" for i in range(ilen): x = encoding[i] - if x == '-': + if x == "-": if y: fields.append((msb, ilen - i, y)) - y = '' + y = "" msb -= 1 else: y += str(x) @@ -401,15 +495,16 @@ def parse_instruction_fields(inst_data, ilen): def generate_dataset_title(title, ilen): """Generate LaTeX dataset title.""" - return f''' + return f""" \\multicolumn{{{ilen}}}{{c}}{{}} & \\\\ \\multicolumn{{{ilen}}}{{c}}{{\\bf {title} }} & \\\\ \\cline{{2-{ilen + 1}}} -''' +""" + def generate_table_header(column_size, ilen, type_entries): """Generate LaTeX table header.""" - return f''' + return f""" \\newpage \\begin{{table}}[p] @@ -420,16 +515,16 @@ def generate_table_header(column_size, ilen, type_entries): & {type_entries} -''' +""" def generate_table_footer(caption): """Generate LaTeX table footer.""" - return f''' + return f""" \\end{{tabular}} \\end{{center}} \\end{{small}} {caption} \\end{{table}} -''' +""" diff --git a/parse.py b/parse.py index 586cd66e..bba59d81 100755 --- a/parse.py +++ b/parse.py @@ -1,33 +1,36 @@ #!/usr/bin/env python3 import collections import logging +import pprint import sys + import yaml -import pprint -from constants import * -from shared_utils import create_inst_dict, add_segmented_vls_insn -from latex_utils import make_latex_table, make_priv_latex_table +from c_utils import make_c from chisel_utils import make_chisel +from constants import * +from go_utils import make_go +from latex_utils import make_latex_table, make_priv_latex_table from rust_utils import make_rust +from shared_utils import add_segmented_vls_insn, create_inst_dict from sverilog_utils import make_sverilog -from c_utils import make_c -from go_utils import make_go -LOG_FORMAT = '%(levelname)s:: %(message)s' +LOG_FORMAT = "%(levelname)s:: %(message)s" LOG_LEVEL = logging.INFO pretty_printer = pprint.PrettyPrinter(indent=2) logging.basicConfig(level=LOG_LEVEL, format=LOG_FORMAT) + def remove_non_extensions(args): """ Removes non-extension flags from the command-line arguments. """ extensions = args[1:] - flags = ['-c', '-latex', '-chisel', '-sverilog', '-rust', '-go', '-spinalhdl'] + flags = ["-c", "-latex", "-chisel", "-sverilog", "-rust", "-go", "-spinalhdl"] return [ext for ext in extensions if ext not in flags] + def process_instruction_dict(extensions, include_pseudo): """ Processes the instruction dictionary by creating and adding segmented instructions. @@ -36,78 +39,90 @@ def process_instruction_dict(extensions, include_pseudo): instr_dict = add_segmented_vls_insn(instr_dict) return collections.OrderedDict(sorted(instr_dict.items())) -def write_yaml(instr_dict, filename='instr_dict.yaml'): + +def write_yaml(instr_dict, filename="instr_dict.yaml"): """ Writes the instruction dictionary to a YAML file. """ - with open(filename, 'w') as outfile: + with open(filename, "w") as outfile: yaml.dump(instr_dict, outfile, default_flow_style=False) + def generate_outputs(instr_dict, extensions): """ Generates output files based on selected extensions and flags. """ # Dictionary to map extensions to their respective functions and logging messages extension_map = { - '-c': { - 'function': lambda: make_c(collections.OrderedDict(sorted(create_inst_dict(extensions, False, include_pseudo_ops=emitted_pseudo_ops).items()))), - 'message': 'encoding.out.h generated successfully' + "-c": { + "function": lambda: make_c( + collections.OrderedDict( + sorted( + create_inst_dict( + extensions, False, include_pseudo_ops=emitted_pseudo_ops + ).items() + ) + ) + ), + "message": "encoding.out.h generated successfully", + }, + "-chisel": { + "function": lambda: make_chisel(instr_dict), + "message": "inst.chisel generated successfully", }, - '-chisel': { - 'function': lambda: make_chisel(instr_dict), - 'message': 'inst.chisel generated successfully' + "-spinalhdl": { + "function": lambda: make_chisel(instr_dict, spinal_hdl=True), + "message": "inst.spinalhdl generated successfully", }, - '-spinalhdl': { - 'function': lambda: make_chisel(instr_dict, spinal_hdl=True), - 'message': 'inst.spinalhdl generated successfully' + "-sverilog": { + "function": lambda: make_sverilog(instr_dict), + "message": "inst.sverilog generated successfully", }, - '-sverilog': { - 'function': lambda: make_sverilog(instr_dict), - 'message': 'inst.sverilog generated successfully' + "-rust": { + "function": lambda: make_rust(instr_dict), + "message": "inst.rs generated successfully", }, - '-rust': { - 'function': lambda: make_rust(instr_dict), - 'message': 'inst.rs generated successfully' + "-go": { + "function": lambda: make_go(instr_dict), + "message": "inst.go generated successfully", }, - '-go': { - 'function': lambda: make_go(instr_dict), - 'message': 'inst.go generated successfully' + "-latex": { + "function": lambda: (make_latex_table(), make_priv_latex_table()), + "message": [ + "instr-table.tex generated successfully", + "priv-instr-table.tex generated successfully", + ], }, - '-latex': { - 'function': lambda: (make_latex_table(), make_priv_latex_table()), - 'message': [ - 'instr-table.tex generated successfully', - 'priv-instr-table.tex generated successfully' - ] - } } for ext, actions in extension_map.items(): if ext in extensions: try: - actions['function']() - if isinstance(actions['message'], list): - for msg in actions['message']: + actions["function"]() + if isinstance(actions["message"], list): + for msg in actions["message"]: logging.info(msg) else: - logging.info(actions['message']) + logging.info(actions["message"]) except Exception as e: logging.error(f"Error generating output for {ext}: {e}") + def main(): """ Main function for processing and generation of files based on command-line arguments. """ - print(f'Running with args : {sys.argv}') + print(f"Running with args : {sys.argv}") extensions = remove_non_extensions(sys.argv) - print(f'Extensions selected : {extensions}') + print(f"Extensions selected : {extensions}") - include_pseudo = '-go' in sys.argv[1:] + include_pseudo = "-go" in sys.argv[1:] instr_dict = process_instruction_dict(extensions, include_pseudo) write_yaml(instr_dict) generate_outputs(instr_dict, sys.argv[1:]) + if __name__ == "__main__": main() diff --git a/rust_utils.py b/rust_utils.py index 6f540a25..19a47b95 100644 --- a/rust_utils.py +++ b/rust_utils.py @@ -1,31 +1,39 @@ -from constants import * +import collections import copy -import re import glob +import logging import os import pprint -import logging -import collections -import yaml +import re import sys + +import yaml + +from constants import * + # from shared_utils import overlaps, overlap_allowed, extension_overlap_allowed, instruction_overlap_allowed, process_enc_line, same_base_isa, add_segmented_vls_insn, expand_nf_field from shared_utils import * pp = pprint.PrettyPrinter(indent=2) -logging.basicConfig(level=logging.INFO, format='%(levelname)s:: %(message)s') +logging.basicConfig(level=logging.INFO, format="%(levelname)s:: %(message)s") + def make_rust(instr_dict): - mask_match_str= '' + mask_match_str = "" for i in instr_dict: mask_match_str += f'const MATCH_{i.upper().replace(".","_")}: u32 = {(instr_dict[i]["match"])};\n' mask_match_str += f'const MASK_{i.upper().replace(".","_")}: u32 = {(instr_dict[i]["mask"])};\n' - for num, name in csrs+csrs32: - mask_match_str += f'const CSR_{name.upper()}: u16 = {hex(num)};\n' + for num, name in csrs + csrs32: + mask_match_str += f"const CSR_{name.upper()}: u16 = {hex(num)};\n" for num, name in causes: - mask_match_str += f'const CAUSE_{name.upper().replace(" ","_")}: u8 = {hex(num)};\n' - rust_file = open('inst.rs','w') - rust_file.write(f''' + mask_match_str += ( + f'const CAUSE_{name.upper().replace(" ","_")}: u8 = {hex(num)};\n' + ) + rust_file = open("inst.rs", "w") + rust_file.write( + f""" /* Automatically generated by parse_opcodes */ {mask_match_str} -''') +""" + ) rust_file.close() diff --git a/shared_utils.py b/shared_utils.py index c10d175f..8c081e20 100644 --- a/shared_utils.py +++ b/shared_utils.py @@ -1,14 +1,14 @@ #!/usr/bin/env python3 import copy -import re import glob +import logging import os import pprint -import logging +import re from constants import * -LOG_FORMAT = '%(levelname)s:: %(message)s' +LOG_FORMAT = "%(levelname)s:: %(message)s" LOG_LEVEL = logging.INFO pretty_printer = pprint.PrettyPrinter(indent=2) @@ -16,7 +16,7 @@ def process_enc_line(line, ext): - ''' + """ This function processes each line of the encoding files (rv*). As part of the processing, the function ensures that the encoding is legal through the following checks:: @@ -41,7 +41,7 @@ def process_enc_line(line, ext): this instruction - mask: hex value representin the bits that need to be masked to extract the value required for matching. - ''' + """ encoding = initialize_encoding() name, remaining = parse_instruction_name(line) @@ -65,13 +65,13 @@ def process_enc_line(line, ext): def initialize_encoding(): """Initialize a 32-bit encoding with '-' representing 'don't care'.""" - return ['-'] * 32 + return ["-"] * 32 def parse_instruction_name(line): """Extract the instruction name and remaining part of the line.""" - name, remaining = line.split(' ', 1) - name = name.replace('.', '_').lstrip() + name, remaining = line.split(" ", 1) + name = name.replace(".", "_").lstrip() return name, remaining @@ -94,15 +94,19 @@ def validate_entry_value(msb, lsb, entry, line): """Ensure that the value assigned to a bit range is legal for its width.""" entry_value = int(entry, 0) if entry_value >= (1 << (msb - lsb + 1)): - log_and_exit(f"{get_instruction_name(line)} has an illegal value for the bit width {msb - lsb}") + log_and_exit( + f"{get_instruction_name(line)} has an illegal value for the bit width {msb - lsb}" + ) def update_encoding(msb, lsb, entry, encoding, line): """Update the encoding array for a given bit range.""" entry_value = int(entry, 0) for ind in range(lsb, msb + 1): - if encoding[31 - ind] != '-': - log_and_exit(f"{get_instruction_name(line)} has overlapping bits in its opcodes") + if encoding[31 - ind] != "-": + log_and_exit( + f"{get_instruction_name(line)} has overlapping bits in its opcodes" + ) encoding[31 - ind] = str((entry_value >> (ind - lsb)) & 1) @@ -111,22 +115,24 @@ def process_single_fixed(remaining, encoding, line): for lsb, value, _ in single_fixed.findall(remaining): lsb = int(lsb, 0) value = int(value, 0) - if encoding[31 - lsb] != '-': - log_and_exit(f"{get_instruction_name(line)} has overlapping bits in its opcodes") + if encoding[31 - lsb] != "-": + log_and_exit( + f"{get_instruction_name(line)} has overlapping bits in its opcodes" + ) encoding[31 - lsb] = str(value) - return fixed_ranges.sub(' ', remaining) + return fixed_ranges.sub(" ", remaining) def create_match_and_mask(encoding): """Generate match and mask strings from the encoding array.""" - match = ''.join(encoding).replace('-', '0') - mask = ''.join(encoding).replace('0', '1').replace('-', '0') + match = "".join(encoding).replace("-", "0") + mask = "".join(encoding).replace("0", "1").replace("-", "0") return match, mask def process_arguments(remaining, encoding, name): """Process instruction arguments and update the encoding with argument positions.""" - args = single_fixed.sub(' ', remaining).split() + args = single_fixed.sub(" ", remaining).split() encoding_args = encoding.copy() for arg in args: if arg not in arg_lut: @@ -138,16 +144,18 @@ def process_arguments(remaining, encoding, name): def handle_missing_arg(arg, name): """Handle missing argument mapping in arg_lut.""" - if '=' in arg and (existing_arg := arg.split('=')[0]) in arg_lut: - arg_lut[arg] = arg_lut[existing_arg] - else: - log_and_exit(f"Variable {arg} in instruction {name} not mapped in arg_lut") + if "=" in arg: + existing_arg = arg.split("=")[0] + if existing_arg in arg_lut: + arg_lut[arg] = arg_lut[existing_arg] + return + log_and_exit(f"Variable {arg} in instruction {name} not mapped in arg_lut") def update_arg_encoding(msb, lsb, arg, encoding_args, name): """Update the encoding array with the argument positions.""" for ind in range(lsb, msb + 1): - if encoding_args[31 - ind] != '-': + if encoding_args[31 - ind] != "-": log_and_exit(f"Variable {arg} overlaps in bit {ind} in instruction {name}") encoding_args[31 - ind] = arg @@ -155,11 +163,11 @@ def update_arg_encoding(msb, lsb, arg, encoding_args, name): def create_instruction_dict(encoding, args, ext, match, mask): """Create the final dictionary for the instruction.""" return { - 'encoding': ''.join(encoding), - 'variable_fields': args, - 'extension': [os.path.basename(ext)], - 'match': hex(int(match, 2)), - 'mask': hex(int(mask, 2)), + "encoding": "".join(encoding), + "variable_fields": args, + "extension": [os.path.basename(ext)], + "match": hex(int(match, 2)), + "mask": hex(int(mask, 2)), } @@ -171,53 +179,53 @@ def log_and_exit(message): def get_instruction_name(line): """Helper to extract the instruction name from a line.""" - return line.split(' ')[0] + return line.split(" ")[0] + def overlaps(x, y): """ Check if two bit strings overlap without conflicts. - + Args: x (str): First bit string. y (str): Second bit string. - + Returns: bool: True if the bit strings overlap without conflicts, False otherwise. - - In the context of RISC-V opcodes, this function ensures that the bit ranges + + In the context of RISC-V opcodes, this function ensures that the bit ranges defined by two different bit strings do not conflict. """ - + # Minimum length of the two strings min_len = min(len(x), len(y)) - + for char_x, char_y in zip(x[:min_len], y[:min_len]): - if char_x != '-' and char_y != '-' and char_x != char_y: + if char_x != "-" and char_y != "-" and char_x != char_y: return False - + return True def overlap_allowed(a, x, y): """ Check if there is an overlap between keys and values in a dictionary. - + Args: a (dict): The dictionary where keys are mapped to sets or lists of keys. x (str): The first key to check. y (str): The second key to check. - + Returns: - bool: True if both (x, y) or (y, x) are present in the dictionary + bool: True if both (x, y) or (y, x) are present in the dictionary as described, False otherwise. - - This function determines if `x` is a key in the dictionary `a` and - its corresponding value contains `y`, or if `y` is a key and its + + This function determines if `x` is a key in the dictionary `a` and + its corresponding value contains `y`, or if `y` is a key and its corresponding value contains `x`. """ - - return x in a and y in a[x] or \ - y in a and x in a[y] + + return x in a and y in a[x] or y in a and x in a[y] # Checks if overlap between two extensions is allowed @@ -235,9 +243,11 @@ def same_base_isa(ext_name, ext_name_list): type1 = ext_name.split("_")[0] for ext_name1 in ext_name_list: type2 = ext_name1.split("_")[0] - if type1 == type2 or \ - (type2 == "rv" and type1 in ["rv32", "rv64"]) or \ - (type1 == "rv" and type2 in ["rv32", "rv64"]): + if ( + type1 == type2 + or (type2 == "rv" and type1 in ["rv32", "rv64"]) + or (type1 == "rv" and type2 in ["rv32", "rv64"]) + ): return True return False @@ -246,7 +256,7 @@ def same_base_isa(ext_name, ext_name_list): def add_segmented_vls_insn(instr_dict): updated_dict = {} for k, v in instr_dict.items(): - if "nf" in v['variable_fields']: + if "nf" in v["variable_fields"]: updated_dict.update(expand_nf_field(k, v)) else: updated_dict[k] = v @@ -255,27 +265,33 @@ def add_segmented_vls_insn(instr_dict): # Expands nf field in instruction name and updates instruction details def expand_nf_field(name, single_dict): - if "nf" not in single_dict['variable_fields']: + if "nf" not in single_dict["variable_fields"]: logging.error(f"Cannot expand nf field for instruction {name}") raise SystemExit(1) - single_dict['variable_fields'].remove("nf") # Remove "nf" from variable fields - single_dict['mask'] = hex(int(single_dict['mask'], 16) | (0b111 << 29)) # Update mask + single_dict["variable_fields"].remove("nf") # Remove "nf" from variable fields + single_dict["mask"] = hex( + int(single_dict["mask"], 16) | (0b111 << 29) + ) # Update mask - name_expand_index = name.find('e') + name_expand_index = name.find("e") expanded_instructions = [] for nf in range(8): # Expand nf for values 0 to 7 new_single_dict = copy.deepcopy(single_dict) - new_single_dict['match'] = hex(int(single_dict['match'], 16) | (nf << 29)) - new_single_dict['encoding'] = format(nf, '03b') + single_dict['encoding'][3:] - new_name = name if nf == 0 else f"{name[:name_expand_index]}seg{nf+1}{name[name_expand_index:]}" + new_single_dict["match"] = hex(int(single_dict["match"], 16) | (nf << 29)) + new_single_dict["encoding"] = format(nf, "03b") + single_dict["encoding"][3:] + new_name = ( + name + if nf == 0 + else f"{name[:name_expand_index]}seg{nf+1}{name[name_expand_index:]}" + ) expanded_instructions.append((new_name, new_single_dict)) return expanded_instructions # Extracts the extensions used in an instruction dictionary def instr_dict_2_extensions(instr_dict): - return list({item['extension'][0] for item in instr_dict.values()}) + return list({item["extension"][0] for item in instr_dict.values()}) # Returns signed interpretation of a value within a given width @@ -289,103 +305,127 @@ def read_lines(file): lines = (line.rstrip() for line in fp) return [line for line in lines if line and not line.startswith("#")] + def process_standard_instructions(lines, instr_dict, file_name): """Processes standard instructions from the given lines and updates the instruction dictionary.""" for line in lines: - if '$import' in line or '$pseudo' in line: + if "$import" in line or "$pseudo" in line: continue - logging.debug(f'Processing line: {line}') + logging.debug(f"Processing line: {line}") name, single_dict = process_enc_line(line, file_name) ext_name = os.path.basename(file_name) if name in instr_dict: var = instr_dict[name]["extension"] if same_base_isa(ext_name, var): - log_and_exit(f'Instruction {name} from {ext_name} is already added from {var} in same base ISA') - elif instr_dict[name]['encoding'] != single_dict['encoding']: - log_and_exit(f'Instruction {name} from {ext_name} has different encodings in different base ISAs') - - instr_dict[name]['extension'].extend(single_dict['extension']) + log_and_exit( + f"Instruction {name} from {ext_name} is already added from {var} in same base ISA" + ) + elif instr_dict[name]["encoding"] != single_dict["encoding"]: + log_and_exit( + f"Instruction {name} from {ext_name} has different encodings in different base ISAs" + ) + + instr_dict[name]["extension"].extend(single_dict["extension"]) else: for key, item in instr_dict.items(): - if overlaps(item['encoding'], single_dict['encoding']) and \ - not extension_overlap_allowed(ext_name, item['extension'][0]) and \ - not instruction_overlap_allowed(name, key) and \ - same_base_isa(ext_name, item['extension']): - log_and_exit(f'Instruction {name} in extension {ext_name} overlaps with {key} in {item["extension"]}') + if ( + overlaps(item["encoding"], single_dict["encoding"]) + and not extension_overlap_allowed(ext_name, item["extension"][0]) + and not instruction_overlap_allowed(name, key) + and same_base_isa(ext_name, item["extension"]) + ): + log_and_exit( + f'Instruction {name} in extension {ext_name} overlaps with {key} in {item["extension"]}' + ) instr_dict[name] = single_dict -def process_pseudo_instructions(lines, instr_dict, file_name, opcodes_dir, include_pseudo, include_pseudo_ops): + +def process_pseudo_instructions( + lines, instr_dict, file_name, opcodes_dir, include_pseudo, include_pseudo_ops +): """Processes pseudo instructions from the given lines and updates the instruction dictionary.""" for line in lines: - if '$pseudo' not in line: + if "$pseudo" not in line: continue - logging.debug(f'Processing pseudo line: {line}') + logging.debug(f"Processing pseudo line: {line}") ext, orig_inst, pseudo_inst, line_content = pseudo_regex.findall(line)[0] ext_file = find_extension_file(ext, opcodes_dir) validate_instruction_in_extension(orig_inst, ext_file, file_name, pseudo_inst) - name, single_dict = process_enc_line(f'{pseudo_inst} {line_content}', file_name) - if orig_inst.replace('.', '_') not in instr_dict or include_pseudo or name in include_pseudo_ops: + name, single_dict = process_enc_line(f"{pseudo_inst} {line_content}", file_name) + if ( + orig_inst.replace(".", "_") not in instr_dict + or include_pseudo + or name in include_pseudo_ops + ): if name not in instr_dict: instr_dict[name] = single_dict - logging.debug(f'Including pseudo_op: {name}') + logging.debug(f"Including pseudo_op: {name}") else: - if single_dict['match'] != instr_dict[name]['match']: - instr_dict[f'{name}_pseudo'] = single_dict - elif single_dict['extension'] not in instr_dict[name]['extension']: - instr_dict[name]['extension'].extend(single_dict['extension']) + if single_dict["match"] != instr_dict[name]["match"]: + instr_dict[f"{name}_pseudo"] = single_dict + elif single_dict["extension"] not in instr_dict[name]["extension"]: + instr_dict[name]["extension"].extend(single_dict["extension"]) + def process_imported_instructions(lines, instr_dict, file_name, opcodes_dir): """Processes imported instructions from the given lines and updates the instruction dictionary.""" for line in lines: - if '$import' not in line: + if "$import" not in line: continue - logging.debug(f'Processing imported line: {line}') + logging.debug(f"Processing imported line: {line}") import_ext, reg_instr = imported_regex.findall(line)[0] ext_file = find_extension_file(import_ext, opcodes_dir) validate_instruction_in_extension(reg_instr, ext_file, file_name, line) for oline in open(ext_file): - if re.findall(f'^\\s*{reg_instr}\\s+', oline): + if re.findall(f"^\\s*{reg_instr}\\s+", oline): name, single_dict = process_enc_line(oline, file_name) if name in instr_dict: - if instr_dict[name]['encoding'] != single_dict['encoding']: - log_and_exit(f'Imported instruction {name} from {os.path.basename(file_name)} has different encodings') - instr_dict[name]['extension'].extend(single_dict['extension']) + if instr_dict[name]["encoding"] != single_dict["encoding"]: + log_and_exit( + f"Imported instruction {name} from {os.path.basename(file_name)} has different encodings" + ) + instr_dict[name]["extension"].extend(single_dict["extension"]) else: instr_dict[name] = single_dict break + def find_extension_file(ext, opcodes_dir): """Finds the extension file path, considering the unratified directory if necessary.""" - ext_file = f'{opcodes_dir}/{ext}' + ext_file = f"{opcodes_dir}/{ext}" if not os.path.exists(ext_file): - ext_file = f'{opcodes_dir}/unratified/{ext}' + ext_file = f"{opcodes_dir}/unratified/{ext}" if not os.path.exists(ext_file): - log_and_exit(f'Extension {ext} not found.') + log_and_exit(f"Extension {ext} not found.") return ext_file + def validate_instruction_in_extension(inst, ext_file, file_name, pseudo_inst): """Validates if the original instruction exists in the dependent extension.""" found = False for oline in open(ext_file): - if re.findall(f'^\\s*{inst}\\s+', oline): + if re.findall(f"^\\s*{inst}\\s+", oline): found = True break if not found: - log_and_exit(f'Original instruction {inst} required by pseudo_op {pseudo_inst} in {file_name} not found in {ext_file}') + log_and_exit( + f"Original instruction {inst} required by pseudo_op {pseudo_inst} in {file_name} not found in {ext_file}" + ) + def create_inst_dict(file_filter, include_pseudo=False, include_pseudo_ops=[]): """Creates a dictionary of instructions based on the provided file filters.""" - - ''' + + """ This function return a dictionary containing all instructions associated - with an extension defined by the file_filter input. - + with an extension defined by the file_filter input. + Allowed input extensions: needs to be rv* file name without the 'rv' prefix i.e. '_i', '32_i', etc. Each node of the dictionary will correspond to an instruction which again is @@ -401,7 +441,7 @@ def create_inst_dict(file_filter, include_pseudo=False, include_pseudo_ops=[]): this instruction - mask: hex value representin the bits that need to be masked to extract the value required for matching. - + In order to build this dictionary, the function does 2 passes over the same rv file: - First pass: extracts all standard instructions, skipping pseudo ops @@ -413,28 +453,39 @@ def create_inst_dict(file_filter, include_pseudo=False, include_pseudo_ops=[]): - Checks if the dependent extension and instruction exist. - Adds the pseudo_op to the dictionary if the dependent instruction is not already present; otherwise, it is skipped. - ''' + """ opcodes_dir = os.path.dirname(os.path.realpath(__file__)) instr_dict = {} - file_names = [file for fil in file_filter for file in sorted(glob.glob(f'{opcodes_dir}/{fil}'), reverse=True)] - - logging.debug('Collecting standard instructions') + file_names = [ + file + for fil in file_filter + for file in sorted(glob.glob(f"{opcodes_dir}/{fil}"), reverse=True) + ] + + logging.debug("Collecting standard instructions") for file_name in file_names: - logging.debug(f'Parsing File: {file_name} for standard instructions') + logging.debug(f"Parsing File: {file_name} for standard instructions") lines = read_lines(file_name) process_standard_instructions(lines, instr_dict, file_name) - logging.debug('Collecting pseudo instructions') + logging.debug("Collecting pseudo instructions") for file_name in file_names: - logging.debug(f'Parsing File: {file_name} for pseudo instructions') + logging.debug(f"Parsing File: {file_name} for pseudo instructions") lines = read_lines(file_name) - process_pseudo_instructions(lines, instr_dict, file_name, opcodes_dir, include_pseudo, include_pseudo_ops) - - logging.debug('Collecting imported instructions') + process_pseudo_instructions( + lines, + instr_dict, + file_name, + opcodes_dir, + include_pseudo, + include_pseudo_ops, + ) + + logging.debug("Collecting imported instructions") for file_name in file_names: - logging.debug(f'Parsing File: {file_name} for imported instructions') + logging.debug(f"Parsing File: {file_name} for imported instructions") lines = read_lines(file_name) process_imported_instructions(lines, instr_dict, file_name, opcodes_dir) - return instr_dict \ No newline at end of file + return instr_dict diff --git a/sverilog_utils.py b/sverilog_utils.py index 4a6ace10..1fe20680 100644 --- a/sverilog_utils.py +++ b/sverilog_utils.py @@ -1,30 +1,37 @@ -import re +import collections import glob +import logging import os import pprint -import logging -import collections -import yaml +import re import sys + +import yaml + # from shared_utils import overlaps, overlap_allowed, extension_overlap_allowed, instruction_overlap_allowed, process_enc_line, same_base_isa, add_segmented_vls_insn, expand_nf_field from shared_utils import * pp = pprint.PrettyPrinter(indent=2) -logging.basicConfig(level=logging.INFO, format='%(levelname)s:: %(message)s') +logging.basicConfig(level=logging.INFO, format="%(levelname)s:: %(message)s") + def make_sverilog(instr_dict): - names_str = '' + names_str = "" for i in instr_dict: names_str += f" localparam [31:0] {i.upper().replace('.','_'):<18s} = 32'b{instr_dict[i]['encoding'].replace('-','?')};\n" - names_str += ' /* CSR Addresses */\n' - for num, name in csrs+csrs32: - names_str += f" localparam logic [11:0] CSR_{name.upper()} = 12'h{hex(num)[2:]};\n" + names_str += " /* CSR Addresses */\n" + for num, name in csrs + csrs32: + names_str += ( + f" localparam logic [11:0] CSR_{name.upper()} = 12'h{hex(num)[2:]};\n" + ) - sverilog_file = open('inst.sverilog','w') - sverilog_file.write(f''' + sverilog_file = open("inst.sverilog", "w") + sverilog_file.write( + f""" /* Automatically generated by parse_opcodes */ package riscv_instr; {names_str} endpackage -''') - sverilog_file.close() \ No newline at end of file +""" + ) + sverilog_file.close()