1 from nmigen
import Module
, Signal
2 from nmigen
.sim
.pysim
import Simulator
, Delay
, Settle
3 from nmigen
.cli
import rtlil
5 from soc
.decoder
.isa
.caller
import ISACaller
, special_sprs
6 from soc
.decoder
.power_decoder
import (create_pdecode
)
7 from soc
.decoder
.power_decoder2
import (PowerDecode2
)
8 from soc
.decoder
.power_enums
import (XER_bits
, Function
, MicrOp
, CryIn
)
9 from soc
.decoder
.selectable_int
import SelectableInt
10 from soc
.simulator
.program
import Program
11 from soc
.decoder
.isa
.all
import ISA
12 from soc
.config
.endian
import bigendian
14 from soc
.fu
.test
.common
import (TestAccumulatorBase
, TestCase
, ALUHelpers
)
15 from soc
.fu
.mul
.pipeline
import MulBasePipe
16 from soc
.fu
.mul
.pipe_data
import MulPipeSpec
20 def get_cu_inputs(dec2
, sim
):
21 """naming (res) must conform to MulFunctionUnit input regspec
25 yield from ALUHelpers
.get_sim_int_ra(res
, sim
, dec2
) # RA
26 yield from ALUHelpers
.get_sim_int_rb(res
, sim
, dec2
) # RB
27 yield from ALUHelpers
.get_sim_xer_so(res
, sim
, dec2
) # XER.so
29 print("alu get_cu_inputs", res
)
34 def set_alu_inputs(alu
, dec2
, sim
):
35 # TODO: see https://bugs.libre-soc.org/show_bug.cgi?id=305#c43
36 # detect the immediate here (with m.If(self.i.ctx.op.imm_data.imm_ok))
37 # and place it into data_i.b
39 inp
= yield from get_cu_inputs(dec2
, sim
)
40 print("set alu inputs", inp
)
41 yield from ALUHelpers
.set_int_ra(alu
, dec2
, inp
)
42 yield from ALUHelpers
.set_int_rb(alu
, dec2
, inp
)
44 yield from ALUHelpers
.set_xer_so(alu
, dec2
, inp
)
47 # This test bench is a bit different than is usual. Initially when I
48 # was writing it, I had all of the tests call a function to create a
49 # device under test and simulator, initialize the dut, run the
50 # simulation for ~2 cycles, and assert that the dut output what it
51 # should have. However, this was really slow, since it needed to
52 # create and tear down the dut and simulator for every test case.
54 # Now, instead of doing that, every test case in MulTestCase puts some
55 # data into the test_data list below, describing the instructions to
56 # be tested and the initial state. Once all the tests have been run,
57 # test_data gets passed to TestRunner which then sets up the DUT and
58 # simulator once, runs all the data through it, and asserts that the
59 # results match the pseudocode sim at every cycle.
61 # By doing this, I've reduced the time it takes to run the test suite
62 # massively. Before, it took around 1 minute on my computer, now it
63 # takes around 3 seconds
66 class MulTestCase(TestAccumulatorBase
):
68 def case_0_mullw(self
):
69 lst
= [f
"mullw 3, 1, 2"]
70 initial_regs
= [0] * 32
71 #initial_regs[1] = 0xffffffffffffffff
72 #initial_regs[2] = 0xffffffffffffffff
73 initial_regs
[1] = 0x2ffffffff
75 self
.add_case(Program(lst
, bigendian
), initial_regs
)
77 def case_1_mullwo_(self
):
78 lst
= [f
"mullwo. 3, 1, 2"]
79 initial_regs
= [0] * 32
80 initial_regs
[1] = 0x3b34b06f
81 initial_regs
[2] = 0xfdeba998
82 self
.add_case(Program(lst
, bigendian
), initial_regs
)
84 def case_2_mullwo(self
):
85 lst
= [f
"mullwo 3, 1, 2"]
86 initial_regs
= [0] * 32
87 initial_regs
[1] = 0xffffffffffffa988 # -5678
88 initial_regs
[2] = 0xffffffffffffedcc # -1234
89 self
.add_case(Program(lst
, bigendian
), initial_regs
)
91 def case_3_mullw(self
):
92 lst
= ["mullw 3, 1, 2",
94 initial_regs
= [0] * 32
97 self
.add_case(Program(lst
, bigendian
), initial_regs
)
99 def case_4_mullw_rand(self
):
101 lst
= ["mullw 3, 1, 2"]
102 initial_regs
= [0] * 32
103 initial_regs
[1] = random
.randint(0, (1 << 64)-1)
104 initial_regs
[2] = random
.randint(0, (1 << 64)-1)
105 self
.add_case(Program(lst
, bigendian
), initial_regs
)
107 def case_4_mullw_nonrand(self
):
109 lst
= ["mullw 3, 1, 2"]
110 initial_regs
= [0] * 32
111 initial_regs
[1] = i
+1
112 initial_regs
[2] = i
+20
113 self
.add_case(Program(lst
, bigendian
), initial_regs
)
115 def case_mulhw__regression_1(self
):
116 lst
= ["mulhw. 3, 1, 2"
118 initial_regs
= [0] * 32
119 initial_regs
[1] = 0x7745b36eca6646fa
120 initial_regs
[2] = 0x47dfba3a63834ba2
121 self
.add_case(Program(lst
, bigendian
), initial_regs
)
123 def case_rand_mul_lh(self
):
124 insns
= ["mulhw", "mulhw.", "mulhwu", "mulhwu."]
126 choice
= random
.choice(insns
)
127 lst
= [f
"{choice} 3, 1, 2"]
128 initial_regs
= [0] * 32
129 initial_regs
[1] = random
.randint(0, (1 << 64)-1)
130 initial_regs
[2] = random
.randint(0, (1 << 64)-1)
131 self
.add_case(Program(lst
, bigendian
), initial_regs
)
133 def case_rand_mullw(self
):
134 insns
= ["mullw", "mullw.", "mullwo", "mullwo."]
136 choice
= random
.choice(insns
)
137 lst
= [f
"{choice} 3, 1, 2"]
138 initial_regs
= [0] * 32
139 initial_regs
[1] = random
.randint(0, (1 << 64)-1)
140 initial_regs
[2] = random
.randint(0, (1 << 64)-1)
141 self
.add_case(Program(lst
, bigendian
), initial_regs
)
143 def case_rand_mulld(self
):
144 insns
= ["mulld", "mulld.", "mulldo", "mulldo."]
146 choice
= random
.choice(insns
)
147 lst
= [f
"{choice} 3, 1, 2"]
148 initial_regs
= [0] * 32
149 initial_regs
[1] = random
.randint(0, (1 << 64)-1)
150 initial_regs
[2] = random
.randint(0, (1 << 64)-1)
151 self
.add_case(Program(lst
, bigendian
), initial_regs
)
153 def case_rand_mulhd(self
):
154 insns
= ["mulhd", "mulhd."]
156 choice
= random
.choice(insns
)
157 lst
= [f
"{choice} 3, 1, 2"]
158 initial_regs
= [0] * 32
159 initial_regs
[1] = random
.randint(0, (1 << 64)-1)
160 initial_regs
[2] = random
.randint(0, (1 << 64)-1)
161 self
.add_case(Program(lst
, bigendian
), initial_regs
)
178 0xFFFF_FFFF_FFFF_FFFF,
179 0xFFFF_FFFF_FFFF_FFFE,
180 0x7FFF_FFFF_FFFF_FFFF,
181 0x8000_0000_0000_0000,
182 0x1234_5678_0000_0000,
183 0x1234_5678_8000_0000,
184 0x1234_5678_FFFF_FFFF,
185 0x1234_5678_7FFF_FFFF,
194 l
= [f
"{instr} 3, 1, 2"]
195 for ra
in test_values
:
196 for rb
in test_values
:
197 initial_regs
= [0] * 32
200 # use "with" so as to close the files used
201 with
Program(l
, bigendian
) as prog
:
202 self
.add_case(prog
, initial_regs
)
204 def case_all_rb_randint(self
):
219 0xFFFF_FFFF_FFFF_FFFF,
220 0xFFFF_FFFF_FFFF_FFFE,
221 0x7FFF_FFFF_FFFF_FFFF,
222 0x8000_0000_0000_0000,
223 0x1234_5678_0000_0000,
224 0x1234_5678_8000_0000,
225 0x1234_5678_FFFF_FFFF,
226 0x1234_5678_7FFF_FFFF,
235 l
= [f
"{instr} 3, 1, 2"]
236 for ra
in test_values
:
237 initial_regs
= [0] * 32
239 initial_regs
[2] = random
.randint(0, (1 << 64)-1)
240 # use "with" so as to close the files used
241 with
Program(l
, bigendian
) as prog
:
242 self
.add_case(prog
, initial_regs
)
244 def case_all_rb_close_to_ov(self
):
259 0xFFFF_FFFF_FFFF_FFFF,
260 0xFFFF_FFFF_FFFF_FFFE,
261 0x7FFF_FFFF_FFFF_FFFF,
262 0x8000_0000_0000_0000,
263 0x1234_5678_0000_0000,
264 0x1234_5678_8000_0000,
265 0x1234_5678_FFFF_FFFF,
266 0x1234_5678_7FFF_FFFF,
274 x
= 0x7fffffff + random
.randint(0, 1)
275 ra
= random
.randint(0, (1 << 64)-1)
279 l
= [f
"{instr} 3, 1, 2"]
280 initial_regs
= [0] * 32
283 # use "with" so as to close the files used
284 with
Program(l
, bigendian
) as prog
:
285 self
.add_case(prog
, initial_regs
)
287 def case_mulli(self
):
289 test_values
= [-32768, -32767, -32766, -2, -1, 0, 1, 2, 32766, 32767, random
.randint(-1 << 15, (1 << 15)-1), random
.randint(-1 << 15, (
290 1 << 15) - 1), random
.randint(-1 << 15, (1 << 15)-1), random
.randint(-1 << 15, (1 << 15)-1), random
.randint(-1 << 15, (1 << 15)-1)]
292 l
= ["mulli 3, 1, 2"]
293 for ra
in test_values
:
294 for rb
in test_values
:
295 initial_regs
= [0] * 32
298 # use "with" so as to close the files used
299 with
Program(l
, bigendian
) as prog
:
300 self
.add_case(prog
, initial_regs
)
302 # TODO add test case for these 3 operand cases (madd
303 # needs to be implemented)
304 # "maddhd","maddhdu","maddld"
306 def case_ilang(self
):
307 pspec
= MulPipeSpec(id_wid
=2)
308 alu
= MulBasePipe(pspec
)
309 vl
= rtlil
.convert(alu
, ports
=alu
.ports())
310 with
open("mul_pipeline.il", "w") as f
:
314 class TestRunner(unittest
.TestCase
):
315 def __init__(self
, test_data
):
316 super().__init
__("run_all")
317 self
.test_data
= test_data
322 instruction
= Signal(32)
324 pdecode
= create_pdecode()
326 m
.submodules
.pdecode2
= pdecode2
= PowerDecode2(pdecode
)
328 pspec
= MulPipeSpec(id_wid
=2)
329 m
.submodules
.alu
= alu
= MulBasePipe(pspec
)
331 comb
+= alu
.p
.data_i
.ctx
.op
.eq_from_execute1(pdecode2
.e
)
332 comb
+= alu
.n
.ready_i
.eq(1)
333 comb
+= pdecode2
.dec
.raw_opcode_in
.eq(instruction
)
339 for test
in self
.test_data
:
341 program
= test
.program
342 self
.subTest(test
.name
)
343 sim
= ISA(pdecode2
, test
.regs
, test
.sprs
, test
.cr
,
346 gen
= program
.generate_instructions()
347 instructions
= list(zip(gen
, program
.assembly
.splitlines()))
350 index
= sim
.pc
.CIA
.value
//4
351 while index
< len(instructions
):
352 ins
, code
= instructions
[index
]
354 print("instruction: 0x{:X}".format(ins
& 0xffffffff))
357 so
= 1 if sim
.spr
['XER'][XER_bits
['SO']] else 0
358 ov
= 1 if sim
.spr
['XER'][XER_bits
['OV']] else 0
359 ov32
= 1 if sim
.spr
['XER'][XER_bits
['OV32']] else 0
360 print("before: so/ov/32", so
, ov
, ov32
)
362 # ask the decoder to decode this binary data (endian'd)
363 yield pdecode2
.dec
.bigendian
.eq(bigendian
) # little / big?
364 yield instruction
.eq(ins
) # raw binary instr.
366 fn_unit
= yield pdecode2
.e
.do
.fn_unit
367 self
.assertEqual(fn_unit
, Function
.MUL
.value
)
368 yield from set_alu_inputs(alu
, pdecode2
, sim
)
370 # set valid for one cycle, propagate through pipeline...
371 yield alu
.p
.valid_i
.eq(1)
373 yield alu
.p
.valid_i
.eq(0)
375 opname
= code
.split(' ')[0]
376 yield from sim
.call(opname
)
377 index
= sim
.pc
.CIA
.value
//4
379 # ...wait for valid to pop out the end
380 vld
= yield alu
.n
.valid_o
383 vld
= yield alu
.n
.valid_o
386 yield from self
.check_alu_outputs(alu
, pdecode2
, sim
, code
)
389 sim
.add_sync_process(process
)
390 with sim
.write_vcd("mul_simulator.vcd", "mul_simulator.gtkw",
394 def check_alu_outputs(self
, alu
, dec2
, sim
, code
):
396 rc
= yield dec2
.e
.do
.rc
.data
397 cridx_ok
= yield dec2
.e
.write_cr
.ok
398 cridx
= yield dec2
.e
.write_cr
.data
400 print("check extra output", repr(code
), cridx_ok
, cridx
)
402 self
.assertEqual(cridx
, 0, code
)
404 oe
= yield dec2
.e
.do
.oe
.oe
405 oe_ok
= yield dec2
.e
.do
.oe
.ok
406 if not oe
or not oe_ok
:
407 # if OE not enabled, XER SO and OV must correspondingly be false
408 so_ok
= yield alu
.n
.data_o
.xer_so
.ok
409 ov_ok
= yield alu
.n
.data_o
.xer_ov
.ok
410 self
.assertEqual(so_ok
, False, code
)
411 self
.assertEqual(ov_ok
, False, code
)
416 yield from ALUHelpers
.get_cr_a(res
, alu
, dec2
)
417 yield from ALUHelpers
.get_xer_ov(res
, alu
, dec2
)
418 yield from ALUHelpers
.get_int_o(res
, alu
, dec2
)
419 yield from ALUHelpers
.get_xer_so(res
, alu
, dec2
)
421 yield from ALUHelpers
.get_sim_int_o(sim_o
, sim
, dec2
)
422 yield from ALUHelpers
.get_wr_sim_cr_a(sim_o
, sim
, dec2
)
423 yield from ALUHelpers
.get_sim_xer_ov(sim_o
, sim
, dec2
)
424 yield from ALUHelpers
.get_sim_xer_so(sim_o
, sim
, dec2
)
426 ALUHelpers
.check_int_o(self
, res
, sim_o
, code
)
427 ALUHelpers
.check_xer_ov(self
, res
, sim_o
, code
)
428 ALUHelpers
.check_xer_so(self
, res
, sim_o
, code
)
429 ALUHelpers
.check_cr_a(self
, res
, sim_o
, "CR%d %s" % (cridx
, code
))
432 if __name__
== "__main__":
433 unittest
.main(exit
=False)
434 suite
= unittest
.TestSuite()
435 suite
.addTest(TestRunner(MulTestCase().test_data
))
437 runner
= unittest
.TextTestRunner()