Allow the formal engine to perform a same-cycle result in the ALU
[soc.git] / src / soc / fu / ldst / loadstore.py
index 6283f8ac439824002f55b29ba0803bb363133572..6e868b1eebdd7eeffbc6f1812c29e508a0a0ad9c 100644 (file)
@@ -19,7 +19,7 @@ Links:
 
 from nmigen import (Elaboratable, Module, Signal, Shape, unsigned, Cat, Mux,
                     Record, Memory,
-                    Const)
+                    Const, C)
 from nmutil.iocontrol import RecordObject
 from nmutil.util import rising_edge, Display
 from enum import Enum, unique
@@ -67,6 +67,10 @@ class LDSTRequest(RecordObject):
         self.mode_32bit    = Signal() # XXX UNUSED AT PRESENT
         self.alignstate    = Signal(Misalign) # progress of alignment request
         self.align_intr    = Signal()
+        # atomic (LR/SC reservation)
+        self.reserve       = Signal()
+        self.atomic        = Signal()
+        self.atomic_last   = Signal()
 
 
 # glue logic for microwatt mmu and dcache
@@ -108,15 +112,14 @@ class LoadStore1(PortInterfaceBase):
         self.dcbz          = Signal()
         self.raddr          = Signal(64)
         self.maddr          = Signal(64)
-        self.store_data    = Signal(128)   # 128-bit to cope with
-        self.load_data     = Signal(128)   # misalignment
+        self.store_data    = Signal(64)   # first half (aligned)
+        self.store_data2   = Signal(64)   # second half (misaligned)
+        self.load_data     = Signal(128)   # 128 to cope with misalignment
         self.load_data_delay = Signal(128) # perform 2 LD/STs
         self.byte_sel      = Signal(16)    # also for misaligned, 16-bit
         self.alignstate    = Signal(Misalign) # progress of alignment request
+        self.next_addr      = Signal(64)      # 2nd (aligned) read/write addr
         #self.xerc         : xer_common_t;
-        #self.reserve       = Signal()
-        #self.atomic        = Signal()
-        #self.atomic_last   = Signal()
         #self.rc            = Signal()
         self.nc            = Signal()              # non-cacheable access
         self.mode_32bit    = Signal() # XXX UNUSED AT PRESENT
@@ -128,6 +131,7 @@ class LoadStore1(PortInterfaceBase):
         self.busy          = Signal()
         self.wait_dcache   = Signal()
         self.wait_mmu      = Signal()
+        self.lrsc_misalign = Signal()
         #self.intr_vec     : integer range 0 to 16#fff#;
         #self.nia           = Signal(64)
         #self.srr1          = Signal(16)
@@ -145,7 +149,8 @@ class LoadStore1(PortInterfaceBase):
     def external_busy(self, m):
         return self.instr_fault | self.r_instr_fault
 
-    def set_wr_addr(self, m, addr, mask, misalign, msr, is_dcbz):
+    def set_wr_addr(self, m, addr, mask, misalign, msr, is_dcbz, is_nc):
+        m.d.comb += self.req.nc.eq(is_nc)
         m.d.comb += self.req.load.eq(0) # store operation
         m.d.comb += self.req.byte_sel.eq(mask)
         m.d.comb += self.req.raddr.eq(addr)
@@ -155,6 +160,7 @@ class LoadStore1(PortInterfaceBase):
         m.d.comb += self.req.dcbz.eq(is_dcbz)
         with m.If(misalign):
             m.d.comb += self.req.alignstate.eq(Misalign.NEED2WORDS)
+            m.d.sync += self.next_addr.eq(Cat(C(0, 3), addr[3:]+1))
 
         # m.d.comb += Display("set_wr_addr %i dcbz %i",addr,is_dcbz)
 
@@ -166,9 +172,15 @@ class LoadStore1(PortInterfaceBase):
         with m.If(is_dcbz & self.req.nc):
             m.d.comb += self.req.align_intr.eq(1)
 
+        # hmm, rather than add yet another argument to set_wr_addr
+        # read direct from PortInterface
+        m.d.comb += self.req.reserve.eq(self.pi.reserve) # atomic request
+        m.d.comb += self.req.atomic.eq(~self.lrsc_misalign)
+        m.d.comb += self.req.atomic_last.eq(~self.lrsc_misalign)
+
         return None
 
