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
28 from openpower
.decoder
.power_enums
import MSRSpec
35 pspec
= TestMemPspec(ldst_ifacetype
='mmu_cache_wb',
38 #disable_cache=True, # hmmm...
44 cmpi
= ConfigMemoryPortInterface(pspec
)
45 m
.submodules
.ldst
= ldst
= cmpi
.pi
46 m
.submodules
.mmu
= mmu
= MMU()
50 l_in
, l_out
= mmu
.l_in
, mmu
.l_out
51 d_in
, d_out
= dcache
.d_in
, dcache
.d_out
52 i_in
, i_out
= icache
.i_in
, icache
.i_out
# FetchToICache, ICacheToDecode
54 # link mmu, dcache and icache together
55 m
.d
.comb
+= dcache
.m_in
.eq(mmu
.d_out
) # MMUToDCacheType
56 m
.d
.comb
+= icache
.m_in
.eq(mmu
.i_out
) # MMUToICacheType
57 m
.d
.comb
+= mmu
.d_in
.eq(dcache
.m_out
) # DCacheToMMUType
59 # link ldst and MMU together
60 comb
+= l_in
.eq(ldst
.m_out
)
61 comb
+= ldst
.m_in
.eq(l_out
)
63 # add a debug status Signal: use "msg.str = "blah"
64 # then toggle with yield msg.eq(0); yield msg.eq(1)
65 debug_status
= Signal(8, decoder
=lambda _
: debug_status
.str)
66 m
.debug_status
= debug_status
72 def icache_read(dut
,addr
,priv
,virt
):
74 icache
= dut
.submodules
.ldst
.icache
78 yield i_in
.priv_mode
.eq(priv
)
79 yield i_in
.virt_mode
.eq(virt
)
81 yield i_in
.nia
.eq(addr
)
82 yield i_in
.stop_mark
.eq(0)
85 yield i_in
.nia
.eq(addr
)
87 valid
= yield i_out
.valid
88 failed
= yield i_out
.fetch_failed
89 while not valid
and not failed
:
91 valid
= yield i_out
.valid
92 failed
= yield i_out
.fetch_failed
96 insn
= yield i_out
.insn
100 return nia
, insn
, valid
, failed
103 test_exceptions
= True
109 print ("set debug message", msg
)
110 dut
.debug_status
.str = msg
# set the message
111 yield dut
.debug_status
.eq(0) # trigger an update
112 yield dut
.debug_status
.eq(1)
115 def _test_loadstore1_ifetch_iface(dut
, mem
):
116 """test_loadstore1_ifetch_iface
118 read in priv mode, non-virtual. tests the FetchUnitInterface
122 mmu
= dut
.submodules
.mmu
123 ldst
= dut
.submodules
.ldst
125 icache
= dut
.submodules
.ldst
.icache
128 print("=== test loadstore instruction (real) ===")
134 yield from debug(dut
, "real mem instruction")
135 # set address to 0x8, update mem[0x8] to 01234 | 0x5678<<32
136 # (have to do 64-bit writes into the dictionary-memory-emulated-thing)
139 expected_insn2
= 0x5678
140 expected_insn
= 0x1234
141 mem
[addr
] = expected_insn | expected_insn2
<<32
143 yield i_in
.priv_mode
.eq(1)
144 insn
= yield from read_from_addr(icache
, addr
, stall
=False)
146 nia
= yield i_out
.nia
# NO, must use FetchUnitInterface
147 print ("fetched %x from addr %x" % (insn
, nia
))
148 assert insn
== expected_insn
150 print("=== test loadstore instruction (2nd, real) ===")
151 yield from debug(dut
, "real mem 2nd (addr 0xc)")
153 insn2
= yield from read_from_addr(icache
, addr2
, stall
=False)
155 nia
= yield i_out
.nia
# NO, must use FetchUnitInterface
156 print ("fetched %x from addr2 %x" % (insn2
, nia
))
157 assert insn2
== expected_insn2
159 print("=== test loadstore instruction (done) ===")
161 yield from debug(dut
, "test done")
165 print ("fetched %x from addr %x" % (insn
, nia
))
166 assert insn
== expected_insn
170 def _test_loadstore1_ifetch_multi(dut
, mem
):
171 mmu
= dut
.submodules
.mmu
172 ldst
= dut
.submodules
.ldst
174 icache
= dut
.submodules
.ldst
.icache
181 yield from debug(dut
, "TODO")
185 # TODO fetch instructions from multiple addresses
186 # should cope with some addresses being invalid
187 addrs
= [0,4,8,0,0x10200,0x10204,0x10208,0x10200]
189 mem
[0x10200]=0xFF00FF00EE00EE00EE
190 mem
[0]=0xFF00FF00EE00EE00EE
192 yield i_in
.priv_mode
.eq(1)
195 yield from debug(dut
, "BROKEN_fetch_from "+hex(addr
))
196 # use the new interface in this test
198 #broken: does not use wishbone yet - investigate
199 insn
= yield from read_from_addr(icache
, addr
, stall
=False)
201 nia
= yield i_out
.nia
# NO, must use FetchUnitInterface
202 print ("TEST_MULTI: fetched %x from addr %x == %x" % (insn
, nia
,addr
))
206 def _test_loadstore1_ifetch(dut
, mem
):
207 """test_loadstore1_ifetch
209 this is quite a complex multi-step test.
211 * first (just because, as a demo) read in priv mode, non-virtual.
212 just like in experiment/icache.py itself.
214 * second, using the (usual) PTE for these things (which came originally
215 from gem5-experimental experiment/radix_walk_example.txt) do a
216 virtual-memory read through the *instruction* cache.
217 this is expected to FAIL
219 * third: mess about with the MMU, setting "iside" (instruction-side),
220 requesting an MMU RADIX LOOKUP. this triggers an itlb_load
221 (instruction-cache TLB entry-insertion)
223 * fourth and finally: retry the read of the instruction through i-cache.
224 this is now expected to SUCCEED
229 mmu
= dut
.submodules
.mmu
230 ldst
= dut
.submodules
.ldst
232 icache
= dut
.submodules
.ldst
.icache
235 print("=== test loadstore instruction (real) ===")
241 # first virtual memory test
243 print ("set process table")
244 yield from debug(dut
, "set prtble")
245 yield mmu
.rin
.prtbl
.eq(0x1000000) # set process table
248 yield from debug(dut
, "real mem instruction")
249 # set address to zero, update mem[0] to 01234
251 expected_insn
= 0x1234
252 mem
[addr
] = expected_insn
254 yield i_in
.priv_mode
.eq(1)
256 yield i_in
.nia
.eq(addr
)
257 yield i_in
.stop_mark
.eq(0)
258 yield i_m_in
.tlbld
.eq(0)
259 yield i_m_in
.tlbie
.eq(0)
260 yield i_m_in
.addr
.eq(0)
261 yield i_m_in
.pte
.eq(0)
266 # miss, stalls for a bit -- this one is different here
267 ##nia, insn, valid, failed = yield from icache_read(dut,addr,0,0)
272 yield i_in
.nia
.eq(addr
)
274 valid
= yield i_out
.valid
277 valid
= yield i_out
.valid
280 nia
= yield i_out
.nia
281 insn
= yield i_out
.insn
285 print ("fetched %x from addr %x" % (insn
, nia
))
286 assert insn
== expected_insn
288 print("=== test loadstore instruction (virtual) ===")
290 # look up i-cache expecting it to fail
292 yield from debug(dut
, "virtual instr req")
293 # set address to 0x10200, update mem[] to 5678
295 real_addr
= virt_addr
296 expected_insn
= 0x5678
297 mem
[real_addr
] = expected_insn
299 yield i_in
.priv_mode
.eq(0)
300 yield i_in
.virt_mode
.eq(1)
302 yield i_in
.nia
.eq(virt_addr
)
303 yield i_in
.stop_mark
.eq(0)
304 yield i_m_in
.tlbld
.eq(0)
305 yield i_m_in
.tlbie
.eq(0)
306 yield i_m_in
.addr
.eq(0)
307 yield i_m_in
.pte
.eq(0)
312 # miss, stalls for a bit
314 yield i_in
.nia
.eq(virt_addr
)
316 valid
= yield i_out
.valid
317 failed
= yield i_out
.fetch_failed
318 while not valid
and not failed
:
320 valid
= yield i_out
.valid
321 failed
= yield i_out
.fetch_failed
324 print ("failed?", "yes" if failed
else "no")
329 print("=== test loadstore instruction (instruction fault) ===")
331 yield from debug(dut
, "instr fault")
335 yield ldst
.priv_mode
.eq(0)
336 yield ldst
.instr_fault
.eq(1)
337 yield ldst
.maddr
.eq(virt_addr
)
338 # still broken -- investigate
339 # msr = MSRSpec(pr=?, dr=?, sf=0)
340 # ld_data, exctype, exc = yield from pi_ld(pi, virt_addr, 8, msr=msr)
342 yield ldst
.instr_fault
.eq(0)
344 done
= yield (ldst
.done
)
345 exc_info
= yield from get_exception_info(pi
.exc_o
)
346 if done
or exc_info
.happened
:
349 assert exc_info
.happened
== 0 # assert just before doing the fault set zero
350 yield ldst
.instr_fault
.eq(0)
355 print("=== test loadstore instruction (try instruction again) ===")
356 yield from debug(dut
, "instr virt retry")
357 # set address to 0x10200, update mem[] to 5678
359 real_addr
= virt_addr
360 expected_insn
= 0x5678
362 yield i_in
.priv_mode
.eq(0)
363 yield i_in
.virt_mode
.eq(1)
365 yield i_in
.nia
.eq(virt_addr
)
366 yield i_in
.stop_mark
.eq(0)
367 yield i_m_in
.tlbld
.eq(0)
368 yield i_m_in
.tlbie
.eq(0)
369 yield i_m_in
.addr
.eq(0)
370 yield i_m_in
.pte
.eq(0)
375 # miss, stalls for a bit
378 yield i_in.nia.eq(virt_addr)
380 valid = yield i_out.valid
381 failed = yield i_out.fetch_failed
382 while not valid and not failed:
384 valid = yield i_out.valid
385 failed = yield i_out.fetch_failed
387 nia = yield i_out.nia
388 insn = yield i_out.insn
392 nia
, insn
, valid
, failed
= yield from icache_read(dut
,virt_addr
,0,1)
394 yield from debug(dut
, "test done")
398 print ("failed?", "yes" if failed
else "no")
401 print ("fetched %x from addr %x" % (insn
, nia
))
402 assert insn
== expected_insn
407 def _test_loadstore1_invalid(dut
, mem
):
408 mmu
= dut
.submodules
.mmu
409 pi
= dut
.submodules
.ldst
.pi
412 print("=== test invalid ===")
415 msr
= MSRSpec(pr
=1, dr
=0, sf
=0) # set problem-state
416 ld_data
, exctype
, exc
= yield from pi_ld(pi
, addr
, 8, msr
=msr
)
417 print("ld_data", ld_data
, exctype
, exc
)
418 assert (exctype
== "slow")
419 invalid
= exc
.invalid
420 assert (invalid
== 1)
422 print("=== test invalid done ===")
427 def _test_loadstore1(dut
, mem
):
428 mmu
= dut
.submodules
.mmu
429 pi
= dut
.submodules
.ldst
.pi
430 ldst
= dut
.submodules
.ldst
# to get at DAR (NOT part of PortInterface)
433 yield mmu
.rin
.prtbl
.eq(0x1000000) # set process table
437 data
= 0xf553b658ba7e1f51
440 msr
= MSRSpec(pr
=0, dr
=0, sf
=0)
441 yield from pi_st(pi
, addr
, data
, 8, msr
=msr
)
444 ld_data
, exctype
, exc
= yield from pi_ld(pi
, addr
, 8, msr
=msr
)
445 assert ld_data
== 0xf553b658ba7e1f51
446 assert exctype
is None
448 ld_data
, exctype
, exc
= yield from pi_ld(pi
, addr
, 8, msr
=msr
)
449 assert ld_data
== 0xf553b658ba7e1f51
450 assert exctype
is None
452 print("do_dcbz ===============")
453 yield from pi_st(pi
, addr
, data
, 8, msr
=msr
, is_dcbz
=1)
454 print("done_dcbz ===============")
457 ld_data
, exctype
, exc
= yield from pi_ld(pi
, addr
, 8, msr
=msr
)
458 print("ld_data after dcbz")
461 assert exctype
is None
464 print("=== alignment error (ld) ===")
466 ld_data
, exctype
, exc
= yield from pi_ld(pi
, addr
, 8, msr
=msr
)
468 alignment
= exc
.alignment
469 happened
= exc
.happened
470 yield # wait for dsr to update
476 assert (happened
== 1)
477 assert (alignment
== 1)
479 assert (exctype
== "fast")
480 yield from wait_busy(pi
, debug
="pi_ld_E_alignment_error")
481 # wait is only needed in case of in exception here
482 print("=== alignment error test passed (ld) ===")
484 # take some cycles in between so that gtkwave separates out
491 print("=== alignment error (st) ===")
493 exctype
, exc
= yield from pi_st(pi
, addr
,0, 8, msr
=msr
)
495 alignment
= exc
.alignment
496 happened
= exc
.happened
500 assert (happened
== 1)
501 assert (alignment
==1)
503 assert (exctype
== "fast")
504 #???? yield from wait_busy(pi, debug="pi_st_E_alignment_error")
505 # wait is only needed in case of in exception here
506 print("=== alignment error test passed (st) ===")
510 print("=== no alignment error (ld) ===")
512 ld_data
, exctype
, exc
= yield from pi_ld(pi
, addr
, 8, msr
=msr
)
513 print("ld_data", ld_data
, exctype
, exc
)
515 alignment
= exc
.alignment
516 happened
= exc
.happened
520 assert (happened
== 0)
521 assert (alignment
== 0)
522 print("=== no alignment error done (ld) ===")
525 addrs
= [0x456920,0xa7a180,0x299420,0x1d9d60]
528 print("== RANDOM addr ==",hex(addr
))
529 ld_data
, exctype
, exc
= yield from pi_ld(pi
, addr
, 8, msr
=msr
)
530 print("ld_data[RANDOM]",ld_data
,exc
,addr
)
531 assert (exctype
== None)
534 print("== RANDOM addr ==",hex(addr
))
535 exc
= yield from pi_st(pi
, addr
,0xFF*addr
, 8, msr
=msr
)
536 assert (exctype
== None)
538 # readback written data and compare
540 print("== RANDOM addr ==",hex(addr
))
541 ld_data
, exctype
, exc
= yield from pi_ld(pi
, addr
, 8, msr
=msr
)
542 print("ld_data[RANDOM_READBACK]",ld_data
,exc
,addr
)
543 assert (exctype
== None)
544 assert (ld_data
== 0xFF*addr
)
546 print("== RANDOM addr done ==")
551 def _test_loadstore1_ifetch_invalid(dut
, mem
):
552 mmu
= dut
.submodules
.mmu
553 ldst
= dut
.submodules
.ldst
555 icache
= dut
.submodules
.ldst
.icache
558 print("=== test loadstore instruction (invalid) ===")
564 # first virtual memory test
566 print ("set process table")
567 yield from debug(dut
, "set prtbl")
568 yield mmu
.rin
.prtbl
.eq(0x1000000) # set process table
571 yield from debug(dut
, "real mem instruction")
572 # set address to zero, update mem[0] to 01234
574 expected_insn
= 0x1234
575 mem
[addr
] = expected_insn
577 yield i_in
.priv_mode
.eq(1)
579 yield i_in
.nia
.eq(addr
)
580 yield i_in
.stop_mark
.eq(0)
581 yield i_m_in
.tlbld
.eq(0)
582 yield i_m_in
.tlbie
.eq(0)
583 yield i_m_in
.addr
.eq(0)
584 yield i_m_in
.pte
.eq(0)
589 # miss, stalls for a bit
591 yield i_in
.nia
.eq(addr
)
593 valid
= yield i_out
.valid
594 nia
= yield i_out
.nia
597 valid
= yield i_out
.valid
600 nia
= yield i_out
.nia
601 insn
= yield i_out
.insn
606 print ("fetched %x from addr %x" % (insn
, nia
))
607 assert insn
== expected_insn
609 print("=== test loadstore instruction (virtual) ===")
610 yield from debug(dut
, "virtual instr req")
612 # look up i-cache expecting it to fail
614 # set address to 0x10200, update mem[] to 5678
616 real_addr
= virt_addr
617 expected_insn
= 0x5678
618 mem
[real_addr
] = expected_insn
620 yield i_in
.priv_mode
.eq(1)
621 yield i_in
.virt_mode
.eq(1)
623 yield i_in
.nia
.eq(virt_addr
)
624 yield i_in
.stop_mark
.eq(0)
625 yield i_m_in
.tlbld
.eq(0)
626 yield i_m_in
.tlbie
.eq(0)
627 yield i_m_in
.addr
.eq(0)
628 yield i_m_in
.pte
.eq(0)
633 # miss, stalls for a bit
635 yield i_in
.nia
.eq(virt_addr
)
637 valid
= yield i_out
.valid
638 failed
= yield i_out
.fetch_failed
639 while not valid
and not failed
:
641 valid
= yield i_out
.valid
642 failed
= yield i_out
.fetch_failed
645 print ("failed?", "yes" if failed
else "no")
650 print("=== test invalid loadstore instruction (instruction fault) ===")
652 yield from debug(dut
, "instr fault (perm err expected)")
655 yield ldst
.priv_mode
.eq(0)
656 yield ldst
.instr_fault
.eq(1)
657 yield ldst
.maddr
.eq(virt_addr
)
658 #ld_data, exctype, exc = yield from pi_ld(pi, virt_addr, 8, msr=msr)
660 yield ldst
.instr_fault
.eq(0)
662 done
= yield (ldst
.done
)
663 exc_info
= yield from get_exception_info(pi
.exc_o
)
664 if done
or exc_info
.happened
:
667 assert exc_info
.happened
== 1 # different here as expected
669 # TODO: work out what kind of exception occurred and check it's
670 # the right one. we *expect* it to be a permissions error because
671 # the RPTE leaf node in pagetables.test2 is marked as "non-executable"
672 # but we also expect instr_fault to be set because it is an instruction
674 print (" MMU lookup exception type?")
675 for fname
in LDSTExceptionTuple
._fields
:
676 print (" fname %20s %d" % (fname
, getattr(exc_info
, fname
)))
678 # ok now printed them out and visually inspected: check them with asserts
679 assert exc_info
.instr_fault
== 1 # instruction fault (yes!)
680 assert exc_info
.perm_error
== 1 # permissions (yes!)
681 assert exc_info
.rc_error
== 0
682 assert exc_info
.alignment
== 0
683 assert exc_info
.invalid
== 0
684 assert exc_info
.segment_fault
== 0
685 assert exc_info
.rc_error
== 0
687 yield from debug(dut
, "test done")
688 yield ldst
.instr_fault
.eq(0)
696 def test_loadstore1_ifetch_unit_iface():
698 m
, cmpi
= setup_mmu()
700 mem
= pagetables
.test1
702 # set this up before passing to Simulator (which calls elaborate)
703 icache
= m
.submodules
.ldst
.icache
704 icache
.use_fetch_interface() # this is the function which converts
705 # to FetchUnitInterface. *including*
706 # rewiring the Wishbone Bus to ibus
712 sim
.add_sync_process(wrap(_test_loadstore1_ifetch_iface(m
, mem
)))
713 # add two wb_get processes onto the *same* memory dictionary.
714 # this shouuuld work.... cross-fingers...
715 sim
.add_sync_process(wrap(wb_get(cmpi
.wb_bus(), mem
)))
716 sim
.add_sync_process(wrap(wb_get(icache
.ibus
, mem
))) # ibus not bus
717 with sim
.write_vcd('test_loadstore1_ifetch_iface.vcd',
718 traces
=[m
.debug_status
]): # include extra debug
722 def test_loadstore1_ifetch():
724 m
, cmpi
= setup_mmu()
726 mem
= pagetables
.test1
732 icache
= m
.submodules
.ldst
.icache
733 sim
.add_sync_process(wrap(_test_loadstore1_ifetch(m
, mem
)))
734 # add two wb_get processes onto the *same* memory dictionary.
735 # this shouuuld work.... cross-fingers...
736 sim
.add_sync_process(wrap(wb_get(cmpi
.wb_bus(), mem
)))
737 sim
.add_sync_process(wrap(wb_get(icache
.bus
, mem
)))
738 with sim
.write_vcd('test_loadstore1_ifetch.vcd',
739 traces
=[m
.debug_status
]): # include extra debug
743 def test_loadstore1():
745 m
, cmpi
= setup_mmu()
747 mem
= pagetables
.test1
753 sim
.add_sync_process(wrap(_test_loadstore1(m
, mem
)))
754 sim
.add_sync_process(wrap(wb_get(cmpi
.wb_bus(), mem
)))
755 with sim
.write_vcd('test_loadstore1.vcd'):
759 def test_loadstore1_invalid():
761 m
, cmpi
= setup_mmu()
769 sim
.add_sync_process(wrap(_test_loadstore1_invalid(m
, mem
)))
770 sim
.add_sync_process(wrap(wb_get(cmpi
.wb_bus(), mem
)))
771 with sim
.write_vcd('test_loadstore1_invalid.vcd'):
774 def test_loadstore1_ifetch_invalid():
775 m
, cmpi
= setup_mmu()
777 # this is a specially-arranged page table which has the permissions
778 # barred for execute on the leaf node (EAA=0x2 instead of EAA=0x3)
779 mem
= pagetables
.test2
785 icache
= m
.submodules
.ldst
.icache
786 sim
.add_sync_process(wrap(_test_loadstore1_ifetch_invalid(m
, mem
)))
787 # add two wb_get processes onto the *same* memory dictionary.
788 # this shouuuld work.... cross-fingers...
789 sim
.add_sync_process(wrap(wb_get(cmpi
.wb_bus(), mem
)))
790 sim
.add_sync_process(wrap(wb_get(icache
.bus
, mem
)))
791 with sim
.write_vcd('test_loadstore1_ifetch_invalid.vcd',
792 traces
=[m
.debug_status
]): # include extra debug
795 def test_loadstore1_ifetch_multi():
796 m
, cmpi
= setup_mmu()
798 # this is a specially-arranged page table which has the permissions
799 # barred for execute on the leaf node (EAA=0x2 instead of EAA=0x3)
800 mem
= pagetables
.test1
806 icache
= m
.submodules
.ldst
.icache
807 icache
.use_fetch_interface() # see test_loadstore1_ifetch_unit_iface():
809 sim
.add_sync_process(wrap(_test_loadstore1_ifetch_multi(m
, mem
)))
810 # add two wb_get processes onto the *same* memory dictionary.
811 # this shouuuld work.... cross-fingers...
812 sim
.add_sync_process(wrap(wb_get(cmpi
.wb_bus(), mem
)))
813 sim
.add_sync_process(wrap(wb_get(icache
.ibus
, mem
))) # ibus not bus
814 with sim
.write_vcd('test_loadstore1_ifetch_multi.vcd',
815 traces
=[m
.debug_status
]): # include extra debug
818 if __name__
== '__main__':
820 test_loadstore1_invalid()
821 test_loadstore1_ifetch() #FIXME
822 test_loadstore1_ifetch_invalid()
823 test_loadstore1_ifetch_multi()
824 test_loadstore1_ifetch_unit_iface()