implement main part of test_loadstore1_ifetch_invalid()
[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
26
27 def setup_mmu():
28
29 wbget.stop = False
30
31 pspec = TestMemPspec(ldst_ifacetype='mmu_cache_wb',
32 imem_ifacetype='',
33 addr_wid=48,
34 #disable_cache=True, # hmmm...
35 mask_wid=8,
36 reg_wid=64)
37
38 m = Module()
39 comb = m.d.comb
40 cmpi = ConfigMemoryPortInterface(pspec)
41 m.submodules.ldst = ldst = cmpi.pi
42 m.submodules.mmu = mmu = MMU()
43 dcache = ldst.dcache
44 icache = ldst.icache
45
46 l_in, l_out = mmu.l_in, mmu.l_out
47 d_in, d_out = dcache.d_in, dcache.d_out
48 i_in, i_out = icache.i_in, icache.i_out # FetchToICache, ICacheToDecode
49
50 # link mmu, dcache and icache together
51 m.d.comb += dcache.m_in.eq(mmu.d_out) # MMUToDCacheType
52 m.d.comb += icache.m_in.eq(mmu.i_out) # MMUToICacheType
53 m.d.comb += mmu.d_in.eq(dcache.m_out) # DCacheToMMUType
54
55 # link ldst and MMU together
56 comb += l_in.eq(ldst.m_out)
57 comb += ldst.m_in.eq(l_out)
58
59 return m, cmpi
60
61
62 test_exceptions = True
63 test_dcbz = True
64 test_random = True
65
66
67 def _test_loadstore1_ifetch(dut, mem):
68 """test_loadstore1_ifetch
69
70 this is quite a complex multi-step test.
71
72 * first (just because, as a demo) read in priv mode, non-virtual.
73 just like in experiment/icache.py itself.
74
75 * second, using the (usual) PTE for these things (which came originally
76 from gem5-experimental experiment/radix_walk_example.txt) do a
77 virtual-memory read through the *instruction* cache.
78 this is expected to FAIL
79
80 * third: mess about with the MMU, setting "iside" (instruction-side),
81 requesting an MMU RADIX LOOKUP. this triggers an itlb_load
82 (instruction-cache TLB entry-insertion)
83
84 * fourth and finally: retry the read of the instruction through i-cache.
85 this is now expected to SUCCEED
86
87 a lot going on.
88 """
89
90 mmu = dut.submodules.mmu
91 ldst = dut.submodules.ldst
92 pi = ldst.pi
93 icache = dut.submodules.ldst.icache
94 wbget.stop = False
95
96 print("=== test loadstore instruction (real) ===")
97
98 i_in = icache.i_in
99 i_out = icache.i_out
100 i_m_in = icache.m_in
101
102 # first virtual memory test
103
104 print ("set process table")
105 yield mmu.rin.prtbl.eq(0x1000000) # set process table
106 yield
107
108 # set address to zero, update mem[0] to 01234
109 addr = 8
110 expected_insn = 0x1234
111 mem[addr] = expected_insn
112
113 yield i_in.priv_mode.eq(1)
114 yield i_in.req.eq(0)
115 yield i_in.nia.eq(addr)
116 yield i_in.stop_mark.eq(0)
117 yield i_m_in.tlbld.eq(0)
118 yield i_m_in.tlbie.eq(0)
119 yield i_m_in.addr.eq(0)
120 yield i_m_in.pte.eq(0)
121 yield
122 yield
123 yield
124
125 # miss, stalls for a bit
126 yield i_in.req.eq(1)
127 yield i_in.nia.eq(addr)
128 yield
129 valid = yield i_out.valid
130 while not valid:
131 yield
132 valid = yield i_out.valid
133 yield i_in.req.eq(0)
134
135 nia = yield i_out.nia
136 insn = yield i_out.insn
137 yield
138 yield
139
140 print ("fetched %x from addr %x" % (insn, nia))
141 assert insn == expected_insn
142
143 print("=== test loadstore instruction (virtual) ===")
144
145 # look up i-cache expecting it to fail
146
147 # set address to 0x10200, update mem[] to 5678
148 virt_addr = 0x10200
149 real_addr = virt_addr
150 expected_insn = 0x5678
151 mem[real_addr] = expected_insn
152
153 yield i_in.priv_mode.eq(0)
154 yield i_in.virt_mode.eq(1)
155 yield i_in.req.eq(0)
156 yield i_in.nia.eq(virt_addr)
157 yield i_in.stop_mark.eq(0)
158 yield i_m_in.tlbld.eq(0)
159 yield i_m_in.tlbie.eq(0)
160 yield i_m_in.addr.eq(0)
161 yield i_m_in.pte.eq(0)
162 yield
163 yield
164 yield
165
166 # miss, stalls for a bit
167 yield i_in.req.eq(1)
168 yield i_in.nia.eq(virt_addr)
169 yield
170 valid = yield i_out.valid
171 failed = yield i_out.fetch_failed
172 while not valid and not failed:
173 yield
174 valid = yield i_out.valid
175 failed = yield i_out.fetch_failed
176 yield i_in.req.eq(0)
177
178 print ("failed?", "yes" if failed else "no")
179 assert failed == 1
180 yield
181 yield
182
183 print("=== test loadstore instruction (instruction fault) ===")
184
185 virt_addr = 0x10200
186
187 yield ldst.priv_mode.eq(0)
188 yield ldst.instr_fault.eq(1)
189 yield ldst.maddr.eq(virt_addr)
190 #ld_data, exctype, exc = yield from pi_ld(pi, virt_addr, 8, msr_pr=1)
191 yield
192 yield ldst.instr_fault.eq(0)
193 while True:
194 done = yield (ldst.done)
195 exc_info = yield from get_exception_info(pi.exc_o)
196 if done or exc_info.happened:
197 break
198 yield
199 assert exc_info.happened == 0 # assert just before doing the fault set zero
200 yield ldst.instr_fault.eq(0)
201 yield
202 yield
203 yield
204
205 print("=== test loadstore instruction (try instruction again) ===")
206 # set address to 0x10200, update mem[] to 5678
207 virt_addr = 0x10200
208 real_addr = virt_addr
209 expected_insn = 0x5678
210
211 yield i_in.priv_mode.eq(0)
212 yield i_in.virt_mode.eq(1)
213 yield i_in.req.eq(0)
214 yield i_in.nia.eq(virt_addr)
215 yield i_in.stop_mark.eq(0)
216 yield i_m_in.tlbld.eq(0)
217 yield i_m_in.tlbie.eq(0)
218 yield i_m_in.addr.eq(0)
219 yield i_m_in.pte.eq(0)
220 yield
221 yield
222 yield
223
224 # miss, stalls for a bit
225 yield i_in.req.eq(1)
226 yield i_in.nia.eq(virt_addr)
227 yield
228 valid = yield i_out.valid
229 failed = yield i_out.fetch_failed
230 while not valid and not failed:
231 yield
232 valid = yield i_out.valid
233 failed = yield i_out.fetch_failed
234 yield i_in.req.eq(0)
235 nia = yield i_out.nia
236 insn = yield i_out.insn
237 yield
238 yield
239
240 print ("failed?", "yes" if failed else "no")
241 assert failed == 0
242
243 print ("fetched %x from addr %x" % (insn, nia))
244 assert insn == expected_insn
245
246 wbget.stop = True
247
248
249 def _test_loadstore1_invalid(dut, mem):
250 mmu = dut.submodules.mmu
251 pi = dut.submodules.ldst.pi
252 wbget.stop = False
253
254 print("=== test invalid ===")
255
256 addr = 0
257 ld_data, exctype, exc = yield from pi_ld(pi, addr, 8, msr_pr=1)
258 print("ld_data", ld_data, exctype, exc)
259 assert (exctype == "slow")
260 invalid = exc.invalid
261 assert (invalid == 1)
262
263 print("=== test invalid done ===")
264
265 wbget.stop = True
266
267
268 def _test_loadstore1(dut, mem):
269 mmu = dut.submodules.mmu
270 pi = dut.submodules.ldst.pi
271 ldst = dut.submodules.ldst # to get at DAR (NOT part of PortInterface)
272 wbget.stop = False
273
274 yield mmu.rin.prtbl.eq(0x1000000) # set process table
275 yield
276
277 addr = 0x100e0
278 data = 0xf553b658ba7e1f51
279
280 if test_dcbz:
281 yield from pi_st(pi, addr, data, 8, msr_pr=1)
282 yield
283
284 ld_data, exctype, exc = yield from pi_ld(pi, addr, 8, msr_pr=1)
285 assert ld_data == 0xf553b658ba7e1f51
286 assert exctype is None
287
288 ld_data, exctype, exc = yield from pi_ld(pi, addr, 8, msr_pr=1)
289 assert ld_data == 0xf553b658ba7e1f51
290 assert exctype is None
291
292 print("do_dcbz ===============")
293 yield from pi_st(pi, addr, data, 8, msr_pr=1, is_dcbz=1)
294 print("done_dcbz ===============")
295 yield
296
297 ld_data, exctype, exc = yield from pi_ld(pi, addr, 8, msr_pr=1)
298 print("ld_data after dcbz")
299 print(ld_data)
300 assert ld_data == 0
301 assert exctype is None
302
303 if test_exceptions:
304 print("=== alignment error (ld) ===")
305 addr = 0xFF100e0FF
306 ld_data, exctype, exc = yield from pi_ld(pi, addr, 8, msr_pr=1)
307 if exc:
308 alignment = exc.alignment
309 happened = exc.happened
310 yield # wait for dsr to update
311 dar = yield ldst.dar
312 else:
313 alignment = 0
314 happened = 0
315 dar = 0
316 assert (happened == 1)
317 assert (alignment == 1)
318 assert (dar == addr)
319 assert (exctype == "fast")
320 yield from wait_busy(pi, debug="pi_ld_E_alignment_error")
321 # wait is only needed in case of in exception here
322 print("=== alignment error test passed (ld) ===")
323
324 # take some cycles in between so that gtkwave separates out
325 # signals
326 yield
327 yield
328 yield
329 yield
330
331 print("=== alignment error (st) ===")
332 addr = 0xFF100e0FF
333 exctype, exc = yield from pi_st(pi, addr,0, 8, msr_pr=1)
334 if exc:
335 alignment = exc.alignment
336 happened = exc.happened
337 else:
338 alignment = 0
339 happened = 0
340 assert (happened == 1)
341 assert (alignment==1)
342 assert (dar==addr)
343 assert (exctype == "fast")
344 #???? yield from wait_busy(pi, debug="pi_st_E_alignment_error")
345 # wait is only needed in case of in exception here
346 print("=== alignment error test passed (st) ===")
347 yield #FIXME hangs
348
349 if True:
350 print("=== no alignment error (ld) ===")
351 addr = 0x100e0
352 ld_data, exctype, exc = yield from pi_ld(pi, addr, 8, msr_pr=1)
353 print("ld_data", ld_data, exctype, exc)
354 if exc:
355 alignment = exc.alignment
356 happened = exc.happened
357 else:
358 alignment = 0
359 happened = 0
360 assert (happened == 0)
361 assert (alignment == 0)
362 print("=== no alignment error done (ld) ===")
363
364 if test_random:
365 addrs = [0x456920,0xa7a180,0x299420,0x1d9d60]
366
367 for addr in addrs:
368 print("== RANDOM addr ==",hex(addr))
369 ld_data, exctype, exc = yield from pi_ld(pi, addr, 8, msr_pr=1)
370 print("ld_data[RANDOM]",ld_data,exc,addr)
371 assert (exctype == None)
372
373 for addr in addrs:
374 print("== RANDOM addr ==",hex(addr))
375 exc = yield from pi_st(pi, addr,0xFF*addr, 8, msr_pr=1)
376 assert (exctype == None)
377
378 # readback written data and compare
379 for addr in addrs:
380 print("== RANDOM addr ==",hex(addr))
381 ld_data, exctype, exc = yield from pi_ld(pi, addr, 8, msr_pr=1)
382 print("ld_data[RANDOM_READBACK]",ld_data,exc,addr)
383 assert (exctype == None)
384 assert (ld_data == 0xFF*addr)
385
386 print("== RANDOM addr done ==")
387
388 wbget.stop = True
389
390
391 def _test_loadstore1_ifetch_invalid(dut, mem):
392 mmu = dut.submodules.mmu
393 ldst = dut.submodules.ldst
394 pi = ldst.pi
395 icache = dut.submodules.ldst.icache
396 wbget.stop = False
397
398 print("=== test loadstore instruction (invalid) ===")
399
400 i_in = icache.i_in
401 i_out = icache.i_out
402 i_m_in = icache.m_in
403
404 # first virtual memory test
405
406 print ("set process table")
407 yield mmu.rin.prtbl.eq(0x1000000) # set process table
408 yield
409
410 # set address to zero, update mem[0] to 01234
411 addr = 8
412 expected_insn = 0x1234
413 mem[addr] = expected_insn
414
415 yield i_in.priv_mode.eq(1)
416 yield i_in.req.eq(0)
417 yield i_in.nia.eq(addr)
418 yield i_in.stop_mark.eq(0)
419 yield i_m_in.tlbld.eq(0)
420 yield i_m_in.tlbie.eq(0)
421 yield i_m_in.addr.eq(0)
422 yield i_m_in.pte.eq(0)
423 yield
424 yield
425 yield
426
427 # miss, stalls for a bit
428 yield i_in.req.eq(1)
429 yield i_in.nia.eq(addr)
430 yield
431 valid = yield i_out.valid
432 nia = yield i_out.nia
433 while not valid:
434 yield
435 valid = yield i_out.valid
436 yield i_in.req.eq(0)
437
438 nia = yield i_out.nia
439 insn = yield i_out.insn
440
441 yield
442 yield
443
444 print ("fetched %x from addr %x" % (insn, nia))
445 assert insn == expected_insn
446
447 print("=== test loadstore instruction (virtual) ===")
448
449 # look up i-cache expecting it to fail
450
451 # set address to 0x10200, update mem[] to 5678
452 virt_addr = 0x10200
453 real_addr = virt_addr
454 expected_insn = 0x5678
455 mem[real_addr] = expected_insn
456
457 yield i_in.priv_mode.eq(1)
458 yield i_in.virt_mode.eq(1)
459 yield i_in.req.eq(0)
460 yield i_in.nia.eq(virt_addr)
461 yield i_in.stop_mark.eq(0)
462 yield i_m_in.tlbld.eq(0)
463 yield i_m_in.tlbie.eq(0)
464 yield i_m_in.addr.eq(0)
465 yield i_m_in.pte.eq(0)
466 yield
467 yield
468 yield
469
470 # miss, stalls for a bit
471 yield i_in.req.eq(1)
472 yield i_in.nia.eq(virt_addr)
473 yield
474 valid = yield i_out.valid
475 failed = yield i_out.fetch_failed
476 while not valid and not failed:
477 yield
478 valid = yield i_out.valid
479 failed = yield i_out.fetch_failed
480 yield i_in.req.eq(0)
481
482 print ("failed?", "yes" if failed else "no")
483 assert failed == 1
484 yield
485 yield
486
487 print("=== test invalid loadstore instruction (instruction fault) ===")
488
489 virt_addr = 0x10200
490
491 yield ldst.priv_mode.eq(0)
492 yield ldst.instr_fault.eq(1)
493 yield ldst.maddr.eq(virt_addr)
494 #ld_data, exctype, exc = yield from pi_ld(pi, virt_addr, 8, msr_pr=1)
495 yield
496 yield ldst.instr_fault.eq(0)
497 while True:
498 done = yield (ldst.done)
499 exc_info = yield from get_exception_info(pi.exc_o)
500 if done or exc_info.happened:
501 break
502 yield
503 assert exc_info.happened == 1 # different here as expected
504 yield ldst.instr_fault.eq(0)
505 yield
506 yield
507 yield
508
509 wbget.stop = True
510
511
512 def test_loadstore1_ifetch():
513
514 m, cmpi = setup_mmu()
515
516 mem = pagetables.test1
517
518 # nmigen Simulation
519 sim = Simulator(m)
520 sim.add_clock(1e-6)
521
522 icache = m.submodules.ldst.icache
523 sim.add_sync_process(wrap(_test_loadstore1_ifetch(m, mem)))
524 # add two wb_get processes onto the *same* memory dictionary.
525 # this shouuuld work.... cross-fingers...
526 sim.add_sync_process(wrap(wb_get(cmpi.wb_bus(), mem)))
527 sim.add_sync_process(wrap(wb_get(icache.bus, mem)))
528 with sim.write_vcd('test_loadstore1_ifetch.vcd'):
529 sim.run()
530
531
532 def test_loadstore1():
533
534 m, cmpi = setup_mmu()
535
536 mem = pagetables.test1
537
538 # nmigen Simulation
539 sim = Simulator(m)
540 sim.add_clock(1e-6)
541
542 sim.add_sync_process(wrap(_test_loadstore1(m, mem)))
543 sim.add_sync_process(wrap(wb_get(cmpi.wb_bus(), mem)))
544 with sim.write_vcd('test_loadstore1.vcd'):
545 sim.run()
546
547
548 def test_loadstore1_invalid():
549
550 m, cmpi = setup_mmu()
551
552 mem = {}
553
554 # nmigen Simulation
555 sim = Simulator(m)
556 sim.add_clock(1e-6)
557
558 sim.add_sync_process(wrap(_test_loadstore1_invalid(m, mem)))
559 sim.add_sync_process(wrap(wb_get(cmpi.wb_bus(), mem)))
560 with sim.write_vcd('test_loadstore1_invalid.vcd'):
561 sim.run()
562
563 def test_loadstore1_ifetch_invalid():
564 m, cmpi = setup_mmu()
565
566 # this is a specially-arranged page table which has the permissions
567 # barred for execute on the leaf node (EAA=0x2 instead of EAA=0x3)
568 mem = pagetables.test2
569
570 # nmigen Simulation
571 sim = Simulator(m)
572 sim.add_clock(1e-6)
573
574 icache = m.submodules.ldst.icache
575 sim.add_sync_process(wrap(_test_loadstore1_ifetch_invalid(m, mem)))
576 # add two wb_get processes onto the *same* memory dictionary.
577 # this shouuuld work.... cross-fingers...
578 sim.add_sync_process(wrap(wb_get(cmpi.wb_bus(), mem)))
579 sim.add_sync_process(wrap(wb_get(icache.bus, mem)))
580 with sim.write_vcd('test_loadstore1_ifetch_invalid.vcd'):
581 sim.run()
582
583
584
585 if __name__ == '__main__':
586 test_loadstore1()
587 test_loadstore1_invalid()
588 test_loadstore1_ifetch()
589 test_loadstore1_ifetch_invalid()
590