-    def set_rd_addr(self, m, addr, mask, misalign, msr):
+    def set_rd_addr(self, m, addr, mask, misalign, msr, is_nc):
         m.d.comb += self.d_valid.eq(1)
         m.d.comb += self.req.load.eq(1) # load operation
         m.d.comb += self.req.byte_sel.eq(mask)
@@ -176,6 +188,7 @@ class LoadStore1(PortInterfaceBase):
         m.d.comb += self.req.priv_mode.eq(~msr.pr) # not-problem  ==> priv
         m.d.comb += self.req.virt_mode.eq(msr.dr) # DR ==> virt
         m.d.comb += self.req.mode_32bit.eq(~msr.sf) # not-sixty-four ==> 32bit
+        m.d.comb += self.req.nc.eq(is_nc)
         # BAD HACK! disable cacheing on LD when address is 0xCxxx_xxxx
         # this is for peripherals. same thing done in Microwatt loadstore1.vhdl
         with m.If(addr[28:] == Const(0xc, 4)):
@@ -184,7 +197,16 @@ class LoadStore1(PortInterfaceBase):
         if self.disable_cache:
             m.d.comb += self.req.nc.eq(1)
         with m.If(misalign):
+            # need two reads: prepare next address in advance
             m.d.comb += self.req.alignstate.eq(Misalign.NEED2WORDS)
+            m.d.sync += self.next_addr.eq(Cat(C(0, 3), addr[3:]+1))
+
+        # hmm, rather than add yet another argument to set_rd_addr
+        # read direct from PortInterface
+        m.d.comb += self.req.reserve.eq(self.pi.reserve) # atomic request
+        m.d.comb += self.req.atomic.eq(~self.lrsc_misalign)
+        m.d.comb += self.req.atomic_last.eq(~self.lrsc_misalign)
+
         return None #FIXME return value
 
     def set_wr_data(self, m, data, wen):
@@ -193,8 +215,10 @@ class LoadStore1(PortInterfaceBase):
         # put data into comb which is picked up in main elaborate()
         m.d.comb += self.d_w_valid.eq(1)
         m.d.comb += self.store_data.eq(data)
-        #m.d.sync += self.d_out.byte_sel.eq(wen) # this might not be needed
+        m.d.sync += self.store_data2.eq(data[64:128])
         st_ok = self.done # TODO indicates write data is valid
+        m.d.comb += self.pi.store_done.data.eq(self.d_in.store_done)
+        m.d.comb += self.pi.store_done.ok.eq(1)
         return st_ok
 
     def get_rd_data(self, m):
@@ -226,6 +250,12 @@ class LoadStore1(PortInterfaceBase):
         maddr = Signal(64)
         m.d.comb += maddr.eq(self.raddr)
 
+        # check for LR/SC misalignment, used in set_rd/wr_addr above
+        comb += self.lrsc_misalign.eq(((self.pi.data_len[0:3]-1) &
+                                        self.req.raddr[0:3]).bool())
+        with m.If(self.lrsc_misalign & self.req.reserve):
+            m.d.comb += self.req.align_intr.eq(1)
+
         # create a blip (single pulse) on valid read/write request
         # this can be over-ridden in the FSM to get dcache to re-run
         # a request when MMU_LOOKUP completes.
@@ -236,6 +266,7 @@ class LoadStore1(PortInterfaceBase):
         # fsm skeleton
         with m.Switch(self.state):
             with m.Case(State.IDLE):
+                sync += self.load_data_delay.eq(0) # clear out
                 with m.If((self.d_validblip | self.instr_fault) &
                           ~exc.happened):
                     comb += self.busy.eq(1)
@@ -299,13 +330,16 @@ class LoadStore1(PortInterfaceBase):
                         with m.If(ldst_r.load):
                             m.d.comb += self.load_data[0:63].eq(d_in.data)
                             sync += self.load_data_delay[0:64].eq(d_in.data)
