typo fix, add missing stop statement to _test_loadstore1_ifetch_multi()
[soc.git] / src / soc / experiment / test / test_loadstore1.py
index 11b60346670f6bd61a34d4744445b9709809c446..cb613d1048280b684f2257f584b99fbf0bf12978 100644 (file)
@@ -1,4 +1,5 @@
-from nmigen import (C, Module, Signal, Elaboratable, Mux, Cat, Repl, Signal)
+from nmigen import (C, Module, Signal, Elaboratable, Mux, Cat, Repl, Signal,
+                    Const)
 from nmigen.cli import main
 from nmigen.cli import rtlil
 from nmutil.mask import Mask, masked
@@ -7,7 +8,8 @@ from random import randint, seed
 from nmigen.sim import Simulator, Delay, Settle
 from nmutil.util import wrap
 
-from soc.config.test.test_pi2ls import pi_ld, pi_st, pi_ldst, wait_busy
+from soc.config.test.test_pi2ls import (pi_ld, pi_st, pi_ldst, wait_busy,
+                                        get_exception_info)
 #from soc.config.test.test_pi2ls import pi_st_debug
 from soc.config.test.test_loadstore import TestMemPspec
 from soc.config.loadstore import ConfigMemoryPortInterface
@@ -18,59 +20,14 @@ from soc.experiment.test import pagetables
 
 from nmigen.compat.sim import run_simulation
 from random import random
+from openpower.test.wb_get import wb_get
+from openpower.test import wb_get as wbget
+from openpower.exceptions import LDSTExceptionTuple
 
-stop = False
-
-def wb_get(wb, mem):
-    """simulator process for getting memory load requests
-    """
-
-    global stop
-    assert(stop==False)
-
-    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))
-
-        # read or write?
-        we = (yield wb.we)
-        if we:
-            store = (yield wb.dat_w)
-            sel = (yield wb.sel)
-            data = mem.get(addr, 0)
-            # note we assume 8-bit sel, here
-            res = 0
-            for i in range(8):
-                mask = 0xff << (i*8)
-                if sel & (1<<i):
-                    res |= store & mask
-                else:
-                    res |= data & mask
-            mem[addr] = res
-            print ("    DCACHE set %x mask %x data %x" % (addr, sel, res))
-        else:
-            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)
-        yield
 
 def setup_mmu():
 
-    global stop
-    stop = False
+    wbget.stop = False
 
     pspec = TestMemPspec(ldst_ifacetype='mmu_cache_wb',
                          imem_ifacetype='',
@@ -85,50 +42,387 @@ def setup_mmu():
     m.submodules.ldst = ldst = cmpi.pi
     m.submodules.mmu = mmu = MMU()
     dcache = ldst.dcache
+    icache = ldst.icache
 
     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
+    i_in, i_out = icache.i_in, icache.i_out # FetchToICache, ICacheToDecode
 
-    # link mmu and dcache together
+    # link mmu, dcache and icache together
     m.d.comb += dcache.m_in.eq(mmu.d_out) # MMUToDCacheType
+    m.d.comb += icache.m_in.eq(mmu.i_out) # MMUToICacheType
     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)
 
+    # add a debug status Signal: use "msg.str = "blah"
+    # then toggle with yield msg.eq(0); yield msg.eq(1)
+    debug_status = Signal(8, decoder=lambda _ : debug_status.str)
+    m.debug_status = debug_status
+    debug_status.str = ''
+
     return m, cmpi
 
