X-Git-Url: https://git.libre-soc.org/?a=blobdiff_plain;f=src%2Fopenpower%2Fdecoder%2Fpower_decoder.py;h=4bf1e351afa865cd8ae2121eb666e4ee5794c866;hb=517f79ed486f44c8cc79485e6cc39a3ff553b947;hp=d53c06762a7ebce59983c4d0972dd715a90346a3;hpb=bb824753cdc455444e0b7accc47552eb41d3513d;p=openpower-isa.git diff --git a/src/openpower/decoder/power_decoder.py b/src/openpower/decoder/power_decoder.py index d53c0676..4bf1e351 100644 --- a/src/openpower/decoder/power_decoder.py +++ b/src/openpower/decoder/power_decoder.py @@ -1,3 +1,4 @@ +# SPDX-License-Identifier: LGPL-3-or-later """Cascading Power ISA Decoder License: LGPLv3+ @@ -87,16 +88,18 @@ Top Level: """ import gc -from collections import namedtuple -from nmigen import Module, Elaboratable, Signal, Cat, Mux -from nmigen.cli import rtlil +from collections import namedtuple, OrderedDict +from nmigen import Module, Elaboratable, Signal, Cat, Mux, Const +from nmigen.cli import rtlil, verilog from openpower.decoder.power_enums import (Function, Form, MicrOp, - In1Sel, In2Sel, In3Sel, OutSel, - SVEXTRA, SVEtype, SVPtype, # Simple-V - RC, LdstLen, LDSTMode, CryIn, - single_bit_flags, CRInSel, - CROutSel, get_signal_name, - default_values, insns, asmidx) + In1Sel, In2Sel, In3Sel, OutSel, + SVEXTRA, SVMode, # Simple-V + SVEType, SVPType, # Simple-V + RCOE, LdstLen, LDSTMode, CryIn, + single_bit_flags, CRInSel, + CROutSel, get_signal_name, + default_values, insns, asmidx, + asmlen) from openpower.decoder.power_fields import DecodeFields from openpower.decoder.power_fieldsn import SigDecode, SignalBitRange from openpower.decoder.power_svp64 import SVP64RM @@ -111,7 +114,7 @@ Subdecoder = namedtuple( # fix autoformatter "opcodes", # a dictionary of minor patterns to find "opint", # true => the pattern must not be in "10----11" format # the bits (as a range) against which "pattern" matches - "bitsel", + "bitsel", # should be in MSB0 order but isn't! it's LSB0. um. "suffix", # shift the opcode down before decoding "subdecoders" # list of further subdecoders for *additional* matches, # *ONLY* after "pattern" has *ALSO* been matched against. @@ -120,9 +123,10 @@ Subdecoder = namedtuple( # fix autoformatter power_op_types = {'function_unit': Function, 'internal_op': MicrOp, 'form': Form, - 'asmcode': 8, - 'SV_Etype': SVEtype, - 'SV_Ptype': SVPtype, + 'asmcode': asmlen, + 'SV_Etype': SVEType, + 'SV_Ptype': SVPType, + 'SV_mode': SVMode, 'in1_sel': In1Sel, 'in2_sel': In2Sel, 'in3_sel': In3Sel, @@ -138,7 +142,7 @@ power_op_types = {'function_unit': Function, 'sv_cr_out': SVEXTRA, 'ldst_len': LdstLen, 'upd': LDSTMode, - 'rc_sel': RC, + 'rc_sel': RCOE, 'cry_in': CryIn } @@ -158,10 +162,12 @@ power_op_csvmap = {'function_unit': 'unit', 'sv_cr_out': 'sv_cr_out', 'SV_Etype': 'SV_Etype', 'SV_Ptype': 'SV_Ptype', + 'SV_mode': 'SV_mode', 'cr_in': 'CR in', 'cr_out': 'CR out', 'ldst_len': 'ldst len', 'upd': 'upd', + 'rsrv': 'rsrv', # atomic operation 'rc_sel': 'rc', 'cry_in': 'cry in', } @@ -179,8 +185,13 @@ class PowerOp: which generates an awful lot of wires, hence the subsetting """ - def __init__(self, incl_asm=True, name=None, subset=None): + def __init__(self, incl_asm=True, name=None, subset=None, fields=None): + self.name = name self.subset = subset + if fields is not None: + for k, v in fields.items(): + setattr(self, k, v) + return debug_report = set() fields = set() for field, ptype in power_op_types.items(): @@ -198,9 +209,21 @@ class PowerOp: debug_report.add(field) fname = get_pname(field, name) setattr(self, field, Signal(reset_less=True, name=fname)) + self._fields = fields # comment out, bit too high debug level - #print("PowerOp debug", name, debug_report) - #print(" fields", fields) + #log("PowerOp debug", name, debug_report) + #log(" fields", fields) + + @staticmethod + def like(other): + """PowerOp.like: creates a duplicate of a given PowerOp instance + """ + fields = {} + for fname in other._fields: + sig = getattr(other, fname, None) + if sig is not None: + fields[fname] = sig.__class__.like(sig) + return PowerOp(subset=other.subset, fields=fields) def _eq(self, row=None): if row is None: @@ -228,7 +251,7 @@ class PowerOp: if field not in power_op_csvmap: continue csvname = power_op_csvmap[field] - #print(field, ptype, csvname, row) + log("_eq", field, ptype, csvname, row) val = row[csvname] if csvname == 'upd' and isinstance(val, int): # LDSTMode different val = ptype(val) @@ -241,8 +264,8 @@ class PowerOp: # process the comment field, strip out "equals" for FP if "=" in asmcode: asmcode = asmcode.split("=")[-1] - log ("asmcode stripping =", asmcode, - asmcode in asmidx, hasattr(self, "asmcode")) + log("asmcode stripping =", asmcode, + asmcode in asmidx, hasattr(self, "asmcode")) if hasattr(self, "asmcode") and asmcode in asmidx: res.append(self.asmcode.eq(asmidx[asmcode])) for bit in single_bit_flags: @@ -302,9 +325,16 @@ class PowerDecoder(Elaboratable): the constructor is called. all quite messy. """ - def __init__(self, width, dec, name=None, col_subset=None, row_subset=None): + def __init__(self, width, dec, name=None, col_subset=None, + row_subset=None, conditions=None): + if conditions is None: + # XXX conditions = {} + conditions = { + 'SVP64FFT': Const(0, 1), + } self.actually_does_something = False self.pname = name + self.conditions = conditions self.col_subset = col_subset self.row_subsetfn = row_subset if not isinstance(dec, list): @@ -316,15 +346,50 @@ class PowerDecoder(Elaboratable): for d in dec: if d.suffix is not None and d.suffix >= width: d.suffix = None + self.width = width + # create some case statement condition patterns for matching + # a single condition. "1----" for the first condition, + # "-1----" for the 2nd etc. + # also create a matching ordered list of conditions, for the switch, + # which will Cat() them together + self.ccases = {} + self.ckeys = list(conditions.keys()) + self.ckeys.sort() + + def find_conditions(self, opcodes): + # look for conditions, create dictionary entries for them + # sorted by opcode + rows = OrderedDict() # start as a dictionary, get as list (after) + for row in opcodes: + condition = row['CONDITIONS'] + opcode = row['opcode'] + if condition: + # check it's expected + assert (condition in self.conditions or + (condition[0] == '~' and + condition[1:] in self.conditions)), \ + "condition %s not in %s" % (condition, str(conditions)) + if opcode not in rows: + rows[opcode] = {} + rows[opcode][condition] = row + else: + # check it's unique + assert opcode not in rows, \ + "opcode %s already in rows for %s" % \ + (opcode, self.pname) + rows[opcode] = row + # after checking for conditions, get just the values (ordered) + return list(rows.values()) + def suffix_mask(self, d): return ((1 << d.suffix) - 1) def divide_opcodes(self, d): divided = {} mask = self.suffix_mask(d) - #print("mask", hex(mask)) + #log("mask", hex(mask)) for row in d.opcodes: opcode = row['opcode'] if d.opint and '-' not in opcode: @@ -350,10 +415,11 @@ class PowerDecoder(Elaboratable): reset_less=True) eq = [] case_does_something = False - eq.append(opcode_switch.eq( - self.opcode_in[d.bitsel[0]:d.bitsel[1]])) + look_for = self.opcode_in[d.bitsel[0]:d.bitsel[1]] + eq.append(opcode_switch.eq(look_for)) if d.suffix: opcodes = self.divide_opcodes(d) + # TODO opcodes = self.find_conditions(opcodes) opc_in = Signal(d.suffix, reset_less=True) eq.append(opc_in.eq(opcode_switch[:d.suffix])) # begin the dynamic Switch statement here @@ -369,7 +435,8 @@ class PowerDecoder(Elaboratable): subdecoder = PowerDecoder(width=32, dec=sd, name=mname, col_subset=self.col_subset, - row_subset=self.row_subsetfn) + row_subset=self.row_subsetfn, + conditions=self.conditions) if not subdecoder.tree_analyse(): del subdecoder continue @@ -390,17 +457,34 @@ class PowerDecoder(Elaboratable): if seqs: case_does_something = True eq += seqs - for row in d.opcodes: - opcode = row['opcode'] + opcodes = self.find_conditions(d.opcodes) + for row in opcodes: + # urrr this is an awful hack. if "conditions" are active + # get the FIRST item (will be the same opcode), and it + # had BETTER have the same unit and also pass other + # row subset conditions. + if 'opcode' not in row: # must be a "CONDITIONS" dict... + is_conditions = True + _row = row[list(row.keys())[0]] + else: + is_conditions = False + _row = row + opcode = _row['opcode'] if d.opint and '-' not in opcode: opcode = int(opcode, 0) - if not row['unit']: + if not _row['unit']: continue if self.row_subsetfn: - if not self.row_subsetfn(opcode, row): + if not self.row_subsetfn(opcode, _row): continue # add in the dynamic Case statement here - switch_case[opcode] = self.op._eq(row) + if is_conditions: + switch_case[opcode] = {} + for k, crow in row.items(): + # log("ordered", k, crow) + switch_case[opcode][k] = self.op._eq(crow) + else: + switch_case[opcode] = self.op._eq(row) self.actually_does_something = True case_does_something = True @@ -408,11 +492,12 @@ class PowerDecoder(Elaboratable): decs.append(cases) if case_does_something: eqs += eq - #print("submodule eqs", self.pname, eq) + #log("submodule eqs", self.pname, eq) - #print("submodules", self.pname, submodules) + #log("submodules", self.pname, submodules) - gc.collect() + # GC collection is really slow and shouldn't be needed + # gc.collect() return self.actually_does_something def handle_subdecoders(self, switch_case, submodules, d): @@ -421,7 +506,7 @@ class PowerDecoder(Elaboratable): if not isinstance(dlist, list): # XXX HACK: take first pattern dlist = [dlist] for dec in dlist: - #print("subdec", dec.pattern, self.pname) + #log("subdec", dec.pattern, self.pname) mname = get_pname("dec%d" % dec.pattern, self.pname) if mname in submodules: # sigh, HACK... @@ -430,10 +515,11 @@ class PowerDecoder(Elaboratable): subdecoder = PowerDecoder(self.width, dec, name=mname, col_subset=self.col_subset, - row_subset=self.row_subsetfn) - log ("subdecoder", mname, subdecoder) + row_subset=self.row_subsetfn, + conditions=self.conditions) + #log("subdecoder", mname, subdecoder) if not subdecoder.tree_analyse(): # doesn't do anything - log ("analysed, DELETING", mname) + #log("analysed, DELETING", mname) del subdecoder continue # skip submodules[mname] = subdecoder @@ -444,7 +530,7 @@ class PowerDecoder(Elaboratable): return eqs def elaborate(self, platform): - #print("decoder elaborate", self.pname, self.submodules) + #log("decoder elaborate", self.pname, self.submodules) m = Module() comb = m.d.comb @@ -458,9 +544,30 @@ class PowerDecoder(Elaboratable): with m.Switch(switch): for key, eqs in cases.items(): with m.Case(key): - comb += eqs + # "conditions" are a further switch statement + if isinstance(eqs, dict): + self.condition_switch(m, eqs) + else: + comb += eqs return m + def condition_switch(self, m, cases): + """against the global list of "conditions", having matched against + bits of the opcode, we FINALLY now have to match against some + additional "conditions". this is because there can be **MULTIPLE** + entries for a given opcode match. here we discern them. + """ + comb = m.d.comb + cswitch = [] + ccases = [] + for casekey, eqs in cases.items(): + if casekey.startswith('~'): + with m.If(~self.conditions[casekey[1:]]): + comb += eqs + else: + with m.If(self.conditions[casekey]): + comb += eqs + def ports(self): return [self.opcode_in] + self.op.ports() @@ -473,8 +580,10 @@ class TopPowerDecoder(PowerDecoder): (reverses byte order). See V3.0B p44 1.11.2 """ - def __init__(self, width, dec, name=None, col_subset=None, row_subset=None): - PowerDecoder.__init__(self, width, dec, name, col_subset, row_subset) + def __init__(self, width, dec, name=None, col_subset=None, + row_subset=None, conditions=None): + PowerDecoder.__init__(self, width, dec, name, + col_subset, row_subset, conditions) self.fields = df = DecodeFields(SignalBitRange, [self.opcode_in]) self.fields.create_specs() self.raw_opcode_in = Signal.like(self.opcode_in, reset_less=True) @@ -537,19 +646,72 @@ class TopPowerDecoder(PowerDecoder): return m def ports(self): - return [self.raw_opcode_in, self.bigendian] + PowerDecoder.ports(self) + res = [self.raw_opcode_in, self.bigendian] + PowerDecoder.ports(self) + for condition in self.conditions.values(): + res.append(condition) + return res + + +############################################################# +# PRIMARY FUNCTION SPECIFYING ALTERNATIVE SVP64 POWER DECODER + +def create_pdecode_svp64_ldst(name=None, col_subset=None, row_subset=None, + include_fp=False): + """create_pdecode - creates a cascading hierarchical POWER ISA decoder + + subsetting of the PowerOp decoding is possible by setting col_subset + """ + #log("create_pdecode_svp64_ldst", name, col_subset, row_subset, include_fp) + + # 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 opcodes. + pminor = [ + Subdecoder(pattern=58, opcodes=get_csv("svldst_minor_58.csv"), + opint=True, bitsel=(0, 2), suffix=None, subdecoders=[]), + # nope - needs 4-in regs + # Subdecoder(pattern=62, opcodes=get_csv("svldst_minor_62.csv"), + # opint=True, bitsel=(0, 2), suffix=None, subdecoders=[]), + ] + + # FP 63L/H decoders. TODO: move mffsfamily to separate subdecoder + if False and include_fp: + pminor.append( + Subdecoder(pattern=63, opcodes=get_csv("minor_63.csv"), + opint=False, bitsel=(1, 11), suffix=None, + subdecoders=[]), + ) + pminor.append( + Subdecoder(pattern=59, opcodes=get_csv("minor_59.csv"), + opint=False, bitsel=(1, 11), suffix=None, + subdecoders=[]), + ) + + # top level: extra merged with major + dec = [] + opcodes = get_csv("svldst_major.csv") + dec.append(Subdecoder(pattern=None, opint=True, opcodes=opcodes, + bitsel=(26, 32), suffix=None, subdecoders=pminor)) + + return TopPowerDecoder(32, dec, name=name, col_subset=col_subset, + row_subset=row_subset) #################################################### # PRIMARY FUNCTION SPECIFYING THE FULL POWER DECODER def create_pdecode(name=None, col_subset=None, row_subset=None, - include_fp=False): + include_fp=False, conditions=None): """create_pdecode - creates a cascading hierarchical POWER ISA decoder subsetting of the PowerOp decoding is possible by setting col_subset + + NOTE (sigh) the bitsel patterns are in LSB0 order, they should be MSB0 """ - log ("create_pdecode", name, col_subset, row_subset, include_fp) + #log("create_pdecode", name, col_subset, row_subset, include_fp) # some alteration to the CSV files is required for SV so we use # a class to do it @@ -559,11 +721,11 @@ def create_pdecode(name=None, col_subset=None, row_subset=None, # minor 19 has extra patterns m19 = [] m19.append(Subdecoder(pattern=19, opcodes=get_csv("minor_19.csv"), - opint=True, bitsel=(1, 11), suffix=None, + opint=False, bitsel=(1, 11), suffix=None, subdecoders=[])) # XXX problem with sub-decoders (can only handle one), # sort this another time - #m19.append(Subdecoder(pattern=19, opcodes=get_csv("minor_19_00000.csv"), + # m19.append(Subdecoder(pattern=19, opcodes=get_csv("minor_19_00000.csv"), # opint=True, bitsel=(1, 6), suffix=None, # subdecoders=[])) @@ -571,7 +733,7 @@ def create_pdecode(name=None, col_subset=None, row_subset=None, pminor = [ m19, Subdecoder(pattern=30, opcodes=get_csv("minor_30.csv"), - opint=True, bitsel=(1, 5), suffix=None, subdecoders=[]), + opint=False, bitsel=(1, 5), suffix=None, subdecoders=[]), Subdecoder(pattern=31, opcodes=get_csv("minor_31.csv"), opint=True, bitsel=(1, 11), suffix=0b00101, subdecoders=[]), Subdecoder(pattern=58, opcodes=get_csv("minor_58.csv"), @@ -579,21 +741,25 @@ def create_pdecode(name=None, col_subset=None, row_subset=None, Subdecoder(pattern=62, opcodes=get_csv("minor_62.csv"), opint=True, bitsel=(0, 2), suffix=None, subdecoders=[]), Subdecoder(pattern=22, opcodes=get_csv("minor_22.csv"), - opint=True, bitsel=(1, 5), suffix=None, subdecoders=[]), + opint=False, bitsel=(0, 11), suffix=None, subdecoders=[]), + Subdecoder(pattern=5, opcodes=get_csv("minor_5.csv"), + opint=True, bitsel=(0, 11), suffix=None, subdecoders=[]), + Subdecoder(pattern=4, opcodes=get_csv("minor_4.csv"), + opint=True, bitsel=(0, 6), suffix=None, subdecoders=[]), ] # FP 63L/H decoders. TODO: move mffsfamily to separate subdecoder if include_fp: pminor.append( Subdecoder(pattern=63, opcodes=get_csv("minor_63.csv"), - opint=False, bitsel=(1, 11), suffix=None, - subdecoders=[]), - ) + opint=False, bitsel=(1, 11), suffix=None, + subdecoders=[]), + ) pminor.append( Subdecoder(pattern=59, opcodes=get_csv("minor_59.csv"), - opint=False, bitsel=(1, 11), suffix=None, - subdecoders=[]), - ) + opint=False, bitsel=(1, 11), suffix=None, + subdecoders=[]), + ) # top level: extra merged with major dec = [] @@ -605,7 +771,32 @@ 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, + conditions=conditions) + +# test function from +# https://github.com/apertus-open-source-cinema/naps/blob/9ebbc0/naps/soc/cli.py#L17 + + +def fragment_repr(original): + from textwrap import indent + attrs_str = "\n" + for attr in ['ports', 'drivers', 'statements', 'attrs', + 'generated', 'flatten']: + attrs_str += f"{attr}={repr(getattr(original, attr))},\n" + + domains_str = "\n" + for name, domain in original.domains.items(): + # TODO: this is not really sound because domains could be non local + domains_str += f"{name}: {domain.name}\n" + attrs_str += f"domains={{{indent(domains_str, ' ')}}},\n" + + children_str = "\n" + for child, name in original.subfragments: + children_str += f"[{name}, {fragment_repr(child)}]\n" + attrs_str += f"children=[{indent(children_str, ' ')}],\n" + + return f"Fragment({indent(attrs_str, ' ')})" if __name__ == '__main__': @@ -614,28 +805,52 @@ if __name__ == '__main__': # row subset def rowsubsetfn(opcode, row): - log("row_subset", opcode, row) - return row['unit'] == 'FPU' + #log("row_subset", opcode, row) + return row['unit'] in ['LDST', 'FPU'] + conditions = { + 'SVP64FFT': Signal(name="svp64fft", reset_less=True), + } pdecode = create_pdecode(name="rowsub", col_subset={'opcode', 'function_unit', - 'form'}, + 'asmcode', + 'in2_sel', 'in3_sel'}, row_subset=rowsubsetfn, - include_fp=True) + include_fp=True, + conditions=conditions) vl = rtlil.convert(pdecode, ports=pdecode.ports()) with open("row_subset_decoder.il", "w") as f: f.write(vl) + vl = verilog.convert(pdecode, ports=pdecode.ports()) + with open("row_subset_decoder.v", "w") as f: + f.write(vl) + # col subset - pdecode = create_pdecode(name="fusubset", col_subset={'function_unit'}) + pdecode = create_pdecode(name="fusubset", col_subset={'function_unit'}, + conditions=conditions) vl = rtlil.convert(pdecode, ports=pdecode.ports()) with open("col_subset_decoder.il", "w") as f: f.write(vl) - # full decoder + from nmigen.hdl.ir import Fragment + elaborated = Fragment.get(pdecode, platform=None) + elaborated_repr = fragment_repr(elaborated) + #log(elaborated_repr) + + exit(0) + exit(0) + + # full decoder pdecode = create_pdecode(include_fp=True) vl = rtlil.convert(pdecode, ports=pdecode.ports()) with open("decoder.il", "w") as f: f.write(vl) + + # full SVP64 decoder + pdecode = create_pdecode_svp64_ldst(include_fp=True) + vl = rtlil.convert(pdecode, ports=pdecode.ports()) + with open("decoder_svp64.il", "w") as f: + f.write(vl)