whoops changed name of ALUInputData to LogicalInputData
[soc.git] / src / soc / decoder / power_decoder.py
index 5b5e710393f3b504bc52a4d992431340714648fa..32cea4d12b9a9d3ee91c77633e5e3fcbe67c0919 100644 (file)
@@ -2,8 +2,8 @@
 
 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 +12,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:
 
-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.
+    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
+
+    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:
 
@@ -53,21 +79,38 @@ Top Level:
 
 """
 
-from nmigen import Module, Elaboratable, Signal
+from collections import namedtuple
+from nmigen import Module, Elaboratable, Signal, Cat, Mux
 from nmigen.cli import rtlil
-from power_enums import (Function, Form, InternalOp, In1Sel, In2Sel, In3Sel,
-                         OutSel, RC, LdstLen, CryIn, get_csv, single_bit_flags,
+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 power_fields import DecodeFields
-from power_fieldsn import SigDecode, SignalBitRange
+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
+         "bitsel",     # the bits (as a range) against which "pattern" matches
+         "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):
@@ -88,6 +131,9 @@ 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
         res = [self.function_unit.eq(Function[row['unit']]),
                self.form.eq(Form[row['form']]),
                self.internal_op.eq(InternalOp[row['internal op']]),
@@ -176,6 +222,7 @@ class PowerDecoder(Elaboratable):
         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
 
         # go through the list of CSV decoders first
         for d in self.dec:
@@ -186,6 +233,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])
@@ -195,6 +243,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:
@@ -208,6 +257,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
@@ -226,15 +276,78 @@ class PowerDecoder(Elaboratable):
         return [self.opcode_in] + self.op.ports()
 
 
-class TopPowerDecoder(PowerDecoder, DecodeFields):
+class TopPowerDecoder(PowerDecoder):
+    """TopPowerDecoder
+
+    top-level hierarchical decoder for POWER ISA
+    bigendian dynamically switches between big and little endian decoding
+    (reverses byte order).  See V3.0B p44 1.11.2
+    """
 
     def __init__(self, width, dec):
         PowerDecoder.__init__(self, width, dec)
-        DecodeFields.__init__(self, SignalBitRange, [self.opcode_in])
-        self.create_specs()
+        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, 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
+        l = []
+        for i in range(0, self.width, 8):
+            l.append(raw_be[i:i+8])
+        l.reverse()
+        raw_le = Cat(*l)
+        comb += self.opcode_in.eq(Mux(self.bigendian, raw_be, raw_le))
+
+        # 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 = []
@@ -247,7 +360,7 @@ def create_pdecode():
     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"),