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
27 from soc
.config
.test
.test_fetch
import read_from_addr
33 pspec
= TestMemPspec(ldst_ifacetype
='mmu_cache_wb',
36 #disable_cache=True, # hmmm...
42 cmpi
= ConfigMemoryPortInterface(pspec
)
43 m
.submodules
.ldst
= ldst
= cmpi
.pi
44 m
.submodules
.mmu
= mmu
= MMU()
48 l_in
, l_out
= mmu
.l_in
, mmu
.l_out
49 d_in
, d_out
= dcache
.d_in
, dcache
.d_out
50 i_in
, i_out
= icache
.i_in
, icache
.i_out
# FetchToICache, ICacheToDecode
52 # link mmu, dcache and icache together
53 m
.d
.comb
+= dcache
.m_in
.eq(mmu
.d_out
) # MMUToDCacheType
54 m
.d
.comb
+= icache
.m_in
.eq(mmu
.i_out
) # MMUToICacheType
55 m
.d
.comb
+= mmu
.d_in
.eq(dcache
.m_out
) # DCacheToMMUType
57 # link ldst and MMU together
58 comb
+= l_in
.eq(ldst
.m_out
)
59 comb
+= ldst
.m_in
.eq(l_out
)
61 # add a debug status Signal: use "msg.str = "blah"
62 # then toggle with yield msg.eq(0); yield msg.eq(1)
63 debug_status
= Signal(8, decoder
=lambda _
: debug_status
.str)
64 m
.debug_status
= debug_status
70 def icache_read(dut
,addr
,priv
,virt
):
72 icache
= dut
.submodules
.ldst
.icache
76 yield i_in
.priv_mode
.eq(priv
)
77 yield i_in
.virt_mode
.eq(virt
)
79 yield i_in
.nia
.eq(addr
)
80 yield i_in
.stop_mark
.eq(0)
83 yield i_in
.nia
.eq(addr
)
85 valid
= yield i_out
.valid
86 failed
= yield i_out
.fetch_failed
87 while not valid
and not failed
:
89 valid
= yield i_out
.valid
90 failed
= yield i_out
.fetch_failed
94 insn
= yield i_out
.insn
98 return nia
, insn
, valid
, failed
101 test_exceptions
= True
107 print ("set debug message", msg
)
108 dut
.debug_status
.str = msg
# set the message
109 yield dut
.debug_status
.eq(0) # trigger an update
110 yield dut
.debug_status
.eq(1)
113 def _test_loadstore1_ifetch_iface(dut
, mem
):
114 """test_loadstore1_ifetch_iface
116 read in priv mode, non-virtual. tests the FetchUnitInterface
120 mmu
= dut
.submodules
.mmu
121 ldst
= dut
.submodules
.ldst
123 icache
= dut
.submodules
.ldst
.icache
126 print("=== test loadstore instruction (real) ===")
132 yield from debug(dut
, "real mem instruction")
133 # set address to 0x8, update mem[0x8] to 01234 | 0x5678<<32
134 # (have to do 64-bit writes into the dictionary-memory-emulated-thing)
137 expected_insn2
= 0x5678
138 expected_insn
= 0x1234
139 mem
[addr
] = expected_insn | expected_insn2
<<32
141 yield i_in
.priv_mode
.eq(1)
142 insn
= yield from read_from_addr(icache
, addr
, stall
=False)
144 nia
= yield i_out
.nia
# NO, must use FetchUnitInterface
145 print ("fetched %x from addr %x" % (insn
, nia
))
146 assert insn
== expected_insn
148 print("=== test loadstore instruction (2nd, real) ===")
149 yield from debug(dut
, "real mem 2nd (addr 0xc)")
151 insn2
= yield from read_from_addr(icache
, addr2
, stall
=False)
153 nia
= yield i_out
.nia
# NO, must use FetchUnitInterface
154 print ("fetched %x from addr2 %x" % (insn2
, nia
))
155 assert insn2
== expected_insn2
157 print("=== test loadstore instruction (done) ===")
159 yield from debug(dut
, "test done")
163 print ("fetched %x from addr %x" % (insn
, nia
))
164 assert insn
== expected_insn
168 def _test_loadstore1_ifetch_multi(dut
, mem
):
169 mmu
= dut
.submodules
.mmu
170 ldst
= dut
.submodules
.ldst
172 icache
= dut
.submodules
.ldst
.icache
179 yield from debug(dut
, "TODO")
183 # TODO fetch instructions from multiple addresses
184 # should cope with some addresses being invalid
185 #addrs = [0x10200,0x10204,10208,10200]
188 mem
[0x10200]=0xFF00FF00EE00EE00EE
189 mem
[0]=0xFF00FF00EE00EE00EE
191 yield i_in
.priv_mode
.eq(1)
194 yield from debug(dut
, "BROKEN_fetch_from "+hex(addr
))
195 # use the new interface in this test
197 #broken: does not use wishbone yet - investigate
198 insn
= yield from read_from_addr(icache
, addr
, stall
=False)
200 nia
= yield i_out
.nia
# NO, must use FetchUnitInterface
201 print ("fetched %x from addr %x" % (insn
, nia
))
205 def _test_loadstore1_ifetch(dut
, mem
):
206 """test_loadstore1_ifetch
208 this is quite a complex multi-step test.
210 * first (just because, as a demo) read in priv mode, non-virtual.
211 just like in experiment/icache.py itself.
213 * second, using the (usual) PTE for these things (which came originally
214 from gem5-experimental experiment/radix_walk_example.txt) do a
215 virtual-memory read through the *instruction* cache.
216 this is expected to FAIL
218 * third: mess about with the MMU, setting "iside" (instruction-side),
219 requesting an MMU RADIX LOOKUP. this triggers an itlb_load
220 (instruction-cache TLB entry-insertion)
222 * fourth and finally: retry the read of the instruction through i-cache.
223 this is now expected to SUCCEED
228 mmu
= dut
.submodules
.mmu
229 ldst
= dut
.submodules
.ldst
231 icache
= dut
.submodules
.ldst
.icache
234 print("=== test loadstore instruction (real) ===")
240 # first virtual memory test
242 print ("set process table")
243 yield from debug(dut
, "set prtble")
244 yield mmu
.rin
.prtbl
.eq(0x1000000) # set process table
247 yield from debug(dut
, "real mem instruction")
248 # set address to zero, update mem[0] to 01234
250 expected_insn
= 0x1234
251 mem
[addr
] = expected_insn
253 yield i_in
.priv_mode
.eq(1)
255 yield i_in
.nia
.eq(addr
)
256 yield i_in
.stop_mark
.eq(0)
257 yield i_m_in
.tlbld
.eq(0)
258 yield i_m_in
.tlbie
.eq(0)
259 yield i_m_in
.addr
.eq(0)
260 yield i_m_in
.pte
.eq(0)
265 # miss, stalls for a bit -- this one is different here
266 ##nia, insn, valid, failed = yield from icache_read(dut,addr,0,0)
271 yield i_in
.nia
.eq(addr
)
273 valid
= yield i_out
.valid
276 valid
= yield i_out
.valid
279 nia
= yield i_out
.nia
280 insn
= yield i_out
.insn
284 print ("fetched %x from addr %x" % (insn
, nia
))
285 assert insn
== expected_insn
287 print("=== test loadstore instruction (virtual) ===")
289 # look up i-cache expecting it to fail
291 yield from debug(dut
, "virtual instr req")
292 # set address to 0x10200, update mem[] to 5678
294 real_addr
= virt_addr
295 expected_insn
= 0x5678
296 mem
[real_addr
] = expected_insn
298 yield i_in
.priv_mode
.eq(0)
299 yield i_in
.virt_mode
.eq(1)
301 yield i_in
.nia
.eq(virt_addr
)
302 yield i_in
.stop_mark
.eq(0)
303 yield i_m_in
.tlbld
.eq(0)
304 yield i_m_in
.tlbie
.eq(0)
305 yield i_m_in
.addr
.eq(0)
306 yield i_m_in
.pte
.eq(0)
311 # miss, stalls for a bit
313 yield i_in
.nia
.eq(virt_addr
)
315 valid
= yield i_out
.valid
316 failed
= yield i_out
.fetch_failed
317 while not valid
and not failed
:
319 valid
= yield i_out
.valid
320 failed
= yield i_out
.fetch_failed
323 print ("failed?", "yes" if failed
else "no")
328 print("=== test loadstore instruction (instruction fault) ===")
330 yield from debug(dut
, "instr fault")
334 yield ldst
.priv_mode
.eq(0)
335 yield ldst
.instr_fault
.eq(1)
336 yield ldst
.maddr
.eq(virt_addr
)
337 #ld_data, exctype, exc = yield from pi_ld(pi, virt_addr, 8, msr_pr=1)
339 yield ldst
.instr_fault
.eq(0)
341 done
= yield (ldst
.done
)
342 exc_info
= yield from get_exception_info(pi
.exc_o
)
343 if done
or exc_info
.happened
:
346 assert exc_info
.happened
== 0 # assert just before doing the fault set zero
347 yield ldst
.instr_fault
.eq(0)
352 print("=== test loadstore instruction (try instruction again) ===")
353 yield from debug(dut
, "instr virt retry")
354 # set address to 0x10200, update mem[] to 5678
356 real_addr
= virt_addr
357 expected_insn
= 0x5678
359 yield i_in
.priv_mode
.eq(0)
360 yield i_in
.virt_mode
.eq(1)
362 yield i_in
.nia
.eq(virt_addr
)
363 yield i_in
.stop_mark
.eq(0)
364 yield i_m_in
.tlbld
.eq(0)
365 yield i_m_in
.tlbie
.eq(0)
366 yield i_m_in
.addr
.eq(0)
367 yield i_m_in
.pte
.eq(0)
372 # miss, stalls for a bit
375 yield i_in.nia.eq(virt_addr)
377 valid = yield i_out.valid
378 failed = yield i_out.fetch_failed
379 while not valid and not failed:
381 valid = yield i_out.valid
382 failed = yield i_out.fetch_failed
384 nia = yield i_out.nia
385 insn = yield i_out.insn
389 nia
, insn
, valid
, failed
= yield from icache_read(dut
,virt_addr
,0,1)
391 yield from debug(dut
, "test done")
395 print ("failed?", "yes" if failed
else "no")
398 print ("fetched %x from addr %x" % (insn
, nia
))
399 assert insn
== expected_insn
404 def _test_loadstore1_invalid(dut
, mem
):
405 mmu
= dut
.submodules
.mmu
406 pi
= dut
.submodules
.ldst
.pi
409 print("=== test invalid ===")
412 ld_data
, exctype
, exc
= yield from pi_ld(pi
, addr
, 8, msr_pr
=1)
413 print("ld_data", ld_data
, exctype
, exc
)
414 assert (exctype
== "slow")
415 invalid
= exc
.invalid
416 assert (invalid
== 1)
418 print("=== test invalid done ===")
423 def _test_loadstore1(dut
, mem
):
424 mmu
= dut
.submodules
.mmu
425 pi
= dut
.submodules
.ldst
.pi
426 ldst
= dut
.submodules
.ldst
# to get at DAR (NOT part of PortInterface)
429 yield mmu
.rin
.prtbl
.eq(0x1000000) # set process table
433 data
= 0xf553b658ba7e1f51
436 yield from pi_st(pi
, addr
, data
, 8, msr_pr
=1)
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 ld_data
, exctype
, exc
= yield from pi_ld(pi
, addr
, 8, msr_pr
=1)
444 assert ld_data
== 0xf553b658ba7e1f51
445 assert exctype
is None
447 print("do_dcbz ===============")
448 yield from pi_st(pi
, addr
, data
, 8, msr_pr
=1, is_dcbz
=1)
449 print("done_dcbz ===============")
452 ld_data
, exctype
, exc
= yield from pi_ld(pi
, addr
, 8, msr_pr
=1)
453 print("ld_data after dcbz")
456 assert exctype
is None
459 print("=== alignment error (ld) ===")
461 ld_data
, exctype
, exc
= yield from pi_ld(pi
, addr
, 8, msr_pr
=1)
463 alignment
= exc
.alignment
464 happened
= exc
.happened
465 yield # wait for dsr to update
471 assert (happened
== 1)
472 assert (alignment
== 1)
474 assert (exctype
== "fast")
475 yield from wait_busy(pi
, debug
="pi_ld_E_alignment_error")
476 # wait is only needed in case of in exception here
477 print("=== alignment error test passed (ld) ===")
479 # take some cycles in between so that gtkwave separates out
486 print("=== alignment error (st) ===")
488 exctype
, exc
= yield from pi_st(pi
, addr
,0, 8, msr_pr
=1)
490 alignment
= exc
.alignment
491 happened
= exc
.happened
495 assert (happened
== 1)
496 assert (alignment
==1)
498 assert (exctype
== "fast")
499 #???? yield from wait_busy(pi, debug="pi_st_E_alignment_error")
500 # wait is only needed in case of in exception here
501 print("=== alignment error test passed (st) ===")
505 print("=== no alignment error (ld) ===")
507 ld_data
, exctype
, exc
= yield from pi_ld(pi
, addr
, 8, msr_pr
=1)
508 print("ld_data", ld_data
, exctype
, exc
)
510 alignment
= exc
.alignment
511 happened
= exc
.happened
515 assert (happened
== 0)
516 assert (alignment
== 0)
517 print("=== no alignment error done (ld) ===")
520 addrs
= [0x456920,0xa7a180,0x299420,0x1d9d60]
523 print("== RANDOM addr ==",hex(addr
))
524 ld_data
, exctype
, exc
= yield from pi_ld(pi
, addr
, 8, msr_pr
=1)
525 print("ld_data[RANDOM]",ld_data
,exc
,addr
)
526 assert (exctype
== None)
529 print("== RANDOM addr ==",hex(addr
))
530 exc
= yield from pi_st(pi
, addr
,0xFF*addr
, 8, msr_pr
=1)
531 assert (exctype
== None)
533 # readback written data and compare
535 print("== RANDOM addr ==",hex(addr
))
536 ld_data
, exctype
, exc
= yield from pi_ld(pi
, addr
, 8, msr_pr
=1)
537 print("ld_data[RANDOM_READBACK]",ld_data
,exc
,addr
)
538 assert (exctype
== None)
539 assert (ld_data
== 0xFF*addr
)
541 print("== RANDOM addr done ==")
546 def _test_loadstore1_ifetch_invalid(dut
, mem
):
547 mmu
= dut
.submodules
.mmu
548 ldst
= dut
.submodules
.ldst
550 icache
= dut
.submodules
.ldst
.icache
553 print("=== test loadstore instruction (invalid) ===")
559 # first virtual memory test
561 print ("set process table")
562 yield from debug(dut
, "set prtbl")
563 yield mmu
.rin
.prtbl
.eq(0x1000000) # set process table
566 yield from debug(dut
, "real mem instruction")
567 # set address to zero, update mem[0] to 01234
569 expected_insn
= 0x1234
570 mem
[addr
] = expected_insn
572 yield i_in
.priv_mode
.eq(1)
574 yield i_in
.nia
.eq(addr
)
575 yield i_in
.stop_mark
.eq(0)
576 yield i_m_in
.tlbld
.eq(0)
577 yield i_m_in
.tlbie
.eq(0)
578 yield i_m_in
.addr
.eq(0)
579 yield i_m_in
.pte
.eq(0)
584 # miss, stalls for a bit
586 yield i_in
.nia
.eq(addr
)
588 valid
= yield i_out
.valid
589 nia
= yield i_out
.nia
592 valid
= yield i_out
.valid
595 nia
= yield i_out
.nia
596 insn
= yield i_out
.insn
601 print ("fetched %x from addr %x" % (insn
, nia
))
602 assert insn
== expected_insn
604 print("=== test loadstore instruction (virtual) ===")
605 yield from debug(dut
, "virtual instr req")
607 # look up i-cache expecting it to fail
609 # set address to 0x10200, update mem[] to 5678
611 real_addr
= virt_addr
612 expected_insn
= 0x5678
613 mem
[real_addr
] = expected_insn
615 yield i_in
.priv_mode
.eq(1)
616 yield i_in
.virt_mode
.eq(1)
618 yield i_in
.nia
.eq(virt_addr
)
619 yield i_in
.stop_mark
.eq(0)
620 yield i_m_in
.tlbld
.eq(0)
621 yield i_m_in
.tlbie
.eq(0)
622 yield i_m_in
.addr
.eq(0)
623 yield i_m_in
.pte
.eq(0)
628 # miss, stalls for a bit
630 yield i_in
.nia
.eq(virt_addr
)
632 valid
= yield i_out
.valid
633 failed
= yield i_out
.fetch_failed
634 while not valid
and not failed
:
636 valid
= yield i_out
.valid
637 failed
= yield i_out
.fetch_failed
640 print ("failed?", "yes" if failed
else "no")
645 print("=== test invalid loadstore instruction (instruction fault) ===")
647 yield from debug(dut
, "instr fault (perm err expected)")
650 yield ldst
.priv_mode
.eq(0)
651 yield ldst
.instr_fault
.eq(1)
652 yield ldst
.maddr
.eq(virt_addr
)
653 #ld_data, exctype, exc = yield from pi_ld(pi, virt_addr, 8, msr_pr=1)
655 yield ldst
.instr_fault
.eq(0)
657 done
= yield (ldst
.done
)
658 exc_info
= yield from get_exception_info(pi
.exc_o
)
659 if done
or exc_info
.happened
:
662 assert exc_info
.happened
== 1 # different here as expected
664 # TODO: work out what kind of exception occurred and check it's
665 # the right one. we *expect* it to be a permissions error because
666 # the RPTE leaf node in pagetables.test2 is marked as "non-executable"
667 # but we also expect instr_fault to be set because it is an instruction
669 print (" MMU lookup exception type?")
670 for fname
in LDSTExceptionTuple
._fields
:
671 print (" fname %20s %d" % (fname
, getattr(exc_info
, fname
)))
673 # ok now printed them out and visually inspected: check them with asserts
674 assert exc_info
.instr_fault
== 1 # instruction fault (yes!)
675 assert exc_info
.perm_error
== 1 # permissions (yes!)
676 assert exc_info
.rc_error
== 0
677 assert exc_info
.alignment
== 0
678 assert exc_info
.invalid
== 0
679 assert exc_info
.segment_fault
== 0
680 assert exc_info
.rc_error
== 0
682 yield from debug(dut
, "test done")
683 yield ldst
.instr_fault
.eq(0)
691 def test_loadstore1_ifetch_unit_iface():
693 m
, cmpi
= setup_mmu()
695 mem
= pagetables
.test1
697 # set this up before passing to Simulator (which calls elaborate)
698 icache
= m
.submodules
.ldst
.icache
699 icache
.use_fetch_interface() # this is the function which converts
700 # to FetchUnitInterface. *including*
701 # rewiring the Wishbone Bus to ibus
707 sim
.add_sync_process(wrap(_test_loadstore1_ifetch_iface(m
, mem
)))
708 # add two wb_get processes onto the *same* memory dictionary.
709 # this shouuuld work.... cross-fingers...
710 sim
.add_sync_process(wrap(wb_get(cmpi
.wb_bus(), mem
)))
711 sim
.add_sync_process(wrap(wb_get(icache
.ibus
, mem
))) # ibus not bus
712 with sim
.write_vcd('test_loadstore1_ifetch_iface.vcd',
713 traces
=[m
.debug_status
]): # include extra debug
717 def test_loadstore1_ifetch():
719 m
, cmpi
= setup_mmu()
721 mem
= pagetables
.test1
727 icache
= m
.submodules
.ldst
.icache
728 sim
.add_sync_process(wrap(_test_loadstore1_ifetch(m
, mem
)))
729 # add two wb_get processes onto the *same* memory dictionary.
730 # this shouuuld work.... cross-fingers...
731 sim
.add_sync_process(wrap(wb_get(cmpi
.wb_bus(), mem
)))
732 sim
.add_sync_process(wrap(wb_get(icache
.bus
, mem
)))
733 with sim
.write_vcd('test_loadstore1_ifetch.vcd',
734 traces
=[m
.debug_status
]): # include extra debug
738 def test_loadstore1():
740 m
, cmpi
= setup_mmu()
742 mem
= pagetables
.test1
748 sim
.add_sync_process(wrap(_test_loadstore1(m
, mem
)))
749 sim
.add_sync_process(wrap(wb_get(cmpi
.wb_bus(), mem
)))
750 with sim
.write_vcd('test_loadstore1.vcd'):
754 def test_loadstore1_invalid():
756 m
, cmpi
= setup_mmu()
764 sim
.add_sync_process(wrap(_test_loadstore1_invalid(m
, mem
)))
765 sim
.add_sync_process(wrap(wb_get(cmpi
.wb_bus(), mem
)))
766 with sim
.write_vcd('test_loadstore1_invalid.vcd'):
769 def test_loadstore1_ifetch_invalid():
770 m
, cmpi
= setup_mmu()
772 # this is a specially-arranged page table which has the permissions
773 # barred for execute on the leaf node (EAA=0x2 instead of EAA=0x3)
774 mem
= pagetables
.test2
780 icache
= m
.submodules
.ldst
.icache
781 sim
.add_sync_process(wrap(_test_loadstore1_ifetch_invalid(m
, mem
)))
782 # add two wb_get processes onto the *same* memory dictionary.
783 # this shouuuld work.... cross-fingers...
784 sim
.add_sync_process(wrap(wb_get(cmpi
.wb_bus(), mem
)))
785 sim
.add_sync_process(wrap(wb_get(icache
.bus
, mem
)))
786 with sim
.write_vcd('test_loadstore1_ifetch_invalid.vcd',
787 traces
=[m
.debug_status
]): # include extra debug
790 def test_loadstore1_ifetch_multi():
791 m
, cmpi
= setup_mmu()
793 # this is a specially-arranged page table which has the permissions
794 # barred for execute on the leaf node (EAA=0x2 instead of EAA=0x3)
795 mem
= pagetables
.test1
801 icache
= m
.submodules
.ldst
.icache
802 icache
.use_fetch_interface() # see test_loadstore1_ifetch_unit_iface():
804 sim
.add_sync_process(wrap(_test_loadstore1_ifetch_multi(m
, mem
)))
805 # add two wb_get processes onto the *same* memory dictionary.
806 # this shouuuld work.... cross-fingers...
807 sim
.add_sync_process(wrap(wb_get(cmpi
.wb_bus(), mem
)))
808 sim
.add_sync_process(wrap(wb_get(icache
.bus
, mem
)))
809 with sim
.write_vcd('test_loadstore1_ifetch_multi.vcd',
810 traces
=[m
.debug_status
]): # include extra debug
813 if __name__
== '__main__':
815 test_loadstore1_invalid()
816 test_loadstore1_ifetch()
817 test_loadstore1_ifetch_invalid()
818 test_loadstore1_ifetch_multi()
819 test_loadstore1_ifetch_unit_iface()