test_loadstore1.py: begin code deduplication
[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 yield i_in.priv_mode.eq(priv)
72 yield i_in.virt_mode.eq(virt)
73 yield i_in.req.eq(1)
74 yield i_in.nia.eq(addr)
75 yield i_in.stop_mark.eq(0)
76
77 yield i_in.req.eq(1)
78 yield i_in.nia.eq(addr)
79 yield
80 valid = yield i_out.valid
81 failed = yield i_out.fetch_failed
82 while not valid and not failed:
83 yield
84 valid = yield i_out.valid
85 failed = yield i_out.fetch_failed
86 yield i_in.req.eq(0)
87
88 nia = yield i_out.nia
89 insn = yield i_out.insn
90 yield
91 yield
92
93 return nia, insn, valid, failed
94
95
96 test_exceptions = True
97 test_dcbz = True
98 test_random = True
99
100
101 def debug(dut, msg):
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)
106
107
108 def _test_loadstore1_ifetch(dut, mem):
109 """test_loadstore1_ifetch
110
111 this is quite a complex multi-step test.
112
113 * first (just because, as a demo) read in priv mode, non-virtual.
114 just like in experiment/icache.py itself.
115
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
120
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)
124
125 * fourth and finally: retry the read of the instruction through i-cache.
126 this is now expected to SUCCEED
127
128 a lot going on.
129 """
130
131 mmu = dut.submodules.mmu
132 ldst = dut.submodules.ldst
133 pi = ldst.pi
134 icache = dut.submodules.ldst.icache
135 wbget.stop = False
136
137 print("=== test loadstore instruction (real) ===")
138
139 i_in = icache.i_in
140 i_out = icache.i_out
141 i_m_in = icache.m_in
142
143 # first virtual memory test
144
145 print ("set process table")
146 yield from debug(dut, "set prtble")
147 yield mmu.rin.prtbl.eq(0x1000000) # set process table
148 yield
149
150 yield from debug(dut, "real mem instruction")
151 # set address to zero, update mem[0] to 01234
152 addr = 8
153 expected_insn = 0x1234
154 mem[addr] = expected_insn
155
156 yield i_in.priv_mode.eq(1)
157 yield i_in.req.eq(0)
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)
164 yield
165 yield
166 yield
167
168 # miss, stalls for a bit
169 yield i_in.req.eq(1)
170 yield i_in.nia.eq(addr)
171 yield
172 valid = yield i_out.valid
173 while not valid:
174 yield
175 valid = yield i_out.valid
176 yield i_in.req.eq(0)
177
178 nia = yield i_out.nia
179 insn = yield i_out.insn
180 yield
181 yield
182
183 print ("fetched %x from addr %x" % (insn, nia))
184 assert insn == expected_insn
185
186 print("=== test loadstore instruction (virtual) ===")
187
188 # look up i-cache expecting it to fail
189
190 yield from debug(dut, "virtual instr req")
191 # set address to 0x10200, update mem[] to 5678
192 virt_addr = 0x10200
193 real_addr = virt_addr
194 expected_insn = 0x5678
195 mem[real_addr] = expected_insn
196
197 yield i_in.priv_mode.eq(0)
198 yield i_in.virt_mode.eq(1)
199 yield i_in.req.eq(0)
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)
206 yield
207 yield
208 yield
209
210 # miss, stalls for a bit
211 yield i_in.req.eq(1)
212 yield i_in.nia.eq(virt_addr)
213 yield
214 valid = yield i_out.valid
215 failed = yield i_out.fetch_failed
216 while not valid and not failed:
217 yield
218 valid = yield i_out.valid
219 failed = yield i_out.fetch_failed
220 yield i_in.req.eq(0)
221
222 print ("failed?", "yes" if failed else "no")
223 assert failed == 1
224 yield
225 yield
226
227 print("=== test loadstore instruction (instruction fault) ===")
228
229 yield from debug(dut, "instr fault")
230
231 virt_addr = 0x10200
232
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)
237 yield
238 yield ldst.instr_fault.eq(0)
239 while True:
240 done = yield (ldst.done)
241 exc_info = yield from get_exception_info(pi.exc_o)
242 if done or exc_info.happened:
243 break
244 yield
245 assert exc_info.happened == 0 # assert just before doing the fault set zero
246 yield ldst.instr_fault.eq(0)
247 yield
248 yield
249 yield
250
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
254 virt_addr = 0x10200
255 real_addr = virt_addr
256 expected_insn = 0x5678
257
258 yield i_in.priv_mode.eq(0)
259 yield i_in.virt_mode.eq(1)
260 yield i_in.req.eq(0)
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)
267 yield
268 yield
269 yield
270
271 # miss, stalls for a bit
272 yield i_in.req.eq(1)
273 yield i_in.nia.eq(virt_addr)
274 yield
275 valid = yield i_out.valid
276 failed = yield i_out.fetch_failed
277 while not valid and not failed:
278 yield
279 valid = yield i_out.valid
280 failed = yield i_out.fetch_failed
281 yield i_in.req.eq(0)
282 nia = yield i_out.nia
283 insn = yield i_out.insn
284
285 yield from debug(dut, "test done")
286 yield
287 yield
288
289 print ("failed?", "yes" if failed else "no")
290 assert failed == 0
291
292 print ("fetched %x from addr %x" % (insn, nia))
293 assert insn == expected_insn
294
295 wbget.stop = True
296
297
298 def _test_loadstore1_invalid(dut, mem):
299 mmu = dut.submodules.mmu
300 pi = dut.submodules.ldst.pi
301 wbget.stop = False
302
303 print("=== test invalid ===")
304
305 addr = 0
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)
311
312 print("=== test invalid done ===")
313
314 wbget.stop = True
315
316
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)
321 wbget.stop = False
322
323 yield mmu.rin.prtbl.eq(0x1000000) # set process table
324 yield
325
326 addr = 0x100e0
327 data = 0xf553b658ba7e1f51
328
329 if test_dcbz:
330 yield from pi_st(pi, addr, data, 8, msr_pr=1)
331 yield
332
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
336
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
340
341 print("do_dcbz ===============")
342 yield from pi_st(pi, addr, data, 8, msr_pr=1, is_dcbz=1)
343 print("done_dcbz ===============")
344 yield
345
346 ld_data, exctype, exc = yield from pi_ld(pi, addr, 8, msr_pr=1)
347 print("ld_data after dcbz")
348 print(ld_data)
349 assert ld_data == 0
350 assert exctype is None
351
352 if test_exceptions:
353 print("=== alignment error (ld) ===")
354 addr = 0xFF100e0FF
355 ld_data, exctype, exc = yield from pi_ld(pi, addr, 8, msr_pr=1)
356 if exc:
357 alignment = exc.alignment
358 happened = exc.happened
359 yield # wait for dsr to update
360 dar = yield ldst.dar
361 else:
362 alignment = 0
363 happened = 0
364 dar = 0
365 assert (happened == 1)
366 assert (alignment == 1)
367 assert (dar == addr)
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) ===")
372
373 # take some cycles in between so that gtkwave separates out
374 # signals
375 yield
376 yield
377 yield
378 yield
379
380 print("=== alignment error (st) ===")
381 addr = 0xFF100e0FF
382 exctype, exc = yield from pi_st(pi, addr,0, 8, msr_pr=1)
383 if exc:
384 alignment = exc.alignment
385 happened = exc.happened
386 else:
387 alignment = 0
388 happened = 0
389 assert (happened == 1)
390 assert (alignment==1)
391 assert (dar==addr)
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) ===")
396 yield #FIXME hangs
397
398 if True:
399 print("=== no alignment error (ld) ===")
400 addr = 0x100e0
401 ld_data, exctype, exc = yield from pi_ld(pi, addr, 8, msr_pr=1)
402 print("ld_data", ld_data, exctype, exc)
403 if exc:
404 alignment = exc.alignment
405 happened = exc.happened
406 else:
407 alignment = 0
408 happened = 0
409 assert (happened == 0)
410 assert (alignment == 0)
411 print("=== no alignment error done (ld) ===")
412
413 if test_random:
414 addrs = [0x456920,0xa7a180,0x299420,0x1d9d60]
415
416 for addr in addrs:
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)
421
422 for addr in addrs:
423 print("== RANDOM addr ==",hex(addr))
424 exc = yield from pi_st(pi, addr,0xFF*addr, 8, msr_pr=1)
425 assert (exctype == None)
426
427 # readback written data and compare
428 for addr in addrs:
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)
434
435 print("== RANDOM addr done ==")
436
437 wbget.stop = True
438
439
440 def _test_loadstore1_ifetch_invalid(dut, mem):
441 mmu = dut.submodules.mmu
442 ldst = dut.submodules.ldst
443 pi = ldst.pi
444 icache = dut.submodules.ldst.icache
445 wbget.stop = False
446
447 print("=== test loadstore instruction (invalid) ===")
448
449 i_in = icache.i_in
450 i_out = icache.i_out
451 i_m_in = icache.m_in
452
453 # first virtual memory test
454
455 print ("set process table")
456 yield from debug(dut, "set prtbl")
457 yield mmu.rin.prtbl.eq(0x1000000) # set process table
458 yield
459
460 yield from debug(dut, "real mem instruction")
461 # set address to zero, update mem[0] to 01234
462 addr = 8
463 expected_insn = 0x1234
464 mem[addr] = expected_insn
465
466 yield i_in.priv_mode.eq(1)
467 yield i_in.req.eq(0)
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)
474 yield
475 yield
476 yield
477
478 # miss, stalls for a bit
479 yield i_in.req.eq(1)
480 yield i_in.nia.eq(addr)
481 yield
482 valid = yield i_out.valid
483 nia = yield i_out.nia
484 while not valid:
485 yield
486 valid = yield i_out.valid
487 yield i_in.req.eq(0)
488
489 nia = yield i_out.nia
490 insn = yield i_out.insn
491
492 yield
493 yield
494
495 print ("fetched %x from addr %x" % (insn, nia))
496 assert insn == expected_insn
497
498 print("=== test loadstore instruction (virtual) ===")
499 yield from debug(dut, "virtual instr req")
500
501 # look up i-cache expecting it to fail
502
503 # set address to 0x10200, update mem[] to 5678
504 virt_addr = 0x10200
505 real_addr = virt_addr
506 expected_insn = 0x5678
507 mem[real_addr] = expected_insn
508
509 yield i_in.priv_mode.eq(1)
510 yield i_in.virt_mode.eq(1)
511 yield i_in.req.eq(0)
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)
518 yield
519 yield
520 yield
521
522 # miss, stalls for a bit
523 yield i_in.req.eq(1)
524 yield i_in.nia.eq(virt_addr)
525 yield
526 valid = yield i_out.valid
527 failed = yield i_out.fetch_failed
528 while not valid and not failed:
529 yield
530 valid = yield i_out.valid
531 failed = yield i_out.fetch_failed
532 yield i_in.req.eq(0)
533
534 print ("failed?", "yes" if failed else "no")
535 assert failed == 1
536 yield
537 yield
538
539 print("=== test invalid loadstore instruction (instruction fault) ===")
540
541 yield from debug(dut, "instr fault (perm err expected)")
542 virt_addr = 0x10200
543
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)
548 yield
549 yield ldst.instr_fault.eq(0)
550 while True:
551 done = yield (ldst.done)
552 exc_info = yield from get_exception_info(pi.exc_o)
553 if done or exc_info.happened:
554 break
555 yield
556 assert exc_info.happened == 1 # different here as expected
557
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
562 # (iside) lookup
563 print (" MMU lookup exception type?")
564 for fname in LDSTExceptionTuple._fields:
565 print (" fname %20s %d" % (fname, getattr(exc_info, fname)))
566
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
575
576 yield from debug(dut, "test done")
577 yield ldst.instr_fault.eq(0)
578 yield
579 yield
580 yield
581
582 wbget.stop = True
583
584
585 def test_loadstore1_ifetch():
586
587 m, cmpi = setup_mmu()
588
589 mem = pagetables.test1
590
591 # nmigen Simulation
592 sim = Simulator(m)
593 sim.add_clock(1e-6)
594
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
603 sim.run()
604
605
606 def test_loadstore1():
607
608 m, cmpi = setup_mmu()
609
610 mem = pagetables.test1
611
612 # nmigen Simulation
613 sim = Simulator(m)
614 sim.add_clock(1e-6)
615
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'):
619 sim.run()
620
621
622 def test_loadstore1_invalid():
623
624 m, cmpi = setup_mmu()
625
626 mem = {}
627
628 # nmigen Simulation
629 sim = Simulator(m)
630 sim.add_clock(1e-6)
631
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'):
635 sim.run()
636
637 def test_loadstore1_ifetch_invalid():
638 m, cmpi = setup_mmu()
639
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
643
644 # nmigen Simulation
645 sim = Simulator(m)
646 sim.add_clock(1e-6)
647
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
656 sim.run()
657
658
659
660 if __name__ == '__main__':
661 test_loadstore1()
662 test_loadstore1_invalid()
663 test_loadstore1_ifetch()
664 test_loadstore1_ifetch_invalid()
665