issuer.py: add microwatt_old and microwatt_debug options
[soc.git] / src / soc / simple / core.py
index d8f8d9ced3afc53b9baacd9ca488557994c99adf..9a4abacc3135e647ae4be3d9a8b7882e7ce68fe4 100644 (file)
@@ -71,6 +71,47 @@ def sort_fuspecs(fuspecs):
     return res  # enumerate(res)
 
 
+# a hazard bitvector "remap" function which returns an AST expression
+# that remaps read/write hazard regfile port numbers to either a full
+# bitvector or a reduced subset one.  SPR for example is reduced to a
+# single bit.
+# CRITICALLY-IMPORTANT NOTE: these bitvectors *have* to match up per
+# regfile!  therefore the remapping is per regfile, *NOT* per regfile
+# port and certainly not based on whether it is a read port or write port.
+# note that any reductions here will result in degraded performance due
+# to conflicts, but at least it keeps the hazard matrix sizes down to "sane"
+def bitvector_remap(regfile, rfile, port):
+    # 8-bits (at the moment, no SVP64), CR is unary: no remap
+    if regfile == 'CR':
+        return port
+    # 3 bits, unary alrady: return the port
+    if regfile == 'XER':
+        return port
+    # 3 bits, unary: return the port
+    if regfile == 'XER':
+        return port
+    # 5 bits, unary: return the port
+    if regfile == 'STATE':
+        return port
+    # 9 bits (9 entries), might be unary already
+    if regfile == 'FAST':
+        if rfile.unary: # FAST might be unary already
+            return port
+        else:
+            return 1 << port
+    # 10 bits (!!) - reduce to one
+    if regfile == 'SPR':
+        if rfile.unary: # FAST might be unary already
+            return port
+        else:
+            return 1 << port
+    if regfile == 'INT':
+        if rfile.unary: # INT, check if unary/binary
+            return port
+        else:
+            return 1 << port
+
+
 # derive from ControlBase rather than have a separate Stage instance,
 # this is simpler to do
 class NonProductionCore(ControlBase):
@@ -108,14 +149,34 @@ class NonProductionCore(ControlBase):
 
         # link LoadStore1 into MMU
         mmu = self.fus.get_fu('mmu0')
+        ldst0 = self.fus.get_fu('ldst0')
         print ("core pspec", pspec.ldst_ifacetype)
         print ("core mmu", mmu)
         if mmu is not None:
-            print ("core lsmem.lsi", l0.cmpi.lsmem.lsi)
-            mmu.alu.set_ldst_interface(l0.cmpi.lsmem.lsi)
+            lsi = l0.cmpi.lsmem.lsi # a LoadStore1 Interface object
+            print ("core lsmem.lsi", lsi)
+            mmu.alu.set_ldst_interface(lsi)
+            # urr store I-Cache in core so it is easier to get at
+            self.icache = lsi.icache
+
+        # alternative reset values for STATE regs. these probably shouldn't
+        # be set, here, instead have them done by Issuer. which they are.
+        # as well. because core.state overrides them. sigh.
+        self.msr_at_reset = 0x0
+        self.pc_at_reset = 0x0
+        if hasattr(pspec, "msr_reset") and isinstance(pspec.msr_reset, int):
+            self.msr_at_reset = pspec.msr_reset
+        if hasattr(pspec, "pc_reset") and isinstance(pspec.pc_reset, int):
+            self.pc_at_reset = pspec.pc_reset
+        state_resets = [self.pc_at_reset,  # PC at reset
+                        self.msr_at_reset, # MSR at reset
+                        0x0,               # SVSTATE at reset
+                        0x0,               # DEC at reset
+                        0x0]               # TB at reset
 
         # register files (yes plural)
-        self.regs = RegFiles(pspec, make_hazard_vecs=self.make_hazard_vecs)
+        self.regs = RegFiles(pspec, make_hazard_vecs=self.make_hazard_vecs,
+                                    state_resets=state_resets)
 
         # set up input and output: unusual requirement to set data directly
         # (due to the way that the core is set up in a different domain,
@@ -153,6 +214,7 @@ class NonProductionCore(ControlBase):
                                             svp64_en=self.svp64_en,
                                             regreduce_en=self.regreduce_en)
             self.des[funame] = self.decoders[funame].do
+            print ("create decoder subset", funame, opkls, self.des[funame])
 
         # create per-Function Unit write-after-write hazard signals
         # yes, really, this should have been added in ReservationStations
@@ -164,6 +226,10 @@ class NonProductionCore(ControlBase):
         if "mmu0" in self.decoders:
             self.decoders["mmu0"].mmu0_spr_dec = self.decoders["spr0"]
 
