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 yield i_in
.priv_mode
.eq(priv
)
72 yield i_in
.virt_mode
.eq(virt
)
74 yield i_in
.nia
.eq(addr
)
75 yield i_in
.stop_mark
.eq(0)
78 yield i_in
.nia
.eq(addr
)
80 valid
= yield i_out
.valid
81 failed
= yield i_out
.fetch_failed
82 while not valid
and not failed
:
84 valid
= yield i_out
.valid
85 failed
= yield i_out
.fetch_failed
89 insn
= yield i_out
.insn
93 return nia
, insn
, valid
, failed
96 test_exceptions
= True
102 print ("set debug message", msg
)
103 dut
.debug_status
.str = msg
# set the message
104 yield dut
.debug_status
.eq(0) # trigger an update
105 yield dut
.debug_status
.eq(1)
108 def _test_loadstore1_ifetch(dut
, mem
):
109 """test_loadstore1_ifetch
111 this is quite a complex multi-step test.
113 * first (just because, as a demo) read in priv mode, non-virtual.
114 just like in experiment/icache.py itself.
116 * second, using the (usual) PTE for these things (which came originally
117 from gem5-experimental experiment/radix_walk_example.txt) do a
118 virtual-memory read through the *instruction* cache.
119 this is expected to FAIL
121 * third: mess about with the MMU, setting "iside" (instruction-side),
122 requesting an MMU RADIX LOOKUP. this triggers an itlb_load
123 (instruction-cache TLB entry-insertion)
125 * fourth and finally: retry the read of the instruction through i-cache.
126 this is now expected to SUCCEED
131 mmu
= dut
.submodules
.mmu
132 ldst
= dut
.submodules
.ldst
134 icache
= dut
.submodules
.ldst
.icache
137 print("=== test loadstore instruction (real) ===")
143 # first virtual memory test
145 print ("set process table")
146 yield from debug(dut
, "set prtble")
147 yield mmu
.rin
.prtbl
.eq(0x1000000) # set process table
150 yield from debug(dut
, "real mem instruction")
151 # set address to zero, update mem[0] to 01234
153 expected_insn
= 0x1234
154 mem
[addr
] = expected_insn
156 yield i_in
.priv_mode
.eq(1)
158 yield i_in
.nia
.eq(addr
)
159 yield i_in
.stop_mark
.eq(0)
160 yield i_m_in
.tlbld
.eq(0)
161 yield i_m_in
.tlbie
.eq(0)
162 yield i_m_in
.addr
.eq(0)
163 yield i_m_in
.pte
.eq(0)
168 # miss, stalls for a bit
170 yield i_in
.nia
.eq(addr
)
172 valid
= yield i_out
.valid
175 valid
= yield i_out
.valid
178 nia
= yield i_out
.nia
179 insn
= yield i_out
.insn
183 print ("fetched %x from addr %x" % (insn
, nia
))
184 assert insn
== expected_insn
186 print("=== test loadstore instruction (virtual) ===")
188 # look up i-cache expecting it to fail
190 yield from debug(dut
, "virtual instr req")
191 # set address to 0x10200, update mem[] to 5678
193 real_addr
= virt_addr
194 expected_insn
= 0x5678
195 mem
[real_addr
] = expected_insn
197 yield i_in
.priv_mode
.eq(0)
198 yield i_in
.virt_mode
.eq(1)
200 yield i_in
.nia
.eq(virt_addr
)
201 yield i_in
.stop_mark
.eq(0)
202 yield i_m_in
.tlbld
.eq(0)
203 yield i_m_in
.tlbie
.eq(0)
204 yield i_m_in
.addr
.eq(0)
205 yield i_m_in
.pte
.eq(0)
210 # miss, stalls for a bit
212 yield i_in
.nia
.eq(virt_addr
)
214 valid
= yield i_out
.valid
215 failed
= yield i_out
.fetch_failed
216 while not valid
and not failed
:
218 valid
= yield i_out
.valid
219 failed
= yield i_out
.fetch_failed
222 print ("failed?", "yes" if failed
else "no")
227 print("=== test loadstore instruction (instruction fault) ===")
229 yield from debug(dut
, "instr fault")
233 yield ldst
.priv_mode
.eq(0)
234 yield ldst
.instr_fault
.eq(1)
235 yield ldst
.maddr
.eq(virt_addr
)
236 #ld_data, exctype, exc = yield from pi_ld(pi, virt_addr, 8, msr_pr=1)
238 yield ldst
.instr_fault
.eq(0)
240 done
= yield (ldst
.done
)
241 exc_info
= yield from get_exception_info(pi
.exc_o
)
242 if done
or exc_info
.happened
:
245 assert exc_info
.happened
== 0 # assert just before doing the fault set zero
246 yield ldst
.instr_fault
.eq(0)
251 print("=== test loadstore instruction (try instruction again) ===")
252 yield from debug(dut
, "instr virt retry")
253 # set address to 0x10200, update mem[] to 5678
255 real_addr
= virt_addr
256 expected_insn
= 0x5678
258 yield i_in
.priv_mode
.eq(0)
259 yield i_in
.virt_mode
.eq(1)
261 yield i_in
.nia
.eq(virt_addr
)
262 yield i_in
.stop_mark
.eq(0)
263 yield i_m_in
.tlbld
.eq(0)
264 yield i_m_in
.tlbie
.eq(0)
265 yield i_m_in
.addr
.eq(0)
266 yield i_m_in
.pte
.eq(0)
271 # miss, stalls for a bit
273 yield i_in
.nia
.eq(virt_addr
)
275 valid
= yield i_out
.valid
276 failed
= yield i_out
.fetch_failed
277 while not valid
and not failed
:
279 valid
= yield i_out
.valid
280 failed
= yield i_out
.fetch_failed
282 nia
= yield i_out
.nia
283 insn
= yield i_out
.insn
285 yield from debug(dut
, "test done")
289 print ("failed?", "yes" if failed
else "no")
292 print ("fetched %x from addr %x" % (insn
, nia
))
293 assert insn
== expected_insn
298 def _test_loadstore1_invalid(dut
, mem
):
299 mmu
= dut
.submodules
.mmu
300 pi
= dut
.submodules
.ldst
.pi
303 print("=== test invalid ===")
306 ld_data
, exctype
, exc
= yield from pi_ld(pi
, addr
, 8, msr_pr
=1)
307 print("ld_data", ld_data
, exctype
, exc
)
308 assert (exctype
== "slow")
309 invalid
= exc
.invalid
310 assert (invalid
== 1)
312 print("=== test invalid done ===")
317 def _test_loadstore1(dut
, mem
):
318 mmu
= dut
.submodules
.mmu
319 pi
= dut
.submodules
.ldst
.pi
320 ldst
= dut
.submodules
.ldst
# to get at DAR (NOT part of PortInterface)
323 yield mmu
.rin
.prtbl
.eq(0x1000000) # set process table
327 data
= 0xf553b658ba7e1f51
330 yield from pi_st(pi
, addr
, data
, 8, msr_pr
=1)
333 ld_data
, exctype
, exc
= yield from pi_ld(pi
, addr
, 8, msr_pr
=1)
334 assert ld_data
== 0xf553b658ba7e1f51
335 assert exctype
is None
337 ld_data
, exctype
, exc
= yield from pi_ld(pi
, addr
, 8, msr_pr
=1)
338 assert ld_data
== 0xf553b658ba7e1f51
339 assert exctype
is None
341 print("do_dcbz ===============")
342 yield from pi_st(pi
, addr
, data
, 8, msr_pr
=1, is_dcbz
=1)
343 print("done_dcbz ===============")
346 ld_data
, exctype
, exc
= yield from pi_ld(pi
, addr
, 8, msr_pr
=1)
347 print("ld_data after dcbz")
350 assert exctype
is None
353 print("=== alignment error (ld) ===")
355 ld_data
, exctype
, exc
= yield from pi_ld(pi
, addr
, 8, msr_pr
=1)
357 alignment
= exc
.alignment
358 happened
= exc
.happened
359 yield # wait for dsr to update
365 assert (happened
== 1)
366 assert (alignment
== 1)
368 assert (exctype
== "fast")
369 yield from wait_busy(pi
, debug
="pi_ld_E_alignment_error")
370 # wait is only needed in case of in exception here
371 print("=== alignment error test passed (ld) ===")
373 # take some cycles in between so that gtkwave separates out
380 print("=== alignment error (st) ===")
382 exctype
, exc
= yield from pi_st(pi
, addr
,0, 8, msr_pr
=1)
384 alignment
= exc
.alignment
385 happened
= exc
.happened
389 assert (happened
== 1)
390 assert (alignment
==1)
392 assert (exctype
== "fast")
393 #???? yield from wait_busy(pi, debug="pi_st_E_alignment_error")
394 # wait is only needed in case of in exception here
395 print("=== alignment error test passed (st) ===")
399 print("=== no alignment error (ld) ===")
401 ld_data
, exctype
, exc
= yield from pi_ld(pi
, addr
, 8, msr_pr
=1)
402 print("ld_data", ld_data
, exctype
, exc
)
404 alignment
= exc
.alignment
405 happened
= exc
.happened
409 assert (happened
== 0)
410 assert (alignment
== 0)
411 print("=== no alignment error done (ld) ===")
414 addrs
= [0x456920,0xa7a180,0x299420,0x1d9d60]
417 print("== RANDOM addr ==",hex(addr
))
418 ld_data
, exctype
, exc
= yield from pi_ld(pi
, addr
, 8, msr_pr
=1)
419 print("ld_data[RANDOM]",ld_data
,exc
,addr
)
420 assert (exctype
== None)
423 print("== RANDOM addr ==",hex(addr
))
424 exc
= yield from pi_st(pi
, addr
,0xFF*addr
, 8, msr_pr
=1)
425 assert (exctype
== None)
427 # readback written data and compare
429 print("== RANDOM addr ==",hex(addr
))
430 ld_data
, exctype
, exc
= yield from pi_ld(pi
, addr
, 8, msr_pr
=1)
431 print("ld_data[RANDOM_READBACK]",ld_data
,exc
,addr
)
432 assert (exctype
== None)
433 assert (ld_data
== 0xFF*addr
)
435 print("== RANDOM addr done ==")
440 def _test_loadstore1_ifetch_invalid(dut
, mem
):
441 mmu
= dut
.submodules
.mmu
442 ldst
= dut
.submodules
.ldst
444 icache
= dut
.submodules
.ldst
.icache
447 print("=== test loadstore instruction (invalid) ===")
453 # first virtual memory test
455 print ("set process table")
456 yield from debug(dut
, "set prtbl")
457 yield mmu
.rin
.prtbl
.eq(0x1000000) # set process table
460 yield from debug(dut
, "real mem instruction")
461 # set address to zero, update mem[0] to 01234
463 expected_insn
= 0x1234
464 mem
[addr
] = expected_insn
466 yield i_in
.priv_mode
.eq(1)
468 yield i_in
.nia
.eq(addr
)
469 yield i_in
.stop_mark
.eq(0)
470 yield i_m_in
.tlbld
.eq(0)
471 yield i_m_in
.tlbie
.eq(0)
472 yield i_m_in
.addr
.eq(0)
473 yield i_m_in
.pte
.eq(0)
478 # miss, stalls for a bit
480 yield i_in
.nia
.eq(addr
)
482 valid
= yield i_out
.valid
483 nia
= yield i_out
.nia
486 valid
= yield i_out
.valid
489 nia
= yield i_out
.nia
490 insn
= yield i_out
.insn
495 print ("fetched %x from addr %x" % (insn
, nia
))
496 assert insn
== expected_insn
498 print("=== test loadstore instruction (virtual) ===")
499 yield from debug(dut
, "virtual instr req")
501 # look up i-cache expecting it to fail
503 # set address to 0x10200, update mem[] to 5678
505 real_addr
= virt_addr
506 expected_insn
= 0x5678
507 mem
[real_addr
] = expected_insn
509 yield i_in
.priv_mode
.eq(1)
510 yield i_in
.virt_mode
.eq(1)
512 yield i_in
.nia
.eq(virt_addr
)
513 yield i_in
.stop_mark
.eq(0)
514 yield i_m_in
.tlbld
.eq(0)
515 yield i_m_in
.tlbie
.eq(0)
516 yield i_m_in
.addr
.eq(0)
517 yield i_m_in
.pte
.eq(0)
522 # miss, stalls for a bit
524 yield i_in
.nia
.eq(virt_addr
)
526 valid
= yield i_out
.valid
527 failed
= yield i_out
.fetch_failed
528 while not valid
and not failed
:
530 valid
= yield i_out
.valid
531 failed
= yield i_out
.fetch_failed
534 print ("failed?", "yes" if failed
else "no")
539 print("=== test invalid loadstore instruction (instruction fault) ===")
541 yield from debug(dut
, "instr fault (perm err expected)")
544 yield ldst
.priv_mode
.eq(0)
545 yield ldst
.instr_fault
.eq(1)
546 yield ldst
.maddr
.eq(virt_addr
)
547 #ld_data, exctype, exc = yield from pi_ld(pi, virt_addr, 8, msr_pr=1)
549 yield ldst
.instr_fault
.eq(0)
551 done
= yield (ldst
.done
)
552 exc_info
= yield from get_exception_info(pi
.exc_o
)
553 if done
or exc_info
.happened
:
556 assert exc_info
.happened
== 1 # different here as expected
558 # TODO: work out what kind of exception occurred and check it's
559 # the right one. we *expect* it to be a permissions error because
560 # the RPTE leaf node in pagetables.test2 is marked as "non-executable"
561 # but we also expect instr_fault to be set because it is an instruction
563 print (" MMU lookup exception type?")
564 for fname
in LDSTExceptionTuple
._fields
:
565 print (" fname %20s %d" % (fname
, getattr(exc_info
, fname
)))
567 # ok now printed them out and visually inspected: check them with asserts
568 assert exc_info
.instr_fault
== 1 # instruction fault (yes!)
569 assert exc_info
.perm_error
== 1 # permissions (yes!)
570 assert exc_info
.rc_error
== 0
571 assert exc_info
.alignment
== 0
572 assert exc_info
.invalid
== 0
573 assert exc_info
.segment_fault
== 0
574 assert exc_info
.rc_error
== 0
576 yield from debug(dut
, "test done")
577 yield ldst
.instr_fault
.eq(0)
585 def test_loadstore1_ifetch():
587 m
, cmpi
= setup_mmu()
589 mem
= pagetables
.test1
595 icache
= m
.submodules
.ldst
.icache
596 sim
.add_sync_process(wrap(_test_loadstore1_ifetch(m
, mem
)))
597 # add two wb_get processes onto the *same* memory dictionary.
598 # this shouuuld work.... cross-fingers...
599 sim
.add_sync_process(wrap(wb_get(cmpi
.wb_bus(), mem
)))
600 sim
.add_sync_process(wrap(wb_get(icache
.bus
, mem
)))
601 with sim
.write_vcd('test_loadstore1_ifetch.vcd',
602 traces
=[m
.debug_status
]): # include extra debug
606 def test_loadstore1():
608 m
, cmpi
= setup_mmu()
610 mem
= pagetables
.test1
616 sim
.add_sync_process(wrap(_test_loadstore1(m
, mem
)))
617 sim
.add_sync_process(wrap(wb_get(cmpi
.wb_bus(), mem
)))
618 with sim
.write_vcd('test_loadstore1.vcd'):
622 def test_loadstore1_invalid():
624 m
, cmpi
= setup_mmu()
632 sim
.add_sync_process(wrap(_test_loadstore1_invalid(m
, mem
)))
633 sim
.add_sync_process(wrap(wb_get(cmpi
.wb_bus(), mem
)))
634 with sim
.write_vcd('test_loadstore1_invalid.vcd'):
637 def test_loadstore1_ifetch_invalid():
638 m
, cmpi
= setup_mmu()
640 # this is a specially-arranged page table which has the permissions
641 # barred for execute on the leaf node (EAA=0x2 instead of EAA=0x3)
642 mem
= pagetables
.test2
648 icache
= m
.submodules
.ldst
.icache
649 sim
.add_sync_process(wrap(_test_loadstore1_ifetch_invalid(m
, mem
)))
650 # add two wb_get processes onto the *same* memory dictionary.
651 # this shouuuld work.... cross-fingers...
652 sim
.add_sync_process(wrap(wb_get(cmpi
.wb_bus(), mem
)))
653 sim
.add_sync_process(wrap(wb_get(icache
.bus
, mem
)))
654 with sim
.write_vcd('test_loadstore1_ifetch_invalid.vcd',
655 traces
=[m
.debug_status
]): # include extra debug
660 if __name__
== '__main__':
662 test_loadstore1_invalid()
663 test_loadstore1_ifetch()
664 test_loadstore1_ifetch_invalid()