-test_exceptions = False
+
+def icache_read(dut,addr,priv,virt):
+
+    icache = dut.submodules.ldst.icache
+    i_in = icache.i_in
+    i_out  = icache.i_out
+
+    yield i_in.priv_mode.eq(priv)
+    yield i_in.virt_mode.eq(virt)
+    yield i_in.req.eq(1)
+    yield i_in.nia.eq(addr)
+    yield i_in.stop_mark.eq(0)
+
+    yield i_in.req.eq(1)
+    yield i_in.nia.eq(addr)
+    yield
+    valid = yield i_out.valid
+    failed = yield i_out.fetch_failed
+    while not valid and not failed:
+        yield
+        valid = yield i_out.valid
+        failed = yield i_out.fetch_failed
+    yield i_in.req.eq(0)
+
+    nia   = yield i_out.nia
+    insn  = yield i_out.insn
+    yield
+    yield
+
+    return nia, insn, valid, failed
+
+
+test_exceptions = True
 test_dcbz = True
 test_random = True
 
+
+def debug(dut, msg):
+    print ("set debug message", msg)
+    dut.debug_status.str = msg # set the message
+    yield dut.debug_status.eq(0) # trigger an update
+    yield dut.debug_status.eq(1)
+
+
+def _test_loadstore1_ifetch_iface(dut, mem):
+    """test_loadstore1_ifetch_iface
+
+    read in priv mode, non-virtual.  tests the FetchUnitInterface
+
+    """
+
+    mmu = dut.submodules.mmu
+    ldst = dut.submodules.ldst
+    pi = ldst.pi
+    icache = dut.submodules.ldst.icache
+    wbget.stop = False
+
+
+    print("=== test loadstore instruction (real) ===")
+
+    i_in = icache.i_in
+    i_out  = icache.i_out
+    i_m_in = icache.m_in
+
+    yield from debug(dut, "real mem instruction")
+    # set address to zero, update mem[0] to 01234
+    addr = 8
+    expected_insn = 0x1234
+    mem[addr] = expected_insn
+
+    yield i_in.priv_mode.eq(1)
+    yield i_in.req.eq(0)  # NO, must use FetchUnitInterface
+    yield i_in.nia.eq(addr)  # NO, must use FetchUnitInterface
+    yield i_in.stop_mark.eq(0)  # NO, must use FetchUnitInterface
+    yield i_m_in.tlbld.eq(0)
+    yield i_m_in.tlbie.eq(0)
+    yield i_m_in.addr.eq(0)
+    yield i_m_in.pte.eq(0)
+    yield
+    yield
+    yield
+
+    # miss, stalls for a bit -- this one is different here
+    ##nia, insn, valid, failed = yield from icache_read(dut,addr,0,0)
+    ##assert(valid==0)
+    ##assert(failed==1)
+
+    yield i_in.req.eq(1)  # NO, must use FetchUnitInterface
+    yield i_in.nia.eq(addr)  # NO, must use FetchUnitInterface
+    yield
+    valid = yield i_out.valid  # NO, must use FetchUnitInterface
+    while not valid:  # NO, must use FetchUnitInterface
+        yield  # NO, must use FetchUnitInterface
+        valid = yield i_out.valid  # NO, must use FetchUnitInterface
+    yield i_in.req.eq(0)  # NO, must use FetchUnitInterface
+
+    nia   = yield i_out.nia  # NO, must use FetchUnitInterface
+    insn  = yield i_out.insn  # NO, must use FetchUnitInterface
+    yield
+    yield
+
+    print ("fetched %x from addr %x" % (insn, nia))
+    assert insn == expected_insn
+
+    print("=== test loadstore instruction (done) ===")
+
+    yield from debug(dut, "test done")
+    yield
+    yield
+
+    print ("failed?", "yes" if failed else "no")
+    assert failed == 0
+
+    print ("fetched %x from addr %x" % (insn, nia))
+    assert insn == expected_insn
+
+    wbget.stop = True
+
+def _test_loadstore1_ifetch_multi(dut, mem):
+    yield from debug(dut, "TODO")
+    yield
+    yield
+    yield
+    # TODO fetch instructions from multiple addresses
+    # should cope with some addresses being invalid
+    addrs = [0x10200,0x10204,10208,10200]
+    for addr in addrs:
+        yield from debug(dut, "TODO_fetch_from "+hex(addr))
+        # use the new interface in this test
+        yield
+        yield
+        yield
+
+    wbget.stop = True
+
+def _test_loadstore1_ifetch(dut, mem):
+    """test_loadstore1_ifetch
+
+    this is quite a complex multi-step test.
+
+    * first (just because, as a demo) read in priv mode, non-virtual.
+      just like in experiment/icache.py itself.
+
+    * second, using the (usual) PTE for these things (which came originally
+      from gem5-experimental experiment/radix_walk_example.txt) do a
+      virtual-memory read through the *instruction* cache.
+      this is expected to FAIL
+
+    * third: mess about with the MMU, setting "iside" (instruction-side),
+      requesting an MMU RADIX LOOKUP.  this triggers an itlb_load
+      (instruction-cache TLB entry-insertion)
+
+    * fourth and finally: retry the read of the instruction through i-cache.
+      this is now expected to SUCCEED
+
+    a lot going on.
+    """
+
+    mmu = dut.submodules.mmu
+    ldst = dut.submodules.ldst
+    pi = ldst.pi
+    icache = dut.submodules.ldst.icache
+    wbget.stop = False
+
+    print("=== test loadstore instruction (real) ===")
+
+    i_in = icache.i_in
+    i_out  = icache.i_out
+    i_m_in = icache.m_in
+
+    # first virtual memory test
+
+    print ("set process table")
+    yield from debug(dut, "set prtble")
+    yield mmu.rin.prtbl.eq(0x1000000) # set process table
+    yield
+
+    yield from debug(dut, "real mem instruction")
+    # set address to zero, update mem[0] to 01234
+    addr = 8
+    expected_insn = 0x1234
+    mem[addr] = expected_insn
+
+    yield i_in.priv_mode.eq(1)
+    yield i_in.req.eq(0)
+    yield i_in.nia.eq(addr)
+    yield i_in.stop_mark.eq(0)
+    yield i_m_in.tlbld.eq(0)
+    yield i_m_in.tlbie.eq(0)
+    yield i_m_in.addr.eq(0)
+    yield i_m_in.pte.eq(0)
+    yield
+    yield
+    yield
+
+    # miss, stalls for a bit -- this one is different here
+    ##nia, insn, valid, failed = yield from icache_read(dut,addr,0,0)
+    ##assert(valid==0)
+    ##assert(failed==1)
+
+    yield i_in.req.eq(1)
+    yield i_in.nia.eq(addr)
+    yield
+    valid = yield i_out.valid
+    while not valid:
+        yield
+        valid = yield i_out.valid
+    yield i_in.req.eq(0)
+
+    nia   = yield i_out.nia
+    insn  = yield i_out.insn
+    yield
+    yield
+
+    print ("fetched %x from addr %x" % (insn, nia))
+    assert insn == expected_insn
+
+    print("=== test loadstore instruction (virtual) ===")
+
+    # look up i-cache expecting it to fail
+
+    yield from debug(dut, "virtual instr req")
+    # set address to 0x10200, update mem[] to 5678
+    virt_addr = 0x10200
+    real_addr = virt_addr
+    expected_insn = 0x5678
+    mem[real_addr] = expected_insn
+
+    yield i_in.priv_mode.eq(0)
+    yield i_in.virt_mode.eq(1)
+    yield i_in.req.eq(0)
+    yield i_in.nia.eq(virt_addr)
+    yield i_in.stop_mark.eq(0)
+    yield i_m_in.tlbld.eq(0)
+    yield i_m_in.tlbie.eq(0)
+    yield i_m_in.addr.eq(0)
+    yield i_m_in.pte.eq(0)
+    yield
+    yield
+    yield
+
+    # miss, stalls for a bit
+    yield i_in.req.eq(1)
+    yield i_in.nia.eq(virt_addr)
+    yield
+    valid = yield i_out.valid
+    failed = yield i_out.fetch_failed
+    while not valid and not failed:
+        yield
+        valid = yield i_out.valid
+        failed = yield i_out.fetch_failed
+    yield i_in.req.eq(0)
+
+    print ("failed?", "yes" if failed else "no")
+    assert failed == 1
+    yield
+    yield
+
+    print("=== test loadstore instruction (instruction fault) ===")
+
+    yield from debug(dut, "instr fault")
+
+    virt_addr = 0x10200
+
+    yield ldst.priv_mode.eq(0)
+    yield ldst.instr_fault.eq(1)
+    yield ldst.maddr.eq(virt_addr)
+    #ld_data, exctype, exc = yield from pi_ld(pi, virt_addr, 8, msr_pr=1)
+    yield
+    yield ldst.instr_fault.eq(0)
+    while True:
+        done = yield (ldst.done)
+        exc_info = yield from get_exception_info(pi.exc_o)
+        if done or exc_info.happened:
+            break
+        yield
+    assert exc_info.happened == 0 # assert just before doing the fault set zero
+    yield ldst.instr_fault.eq(0)
+    yield
+    yield
+    yield
+
+    print("=== test loadstore instruction (try instruction again) ===")
+    yield from debug(dut, "instr virt retry")
+    # set address to 0x10200, update mem[] to 5678
+    virt_addr = 0x10200
+    real_addr = virt_addr
+    expected_insn = 0x5678
+
+    yield i_in.priv_mode.eq(0)
+    yield i_in.virt_mode.eq(1)
+    yield i_in.req.eq(0)
+    yield i_in.nia.eq(virt_addr)
+    yield i_in.stop_mark.eq(0)
+    yield i_m_in.tlbld.eq(0)
+    yield i_m_in.tlbie.eq(0)
+    yield i_m_in.addr.eq(0)
+    yield i_m_in.pte.eq(0)
+    yield
+    yield
+    yield
+
+    # miss, stalls for a bit
+    """
+    yield i_in.req.eq(1)
+    yield i_in.nia.eq(virt_addr)
+    yield
+    valid = yield i_out.valid
+    failed = yield i_out.fetch_failed
+    while not valid and not failed:
+        yield
+        valid = yield i_out.valid
+        failed = yield i_out.fetch_failed
+    yield i_in.req.eq(0)
+    nia   = yield i_out.nia
+    insn  = yield i_out.insn
+    """
+
+    ## part 4
+    nia, insn, valid, failed = yield from icache_read(dut,virt_addr,0,1)
+
+    yield from debug(dut, "test done")
+    yield
+    yield
+
+    print ("failed?", "yes" if failed else "no")
+    assert failed == 0
+
+    print ("fetched %x from addr %x" % (insn, nia))
+    assert insn == expected_insn
+
+    wbget.stop = True
+
+
 def _test_loadstore1_invalid(dut, mem):
     mmu = dut.submodules.mmu
     pi = dut.submodules.ldst.pi
