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(dut
, mem
):
113 """test_loadstore1_ifetch
115 this is quite a complex multi-step test.
117 * first (just because, as a demo) read in priv mode, non-virtual.
118 just like in experiment/icache.py itself.
120 * second, using the (usual) PTE for these things (which came originally
121 from gem5-experimental experiment/radix_walk_example.txt) do a
122 virtual-memory read through the *instruction* cache.
123 this is expected to FAIL
125 * third: mess about with the MMU, setting "iside" (instruction-side),
126 requesting an MMU RADIX LOOKUP. this triggers an itlb_load
127 (instruction-cache TLB entry-insertion)
129 * fourth and finally: retry the read of the instruction through i-cache.
130 this is now expected to SUCCEED
135 mmu
= dut
.submodules
.mmu
136 ldst
= dut
.submodules
.ldst
138 icache
= dut
.submodules
.ldst
.icache
141 print("=== test loadstore instruction (real) ===")
147 # first virtual memory test
149 print ("set process table")
150 yield from debug(dut
, "set prtble")
151 yield mmu
.rin
.prtbl
.eq(0x1000000) # set process table
154 yield from debug(dut
, "real mem instruction")
155 # set address to zero, update mem[0] to 01234
157 expected_insn
= 0x1234
158 mem
[addr
] = expected_insn
160 yield i_in
.priv_mode
.eq(1)
162 yield i_in
.nia
.eq(addr
)
163 yield i_in
.stop_mark
.eq(0)
164 yield i_m_in
.tlbld
.eq(0)
165 yield i_m_in
.tlbie
.eq(0)
166 yield i_m_in
.addr
.eq(0)
167 yield i_m_in
.pte
.eq(0)
172 # miss, stalls for a bit -- this one is different here
173 ##nia, insn, valid, failed = yield from icache_read(dut,addr,0,0)
178 yield i_in
.nia
.eq(addr
)
180 valid
= yield i_out
.valid
183 valid
= yield i_out
.valid
186 nia
= yield i_out
.nia
187 insn
= yield i_out
.insn
191 print ("fetched %x from addr %x" % (insn
, nia
))
192 assert insn
== expected_insn
194 print("=== test loadstore instruction (virtual) ===")
196 # look up i-cache expecting it to fail
198 yield from debug(dut
, "virtual instr req")
199 # set address to 0x10200, update mem[] to 5678
201 real_addr
= virt_addr
202 expected_insn
= 0x5678
203 mem
[real_addr
] = expected_insn
205 yield i_in
.priv_mode
.eq(0)
206 yield i_in
.virt_mode
.eq(1)
208 yield i_in
.nia
.eq(virt_addr
)
209 yield i_in
.stop_mark
.eq(0)
210 yield i_m_in
.tlbld
.eq(0)
211 yield i_m_in
.tlbie
.eq(0)
212 yield i_m_in
.addr
.eq(0)
213 yield i_m_in
.pte
.eq(0)
218 # miss, stalls for a bit
220 yield i_in
.nia
.eq(virt_addr
)
222 valid
= yield i_out
.valid
223 failed
= yield i_out
.fetch_failed
224 while not valid
and not failed
:
226 valid
= yield i_out
.valid
227 failed
= yield i_out
.fetch_failed
230 print ("failed?", "yes" if failed
else "no")
235 print("=== test loadstore instruction (instruction fault) ===")
237 yield from debug(dut
, "instr fault")
241 yield ldst
.priv_mode
.eq(0)
242 yield ldst
.instr_fault
.eq(1)
243 yield ldst
.maddr
.eq(virt_addr
)
244 #ld_data, exctype, exc = yield from pi_ld(pi, virt_addr, 8, msr_pr=1)
246 yield ldst
.instr_fault
.eq(0)
248 done
= yield (ldst
.done
)
249 exc_info
= yield from get_exception_info(pi
.exc_o
)
250 if done
or exc_info
.happened
:
253 assert exc_info
.happened
== 0 # assert just before doing the fault set zero
254 yield ldst
.instr_fault
.eq(0)
259 print("=== test loadstore instruction (try instruction again) ===")
260 yield from debug(dut
, "instr virt retry")
261 # set address to 0x10200, update mem[] to 5678
263 real_addr
= virt_addr
264 expected_insn
= 0x5678
266 yield i_in
.priv_mode
.eq(0)
267 yield i_in
.virt_mode
.eq(1)
269 yield i_in
.nia
.eq(virt_addr
)
270 yield i_in
.stop_mark
.eq(0)
271 yield i_m_in
.tlbld
.eq(0)
272 yield i_m_in
.tlbie
.eq(0)
273 yield i_m_in
.addr
.eq(0)
274 yield i_m_in
.pte
.eq(0)
279 # miss, stalls for a bit
282 yield i_in.nia.eq(virt_addr)
284 valid = yield i_out.valid
285 failed = yield i_out.fetch_failed
286 while not valid and not failed:
288 valid = yield i_out.valid
289 failed = yield i_out.fetch_failed
291 nia = yield i_out.nia
292 insn = yield i_out.insn
296 nia
, insn
, valid
, failed
= yield from icache_read(dut
,virt_addr
,0,1)
298 yield from debug(dut
, "test done")
302 print ("failed?", "yes" if failed
else "no")
305 print ("fetched %x from addr %x" % (insn
, nia
))
306 assert insn
== expected_insn
311 def _test_loadstore1_invalid(dut
, mem
):
312 mmu
= dut
.submodules
.mmu
313 pi
= dut
.submodules
.ldst
.pi
316 print("=== test invalid ===")
319 ld_data
, exctype
, exc
= yield from pi_ld(pi
, addr
, 8, msr_pr
=1)
320 print("ld_data", ld_data
, exctype
, exc
)
321 assert (exctype
== "slow")
322 invalid
= exc
.invalid
323 assert (invalid
== 1)
325 print("=== test invalid done ===")
330 def _test_loadstore1(dut
, mem
):
331 mmu
= dut
.submodules
.mmu
332 pi
= dut
.submodules
.ldst
.pi
333 ldst
= dut
.submodules
.ldst
# to get at DAR (NOT part of PortInterface)
336 yield mmu
.rin
.prtbl
.eq(0x1000000) # set process table
340 data
= 0xf553b658ba7e1f51
343 yield from pi_st(pi
, addr
, data
, 8, msr_pr
=1)
346 ld_data
, exctype
, exc
= yield from pi_ld(pi
, addr
, 8, msr_pr
=1)
347 assert ld_data
== 0xf553b658ba7e1f51
348 assert exctype
is None
350 ld_data
, exctype
, exc
= yield from pi_ld(pi
, addr
, 8, msr_pr
=1)
351 assert ld_data
== 0xf553b658ba7e1f51
352 assert exctype
is None
354 print("do_dcbz ===============")
355 yield from pi_st(pi
, addr
, data
, 8, msr_pr
=1, is_dcbz
=1)
356 print("done_dcbz ===============")
359 ld_data
, exctype
, exc
= yield from pi_ld(pi
, addr
, 8, msr_pr
=1)
360 print("ld_data after dcbz")
363 assert exctype
is None
366 print("=== alignment error (ld) ===")
368 ld_data
, exctype
, exc
= yield from pi_ld(pi
, addr
, 8, msr_pr
=1)
370 alignment
= exc
.alignment
371 happened
= exc
.happened
372 yield # wait for dsr to update
378 assert (happened
== 1)
379 assert (alignment
== 1)
381 assert (exctype
== "fast")
382 yield from wait_busy(pi
, debug
="pi_ld_E_alignment_error")
383 # wait is only needed in case of in exception here
384 print("=== alignment error test passed (ld) ===")
386 # take some cycles in between so that gtkwave separates out
393 print("=== alignment error (st) ===")
395 exctype
, exc
= yield from pi_st(pi
, addr
,0, 8, msr_pr
=1)
397 alignment
= exc
.alignment
398 happened
= exc
.happened
402 assert (happened
== 1)
403 assert (alignment
==1)
405 assert (exctype
== "fast")
406 #???? yield from wait_busy(pi, debug="pi_st_E_alignment_error")
407 # wait is only needed in case of in exception here
408 print("=== alignment error test passed (st) ===")
412 print("=== no alignment error (ld) ===")
414 ld_data
, exctype
, exc
= yield from pi_ld(pi
, addr
, 8, msr_pr
=1)
415 print("ld_data", ld_data
, exctype
, exc
)
417 alignment
= exc
.alignment
418 happened
= exc
.happened
422 assert (happened
== 0)
423 assert (alignment
== 0)
424 print("=== no alignment error done (ld) ===")
427 addrs
= [0x456920,0xa7a180,0x299420,0x1d9d60]
430 print("== RANDOM addr ==",hex(addr
))
431 ld_data
, exctype
, exc
= yield from pi_ld(pi
, addr
, 8, msr_pr
=1)
432 print("ld_data[RANDOM]",ld_data
,exc
,addr
)
433 assert (exctype
== None)
436 print("== RANDOM addr ==",hex(addr
))
437 exc
= yield from pi_st(pi
, addr
,0xFF*addr
, 8, msr_pr
=1)
438 assert (exctype
== None)
440 # readback written data and compare
442 print("== RANDOM addr ==",hex(addr
))
443 ld_data
, exctype
, exc
= yield from pi_ld(pi
, addr
, 8, msr_pr
=1)
444 print("ld_data[RANDOM_READBACK]",ld_data
,exc
,addr
)
445 assert (exctype
== None)
446 assert (ld_data
== 0xFF*addr
)
448 print("== RANDOM addr done ==")
453 def _test_loadstore1_ifetch_invalid(dut
, mem
):
454 mmu
= dut
.submodules
.mmu
455 ldst
= dut
.submodules
.ldst
457 icache
= dut
.submodules
.ldst
.icache
460 print("=== test loadstore instruction (invalid) ===")
466 # first virtual memory test
468 print ("set process table")
469 yield from debug(dut
, "set prtbl")
470 yield mmu
.rin
.prtbl
.eq(0x1000000) # set process table
473 yield from debug(dut
, "real mem instruction")
474 # set address to zero, update mem[0] to 01234
476 expected_insn
= 0x1234
477 mem
[addr
] = expected_insn
479 yield i_in
.priv_mode
.eq(1)
481 yield i_in
.nia
.eq(addr
)
482 yield i_in
.stop_mark
.eq(0)
483 yield i_m_in
.tlbld
.eq(0)
484 yield i_m_in
.tlbie
.eq(0)
485 yield i_m_in
.addr
.eq(0)
486 yield i_m_in
.pte
.eq(0)
491 # miss, stalls for a bit
493 yield i_in
.nia
.eq(addr
)
495 valid
= yield i_out
.valid
496 nia
= yield i_out
.nia
499 valid
= yield i_out
.valid
502 nia
= yield i_out
.nia
503 insn
= yield i_out
.insn
508 print ("fetched %x from addr %x" % (insn
, nia
))
509 assert insn
== expected_insn
511 print("=== test loadstore instruction (virtual) ===")
512 yield from debug(dut
, "virtual instr req")
514 # look up i-cache expecting it to fail
516 # set address to 0x10200, update mem[] to 5678
518 real_addr
= virt_addr
519 expected_insn
= 0x5678
520 mem
[real_addr
] = expected_insn
522 yield i_in
.priv_mode
.eq(1)
523 yield i_in
.virt_mode
.eq(1)
525 yield i_in
.nia
.eq(virt_addr
)
526 yield i_in
.stop_mark
.eq(0)
527 yield i_m_in
.tlbld
.eq(0)
528 yield i_m_in
.tlbie
.eq(0)
529 yield i_m_in
.addr
.eq(0)
530 yield i_m_in
.pte
.eq(0)
535 # miss, stalls for a bit
537 yield i_in
.nia
.eq(virt_addr
)
539 valid
= yield i_out
.valid
540 failed
= yield i_out
.fetch_failed
541 while not valid
and not failed
:
543 valid
= yield i_out
.valid
544 failed
= yield i_out
.fetch_failed
547 print ("failed?", "yes" if failed
else "no")
552 print("=== test invalid loadstore instruction (instruction fault) ===")
554 yield from debug(dut
, "instr fault (perm err expected)")
557 yield ldst
.priv_mode
.eq(0)
558 yield ldst
.instr_fault
.eq(1)
559 yield ldst
.maddr
.eq(virt_addr
)
560 #ld_data, exctype, exc = yield from pi_ld(pi, virt_addr, 8, msr_pr=1)
562 yield ldst
.instr_fault
.eq(0)
564 done
= yield (ldst
.done
)
565 exc_info
= yield from get_exception_info(pi
.exc_o
)
566 if done
or exc_info
.happened
:
569 assert exc_info
.happened
== 1 # different here as expected
571 # TODO: work out what kind of exception occurred and check it's
572 # the right one. we *expect* it to be a permissions error because
573 # the RPTE leaf node in pagetables.test2 is marked as "non-executable"
574 # but we also expect instr_fault to be set because it is an instruction
576 print (" MMU lookup exception type?")
577 for fname
in LDSTExceptionTuple
._fields
:
578 print (" fname %20s %d" % (fname
, getattr(exc_info
, fname
)))
580 # ok now printed them out and visually inspected: check them with asserts
581 assert exc_info
.instr_fault
== 1 # instruction fault (yes!)
582 assert exc_info
.perm_error
== 1 # permissions (yes!)
583 assert exc_info
.rc_error
== 0
584 assert exc_info
.alignment
== 0
585 assert exc_info
.invalid
== 0
586 assert exc_info
.segment_fault
== 0
587 assert exc_info
.rc_error
== 0
589 yield from debug(dut
, "test done")
590 yield ldst
.instr_fault
.eq(0)
598 def test_loadstore1_ifetch():
600 m
, cmpi
= setup_mmu()
602 mem
= pagetables
.test1
608 icache
= m
.submodules
.ldst
.icache
609 sim
.add_sync_process(wrap(_test_loadstore1_ifetch(m
, mem
)))
610 # add two wb_get processes onto the *same* memory dictionary.
611 # this shouuuld work.... cross-fingers...
612 sim
.add_sync_process(wrap(wb_get(cmpi
.wb_bus(), mem
)))
613 sim
.add_sync_process(wrap(wb_get(icache
.bus
, mem
)))
614 with sim
.write_vcd('test_loadstore1_ifetch.vcd',
615 traces
=[m
.debug_status
]): # include extra debug
619 def test_loadstore1():
621 m
, cmpi
= setup_mmu()
623 mem
= pagetables
.test1
629 sim
.add_sync_process(wrap(_test_loadstore1(m
, mem
)))
630 sim
.add_sync_process(wrap(wb_get(cmpi
.wb_bus(), mem
)))
631 with sim
.write_vcd('test_loadstore1.vcd'):
635 def test_loadstore1_invalid():
637 m
, cmpi
= setup_mmu()
645 sim
.add_sync_process(wrap(_test_loadstore1_invalid(m
, mem
)))
646 sim
.add_sync_process(wrap(wb_get(cmpi
.wb_bus(), mem
)))
647 with sim
.write_vcd('test_loadstore1_invalid.vcd'):
650 def test_loadstore1_ifetch_invalid():
651 m
, cmpi
= setup_mmu()
653 # this is a specially-arranged page table which has the permissions
654 # barred for execute on the leaf node (EAA=0x2 instead of EAA=0x3)
655 mem
= pagetables
.test2
661 icache
= m
.submodules
.ldst
.icache
662 sim
.add_sync_process(wrap(_test_loadstore1_ifetch_invalid(m
, mem
)))
663 # add two wb_get processes onto the *same* memory dictionary.
664 # this shouuuld work.... cross-fingers...
665 sim
.add_sync_process(wrap(wb_get(cmpi
.wb_bus(), mem
)))
666 sim
.add_sync_process(wrap(wb_get(icache
.bus
, mem
)))
667 with sim
.write_vcd('test_loadstore1_ifetch_invalid.vcd',
668 traces
=[m
.debug_status
]): # include extra debug
673 if __name__
== '__main__':
675 test_loadstore1_invalid()
676 test_loadstore1_ifetch()
677 test_loadstore1_ifetch_invalid()