update copyright notices to include additional primary author
[soc.git] / src / soc / decoder / power_decoder.py
index f349e6bb7d34fa3c3f7b57de2515e74d98193b77..e4b87db1ca7debad7764c8318f222facd01f9914 100644 (file)
@@ -1,9 +1,14 @@
 """Cascading Power ISA Decoder
 
+License: LGPLv3
+
+# Copyright (C) 2020 Luke Kenneth Casson Leighton <lkcl@lkcl.net>
+# Copyright (C) 2020 Michael Nolan <mtnolan2640@gmail.com>
+
 This module uses CSV tables in a hierarchical/peer cascading fashion,
 to create a multi-level instruction decoder by recognising appropriate
-patterns.  The output is a flattened (1-level) series of fields suitable
-for a simple RISC engine.
+patterns.  The output is a wide, flattened (1-level) series of bitfields,
+suitable for a simple RISC engine.
 
 This is based on Anton Blanchard's excellent microwatt work:
 https://github.com/antonblanchard/microwatt/blob/master/decode1.vhdl
@@ -12,15 +17,41 @@ The basic principle is that the python code does the heavy lifting
 (reading the CSV files, constructing the hierarchy), creating the HDL
 AST with for-loops generating switch-case statements.
 
-PowerDecoder takes a *list* of CSV files with an associated bit-range
-that it is requested to match against the "opcode" row of the CSV file.
-This pattern can be either an integer, a binary number, *or* a wildcard
-nmigen Case pattern of the form "001--1-100".
+Where "normal" HDL would do this, in laborious excruciating detail:
+
+    switch (opcode & major_mask_bits):
+        case opcode_2: decode_opcode_2()
+        case opcode_19:
+                switch (opcode & minor_19_mask_bits)
+                    case minor_opcode_19_operation_X:
+                    case minor_opcode_19_operation_y:
+
+we take *full* advantage of the decoupling between python and the
+nmigen AST data structure, to do this:
+
+    with m.Switch(opcode & self.mask):
+        for case_bitmask in subcases:
+            with m.If(opcode & case_bitmask): {do_something}
+
+this includes specifying the information sufficient to perform subdecoding.
+
+create_pdecode()
+
+    the full hierarchical tree for decoding POWER9 is specified here
+
+PowerDecoder
+
+    takes a *list* of CSV files with an associated bit-range that it
+    is requested to match against the "opcode" row of the CSV file.
+    This pattern can be either an integer, a binary number, *or* a
+    wildcard nmigen Case pattern of the form "001--1-100".
+
+Subdecoders
 
-Subdecoders are *additional* cases with further decoding.  The "pattern"
-argument is specified as one of the Case statements (a peer of the opcode
-row in the CSV file), and thus further fields of the opcode may be decoded
-giving increasing levels of detail.
+    these are *additional* cases with further decoding.  The "pattern"
+    argument is specified as one of the Case statements (a peer of the
+    opcode row in the CSV file), and thus further fields of the opcode
+    may be decoded giving increasing levels of detail.
 
 Top Level:
 
@@ -51,36 +82,60 @@ Top Level:
       ),
     ]
 
+
 """
 
