1 from nmigen
import (C
, Module
, Signal
, Elaboratable
, Mux
, Cat
, Repl
, Signal
,
3 from nmigen
.cli
import main
4 from nmigen
.cli
import rtlil
5 from nmutil
.mask
import Mask
, masked
6 from nmutil
.util
import Display
7 from random
import randint
, seed
8 from nmigen
.sim
import Simulator
, Delay
, Settle
9 from nmutil
.util
import wrap
11 from soc
.config
.test
.test_pi2ls
import (pi_ld
, pi_st
, pi_ldst
, wait_busy
,
13 #from soc.config.test.test_pi2ls import pi_st_debug
14 from soc
.config
.test
.test_loadstore
import TestMemPspec
15 from soc
.config
.loadstore
import ConfigMemoryPortInterface
17 from soc
.fu
.ldst
.loadstore
import LoadStore1
18 from soc
.experiment
.mmu
import MMU
19 from soc
.experiment
.test
import pagetables
21 from nmigen
.compat
.sim
import run_simulation
22 from random
import random
23 from openpower
.test
.wb_get
import wb_get
24 from openpower
.test
import wb_get
as wbget
25 from openpower
.exceptions
import LDSTExceptionTuple
32 pspec
= TestMemPspec(ldst_ifacetype
='mmu_cache_wb',
35 #disable_cache=True, # hmmm...
41 cmpi
= ConfigMemoryPortInterface(pspec
)
42 m
.submodules
.ldst
= ldst
= cmpi
.pi
43 m
.submodules
.mmu
= mmu
= MMU()
47 l_in
, l_out
= mmu
.l_in
, mmu
.l_out
48 d_in
, d_out
= dcache
.d_in
, dcache
.d_out
49 i_in
, i_out
= icache
.i_in
, icache
.i_out
# FetchToICache, ICacheToDecode
51 # link mmu, dcache and icache together
52 m
.d
.comb
+= dcache
.m_in
.eq(mmu
.d_out
) # MMUToDCacheType
53 m
.d
.comb
+= icache
.m_in
.eq(mmu
.i_out
) # MMUToICacheType
54 m
.d
.comb
+= mmu
.d_in
.eq(dcache
.m_out
) # DCacheToMMUType
56 # link ldst and MMU together
57 comb
+= l_in
.eq(ldst
.m_out
)
58 comb
+= ldst
.m_in
.eq(l_out
)
60 # add a debug status Signal: use "msg.str = "blah"
61 # then toggle with yield msg.eq(0); yield msg.eq(1)
62 debug_status
= Signal(8, decoder
=lambda _
: debug_status
.str)
63 m
.debug_status
= debug_status
69 def icache_read(dut
,addr
,priv
,virt
):
71 icache
= dut
.submodules
.ldst
.icache
75 yield i_in
.priv_mode
.eq(priv
)
76 yield i_in
.virt_mode
.eq(virt
)
78 yield i_in
.nia
.eq(addr
)
79 yield i_in
.stop_mark
.eq(0)
82 yield i_in
.nia
.eq(addr
)
84 valid
= yield i_out
.valid
85 failed
= yield i_out
.fetch_failed
86 while not valid
and not failed
:
88 valid
= yield i_out
.valid
89 failed
= yield i_out
.fetch_failed
93 insn
= yield i_out
.insn
97 return nia
, insn
, valid
, failed
100 test_exceptions
= True
106 print ("set debug message", msg
)
107 dut
.debug_status
.str = msg
# set the message
108 yield dut
.debug_status
.eq(0) # trigger an update
109 yield dut
.debug_status
.eq(1)
112 def _test_loadstore1_ifetch_iface(dut
, mem
):
113 """test_loadstore1_ifetch_iface
115 read in priv mode, non-virtual. tests the FetchUnitInterface
119 mmu
= dut
.submodules
.mmu
120 ldst
= dut
.submodules
.ldst
122 icache
= dut
.submodules
.ldst
.icache
126 print("=== test loadstore instruction (real) ===")
132 yield from debug(dut
, "real mem instruction")
133 # set address to zero, update mem[0] to 01234
135 expected_insn
= 0x1234
136 mem
[addr
] = expected_insn
138 yield i_in
.priv_mode
.eq(1)
139 yield i_in
.req
.eq(0) # NO, must use FetchUnitInterface
140 yield i_in
.nia
.eq(addr
) # NO, must use FetchUnitInterface
141 yield i_in
.stop_mark
.eq(0) # NO, must use FetchUnitInterface
142 yield i_m_in
.tlbld
.eq(0)
143 yield i_m_in
.tlbie
.eq(0)
144 yield i_m_in
.addr
.eq(0)
145 yield i_m_in
.pte
.eq(0)
150 # miss, stalls for a bit -- this one is different here
151 ##nia, insn, valid, failed = yield from icache_read(dut,addr,0,0)
155 yield i_in
.req
.eq(1) # NO, must use FetchUnitInterface
156 yield i_in
.nia
.eq(addr
) # NO, must use FetchUnitInterface
158 valid
= yield i_out
.valid
# NO, must use FetchUnitInterface
159 while not valid
: # NO, must use FetchUnitInterface
160 yield # NO, must use FetchUnitInterface
161 valid
= yield i_out
.valid
# NO, must use FetchUnitInterface
162 yield i_in
.req
.eq(0) # NO, must use FetchUnitInterface
164 nia
= yield i_out
.nia
# NO, must use FetchUnitInterface
165 insn
= yield i_out
.insn
# NO, must use FetchUnitInterface
169 print ("fetched %x from addr %x" % (insn
, nia
))
170 assert insn
== expected_insn
172 print("=== test loadstore instruction (done) ===")
174 yield from debug(dut
, "test done")
178 print ("failed?", "yes" if failed
else "no")
181 print ("fetched %x from addr %x" % (insn
, nia
))
182 assert insn
== expected_insn
186 def _test_loadstore1_ifetch_multi(dut
, mem
):
187 yield from debug(dut
, "TODO")
191 # TODO fetch instructions from multiple addresses
192 # should cope with some addresses being invalid
193 addrs
= [0x10200,0x10204,10208,10200]
195 yield from debug(dut
, "TODO_fetch_from "+hex(addrs
))
196 # use the new interface in this test
201 def _test_loadstore1_ifetch(dut
, mem
):
202 """test_loadstore1_ifetch
204 this is quite a complex multi-step test.
206 * first (just because, as a demo) read in priv mode, non-virtual.
207 just like in experiment/icache.py itself.
209 * second, using the (usual) PTE for these things (which came originally
210 from gem5-experimental experiment/radix_walk_example.txt) do a
211 virtual-memory read through the *instruction* cache.
212 this is expected to FAIL
214 * third: mess about with the MMU, setting "iside" (instruction-side),
215 requesting an MMU RADIX LOOKUP. this triggers an itlb_load
216 (instruction-cache TLB entry-insertion)
218 * fourth and finally: retry the read of the instruction through i-cache.
219 this is now expected to SUCCEED
224 mmu
= dut
.submodules
.mmu
225 ldst
= dut
.submodules
.ldst
227 icache
= dut
.submodules
.ldst
.icache
230 print("=== test loadstore instruction (real) ===")
236 # first virtual memory test
238 print ("set process table")
239 yield from debug(dut
, "set prtble")
240 yield mmu
.rin
.prtbl
.eq(0x1000000) # set process table
243 yield from debug(dut
, "real mem instruction")
244 # set address to zero, update mem[0] to 01234
246 expected_insn
= 0x1234
247 mem
[addr
] = expected_insn
249 yield i_in
.priv_mode
.eq(1)
251 yield i_in
.nia
.eq(addr
)
252 yield i_in
.stop_mark
.eq(0)
253 yield i_m_in
.tlbld
.eq(0)
254 yield i_m_in
.tlbie
.eq(0)
255 yield i_m_in
.addr
.eq(0)
256 yield i_m_in
.pte
.eq(0)
261 # miss, stalls for a bit -- this one is different here
262 ##nia, insn, valid, failed = yield from icache_read(dut,addr,0,0)
267 yield i_in
.nia
.eq(addr
)
269 valid
= yield i_out
.valid
272 valid
= yield i_out
.valid
275 nia
= yield i_out
.nia
276 insn
= yield i_out
.insn
280 print ("fetched %x from addr %x" % (insn
, nia
))
281 assert insn
== expected_insn
283 print("=== test loadstore instruction (virtual) ===")
285 # look up i-cache expecting it to fail
287 yield from debug(dut
, "virtual instr req")
288 # set address to 0x10200, update mem[] to 5678
290 real_addr
= virt_addr
291 expected_insn
= 0x5678
292 mem
[real_addr
] = expected_insn
294 yield i_in
.priv_mode
.eq(0)
295 yield i_in
.virt_mode
.eq(1)
297 yield i_in
.nia
.eq(virt_addr
)
298 yield i_in
.stop_mark
.eq(0)
299 yield i_m_in
.tlbld
.eq(0)
300 yield i_m_in
.tlbie
.eq(0)
301 yield i_m_in
.addr
.eq(0)
302 yield i_m_in
.pte
.eq(0)
307 # miss, stalls for a bit
309 yield i_in
.nia
.eq(virt_addr
)
311 valid
= yield i_out
.valid
312 failed
= yield i_out
.fetch_failed
313 while not valid
and not failed
:
315 valid
= yield i_out
.valid
316 failed
= yield i_out
.fetch_failed
319 print ("failed?", "yes" if failed
else "no")
324 print("=== test loadstore instruction (instruction fault) ===")
326 yield from debug(dut
, "instr fault")
330 yield ldst
.priv_mode
.eq(0)
331 yield ldst
.instr_fault
.eq(1)
332 yield ldst
.maddr
.eq(virt_addr
)
333 #ld_data, exctype, exc = yield from pi_ld(pi, virt_addr, 8, msr_pr=1)
335 yield ldst
.instr_fault
.eq(0)
337 done
= yield (ldst
.done
)
338 exc_info
= yield from get_exception_info(pi
.exc_o
)
339 if done
or exc_info
.happened
:
342 assert exc_info
.happened
== 0 # assert just before doing the fault set zero
343 yield ldst
.instr_fault
.eq(0)
348 print("=== test loadstore instruction (try instruction again) ===")
349 yield from debug(dut
, "instr virt retry")
350 # set address to 0x10200, update mem[] to 5678
352 real_addr
= virt_addr
353 expected_insn
= 0x5678
355 yield i_in
.priv_mode
.eq(0)
356 yield i_in
.virt_mode
.eq(1)
358 yield i_in
.nia
.eq(virt_addr
)
359 yield i_in
.stop_mark
.eq(0)
360 yield i_m_in
.tlbld
.eq(0)
361 yield i_m_in
.tlbie
.eq(0)
362 yield i_m_in
.addr
.eq(0)
363 yield i_m_in
.pte
.eq(0)
368 # miss, stalls for a bit
371 yield i_in.nia.eq(virt_addr)
373 valid = yield i_out.valid
374 failed = yield i_out.fetch_failed
375 while not valid and not failed:
377 valid = yield i_out.valid
378 failed = yield i_out.fetch_failed
380 nia = yield i_out.nia
381 insn = yield i_out.insn
385 nia
, insn
, valid
, failed
= yield from icache_read(dut
,virt_addr
,0,1)
387 yield from debug(dut
, "test done")
391 print ("failed?", "yes" if failed
else "no")
394 print ("fetched %x from addr %x" % (insn
, nia
))
395 assert insn
== expected_insn
400 def _test_loadstore1_invalid(dut
, mem
):
401 mmu
= dut
.submodules
.mmu
402 pi
= dut
.submodules
.ldst
.pi
405 print("=== test invalid ===")
408 ld_data
, exctype
, exc
= yield from pi_ld(pi
, addr
, 8, msr_pr
=1)
409 print("ld_data", ld_data
, exctype
, exc
)
410 assert (exctype
== "slow")
411 invalid
= exc
.invalid
412 assert (invalid
== 1)
414 print("=== test invalid done ===")
419 def _test_loadstore1(dut
, mem
):
420 mmu
= dut
.submodules
.mmu
421 pi
= dut
.submodules
.ldst
.pi
422 ldst
= dut
.submodules
.ldst
# to get at DAR (NOT part of PortInterface)
425 yield mmu
.rin
.prtbl
.eq(0x1000000) # set process table
429 data
= 0xf553b658ba7e1f51
432 yield from pi_st(pi
, addr
, data
, 8, msr_pr
=1)
435 ld_data
, exctype
, exc
= yield from pi_ld(pi
, addr
, 8, msr_pr
=1)
436 assert ld_data
== 0xf553b658ba7e1f51
437 assert exctype
is None
439 ld_data
, exctype
, exc
= yield from pi_ld(pi
, addr
, 8, msr_pr
=1)
440 assert ld_data
== 0xf553b658ba7e1f51
441 assert exctype
is None
443 print("do_dcbz ===============")
444 yield from pi_st(pi
, addr
, data
, 8, msr_pr
=1, is_dcbz
=1)
445 print("done_dcbz ===============")
448 ld_data
, exctype
, exc
= yield from pi_ld(pi
, addr
, 8, msr_pr
=1)
449 print("ld_data after dcbz")
452 assert exctype
is None
455 print("=== alignment error (ld) ===")
457 ld_data
, exctype
, exc
= yield from pi_ld(pi
, addr
, 8, msr_pr
=1)
459 alignment
= exc
.alignment
460 happened
= exc
.happened
461 yield # wait for dsr to update
467 assert (happened
== 1)
468 assert (alignment
== 1)
470 assert (exctype
== "fast")
471 yield from wait_busy(pi
, debug
="pi_ld_E_alignment_error")
472 # wait is only needed in case of in exception here
473 print("=== alignment error test passed (ld) ===")
475 # take some cycles in between so that gtkwave separates out
482 print("=== alignment error (st) ===")
484 exctype
, exc
= yield from pi_st(pi
, addr
,0, 8, msr_pr
=1)
486 alignment
= exc
.alignment
487 happened
= exc
.happened
491 assert (happened
== 1)
492 assert (alignment
==1)
494 assert (exctype
== "fast")
495 #???? yield from wait_busy(pi, debug="pi_st_E_alignment_error")
496 # wait is only needed in case of in exception here
497 print("=== alignment error test passed (st) ===")
501 print("=== no alignment error (ld) ===")
503 ld_data
, exctype
, exc
= yield from pi_ld(pi
, addr
, 8, msr_pr
=1)
504 print("ld_data", ld_data
, exctype
, exc
)
506 alignment
= exc
.alignment
507 happened
= exc
.happened
511 assert (happened
== 0)
512 assert (alignment
== 0)
513 print("=== no alignment error done (ld) ===")
516 addrs
= [0x456920,0xa7a180,0x299420,0x1d9d60]
519 print("== RANDOM addr ==",hex(addr
))
520 ld_data
, exctype
, exc
= yield from pi_ld(pi
, addr
, 8, msr_pr
=1)
521 print("ld_data[RANDOM]",ld_data
,exc
,addr
)
522 assert (exctype
== None)
525 print("== RANDOM addr ==",hex(addr
))
526 exc
= yield from pi_st(pi
, addr
,0xFF*addr
, 8, msr_pr
=1)
527 assert (exctype
== None)
529 # readback written data and compare
531 print("== RANDOM addr ==",hex(addr
))
532 ld_data
, exctype
, exc
= yield from pi_ld(pi
, addr
, 8, msr_pr
=1)
533 print("ld_data[RANDOM_READBACK]",ld_data
,exc
,addr
)
534 assert (exctype
== None)
535 assert (ld_data
== 0xFF*addr
)
537 print("== RANDOM addr done ==")
542 def _test_loadstore1_ifetch_invalid(dut
, mem
):
543 mmu
= dut
.submodules
.mmu
544 ldst
= dut
.submodules
.ldst
546 icache
= dut
.submodules
.ldst
.icache
549 print("=== test loadstore instruction (invalid) ===")
555 # first virtual memory test
557 print ("set process table")
558 yield from debug(dut
, "set prtbl")
559 yield mmu
.rin
.prtbl
.eq(0x1000000) # set process table
562 yield from debug(dut
, "real mem instruction")
563 # set address to zero, update mem[0] to 01234
565 expected_insn
= 0x1234
566 mem
[addr
] = expected_insn
568 yield i_in
.priv_mode
.eq(1)
570 yield i_in
.nia
.eq(addr
)
571 yield i_in
.stop_mark
.eq(0)
572 yield i_m_in
.tlbld
.eq(0)
573 yield i_m_in
.tlbie
.eq(0)
574 yield i_m_in
.addr
.eq(0)
575 yield i_m_in
.pte
.eq(0)
580 # miss, stalls for a bit
582 yield i_in
.nia
.eq(addr
)
584 valid
= yield i_out
.valid
585 nia
= yield i_out
.nia
588 valid
= yield i_out
.valid
591 nia
= yield i_out
.nia
592 insn
= yield i_out
.insn
597 print ("fetched %x from addr %x" % (insn
, nia
))
598 assert insn
== expected_insn
600 print("=== test loadstore instruction (virtual) ===")
601 yield from debug(dut
, "virtual instr req")
603 # look up i-cache expecting it to fail
605 # set address to 0x10200, update mem[] to 5678
607 real_addr
= virt_addr
608 expected_insn
= 0x5678
609 mem
[real_addr
] = expected_insn
611 yield i_in
.priv_mode
.eq(1)
612 yield i_in
.virt_mode
.eq(1)
614 yield i_in
.nia
.eq(virt_addr
)
615 yield i_in
.stop_mark
.eq(0)
616 yield i_m_in
.tlbld
.eq(0)
617 yield i_m_in
.tlbie
.eq(0)
618 yield i_m_in
.addr
.eq(0)
619 yield i_m_in
.pte
.eq(0)
624 # miss, stalls for a bit
626 yield i_in
.nia
.eq(virt_addr
)
628 valid
= yield i_out
.valid
629 failed
= yield i_out
.fetch_failed
630 while not valid
and not failed
:
632 valid
= yield i_out
.valid
633 failed
= yield i_out
.fetch_failed
636 print ("failed?", "yes" if failed
else "no")
641 print("=== test invalid loadstore instruction (instruction fault) ===")
643 yield from debug(dut
, "instr fault (perm err expected)")
646 yield ldst
.priv_mode
.eq(0)
647 yield ldst
.instr_fault
.eq(1)
648 yield ldst
.maddr
.eq(virt_addr
)
649 #ld_data, exctype, exc = yield from pi_ld(pi, virt_addr, 8, msr_pr=1)
651 yield ldst
.instr_fault
.eq(0)
653 done
= yield (ldst
.done
)
654 exc_info
= yield from get_exception_info(pi
.exc_o
)
655 if done
or exc_info
.happened
:
658 assert exc_info
.happened
== 1 # different here as expected
660 # TODO: work out what kind of exception occurred and check it's
661 # the right one. we *expect* it to be a permissions error because
662 # the RPTE leaf node in pagetables.test2 is marked as "non-executable"
663 # but we also expect instr_fault to be set because it is an instruction
665 print (" MMU lookup exception type?")
666 for fname
in LDSTExceptionTuple
._fields
:
667 print (" fname %20s %d" % (fname
, getattr(exc_info
, fname
)))
669 # ok now printed them out and visually inspected: check them with asserts
670 assert exc_info
.instr_fault
== 1 # instruction fault (yes!)
671 assert exc_info
.perm_error
== 1 # permissions (yes!)
672 assert exc_info
.rc_error
== 0
673 assert exc_info
.alignment
== 0
674 assert exc_info
.invalid
== 0
675 assert exc_info
.segment_fault
== 0
676 assert exc_info
.rc_error
== 0
678 yield from debug(dut
, "test done")
679 yield ldst
.instr_fault
.eq(0)
687 def test_loadstore1_ifetch_unit_iface():
689 m
, cmpi
= setup_mmu()
691 mem
= pagetables
.test1
697 icache
= m
.submodules
.ldst
.icache
698 icache
.use_fetch_interface() # this is the function which converts
699 # to FetchUnitInterface. *including*
700 # rewiring the Wishbone Bus to ibus
701 sim
.add_sync_process(wrap(_test_loadstore1_ifetch_iface(m
, mem
)))
702 # add two wb_get processes onto the *same* memory dictionary.
703 # this shouuuld work.... cross-fingers...
704 sim
.add_sync_process(wrap(wb_get(cmpi
.wb_bus(), mem
)))
705 sim
.add_sync_process(wrap(wb_get(icache
.ibus
, mem
))) # ibus not bus
706 with sim
.write_vcd('test_loadstore1_ifetch_iface.vcd',
707 traces
=[m
.debug_status
]): # include extra debug
711 def test_loadstore1_ifetch():
713 m
, cmpi
= setup_mmu()
715 mem
= pagetables
.test1
721 icache
= m
.submodules
.ldst
.icache
722 sim
.add_sync_process(wrap(_test_loadstore1_ifetch(m
, mem
)))
723 # add two wb_get processes onto the *same* memory dictionary.
724 # this shouuuld work.... cross-fingers...
725 sim
.add_sync_process(wrap(wb_get(cmpi
.wb_bus(), mem
)))
726 sim
.add_sync_process(wrap(wb_get(icache
.bus
, mem
)))
727 with sim
.write_vcd('test_loadstore1_ifetch.vcd',
728 traces
=[m
.debug_status
]): # include extra debug
732 def test_loadstore1():
734 m
, cmpi
= setup_mmu()
736 mem
= pagetables
.test1
742 sim
.add_sync_process(wrap(_test_loadstore1(m
, mem
)))
743 sim
.add_sync_process(wrap(wb_get(cmpi
.wb_bus(), mem
)))
744 with sim
.write_vcd('test_loadstore1.vcd'):
748 def test_loadstore1_invalid():
750 m
, cmpi
= setup_mmu()
758 sim
.add_sync_process(wrap(_test_loadstore1_invalid(m
, mem
)))
759 sim
.add_sync_process(wrap(wb_get(cmpi
.wb_bus(), mem
)))
760 with sim
.write_vcd('test_loadstore1_invalid.vcd'):
763 def test_loadstore1_ifetch_invalid():
764 m
, cmpi
= setup_mmu()
766 # this is a specially-arranged page table which has the permissions
767 # barred for execute on the leaf node (EAA=0x2 instead of EAA=0x3)
768 mem
= pagetables
.test2
774 icache
= m
.submodules
.ldst
.icache
775 sim
.add_sync_process(wrap(_test_loadstore1_ifetch_invalid(m
, mem
)))
776 # add two wb_get processes onto the *same* memory dictionary.
777 # this shouuuld work.... cross-fingers...
778 sim
.add_sync_process(wrap(wb_get(cmpi
.wb_bus(), mem
)))
779 sim
.add_sync_process(wrap(wb_get(icache
.bus
, mem
)))
780 with sim
.write_vcd('test_loadstore1_ifetch_invalid.vcd',
781 traces
=[m
.debug_status
]): # include extra debug
784 def test_loadstore1_ifetch_multi():
785 m
, cmpi
= setup_mmu()
787 # this is a specially-arranged page table which has the permissions
788 # barred for execute on the leaf node (EAA=0x2 instead of EAA=0x3)
789 mem
= pagetables
.test1
795 icache
= m
.submodules
.ldst
.icache
796 sim
.add_sync_process(wrap(_test_loadstore1_ifetch_multi(m
, mem
)))
797 # add two wb_get processes onto the *same* memory dictionary.
798 # this shouuuld work.... cross-fingers...
799 sim
.add_sync_process(wrap(wb_get(cmpi
.wb_bus(), mem
)))
800 sim
.add_sync_process(wrap(wb_get(icache
.bus
, mem
)))
801 with sim
.write_vcd('test_loadstore1_ifetch_multi.vcd',
802 traces
=[m
.debug_status
]): # include extra debug
805 if __name__
== '__main__':
807 test_loadstore1_invalid()
808 test_loadstore1_ifetch()
809 test_loadstore1_fetch_unit_iface()
810 test_loadstore1_ifetch_invalid()
811 test_loadstore1_ifetch_multi()