-                        # mmm kinda cheating, make a 2nd blip
+                        with m.Else():
+                            m.d.sync += d_out.data.eq(self.store_data2)
+                        # mmm kinda cheating, make a 2nd blip.
+                        # use an aligned version of the address
                         m.d.comb += self.d_validblip.eq(1)
                         comb += self.req.eq(ldst_r) # from copy of request
-                        comb += self.req.raddr.eq(ldst_r.raddr + 8)
+                        comb += self.req.raddr.eq(self.next_addr)
                         comb += self.req.byte_sel.eq(ldst_r.byte_sel[8:])
                         comb += self.req.alignstate.eq(Misalign.WAITSECOND)
-                        sync += ldst_r.raddr.eq(ldst_r.raddr + 8)
+                        sync += ldst_r.raddr.eq(self.next_addr)
                         sync += ldst_r.byte_sel.eq(ldst_r.byte_sel[8:])
                         sync += ldst_r.alignstate.eq(Misalign.WAITSECOND)
                         sync += Display("    second req %x", self.req.raddr)
@@ -390,6 +424,12 @@ class LoadStore1(PortInterfaceBase):
         comb += exc.perm_error.eq(m_in.perm_error)
         comb += exc.rc_error.eq(m_in.rc_error)
         comb += exc.segment_fault.eq(m_in.segerr)
+        # conditions for 0x400 trap need these in SRR1
+        with m.If(exception & ~exc.alignment & exc.instr_fault):
+            comb += exc.srr1[14].eq(exc.invalid)      # 47-33
+            comb += exc.srr1[12].eq(exc.perm_error)   # 47-35
+            comb += exc.srr1[3].eq(exc.badtree)       # 47-44
+            comb += exc.srr1[2].eq(exc.rc_error)      # 47-45
 
         # TODO, connect dcache wb_in/wb_out to "standard" nmigen Wishbone bus
         comb += dbus.adr.eq(dcache.bus.adr)
@@ -404,12 +444,9 @@ class LoadStore1(PortInterfaceBase):
         if hasattr(dbus, "stall"):
             comb += dcache.bus.stall.eq(dbus.stall)
 
-        # update out d data when flag set
+        # update out d data when flag set, for first half (second done in FSM)
         with m.If(self.d_w_valid):
-            with m.If(ldst_r.alignstate == Misalign.WAITSECOND):
-                m.d.sync += d_out.data.eq(self.store_data[64:128])
-            with m.Else():
-                m.d.sync += d_out.data.eq(self.store_data[0:64])
+            m.d.sync += d_out.data.eq(self.store_data)
         #with m.Else():
         #    m.d.sync += d_out.data.eq(0)
         # unit test passes with that change
@@ -427,6 +464,9 @@ class LoadStore1(PortInterfaceBase):
             m.d.comb += d_out.nc.eq(self.req.nc)
             m.d.comb += d_out.priv_mode.eq(self.req.priv_mode)
             m.d.comb += d_out.virt_mode.eq(self.req.virt_mode)
+            m.d.comb += d_out.reserve.eq(self.req.reserve)
+            m.d.comb += d_out.atomic.eq(self.req.atomic)
+            m.d.comb += d_out.atomic_last.eq(self.req.atomic_last)
             #m.d.comb += Display("validblip dcbz=%i addr=%x",
             #self.req.dcbz,self.req.addr)
             m.d.comb += d_out.dcbz.eq(self.req.dcbz)
@@ -437,6 +477,9 @@ class LoadStore1(PortInterfaceBase):
             m.d.comb += d_out.nc.eq(ldst_r.nc)
             m.d.comb += d_out.priv_mode.eq(ldst_r.priv_mode)
             m.d.comb += d_out.virt_mode.eq(ldst_r.virt_mode)
+            m.d.comb += d_out.reserve.eq(ldst_r.reserve)
+            m.d.comb += d_out.atomic.eq(ldst_r.atomic)
+            m.d.comb += d_out.atomic_last.eq(ldst_r.atomic_last)
             #m.d.comb += Display("no_validblip dcbz=%i addr=%x",
             #ldst_r.dcbz,ldst_r.addr)
             m.d.comb += d_out.dcbz.eq(ldst_r.dcbz)