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(addr
))
196 # use the new interface in this test
203 def _test_loadstore1_ifetch(dut
, mem
):
204 """test_loadstore1_ifetch
206 this is quite a complex multi-step test.
208 * first (just because, as a demo) read in priv mode, non-virtual.
209 just like in experiment/icache.py itself.
211 * second, using the (usual) PTE for these things (which came originally
212 from gem5-experimental experiment/radix_walk_example.txt) do a
213 virtual-memory read through the *instruction* cache.
214 this is expected to FAIL
216 * third: mess about with the MMU, setting "iside" (instruction-side),
217 requesting an MMU RADIX LOOKUP. this triggers an itlb_load
218 (instruction-cache TLB entry-insertion)
220 * fourth and finally: retry the read of the instruction through i-cache.
221 this is now expected to SUCCEED
226 mmu
= dut
.submodules
.mmu
227 ldst
= dut
.submodules
.ldst
229 icache
= dut
.submodules
.ldst
.icache
232 print("=== test loadstore instruction (real) ===")
238 # first virtual memory test
240 print ("set process table")
241 yield from debug(dut
, "set prtble")
242 yield mmu
.rin
.prtbl
.eq(0x1000000) # set process table
245 yield from debug(dut
, "real mem instruction")
246 # set address to zero, update mem[0] to 01234
248 expected_insn
= 0x1234
249 mem
[addr
] = expected_insn
251 yield i_in
.priv_mode
.eq(1)
253 yield i_in
.nia
.eq(addr
)
254 yield i_in
.stop_mark
.eq(0)
255 yield i_m_in
.tlbld
.eq(0)
256 yield i_m_in
.tlbie
.eq(0)
257 yield i_m_in
.addr
.eq(0)
258 yield i_m_in
.pte
.eq(0)
263 # miss, stalls for a bit -- this one is different here
264 ##nia, insn, valid, failed = yield from icache_read(dut,addr,0,0)
269 yield i_in
.nia
.eq(addr
)
271 valid
= yield i_out
.valid
274 valid
= yield i_out
.valid
277 nia
= yield i_out
.nia
278 insn
= yield i_out
.insn
282 print ("fetched %x from addr %x" % (insn
, nia
))
283 assert insn
== expected_insn
285 print("=== test loadstore instruction (virtual) ===")
287 # look up i-cache expecting it to fail
289 yield from debug(dut
, "virtual instr req")
290 # set address to 0x10200, update mem[] to 5678
292 real_addr
= virt_addr
293 expected_insn
= 0x5678
294 mem
[real_addr
] = expected_insn
296 yield i_in
.priv_mode
.eq(0)
297 yield i_in
.virt_mode
.eq(1)
299 yield i_in
.nia
.eq(virt_addr
)
300 yield i_in
.stop_mark
.eq(0)
301 yield i_m_in
.tlbld
.eq(0)
302 yield i_m_in
.tlbie
.eq(0)
303 yield i_m_in
.addr
.eq(0)
304 yield i_m_in
.pte
.eq(0)
309 # miss, stalls for a bit
311 yield i_in
.nia
.eq(virt_addr
)
313 valid
= yield i_out
.valid
314 failed
= yield i_out
.fetch_failed
315 while not valid
and not failed
:
317 valid
= yield i_out
.valid
318 failed
= yield i_out
.fetch_failed
321 print ("failed?", "yes" if failed
else "no")
326 print("=== test loadstore instruction (instruction fault) ===")
328 yield from debug(dut
, "instr fault")
332 yield ldst
.priv_mode
.eq(0)
333 yield ldst
.instr_fault
.eq(1)
334 yield ldst
.maddr
.eq(virt_addr
)
335 #ld_data, exctype, exc = yield from pi_ld(pi, virt_addr, 8, msr_pr=1)
337 yield ldst
.instr_fault
.eq(0)
339 done
= yield (ldst
.done
)
340 exc_info
= yield from get_exception_info(pi
.exc_o
)
341 if done
or exc_info
.happened
:
344 assert exc_info
.happened
== 0 # assert just before doing the fault set zero
345 yield ldst
.instr_fault
.eq(0)
350 print("=== test loadstore instruction (try instruction again) ===")
351 yield from debug(dut
, "instr virt retry")
352 # set address to 0x10200, update mem[] to 5678
354 real_addr
= virt_addr
355 expected_insn
= 0x5678
357 yield i_in
.priv_mode
.eq(0)
358 yield i_in
.virt_mode
.eq(1)
360 yield i_in
.nia
.eq(virt_addr
)
361 yield i_in
.stop_mark
.eq(0)
362 yield i_m_in
.tlbld
.eq(0)
363 yield i_m_in
.tlbie
.eq(0)
364 yield i_m_in
.addr
.eq(0)
365 yield i_m_in
.pte
.eq(0)
370 # miss, stalls for a bit
373 yield i_in.nia.eq(virt_addr)
375 valid = yield i_out.valid
376 failed = yield i_out.fetch_failed
377 while not valid and not failed:
379 valid = yield i_out.valid
380 failed = yield i_out.fetch_failed
382 nia = yield i_out.nia
383 insn = yield i_out.insn
387 nia
, insn
, valid
, failed
= yield from icache_read(dut
,virt_addr
,0,1)
389 yield from debug(dut
, "test done")
393 print ("failed?", "yes" if failed
else "no")
396 print ("fetched %x from addr %x" % (insn
, nia
))
397 assert insn
== expected_insn
402 def _test_loadstore1_invalid(dut
, mem
):
403 mmu
= dut
.submodules
.mmu
404 pi
= dut
.submodules
.ldst
.pi
407 print("=== test invalid ===")
410 ld_data
, exctype
, exc
= yield from pi_ld(pi
, addr
, 8, msr_pr
=1)
411 print("ld_data", ld_data
, exctype
, exc
)
412 assert (exctype
== "slow")
413 invalid
= exc
.invalid
414 assert (invalid
== 1)
416 print("=== test invalid done ===")
421 def _test_loadstore1(dut
, mem
):
422 mmu
= dut
.submodules
.mmu
423 pi
= dut
.submodules
.ldst
.pi
424 ldst
= dut
.submodules
.ldst
# to get at DAR (NOT part of PortInterface)
427 yield mmu
.rin
.prtbl
.eq(0x1000000) # set process table
431 data
= 0xf553b658ba7e1f51
434 yield from pi_st(pi
, addr
, data
, 8, msr_pr
=1)
437 ld_data
, exctype
, exc
= yield from pi_ld(pi
, addr
, 8, msr_pr
=1)
438 assert ld_data
== 0xf553b658ba7e1f51
439 assert exctype
is None
441 ld_data
, exctype
, exc
= yield from pi_ld(pi
, addr
, 8, msr_pr
=1)
442 assert ld_data
== 0xf553b658ba7e1f51
443 assert exctype
is None
445 print("do_dcbz ===============")
446 yield from pi_st(pi
, addr
, data
, 8, msr_pr
=1, is_dcbz
=1)
447 print("done_dcbz ===============")
450 ld_data
, exctype
, exc
= yield from pi_ld(pi
, addr
, 8, msr_pr
=1)
451 print("ld_data after dcbz")
454 assert exctype
is None
457 print("=== alignment error (ld) ===")
459 ld_data
, exctype
, exc
= yield from pi_ld(pi
, addr
, 8, msr_pr
=1)
461 alignment
= exc
.alignment
462 happened
= exc
.happened
463 yield # wait for dsr to update
469 assert (happened
== 1)
470 assert (alignment
== 1)
472 assert (exctype
== "fast")
473 yield from wait_busy(pi
, debug
="pi_ld_E_alignment_error")
474 # wait is only needed in case of in exception here
475 print("=== alignment error test passed (ld) ===")
477 # take some cycles in between so that gtkwave separates out
484 print("=== alignment error (st) ===")
486 exctype
, exc
= yield from pi_st(pi
, addr
,0, 8, msr_pr
=1)
488 alignment
= exc
.alignment
489 happened
= exc
.happened
493 assert (happened
== 1)
494 assert (alignment
==1)
496 assert (exctype
== "fast")
497 #???? yield from wait_busy(pi, debug="pi_st_E_alignment_error")
498 # wait is only needed in case of in exception here
499 print("=== alignment error test passed (st) ===")
503 print("=== no alignment error (ld) ===")
505 ld_data
, exctype
, exc
= yield from pi_ld(pi
, addr
, 8, msr_pr
=1)
506 print("ld_data", ld_data
, exctype
, exc
)
508 alignment
= exc
.alignment
509 happened
= exc
.happened
513 assert (happened
== 0)
514 assert (alignment
== 0)
515 print("=== no alignment error done (ld) ===")
518 addrs
= [0x456920,0xa7a180,0x299420,0x1d9d60]
521 print("== RANDOM addr ==",hex(addr
))
522 ld_data
, exctype
, exc
= yield from pi_ld(pi
, addr
, 8, msr_pr
=1)
523 print("ld_data[RANDOM]",ld_data
,exc
,addr
)
524 assert (exctype
== None)
527 print("== RANDOM addr ==",hex(addr
))
528 exc
= yield from pi_st(pi
, addr
,0xFF*addr
, 8, msr_pr
=1)
529 assert (exctype
== None)
531 # readback written data and compare
533 print("== RANDOM addr ==",hex(addr
))
534 ld_data
, exctype
, exc
= yield from pi_ld(pi
, addr
, 8, msr_pr
=1)
535 print("ld_data[RANDOM_READBACK]",ld_data
,exc
,addr
)
536 assert (exctype
== None)
537 assert (ld_data
== 0xFF*addr
)
539 print("== RANDOM addr done ==")
544 def _test_loadstore1_ifetch_invalid(dut
, mem
):
545 mmu
= dut
.submodules
.mmu
546 ldst
= dut
.submodules
.ldst
548 icache
= dut
.submodules
.ldst
.icache
551 print("=== test loadstore instruction (invalid) ===")
557 # first virtual memory test
559 print ("set process table")
560 yield from debug(dut
, "set prtbl")
561 yield mmu
.rin
.prtbl
.eq(0x1000000) # set process table
564 yield from debug(dut
, "real mem instruction")
565 # set address to zero, update mem[0] to 01234
567 expected_insn
= 0x1234
568 mem
[addr
] = expected_insn
570 yield i_in
.priv_mode
.eq(1)
572 yield i_in
.nia
.eq(addr
)
573 yield i_in
.stop_mark
.eq(0)
574 yield i_m_in
.tlbld
.eq(0)
575 yield i_m_in
.tlbie
.eq(0)
576 yield i_m_in
.addr
.eq(0)
577 yield i_m_in
.pte
.eq(0)
582 # miss, stalls for a bit
584 yield i_in
.nia
.eq(addr
)
586 valid
= yield i_out
.valid
587 nia
= yield i_out
.nia
590 valid
= yield i_out
.valid
593 nia
= yield i_out
.nia
594 insn
= yield i_out
.insn
599 print ("fetched %x from addr %x" % (insn
, nia
))
600 assert insn
== expected_insn
602 print("=== test loadstore instruction (virtual) ===")
603 yield from debug(dut
, "virtual instr req")
605 # look up i-cache expecting it to fail
607 # set address to 0x10200, update mem[] to 5678
609 real_addr
= virt_addr
610 expected_insn
= 0x5678
611 mem
[real_addr
] = expected_insn
613 yield i_in
.priv_mode
.eq(1)
614 yield i_in
.virt_mode
.eq(1)
616 yield i_in
.nia
.eq(virt_addr
)
617 yield i_in
.stop_mark
.eq(0)
618 yield i_m_in
.tlbld
.eq(0)
619 yield i_m_in
.tlbie
.eq(0)
620 yield i_m_in
.addr
.eq(0)
621 yield i_m_in
.pte
.eq(0)
626 # miss, stalls for a bit
628 yield i_in
.nia
.eq(virt_addr
)
630 valid
= yield i_out
.valid
631 failed
= yield i_out
.fetch_failed
632 while not valid
and not failed
:
634 valid
= yield i_out
.valid
635 failed
= yield i_out
.fetch_failed
638 print ("failed?", "yes" if failed
else "no")
643 print("=== test invalid loadstore instruction (instruction fault) ===")
645 yield from debug(dut
, "instr fault (perm err expected)")
648 yield ldst
.priv_mode
.eq(0)
649 yield ldst
.instr_fault
.eq(1)
650 yield ldst
.maddr
.eq(virt_addr
)
651 #ld_data, exctype, exc = yield from pi_ld(pi, virt_addr, 8, msr_pr=1)
653 yield ldst
.instr_fault
.eq(0)
655 done
= yield (ldst
.done
)
656 exc_info
= yield from get_exception_info(pi
.exc_o
)
657 if done
or exc_info
.happened
:
660 assert exc_info
.happened
== 1 # different here as expected
662 # TODO: work out what kind of exception occurred and check it's
663 # the right one. we *expect* it to be a permissions error because
664 # the RPTE leaf node in pagetables.test2 is marked as "non-executable"
665 # but we also expect instr_fault to be set because it is an instruction
667 print (" MMU lookup exception type?")
668 for fname
in LDSTExceptionTuple
._fields
:
669 print (" fname %20s %d" % (fname
, getattr(exc_info
, fname
)))
671 # ok now printed them out and visually inspected: check them with asserts
672 assert exc_info
.instr_fault
== 1 # instruction fault (yes!)
673 assert exc_info
.perm_error
== 1 # permissions (yes!)
674 assert exc_info
.rc_error
== 0
675 assert exc_info
.alignment
== 0
676 assert exc_info
.invalid
== 0
677 assert exc_info
.segment_fault
== 0
678 assert exc_info
.rc_error
== 0
680 yield from debug(dut
, "test done")
681 yield ldst
.instr_fault
.eq(0)
689 def test_loadstore1_ifetch_unit_iface():
691 m
, cmpi
= setup_mmu()
693 mem
= pagetables
.test1
699 icache
= m
.submodules
.ldst
.icache
700 icache
.use_fetch_interface() # this is the function which converts
701 # to FetchUnitInterface. *including*
702 # rewiring the Wishbone Bus to ibus
703 sim
.add_sync_process(wrap(_test_loadstore1_ifetch_iface(m
, mem
)))
704 # add two wb_get processes onto the *same* memory dictionary.
705 # this shouuuld work.... cross-fingers...
706 sim
.add_sync_process(wrap(wb_get(cmpi
.wb_bus(), mem
)))
707 sim
.add_sync_process(wrap(wb_get(icache
.ibus
, mem
))) # ibus not bus
708 with sim
.write_vcd('test_loadstore1_ifetch_iface.vcd',
709 traces
=[m
.debug_status
]): # include extra debug
713 def test_loadstore1_ifetch():
715 m
, cmpi
= setup_mmu()
717 mem
= pagetables
.test1
723 icache
= m
.submodules
.ldst
.icache
724 sim
.add_sync_process(wrap(_test_loadstore1_ifetch(m
, mem
)))
725 # add two wb_get processes onto the *same* memory dictionary.
726 # this shouuuld work.... cross-fingers...
727 sim
.add_sync_process(wrap(wb_get(cmpi
.wb_bus(), mem
)))
728 sim
.add_sync_process(wrap(wb_get(icache
.bus
, mem
)))
729 with sim
.write_vcd('test_loadstore1_ifetch.vcd',
730 traces
=[m
.debug_status
]): # include extra debug
734 def test_loadstore1():
736 m
, cmpi
= setup_mmu()
738 mem
= pagetables
.test1
744 sim
.add_sync_process(wrap(_test_loadstore1(m
, mem
)))
745 sim
.add_sync_process(wrap(wb_get(cmpi
.wb_bus(), mem
)))
746 with sim
.write_vcd('test_loadstore1.vcd'):
750 def test_loadstore1_invalid():
752 m
, cmpi
= setup_mmu()
760 sim
.add_sync_process(wrap(_test_loadstore1_invalid(m
, mem
)))
761 sim
.add_sync_process(wrap(wb_get(cmpi
.wb_bus(), mem
)))
762 with sim
.write_vcd('test_loadstore1_invalid.vcd'):
765 def test_loadstore1_ifetch_invalid():
766 m
, cmpi
= setup_mmu()
768 # this is a specially-arranged page table which has the permissions
769 # barred for execute on the leaf node (EAA=0x2 instead of EAA=0x3)
770 mem
= pagetables
.test2
776 icache
= m
.submodules
.ldst
.icache
777 sim
.add_sync_process(wrap(_test_loadstore1_ifetch_invalid(m
, mem
)))
778 # add two wb_get processes onto the *same* memory dictionary.
779 # this shouuuld work.... cross-fingers...
780 sim
.add_sync_process(wrap(wb_get(cmpi
.wb_bus(), mem
)))
781 sim
.add_sync_process(wrap(wb_get(icache
.bus
, mem
)))
782 with sim
.write_vcd('test_loadstore1_ifetch_invalid.vcd',
783 traces
=[m
.debug_status
]): # include extra debug
786 def test_loadstore1_ifetch_multi():
787 m
, cmpi
= setup_mmu()
789 # this is a specially-arranged page table which has the permissions
790 # barred for execute on the leaf node (EAA=0x2 instead of EAA=0x3)
791 mem
= pagetables
.test1
797 icache
= m
.submodules
.ldst
.icache
798 sim
.add_sync_process(wrap(_test_loadstore1_ifetch_multi(m
, mem
)))
799 # add two wb_get processes onto the *same* memory dictionary.
800 # this shouuuld work.... cross-fingers...
801 sim
.add_sync_process(wrap(wb_get(cmpi
.wb_bus(), mem
)))
802 sim
.add_sync_process(wrap(wb_get(icache
.bus
, mem
)))
803 with sim
.write_vcd('test_loadstore1_ifetch_multi.vcd',
804 traces
=[m
.debug_status
]): # include extra debug
807 if __name__
== '__main__':
809 test_loadstore1_invalid()
810 test_loadstore1_ifetch()
811 test_loadstore1_fetch_unit_iface()
812 test_loadstore1_ifetch_invalid()
813 test_loadstore1_ifetch_multi()