use icache_read in one place
[soc.git] / src / soc / experiment / test / test_loadstore1.py
1 from nmigen import (C, Module, Signal, Elaboratable, Mux, Cat, Repl, Signal,
2 Const)
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
10
11 from soc.config.test.test_pi2ls import (pi_ld, pi_st, pi_ldst, wait_busy,
12 get_exception_info)
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
16
17 from soc.fu.ldst.loadstore import LoadStore1
18 from soc.experiment.mmu import MMU
19 from soc.experiment.test import pagetables
20
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
26
27
28 def setup_mmu():
29
30 wbget.stop = False
31
32 pspec = TestMemPspec(ldst_ifacetype='mmu_cache_wb',
33 imem_ifacetype='',
34 addr_wid=48,
35 #disable_cache=True, # hmmm...
36 mask_wid=8,
37 reg_wid=64)
38
39 m = Module()
40 comb = m.d.comb
41 cmpi = ConfigMemoryPortInterface(pspec)
42 m.submodules.ldst = ldst = cmpi.pi
43 m.submodules.mmu = mmu = MMU()
44 dcache = ldst.dcache
45 icache = ldst.icache
46
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
50
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
55
56 # link ldst and MMU together
57 comb += l_in.eq(ldst.m_out)
58 comb += ldst.m_in.eq(l_out)
59
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
64 debug_status.str = ''
65
66 return m, cmpi
67
68
69 def icache_read(dut,addr,priv,virt):
70
71 icache = dut.submodules.ldst.icache
72 i_in = icache.i_in
73 i_out = icache.i_out
74
75 yield i_in.priv_mode.eq(priv)
76 yield i_in.virt_mode.eq(virt)
77 yield i_in.req.eq(1)
78 yield i_in.nia.eq(addr)
79 yield i_in.stop_mark.eq(0)
80
81 yield i_in.req.eq(1)
82 yield i_in.nia.eq(addr)
83 yield
84 valid = yield i_out.valid
85 failed = yield i_out.fetch_failed
86 while not valid and not failed:
87 yield
88 valid = yield i_out.valid
89 failed = yield i_out.fetch_failed
90 yield i_in.req.eq(0)
91
92 nia = yield i_out.nia
93 insn = yield i_out.insn
94 yield
95 yield
96
97 return nia, insn, valid, failed
98
99
100 test_exceptions = True
101 test_dcbz = True
102 test_random = True
103
104
105 def debug(dut, msg):
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)
110
111
112 def _test_loadstore1_ifetch(dut, mem):
113 """test_loadstore1_ifetch
114
115 this is quite a complex multi-step test.
116
117 * first (just because, as a demo) read in priv mode, non-virtual.
118 just like in experiment/icache.py itself.
119
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
124
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)
128
129 * fourth and finally: retry the read of the instruction through i-cache.
130 this is now expected to SUCCEED
131
132 a lot going on.
133 """
134
135 mmu = dut.submodules.mmu
136 ldst = dut.submodules.ldst
137 pi = ldst.pi
138 icache = dut.submodules.ldst.icache
139 wbget.stop = False
140
141 print("=== test loadstore instruction (real) ===")
142
143 i_in = icache.i_in
144 i_out = icache.i_out
145 i_m_in = icache.m_in
146
147 # first virtual memory test
148
149 print ("set process table")
150 yield from debug(dut, "set prtble")
151 yield mmu.rin.prtbl.eq(0x1000000) # set process table
152 yield
153
154 yield from debug(dut, "real mem instruction")
155 # set address to zero, update mem[0] to 01234
156 addr = 8
157 expected_insn = 0x1234
158 mem[addr] = expected_insn
159
160 yield i_in.priv_mode.eq(1)
161 yield i_in.req.eq(0)
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)
168 yield
169 yield
170 yield
171
172 # miss, stalls for a bit -- this one is different here
173 ##nia, insn, valid, failed = yield from icache_read(dut,addr,0,0)
174 ##assert(valid==0)
175 ##assert(failed==1)
176
177 yield i_in.req.eq(1)
178 yield i_in.nia.eq(addr)
179 yield
180 valid = yield i_out.valid
181 while not valid:
182 yield
183 valid = yield i_out.valid
184 yield i_in.req.eq(0)
185
186 nia = yield i_out.nia
187 insn = yield i_out.insn
188 yield
189 yield
190
191 print ("fetched %x from addr %x" % (insn, nia))
192 assert insn == expected_insn
193
194 print("=== test loadstore instruction (virtual) ===")
195
196 # look up i-cache expecting it to fail
197
198 yield from debug(dut, "virtual instr req")
199 # set address to 0x10200, update mem[] to 5678
200 virt_addr = 0x10200
201 real_addr = virt_addr
202 expected_insn = 0x5678
203 mem[real_addr] = expected_insn
204
205 yield i_in.priv_mode.eq(0)
206 yield i_in.virt_mode.eq(1)
207 yield i_in.req.eq(0)
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)
214 yield
215 yield
216 yield
217
218 # miss, stalls for a bit
219 yield i_in.req.eq(1)
220 yield i_in.nia.eq(virt_addr)
221 yield
222 valid = yield i_out.valid
223 failed = yield i_out.fetch_failed
224 while not valid and not failed:
225 yield
226 valid = yield i_out.valid
227 failed = yield i_out.fetch_failed
228 yield i_in.req.eq(0)
229
230 print ("failed?", "yes" if failed else "no")
231 assert failed == 1
232 yield
233 yield
234
235 print("=== test loadstore instruction (instruction fault) ===")
236
237 yield from debug(dut, "instr fault")
238
239 virt_addr = 0x10200
240
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)
245 yield
246 yield ldst.instr_fault.eq(0)
247 while True:
248 done = yield (ldst.done)
249 exc_info = yield from get_exception_info(pi.exc_o)
250 if done or exc_info.happened:
251 break
252 yield
253 assert exc_info.happened == 0 # assert just before doing the fault set zero
254 yield ldst.instr_fault.eq(0)
255 yield
256 yield
257 yield
258
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
262 virt_addr = 0x10200
263 real_addr = virt_addr
264 expected_insn = 0x5678
265
266 yield i_in.priv_mode.eq(0)
267 yield i_in.virt_mode.eq(1)
268 yield i_in.req.eq(0)
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)
275 yield
276 yield
277 yield
278
279 # miss, stalls for a bit
280 """
281 yield i_in.req.eq(1)
282 yield i_in.nia.eq(virt_addr)
283 yield
284 valid = yield i_out.valid
285 failed = yield i_out.fetch_failed
286 while not valid and not failed:
287 yield
288 valid = yield i_out.valid
289 failed = yield i_out.fetch_failed
290 yield i_in.req.eq(0)
291 nia = yield i_out.nia
292 insn = yield i_out.insn
293 """
294
295 ## part 4
296 nia, insn, valid, failed = yield from icache_read(dut,virt_addr,0,1)
297
298 yield from debug(dut, "test done")
299 yield
300 yield
301
302 print ("failed?", "yes" if failed else "no")
303 assert failed == 0
304
305 print ("fetched %x from addr %x" % (insn, nia))
306 assert insn == expected_insn
307
308 wbget.stop = True
309
310
311 def _test_loadstore1_invalid(dut, mem):
312 mmu = dut.submodules.mmu
313 pi = dut.submodules.ldst.pi
314 wbget.stop = False
315
316 print("=== test invalid ===")
317
318 addr = 0
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)
324
325 print("=== test invalid done ===")
326
327 wbget.stop = True
328
329
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)
334 wbget.stop = False
335
336 yield mmu.rin.prtbl.eq(0x1000000) # set process table
337 yield
338
339 addr = 0x100e0
340 data = 0xf553b658ba7e1f51
341
342 if test_dcbz:
343 yield from pi_st(pi, addr, data, 8, msr_pr=1)
344 yield
345
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
349
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
353
354 print("do_dcbz ===============")
355 yield from pi_st(pi, addr, data, 8, msr_pr=1, is_dcbz=1)
356 print("done_dcbz ===============")
357 yield
358
359 ld_data, exctype, exc = yield from pi_ld(pi, addr, 8, msr_pr=1)
360 print("ld_data after dcbz")
361 print(ld_data)
362 assert ld_data == 0
363 assert exctype is None
364
365 if test_exceptions:
366 print("=== alignment error (ld) ===")
367 addr = 0xFF100e0FF
368 ld_data, exctype, exc = yield from pi_ld(pi, addr, 8, msr_pr=1)
369 if exc:
370 alignment = exc.alignment
371 happened = exc.happened
372 yield # wait for dsr to update
373 dar = yield ldst.dar
374 else:
375 alignment = 0
376 happened = 0
377 dar = 0
378 assert (happened == 1)
379 assert (alignment == 1)
380 assert (dar == addr)
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) ===")
385
386 # take some cycles in between so that gtkwave separates out
387 # signals
388 yield
389 yield
390 yield
391 yield
392
393 print("=== alignment error (st) ===")
394 addr = 0xFF100e0FF
395 exctype, exc = yield from pi_st(pi, addr,0, 8, msr_pr=1)
396 if exc:
397 alignment = exc.alignment
398 happened = exc.happened
399 else:
400 alignment = 0
401 happened = 0
402 assert (happened == 1)
403 assert (alignment==1)
404 assert (dar==addr)
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) ===")
409 yield #FIXME hangs
410
411 if True:
412 print("=== no alignment error (ld) ===")
413 addr = 0x100e0
414 ld_data, exctype, exc = yield from pi_ld(pi, addr, 8, msr_pr=1)
415 print("ld_data", ld_data, exctype, exc)
416 if exc:
417 alignment = exc.alignment
418 happened = exc.happened
419 else:
420 alignment = 0
421 happened = 0
422 assert (happened == 0)
423 assert (alignment == 0)
424 print("=== no alignment error done (ld) ===")
425
426 if test_random:
427 addrs = [0x456920,0xa7a180,0x299420,0x1d9d60]
428
429 for addr in addrs:
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)
434
435 for addr in addrs:
436 print("== RANDOM addr ==",hex(addr))
437 exc = yield from pi_st(pi, addr,0xFF*addr, 8, msr_pr=1)
438 assert (exctype == None)
439
440 # readback written data and compare
441 for addr in addrs:
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)
447
448 print("== RANDOM addr done ==")
449
450 wbget.stop = True
451
452
453 def _test_loadstore1_ifetch_invalid(dut, mem):
454 mmu = dut.submodules.mmu
455 ldst = dut.submodules.ldst
456 pi = ldst.pi
457 icache = dut.submodules.ldst.icache
458 wbget.stop = False
459
460 print("=== test loadstore instruction (invalid) ===")
461
462 i_in = icache.i_in
463 i_out = icache.i_out
464 i_m_in = icache.m_in
465
466 # first virtual memory test
467
468 print ("set process table")
469 yield from debug(dut, "set prtbl")
470 yield mmu.rin.prtbl.eq(0x1000000) # set process table
471 yield
472
473 yield from debug(dut, "real mem instruction")
474 # set address to zero, update mem[0] to 01234
475 addr = 8
476 expected_insn = 0x1234
477 mem[addr] = expected_insn
478
479 yield i_in.priv_mode.eq(1)
480 yield i_in.req.eq(0)
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)
487 yield
488 yield
489 yield
490
491 # miss, stalls for a bit
492 yield i_in.req.eq(1)
493 yield i_in.nia.eq(addr)
494 yield
495 valid = yield i_out.valid
496 nia = yield i_out.nia
497 while not valid:
498 yield
499 valid = yield i_out.valid
500 yield i_in.req.eq(0)
501
502 nia = yield i_out.nia
503 insn = yield i_out.insn
504
505 yield
506 yield
507
508 print ("fetched %x from addr %x" % (insn, nia))
509 assert insn == expected_insn
510
511 print("=== test loadstore instruction (virtual) ===")
512 yield from debug(dut, "virtual instr req")
513
514 # look up i-cache expecting it to fail
515
516 # set address to 0x10200, update mem[] to 5678
517 virt_addr = 0x10200
518 real_addr = virt_addr
519 expected_insn = 0x5678
520 mem[real_addr] = expected_insn
521
522 yield i_in.priv_mode.eq(1)
523 yield i_in.virt_mode.eq(1)
524 yield i_in.req.eq(0)
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)
531 yield
532 yield
533 yield
534
535 # miss, stalls for a bit
536 yield i_in.req.eq(1)
537 yield i_in.nia.eq(virt_addr)
538 yield
539 valid = yield i_out.valid
540 failed = yield i_out.fetch_failed
541 while not valid and not failed:
542 yield
543 valid = yield i_out.valid
544 failed = yield i_out.fetch_failed
545 yield i_in.req.eq(0)
546
547 print ("failed?", "yes" if failed else "no")
548 assert failed == 1
549 yield
550 yield
551
552 print("=== test invalid loadstore instruction (instruction fault) ===")
553
554 yield from debug(dut, "instr fault (perm err expected)")
555 virt_addr = 0x10200
556
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)
561 yield
562 yield ldst.instr_fault.eq(0)
563 while True:
564 done = yield (ldst.done)
565 exc_info = yield from get_exception_info(pi.exc_o)
566 if done or exc_info.happened:
567 break
568 yield
569 assert exc_info.happened == 1 # different here as expected
570
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
575 # (iside) lookup
576 print (" MMU lookup exception type?")
577 for fname in LDSTExceptionTuple._fields:
578 print (" fname %20s %d" % (fname, getattr(exc_info, fname)))
579
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
588
589 yield from debug(dut, "test done")
590 yield ldst.instr_fault.eq(0)
591 yield
592 yield
593 yield
594
595 wbget.stop = True
596
597
598 def test_loadstore1_ifetch():
599
600 m, cmpi = setup_mmu()
601
602 mem = pagetables.test1
603
604 # nmigen Simulation
605 sim = Simulator(m)
606 sim.add_clock(1e-6)
607
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
616 sim.run()
617
618
619 def test_loadstore1():
620
621 m, cmpi = setup_mmu()
622
623 mem = pagetables.test1
624
625 # nmigen Simulation
626 sim = Simulator(m)
627 sim.add_clock(1e-6)
628
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'):
632 sim.run()
633
634
635 def test_loadstore1_invalid():
636
637 m, cmpi = setup_mmu()
638
639 mem = {}
640
641 # nmigen Simulation
642 sim = Simulator(m)
643 sim.add_clock(1e-6)
644
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'):
648 sim.run()
649
650 def test_loadstore1_ifetch_invalid():
651 m, cmpi = setup_mmu()
652
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
656
657 # nmigen Simulation
658 sim = Simulator(m)
659 sim.add_clock(1e-6)
660
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
669 sim.run()
670
671
672
673 if __name__ == '__main__':
674 test_loadstore1()
675 test_loadstore1_invalid()
676 test_loadstore1_ifetch()
677 test_loadstore1_ifetch_invalid()
678