From b2c7d38af7425ddff5af1261c29d2bee7a0d1f2d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torbj=C3=B8rn=20Viem=20Ness?= Date: Fri, 10 Feb 2023 11:38:23 +0100 Subject: [PATCH 1/3] Improved the latex table generation. Now automatically generates the list of instruction types based on the encodings used in the list of instructions to be printed, instead of requiring the headers to be passed alongside the data. Also inserts new pages whenever the table grows to such a length that entries would spill over to the next one. --- parse.py | 183 +++++++++++++++++++++++++++++++++++++------------------ 1 file changed, 123 insertions(+), 60 deletions(-) diff --git a/parse.py b/parse.py index 9714e997..170f1a15 100755 --- a/parse.py +++ b/parse.py @@ -536,6 +536,8 @@ def make_ext_latex_table(type_list, dataset, latex_file, ilen, caption): multicolumn entry in the table. ''' + max_entries_per_page = 42 # Arbitrary limit chosen at random, promise! + entries_printed = 0 # Keep track of the instructions printed (headers count as 2) to not overflow a page column_size = "".join(['p{0.002in}']*(ilen+1)) type_entries = ''' @@ -574,58 +576,18 @@ def make_ext_latex_table(type_list, dataset, latex_file, ilen, caption): \\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 = [] - - # 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 in range(len(fields)): - (msb, lsb, name) = fields[r] - 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 = '' + content_list = [] + auto_types = {key: False for key, _ in latex_inst_type.items()} + # For simplicity's sake, assume R-type always present for 32-bit + auto_types['R-type'] = (ilen == 32) for (ext_list, title, filter_list, include_pseudo) in dataset: instr_dict = {} + # Support multi-page tables + entries_list = [] + if title != '': + entries_printed += 2 # Each time a new table starts the heading eats 2 lines # for all extensions list in ext_list, create a dictionary of # instructions associated with those extensions. @@ -654,6 +616,22 @@ def make_ext_latex_table(type_list, dataset, latex_file, ilen, caption): (msb,lsb) = arg_lut[f] name = f.replace('_','.') if f not in latex_mapping else latex_mapping[f] fields.append((msb, lsb, name)) + # if 'funct7' == f: # NOTE: this doesn't really get detected but in most cases we can assume R-type is present by default + # auto_types['R-type'] = True + if 'imm20' == f: + auto_types['U-type'] = True + elif 'imm12hi' == f: + auto_types['S-type'] = True + elif 'bimm12hi' == f: + auto_types['B-type'] = True + elif 'rs3' == f: + auto_types['R4-type'] = True + elif 'jimm20' == f: + auto_types['J-type'] = True + # elif 'shamt' in arguments[n] or 'shamtw' in arguments[n] or 'imm12' in arguments[n] or n == 'ecall' or n == 'ebreak' or n[:3] == 'csr': + elif 'imm12' == f: + auto_types['I-type'] = True + msb = ilen -1 y = '' @@ -691,25 +669,110 @@ def make_ext_latex_table(type_list, dataset, latex_file, ilen, caption): entry += f'\\multicolumn{{{msb - lsb + 1}}}{{c|}}{{{name}}} &\n' entry += f'\\cline{{2-{ilen+1}}}\n&\n\n' instr_entries += entry + entries_printed += 1 + + if entries_printed >= max_entries_per_page: + # Once we've filled a page, break and start a new one + entries_list.append(str(instr_entries)) # Make sure it's a deep copy and not just a link + instr_entries = '' + entries_printed = 0 + + # Flush out the rest, if any + if entries_printed > 0: + entries_list.append(str(instr_entries)) # Make sure it's a deep copy and not just a link + instr_entries = '' # 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''' + content_list.append((title, entries_list)) -\\multicolumn{{{ilen}}}{{c}}{{}} & \\\\ -\\multicolumn{{{ilen}}}{{c}}{{\\bf {title} }} & \\\\ -\\cline{{2-{ilen+1}}} + # 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 auto_types[key]} - & -{instr_entries} -''' - else: - content += f''' -{instr_entries} -''' + # iterate over each instruction type and create a table entry + for t in type_dict: + fields = [] + # 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 in range(len(fields)): + (msb, lsb, name) = fields[r] + 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 + # If we for some reason don't have any type entries, skip printing the single empty row altogether + if len(type_dict) == 0: + type_entries = '' + + # Now that we have everything, run through and generate the proper output! + for (title, entries_list) in content_list: + for i, instr_entries in enumerate(entries_list): + if i > 0: # Second time around, add footer and header because it's a new page + content += f''' + +\\end{{tabular}} +\\end{{center}} +\\end{{small}} +\\end{{table}} +\\newpage + +\\begin{{table}}[p] +\\begin{{small}} +\\begin{{center}} + \\begin{{tabular}} {{{column_size}l}} + {" ".join(['&']*ilen)} \\\\ + + & +{type_entries} +''' + if title != '': + content += f''' + + \\multicolumn{{{ilen}}}{{c}}{{}} & \\\\ + \\multicolumn{{{ilen}}}{{c}}{{\\bf {title + ", cont'd" if i > 0 else title} }} & \\\\ + \\cline{{2-{ilen+1}}} + + & + {instr_entries} + ''' + else: + content += f''' + {instr_entries} + ''' + header = f''' \\newpage From b213663e86cf3bd451c7f0777679605cfe9fda77 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torbj=C3=B8rn=20Viem=20Ness?= Date: Fri, 10 Feb 2023 11:46:59 +0100 Subject: [PATCH 2/3] Added Zc* table as example of 16 bit encodings --- parse.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/parse.py b/parse.py index 170f1a15..8812118e 100755 --- a/parse.py +++ b/parse.py @@ -475,6 +475,13 @@ def make_latex_table(): 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) + # Zc* extensions + # caption = '\\caption{Zc* extensions for code size reduction on RISC-V}' + # dataset_list = [(['_zcb', '64_zcb'], 'Zcb (basic compressed ops) extension', [], True)] + # dataset_list.append((['_zcmp'], 'Zcmp (push/pop) extension', [], True)) + # dataset_list.append((['_zcmt'], 'Zcmt (table jump) extension', [], True)) + # make_ext_latex_table(type_list, dataset_list, latex_file, 16, caption) + ## The following is demo to show that Compressed instructions can also be # dumped in the same manner as above @@ -686,7 +693,7 @@ def make_ext_latex_table(type_list, dataset, latex_file, ilen, caption): # with the title of that dataset as sub-heading (sort-of) content_list.append((title, entries_list)) - # depending on the type_list input we create a subset dictionary of + # depending on the autodetected encoding types 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 auto_types[key]} From a085f0220af1ad315fafe5089959e7404f770453 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torbj=C3=B8rn=20Viem=20Ness?= Date: Fri, 10 Feb 2023 15:01:16 +0100 Subject: [PATCH 3/3] Added bit positions above the 16-bit instruction tables --- parse.py | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/parse.py b/parse.py index 8812118e..7f04f924 100755 --- a/parse.py +++ b/parse.py @@ -582,7 +582,11 @@ def make_ext_latex_table(type_list, dataset, latex_file, ilen, caption): \\multicolumn{1}{c}{0} \\\\ \\cline{2-17}\n&\n\n ''' - + if ilen == 16: + # For compressed instructions, print the bit positions above each table to make it easier to read + c_instr_header = type_entries + else: + c_instr_header = '' # for each entry in the dataset create a table content = '' content_list = [] @@ -770,13 +774,12 @@ def make_ext_latex_table(type_list, dataset, latex_file, ilen, caption): \\multicolumn{{{ilen}}}{{c}}{{}} & \\\\ \\multicolumn{{{ilen}}}{{c}}{{\\bf {title + ", cont'd" if i > 0 else title} }} & \\\\ - \\cline{{2-{ilen+1}}} - - & - {instr_entries} - ''' - else: - content += f''' +''' + if ilen == 32: + content += f' \\cline{{2-{ilen+1}}} &' + else: + content += '&\n' + c_instr_header + content += f''' {instr_entries} '''