add a quick logic test of astor tree-dump
[openpower-isa.git] / src / openpower / decoder / power_decoder.py
index 9bfac94356b024b3bb774b82a8f67aa1d466c370..250cb9b2878796acb5abc3730595e2dbed8d9d3b 100644 (file)
@@ -87,20 +87,23 @@ 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)
+                                     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
 
+from openpower.util import log
+
 # key data structure in which the POWER decoder is specified,
 # in a hierarchical fashion
 Subdecoder = namedtuple(  # fix autoformatter
@@ -118,7 +121,7 @@ Subdecoder = namedtuple(  # fix autoformatter
 power_op_types = {'function_unit': Function,
                   'internal_op': MicrOp,
                   'form': Form,
-                  'asmcode': 8,
+                  'asmcode': asmlen,
                   'SV_Etype': SVEtype,
                   'SV_Ptype': SVPtype,
                   'in1_sel': In1Sel,
@@ -177,8 +180,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():
@@ -196,8 +204,21 @@ 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)
+        self._fields = fields
+        # comment out, bit too high debug level
+        #print("PowerOp debug", name, debug_report)
+        #print("        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:
@@ -209,12 +230,12 @@ class PowerOp:
             if row['CR in'] == '1':
                 import pdb
                 pdb.set_trace()
-                print(row)
+                log(row)
             if row['CR out'] == '0':
                 import pdb
                 pdb.set_trace()
-                print(row)
-            print(row)
+                log(row)
+            log(row)
         ldst_mode = row['upd']
         if ldst_mode.isdigit():
             row['upd'] = int(ldst_mode)
@@ -225,7 +246,7 @@ class PowerOp:
             if field not in power_op_csvmap:
                 continue
             csvname = power_op_csvmap[field]
-            print(field, ptype, csvname, row)
+            # log(field, ptype, csvname, row)
             val = row[csvname]
             if csvname == 'upd' and isinstance(val, int):  # LDSTMode different
                 val = ptype(val)
@@ -233,8 +254,13 @@ class PowerOp:
                 val = ptype[val]
             res.append(getattr(self, field).eq(val))
         if False:
-            print(row.keys())
+            log(row.keys())
         asmcode = row['comment']
+        # 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"))
         if hasattr(self, "asmcode") and asmcode in asmidx:
             res.append(self.asmcode.eq(asmidx[asmcode]))
         for bit in single_bit_flags:
@@ -294,9 +320,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 = {'SVP64BREV': Const(0, 1),
+                          '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):
@@ -308,15 +341,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))
+        #print("mask", hex(mask))
         for row in d.opcodes:
             opcode = row['opcode']
             if d.opint and '-' not in opcode:
@@ -342,10 +410,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
@@ -361,7 +430,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
@@ -382,17 +452,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
 
@@ -400,36 +487,44 @@ class PowerDecoder(Elaboratable):
                 decs.append(cases)
             if case_does_something:
                 eqs += eq
-                print("submodule eqs", self.pname, eq)
+                #print("submodule eqs", self.pname, eq)
 
-        print("submodules", self.pname, submodules)
+        #print("submodules", self.pname, submodules)
 
         gc.collect()
         return self.actually_does_something
 
     def handle_subdecoders(self, switch_case, submodules, d):
         eqs = []
-        for dec in d.subdecoders:
-            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
+        for dlist in d.subdecoders:
+            if not isinstance(dlist, list):  # XXX HACK: take first pattern
+                dlist = [dlist]
+            for dec in dlist:
+                #print("subdec", dec.pattern, self.pname)
+                mname = get_pname("dec%d" % dec.pattern, self.pname)
+                if mname in submodules:
+                    # sigh, HACK...
+                    mname += "_1"
+                    assert mname not in submodules
+                subdecoder = PowerDecoder(self.width, dec,
+                                          name=mname,
+                                          col_subset=self.col_subset,
+                                          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)
+                    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)
+        #print("decoder elaborate", self.pname, self.submodules)
         m = Module()
         comb = m.d.comb
 
@@ -443,9 +538,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()
 
@@ -458,8 +574,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)
@@ -522,17 +640,70 @@ 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):
+def create_pdecode(name=None, col_subset=None, row_subset=None,
+                   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
     """
+    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
@@ -544,9 +715,11 @@ def create_pdecode(name=None, col_subset=None, row_subset=None):
     m19.append(Subdecoder(pattern=19, opcodes=get_csv("minor_19.csv"),
                           opint=True, bitsel=(1, 11), suffix=None,
                           subdecoders=[]))
-    m19.append(Subdecoder(pattern=19, opcodes=get_csv("minor_19_00000.csv"),
-                          opint=True, bitsel=(1, 6), 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"),
+    #                      opint=True, bitsel=(1, 6), suffix=None,
+    #                      subdecoders=[]))
 
     # minor opcodes.
     pminor = [
@@ -563,6 +736,19 @@ def create_pdecode(name=None, col_subset=None, row_subset=None):
                    opint=True, bitsel=(1, 5), 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=[]),
+            )
+        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("major.csv")
@@ -573,7 +759,30 @@ 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__':
@@ -582,26 +791,54 @@ if __name__ == '__main__':
         # row subset
 
         def rowsubsetfn(opcode, row):
-            print("row_subset", opcode, row)
-            return row['unit'] == 'ALU'
+            log("row_subset", opcode, row)
+            return row['unit'] in ['LDST', 'FPU']
 
+        conditions = {'SVP64BREV': Signal(name="svp64brev", reset_less=True),
+                      'SVP64FFT': Signal(name="svp64fft", reset_less=True),
+                     }
         pdecode = create_pdecode(name="rowsub",
-                                 col_subset={'function_unit', 'in1_sel'},
-                                 row_subset=rowsubsetfn)
+                                 col_subset={'opcode', 'function_unit',
+                                              'asmcode',
+                                             'in2_sel', 'in3_sel'},
+                                 row_subset=rowsubsetfn,
+                                 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)
+        print (elaborated_repr)
+
+        exit(0)
+
+        exit(0)
 
-    pdecode = create_pdecode()
+    # 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)
+
+