4e7ee7fe885ea0047dc1404ce668ff058150c94a
[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 # some more cycles for gtkwave debugging
428 yield
429 yield
430 yield
431
432 wbget.stop = True
433 return
434 # TODO: implement rest
435
436 # miss, stalls for a bit
437 yield i_in.req.eq(1)
438 yield i_in.nia.eq(addr)
439 yield
440 valid = yield i_out.valid
441 while not valid:
442 yield
443 valid = yield i_out.valid
444 yield i_in.req.eq(0)
445
446 nia = yield i_out.nia
447 insn = yield i_out.insn
448 yield
449 yield
450
451 print ("fetched %x from addr %x" % (insn, nia))
452 assert insn == expected_insn
453
454 print("=== test loadstore instruction (virtual) ===")
455
456 # look up i-cache expecting it to fail
457
458 # set address to 0x10200, update mem[] to 5678
459 virt_addr = 0x10200
460 real_addr = virt_addr
461 expected_insn = 0x5678
462 mem[real_addr] = expected_insn
463
464 yield i_in.priv_mode.eq(1)
465 yield i_in.virt_mode.eq(1)
466 yield i_in.req.eq(0)
467 yield i_in.nia.eq(virt_addr)
468 yield i_in.stop_mark.eq(0)
469 yield i_m_in.tlbld.eq(0)
470 yield i_m_in.tlbie.eq(0)
471 yield i_m_in.addr.eq(0)
472 yield i_m_in.pte.eq(0)
473 yield
474 yield
475 yield
476
477 # miss, stalls for a bit
478 yield i_in.req.eq(1)
479 yield i_in.nia.eq(virt_addr)
480 yield
481 valid = yield i_out.valid
482 failed = yield i_out.fetch_failed
483 while not valid and not failed:
484 yield
485 valid = yield i_out.valid
486 failed = yield i_out.fetch_failed
487 yield i_in.req.eq(0)
488
489 print ("failed?", "yes" if failed else "no")
490 assert failed == 1
491 yield
492 yield
493
494
495 def test_loadstore1_ifetch():
496
497 m, cmpi = setup_mmu()
498
499 mem = pagetables.test1
500
501 # nmigen Simulation
502 sim = Simulator(m)
503 sim.add_clock(1e-6)
504
505 icache = m.submodules.ldst.icache
506 sim.add_sync_process(wrap(_test_loadstore1_ifetch(m, mem)))
507 # add two wb_get processes onto the *same* memory dictionary.
508 # this shouuuld work.... cross-fingers...
509 sim.add_sync_process(wrap(wb_get(cmpi.wb_bus(), mem)))
510 sim.add_sync_process(wrap(wb_get(icache.bus, mem)))
511 with sim.write_vcd('test_loadstore1_ifetch.vcd'):
512 sim.run()
513
514
515 def test_loadstore1():
516
517 m, cmpi = setup_mmu()
518
519 mem = pagetables.test1
520
521 # nmigen Simulation
522 sim = Simulator(m)
523 sim.add_clock(1e-6)
524
525 sim.add_sync_process(wrap(_test_loadstore1(m, mem)))
526 sim.add_sync_process(wrap(wb_get(cmpi.wb_bus(), mem)))
527 with sim.write_vcd('test_loadstore1.vcd'):
528 sim.run()
529
530
531 def test_loadstore1_invalid():
532
533 m, cmpi = setup_mmu()
534
535 mem = {}
536
537 # nmigen Simulation
538 sim = Simulator(m)
539 sim.add_clock(1e-6)
540
541 sim.add_sync_process(wrap(_test_loadstore1_invalid(m, mem)))
542 sim.add_sync_process(wrap(wb_get(cmpi.wb_bus(), mem)))
543 with sim.write_vcd('test_loadstore1_invalid.vcd'):
544 sim.run()
545
546 def test_loadstore1_ifetch_invalid():
547 m, cmpi = setup_mmu()
548
549 # this is a specially-arranged page table which has the permissions
550 # barred for execute on the leaf node (EAA=0x2 instead of EAA=0x3)
551 mem = pagetables.test2
552
553 # nmigen Simulation
554 sim = Simulator(m)
555 sim.add_clock(1e-6)
556
557 icache = m.submodules.ldst.icache
558 sim.add_sync_process(wrap(_test_loadstore1_ifetch_invalid(m, mem)))
559 # add two wb_get processes onto the *same* memory dictionary.
560 # this shouuuld work.... cross-fingers...
561 sim.add_sync_process(wrap(wb_get(cmpi.wb_bus(), mem)))
562 sim.add_sync_process(wrap(wb_get(icache.bus, mem)))
563 with sim.write_vcd('test_loadstore1_ifetch_invalid.vcd'):
564 sim.run()
565
566
567
568 if __name__ == '__main__':
569 #test_loadstore1()
570 #test_loadstore1_invalid()
571 #test_loadstore1_ifetch()
572 test_loadstore1_ifetch_invalid()