raise interrupt on misaligned atomic LDST
[soc.git] / src / soc / fu / mmu / fsm.py
index f09fd987e311f80f72db005ddec4be26c7c437d9..24be3f5402710bed1fb3a016e672ef12cc13671b 100644 (file)
@@ -26,6 +26,7 @@ from soc.experiment.mem_types import MMUToLoadStore1Type
 from soc.fu.ldst.loadstore import LoadStore1, TestSRAMLoadStore1
 from nmutil.util import Display
 
+
 class FSMMMUStage(ControlBase):
     """FSM MMU
 
@@ -44,6 +45,7 @@ class FSMMMUStage(ControlBase):
         # set up p/n data
         self.p.i_data = MMUInputData(pspec)
         self.n.o_data = MMUOutputData(pspec)
+        self.exc_o = self.n.o_data.exception # AllFunctionUnits needs this
 
         self.mmu = MMU()
 
@@ -64,41 +66,39 @@ class FSMMMUStage(ControlBase):
         # incoming PortInterface
         self.ldst = ldst
         self.dcache = self.ldst.dcache
+        self.icache = self.ldst.icache
         self.pi = self.ldst.pi
 
     def elaborate(self, platform):
         assert hasattr(self, "dcache"), "remember to call set_ldst_interface"
         m = super().elaborate(platform)
         comb, sync = m.d.comb, m.d.sync
-        dcache = self.dcache
+        dcache, icache = self.dcache, self.icache
+        ldst = self.ldst # managed externally: do not add here
 
-        # link mmu and dcache together
+        # link mmu, dcache and icache together
         m.submodules.mmu = mmu = self.mmu
-        ldst = self.ldst # managed externally: do not add here
         m.d.comb += dcache.m_in.eq(mmu.d_out) # MMUToDCacheType
         m.d.comb += mmu.d_in.eq(dcache.m_out) # DCacheToMMUType
+        m.d.comb += icache.m_in.eq(mmu.i_out) # MMUToICacheType
 
         l_in, l_out = mmu.l_in, mmu.l_out
         d_in, d_out = dcache.d_in, dcache.d_out
-        wb_out, wb_in = dcache.wb_out, dcache.wb_in
 
         # link ldst and MMU together
         comb += l_in.eq(ldst.m_out)
         comb += ldst.m_in.eq(l_out)
 
         i_data, o_data = self.p.i_data, self.n.o_data
-        a_i, b_i, o, spr1_o = i_data.ra, i_data.rb, o_data.o, o_data.spr1
         op = i_data.ctx.op
+        cia_i = op.cia
         msr_i = op.msr
-        spr1_i = i_data.spr1
-
-        # these are set / got here *ON BEHALF* of LoadStore1
-        # XXX have to deal with this another way
-        # dsisr, dar = ldst.dsisr, ldst.dar
+        a_i, b_i, spr1_i = i_data.ra, i_data.rb, i_data.spr1
+        o, exc_o, spr1_o = o_data.o, o_data.exception, o_data.spr1
 
         # busy/done signals
-        busy = Signal()
-        done = Signal()
+        busy = Signal(name="mmu_fsm_busy")
+        done = Signal(name="mmu_fsm_done")
         m.d.comb += self.n.o_valid.eq(busy & done)
         m.d.comb += self.p.o_ready.eq(~busy)
 
@@ -107,11 +107,6 @@ class FSMMMUStage(ControlBase):
         spr = Signal(len(x_fields.SPR))
         comb += spr.eq(decode_spr_num(x_fields.SPR))
 
-        # based on MSR bits, set priv and virt mode.  TODO: 32-bit mode
-        comb += d_in.priv_mode.eq(~msr_i[MSR.PR])
-        comb += d_in.virt_mode.eq(msr_i[MSR.DR])
-        #comb += d_in.mode_32bit.eq(msr_i[MSR.SF]) # ?? err
-
         # ok so we have to "pulse" the MMU (or dcache) rather than
         # hold the valid hi permanently.  guess what this does...
         valid = Signal()
@@ -131,6 +126,11 @@ class FSMMMUStage(ControlBase):
             # WIP: properly implement MicrOp.OP_MTSPR and MicrOp.OP_MFSPR
 
             with m.Switch(op.insn_type):
+
+                ##########
+                # OP_MTSPR
+                ##########
+
                 with m.Case(MicrOp.OP_MTSPR):
                     comb += Display("MMUTEST: OP_MTSPR: spr=%i", spr)
                     # despite redirection this FU **MUST** behave exactly
@@ -148,10 +148,12 @@ class FSMMMUStage(ControlBase):
                         comb += self.debug0.eq(3)
                         #if matched update local cached value
                         #commented out because there is a driver conflict
-                        #with m.If(spr[0]):
-                        #    sync += dsisr.eq(a_i[:32])
-                        #with m.Else():
-                        #    sync += dar.eq(a_i)
+                        comb += ldst.sprval_in.eq(a_i)
+                        comb += ldst.mmu_set_spr.eq(1)
+                        with m.If(spr[0]):
+                            comb += ldst.mmu_set_dar.eq(1)
+                        with m.Else():
+                            comb += ldst.mmu_set_dsisr.eq(1)
                         comb += done.eq(1)
                     # pass it over to the MMU instead
                     with m.Else():
@@ -165,13 +167,35 @@ class FSMMMUStage(ControlBase):
                         comb += l_in.rs.eq(a_i)    # incoming operand (RS)
                         comb += done.eq(1) # FIXME l_out.done
 
+                ##########
+                # OP_MFSPR
+                ##########
+
                 with m.Case(MicrOp.OP_MFSPR):
                     comb += Display("MMUTEST: OP_MFSPR: spr=%i returns=%i",
                                     spr, spr1_i)
-                    comb += o.data.eq(spr1_i)
+                    # partial SPR number decoding perfectly fine
+                    with m.If(spr[9] | spr[5]):
+                        # identified as an MMU OP_MFSPR, contact the MMU.
+                        # interestingly, the read is combinatorial: no need
+                        # to set "valid", just set the SPR number
+                        comb += l_in.sprn.eq(spr)  # which SPR
+                        comb += o.data.eq(l_out.sprval)
+                    with m.Else():
+                        # identified as DSISR or DAR.  again: read the SPR
+                        # directly, combinatorial access
+                        with m.If(spr[0]):
+                            comb += o.data.eq(ldst.dar)
+                        with m.Else():
+                            comb += o.data.eq(ldst.dsisr)
+
                     comb += o.ok.eq(1)
                     comb += done.eq(1)
 
+                ##########
+                # OP_TLBIE
+                ##########
+
                 with m.Case(MicrOp.OP_TLBIE):
                     comb += Display("MMUTEST: OP_TLBIE: insn_bits=%i", spr)
                     # pass TLBIE request to MMU (spec: v3.0B p1034)
@@ -187,6 +211,33 @@ class FSMMMUStage(ControlBase):
                     comb += done.eq(l_out.done) # zzzz
                     comb += self.debug0.eq(2)
 
+                ##########
+                # OP_FETCH_FAILED
+                ##########
+
+                with m.Case(MicrOp.OP_FETCH_FAILED):
+                    comb += Display("MMUTEST: OP_FETCH_FAILED: @%x", cia_i)
+                    # trigger an instruction fetch failed MMU event.
+                    # PowerDecoder2 drops svstate.pc into NIA for us
+                    # really, this should be direct communication with the
+                    # MMU, rather than going through LoadStore1.  but, doing
+                    # so allows for the opportunity to prevent LoadStore1
+                    # from accepting any other LD/ST requests.
+                    comb += valid.eq(1)   # start "pulse"
+                    comb += ldst.instr_fault.eq(blip)
+                    comb += ldst.priv_mode.eq(~msr_i[MSR.PR])
+                    comb += ldst.maddr.eq(cia_i)
+                    # XXX should not access this!
+                    comb += done.eq(ldst.done)
+                    comb += self.debug0.eq(3)
+                    # LDST unit contains exception data, which (messily)
+                    # is copied over, here.  not ideal but it will do for now
+                    comb += exc_o.eq(ldst.pi.exc_o)
+
+                ############
+                # OP_ILLEGAL
+                ############
+
                 with m.Case(MicrOp.OP_ILLEGAL):
                     comb += self.illegal.eq(1)