+        # allow pausing of the DEC/TB FSM back in Issuer, by spotting
+        # if there is an MTSPR instruction
+        self.pause_dec_tb = Signal()
+
     # next 3 functions are Stage API Compliance
     def setup(self, m, i):
         pass
@@ -333,7 +399,7 @@ class NonProductionCore(ControlBase):
 
         # rdmask, which is for registers needs to come from the *main* decoder
         for funame, fu in fus.items():
-            rdmask = get_rdflags(self.ireg.e, fu)
+            rdmask = get_rdflags(m, self.ireg.e, fu)
             comb += fu.rdmaskn.eq(~rdmask)
 
         # sigh - need a NOP counter
@@ -377,6 +443,20 @@ class NonProductionCore(ControlBase):
                                     # is a waw hazard. decoder has to still
                                     # be asserted in order to detect that, tho
                                     comb += fu.oper_i.eq_from(do)
+                                    if funame == 'mmu0':
+                                        # URRR this is truly dreadful.
+                                        # OP_FETCH_FAILED is a "fake" op.
+                                        # no instruction creates it.  OP_TRAP
+                                        # uses the *main* decoder: this is
+                                        # a *Satellite* decoder that reacts
+                                        # on *insn_in*... not fake ops. gaah.
+                                        main_op = self.ireg.e.do
+                                        with m.If(main_op.insn_type ==
+                                                  MicrOp.OP_FETCH_FAILED):
+                                            comb += fu.oper_i.insn_type.eq(
+                                                  MicrOp.OP_FETCH_FAILED)
+                                            comb += fu.oper_i.fn_unit.eq(
+                                                  Function.MMU)
                                     # issue when valid (and no write-hazard)
                                     comb += fu.issue_i.eq(~self.waw_hazard)
                                     # instruction ok, indicate ready
@@ -443,6 +523,14 @@ class NonProductionCore(ControlBase):
                     funame.lower().startswith('trap')):
                     with m.If(fu.busy_o):
                         comb += busy_o.eq(1)
+                # for SPR pipeline pause dec/tb FSM to avoid race condition
+                # TODO: really this should be much more sophisticated,
+                # spot MTSPR, spot that DEC/TB is what is to be updated.
+                # a job for PowerDecoder2, there
+                if funame.lower().startswith('spr'):
+                    with m.If(fu.busy_o #& fu.oper_i.insn_type == OP_MTSPR
+                        ):
+                        comb += self.pause_dec_tb.eq(1)
 
         # return both the function unit "enable" dict as well as the "busy".
         # the "busy-or-issued" can be passed in to the Read/Write port
@@ -519,7 +607,7 @@ class NonProductionCore(ControlBase):
                 rdflag = Signal(name="rdflag_%s_%s" % (funame, rhname),
                                 reset_less=True)
                 if rhname not in fu.rf_latches:
-                    rfl = Signal(name="rdflag_latch_"+rhname)
+                    rfl = Signal(name="rdflag_latch_%s_%s" % (funame, rhname))
                     fu.rf_latches[rhname] = rfl
                     with m.If(fu.issue_i):
                         sync += rfl.eq(rdflags[i])
@@ -643,13 +731,12 @@ class NonProductionCore(ControlBase):
         rd_hazard = []
 
         # dictionary of lists of regfile read ports
-        byregfiles_rd, byregfiles_rdspec = self.get_byregfiles(True)
+        byregfiles_rdspec = self.get_byregfiles(m, True)
 
         # okaay, now we need a PriorityPicker per regfile per regfile port
         # loootta pickers... peter piper picked a pack of pickled peppers...
         rdpickers = {}
-        for regfile, spec in byregfiles_rd.items():
-            fuspecs = byregfiles_rdspec[regfile]
+        for regfile, fuspecs in byregfiles_rdspec.items():
             rdpickers[regfile] = {}
 
             # argh.  an experiment to merge RA and RB in the INT regfile
@@ -770,7 +857,6 @@ class NonProductionCore(ControlBase):
             wvset = wv.s # write-vec bit-level hazard ctrl
             wvclr = wv.r # write-vec bit-level hazard ctrl
             wvchk = wv.q # write-after-write hazard check
-            wvchk_qint = wv.q # write-after-write hazard check, NOT delayed
 
         fspecs = fspec
         if not isinstance(fspecs, list):
@@ -915,7 +1001,7 @@ class NonProductionCore(ControlBase):
                 # write-hazard is ANDed with (filtered by) what is actually
                 # being requested.  the wvchk data is on a one-clock delay,
                 # and wvchk_en comes directly from the main decoder
-                comb += whazard.eq((wvchk_qint & wvchk_en).bool())
+                comb += whazard.eq((wvchk & wvchk_en).bool())
                 with m.If(whazard):
                     comb += fu._waw_hazard.eq(1)
 
