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 = [0x10200,0x10204,10208,10200]
190 mem
[0x10200]=0xFF00FF00EE00EE00EE
191 mem
[0]=0xFF00FF00EE00EE00EE
193 yield i_in
.priv_mode
.eq(1)
196 yield from debug(dut
, "BROKEN_fetch_from "+hex(addr
))
197 # use the new interface in this test
199 #broken: does not use wishbone yet - investigate
200 insn
= yield from read_from_addr(icache
, addr
, stall
=False)
202 nia
= yield i_out
.nia
# NO, must use FetchUnitInterface
203 print ("fetched %x from addr %x" % (insn
, nia
))
207 def _test_loadstore1_ifetch(dut
, mem
):
208 """test_loadstore1_ifetch
210 this is quite a complex multi-step test.
212 * first (just because, as a demo) read in priv mode, non-virtual.
213 just like in experiment/icache.py itself.
215 * second, using the (usual) PTE for these things (which came originally
216 from gem5-experimental experiment/radix_walk_example.txt) do a
217 virtual-memory read through the *instruction* cache.
218 this is expected to FAIL
220 * third: mess about with the MMU, setting "iside" (instruction-side),
221 requesting an MMU RADIX LOOKUP. this triggers an itlb_load
222 (instruction-cache TLB entry-insertion)
224 * fourth and finally: retry the read of the instruction through i-cache.
225 this is now expected to SUCCEED
230 mmu
= dut
.submodules
.mmu
231 ldst
= dut
.submodules
.ldst
233 icache
= dut
.submodules
.ldst
.icache
236 print("=== test loadstore instruction (real) ===")
242 # first virtual memory test
244 print ("set process table")
245 yield from debug(dut
, "set prtble")
246 yield mmu
.rin
.prtbl
.eq(0x1000000) # set process table
249 yield from debug(dut
, "real mem instruction")
250 # set address to zero, update mem[0] to 01234
252 expected_insn
= 0x1234
253 mem
[addr
] = expected_insn
255 yield i_in
.priv_mode
.eq(1)
257 yield i_in
.nia
.eq(addr
)
258 yield i_in
.stop_mark
.eq(0)
259 yield i_m_in
.tlbld
.eq(0)
260 yield i_m_in
.tlbie
.eq(0)
261 yield i_m_in
.addr
.eq(0)
262 yield i_m_in
.pte
.eq(0)
267 # miss, stalls for a bit -- this one is different here
268 ##nia, insn, valid, failed = yield from icache_read(dut,addr,0,0)
273 yield i_in
.nia
.eq(addr
)
275 valid
= yield i_out
.valid
278 valid
= yield i_out
.valid
281 nia
= yield i_out
.nia
282 insn
= yield i_out
.insn
286 print ("fetched %x from addr %x" % (insn
, nia
))
287 assert insn
== expected_insn
289 print("=== test loadstore instruction (virtual) ===")
291 # look up i-cache expecting it to fail
293 yield from debug(dut
, "virtual instr req")
294 # set address to 0x10200, update mem[] to 5678
296 real_addr
= virt_addr
297 expected_insn
= 0x5678
298 mem
[real_addr
] = expected_insn
300 yield i_in
.priv_mode
.eq(0)
301 yield i_in
.virt_mode
.eq(1)
303 yield i_in
.nia
.eq(virt_addr
)
304 yield i_in
.stop_mark
.eq(0)
305 yield i_m_in
.tlbld
.eq(0)
306 yield i_m_in
.tlbie
.eq(0)
307 yield i_m_in
.addr
.eq(0)
308 yield i_m_in
.pte
.eq(0)
313 # miss, stalls for a bit
315 yield i_in
.nia
.eq(virt_addr
)
317 valid
= yield i_out
.valid
318 failed
= yield i_out
.fetch_failed
319 while not valid
and not failed
:
321 valid
= yield i_out
.valid
322 failed
= yield i_out
.fetch_failed
325 print ("failed?", "yes" if failed
else "no")
330 print("=== test loadstore instruction (instruction fault) ===")
332 yield from debug(dut
, "instr fault")
336 yield ldst
.priv_mode
.eq(0)
337 yield ldst
.instr_fault
.eq(1)
338 yield ldst
.maddr
.eq(virt_addr
)
339 # still broken -- investigate
340 # msr = MSRSpec(pr=?, dr=?, sf=0)
341 # ld_data, exctype, exc = yield from pi_ld(pi, virt_addr, 8, msr=msr)
343 yield ldst
.instr_fault
.eq(0)
345 done
= yield (ldst
.done
)
346 exc_info
= yield from get_exception_info(pi
.exc_o
)
347 if done
or exc_info
.happened
:
350 assert exc_info
.happened
== 0 # assert just before doing the fault set zero
351 yield ldst
.instr_fault
.eq(0)
356 print("=== test loadstore instruction (try instruction again) ===")
357 yield from debug(dut
, "instr virt retry")
358 # set address to 0x10200, update mem[] to 5678
360 real_addr
= virt_addr
361 expected_insn
= 0x5678
363 yield i_in
.priv_mode
.eq(0)
364 yield i_in
.virt_mode
.eq(1)
366 yield i_in
.nia
.eq(virt_addr
)
367 yield i_in
.stop_mark
.eq(0)
368 yield i_m_in
.tlbld
.eq(0)
369 yield i_m_in
.tlbie
.eq(0)
370 yield i_m_in
.addr
.eq(0)
371 yield i_m_in
.pte
.eq(0)
376 # miss, stalls for a bit
379 yield i_in.nia.eq(virt_addr)
381 valid = yield i_out.valid
382 failed = yield i_out.fetch_failed
383 while not valid and not failed:
385 valid = yield i_out.valid
386 failed = yield i_out.fetch_failed
388 nia = yield i_out.nia
389 insn = yield i_out.insn
393 nia
, insn
, valid
, failed
= yield from icache_read(dut
,virt_addr
,0,1)
395 yield from debug(dut
, "test done")
399 print ("failed?", "yes" if failed
else "no")
402 print ("fetched %x from addr %x" % (insn
, nia
))
403 assert insn
== expected_insn
408 def _test_loadstore1_invalid(dut
, mem
):
409 mmu
= dut
.submodules
.mmu
410 pi
= dut
.submodules
.ldst
.pi
413 print("=== test invalid ===")
416 msr
= MSRSpec(pr
=1, dr
=0, sf
=0) # set problem-state
417 ld_data
, exctype
, exc
= yield from pi_ld(pi
, addr
, 8, msr
=msr
)
418 print("ld_data", ld_data
, exctype
, exc
)
419 assert (exctype
== "slow")
420 invalid
= exc
.invalid
421 assert (invalid
== 1)
423 print("=== test invalid done ===")
428 def _test_loadstore1(dut
, mem
):
429 mmu
= dut
.submodules
.mmu
430 pi
= dut
.submodules
.ldst
.pi
431 ldst
= dut
.submodules
.ldst
# to get at DAR (NOT part of PortInterface)
434 yield mmu
.rin
.prtbl
.eq(0x1000000) # set process table
438 data
= 0xf553b658ba7e1f51
441 msr
= MSRSpec(pr
=0, dr
=0, sf
=0)
442 yield from pi_st(pi
, addr
, data
, 8, msr
=msr
)
445 ld_data
, exctype
, exc
= yield from pi_ld(pi
, addr
, 8, msr
=msr
)
446 assert ld_data
== 0xf553b658ba7e1f51
447 assert exctype
is None
449 ld_data
, exctype
, exc
= yield from pi_ld(pi
, addr
, 8, msr
=msr
)
450 assert ld_data
== 0xf553b658ba7e1f51
451 assert exctype
is None
453 print("do_dcbz ===============")
454 yield from pi_st(pi
, addr
, data
, 8, msr
=msr
, is_dcbz
=1)
455 print("done_dcbz ===============")
458 ld_data
, exctype
, exc
= yield from pi_ld(pi
, addr
, 8, msr
=msr
)
459 print("ld_data after dcbz")
462 assert exctype
is None
465 print("=== alignment error (ld) ===")
467 ld_data
, exctype
, exc
= yield from pi_ld(pi
, addr
, 8, msr
=msr
)
469 alignment
= exc
.alignment
470 happened
= exc
.happened
471 yield # wait for dsr to update
477 assert (happened
== 1)
478 assert (alignment
== 1)
480 assert (exctype
== "fast")
481 yield from wait_busy(pi
, debug
="pi_ld_E_alignment_error")
482 # wait is only needed in case of in exception here
483 print("=== alignment error test passed (ld) ===")
485 # take some cycles in between so that gtkwave separates out
492 print("=== alignment error (st) ===")
494 exctype
, exc
= yield from pi_st(pi
, addr
,0, 8, msr
=msr
)
496 alignment
= exc
.alignment
497 happened
= exc
.happened
501 assert (happened
== 1)
502 assert (alignment
==1)
504 assert (exctype
== "fast")
505 #???? yield from wait_busy(pi, debug="pi_st_E_alignment_error")
506 # wait is only needed in case of in exception here
507 print("=== alignment error test passed (st) ===")
511 print("=== no alignment error (ld) ===")
513 ld_data
, exctype
, exc
= yield from pi_ld(pi
, addr
, 8, msr
=msr
)
514 print("ld_data", ld_data
, exctype
, exc
)
516 alignment
= exc
.alignment
517 happened
= exc
.happened
521 assert (happened
== 0)
522 assert (alignment
== 0)
523 print("=== no alignment error done (ld) ===")
526 addrs
= [0x456920,0xa7a180,0x299420,0x1d9d60]
529 print("== RANDOM addr ==",hex(addr
))
530 ld_data
, exctype
, exc
= yield from pi_ld(pi
, addr
, 8, msr
=msr
)
531 print("ld_data[RANDOM]",ld_data
,exc
,addr
)
532 assert (exctype
== None)
535 print("== RANDOM addr ==",hex(addr
))
536 exc
= yield from pi_st(pi
, addr
,0xFF*addr
, 8, msr
=msr
)
537 assert (exctype
== None)
539 # readback written data and compare
541 print("== RANDOM addr ==",hex(addr
))
542 ld_data
, exctype
, exc
= yield from pi_ld(pi
, addr
, 8, msr
=msr
)
543 print("ld_data[RANDOM_READBACK]",ld_data
,exc
,addr
)
544 assert (exctype
== None)
545 assert (ld_data
== 0xFF*addr
)
547 print("== RANDOM addr done ==")
552 def _test_loadstore1_ifetch_invalid(dut
, mem
):
553 mmu
= dut
.submodules
.mmu
554 ldst
= dut
.submodules
.ldst
556 icache
= dut
.submodules
.ldst
.icache
559 print("=== test loadstore instruction (invalid) ===")
565 # first virtual memory test
567 print ("set process table")
568 yield from debug(dut
, "set prtbl")
569 yield mmu
.rin
.prtbl
.eq(0x1000000) # set process table
572 yield from debug(dut
, "real mem instruction")
573 # set address to zero, update mem[0] to 01234
575 expected_insn
= 0x1234
576 mem
[addr
] = expected_insn
578 yield i_in
.priv_mode
.eq(1)
580 yield i_in
.nia
.eq(addr
)
581 yield i_in
.stop_mark
.eq(0)
582 yield i_m_in
.tlbld
.eq(0)
583 yield i_m_in
.tlbie
.eq(0)
584 yield i_m_in
.addr
.eq(0)
585 yield i_m_in
.pte
.eq(0)
590 # miss, stalls for a bit
592 yield i_in
.nia
.eq(addr
)
594 valid
= yield i_out
.valid
595 nia
= yield i_out
.nia
598 valid
= yield i_out
.valid
601 nia
= yield i_out
.nia
602 insn
= yield i_out
.insn
607 print ("fetched %x from addr %x" % (insn
, nia
))
608 assert insn
== expected_insn
610 print("=== test loadstore instruction (virtual) ===")
611 yield from debug(dut
, "virtual instr req")
613 # look up i-cache expecting it to fail
615 # set address to 0x10200, update mem[] to 5678
617 real_addr
= virt_addr
618 expected_insn
= 0x5678
619 mem
[real_addr
] = expected_insn
621 yield i_in
.priv_mode
.eq(1)
622 yield i_in
.virt_mode
.eq(1)
624 yield i_in
.nia
.eq(virt_addr
)
625 yield i_in
.stop_mark
.eq(0)
626 yield i_m_in
.tlbld
.eq(0)
627 yield i_m_in
.tlbie
.eq(0)
628 yield i_m_in
.addr
.eq(0)
629 yield i_m_in
.pte
.eq(0)
634 # miss, stalls for a bit
636 yield i_in
.nia
.eq(virt_addr
)
638 valid
= yield i_out
.valid
639 failed
= yield i_out
.fetch_failed
640 while not valid
and not failed
:
642 valid
= yield i_out
.valid
643 failed
= yield i_out
.fetch_failed
646 print ("failed?", "yes" if failed
else "no")
651 print("=== test invalid loadstore instruction (instruction fault) ===")
653 yield from debug(dut
, "instr fault (perm err expected)")
656 yield ldst
.priv_mode
.eq(0)
657 yield ldst
.instr_fault
.eq(1)
658 yield ldst
.maddr
.eq(virt_addr
)
659 #ld_data, exctype, exc = yield from pi_ld(pi, virt_addr, 8, msr=msr)
661 yield ldst
.instr_fault
.eq(0)
663 done
= yield (ldst
.done
)
664 exc_info
= yield from get_exception_info(pi
.exc_o
)
665 if done
or exc_info
.happened
:
668 assert exc_info
.happened
== 1 # different here as expected
670 # TODO: work out what kind of exception occurred and check it's
671 # the right one. we *expect* it to be a permissions error because
672 # the RPTE leaf node in pagetables.test2 is marked as "non-executable"
673 # but we also expect instr_fault to be set because it is an instruction
675 print (" MMU lookup exception type?")
676 for fname
in LDSTExceptionTuple
._fields
:
677 print (" fname %20s %d" % (fname
, getattr(exc_info
, fname
)))
679 # ok now printed them out and visually inspected: check them with asserts
680 assert exc_info
.instr_fault
== 1 # instruction fault (yes!)
681 assert exc_info
.perm_error
== 1 # permissions (yes!)
682 assert exc_info
.rc_error
== 0
683 assert exc_info
.alignment
== 0
684 assert exc_info
.invalid
== 0
685 assert exc_info
.segment_fault
== 0
686 assert exc_info
.rc_error
== 0
688 yield from debug(dut
, "test done")
689 yield ldst
.instr_fault
.eq(0)
697 def test_loadstore1_ifetch_unit_iface():
699 m
, cmpi
= setup_mmu()
701 mem
= pagetables
.test1
703 # set this up before passing to Simulator (which calls elaborate)
704 icache
= m
.submodules
.ldst
.icache
705 icache
.use_fetch_interface() # this is the function which converts
706 # to FetchUnitInterface. *including*
707 # rewiring the Wishbone Bus to ibus
713 sim
.add_sync_process(wrap(_test_loadstore1_ifetch_iface(m
, mem
)))
714 # add two wb_get processes onto the *same* memory dictionary.
715 # this shouuuld work.... cross-fingers...
716 sim
.add_sync_process(wrap(wb_get(cmpi
.wb_bus(), mem
)))
717 sim
.add_sync_process(wrap(wb_get(icache
.ibus
, mem
))) # ibus not bus
718 with sim
.write_vcd('test_loadstore1_ifetch_iface.vcd',
719 traces
=[m
.debug_status
]): # include extra debug
723 def test_loadstore1_ifetch():
725 m
, cmpi
= setup_mmu()
727 mem
= pagetables
.test1
733 icache
= m
.submodules
.ldst
.icache
734 sim
.add_sync_process(wrap(_test_loadstore1_ifetch(m
, mem
)))
735 # add two wb_get processes onto the *same* memory dictionary.
736 # this shouuuld work.... cross-fingers...
737 sim
.add_sync_process(wrap(wb_get(cmpi
.wb_bus(), mem
)))
738 sim
.add_sync_process(wrap(wb_get(icache
.bus
, mem
)))
739 with sim
.write_vcd('test_loadstore1_ifetch.vcd',
740 traces
=[m
.debug_status
]): # include extra debug
744 def test_loadstore1():
746 m
, cmpi
= setup_mmu()
748 mem
= pagetables
.test1
754 sim
.add_sync_process(wrap(_test_loadstore1(m
, mem
)))
755 sim
.add_sync_process(wrap(wb_get(cmpi
.wb_bus(), mem
)))
756 with sim
.write_vcd('test_loadstore1.vcd'):
760 def test_loadstore1_invalid():
762 m
, cmpi
= setup_mmu()
770 sim
.add_sync_process(wrap(_test_loadstore1_invalid(m
, mem
)))
771 sim
.add_sync_process(wrap(wb_get(cmpi
.wb_bus(), mem
)))
772 with sim
.write_vcd('test_loadstore1_invalid.vcd'):
775 def test_loadstore1_ifetch_invalid():
776 m
, cmpi
= setup_mmu()
778 # this is a specially-arranged page table which has the permissions
779 # barred for execute on the leaf node (EAA=0x2 instead of EAA=0x3)
780 mem
= pagetables
.test2
786 icache
= m
.submodules
.ldst
.icache
787 sim
.add_sync_process(wrap(_test_loadstore1_ifetch_invalid(m
, mem
)))
788 # add two wb_get processes onto the *same* memory dictionary.
789 # this shouuuld work.... cross-fingers...
790 sim
.add_sync_process(wrap(wb_get(cmpi
.wb_bus(), mem
)))
791 sim
.add_sync_process(wrap(wb_get(icache
.bus
, mem
)))
792 with sim
.write_vcd('test_loadstore1_ifetch_invalid.vcd',
793 traces
=[m
.debug_status
]): # include extra debug
796 def test_loadstore1_ifetch_multi():
797 m
, cmpi
= setup_mmu()
799 # this is a specially-arranged page table which has the permissions
800 # barred for execute on the leaf node (EAA=0x2 instead of EAA=0x3)
801 mem
= pagetables
.test1
807 icache
= m
.submodules
.ldst
.icache
808 icache
.use_fetch_interface() # see test_loadstore1_ifetch_unit_iface():
810 sim
.add_sync_process(wrap(_test_loadstore1_ifetch_multi(m
, mem
)))
811 # add two wb_get processes onto the *same* memory dictionary.
812 # this shouuuld work.... cross-fingers...
813 sim
.add_sync_process(wrap(wb_get(cmpi
.wb_bus(), mem
)))
814 sim
.add_sync_process(wrap(wb_get(icache
.bus
, mem
)))
815 with sim
.write_vcd('test_loadstore1_ifetch_multi.vcd',
816 traces
=[m
.debug_status
]): # include extra debug
819 if __name__
== '__main__':
821 test_loadstore1_invalid()
822 test_loadstore1_ifetch() #FIXME
823 test_loadstore1_ifetch_invalid()
824 test_loadstore1_ifetch_multi()
825 test_loadstore1_ifetch_unit_iface()