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 yield from debug(dut
, "TODO")
173 # TODO fetch instructions from multiple addresses
174 # should cope with some addresses being invalid
175 addrs
= [0x10200,0x10204,10208,10200]
177 yield from debug(dut
, "TODO_fetch_from "+hex(addr
))
178 # use the new interface in this test
185 def _test_loadstore1_ifetch(dut
, mem
):
186 """test_loadstore1_ifetch
188 this is quite a complex multi-step test.
190 * first (just because, as a demo) read in priv mode, non-virtual.
191 just like in experiment/icache.py itself.
193 * second, using the (usual) PTE for these things (which came originally
194 from gem5-experimental experiment/radix_walk_example.txt) do a
195 virtual-memory read through the *instruction* cache.
196 this is expected to FAIL
198 * third: mess about with the MMU, setting "iside" (instruction-side),
199 requesting an MMU RADIX LOOKUP. this triggers an itlb_load
200 (instruction-cache TLB entry-insertion)
202 * fourth and finally: retry the read of the instruction through i-cache.
203 this is now expected to SUCCEED
208 mmu
= dut
.submodules
.mmu
209 ldst
= dut
.submodules
.ldst
211 icache
= dut
.submodules
.ldst
.icache
214 print("=== test loadstore instruction (real) ===")
220 # first virtual memory test
222 print ("set process table")
223 yield from debug(dut
, "set prtble")
224 yield mmu
.rin
.prtbl
.eq(0x1000000) # set process table
227 yield from debug(dut
, "real mem instruction")
228 # set address to zero, update mem[0] to 01234
230 expected_insn
= 0x1234
231 mem
[addr
] = expected_insn
233 yield i_in
.priv_mode
.eq(1)
235 yield i_in
.nia
.eq(addr
)
236 yield i_in
.stop_mark
.eq(0)
237 yield i_m_in
.tlbld
.eq(0)
238 yield i_m_in
.tlbie
.eq(0)
239 yield i_m_in
.addr
.eq(0)
240 yield i_m_in
.pte
.eq(0)
245 # miss, stalls for a bit -- this one is different here
246 ##nia, insn, valid, failed = yield from icache_read(dut,addr,0,0)
251 yield i_in
.nia
.eq(addr
)
253 valid
= yield i_out
.valid
256 valid
= yield i_out
.valid
259 nia
= yield i_out
.nia
260 insn
= yield i_out
.insn
264 print ("fetched %x from addr %x" % (insn
, nia
))
265 assert insn
== expected_insn
267 print("=== test loadstore instruction (virtual) ===")
269 # look up i-cache expecting it to fail
271 yield from debug(dut
, "virtual instr req")
272 # set address to 0x10200, update mem[] to 5678
274 real_addr
= virt_addr
275 expected_insn
= 0x5678
276 mem
[real_addr
] = expected_insn
278 yield i_in
.priv_mode
.eq(0)
279 yield i_in
.virt_mode
.eq(1)
281 yield i_in
.nia
.eq(virt_addr
)
282 yield i_in
.stop_mark
.eq(0)
283 yield i_m_in
.tlbld
.eq(0)
284 yield i_m_in
.tlbie
.eq(0)
285 yield i_m_in
.addr
.eq(0)
286 yield i_m_in
.pte
.eq(0)
291 # miss, stalls for a bit
293 yield i_in
.nia
.eq(virt_addr
)
295 valid
= yield i_out
.valid
296 failed
= yield i_out
.fetch_failed
297 while not valid
and not failed
:
299 valid
= yield i_out
.valid
300 failed
= yield i_out
.fetch_failed
303 print ("failed?", "yes" if failed
else "no")
308 print("=== test loadstore instruction (instruction fault) ===")
310 yield from debug(dut
, "instr fault")
314 yield ldst
.priv_mode
.eq(0)
315 yield ldst
.instr_fault
.eq(1)
316 yield ldst
.maddr
.eq(virt_addr
)
317 #ld_data, exctype, exc = yield from pi_ld(pi, virt_addr, 8, msr_pr=1)
319 yield ldst
.instr_fault
.eq(0)
321 done
= yield (ldst
.done
)
322 exc_info
= yield from get_exception_info(pi
.exc_o
)
323 if done
or exc_info
.happened
:
326 assert exc_info
.happened
== 0 # assert just before doing the fault set zero
327 yield ldst
.instr_fault
.eq(0)
332 print("=== test loadstore instruction (try instruction again) ===")
333 yield from debug(dut
, "instr virt retry")
334 # set address to 0x10200, update mem[] to 5678
336 real_addr
= virt_addr
337 expected_insn
= 0x5678
339 yield i_in
.priv_mode
.eq(0)
340 yield i_in
.virt_mode
.eq(1)
342 yield i_in
.nia
.eq(virt_addr
)
343 yield i_in
.stop_mark
.eq(0)
344 yield i_m_in
.tlbld
.eq(0)
345 yield i_m_in
.tlbie
.eq(0)
346 yield i_m_in
.addr
.eq(0)
347 yield i_m_in
.pte
.eq(0)
352 # miss, stalls for a bit
355 yield i_in.nia.eq(virt_addr)
357 valid = yield i_out.valid
358 failed = yield i_out.fetch_failed
359 while not valid and not failed:
361 valid = yield i_out.valid
362 failed = yield i_out.fetch_failed
364 nia = yield i_out.nia
365 insn = yield i_out.insn
369 nia
, insn
, valid
, failed
= yield from icache_read(dut
,virt_addr
,0,1)
371 yield from debug(dut
, "test done")
375 print ("failed?", "yes" if failed
else "no")
378 print ("fetched %x from addr %x" % (insn
, nia
))
379 assert insn
== expected_insn
384 def _test_loadstore1_invalid(dut
, mem
):
385 mmu
= dut
.submodules
.mmu
386 pi
= dut
.submodules
.ldst
.pi
389 print("=== test invalid ===")
392 ld_data
, exctype
, exc
= yield from pi_ld(pi
, addr
, 8, msr_pr
=1)
393 print("ld_data", ld_data
, exctype
, exc
)
394 assert (exctype
== "slow")
395 invalid
= exc
.invalid
396 assert (invalid
== 1)
398 print("=== test invalid done ===")
403 def _test_loadstore1(dut
, mem
):
404 mmu
= dut
.submodules
.mmu
405 pi
= dut
.submodules
.ldst
.pi
406 ldst
= dut
.submodules
.ldst
# to get at DAR (NOT part of PortInterface)
409 yield mmu
.rin
.prtbl
.eq(0x1000000) # set process table
413 data
= 0xf553b658ba7e1f51
416 yield from pi_st(pi
, addr
, data
, 8, msr_pr
=1)
419 ld_data
, exctype
, exc
= yield from pi_ld(pi
, addr
, 8, msr_pr
=1)
420 assert ld_data
== 0xf553b658ba7e1f51
421 assert exctype
is None
423 ld_data
, exctype
, exc
= yield from pi_ld(pi
, addr
, 8, msr_pr
=1)
424 assert ld_data
== 0xf553b658ba7e1f51
425 assert exctype
is None
427 print("do_dcbz ===============")
428 yield from pi_st(pi
, addr
, data
, 8, msr_pr
=1, is_dcbz
=1)
429 print("done_dcbz ===============")
432 ld_data
, exctype
, exc
= yield from pi_ld(pi
, addr
, 8, msr_pr
=1)
433 print("ld_data after dcbz")
436 assert exctype
is None
439 print("=== alignment error (ld) ===")
441 ld_data
, exctype
, exc
= yield from pi_ld(pi
, addr
, 8, msr_pr
=1)
443 alignment
= exc
.alignment
444 happened
= exc
.happened
445 yield # wait for dsr to update
451 assert (happened
== 1)
452 assert (alignment
== 1)
454 assert (exctype
== "fast")
455 yield from wait_busy(pi
, debug
="pi_ld_E_alignment_error")
456 # wait is only needed in case of in exception here
457 print("=== alignment error test passed (ld) ===")
459 # take some cycles in between so that gtkwave separates out
466 print("=== alignment error (st) ===")
468 exctype
, exc
= yield from pi_st(pi
, addr
,0, 8, msr_pr
=1)
470 alignment
= exc
.alignment
471 happened
= exc
.happened
475 assert (happened
== 1)
476 assert (alignment
==1)
478 assert (exctype
== "fast")
479 #???? yield from wait_busy(pi, debug="pi_st_E_alignment_error")
480 # wait is only needed in case of in exception here
481 print("=== alignment error test passed (st) ===")
485 print("=== no alignment error (ld) ===")
487 ld_data
, exctype
, exc
= yield from pi_ld(pi
, addr
, 8, msr_pr
=1)
488 print("ld_data", ld_data
, exctype
, exc
)
490 alignment
= exc
.alignment
491 happened
= exc
.happened
495 assert (happened
== 0)
496 assert (alignment
== 0)
497 print("=== no alignment error done (ld) ===")
500 addrs
= [0x456920,0xa7a180,0x299420,0x1d9d60]
503 print("== RANDOM addr ==",hex(addr
))
504 ld_data
, exctype
, exc
= yield from pi_ld(pi
, addr
, 8, msr_pr
=1)
505 print("ld_data[RANDOM]",ld_data
,exc
,addr
)
506 assert (exctype
== None)
509 print("== RANDOM addr ==",hex(addr
))
510 exc
= yield from pi_st(pi
, addr
,0xFF*addr
, 8, msr_pr
=1)
511 assert (exctype
== None)
513 # readback written data and compare
515 print("== RANDOM addr ==",hex(addr
))
516 ld_data
, exctype
, exc
= yield from pi_ld(pi
, addr
, 8, msr_pr
=1)
517 print("ld_data[RANDOM_READBACK]",ld_data
,exc
,addr
)
518 assert (exctype
== None)
519 assert (ld_data
== 0xFF*addr
)
521 print("== RANDOM addr done ==")
526 def _test_loadstore1_ifetch_invalid(dut
, mem
):
527 mmu
= dut
.submodules
.mmu
528 ldst
= dut
.submodules
.ldst
530 icache
= dut
.submodules
.ldst
.icache
533 print("=== test loadstore instruction (invalid) ===")
539 # first virtual memory test
541 print ("set process table")
542 yield from debug(dut
, "set prtbl")
543 yield mmu
.rin
.prtbl
.eq(0x1000000) # set process table
546 yield from debug(dut
, "real mem instruction")
547 # set address to zero, update mem[0] to 01234
549 expected_insn
= 0x1234
550 mem
[addr
] = expected_insn
552 yield i_in
.priv_mode
.eq(1)
554 yield i_in
.nia
.eq(addr
)
555 yield i_in
.stop_mark
.eq(0)
556 yield i_m_in
.tlbld
.eq(0)
557 yield i_m_in
.tlbie
.eq(0)
558 yield i_m_in
.addr
.eq(0)
559 yield i_m_in
.pte
.eq(0)
564 # miss, stalls for a bit
566 yield i_in
.nia
.eq(addr
)
568 valid
= yield i_out
.valid
569 nia
= yield i_out
.nia
572 valid
= yield i_out
.valid
575 nia
= yield i_out
.nia
576 insn
= yield i_out
.insn
581 print ("fetched %x from addr %x" % (insn
, nia
))
582 assert insn
== expected_insn
584 print("=== test loadstore instruction (virtual) ===")
585 yield from debug(dut
, "virtual instr req")
587 # look up i-cache expecting it to fail
589 # set address to 0x10200, update mem[] to 5678
591 real_addr
= virt_addr
592 expected_insn
= 0x5678
593 mem
[real_addr
] = expected_insn
595 yield i_in
.priv_mode
.eq(1)
596 yield i_in
.virt_mode
.eq(1)
598 yield i_in
.nia
.eq(virt_addr
)
599 yield i_in
.stop_mark
.eq(0)
600 yield i_m_in
.tlbld
.eq(0)
601 yield i_m_in
.tlbie
.eq(0)
602 yield i_m_in
.addr
.eq(0)
603 yield i_m_in
.pte
.eq(0)
608 # miss, stalls for a bit
610 yield i_in
.nia
.eq(virt_addr
)
612 valid
= yield i_out
.valid
613 failed
= yield i_out
.fetch_failed
614 while not valid
and not failed
:
616 valid
= yield i_out
.valid
617 failed
= yield i_out
.fetch_failed
620 print ("failed?", "yes" if failed
else "no")
625 print("=== test invalid loadstore instruction (instruction fault) ===")
627 yield from debug(dut
, "instr fault (perm err expected)")
630 yield ldst
.priv_mode
.eq(0)
631 yield ldst
.instr_fault
.eq(1)
632 yield ldst
.maddr
.eq(virt_addr
)
633 #ld_data, exctype, exc = yield from pi_ld(pi, virt_addr, 8, msr_pr=1)
635 yield ldst
.instr_fault
.eq(0)
637 done
= yield (ldst
.done
)
638 exc_info
= yield from get_exception_info(pi
.exc_o
)
639 if done
or exc_info
.happened
:
642 assert exc_info
.happened
== 1 # different here as expected
644 # TODO: work out what kind of exception occurred and check it's
645 # the right one. we *expect* it to be a permissions error because
646 # the RPTE leaf node in pagetables.test2 is marked as "non-executable"
647 # but we also expect instr_fault to be set because it is an instruction
649 print (" MMU lookup exception type?")
650 for fname
in LDSTExceptionTuple
._fields
:
651 print (" fname %20s %d" % (fname
, getattr(exc_info
, fname
)))
653 # ok now printed them out and visually inspected: check them with asserts
654 assert exc_info
.instr_fault
== 1 # instruction fault (yes!)
655 assert exc_info
.perm_error
== 1 # permissions (yes!)
656 assert exc_info
.rc_error
== 0
657 assert exc_info
.alignment
== 0
658 assert exc_info
.invalid
== 0
659 assert exc_info
.segment_fault
== 0
660 assert exc_info
.rc_error
== 0
662 yield from debug(dut
, "test done")
663 yield ldst
.instr_fault
.eq(0)
671 def test_loadstore1_ifetch_unit_iface():
673 m
, cmpi
= setup_mmu()
675 mem
= pagetables
.test1
677 # set this up before passing to Simulator (which calls elaborate)
678 icache
= m
.submodules
.ldst
.icache
679 icache
.use_fetch_interface() # this is the function which converts
680 # to FetchUnitInterface. *including*
681 # rewiring the Wishbone Bus to ibus
687 sim
.add_sync_process(wrap(_test_loadstore1_ifetch_iface(m
, mem
)))
688 # add two wb_get processes onto the *same* memory dictionary.
689 # this shouuuld work.... cross-fingers...
690 sim
.add_sync_process(wrap(wb_get(cmpi
.wb_bus(), mem
)))
691 sim
.add_sync_process(wrap(wb_get(icache
.ibus
, mem
))) # ibus not bus
692 with sim
.write_vcd('test_loadstore1_ifetch_iface.vcd',
693 traces
=[m
.debug_status
]): # include extra debug
697 def test_loadstore1_ifetch():
699 m
, cmpi
= setup_mmu()
701 mem
= pagetables
.test1
707 icache
= m
.submodules
.ldst
.icache
708 sim
.add_sync_process(wrap(_test_loadstore1_ifetch(m
, mem
)))
709 # add two wb_get processes onto the *same* memory dictionary.
710 # this shouuuld work.... cross-fingers...
711 sim
.add_sync_process(wrap(wb_get(cmpi
.wb_bus(), mem
)))
712 sim
.add_sync_process(wrap(wb_get(icache
.bus
, mem
)))
713 with sim
.write_vcd('test_loadstore1_ifetch.vcd',
714 traces
=[m
.debug_status
]): # include extra debug
718 def test_loadstore1():
720 m
, cmpi
= setup_mmu()
722 mem
= pagetables
.test1
728 sim
.add_sync_process(wrap(_test_loadstore1(m
, mem
)))
729 sim
.add_sync_process(wrap(wb_get(cmpi
.wb_bus(), mem
)))
730 with sim
.write_vcd('test_loadstore1.vcd'):
734 def test_loadstore1_invalid():
736 m
, cmpi
= setup_mmu()
744 sim
.add_sync_process(wrap(_test_loadstore1_invalid(m
, mem
)))
745 sim
.add_sync_process(wrap(wb_get(cmpi
.wb_bus(), mem
)))
746 with sim
.write_vcd('test_loadstore1_invalid.vcd'):
749 def test_loadstore1_ifetch_invalid():
750 m
, cmpi
= setup_mmu()
752 # this is a specially-arranged page table which has the permissions
753 # barred for execute on the leaf node (EAA=0x2 instead of EAA=0x3)
754 mem
= pagetables
.test2
760 icache
= m
.submodules
.ldst
.icache
761 sim
.add_sync_process(wrap(_test_loadstore1_ifetch_invalid(m
, mem
)))
762 # add two wb_get processes onto the *same* memory dictionary.
763 # this shouuuld work.... cross-fingers...
764 sim
.add_sync_process(wrap(wb_get(cmpi
.wb_bus(), mem
)))
765 sim
.add_sync_process(wrap(wb_get(icache
.bus
, mem
)))
766 with sim
.write_vcd('test_loadstore1_ifetch_invalid.vcd',
767 traces
=[m
.debug_status
]): # include extra debug
770 def test_loadstore1_ifetch_multi():
771 m
, cmpi
= setup_mmu()
773 # this is a specially-arranged page table which has the permissions
774 # barred for execute on the leaf node (EAA=0x2 instead of EAA=0x3)
775 mem
= pagetables
.test1
781 icache
= m
.submodules
.ldst
.icache
782 sim
.add_sync_process(wrap(_test_loadstore1_ifetch_multi(m
, mem
)))
783 # add two wb_get processes onto the *same* memory dictionary.
784 # this shouuuld work.... cross-fingers...
785 sim
.add_sync_process(wrap(wb_get(cmpi
.wb_bus(), mem
)))
786 sim
.add_sync_process(wrap(wb_get(icache
.bus
, mem
)))
787 with sim
.write_vcd('test_loadstore1_ifetch_multi.vcd',
788 traces
=[m
.debug_status
]): # include extra debug
791 if __name__
== '__main__':
793 test_loadstore1_invalid()
794 test_loadstore1_ifetch()
795 test_loadstore1_ifetch_invalid()
796 test_loadstore1_ifetch_multi()
797 test_loadstore1_ifetch_unit_iface()