-    global stop
-    stop = False
+    wbget.stop = False
 
     print("=== test invalid ===")
 
     addr = 0
-    ld_data, exc = yield from pi_ld(pi, addr, 8, msr_pr=1)
-    print("ld_data",ld_data,exc)
-    assert(exc=="slow")
-    invalid = yield pi.exc_o.invalid
-    assert(invalid==1)
+    ld_data, exctype, exc = yield from pi_ld(pi, addr, 8, msr_pr=1)
+    print("ld_data", ld_data, exctype, exc)
+    assert (exctype == "slow")
+    invalid = exc.invalid
+    assert (invalid == 1)
 
     print("=== test invalid done ===")
 
-    stop = True
+    wbget.stop = True
 
 
 def _test_loadstore1(dut, mem):
     mmu = dut.submodules.mmu
     pi = dut.submodules.ldst.pi
-    global stop
-    stop = False
+    ldst = dut.submodules.ldst # to get at DAR (NOT part of PortInterface)
+    wbget.stop = False
 
     yield mmu.rin.prtbl.eq(0x1000000) # set process table
     yield
@@ -140,77 +434,302 @@ def _test_loadstore1(dut, mem):
         yield from pi_st(pi, addr, data, 8, msr_pr=1)
         yield
 
-        ld_data, exc = yield from pi_ld(pi, addr, 8, msr_pr=1)
+        ld_data, exctype, exc = yield from pi_ld(pi, addr, 8, msr_pr=1)
         assert ld_data == 0xf553b658ba7e1f51
