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
):
83 0xFFFF_FFFF_FFFF_FFFF,
84 0xFFFF_FFFF_FFFF_FFFE,
85 0x7FFF_FFFF_FFFF_FFFF,
86 0x8000_0000_0000_0000,
87 0x1234_5678_0000_0000,
88 0x1234_5678_8000_0000,
89 0x1234_5678_FFFF_FFFF,
90 0x1234_5678_7FFF_FFFF,
99 l
= [f
"{instr} 3, 1, 2"]
100 for ra
in test_values
:
101 for rb
in test_values
:
102 initial_regs
= [0] * 32
105 # use "with" so as to close the files used
106 with
Program(l
, bigendian
) as prog
:
107 self
.add_case(prog
, initial_regs
)
109 def case_all_rb_randint(self
):
124 0xFFFF_FFFF_FFFF_FFFF,
125 0xFFFF_FFFF_FFFF_FFFE,
126 0x7FFF_FFFF_FFFF_FFFF,
127 0x8000_0000_0000_0000,
128 0x1234_5678_0000_0000,
129 0x1234_5678_8000_0000,
130 0x1234_5678_FFFF_FFFF,
131 0x1234_5678_7FFF_FFFF,
140 l
= [f
"{instr} 3, 1, 2"]
141 for ra
in test_values
:
142 initial_regs
= [0] * 32
144 initial_regs
[2] = random
.randint(0, (1 << 64)-1)
145 # use "with" so as to close the files used
146 with
Program(l
, bigendian
) as prog
:
147 self
.add_case(prog
, initial_regs
)
149 def case_all_rb_close_to_ov(self
):
164 0xFFFF_FFFF_FFFF_FFFF,
165 0xFFFF_FFFF_FFFF_FFFE,
166 0x7FFF_FFFF_FFFF_FFFF,
167 0x8000_0000_0000_0000,
168 0x1234_5678_0000_0000,
169 0x1234_5678_8000_0000,
170 0x1234_5678_FFFF_FFFF,
171 0x1234_5678_7FFF_FFFF,
181 x
= 0x7fffffff + random
.randint((-1 << 31), (1 << 31) - 1)
182 ra
= random
.randint(0, (1 << 32)-1)
185 l
= [f
"{instr} 3, 1, 2"]
186 initial_regs
= [0] * 32
189 # use "with" so as to close the files used
190 with
Program(l
, bigendian
) as prog
:
191 self
.add_case(prog
, initial_regs
)
193 def case_mulli(self
):
195 imm_values
= [-32768, -32767, -32766, -2, -1, 0, 1, 2, 32766, 32767]
201 0xFFFF_FFFF_FFFF_FFFF,
202 0xFFFF_FFFF_FFFF_FFFE,
203 0x7FFF_FFFF_FFFF_FFFF,
204 0x8000_0000_0000_0000,
205 0x1234_5678_0000_0000,
206 0x1234_5678_8000_0000,
207 0x1234_5678_FFFF_FFFF,
208 0x1234_5678_7FFF_FFFF,
217 imm_values
.append(random
.randint(-1 << 15, (1 << 15) - 1))
220 ra_values
.append(random
.randint(0, (1 << 64) - 1))
223 for imm
in imm_values
:
224 l
= [f
"mulli 0, 1, {imm}"]
225 initial_regs
= [0] * 32
227 # use "with" so as to close the files used
228 with
Program(l
, bigendian
) as prog
:
229 self
.add_case(prog
, initial_regs
)
231 # TODO add test case for these 3 operand cases (madd
232 # needs to be implemented)
233 # "maddhd","maddhdu","maddld"
235 def case_ilang(self
):
236 pspec
= MulPipeSpec(id_wid
=2)
237 alu
= MulBasePipe(pspec
)
238 vl
= rtlil
.convert(alu
, ports
=alu
.ports())
239 with
open("mul_pipeline.il", "w") as f
:
243 class TestRunner(unittest
.TestCase
):
244 def __init__(self
, test_data
):
245 super().__init
__("run_all")
246 self
.test_data
= test_data
251 instruction
= Signal(32)
253 pdecode
= create_pdecode()
255 m
.submodules
.pdecode2
= pdecode2
= PowerDecode2(pdecode
)
257 pspec
= MulPipeSpec(id_wid
=2)
258 m
.submodules
.alu
= alu
= MulBasePipe(pspec
)
260 comb
+= alu
.p
.data_i
.ctx
.op
.eq_from_execute1(pdecode2
.do
)
261 comb
+= alu
.n
.ready_i
.eq(1)
262 comb
+= pdecode2
.dec
.raw_opcode_in
.eq(instruction
)
268 for test
in self
.test_data
:
270 program
= test
.program
271 self
.subTest(test
.name
)
272 sim
= ISA(pdecode2
, test
.regs
, test
.sprs
, test
.cr
,
275 gen
= program
.generate_instructions()
276 instructions
= list(zip(gen
, program
.assembly
.splitlines()))
279 index
= sim
.pc
.CIA
.value
//4
280 while index
< len(instructions
):
281 ins
, code
= instructions
[index
]
283 print("instruction: 0x{:X}".format(ins
& 0xffffffff))
286 so
= 1 if sim
.spr
['XER'][XER_bits
['SO']] else 0
287 ov
= 1 if sim
.spr
['XER'][XER_bits
['OV']] else 0
288 ov32
= 1 if sim
.spr
['XER'][XER_bits
['OV32']] else 0
289 print("before: so/ov/32", so
, ov
, ov32
)
291 # ask the decoder to decode this binary data (endian'd)
292 yield pdecode2
.dec
.bigendian
.eq(bigendian
) # little / big?
293 yield instruction
.eq(ins
) # raw binary instr.
295 fn_unit
= yield pdecode2
.e
.do
.fn_unit
296 self
.assertEqual(fn_unit
, Function
.MUL
.value
)
297 yield from set_alu_inputs(alu
, pdecode2
, sim
)
299 # set valid for one cycle, propagate through pipeline...
300 yield alu
.p
.valid_i
.eq(1)
302 yield alu
.p
.valid_i
.eq(0)
304 opname
= code
.split(' ')[0]
305 yield from sim
.call(opname
)
306 index
= sim
.pc
.CIA
.value
//4
308 # ...wait for valid to pop out the end
309 vld
= yield alu
.n
.valid_o
312 vld
= yield alu
.n
.valid_o
315 yield from self
.check_alu_outputs(alu
, pdecode2
, sim
, code
)
318 sim
.add_sync_process(process
)
319 with sim
.write_vcd("mul_simulator.vcd", "mul_simulator.gtkw",
323 def check_alu_outputs(self
, alu
, dec2
, sim
, code
):
325 rc
= yield dec2
.e
.do
.rc
.data
326 cridx_ok
= yield dec2
.e
.write_cr
.ok
327 cridx
= yield dec2
.e
.write_cr
.data
329 print("check extra output", repr(code
), cridx_ok
, cridx
)
331 self
.assertEqual(cridx
, 0, code
)
333 oe
= yield dec2
.e
.do
.oe
.oe
334 oe_ok
= yield dec2
.e
.do
.oe
.ok
335 if not oe
or not oe_ok
:
336 # if OE not enabled, XER SO and OV must correspondingly be false
337 so_ok
= yield alu
.n
.data_o
.xer_so
.ok
338 ov_ok
= yield alu
.n
.data_o
.xer_ov
.ok
339 self
.assertEqual(so_ok
, False, code
)
340 self
.assertEqual(ov_ok
, False, code
)
345 yield from ALUHelpers
.get_cr_a(res
, alu
, dec2
)
346 yield from ALUHelpers
.get_xer_ov(res
, alu
, dec2
)
347 yield from ALUHelpers
.get_int_o(res
, alu
, dec2
)
348 yield from ALUHelpers
.get_xer_so(res
, alu
, dec2
)
350 yield from ALUHelpers
.get_sim_int_o(sim_o
, sim
, dec2
)
351 yield from ALUHelpers
.get_wr_sim_cr_a(sim_o
, sim
, dec2
)
352 yield from ALUHelpers
.get_sim_xer_ov(sim_o
, sim
, dec2
)
353 yield from ALUHelpers
.get_sim_xer_so(sim_o
, sim
, dec2
)
355 ALUHelpers
.check_int_o(self
, res
, sim_o
, code
)
356 ALUHelpers
.check_xer_ov(self
, res
, sim_o
, code
)
357 ALUHelpers
.check_xer_so(self
, res
, sim_o
, code
)
358 ALUHelpers
.check_cr_a(self
, res
, sim_o
, "CR%d %s" % (cridx
, code
))
361 if __name__
== "__main__":
362 unittest
.main(exit
=False)
363 suite
= unittest
.TestSuite()
364 suite
.addTest(TestRunner(MulTestCase().test_data
))
366 runner
= unittest
.TextTestRunner()