@@ -961,15 +1047,14 @@ class NonProductionCore(ControlBase):
         fus = self.fus.fus
         regs = self.regs
         # dictionary of lists of regfile write ports
-        byregfiles_wr, byregfiles_wrspec = self.get_byregfiles(False)
+        byregfiles_wrspec = self.get_byregfiles(m, False)
 
         # same for write ports.
         # BLECH!  complex code-duplication! BLECH!
         wrpickers = {}
         wvclrers = defaultdict(list)
         wvseters = defaultdict(list)
-        for regfile, spec in byregfiles_wr.items():
-            fuspecs = byregfiles_wrspec[regfile]
+        for regfile, fuspecs in byregfiles_wrspec.items():
             wrpickers[regfile] = {}
 
             if self.regreduce_en:
@@ -1012,7 +1097,7 @@ class NonProductionCore(ControlBase):
             comb += wvclr.eq(ortreereduce_sig(wvclren)) # clear (regfile write)
             comb += wvset.eq(ortreereduce_sig(wvseten)) # set (issue time)
 
-    def get_byregfiles(self, readmode):
+    def get_byregfiles(self, m, readmode):
 
         mode = "read" if readmode else "write"
         regs = self.regs
@@ -1021,44 +1106,44 @@ class NonProductionCore(ControlBase):
 
         # dictionary of dictionaries of lists/tuples of regfile ports.
         # first key: regfile.  second key: regfile port name
-        byregfiles = defaultdict(lambda: defaultdict(list))
         byregfiles_spec = defaultdict(dict)
 
         for (funame, fu) in fus.items():
             # create in each FU a receptacle for the read/write register
-            # hazard numbers.  to be latched in connect_rd/write_ports
-            # XXX better that this is moved into the actual FUs, but
-            # the issue there is that this function is actually better
-            # suited at the moment
+            # hazard numbers (and okflags for read).  to be latched in
+            # connect_rd/write_ports
             if readmode:
                 fu.rd_latches = {} # read reg number latches
                 fu.rf_latches = {} # read flag latches
             else:
                 fu.wr_latches = {}
 
+            # construct regfile specs: read uses inspec, write outspec
             print("%s ports for %s" % (mode, funame))
             for idx in range(fu.n_src if readmode else fu.n_dst):
-                # construct regfile specs: read uses inspec, write outspec
                 (regfile, regname, wid) = fu.get_io_spec(readmode, idx)
                 print("    %d %s %s %s" % (idx, regfile, regname, str(wid)))
 
                 # the PowerDecoder2 (main one, not the satellites) contains
                 # the decoded regfile numbers. obtain these now
-                okflag, regport = regspec_decode(readmode, e, regfile, regname)
+                decinfo = regspec_decode(m, readmode, e, regfile, regname)
+                okflag, regport = decinfo.okflag, decinfo.regport
 
                 # construct the dictionary of regspec information by regfile
                 if regname not in byregfiles_spec[regfile]:
                     byregfiles_spec[regfile][regname] = \
                         ByRegSpec(okflag, regport, wid, [])
-                # here we start to create "lanes"
+
+                # here we start to create "lanes" where each Function Unit
+                # requiring access to a given [single-contended resource]
+                # regfile port is appended to a list, so that PriorityPickers
+                # can be created to give uncontested access to it
                 fuspec = FUSpec(funame, fu, idx)
-                byregfiles[regfile][idx].append(fuspec)
                 byregfiles_spec[regfile][regname].specs.append(fuspec)
 
         # ok just print that all out, for convenience
-        for regfile, spec in byregfiles.items():
+        for regfile, fuspecs in byregfiles_spec.items():
             print("regfile %s ports:" % mode, regfile)
-            fuspecs = byregfiles_spec[regfile]
             for regname, fspec in fuspecs.items():
                 [okflag, regport, wid, fuspecs] = fspec
                 print("  rf %s port %s lane: %s" % (mode, regfile, regname))
@@ -1068,7 +1153,7 @@ class NonProductionCore(ControlBase):
                     print("    ", funame, fu.__class__.__name__, idx, fusig)
                     print()
 
-        return byregfiles, byregfiles_spec
+        return byregfiles_spec
 
     def __iter__(self):
         yield from self.fus.ports()
@@ -1083,7 +1168,7 @@ class NonProductionCore(ControlBase):
 if __name__ == '__main__':
     pspec = TestMemPspec(ldst_ifacetype='testpi',
                          imem_ifacetype='',
-                         addr_wid=48,
+                         addr_wid=64,
                          allow_overlap=True,
                          mask_wid=8,
                          reg_wid=64)