-        assert exc is None
-        ld_data, exc = yield from pi_ld(pi, addr, 8, msr_pr=1)
+        assert exctype is None
+
+        ld_data, exctype, exc = yield from pi_ld(pi, addr, 8, msr_pr=1)
         assert ld_data == 0xf553b658ba7e1f51
-        assert exc is None
+        assert exctype is None
 
         print("do_dcbz ===============")
         yield from pi_st(pi, addr, data, 8, msr_pr=1, is_dcbz=1)
         print("done_dcbz ===============")
         yield
 
-        ld_data, exc = yield from pi_ld(pi, addr, 8, msr_pr=1)
+        ld_data, exctype, exc = yield from pi_ld(pi, addr, 8, msr_pr=1)
         print("ld_data after dcbz")
         print(ld_data)
         assert ld_data == 0
-        assert exc is None
+        assert exctype is None
 
     if test_exceptions:
         print("=== alignment error (ld) ===")
         addr = 0xFF100e0FF
-        ld_data, exc = yield from pi_ld(pi, addr, 8, msr_pr=1)
-        alignment = yield pi.exc_o.alignment
-        happened = yield pi.exc_o.happened
-        dar = yield pi.dar_o
-        assert(happened==1)
-        assert(alignment==1)
-        assert(dar==addr)
-        assert(exc=="fast")
+        ld_data, exctype, exc = yield from pi_ld(pi, addr, 8, msr_pr=1)
+        if exc:
+            alignment = exc.alignment
+            happened = exc.happened
+            yield # wait for dsr to update
+            dar = yield ldst.dar
+        else:
+            alignment = 0
+            happened = 0
+            dar = 0
+        assert (happened == 1)
+        assert (alignment == 1)
+        assert (dar == addr)
+        assert (exctype == "fast")
         yield from wait_busy(pi, debug="pi_ld_E_alignment_error")
         # wait is only needed in case of in exception here
         print("=== alignment error test passed (ld) ===")
 
