start doing virtual memory queries via PortInterface on LoadStore1
authorLuke Kenneth Casson Leighton <lkcl@lkcl.net>
Wed, 12 May 2021 14:07:09 +0000 (15:07 +0100)
committerLuke Kenneth Casson Leighton <lkcl@lkcl.net>
Wed, 12 May 2021 14:07:09 +0000 (15:07 +0100)
src/soc/config/test/test_pi2ls.py
src/soc/experiment/test/test_ldst_pi.py [new file with mode: 0644]
src/soc/fu/ldst/loadstore.py

index 3b34347e4835d63c5a6ccab76c5a67920391530c..f1807ccf3e7d53ec7d4b7ebe29569e4e10fced13 100644 (file)
@@ -35,7 +35,7 @@ def wait_ldok(port):
         yield
 
 
-def pi_st(port1, addr, data, datalen):
+def pi_st(port1, addr, data, datalen, msr_pr=0):
 
     # have to wait until not busy
     yield from wait_busy(port1, no=False)    # wait until not busy
@@ -43,6 +43,7 @@ def pi_st(port1, addr, data, datalen):
     # set up a ST on the port.  address first:
     yield port1.is_st_i.eq(1)  # indicate ST
     yield port1.data_len.eq(datalen)  # ST length (1/2/4/8)
+    yield port1.msr_pr.eq(msr_pr)  # MSR PR bit (1==>virt, 0==>real)
 
     yield port1.addr.data.eq(addr)  # set address
     yield port1.addr.ok.eq(1)  # set ok
@@ -62,7 +63,7 @@ def pi_st(port1, addr, data, datalen):
     yield port1.addr.ok.eq(0)  # set !ok
 
 
-def pi_ld(port1, addr, datalen):
+def pi_ld(port1, addr, datalen, msr_pr=0):
 
     # have to wait until not busy
     yield from wait_busy(port1, no=False)    # wait until not busy
@@ -70,6 +71,7 @@ def pi_ld(port1, addr, datalen):
     # set up a LD on the port.  address first:
     yield port1.is_ld_i.eq(1)  # indicate LD
     yield port1.data_len.eq(datalen)  # LD length (1/2/4/8)
+    yield port1.msr_pr.eq(msr_pr)  # MSR PR bit (1==>virt, 0==>real)
 
     yield port1.addr.data.eq(addr)  # set address
     yield port1.addr.ok.eq(1)  # set ok
@@ -87,7 +89,7 @@ def pi_ld(port1, addr, datalen):
     return data
 
 
-def pi_ldst(arg, dut):
+def pi_ldst(arg, dut, msr_pr=0):
 
     # do two half-word stores at consecutive addresses, then two loads
     addr1 = 0x04
@@ -95,16 +97,16 @@ def pi_ldst(arg, dut):
     data = 0xbeef
     data2 = 0xf00f
     #data = 0x4
-    yield from pi_st(dut, addr1, data, 2)
-    yield from pi_st(dut, addr2, data2, 2)
-    result = yield from pi_ld(dut, addr1, 2)
-    result2 = yield from pi_ld(dut, addr2, 2)
+    yield from pi_st(dut, addr1, data, 2, msr_pr)
+    yield from pi_st(dut, addr2, data2, 2, msr_pr)
+    result = yield from pi_ld(dut, addr1, 2, msr_pr)
+    result2 = yield from pi_ld(dut, addr2, 2, msr_pr)
     arg.assertEqual(data, result, "data %x != %x" % (result, data))
     arg.assertEqual(data2, result2, "data2 %x != %x" % (result2, data2))
 
     # now load both in a 32-bit load to make sure they're really consecutive
     data3 = data | (data2 << 16)
-    result3 = yield from pi_ld(dut, addr1, 4)
+    result3 = yield from pi_ld(dut, addr1, 4, msr_pr)
     arg.assertEqual(data3, result3, "data3 %x != %x" % (result3, data3))
 
 