+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, InternalOp,
-                         In1Sel, In2Sel, In3Sel, OutSel, RC, LdstLen,
-                         CryIn, get_csv, single_bit_flags,
-                         get_signal_name, default_values)
-from collections import namedtuple
+from soc.decoder.power_enums import (Function, Form, MicrOp,
+                                     In1Sel, In2Sel, In3Sel, OutSel,
+                                     RC, LdstLen, LDSTMode, CryIn, get_csv,
+                                     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
 
 
-Subdecoder = namedtuple("Subdecoder", ["pattern", "opcodes", "opint",
-                                       "bitsel", "suffix", "subdecoders"])
+# key data structure in which the POWER decoder is specified,
+# in a hierarchical fashion
+Subdecoder = namedtuple("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
+                         # the bits (as a range) against which "pattern" matches
+                         "bitsel",
+                         "suffix",     # shift the opcode down before decoding
+                         "subdecoders"  # list of further subdecoders for *additional* matches,
+                         # *ONLY* after "pattern" has *ALSO* been matched against.
+                         ])
 
 
 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
     """
 
-    def __init__(self):
+    def __init__(self, incl_asm=True):
         self.function_unit = Signal(Function, reset_less=True)
-        self.internal_op = Signal(InternalOp, reset_less=True)
+        self.internal_op = Signal(MicrOp, reset_less=True)
         self.form = Signal(Form, reset_less=True)
+        if incl_asm:  # for simulator only
+            self.asmcode = Signal(8, reset_less=True)
         self.in1_sel = Signal(In1Sel, reset_less=True)
         self.in2_sel = Signal(In2Sel, reset_less=True)
         self.in3_sel = Signal(In3Sel, reset_less=True)
         self.out_sel = Signal(OutSel, reset_less=True)
+        self.cr_in = Signal(CRInSel, reset_less=True)
+        self.cr_out = Signal(CROutSel, reset_less=True)
         self.ldst_len = Signal(LdstLen, reset_less=True)
+        self.upd = Signal(LDSTMode, reset_less=True)
         self.rc_sel = Signal(RC, reset_less=True)
         self.cry_in = Signal(CryIn, reset_less=True)
         for bit in single_bit_flags:
@@ -90,17 +145,43 @@ class PowerOp:
     def _eq(self, row=None):
         if row is None:
             row = default_values
+        # TODO: this conversion process from a dict to an object
+        # should really be done using e.g. namedtuple and then
+        # call eq not _eq
+        if False:  # debugging
+            if row['CR in'] == '1':
+                import pdb
+                pdb.set_trace()
+                print(row)
+            if row['CR out'] == '0':
+                import pdb
+                pdb.set_trace()
+                print(row)
+            print(row)
+        ldst_mode = row['upd']
+        if ldst_mode.isdigit():
+            ldst_mode = LDSTMode(int(ldst_mode))
+        else:
+            ldst_mode = LDSTMode[ldst_mode]
         res = [self.function_unit.eq(Function[row['unit']]),
                self.form.eq(Form[row['form']]),
-               self.internal_op.eq(InternalOp[row['internal op']]),
+               self.internal_op.eq(MicrOp[row['internal op']]),
                self.in1_sel.eq(In1Sel[row['in1']]),
                self.in2_sel.eq(In2Sel[row['in2']]),
                self.in3_sel.eq(In3Sel[row['in3']]),
                self.out_sel.eq(OutSel[row['out']]),
+               self.cr_in.eq(CRInSel[row['CR in']]),
+               self.cr_out.eq(CROutSel[row['CR out']]),
                self.ldst_len.eq(LdstLen[row['ldst len']]),
+               self.upd.eq(ldst_mode),
                self.rc_sel.eq(RC[row['rc']]),
                self.cry_in.eq(CryIn[row['cry in']]),
                ]
+        if False:
+            print(row.keys())
+        asmcode = row['comment']
+        if hasattr(self, "asmcode") and asmcode in asmidx:
+            res.append(self.asmcode.eq(asmidx[asmcode]))
         for bit in single_bit_flags:
             sig = getattr(self, get_signal_name(bit))
             res.append(sig.eq(int(row.get(bit, 0))))
@@ -114,12 +195,17 @@ class PowerOp:
                self.in2_sel.eq(otherop.in2_sel),
                self.in3_sel.eq(otherop.in3_sel),
                self.out_sel.eq(otherop.out_sel),
+               self.cr_in.eq(otherop.cr_in),
+               self.cr_out.eq(otherop.cr_out),
                self.rc_sel.eq(otherop.rc_sel),
                self.ldst_len.eq(otherop.ldst_len),
+               self.upd.eq(otherop.upd),
                self.cry_in.eq(otherop.cry_in)]
         for bit in single_bit_flags:
             sig = getattr(self, get_signal_name(bit))
             res.append(sig.eq(getattr(otherop, get_signal_name(bit))))
+        if hasattr(self, "asmcode"):
+            res.append(self.asmcode.eq(otherop.asmcode))
         return res
 
     def ports(self):
@@ -128,10 +214,14 @@ class PowerOp:
                    self.in2_sel,
                    self.in3_sel,
                    self.out_sel,
+                   self.cr_in,
+                   self.cr_out,
                    self.ldst_len,
                    self.rc_sel,
                    self.internal_op,
                    self.form]
+        if hasattr(self, "asmcode"):
+            regular.append(self.asmcode)
         single_bit_ports = [getattr(self, get_signal_name(x))
                             for x in single_bit_flags]
         return regular + single_bit_ports
@@ -189,6 +279,7 @@ class PowerDecoder(Elaboratable):
                 opcodes = self.divide_opcodes(d)
                 opc_in = Signal(d.suffix, reset_less=True)
                 comb += 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])
@@ -198,6 +289,7 @@ class PowerDecoder(Elaboratable):
                         subdecoder = PowerDecoder(width=32, dec=sd)
                         setattr(m.submodules, "dec_sub%d" % key, subdecoder)
                         comb += subdecoder.opcode_in.eq(self.opcode_in)
+                        # add in the dynamic Case statement here
                         with m.Case(key):
                             comb += self.op.eq(subdecoder.op)
             else:
@@ -211,6 +303,7 @@ class PowerDecoder(Elaboratable):
                             opcode = int(opcode, 0)
                         if not row['unit']:
                             continue
+                        # add in the dynamic Case statement here
                         with m.Case(opcode):
                             comb += self.op._eq(row)
         return m
@@ -218,7 +311,7 @@ class PowerDecoder(Elaboratable):
     def handle_subdecoders(self, m, d):
         for dec in d.subdecoders:
             subdecoder = PowerDecoder(self.width, dec)
-            if isinstance(dec, list): # XXX HACK: take first pattern
+            if isinstance(dec, list):  # XXX HACK: take first pattern
                 dec = dec[0]
             setattr(m.submodules, "dec%d" % dec.pattern, subdecoder)
             m.d.comb += subdecoder.opcode_in.eq(self.opcode_in)
@@ -239,50 +332,82 @@ class TopPowerDecoder(PowerDecoder):
 
     def __init__(self, width, dec):
         PowerDecoder.__init__(self, width, dec)
-        self.fields = DecodeFields(SignalBitRange, [self.opcode_in])
+        self.fields = df = DecodeFields(SignalBitRange, [self.opcode_in])
         self.fields.create_specs()
         self.raw_opcode_in = Signal.like(self.opcode_in, reset_less=True)
         self.bigendian = Signal(reset_less=True)
 
-        for name in self.fields.common_fields:
-            value = getattr(self.fields, name)
+        for name, value in self.fields.common_fields.items():
             sig = Signal(value[0:-1].shape(), reset_less=True, name=name)
             setattr(self, name, sig)
 
+        # create signals for all field forms
+        self.form_names = forms = self.fields.instrs.keys()
+        self.sigforms = {}
+        for form in forms:
+            fields = self.fields.instrs[form]
+            fk = fields.keys()
+            Fields = namedtuple("Fields", fk)
+            sf = {}
+            for k, value in fields.items():
+                name = "%s_%s" % (form, k)
+                sig = Signal(value[0:-1].shape(), reset_less=True, name=name)
+                sf[k] = sig
+            instr = Fields(**sf)
+            setattr(self, "Form%s" % form, instr)
+            self.sigforms[form] = instr
+
     def elaborate(self, platform):
         m = PowerDecoder.elaborate(self, platform)
         comb = m.d.comb
-        raw_be = self.raw_opcode_in
+        # raw opcode in assumed to be in LE order: byte-reverse it to get BE
+        raw_le = self.raw_opcode_in
         l = []
         for i in range(0, self.width, 8):
-            l.append(raw_be[i:i+8])
+            l.append(raw_le[i:i+8])
         l.reverse()
-        raw_le = Cat(*l)
+        raw_be = Cat(*l)
         comb += self.opcode_in.eq(Mux(self.bigendian, raw_be, raw_le))
-        for name in self.fields.common_fields:
-            value = getattr(self.fields, name)
+
+        # add all signal from commonly-used fields
+        for name, value in self.fields.common_fields.items():
             sig = getattr(self, name)
             comb += sig.eq(value[0:-1])
+
+        # link signals for all field forms
+        forms = self.form_names
+        for form in forms:
+            sf = self.sigforms[form]
+            fields = self.fields.instrs[form]
+            for k, value in fields.items():
+                sig = getattr(sf, k)
+                comb += sig.eq(value[0:-1])
+
         return m
 
     def ports(self):
         return [self.raw_opcode_in, self.bigendian] + PowerDecoder.ports(self)
 
 
+####################################################
+# PRIMARY FUNCTION SPECIFYING THE FULL POWER DECODER
+
 def create_pdecode():
+    """create_pdecode - creates a cascading hierarchical POWER ISA decoder
+    """
 
     # minor 19 has extra patterns
     m19 = []
     m19.append(Subdecoder(pattern=19, opcodes=get_csv("minor_19.csv"),
-                   opint=True, bitsel=(1, 11), suffix=None, subdecoders=[]))
+                          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=[]))
+                          opint=True, bitsel=(1, 6), suffix=None, subdecoders=[]))
 
     # minor opcodes.
     pminor = [
         m19,
         Subdecoder(pattern=30, opcodes=get_csv("minor_30.csv"),
-                   opint=True, bitsel=(1, 6), suffix=None, subdecoders=[]),
+                   opint=True, 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"),
@@ -295,10 +420,10 @@ def create_pdecode():
     dec = []
     opcodes = get_csv("major.csv")
     dec.append(Subdecoder(pattern=None, opint=True, opcodes=opcodes,
-                     bitsel=(26, 32), suffix=None, subdecoders=pminor))
+                          bitsel=(26, 32), suffix=None, subdecoders=pminor))
     opcodes = get_csv("extra.csv")
     dec.append(Subdecoder(pattern=None, opint=False, opcodes=opcodes,
-                     bitsel=(0, 32), suffix=None, subdecoders=[]))
+                          bitsel=(0, 32), suffix=None, subdecoders=[]))
 
     return TopPowerDecoder(32, dec)