+        # take some cycles in between so that gtkwave separates out
+        # signals
+        yield
+        yield
+        yield
+        yield
+
         print("=== alignment error (st) ===")
         addr = 0xFF100e0FF
-        exc = yield from pi_st(pi, addr,0, 8, msr_pr=1)
-        alignment = yield pi.exc_o.alignment
-        happened = yield pi.exc_o.happened
-        dar = yield pi.dar_o
-        assert(happened==1)
-        assert(alignment==1)
-        assert(dar==addr)
-        assert(exc=="fast")
-        yield from wait_busy(pi, debug="pi_st_E_alignment_error")
+        exctype, exc = yield from pi_st(pi, addr,0, 8, msr_pr=1)
+        if exc:
+            alignment = exc.alignment
+            happened = exc.happened
+        else:
+            alignment = 0
+            happened = 0
+        assert (happened == 1)
+        assert (alignment==1)
+        assert (dar==addr)
+        assert (exctype == "fast")
+        #???? yield from wait_busy(pi, debug="pi_st_E_alignment_error")
         # wait is only needed in case of in exception here
         print("=== alignment error test passed (st) ===")
-        yield # IMPORTANT: wait one clock cycle after failed st
+        yield #FIXME hangs
 
-        print("=== no error ===")
+    if True:
+        print("=== no alignment error (ld) ===")
         addr = 0x100e0