diff --git a/src/soc/experiment/test/test_ldst_pi.py b/src/soc/experiment/test/test_ldst_pi.py
new file mode 100644 (file)
index 0000000..82f5648
--- /dev/null
@@ -0,0 +1,176 @@
+"""MMU PortInterface Test
+
+quite basic, goes directly to the MMU to assert signals (does not
+yet use PortInterface)
+"""
+
+from nmigen import (C, Module, Signal, Elaboratable, Mux, Cat, Repl, Signal)
+from nmigen.cli import main
+from nmigen.cli import rtlil
+from nmutil.mask import Mask, masked
+from nmutil.util import Display
+
+if True:
+    from nmigen.back.pysim import Simulator, Delay, Settle
+else:
+    from nmigen.sim.cxxsim import Simulator, Delay, Settle
+from nmutil.util import wrap
+
+from soc.config.test.test_pi2ls import pi_ld, pi_st, pi_ldst
+from soc.config.test.test_loadstore import TestMemPspec
+from soc.config.loadstore import ConfigMemoryPortInterface
+
+from soc.fu.ldst.loadstore import LoadStore1
+from soc.experiment.mmu import MMU
+
+from nmigen.compat.sim import run_simulation
+
+
+stop = False
+
+def wb_get(wb):
+    """simulator process for getting memory load requests
+    """
+
+    global stop
+
+    def b(x):
+        return int.from_bytes(x.to_bytes(8, byteorder='little'),
+                              byteorder='big', signed=False)
+
+    mem = {0x10000:    # PARTITION_TABLE_2
+                       # PATB_GR=1 PRTB=0x1000 PRTS=0xb
+           b(0x800000000100000b),
+
+           0x30000:     # RADIX_ROOT_PTE
+                        # V = 1 L = 0 NLB = 0x400 NLS = 9
+           b(0x8000000000040009),
+
+           0x40000:     # RADIX_SECOND_LEVEL
+                        #         V = 1 L = 1 SW = 0 RPN = 0
+                           # R = 1 C = 1 ATT = 0 EAA 0x7
+           b(0xc000000000000187),
+
+          0x1000000:   # PROCESS_TABLE_3
+                       # RTS1 = 0x2 RPDB = 0x300 RTS2 = 0x5 RPDS = 13
+           b(0x40000000000300ad),
+          }
+
+    while not stop:
+        while True: # wait for dc_valid
+            if stop:
+                return
+            cyc = yield (wb.cyc)
+            stb = yield (wb.stb)
+            if cyc and stb:
+                break
+            yield
+        addr = (yield wb.adr) << 3
+        if addr not in mem:
+            print ("    WB LOOKUP NO entry @ %x, returning zero" % (addr))
+
+        data = mem.get(addr, 0)
+        yield wb.dat_r.eq(data)
+        print ("    DCACHE get %x data %x" % (addr, data))
+        yield wb.ack.eq(1)
+        yield
+        yield wb.ack.eq(0)
+
+
+def mmu_lookup(dut, addr):
+    mmu = dut.submodules.mmu
+    global stop
+
+    print("pi_ld")
+    yield from pi_ld(dut.submodules.ldst.pi, addr, 1, msr_pr=1)
+    print("pi_ld done")
+    """
+    # original test code kept for reference
+    while not stop: # wait for dc_valid / err
+        print("waiting for mmu")
+        l_done = yield (mmu.l_out.done)
+        l_err = yield (mmu.l_out.err)
+        l_badtree = yield (mmu.l_out.badtree)
+        l_permerr = yield (mmu.l_out.perm_error)
+        l_rc_err = yield (mmu.l_out.rc_error)
+        l_segerr = yield (mmu.l_out.segerr)
+        l_invalid = yield (mmu.l_out.invalid)
+        if (l_done or l_err or l_badtree or
+            l_permerr or l_rc_err or l_segerr or l_invalid):
+            break
+        yield
+    """
+    phys_addr = yield mmu.d_out.addr
+    pte = yield mmu.d_out.pte
+    l_done = yield (mmu.l_out.done)
+    l_err = yield (mmu.l_out.err)
+    l_badtree = yield (mmu.l_out.badtree)
+    print ("translated done %d err %d badtree %d addr %x pte %x" % \
+               (l_done, l_err, l_badtree, phys_addr, pte))
+    yield
+    yield mmu.l_in.valid.eq(0)
+
+    return phys_addr
+
+
+def ldst_sim(dut):
+    mmu = dut.submodules.mmu
+    global stop
+    yield mmu.rin.prtbl.eq(0x1000000) # set process table
+    yield
+
+    addr = 0x10000
+    data = 0
+    print("pi_st")
+
+    # TODO mmu_lookup using port interface
+    # set inputs 
+    phys_addr = yield from mmu_lookup(dut, 0x10000)
+    assert phys_addr == 0x40000
+
+    phys_addr = yield from mmu_lookup(dut, 0x10000)
+    assert phys_addr == 0x40000
+
+    stop = True
+
+
+def test_mmu():
+
+    pspec = TestMemPspec(ldst_ifacetype='mmu_cache_wb',
+                         imem_ifacetype='',
+                         addr_wid=48,
+                         mask_wid=8,
+                         reg_wid=64)
+
+    m = Module()
+    comb = m.d.comb
+    cmpi = ConfigMemoryPortInterface(pspec)
+    m.submodules.ldst = ldst = cmpi.pi
+    m.submodules.mmu = mmu = MMU()
+    dcache = ldst.dcache
+
+    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 mmu and dcache together
+    m.d.comb += dcache.m_in.eq(mmu.d_out) # MMUToDCacheType
+    m.d.comb += mmu.d_in.eq(dcache.m_out) # DCacheToMMUType
+
+    # link ldst and MMU together
+    comb += l_in.eq(ldst.m_out)
+    comb += ldst.m_in.eq(l_out)
+
+
+    # nmigen Simulation
+    sim = Simulator(m)
+    sim.add_clock(1e-6)
+
+    sim.add_sync_process(wrap(ldst_sim(m)))
+    sim.add_sync_process(wrap(wb_get(cmpi.wb_bus())))
+    with sim.write_vcd('test_ldst_pi.vcd'):
+        sim.run()
+
+
+if __name__ == '__main__':
+    test_mmu()
index c7732a111a5be0d103b01633e24f4b3185850e56..e5815b16639d3d86e44be87f3a22201b06a9d931 100644 (file)
@@ -160,7 +160,8 @@ class LoadStore1(PortInterfaceBase):
         m.submodules.dcache = dcache = self.dcache
 
         # temp vars
