whoops, no ability to add comments in between functions in pseudocode
[openpower-isa.git] / src / openpower / decoder / isa / test_caller_svp64_ldst.py
1 from nmigen import Module, Signal
2 from nmigen.back.pysim import Simulator, Delay, Settle
3 from nmutil.formaltest import FHDLTestCase
4 import unittest
5 from openpower.decoder.isa.caller import ISACaller
6 from openpower.decoder.power_decoder import (create_pdecode)
7 from openpower.decoder.power_decoder2 import (PowerDecode2)
8 from openpower.simulator.program import Program
9 from openpower.decoder.isa.caller import ISACaller, SVP64State
10 from openpower.decoder.selectable_int import SelectableInt
11 from openpower.decoder.orderedset import OrderedSet
12 from openpower.decoder.isa.all import ISA
13 from openpower.decoder.isa.test_caller import Register, run_tst
14 from openpower.sv.trans.svp64 import SVP64Asm
15 from openpower.consts import SVP64CROffs
16 from openpower.decoder.helpers import fp64toselectable
17 from openpower.decoder.isa.remap_dct_yield import (halfrev2, reverse_bits,
18 )
19 from copy import deepcopy
20
21
22 class DecoderTestCase(FHDLTestCase):
23
24 def _check_regs(self, sim, expected):
25 for i in range(32):
26 self.assertEqual(sim.gpr(i), SelectableInt(expected[i], 64))
27
28 def _check_fpregs(self, sim, expected):
29 for i in range(32):
30 self.assertEqual(sim.fpr(i), SelectableInt(expected[i], 64))
31
32 def test_sv_load_store_elementstride(self):
33 """>>> lst = ["addi 1, 0, 0x0010",
34 "addi 2, 0, 0x0008",
35 "addi 4, 0, 0x1234",
36 "addi 5, 0, 0x1235",
37 "sv.stw/els 4.v, 16(1)",
38 "sv.lwz/els 8.v, 16(1)"]
39
40 note: element stride mode is only enabled when RA is a scalar
41 and when the immediate is non-zero
42
43 element stride is computed as:
44 for i in range(VL):
45 EA = (RA|0) + EXTS(D) * i
46 """
47 lst = SVP64Asm(["addi 1, 0, 0x0010",
48 "addi 2, 0, 0x0008",
49 "addi 4, 0, 0x1234",
50 "addi 5, 0, 0x1235",
51 "sv.stw/els 4.v, 24(1)", # scalar r1 + 16 + 24*offs
52 "sv.lwz/els 8.v, 24(1)"]) # scalar r1 + 16 + 24*offs
53 lst = list(lst)
54
55 # SVSTATE (in this case, VL=2)
56 svstate = SVP64State()
57 svstate.vl = 2 # VL
58 svstate.maxvl = 2 # MAXVL
59 print ("SVSTATE", bin(svstate.asint()))
60
61 with Program(lst, bigendian=False) as program:
62 sim = self.run_tst_program(program, svstate=svstate)
63 mem = sim.mem.dump(printout=False)
64 print (mem)
65 # contents of memory expected at:
66 # element 0: r1=0x10, D=24, => EA = 0x10+24*0 = 16 (0x10)
67 # element 1: r1=0x10, D=24, => EA = 0x10+24*1 = 40 (0x28)
68 # therefore, at address 0x10 ==> 0x1234
69 # therefore, at address 0x28 ==> 0x1235
70 expected_mem = [(16, 0x1234),
71 (40, 0x1235)]
72 self.assertEqual(mem, expected_mem)
73 print(sim.gpr(1))
74 self.assertEqual(sim.gpr(8), SelectableInt(0x1234, 64))
75 self.assertEqual(sim.gpr(9), SelectableInt(0x1235, 64))
76
77 def test_sv_load_store_unitstride(self):
78 """>>> lst = ["addi 1, 0, 0x0010",
79 "addi 2, 0, 0x0008",
80 "addi 5, 0, 0x1234",
81 "addi 6, 0, 0x1235",
82 "sv.stw 8.v, 8(1)",
83 "sv.lwz 12.v, 8(1)"]
84
85 note: unit stride mode is only enabled when RA is a scalar.
86
87 unit stride is computed as:
88 for i in range(VL):
89 EA = (RA|0) + EXTS(D) + LDSTsize * i
90 where for stw and lwz, LDSTsize is 4 because it is 32-bit words
91 """
92 lst = SVP64Asm(["addi 1, 0, 0x0010",
93 "addi 2, 0, 0x0008",
94 "addi 8, 0, 0x1234",
95 "addi 9, 0, 0x1235",
96 "sv.stw 8.v, 8(1)", # scalar r1 + 8 + wordlen*offs
97 "sv.lwz 12.v, 8(1)"]) # scalar r1 + 8 + wordlen*offs
98 lst = list(lst)
99
100 # SVSTATE (in this case, VL=2)
101 svstate = SVP64State()
102 svstate.vl = 2 # VL
103 svstate.maxvl = 2 # MAXVL
104 print ("SVSTATE", bin(svstate.asint()))
105
106 with Program(lst, bigendian=False) as program:
107 sim = self.run_tst_program(program, svstate=svstate)
108 mem = sim.mem.dump(printout=False)
109 print ("Mem")
110 print (mem)
111 # contents of memory expected at:
112 # element 0: r1=0x10, D=8, wordlen=4 => EA = 0x10+8+4*0 = 0x24
113 # element 1: r1=0x10, D=8, wordlen=4 => EA = 0x10+8+4*8 = 0x28
114 # therefore, at address 0x24 ==> 0x1234
115 # therefore, at address 0x28 ==> 0x1235
116 self.assertEqual(mem, [(24, 0x123500001234)])
117 print(sim.gpr(1))
118 self.assertEqual(sim.gpr(12), SelectableInt(0x1234, 64))
119 self.assertEqual(sim.gpr(13), SelectableInt(0x1235, 64))
120
121 def test_sv_load_store_bitreverse(self):
122 """>>> lst = ["addi 1, 0, 0x0010",
123 "addi 2, 0, 0x0004",
124 "addi 3, 0, 0x0002",
125 "addi 5, 0, 0x101",
126 "addi 6, 0, 0x202",
127 "addi 7, 0, 0x303",
128 "addi 8, 0, 0x404",
129 "sv.stw 5.v, 0(1)",
130 "sv.lwzbr 12.v, 4(1), 2"]
131
132 note: bitreverse mode is... odd. it's the butterfly generator
133 from Cooley-Tukey FFT:
134 https://en.wikipedia.org/wiki/Cooley%E2%80%93Tukey_FFT_algorithm#Data_reordering,_bit_reversal,_and_in-place_algorithms
135
136 bitreverse LD is computed as:
137 for i in range(VL):
138 EA = (RA|0) + (EXTS(D) * LDSTsize * bitreverse(i, VL)) << RC
139
140 bitreversal of 0 1 2 3 in binary 0b00 0b01 0b10 0b11
141 produces 0 2 1 3 in binary 0b00 0b10 0b01 0b11
142
143 and thus creates the butterfly needed for one iteration of FFT.
144 the RC (shift) is to be able to offset the LDs by Radix-2 spans
145 """
146 lst = SVP64Asm(["addi 1, 0, 0x0010",
147 "addi 2, 0, 0x0000",
148 "addi 5, 0, 0x101",
149 "addi 6, 0, 0x202",
150 "addi 7, 0, 0x303",
151 "addi 8, 0, 0x404",
152 "sv.stw 5.v, 0(1)", # scalar r1 + 0 + wordlen*offs
153 "sv.lwzbr 12.v, 4(1), 2"]) # bit-reversed
154 lst = list(lst)
155
156 # SVSTATE (in this case, VL=4)
157 svstate = SVP64State()
158 svstate.vl = 4 # VL
159 svstate.maxvl = 4 # MAXVL
160 print ("SVSTATE", bin(svstate.asint()))
161
162 with Program(lst, bigendian=False) as program:
163 sim = self.run_tst_program(program, svstate=svstate)
164 mem = sim.mem.dump(printout=False)
165 print (mem)
166
167 self.assertEqual(mem, [(16, 0x020200000101),
168 (24, 0x040400000303)])
169 print(sim.gpr(1))
170 # from STs
171 self.assertEqual(sim.gpr(5), SelectableInt(0x101, 64))
172 self.assertEqual(sim.gpr(6), SelectableInt(0x202, 64))
173 self.assertEqual(sim.gpr(7), SelectableInt(0x303, 64))
174 self.assertEqual(sim.gpr(8), SelectableInt(0x404, 64))
175 # r1=0x10, RC=0, offs=4: contents of memory expected at:
176 # element 0: EA = r1 + bitrev(0b00)*4 => 0x10 + 0b00*4 => 0x10
177 # element 1: EA = r1 + bitrev(0b01)*4 => 0x10 + 0b10*4 => 0x18
178 # element 2: EA = r1 + bitrev(0b10)*4 => 0x10 + 0b01*4 => 0x14
179 # element 3: EA = r1 + bitrev(0b11)*4 => 0x10 + 0b10*4 => 0x1c
180 # therefore loaded from (bit-reversed indexing):
181 # r9 => mem[0x10] which was stored from r5
182 # r10 => mem[0x18] which was stored from r6
183 # r11 => mem[0x18] which was stored from r7
184 # r12 => mem[0x1c] which was stored from r8
185 self.assertEqual(sim.gpr(12), SelectableInt(0x101, 64))
186 self.assertEqual(sim.gpr(13), SelectableInt(0x303, 64))
187 self.assertEqual(sim.gpr(14), SelectableInt(0x202, 64))
188 self.assertEqual(sim.gpr(15), SelectableInt(0x404, 64))
189
190 def test_sv_load_store_bitreverse_fp(self):
191 """>>> lst = ["addi 1, 0, 0x0010",
192 "addi 2, 0, 0x0004",
193 "addi 3, 0, 0x0002",
194 "addi 5, 0, 0x101",
195 "addi 6, 0, 0x202",
196 "addi 7, 0, 0x303",
197 "addi 8, 0, 0x404",
198 "sv.std 5.v, 0(1)",
199 "sv.lfdbr 12.v, 4(1), 2"]
200
201 note: bitreverse mode is... odd. it's the butterfly generator
202 from Cooley-Tukey FFT:
203 https://en.wikipedia.org/wiki/Cooley%E2%80%93Tukey_FFT_algorithm#Data_reordering,_bit_reversal,_and_in-place_algorithms
204
205 bitreverse LD is computed as:
206 for i in range(VL):
207 EA = (RA|0) + (EXTS(D) * LDSTsize * bitreverse(i, VL)) << RC
208
209 bitreversal of 0 1 2 3 in binary 0b00 0b01 0b10 0b11
210 produces 0 2 1 3 in binary 0b00 0b10 0b01 0b11
211
212 and thus creates the butterfly needed for one iteration of FFT.
213 the RC (shift) is to be able to offset the LDs by Radix-2 spans
214 """
215 lst = SVP64Asm(["addi 1, 0, 0x0010",
216 "addi 2, 0, 0x0000",
217 "addi 5, 0, 0x101",
218 "addi 6, 0, 0x202",
219 "addi 7, 0, 0x303",
220 "addi 8, 0, 0x404",
221 "sv.std 5.v, 0(1)", # scalar r1 + 0 + wordlen*offs
222 "sv.lfdbr 12.v, 8(1), 2"]) # bit-reversed
223 lst = list(lst)
224
225 # SVSTATE (in this case, VL=4)
226 svstate = SVP64State()
227 svstate.vl = 4 # VL
228 svstate.maxvl = 4 # MAXVL
229 print ("SVSTATE", bin(svstate.asint()))
230
231 fprs = [0] * 32
232
233 with Program(lst, bigendian=False) as program:
234 sim = self.run_tst_program(program, svstate=svstate,
235 initial_fprs=fprs)
236 mem = sim.mem.dump(printout=False)
237 print (mem)
238
239 self.assertEqual(mem, [(16, 0x101),
240 (24, 0x202),
241 (32, 0x303),
242 (40, 0x404),
243 ])
244 print(sim.gpr(1))
245 # from STs
246 self.assertEqual(sim.gpr(5), SelectableInt(0x101, 64))
247 self.assertEqual(sim.gpr(6), SelectableInt(0x202, 64))
248 self.assertEqual(sim.gpr(7), SelectableInt(0x303, 64))
249 self.assertEqual(sim.gpr(8), SelectableInt(0x404, 64))
250 # r1=0x10, RC=0, offs=4: contents of memory expected at:
251 # element 0: EA = r1 + bitrev(0b00)*4 => 0x10 + 0b00*4 => 0x10
252 # element 1: EA = r1 + bitrev(0b01)*4 => 0x10 + 0b10*4 => 0x18
253 # element 2: EA = r1 + bitrev(0b10)*4 => 0x10 + 0b01*4 => 0x14
254 # element 3: EA = r1 + bitrev(0b11)*4 => 0x10 + 0b10*4 => 0x1c
255 # therefore loaded from (bit-reversed indexing):
256 # r9 => mem[0x10] which was stored from r5
257 # r10 => mem[0x18] which was stored from r6
258 # r11 => mem[0x18] which was stored from r7
259 # r12 => mem[0x1c] which was stored from r8
260 self.assertEqual(sim.fpr(12), SelectableInt(0x101, 64))
261 self.assertEqual(sim.fpr(13), SelectableInt(0x303, 64))
262 self.assertEqual(sim.fpr(14), SelectableInt(0x202, 64))
263 self.assertEqual(sim.fpr(15), SelectableInt(0x404, 64))
264
265 def test_sv_load_store_bitreverse2(self):
266 """>>> lst = ["addi 1, 0, 0x0010",
267 "addi 2, 0, 0x0004",
268 "addi 3, 0, 0x0002",
269 "sv.stfs 4.v, 0(1)",
270 "sv.lfsbr 12.v, 4(1), 2"]
271
272 note: bitreverse mode is... odd. it's the butterfly generator
273 from Cooley-Tukey FFT:
274 https://en.wikipedia.org/wiki/Cooley%E2%80%93Tukey_FFT_algorithm#Data_reordering,_bit_reversal,_and_in-place_algorithms
275
276 bitreverse LD is computed as:
277 for i in range(VL):
278 EA = (RA|0) + (EXTS(D) * LDSTsize * bitreverse(i, VL)) << RC
279
280 bitreversal of 0 1 2 3 in binary 0b00 0b01 0b10 0b11
281 produces 0 2 1 3 in binary 0b00 0b10 0b01 0b11
282
283 and thus creates the butterfly needed for one iteration of FFT.
284 the RC (shift) is to be able to offset the LDs by Radix-2 spans
285 """
286 lst = SVP64Asm(["addi 1, 0, 0x0010",
287 "addi 2, 0, 0x0000",
288 "sv.stfs 4.v, 0(1)", # scalar r1 + 0 + wordlen*offs
289 "sv.lfsbr 12.v, 4(1), 2"]) # bit-reversed
290 lst = list(lst)
291
292 # SVSTATE (in this case, VL=4)
293 svstate = SVP64State()
294 svstate.vl = 4 # VL
295 svstate.maxvl = 4 # MAXVL
296 print ("SVSTATE", bin(svstate.asint()))
297
298 fprs = [0] * 32
299 scalar_a = 1.3
300 scalar_b = -2.0
301 fprs[4] = fp64toselectable(1.0)
302 fprs[5] = fp64toselectable(2.0)
303 fprs[6] = fp64toselectable(3.0)
304 fprs[7] = fp64toselectable(4.0)
305
306 # expected results, remember that bit-reversed load has been done
307 expected_fprs = deepcopy(fprs)
308 expected_fprs[12] = fprs[4] # 0b00 -> 0b00
309 expected_fprs[13] = fprs[6] # 0b01 -> 0b10
310 expected_fprs[14] = fprs[5] # 0b10 -> 0b01
311 expected_fprs[15] = fprs[7] # 0b11 -> 0b11
312
313 with Program(lst, bigendian=False) as program:
314 sim = self.run_tst_program(program, svstate=svstate,
315 initial_fprs=fprs)
316 mem = sim.mem.dump(printout=False)
317 print ("mem dump")
318 print (mem)
319
320 print ("FPRs")
321 sim.fpr.dump()
322
323 #self.assertEqual(mem, [(16, 0x020200000101),
324 # (24, 0x040400000303)])
325 self._check_fpregs(sim, expected_fprs)
326
327 def test_sv_load_store_remap_matrix(self):
328 """>>> lst = ["addi 1, 0, 0x0010",
329 "addi 2, 0, 0x0004",
330 "addi 3, 0, 0x0002",
331 "addi 5, 0, 0x101",
332 "addi 6, 0, 0x202",
333 "addi 7, 0, 0x303",
334 "addi 8, 0, 0x404",
335 "sv.stw 4.v, 0(1)", # scalar r1 + 0 + wordlen*offs
336 "svshape 3, 3, 4, 0, 0",
337 "svremap 1, 1, 2, 0, 0, 0, 0, 1",
338 "sv.lwz 20.v, 0(1)",
339 ]
340
341 REMAPed a LD operation via a Matrix Multiply Schedule,
342 which is set up as 3x4 result
343 """
344 lst = SVP64Asm(["addi 1, 0, 0x0010",
345 "addi 2, 0, 0x0000",
346 "addi 4, 0, 0x101",
347 "addi 5, 0, 0x202",
348 "addi 6, 0, 0x303",
349 "addi 7, 0, 0x404",
350 "addi 8, 0, 0x505",
351 "addi 9, 0, 0x606",
352 "addi 10, 0, 0x707",
353 "addi 11, 0, 0x808",
354 "addi 12, 0, 0x909",
355 "addi 13, 0, 0xa0a",
356 "addi 14, 0, 0xb0b",
357 "addi 15, 0, 0xc0c",
358 "addi 16, 0, 0xd0d",
359 "addi 17, 0, 0xe0e",
360 "addi 18, 0, 0xf0f",
361 "sv.stw 4.v, 0(1)", # scalar r1 + 0 + wordlen*offs
362 "svshape 3, 3, 4, 0, 0",
363 "svremap 1, 1, 2, 0, 0, 0, 0, 1",
364 "sv.lwz 20.v, 0(1)",
365 #"sv.lwzbr 12.v, 4(1), 2", # bit-reversed
366 ])
367 lst = list(lst)
368
369 # SVSTATE (in this case, VL=4)
370 svstate = SVP64State()
371 svstate.vl = 12 # VL
372 svstate.maxvl = 12 # MAXVL
373 print ("SVSTATE", bin(svstate.asint()))
374
375 regs = [0] * 64
376
377 with Program(lst, bigendian=False) as program:
378 sim = self.run_tst_program(program, svstate=svstate,
379 initial_regs=regs)
380 mem = sim.mem.dump(printout=False)
381 print ("Mem")
382 print (mem)
383
384 self.assertEqual(mem, [(16, 0x020200000101),
385 (24, 0x040400000303),
386 (32, 0x060600000505),
387 (40, 0x080800000707),
388 (48, 0x0a0a00000909),
389 (56, 0x0c0c00000b0b)])
390 print(sim.gpr(1))
391 # from STs
392 self.assertEqual(sim.gpr(4), SelectableInt(0x101, 64))
393 self.assertEqual(sim.gpr(5), SelectableInt(0x202, 64))
394 self.assertEqual(sim.gpr(6), SelectableInt(0x303, 64))
395 self.assertEqual(sim.gpr(7), SelectableInt(0x404, 64))
396 self.assertEqual(sim.gpr(8), SelectableInt(0x505, 64))
397 self.assertEqual(sim.gpr(9), SelectableInt(0x606, 64))
398 self.assertEqual(sim.gpr(10), SelectableInt(0x707, 64))
399 self.assertEqual(sim.gpr(11), SelectableInt(0x808, 64))
400 # combination of bit-reversed load with a Matrix REMAP
401 # schedule
402 for i in range(3):
403 self.assertEqual(sim.gpr(20+i), SelectableInt(0x101, 64))
404 self.assertEqual(sim.gpr(23+i), SelectableInt(0x505, 64))
405 self.assertEqual(sim.gpr(26+i), SelectableInt(0x909, 64))
406 self.assertEqual(sim.gpr(29+i), SelectableInt(0x202, 64))
407
408 def test_sv_load_store_bitreverse_remap_halfswap(self):
409 """>>> lst = ["addi 1, 0, 0x0010",
410 "addi 2, 0, 0x0000",
411 "addi 4, 0, 0x101",
412 "addi 5, 0, 0x202",
413 "addi 6, 0, 0x303",
414 "addi 7, 0, 0x404",
415 "addi 8, 0, 0x505",
416 "addi 9, 0, 0x606",
417 "addi 10, 0, 0x707",
418 "addi 11, 0, 0x808",
419 "sv.stw 5.v, 0(1)",
420 "svshape 8, 1, 1, 6, 0",
421 "svremap 31, 1, 2, 3, 0, 0, 0, 0",
422 "sv.lwzbr 12.v, 4(1), 2"]
423
424 bitreverse LD is computed as:
425 for i in range(VL):
426 EA = (RA|0) + (EXTS(D) * LDSTsize * bitreverse(i, VL)) << RC
427
428 bitreversal of 0 1 2 3 in binary 0b00 0b01 0b10 0b11
429 produces 0 2 1 3 in binary 0b00 0b10 0b01 0b11
430
431 and thus creates the butterfly needed for one iteration of FFT.
432 the RC (shift) is to be able to offset the LDs by Radix-2 spans
433
434 on top of the bit-reversal is a REMAP for half-swaps for DCT
435 in-place.
436 """
437 lst = SVP64Asm(["addi 1, 0, 0x0010",
438 "addi 2, 0, 0x0000",
439 "addi 4, 0, 0x001",
440 "addi 5, 0, 0x102",
441 "addi 6, 0, 0x203",
442 "addi 7, 0, 0x304",
443 "addi 8, 0, 0x405",
444 "addi 9, 0, 0x506",
445 "addi 10, 0, 0x607",
446 "addi 11, 0, 0x708",
447 "sv.stw 4.v, 0(1)", # scalar r1 + 0 + wordlen*offs
448 "svshape 8, 1, 1, 6, 0",
449 "svremap 1, 0, 0, 0, 0, 0, 0, 1",
450 #"setvl 0, 0, 8, 0, 1, 1",
451 "sv.lwzbr 12.v, 4(1), 2", # bit-reversed
452 #"sv.lwz 12.v, 0(1)"
453 ])
454 lst = list(lst)
455
456 # SVSTATE (in this case, VL=4)
457 svstate = SVP64State()
458 svstate.vl = 8 # VL
459 svstate.maxvl = 8 # MAXVL
460 print ("SVSTATE", bin(svstate.asint()))
461
462 regs = [0] * 64
463
464 avi = [0x001, 0x102, 0x203, 0x304, 0x405, 0x506, 0x607, 0x708]
465 n = len(avi)
466 levels = n.bit_length() - 1
467 ri = list(range(n))
468 ri = [ri[reverse_bits(i, levels)] for i in range(n)]
469 av = halfrev2(avi, False)
470 av = [av[ri[i]] for i in range(n)]
471
472 with Program(lst, bigendian=False) as program:
473 sim = self.run_tst_program(program, svstate=svstate,
474 initial_regs=regs)
475 mem = sim.mem.dump(printout=False)
476 print ("Mem")
477 print (mem)
478
479 self.assertEqual(mem, [(16, 0x010200000001),
480 (24, 0x030400000203),
481 (32, 0x050600000405),
482 (40, 0x070800000607)])
483 # from STs
484 for i in range(len(avi)):
485 print ("st gpr", i, sim.gpr(i+4), hex(avi[i]))
486 self.assertEqual(sim.gpr(i+4), avi[i])
487 self.assertEqual(sim.gpr(5), SelectableInt(0x102, 64))
488 self.assertEqual(sim.gpr(6), SelectableInt(0x203, 64))
489 self.assertEqual(sim.gpr(7), SelectableInt(0x304, 64))
490 self.assertEqual(sim.gpr(8), SelectableInt(0x405, 64))
491 self.assertEqual(sim.gpr(9), SelectableInt(0x506, 64))
492 self.assertEqual(sim.gpr(10), SelectableInt(0x607, 64))
493 self.assertEqual(sim.gpr(11), SelectableInt(0x708, 64))
494 # combination of bit-reversed load with a DCT half-swap REMAP
495 # schedule
496 for i in range(len(avi)):
497 print ("ld gpr", i, sim.gpr(i+12), hex(av[i]))
498 self.assertEqual(sim.gpr(i+12), av[i])
499
500 def run_tst_program(self, prog, initial_regs=None,
501 svstate=None, initial_fprs=None):
502 if initial_regs is None:
503 initial_regs = [0] * 32
504 if initial_fprs is None:
505 initial_fprs = [0] * 32
506 simulator = run_tst(prog, initial_regs, svstate=svstate,
507 initial_fprs=initial_fprs)
508 print ("GPRs")
509 simulator.gpr.dump()
510 print ("FPRs")
511 simulator.fpr.dump()
512 return simulator
513
514
515 if __name__ == "__main__":
516 unittest.main()