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 write_mem2(mem
, addr
, i1
, i2
):
171 mem
[addr
] = i1 | i2
<<32
173 def _test_loadstore1_ifetch_multi(dut
, mem
):
174 mmu
= dut
.submodules
.mmu
175 ldst
= dut
.submodules
.ldst
177 icache
= dut
.submodules
.ldst
.icache
178 assert wbget
.stop
== False
184 # TODO fetch instructions from multiple addresses
185 # should cope with some addresses being invalid
186 real_addrs
= [0,4,8,0,8,4,0,0,12]
187 write_mem2(mem
,0,0xF0,0xF4)
188 write_mem2(mem
,8,0xF8,0xFC)
190 yield i_in
.priv_mode
.eq(1)
191 for addr
in real_addrs
:
192 yield from debug(dut
, "real_addr "+hex(addr
))
193 # use the new interface in this test
195 insn
= yield from read_from_addr(icache
, addr
, stall
=False)
196 nia
= yield i_out
.nia
# NO, must use FetchUnitInterface
197 print ("TEST_MULTI: fetched %x from addr %x == %x" % (insn
, nia
,addr
))
198 assert insn
==0xF0+addr
204 def _test_loadstore1_ifetch(dut
, mem
):
205 """test_loadstore1_ifetch
207 this is quite a complex multi-step test.
209 * first (just because, as a demo) read in priv mode, non-virtual.
210 just like in experiment/icache.py itself.
212 * second, using the (usual) PTE for these things (which came originally
213 from gem5-experimental experiment/radix_walk_example.txt) do a
214 virtual-memory read through the *instruction* cache.
215 this is expected to FAIL
217 * third: mess about with the MMU, setting "iside" (instruction-side),
218 requesting an MMU RADIX LOOKUP. this triggers an itlb_load
219 (instruction-cache TLB entry-insertion)
221 * fourth and finally: retry the read of the instruction through i-cache.
222 this is now expected to SUCCEED
227 mmu
= dut
.submodules
.mmu
228 ldst
= dut
.submodules
.ldst
230 icache
= dut
.submodules
.ldst
.icache
233 print("=== test loadstore instruction (real) ===")
239 # first virtual memory test
241 print ("set process table")
242 yield from debug(dut
, "set prtble")
243 yield mmu
.rin
.prtbl
.eq(0x1000000) # set process table
246 yield from debug(dut
, "real mem instruction")
247 # set address to zero, update mem[0] to 01234
249 expected_insn
= 0x1234
250 mem
[addr
] = expected_insn
252 yield i_in
.priv_mode
.eq(1)
254 yield i_in
.nia
.eq(addr
)
255 yield i_in
.stop_mark
.eq(0)
256 yield i_m_in
.tlbld
.eq(0)
257 yield i_m_in
.tlbie
.eq(0)
258 yield i_m_in
.addr
.eq(0)
259 yield i_m_in
.pte
.eq(0)
264 # miss, stalls for a bit -- this one is different here
265 ##nia, insn, valid, failed = yield from icache_read(dut,addr,0,0)
270 yield i_in
.nia
.eq(addr
)
272 valid
= yield i_out
.valid
275 valid
= yield i_out
.valid
278 nia
= yield i_out
.nia
279 insn
= yield i_out
.insn
283 print ("fetched %x from addr %x" % (insn
, nia
))
284 assert insn
== expected_insn
286 print("=== test loadstore instruction (virtual) ===")
288 # look up i-cache expecting it to fail
290 yield from debug(dut
, "virtual instr req")
291 # set address to 0x10200, update mem[] to 5678
293 real_addr
= virt_addr
294 expected_insn
= 0x5678
295 mem
[real_addr
] = expected_insn
297 yield i_in
.priv_mode
.eq(0)
298 yield i_in
.virt_mode
.eq(1)
300 yield i_in
.nia
.eq(virt_addr
)
301 yield i_in
.stop_mark
.eq(0)
302 yield i_m_in
.tlbld
.eq(0)
303 yield i_m_in
.tlbie
.eq(0)
304 yield i_m_in
.addr
.eq(0)
305 yield i_m_in
.pte
.eq(0)
310 # miss, stalls for a bit
312 yield i_in
.nia
.eq(virt_addr
)
314 valid
= yield i_out
.valid
315 failed
= yield i_out
.fetch_failed
316 while not valid
and not failed
:
318 valid
= yield i_out
.valid
319 failed
= yield i_out
.fetch_failed
322 print ("failed?", "yes" if failed
else "no")
327 print("=== test loadstore instruction (instruction fault) ===")
329 yield from debug(dut
, "instr fault")
333 yield ldst
.priv_mode
.eq(0)
334 yield ldst
.instr_fault
.eq(1)
335 yield ldst
.maddr
.eq(virt_addr
)
336 # still broken -- investigate
337 # msr = MSRSpec(pr=?, dr=?, sf=0)
338 # ld_data, exctype, exc = yield from pi_ld(pi, virt_addr, 8, msr=msr)
340 yield ldst
.instr_fault
.eq(0)
342 done
= yield (ldst
.done
)
343 exc_info
= yield from get_exception_info(pi
.exc_o
)
344 if done
or exc_info
.happened
:
347 assert exc_info
.happened
== 0 # assert just before doing the fault set zero
348 yield ldst
.instr_fault
.eq(0)
353 print("=== test loadstore instruction (try instruction again) ===")
354 yield from debug(dut
, "instr virt retry")
355 # set address to 0x10200, update mem[] to 5678
357 real_addr
= virt_addr
358 expected_insn
= 0x5678
360 yield i_in
.priv_mode
.eq(0)
361 yield i_in
.virt_mode
.eq(1)
363 yield i_in
.nia
.eq(virt_addr
)
364 yield i_in
.stop_mark
.eq(0)
365 yield i_m_in
.tlbld
.eq(0)
366 yield i_m_in
.tlbie
.eq(0)
367 yield i_m_in
.addr
.eq(0)
368 yield i_m_in
.pte
.eq(0)
373 # miss, stalls for a bit
376 yield i_in.nia.eq(virt_addr)
378 valid = yield i_out.valid
379 failed = yield i_out.fetch_failed
380 while not valid and not failed:
382 valid = yield i_out.valid
383 failed = yield i_out.fetch_failed
385 nia = yield i_out.nia
386 insn = yield i_out.insn
390 nia
, insn
, valid
, failed
= yield from icache_read(dut
,virt_addr
,0,1)
392 yield from debug(dut
, "test done")
396 print ("failed?", "yes" if failed
else "no")
399 print ("fetched %x from addr %x" % (insn
, nia
))
400 assert insn
== expected_insn
405 def _test_loadstore1_invalid(dut
, mem
):
406 mmu
= dut
.submodules
.mmu
407 pi
= dut
.submodules
.ldst
.pi
410 print("=== test invalid ===")
413 msr
= MSRSpec(pr
=1, dr
=0, sf
=0) # set problem-state
414 ld_data
, exctype
, exc
= yield from pi_ld(pi
, addr
, 8, msr
=msr
)
415 print("ld_data", ld_data
, exctype
, exc
)
416 assert (exctype
== "slow")
417 invalid
= exc
.invalid
418 assert (invalid
== 1)
420 print("=== test invalid done ===")
425 def _test_loadstore1(dut
, mem
):
426 mmu
= dut
.submodules
.mmu
427 pi
= dut
.submodules
.ldst
.pi
428 ldst
= dut
.submodules
.ldst
# to get at DAR (NOT part of PortInterface)
431 yield mmu
.rin
.prtbl
.eq(0x1000000) # set process table
435 data
= 0xf553b658ba7e1f51
438 msr
= MSRSpec(pr
=0, dr
=0, sf
=0)
439 yield from pi_st(pi
, addr
, data
, 8, msr
=msr
)
442 ld_data
, exctype
, exc
= yield from pi_ld(pi
, addr
, 8, msr
=msr
)
443 assert ld_data
== 0xf553b658ba7e1f51
444 assert exctype
is None
446 ld_data
, exctype
, exc
= yield from pi_ld(pi
, addr
, 8, msr
=msr
)
447 assert ld_data
== 0xf553b658ba7e1f51
448 assert exctype
is None
450 print("do_dcbz ===============")
451 yield from pi_st(pi
, addr
, data
, 8, msr
=msr
, is_dcbz
=1)
452 print("done_dcbz ===============")
455 ld_data
, exctype
, exc
= yield from pi_ld(pi
, addr
, 8, msr
=msr
)
456 print("ld_data after dcbz")
459 assert exctype
is None
462 print("=== alignment error (ld) ===")
464 ld_data
, exctype
, exc
= yield from pi_ld(pi
, addr
, 8, msr
=msr
)
466 alignment
= exc
.alignment
467 happened
= exc
.happened
468 yield # wait for dsr to update
474 assert (happened
== 1)
475 assert (alignment
== 1)
477 assert (exctype
== "fast")
478 yield from wait_busy(pi
, debug
="pi_ld_E_alignment_error")
479 # wait is only needed in case of in exception here
480 print("=== alignment error test passed (ld) ===")
482 # take some cycles in between so that gtkwave separates out
489 print("=== alignment error (st) ===")
491 exctype
, exc
= yield from pi_st(pi
, addr
,0, 8, msr
=msr
)
493 alignment
= exc
.alignment
494 happened
= exc
.happened
498 assert (happened
== 1)
499 assert (alignment
==1)
501 assert (exctype
== "fast")
502 #???? yield from wait_busy(pi, debug="pi_st_E_alignment_error")
503 # wait is only needed in case of in exception here
504 print("=== alignment error test passed (st) ===")
508 print("=== no alignment error (ld) ===")
510 ld_data
, exctype
, exc
= yield from pi_ld(pi
, addr
, 8, msr
=msr
)
511 print("ld_data", ld_data
, exctype
, exc
)
513 alignment
= exc
.alignment
514 happened
= exc
.happened
518 assert (happened
== 0)
519 assert (alignment
== 0)
520 print("=== no alignment error done (ld) ===")
523 addrs
= [0x456920,0xa7a180,0x299420,0x1d9d60]
526 print("== RANDOM addr ==",hex(addr
))
527 ld_data
, exctype
, exc
= yield from pi_ld(pi
, addr
, 8, msr
=msr
)
528 print("ld_data[RANDOM]",ld_data
,exc
,addr
)
529 assert (exctype
== None)
532 print("== RANDOM addr ==",hex(addr
))
533 exc
= yield from pi_st(pi
, addr
,0xFF*addr
, 8, msr
=msr
)
534 assert (exctype
== None)
536 # readback written data and compare
538 print("== RANDOM addr ==",hex(addr
))
539 ld_data
, exctype
, exc
= yield from pi_ld(pi
, addr
, 8, msr
=msr
)
540 print("ld_data[RANDOM_READBACK]",ld_data
,exc
,addr
)
541 assert (exctype
== None)
542 assert (ld_data
== 0xFF*addr
)
544 print("== RANDOM addr done ==")
549 def _test_loadstore1_ifetch_invalid(dut
, mem
):
550 mmu
= dut
.submodules
.mmu
551 ldst
= dut
.submodules
.ldst
553 icache
= dut
.submodules
.ldst
.icache
556 print("=== test loadstore instruction (invalid) ===")
562 # first virtual memory test
564 print ("set process table")
565 yield from debug(dut
, "set prtbl")
566 yield mmu
.rin
.prtbl
.eq(0x1000000) # set process table
569 yield from debug(dut
, "real mem instruction")
570 # set address to zero, update mem[0] to 01234
572 expected_insn
= 0x1234
573 mem
[addr
] = expected_insn
575 yield i_in
.priv_mode
.eq(1)
577 yield i_in
.nia
.eq(addr
)
578 yield i_in
.stop_mark
.eq(0)
579 yield i_m_in
.tlbld
.eq(0)
580 yield i_m_in
.tlbie
.eq(0)
581 yield i_m_in
.addr
.eq(0)
582 yield i_m_in
.pte
.eq(0)
587 # miss, stalls for a bit
589 yield i_in
.nia
.eq(addr
)
591 valid
= yield i_out
.valid
592 nia
= yield i_out
.nia
595 valid
= yield i_out
.valid
598 nia
= yield i_out
.nia
599 insn
= yield i_out
.insn
604 print ("fetched %x from addr %x" % (insn
, nia
))
605 assert insn
== expected_insn
607 print("=== test loadstore instruction (virtual) ===")
608 yield from debug(dut
, "virtual instr req")
610 # look up i-cache expecting it to fail
612 # set address to 0x10200, update mem[] to 5678
614 real_addr
= virt_addr
615 expected_insn
= 0x5678
616 mem
[real_addr
] = expected_insn
618 yield i_in
.priv_mode
.eq(1)
619 yield i_in
.virt_mode
.eq(1)
621 yield i_in
.nia
.eq(virt_addr
)
622 yield i_in
.stop_mark
.eq(0)
623 yield i_m_in
.tlbld
.eq(0)
624 yield i_m_in
.tlbie
.eq(0)
625 yield i_m_in
.addr
.eq(0)
626 yield i_m_in
.pte
.eq(0)
631 # miss, stalls for a bit
633 yield i_in
.nia
.eq(virt_addr
)
635 valid
= yield i_out
.valid
636 failed
= yield i_out
.fetch_failed
637 while not valid
and not failed
:
639 valid
= yield i_out
.valid
640 failed
= yield i_out
.fetch_failed
643 print ("failed?", "yes" if failed
else "no")
648 print("=== test invalid loadstore instruction (instruction fault) ===")
650 yield from debug(dut
, "instr fault (perm err expected)")
653 yield ldst
.priv_mode
.eq(0)
654 yield ldst
.instr_fault
.eq(1)
655 yield ldst
.maddr
.eq(virt_addr
)
656 #ld_data, exctype, exc = yield from pi_ld(pi, virt_addr, 8, msr=msr)
658 yield ldst
.instr_fault
.eq(0)
660 done
= yield (ldst
.done
)
661 exc_info
= yield from get_exception_info(pi
.exc_o
)
662 if done
or exc_info
.happened
:
665 assert exc_info
.happened
== 1 # different here as expected
667 # TODO: work out what kind of exception occurred and check it's
668 # the right one. we *expect* it to be a permissions error because
669 # the RPTE leaf node in pagetables.test2 is marked as "non-executable"
670 # but we also expect instr_fault to be set because it is an instruction
672 print (" MMU lookup exception type?")
673 for fname
in LDSTExceptionTuple
._fields
:
674 print (" fname %20s %d" % (fname
, getattr(exc_info
, fname
)))
676 # ok now printed them out and visually inspected: check them with asserts
677 assert exc_info
.instr_fault
== 1 # instruction fault (yes!)
678 assert exc_info
.perm_error
== 1 # permissions (yes!)
679 assert exc_info
.rc_error
== 0
680 assert exc_info
.alignment
== 0
681 assert exc_info
.invalid
== 0
682 assert exc_info
.segment_fault
== 0
683 assert exc_info
.rc_error
== 0
685 yield from debug(dut
, "test done")
686 yield ldst
.instr_fault
.eq(0)
694 def test_loadstore1_ifetch_unit_iface():
696 m
, cmpi
= setup_mmu()
698 mem
= pagetables
.test1
700 # set this up before passing to Simulator (which calls elaborate)
701 icache
= m
.submodules
.ldst
.icache
702 icache
.use_fetch_interface() # this is the function which converts
703 # to FetchUnitInterface. *including*
704 # rewiring the Wishbone Bus to ibus
710 sim
.add_sync_process(wrap(_test_loadstore1_ifetch_iface(m
, mem
)))
711 # add two wb_get processes onto the *same* memory dictionary.
712 # this shouuuld work.... cross-fingers...
713 sim
.add_sync_process(wrap(wb_get(cmpi
.wb_bus(), mem
)))
714 sim
.add_sync_process(wrap(wb_get(icache
.ibus
, mem
))) # ibus not bus
715 with sim
.write_vcd('test_loadstore1_ifetch_iface.vcd',
716 traces
=[m
.debug_status
]): # include extra debug
720 def test_loadstore1_ifetch():
722 m
, cmpi
= setup_mmu()
724 mem
= pagetables
.test1
730 icache
= m
.submodules
.ldst
.icache
731 sim
.add_sync_process(wrap(_test_loadstore1_ifetch(m
, mem
)))
732 # add two wb_get processes onto the *same* memory dictionary.
733 # this shouuuld work.... cross-fingers...
734 sim
.add_sync_process(wrap(wb_get(cmpi
.wb_bus(), mem
)))
735 sim
.add_sync_process(wrap(wb_get(icache
.bus
, mem
)))
736 with sim
.write_vcd('test_loadstore1_ifetch.vcd',
737 traces
=[m
.debug_status
]): # include extra debug
741 def test_loadstore1():
743 m
, cmpi
= setup_mmu()
745 mem
= pagetables
.test1
751 sim
.add_sync_process(wrap(_test_loadstore1(m
, mem
)))
752 sim
.add_sync_process(wrap(wb_get(cmpi
.wb_bus(), mem
)))
753 with sim
.write_vcd('test_loadstore1.vcd'):
757 def test_loadstore1_invalid():
759 m
, cmpi
= setup_mmu()
767 sim
.add_sync_process(wrap(_test_loadstore1_invalid(m
, mem
)))
768 sim
.add_sync_process(wrap(wb_get(cmpi
.wb_bus(), mem
)))
769 with sim
.write_vcd('test_loadstore1_invalid.vcd'):
772 def test_loadstore1_ifetch_invalid():
773 m
, cmpi
= setup_mmu()
775 # this is a specially-arranged page table which has the permissions
776 # barred for execute on the leaf node (EAA=0x2 instead of EAA=0x3)
777 mem
= pagetables
.test2
783 icache
= m
.submodules
.ldst
.icache
784 sim
.add_sync_process(wrap(_test_loadstore1_ifetch_invalid(m
, mem
)))
785 # add two wb_get processes onto the *same* memory dictionary.
786 # this shouuuld work.... cross-fingers...
787 sim
.add_sync_process(wrap(wb_get(cmpi
.wb_bus(), mem
)))
788 sim
.add_sync_process(wrap(wb_get(icache
.bus
, mem
)))
789 with sim
.write_vcd('test_loadstore1_ifetch_invalid.vcd',
790 traces
=[m
.debug_status
]): # include extra debug
793 def test_loadstore1_ifetch_multi():
794 m
, cmpi
= setup_mmu()
797 # this is a specially-arranged page table which has the permissions
798 # barred for execute on the leaf node (EAA=0x2 instead of EAA=0x3)
799 mem
= pagetables
.test1
801 # set this up before passing to Simulator (which calls elaborate)
802 icache
= m
.submodules
.ldst
.icache
803 icache
.use_fetch_interface() # this is the function which converts
804 # to FetchUnitInterface. *including*
805 # rewiring the Wishbone Bus to ibus
811 sim
.add_sync_process(wrap(_test_loadstore1_ifetch_multi(m
, mem
)))
812 # add two wb_get processes onto the *same* memory dictionary.
813 # this shouuuld work.... cross-fingers...
814 sim
.add_sync_process(wrap(wb_get(cmpi
.wb_bus(), mem
)))
815 sim
.add_sync_process(wrap(wb_get(icache
.ibus
, mem
))) # ibus not bus
816 with sim
.write_vcd('test_loadstore1_ifetch_multi.vcd',
817 traces
=[m
.debug_status
]): # include extra debug
820 if __name__
== '__main__':
822 test_loadstore1_invalid()
823 test_loadstore1_ifetch() #FIXME
824 test_loadstore1_ifetch_invalid()
825 test_loadstore1_ifetch_unit_iface() # guess: should be working
826 test_loadstore1_ifetch_multi()