-        ld_data, exc = yield from pi_ld(pi, addr, 8, msr_pr=1)
-        print("ld_data",ld_data,exc)
-        print("=== no error done ===")
+        ld_data, exctype, exc = yield from pi_ld(pi, addr, 8, msr_pr=1)
+        print("ld_data", ld_data, exctype, exc)
+        if exc:
+            alignment = exc.alignment
+            happened = exc.happened
+        else:
+            alignment = 0
+            happened = 0
+        assert (happened == 0)
+        assert (alignment == 0)
+        print("=== no alignment error done (ld) ===")
 
     if test_random:
-        addrs = [0x456920,0xa7a180,0x299420,0x1d9d60] # known to cause an error
-        count = 0
+        addrs = [0x456920,0xa7a180,0x299420,0x1d9d60]
 
         for addr in addrs:
             print("== RANDOM addr ==",hex(addr))
-            ld_data, exc = yield from pi_ld(pi, addr, 8, msr_pr=1)
+            ld_data, exctype, exc  = yield from pi_ld(pi, addr, 8, msr_pr=1)
             print("ld_data[RANDOM]",ld_data,exc,addr)
-            #if exc=="wait_ldok_infinite_loop": # break cond for debugging
-            #    print("wait_ldok_infinite_loop:break",count)
-            #    break
-            assert(exc==None)
-            count = count + 1
+            assert (exctype == None)
+
+        for addr in addrs:
+            print("== RANDOM addr ==",hex(addr))
+            exc = yield from pi_st(pi, addr,0xFF*addr, 8, msr_pr=1)
+            assert (exctype == None)
+
+        # readback written data and compare
+        for addr in addrs:
+            print("== RANDOM addr ==",hex(addr))
+            ld_data, exctype, exc = yield from pi_ld(pi, addr, 8, msr_pr=1)
+            print("ld_data[RANDOM_READBACK]",ld_data,exc,addr)
+            assert (exctype == None)
+            assert (ld_data == 0xFF*addr)
 
         print("== RANDOM addr done ==")
 