-        d_out, d_in, m_in, dbus = self.d_out, self.d_in, self.m_in, self.dbus
+        d_out, d_in, dbus = self.d_out, self.d_in, self.dbus
+        m_out, m_in = self.m_out, self.m_in
         exc = self.pi.exc_o
         exception = exc.happened
         mmureq = Signal()
@@ -277,25 +278,25 @@ class LoadStore1(PortInterfaceBase):
         m.d.comb += d_out.byte_sel.eq(self.byte_sel)
         m.d.comb += d_out.addr.eq(self.addr)
         m.d.comb += d_out.nc.eq(self.nc)
+        m.d.comb += d_out.priv_mode.eq(self.priv_mode)
+        m.d.comb += d_out.virt_mode.eq(self.virt_mode)
 
         # XXX these should be possible to remove but for some reason
         # cannot be... yet. TODO, investigate
         m.d.comb += self.done.eq(d_in.valid)
         m.d.comb += self.load_data.eq(d_in.data)
 
-        ''' TODO: translate to nmigen.
-        -- Update outputs to MMU
-        m_out.valid <= mmureq;
-        m_out.iside <= v.instr_fault;
-        m_out.load <= r.load;
+        # Update outputs to MMU
+        m.d.comb += m_out.valid.eq(mmureq)
+        m.d.comb += m_out.iside.eq(self.instr_fault)
+        m.d.comb += m_out.load.eq(self.load)
         # m_out.priv <= r.priv_mode; TODO
-        m_out.tlbie <= v.tlbie;
+        m.d.comb += m_out.tlbie.eq(self.tlbie)
         # m_out.mtspr <= mmu_mtspr; # TODO
         # m_out.sprn <= sprn; # TODO
-        m_out.addr <= maddr;
+        m.d.comb += m_out.addr.eq(maddr)
         # m_out.slbia <= l_in.insn(7); # TODO: no idea what this is
         # m_out.rs <= l_in.data; # nope, probably not needed, TODO investigate
-        '''
 
         return m