X-Git-Url: https://git.libre-soc.org/?a=blobdiff_plain;f=src%2Fsoc%2Fdecoder%2Fpower_decoder.py;h=2b01d4d437e6045983e832c987a30d8dc1329094;hb=d351455aa31a6b9383a3eee69d68eff3d40fe5a6;hp=e4a5b9d02aea7e101d786d2823f9faa17867abdb;hpb=c9d9bd236742424b10e4300924b5eb2e2519be11;p=soc.git diff --git a/src/soc/decoder/power_decoder.py b/src/soc/decoder/power_decoder.py index e4a5b9d0..2b01d4d4 100644 --- a/src/soc/decoder/power_decoder.py +++ b/src/soc/decoder/power_decoder.py @@ -1,6 +1,6 @@ """Cascading Power ISA Decoder -License: LGPLv3 +License: LGPLv3+ # Copyright (C) 2020 Luke Kenneth Casson Leighton # Copyright (C) 2020 Michael Nolan @@ -86,22 +86,25 @@ Top Level: """ +import gc from collections import namedtuple from nmigen import Module, Elaboratable, Signal, Cat, Mux from nmigen.cli import rtlil from soc.decoder.power_enums import (Function, Form, MicrOp, In1Sel, In2Sel, In3Sel, OutSel, - RC, LdstLen, LDSTMode, CryIn, get_csv, + SVEtype, SVPtype, # Simple-V + RC, LdstLen, LDSTMode, CryIn, single_bit_flags, CRInSel, CROutSel, get_signal_name, default_values, insns, asmidx) from soc.decoder.power_fields import DecodeFields from soc.decoder.power_fieldsn import SigDecode, SignalBitRange - +from soc.decoder.power_svp64 import SVP64RM # key data structure in which the POWER decoder is specified, # in a hierarchical fashion -Subdecoder = namedtuple("Subdecoder", +Subdecoder = namedtuple( # fix autoformatter + "Subdecoder", ["pattern", # the major pattern to search for (e.g. major opcode) "opcodes", # a dictionary of minor patterns to find "opint", # true => the pattern must not be in "10----11" format @@ -116,6 +119,8 @@ power_op_types = {'function_unit': Function, 'internal_op': MicrOp, 'form': Form, 'asmcode': 8, + 'SV_Etype': SVEtype, + 'SV_Ptype': SVPtype, 'in1_sel': In1Sel, 'in2_sel': In2Sel, 'in3_sel': In3Sel, @@ -129,19 +134,20 @@ power_op_types = {'function_unit': Function, } power_op_csvmap = {'function_unit': 'unit', - 'form' : 'form', - 'internal_op' : 'internal op', - 'in1_sel' : 'in1', - 'in2_sel' : 'in2', - 'in3_sel' : 'in3', - 'out_sel' : 'out', - 'cr_in' : 'CR in', - 'cr_out' : 'CR out', - 'ldst_len' : 'ldst len', - 'upd' : 'upd', - 'rc_sel' : 'rc', - 'cry_in' : 'cry in', - } + 'form': 'form', + 'internal_op': 'internal op', + 'in1_sel': 'in1', + 'in2_sel': 'in2', + 'in3_sel': 'in3', + 'out_sel': 'out', + 'cr_in': 'CR in', + 'cr_out': 'CR out', + 'ldst_len': 'ldst len', + 'upd': 'upd', + 'rc_sel': 'rc', + 'cry_in': 'cry in', + } + def get_pname(field, pname): if pname is None: @@ -150,15 +156,9 @@ def get_pname(field, pname): class PowerOp: - """PowerOp: spec for execution. op type (ADD etc.) reg specs etc. - - this is an internal data structure, set up by reading CSV files - (which uses _eq to initialise each instance, not eq) - - the "public" API (as far as actual usage as a useful decoder is concerned) - is Decode2ToExecute1Type - - the "subset" allows for only certain columns to be decoded + """PowerOp - a dynamic class that stores (subsets of) CSV rows of data + about a PowerISA instruction. this is a "micro-code" expanded format + which generates an awful lot of wires, hence the subsetting """ def __init__(self, incl_asm=True, name=None, subset=None): @@ -180,8 +180,8 @@ class PowerOp: debug_report.add(field) fname = get_pname(field, name) setattr(self, field, Signal(reset_less=True, name=fname)) - print ("PowerOp debug", name, debug_report) - print (" fields", fields) + print("PowerOp debug", name, debug_report) + print(" fields", fields) def _eq(self, row=None): if row is None: @@ -210,7 +210,7 @@ class PowerOp: continue csvname = power_op_csvmap[field] val = row[csvname] - if csvname == 'upd' and isinstance(val, int): # LDSTMode different + if csvname == 'upd' and isinstance(val, int): # LDSTMode different val = ptype(val) else: val = ptype[val] @@ -258,9 +258,27 @@ class PowerOp: class PowerDecoder(Elaboratable): """PowerDecoder - decodes an incoming opcode into the type of operation + + this is a recursive algorithm, creating Switch statements that can + have further match-and-decode on other parts of the opcode field before + finally landing at a "this CSV entry details gets returned" thing. + + the complicating factor is the row and col subsetting. column subsetting + dynamically chooses only the CSV columns requested, whilst row subsetting + allows a function to be called on the row to determine if the Case + statement is to be generated for that row. this not only generates + completely different Decoders, it also means that some sub-decoders + will turn up blank (empty switch statements). if that happens we do + not want the parent to include a Mux for an entirely blank switch statement + so we have to store the switch/case statements in a tree, and + post-analyse it. + + the reason for the tree is because elaborate can only be called *after* + the constructor is called. all quite messy. """ def __init__(self, width, dec, name=None, col_subset=None, row_subset=None): + self.actually_does_something = False self.pname = name self.col_subset = col_subset self.row_subsetfn = row_subset @@ -295,75 +313,121 @@ class PowerDecoder(Elaboratable): divided[key].append(r) return divided - def elaborate(self, platform): - m = Module() - comb = m.d.comb - - # note: default opcode is "illegal" as this is a combinatorial block - # this only works because OP_ILLEGAL=0 and the default (unset) is 0 + def tree_analyse(self): + self.decs = decs = [] + self.submodules = submodules = {} + self.eqs = eqs = [] # go through the list of CSV decoders first for d in self.dec: + cases = [] opcode_switch = Signal(d.bitsel[1] - d.bitsel[0], reset_less=True) - comb += opcode_switch.eq(self.opcode_in[d.bitsel[0]:d.bitsel[1]]) + eq = [] + case_does_something = False + eq.append(opcode_switch.eq( + self.opcode_in[d.bitsel[0]:d.bitsel[1]])) if d.suffix: opcodes = self.divide_opcodes(d) opc_in = Signal(d.suffix, reset_less=True) - comb += opc_in.eq(opcode_switch[:d.suffix]) + eq.append(opc_in.eq(opcode_switch[:d.suffix])) # begin the dynamic Switch statement here - with m.Switch(opc_in): - for key, row in opcodes.items(): - bitsel = (d.suffix+d.bitsel[0], d.bitsel[1]) - sd = Subdecoder(pattern=None, opcodes=row, - bitsel=bitsel, suffix=None, - opint=False, subdecoders=[]) - subdecoder = PowerDecoder(width=32, dec=sd, - name=self.pname, - col_subset=self.col_subset, - row_subset=self.row_subsetfn) - mname = get_pname("dec_sub%d" % key, self.pname) - setattr(m.submodules, mname, subdecoder) - comb += subdecoder.opcode_in.eq(self.opcode_in) - # XXX hmmm... - #if self.row_subsetfn: - # if not self.row_subsetfn(key, row): - # continue - # add in the dynamic Case statement here - with m.Case(key): - comb += self.op.eq(subdecoder.op) + switch_case = {} + cases.append([opc_in, switch_case]) + sub_eqs = [] + for key, row in opcodes.items(): + bitsel = (d.suffix+d.bitsel[0], d.bitsel[1]) + sd = Subdecoder(pattern=None, opcodes=row, + bitsel=bitsel, suffix=None, + opint=False, subdecoders=[]) + mname = get_pname("dec_sub%d" % key, self.pname) + subdecoder = PowerDecoder(width=32, dec=sd, + name=mname, + col_subset=self.col_subset, + row_subset=self.row_subsetfn) + if not subdecoder.tree_analyse(): + del subdecoder + continue + submodules[mname] = subdecoder + sub_eqs.append(subdecoder.opcode_in.eq(self.opcode_in)) + # add in the dynamic Case statement here + switch_case[key] = self.op.eq(subdecoder.op) + self.actually_does_something = True + case_does_something = True + if case_does_something: + eq += sub_eqs else: # TODO: arguments, here (all of them) need to be a list. # a for-loop around the *list* of decoder args. - with m.Switch(opcode_switch): - self.handle_subdecoders(m, d) - for row in d.opcodes: - opcode = row['opcode'] - if d.opint and '-' not in opcode: - opcode = int(opcode, 0) - if not row['unit']: + switch_case = {} + cases.append([opcode_switch, switch_case]) + seqs = self.handle_subdecoders(switch_case, submodules, d) + if seqs: + case_does_something = True + eq += seqs + for row in d.opcodes: + opcode = row['opcode'] + if d.opint and '-' not in opcode: + opcode = int(opcode, 0) + if not row['unit']: + continue + if self.row_subsetfn: + if not self.row_subsetfn(opcode, row): continue - if self.row_subsetfn: - if not self.row_subsetfn(opcode, row): - continue - # add in the dynamic Case statement here - with m.Case(opcode): - comb += self.op._eq(row) - return m + # add in the dynamic Case statement here + switch_case[opcode] = self.op._eq(row) + self.actually_does_something = True + case_does_something = True + + if cases: + decs.append(cases) + if case_does_something: + eqs += eq + print("submodule eqs", self.pname, eq) + + print("submodules", self.pname, submodules) - def handle_subdecoders(self, m, d): + gc.collect() + return self.actually_does_something + + def handle_subdecoders(self, switch_case, submodules, d): + eqs = [] for dec in d.subdecoders: - subdecoder = PowerDecoder(self.width, dec, - name=self.pname, - col_subset=self.col_subset, - row_subset=self.row_subsetfn) if isinstance(dec, list): # XXX HACK: take first pattern dec = dec[0] + print("subdec", dec.pattern, self.pname) mname = get_pname("dec%d" % dec.pattern, self.pname) + subdecoder = PowerDecoder(self.width, dec, + name=mname, + col_subset=self.col_subset, + row_subset=self.row_subsetfn) + if not subdecoder.tree_analyse(): # doesn't do anything + del subdecoder + continue # skip + submodules[mname] = subdecoder + eqs.append(subdecoder.opcode_in.eq(self.opcode_in)) + switch_case[dec.pattern] = self.op.eq(subdecoder.op) + self.actually_does_something = True + + return eqs + + def elaborate(self, platform): + print("decoder elaborate", self.pname, self.submodules) + m = Module() + comb = m.d.comb + + comb += self.eqs + + for mname, subdecoder in self.submodules.items(): setattr(m.submodules, mname, subdecoder) - m.d.comb += subdecoder.opcode_in.eq(self.opcode_in) - with m.Case(dec.pattern): - m.d.comb += self.op.eq(subdecoder.op) + + for switch_case in self.decs: + for (switch, cases) in switch_case: + with m.Switch(switch): + for key, eqs in cases.items(): + with m.Case(key): + comb += eqs + return m def ports(self): return [self.opcode_in] + self.op.ports() @@ -390,7 +454,7 @@ class TopPowerDecoder(PowerDecoder): setattr(self, fname, sig) # create signals for all field forms - self.form_names = forms = self.fields.instrs.keys() + forms = self.form_names self.sigforms = {} for form in forms: fields = self.fields.instrs[form] @@ -405,6 +469,12 @@ class TopPowerDecoder(PowerDecoder): setattr(self, "Form%s" % form, instr) self.sigforms[form] = instr + self.tree_analyse() + + @property + def form_names(self): + return self.fields.instrs.keys() + def elaborate(self, platform): m = PowerDecoder.elaborate(self, platform) comb = m.d.comb @@ -446,6 +516,11 @@ def create_pdecode(name=None, col_subset=None, row_subset=None): subsetting of the PowerOp decoding is possible by setting col_subset """ + # some alteration to the CSV files is required for SV so we use + # a class to do it + isa = SVP64RM() + get_csv = isa.get_svp64_csv + # minor 19 has extra patterns m19 = [] m19.append(Subdecoder(pattern=19, opcodes=get_csv("minor_19.csv"), @@ -478,30 +553,31 @@ def create_pdecode(name=None, col_subset=None, row_subset=None): bitsel=(0, 32), suffix=None, subdecoders=[])) return TopPowerDecoder(32, dec, name=name, col_subset=col_subset, - row_subset=row_subset) + row_subset=row_subset) if __name__ == '__main__': - # row subset + if True: + # row subset - def rowsubsetfn(opcode, row): - print ("row_subset", opcode, row) - return row['unit'] == 'ALU' + def rowsubsetfn(opcode, row): + print("row_subset", opcode, row) + return row['unit'] == 'ALU' - pdecode = create_pdecode(name="rowsub", - col_subset={'function_unit', 'in1_sel'}, - row_subset=rowsubsetfn) - vl = rtlil.convert(pdecode, ports=pdecode.ports()) - with open("row_subset_decoder.il", "w") as f: - f.write(vl) + pdecode = create_pdecode(name="rowsub", + col_subset={'function_unit', 'in1_sel'}, + row_subset=rowsubsetfn) + vl = rtlil.convert(pdecode, ports=pdecode.ports()) + with open("row_subset_decoder.il", "w") as f: + f.write(vl) - # col subset + # col subset - pdecode = create_pdecode(name="fusubset", col_subset={'function_unit'}) - vl = rtlil.convert(pdecode, ports=pdecode.ports()) - with open("col_subset_decoder.il", "w") as f: - f.write(vl) + pdecode = create_pdecode(name="fusubset", col_subset={'function_unit'}) + vl = rtlil.convert(pdecode, ports=pdecode.ports()) + with open("col_subset_decoder.il", "w") as f: + f.write(vl) # full decoder @@ -509,4 +585,3 @@ if __name__ == '__main__': vl = rtlil.convert(pdecode, ports=pdecode.ports()) with open("decoder.il", "w") as f: f.write(vl) -