-    stop = True
+    wbget.stop = True
+
+
+def _test_loadstore1_ifetch_invalid(dut, mem):
+    mmu = dut.submodules.mmu
+    ldst = dut.submodules.ldst
+    pi = ldst.pi
+    icache = dut.submodules.ldst.icache
+    wbget.stop = False
+
+    print("=== test loadstore instruction (invalid) ===")
+
+    i_in = icache.i_in
+    i_out  = icache.i_out
+    i_m_in = icache.m_in
+
+    # first virtual memory test
+
+    print ("set process table")
+    yield from debug(dut, "set prtbl")
+    yield mmu.rin.prtbl.eq(0x1000000) # set process table
+    yield
+
+    yield from debug(dut, "real mem instruction")
+    # set address to zero, update mem[0] to 01234
+    addr = 8
+    expected_insn = 0x1234
+    mem[addr] = expected_insn
+
+    yield i_in.priv_mode.eq(1)
+    yield i_in.req.eq(0)
+    yield i_in.nia.eq(addr)
+    yield i_in.stop_mark.eq(0)
+    yield i_m_in.tlbld.eq(0)
+    yield i_m_in.tlbie.eq(0)
+    yield i_m_in.addr.eq(0)
+    yield i_m_in.pte.eq(0)
+    yield
+    yield
+    yield
+
+    # miss, stalls for a bit
+    yield i_in.req.eq(1)
+    yield i_in.nia.eq(addr)
+    yield
+    valid = yield i_out.valid
+    nia   = yield i_out.nia
+    while not valid:
+        yield
+        valid = yield i_out.valid
+    yield i_in.req.eq(0)
+
+    nia   = yield i_out.nia
+    insn  = yield i_out.insn
+
+    yield
+    yield
+
+    print ("fetched %x from addr %x" % (insn, nia))
+    assert insn == expected_insn
+
+    print("=== test loadstore instruction (virtual) ===")
+    yield from debug(dut, "virtual instr req")
+
+    # look up i-cache expecting it to fail
+
+    # set address to 0x10200, update mem[] to 5678
+    virt_addr = 0x10200
+    real_addr = virt_addr
+    expected_insn = 0x5678
+    mem[real_addr] = expected_insn
+
+    yield i_in.priv_mode.eq(1)
+    yield i_in.virt_mode.eq(1)
+    yield i_in.req.eq(0)
+    yield i_in.nia.eq(virt_addr)
+    yield i_in.stop_mark.eq(0)
+    yield i_m_in.tlbld.eq(0)
+    yield i_m_in.tlbie.eq(0)
+    yield i_m_in.addr.eq(0)
+    yield i_m_in.pte.eq(0)
+    yield
+    yield
+    yield
+
+    # miss, stalls for a bit
+    yield i_in.req.eq(1)
+    yield i_in.nia.eq(virt_addr)
+    yield
+    valid = yield i_out.valid
+    failed = yield i_out.fetch_failed
+    while not valid and not failed:
+        yield
+        valid = yield i_out.valid
+        failed = yield i_out.fetch_failed
+    yield i_in.req.eq(0)
+
+    print ("failed?", "yes" if failed else "no")
+    assert failed == 1
+    yield
+    yield
+
+    print("=== test invalid loadstore instruction (instruction fault) ===")
+
+    yield from debug(dut, "instr fault (perm err expected)")
+    virt_addr = 0x10200
+
+    yield ldst.priv_mode.eq(0)
+    yield ldst.instr_fault.eq(1)
+    yield ldst.maddr.eq(virt_addr)
+    #ld_data, exctype, exc = yield from pi_ld(pi, virt_addr, 8, msr_pr=1)
+    yield
+    yield ldst.instr_fault.eq(0)
+    while True:
+        done = yield (ldst.done)
+        exc_info = yield from get_exception_info(pi.exc_o)
+        if done or exc_info.happened:
+            break
+        yield
+    assert exc_info.happened == 1 # different here as expected
+
+    # TODO: work out what kind of exception occurred and check it's
+    # the right one.  we *expect* it to be a permissions error because
+    # the RPTE leaf node in pagetables.test2 is marked as "non-executable"
+    # but we also expect instr_fault to be set because it is an instruction
+    # (iside) lookup
+    print ("   MMU lookup exception type?")
+    for fname in LDSTExceptionTuple._fields:
+        print ("   fname %20s %d" % (fname, getattr(exc_info, fname)))
+
+    # ok now printed them out and visually inspected: check them with asserts
+    assert exc_info.instr_fault == 1 # instruction fault (yes!)
+    assert exc_info.perm_error == 1 # permissions (yes!)
+    assert exc_info.rc_error == 0
+    assert exc_info.alignment == 0
+    assert exc_info.invalid == 0
+    assert exc_info.segment_fault == 0
+    assert exc_info.rc_error == 0
+
+    yield from debug(dut, "test done")
+    yield ldst.instr_fault.eq(0)
+    yield
+    yield
+    yield
+
+    wbget.stop = True
+
+
+def test_loadstore1_ifetch_unit_iface():
+
+    m, cmpi = setup_mmu()
+
+    mem = pagetables.test1
+
+    # nmigen Simulation
+    sim = Simulator(m)
+    sim.add_clock(1e-6)
+
+    icache = m.submodules.ldst.icache
+    icache.use_fetch_interface() # this is the function which converts
+                                 # to FetchUnitInterface. *including*
+                                 # rewiring the Wishbone Bus to ibus
+    sim.add_sync_process(wrap(_test_loadstore1_ifetch_iface(m, mem)))
+    # add two wb_get processes onto the *same* memory dictionary.
+    # this shouuuld work.... cross-fingers...
+    sim.add_sync_process(wrap(wb_get(cmpi.wb_bus(), mem)))
+    sim.add_sync_process(wrap(wb_get(icache.ibus, mem))) # ibus not bus
+    with sim.write_vcd('test_loadstore1_ifetch_iface.vcd',
+                      traces=[m.debug_status]): # include extra debug
+        sim.run()
+
+
+def test_loadstore1_ifetch():
+
+    m, cmpi = setup_mmu()
+
+    mem = pagetables.test1
+
+    # nmigen Simulation
+    sim = Simulator(m)
+    sim.add_clock(1e-6)
+
+    icache = m.submodules.ldst.icache
+    sim.add_sync_process(wrap(_test_loadstore1_ifetch(m, mem)))
+    # add two wb_get processes onto the *same* memory dictionary.
+    # this shouuuld work.... cross-fingers...
+    sim.add_sync_process(wrap(wb_get(cmpi.wb_bus(), mem)))
+    sim.add_sync_process(wrap(wb_get(icache.bus, mem)))
+    with sim.write_vcd('test_loadstore1_ifetch.vcd',
+                      traces=[m.debug_status]): # include extra debug
+        sim.run()
+
 
 def test_loadstore1():
 
