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
187 def _test_loadstore1_ifetch(dut
, mem
):
188 """test_loadstore1_ifetch
190 this is quite a complex multi-step test.
192 * first (just because, as a demo) read in priv mode, non-virtual.
193 just like in experiment/icache.py itself.
195 * second, using the (usual) PTE for these things (which came originally
196 from gem5-experimental experiment/radix_walk_example.txt) do a
197 virtual-memory read through the *instruction* cache.
198 this is expected to FAIL
200 * third: mess about with the MMU, setting "iside" (instruction-side),
201 requesting an MMU RADIX LOOKUP. this triggers an itlb_load
202 (instruction-cache TLB entry-insertion)
204 * fourth and finally: retry the read of the instruction through i-cache.
205 this is now expected to SUCCEED
210 mmu
= dut
.submodules
.mmu
211 ldst
= dut
.submodules
.ldst
213 icache
= dut
.submodules
.ldst
.icache
216 print("=== test loadstore instruction (real) ===")
222 # first virtual memory test
224 print ("set process table")
225 yield from debug(dut
, "set prtble")
226 yield mmu
.rin
.prtbl
.eq(0x1000000) # set process table
229 yield from debug(dut
, "real mem instruction")
230 # set address to zero, update mem[0] to 01234
232 expected_insn
= 0x1234
233 mem
[addr
] = expected_insn
235 yield i_in
.priv_mode
.eq(1)
237 yield i_in
.nia
.eq(addr
)
238 yield i_in
.stop_mark
.eq(0)
239 yield i_m_in
.tlbld
.eq(0)
240 yield i_m_in
.tlbie
.eq(0)
241 yield i_m_in
.addr
.eq(0)
242 yield i_m_in
.pte
.eq(0)
247 # miss, stalls for a bit -- this one is different here
248 ##nia, insn, valid, failed = yield from icache_read(dut,addr,0,0)
253 yield i_in
.nia
.eq(addr
)
255 valid
= yield i_out
.valid
258 valid
= yield i_out
.valid
261 nia
= yield i_out
.nia
262 insn
= yield i_out
.insn
266 print ("fetched %x from addr %x" % (insn
, nia
))
267 assert insn
== expected_insn
269 print("=== test loadstore instruction (virtual) ===")
271 # look up i-cache expecting it to fail
273 yield from debug(dut
, "virtual instr req")
274 # set address to 0x10200, update mem[] to 5678
276 real_addr
= virt_addr
277 expected_insn
= 0x5678
278 mem
[real_addr
] = expected_insn
280 yield i_in
.priv_mode
.eq(0)
281 yield i_in
.virt_mode
.eq(1)
283 yield i_in
.nia
.eq(virt_addr
)
284 yield i_in
.stop_mark
.eq(0)
285 yield i_m_in
.tlbld
.eq(0)
286 yield i_m_in
.tlbie
.eq(0)
287 yield i_m_in
.addr
.eq(0)
288 yield i_m_in
.pte
.eq(0)
293 # miss, stalls for a bit
295 yield i_in
.nia
.eq(virt_addr
)
297 valid
= yield i_out
.valid
298 failed
= yield i_out
.fetch_failed
299 while not valid
and not failed
:
301 valid
= yield i_out
.valid
302 failed
= yield i_out
.fetch_failed
305 print ("failed?", "yes" if failed
else "no")
310 print("=== test loadstore instruction (instruction fault) ===")
312 yield from debug(dut
, "instr fault")
316 yield ldst
.priv_mode
.eq(0)
317 yield ldst
.instr_fault
.eq(1)
318 yield ldst
.maddr
.eq(virt_addr
)
319 #ld_data, exctype, exc = yield from pi_ld(pi, virt_addr, 8, msr_pr=1)
321 yield ldst
.instr_fault
.eq(0)
323 done
= yield (ldst
.done
)
324 exc_info
= yield from get_exception_info(pi
.exc_o
)
325 if done
or exc_info
.happened
:
328 assert exc_info
.happened
== 0 # assert just before doing the fault set zero
329 yield ldst
.instr_fault
.eq(0)
334 print("=== test loadstore instruction (try instruction again) ===")
335 yield from debug(dut
, "instr virt retry")
336 # set address to 0x10200, update mem[] to 5678
338 real_addr
= virt_addr
339 expected_insn
= 0x5678
341 yield i_in
.priv_mode
.eq(0)
342 yield i_in
.virt_mode
.eq(1)
344 yield i_in
.nia
.eq(virt_addr
)
345 yield i_in
.stop_mark
.eq(0)
346 yield i_m_in
.tlbld
.eq(0)
347 yield i_m_in
.tlbie
.eq(0)
348 yield i_m_in
.addr
.eq(0)
349 yield i_m_in
.pte
.eq(0)
354 # miss, stalls for a bit
357 yield i_in.nia.eq(virt_addr)
359 valid = yield i_out.valid
360 failed = yield i_out.fetch_failed
361 while not valid and not failed:
363 valid = yield i_out.valid
364 failed = yield i_out.fetch_failed
366 nia = yield i_out.nia
367 insn = yield i_out.insn
371 nia
, insn
, valid
, failed
= yield from icache_read(dut
,virt_addr
,0,1)
373 yield from debug(dut
, "test done")
377 print ("failed?", "yes" if failed
else "no")
380 print ("fetched %x from addr %x" % (insn
, nia
))
381 assert insn
== expected_insn
386 def _test_loadstore1_invalid(dut
, mem
):
387 mmu
= dut
.submodules
.mmu
388 pi
= dut
.submodules
.ldst
.pi
391 print("=== test invalid ===")
394 ld_data
, exctype
, exc
= yield from pi_ld(pi
, addr
, 8, msr_pr
=1)
395 print("ld_data", ld_data
, exctype
, exc
)
396 assert (exctype
== "slow")
397 invalid
= exc
.invalid
398 assert (invalid
== 1)
400 print("=== test invalid done ===")
405 def _test_loadstore1(dut
, mem
):
406 mmu
= dut
.submodules
.mmu
407 pi
= dut
.submodules
.ldst
.pi
408 ldst
= dut
.submodules
.ldst
# to get at DAR (NOT part of PortInterface)
411 yield mmu
.rin
.prtbl
.eq(0x1000000) # set process table
415 data
= 0xf553b658ba7e1f51
418 yield from pi_st(pi
, addr
, data
, 8, msr_pr
=1)
421 ld_data
, exctype
, exc
= yield from pi_ld(pi
, addr
, 8, msr_pr
=1)
422 assert ld_data
== 0xf553b658ba7e1f51
423 assert exctype
is None
425 ld_data
, exctype
, exc
= yield from pi_ld(pi
, addr
, 8, msr_pr
=1)
426 assert ld_data
== 0xf553b658ba7e1f51
427 assert exctype
is None
429 print("do_dcbz ===============")
430 yield from pi_st(pi
, addr
, data
, 8, msr_pr
=1, is_dcbz
=1)
431 print("done_dcbz ===============")
434 ld_data
, exctype
, exc
= yield from pi_ld(pi
, addr
, 8, msr_pr
=1)
435 print("ld_data after dcbz")
438 assert exctype
is None
441 print("=== alignment error (ld) ===")
443 ld_data
, exctype
, exc
= yield from pi_ld(pi
, addr
, 8, msr_pr
=1)
445 alignment
= exc
.alignment
446 happened
= exc
.happened
447 yield # wait for dsr to update
453 assert (happened
== 1)
454 assert (alignment
== 1)
456 assert (exctype
== "fast")
457 yield from wait_busy(pi
, debug
="pi_ld_E_alignment_error")
458 # wait is only needed in case of in exception here
459 print("=== alignment error test passed (ld) ===")
461 # take some cycles in between so that gtkwave separates out
468 print("=== alignment error (st) ===")
470 exctype
, exc
= yield from pi_st(pi
, addr
,0, 8, msr_pr
=1)
472 alignment
= exc
.alignment
473 happened
= exc
.happened
477 assert (happened
== 1)
478 assert (alignment
==1)
480 assert (exctype
== "fast")
481 #???? yield from wait_busy(pi, debug="pi_st_E_alignment_error")
482 # wait is only needed in case of in exception here
483 print("=== alignment error test passed (st) ===")
487 print("=== no alignment error (ld) ===")
489 ld_data
, exctype
, exc
= yield from pi_ld(pi
, addr
, 8, msr_pr
=1)
490 print("ld_data", ld_data
, exctype
, exc
)
492 alignment
= exc
.alignment
493 happened
= exc
.happened
497 assert (happened
== 0)
498 assert (alignment
== 0)
499 print("=== no alignment error done (ld) ===")
502 addrs
= [0x456920,0xa7a180,0x299420,0x1d9d60]
505 print("== RANDOM addr ==",hex(addr
))
506 ld_data
, exctype
, exc
= yield from pi_ld(pi
, addr
, 8, msr_pr
=1)
507 print("ld_data[RANDOM]",ld_data
,exc
,addr
)
508 assert (exctype
== None)
511 print("== RANDOM addr ==",hex(addr
))
512 exc
= yield from pi_st(pi
, addr
,0xFF*addr
, 8, msr_pr
=1)
513 assert (exctype
== None)
515 # readback written data and compare
517 print("== RANDOM addr ==",hex(addr
))
518 ld_data
, exctype
, exc
= yield from pi_ld(pi
, addr
, 8, msr_pr
=1)
519 print("ld_data[RANDOM_READBACK]",ld_data
,exc
,addr
)
520 assert (exctype
== None)
521 assert (ld_data
== 0xFF*addr
)
523 print("== RANDOM addr done ==")
528 def _test_loadstore1_ifetch_invalid(dut
, mem
):
529 mmu
= dut
.submodules
.mmu
530 ldst
= dut
.submodules
.ldst
532 icache
= dut
.submodules
.ldst
.icache
535 print("=== test loadstore instruction (invalid) ===")
541 # first virtual memory test
543 print ("set process table")
544 yield from debug(dut
, "set prtbl")
545 yield mmu
.rin
.prtbl
.eq(0x1000000) # set process table
548 yield from debug(dut
, "real mem instruction")
549 # set address to zero, update mem[0] to 01234
551 expected_insn
= 0x1234
552 mem
[addr
] = expected_insn
554 yield i_in
.priv_mode
.eq(1)
556 yield i_in
.nia
.eq(addr
)
557 yield i_in
.stop_mark
.eq(0)
558 yield i_m_in
.tlbld
.eq(0)
559 yield i_m_in
.tlbie
.eq(0)
560 yield i_m_in
.addr
.eq(0)
561 yield i_m_in
.pte
.eq(0)
566 # miss, stalls for a bit
568 yield i_in
.nia
.eq(addr
)
570 valid
= yield i_out
.valid
571 nia
= yield i_out
.nia
574 valid
= yield i_out
.valid
577 nia
= yield i_out
.nia
578 insn
= yield i_out
.insn
583 print ("fetched %x from addr %x" % (insn
, nia
))
584 assert insn
== expected_insn
586 print("=== test loadstore instruction (virtual) ===")
587 yield from debug(dut
, "virtual instr req")
589 # look up i-cache expecting it to fail
591 # set address to 0x10200, update mem[] to 5678
593 real_addr
= virt_addr
594 expected_insn
= 0x5678
595 mem
[real_addr
] = expected_insn
597 yield i_in
.priv_mode
.eq(1)
598 yield i_in
.virt_mode
.eq(1)
600 yield i_in
.nia
.eq(virt_addr
)
601 yield i_in
.stop_mark
.eq(0)
602 yield i_m_in
.tlbld
.eq(0)
603 yield i_m_in
.tlbie
.eq(0)
604 yield i_m_in
.addr
.eq(0)
605 yield i_m_in
.pte
.eq(0)
610 # miss, stalls for a bit
612 yield i_in
.nia
.eq(virt_addr
)
614 valid
= yield i_out
.valid
615 failed
= yield i_out
.fetch_failed
616 while not valid
and not failed
:
618 valid
= yield i_out
.valid
619 failed
= yield i_out
.fetch_failed
622 print ("failed?", "yes" if failed
else "no")
627 print("=== test invalid loadstore instruction (instruction fault) ===")
629 yield from debug(dut
, "instr fault (perm err expected)")
632 yield ldst
.priv_mode
.eq(0)
633 yield ldst
.instr_fault
.eq(1)
634 yield ldst
.maddr
.eq(virt_addr
)
635 #ld_data, exctype, exc = yield from pi_ld(pi, virt_addr, 8, msr_pr=1)
637 yield ldst
.instr_fault
.eq(0)
639 done
= yield (ldst
.done
)
640 exc_info
= yield from get_exception_info(pi
.exc_o
)
641 if done
or exc_info
.happened
:
644 assert exc_info
.happened
== 1 # different here as expected
646 # TODO: work out what kind of exception occurred and check it's
647 # the right one. we *expect* it to be a permissions error because
648 # the RPTE leaf node in pagetables.test2 is marked as "non-executable"
649 # but we also expect instr_fault to be set because it is an instruction
651 print (" MMU lookup exception type?")
652 for fname
in LDSTExceptionTuple
._fields
:
653 print (" fname %20s %d" % (fname
, getattr(exc_info
, fname
)))
655 # ok now printed them out and visually inspected: check them with asserts
656 assert exc_info
.instr_fault
== 1 # instruction fault (yes!)
657 assert exc_info
.perm_error
== 1 # permissions (yes!)
658 assert exc_info
.rc_error
== 0
659 assert exc_info
.alignment
== 0
660 assert exc_info
.invalid
== 0
661 assert exc_info
.segment_fault
== 0
662 assert exc_info
.rc_error
== 0
664 yield from debug(dut
, "test done")
665 yield ldst
.instr_fault
.eq(0)
673 def test_loadstore1_ifetch_unit_iface():
675 m
, cmpi
= setup_mmu()
677 mem
= pagetables
.test1
683 icache
= m
.submodules
.ldst
.icache
684 icache
.use_fetch_interface() # this is the function which converts
685 # to FetchUnitInterface. *including*
686 # 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
772 if __name__
== '__main__':
774 test_loadstore1_invalid()
775 test_loadstore1_ifetch()
776 test_loadstore1_fetch_unit_iface()
777 test_loadstore1_ifetch_invalid()