@@ -227,6 +746,7 @@ def test_loadstore1():
     with sim.write_vcd('test_loadstore1.vcd'):
         sim.run()
 
+
 def test_loadstore1_invalid():
 
     m, cmpi = setup_mmu()
@@ -242,6 +762,53 @@ def test_loadstore1_invalid():
     with sim.write_vcd('test_loadstore1_invalid.vcd'):
         sim.run()
 
+def test_loadstore1_ifetch_invalid():
+    m, cmpi = setup_mmu()
+
+    # this is a specially-arranged page table which has the permissions
+    # barred for execute on the leaf node (EAA=0x2 instead of EAA=0x3)
+    mem = pagetables.test2
+
+    # nmigen Simulation
+    sim = Simulator(m)
+    sim.add_clock(1e-6)
+
+    icache = m.submodules.ldst.icache
+    sim.add_sync_process(wrap(_test_loadstore1_ifetch_invalid(m, mem)))
+    # add two wb_get processes onto the *same* memory dictionary.
+    # this shouuuld work.... cross-fingers...
+    sim.add_sync_process(wrap(wb_get(cmpi.wb_bus(), mem)))
+    sim.add_sync_process(wrap(wb_get(icache.bus, mem)))
+    with sim.write_vcd('test_loadstore1_ifetch_invalid.vcd',
+                      traces=[m.debug_status]): # include extra debug
+        sim.run()
+
+def test_loadstore1_ifetch_multi():
+    m, cmpi = setup_mmu()
+
+    # this is a specially-arranged page table which has the permissions
+    # barred for execute on the leaf node (EAA=0x2 instead of EAA=0x3)
+    mem = pagetables.test1
+
+    # nmigen Simulation
+    sim = Simulator(m)
+    sim.add_clock(1e-6)
+
+    icache = m.submodules.ldst.icache
+    sim.add_sync_process(wrap(_test_loadstore1_ifetch_multi(m, mem)))
+    # add two wb_get processes onto the *same* memory dictionary.
+    # this shouuuld work.... cross-fingers...
+    sim.add_sync_process(wrap(wb_get(cmpi.wb_bus(), mem)))
+    sim.add_sync_process(wrap(wb_get(icache.bus, mem)))
+    with sim.write_vcd('test_loadstore1_ifetch_multi.vcd',
+                      traces=[m.debug_status]): # include extra debug
+        sim.run()
+
 if __name__ == '__main__':
     test_loadstore1()
     test_loadstore1_invalid()
+    test_loadstore1_ifetch()
+    test_loadstore1_fetch_unit_iface()
+    test_loadstore1_ifetch_invalid()
+    test_loadstore1_ifetch_multi()
+