3 import power_instruction_analyzer
as pia
4 from nmigen
import Module
, Signal
5 from nmigen
.back
.pysim
import Simulator
, Delay
6 from nmigen
.cli
import rtlil
7 from soc
.decoder
.power_decoder
import (create_pdecode
)
8 from soc
.decoder
.power_decoder2
import (PowerDecode2
)
9 from soc
.decoder
.power_enums
import XER_bits
, Function
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 (TestCase
, ALUHelpers
)
15 from soc
.fu
.div
.pipeline
import DivBasePipe
16 from soc
.fu
.div
.pipe_data
import DivPipeSpec
, DivPipeKind
17 from typing
import List
20 def log_rand(n
, min_val
=1):
21 logrange
= random
.randint(1, n
)
22 return random
.randint(min_val
, (1 << logrange
)-1)
25 def get_cu_inputs(dec2
, sim
):
26 """naming (res) must conform to DivFunctionUnit input regspec
30 yield from ALUHelpers
.get_sim_int_ra(res
, sim
, dec2
) # RA
31 yield from ALUHelpers
.get_sim_int_rb(res
, sim
, dec2
) # RB
32 yield from ALUHelpers
.get_sim_xer_so(res
, sim
, dec2
) # XER.so
34 print("alu get_cu_inputs", res
)
39 def set_alu_inputs(alu
, dec2
, sim
):
40 # TODO: see https://bugs.libre-soc.org/show_bug.cgi?id=305#c43
41 # detect the immediate here (with m.If(self.i.ctx.op.imm_data.imm_ok))
42 # and place it into data_i.b
44 inp
= yield from get_cu_inputs(dec2
, sim
)
45 yield from ALUHelpers
.set_int_ra(alu
, dec2
, inp
)
46 yield from ALUHelpers
.set_int_rb(alu
, dec2
, inp
)
48 yield from ALUHelpers
.set_xer_so(alu
, dec2
, inp
)
49 return pia
.InstructionInput(ra
=inp
["ra"], rb
=inp
["rb"], rc
=0)
52 def pia_result_to_output(pia_result
: pia
.InstructionResult
):
54 if pia_result
.rt
is not None:
55 retval
["o"] = pia_result
.rt
56 if pia_result
.cr0
is not None:
57 cr0
: pia
.ConditionRegister
= pia_result
.cr0
68 if pia_result
.overflow
is not None:
69 overflow
: pia
.OverflowFlags
= pia_result
.overflow
76 retval
["xer_so"] = overflow
.so
83 # This test bench is a bit different than is usual. Initially when I
84 # was writing it, I had all of the tests call a function to create a
85 # device under test and simulator, initialize the dut, run the
86 # simulation for ~2 cycles, and assert that the dut output what it
87 # should have. However, this was really slow, since it needed to
88 # create and tear down the dut and simulator for every test case.
90 # Now, instead of doing that, every test case in DivTestCase puts some
91 # data into the test_data list below, describing the instructions to
92 # be tested and the initial state. Once all the tests have been run,
93 # test_data gets passed to TestRunner which then sets up the DUT and
94 # simulator once, runs all the data through it, and asserts that the
95 # results match the pseudocode sim at every cycle.
97 # By doing this, I've reduced the time it takes to run the test suite
98 # massively. Before, it took around 1 minute on my computer, now it
99 # takes around 3 seconds
103 test_data
: List
[TestCase
]
107 for n
, v
in self
.__class
__.__dict
__.items():
108 if n
.startswith("test") and callable(v
):
109 self
._current
_test
_name
= n
112 def run_test_program(self
, prog
, initial_regs
=None, initial_sprs
=None):
113 tc
= TestCase(prog
, self
._current
_test
_name
,
114 initial_regs
, initial_sprs
)
115 self
.test_data
.append(tc
)
117 def test_0_regression(self
):
119 lst
= ["divwo 3, 1, 2"]
120 initial_regs
= [0] * 32
121 initial_regs
[1] = 0xbc716835f32ac00c
122 initial_regs
[2] = 0xcdf69a7f7042db66
123 self
.run_test_program(Program(lst
, bigendian
), initial_regs
)
125 def test_1_regression(self
):
126 lst
= ["divwo 3, 1, 2"]
127 initial_regs
= [0] * 32
128 initial_regs
[1] = 0x10000000000000000-4
129 initial_regs
[2] = 0x10000000000000000-2
130 self
.run_test_program(Program(lst
, bigendian
), initial_regs
)
132 def test_2_regression(self
):
133 lst
= ["divwo 3, 1, 2"]
134 initial_regs
= [0] * 32
135 initial_regs
[1] = 0xffffffffffff9321
136 initial_regs
[2] = 0xffffffffffff7012
137 self
.run_test_program(Program(lst
, bigendian
), initial_regs
)
139 def test_3_regression(self
):
140 lst
= ["divwo. 3, 1, 2"]
141 initial_regs
= [0] * 32
142 initial_regs
[1] = 0x1b8e32f2458746af
143 initial_regs
[2] = 0x6b8aee2ccf7d62e9
144 self
.run_test_program(Program(lst
, bigendian
), initial_regs
)
146 def test_4_regression(self
):
147 lst
= ["divw 3, 1, 2"]
148 initial_regs
= [0] * 32
149 initial_regs
[1] = 0x1c4e6c2f3aa4a05c
150 initial_regs
[2] = 0xe730c2eed6cc8dd7
151 self
.run_test_program(Program(lst
, bigendian
), initial_regs
)
153 def test_5_regression(self
):
154 lst
= ["divw 3, 1, 2",
156 initial_regs
= [0] * 32
157 initial_regs
[1] = 0x1c4e6c2f3aa4a05c
158 initial_regs
[2] = 0xe730c2eed6cc8dd7
159 initial_regs
[4] = 0x1b8e32f2458746af
160 initial_regs
[5] = 0x6b8aee2ccf7d62e9
161 self
.run_test_program(Program(lst
, bigendian
), initial_regs
)
163 def test_6_regression(self
):
164 # CR0 not getting set properly for this one
165 # turns out that overflow is not set correctly in
166 # fu/div/output_stage.py calc_overflow
167 # https://bugs.libre-soc.org/show_bug.cgi?id=425
168 lst
= ["divw. 3, 1, 2"]
169 initial_regs
= [0] * 32
170 initial_regs
[1] = 0x61c1cc3b80f2a6af
171 initial_regs
[2] = 0x9dc66a7622c32bc0
172 self
.run_test_program(Program(lst
, bigendian
), initial_regs
)
174 def test_7_regression(self
):
175 # https://bugs.libre-soc.org/show_bug.cgi?id=425
176 lst
= ["divw. 3, 1, 2"]
177 initial_regs
= [0] * 32
178 initial_regs
[1] = 0xf1791627e05e8096
179 initial_regs
[2] = 0xffc868bf4573da0b
180 self
.run_test_program(Program(lst
, bigendian
), initial_regs
)
182 def test_divw_by_zero_1(self
):
183 lst
= ["divw. 3, 1, 2"]
184 initial_regs
= [0] * 32
185 initial_regs
[1] = 0x1
186 initial_regs
[2] = 0x0
187 self
.run_test_program(Program(lst
, bigendian
), initial_regs
)
189 def test_divw_overflow2(self
):
190 lst
= ["divw. 3, 1, 2"]
191 initial_regs
= [0] * 32
192 initial_regs
[1] = 0x80000000
193 initial_regs
[2] = 0xffffffffffffffff # top bits don't seem to matter
194 self
.run_test_program(Program(lst
, bigendian
), initial_regs
)
196 def test_divw_overflow3(self
):
197 lst
= ["divw. 3, 1, 2"]
198 initial_regs
= [0] * 32
199 initial_regs
[1] = 0x80000000
200 initial_regs
[2] = 0xffffffff
201 self
.run_test_program(Program(lst
, bigendian
), initial_regs
)
203 def test_divwuo_regression_1(self
):
204 lst
= ["divwuo. 3, 1, 2"]
205 initial_regs
= [0] * 32
206 initial_regs
[1] = 0x7591a398c4e32b68
207 initial_regs
[2] = 0x48674ab432867d69
208 self
.run_test_program(Program(lst
, bigendian
), initial_regs
)
210 def test_divwuo_1(self
):
211 lst
= ["divwuo. 3, 1, 2"]
212 initial_regs
= [0] * 32
213 initial_regs
[1] = 0x50
214 initial_regs
[2] = 0x2
215 self
.run_test_program(Program(lst
, bigendian
), initial_regs
)
219 for width
in ("w", "d"):
220 for sign
in ("", "u"):
222 for cnd
in ("", "."):
223 instrs
+= ["div" + width
+ sign
+ ov
+ cnd
,
224 "div" + width
+ "e" + sign
+ ov
+ cnd
]
225 for sign
in ("s", "u"):
226 instrs
+= ["mod" + sign
+ width
]
231 0xFFFF_FFFF_FFFF_FFFF,
232 0xFFFF_FFFF_FFFF_FFFE,
233 0x7FFF_FFFF_FFFF_FFFF,
234 0x8000_0000_0000_0000,
235 0x1234_5678_0000_0000,
236 0x1234_5678_8000_0000,
237 0x1234_5678_FFFF_FFFF,
238 0x1234_5678_7FFF_FFFF,
241 l
= [f
"{instr} 3, 1, 2"]
242 for ra
in test_values
:
243 for rb
in test_values
:
244 initial_regs
= [0] * 32
247 prog
= Program(l
, bigendian
)
248 self
.run_test_program(prog
, initial_regs
)
250 def test_rand_divwu(self
):
251 insns
= ["divwu", "divwu.", "divwuo", "divwuo."]
253 choice
= random
.choice(insns
)
254 lst
= [f
"{choice} 3, 1, 2"]
255 initial_regs
= [0] * 32
256 initial_regs
[1] = log_rand(32)
257 initial_regs
[2] = log_rand(32)
258 self
.run_test_program(Program(lst
, bigendian
), initial_regs
)
260 def test_rand_divw(self
):
261 insns
= ["divw", "divw.", "divwo", "divwo."]
263 choice
= random
.choice(insns
)
264 lst
= [f
"{choice} 3, 1, 2"]
265 initial_regs
= [0] * 32
266 initial_regs
[1] = log_rand(32)
267 initial_regs
[2] = log_rand(32)
268 self
.run_test_program(Program(lst
, bigendian
), initial_regs
)
271 class TestRunner(unittest
.TestCase
):
272 def write_ilang(self
, div_pipe_kind
):
273 pspec
= DivPipeSpec(id_wid
=2, div_pipe_kind
=div_pipe_kind
)
274 alu
= DivBasePipe(pspec
)
275 vl
= rtlil
.convert(alu
, ports
=alu
.ports())
276 with
open(f
"div_pipeline_{div_pipe_kind.name}.il", "w") as f
:
279 def test_write_ilang_div_pipe_core(self
):
280 self
.write_ilang(DivPipeKind
.DivPipeCore
)
282 def test_write_ilang_fsm_div_core(self
):
283 self
.write_ilang(DivPipeKind
.FSMDivCore
)
285 def test_write_ilang_sim_only(self
):
286 self
.write_ilang(DivPipeKind
.SimOnly
)
288 def run_all(self
, div_pipe_kind
):
289 test_data
= DivTestCases().test_data
292 instruction
= Signal(32)
294 pdecode
= create_pdecode()
296 m
.submodules
.pdecode2
= pdecode2
= PowerDecode2(pdecode
)
298 pspec
= DivPipeSpec(id_wid
=2, div_pipe_kind
=div_pipe_kind
)
299 m
.submodules
.alu
= alu
= DivBasePipe(pspec
)
301 comb
+= alu
.p
.data_i
.ctx
.op
.eq_from_execute1(pdecode2
.e
)
302 comb
+= alu
.n
.ready_i
.eq(1)
303 comb
+= pdecode2
.dec
.raw_opcode_in
.eq(instruction
)
309 for test
in test_data
:
311 program
= test
.program
312 with self
.subTest(test
.name
):
313 isa_sim
= ISA(pdecode2
, test
.regs
, test
.sprs
, test
.cr
,
316 gen
= program
.generate_instructions()
318 zip(gen
, program
.assembly
.splitlines()))
321 index
= isa_sim
.pc
.CIA
.value
// 4
322 while index
< len(instructions
):
323 ins
, code
= instructions
[index
]
325 print("instruction: 0x{:X}".format(ins
& 0xffffffff))
327 if 'XER' in isa_sim
.spr
:
328 so
= 1 if isa_sim
.spr
['XER'][XER_bits
['SO']] else 0
329 ov
= 1 if isa_sim
.spr
['XER'][XER_bits
['OV']] else 0
330 ov32
= 1 if isa_sim
.spr
['XER'][XER_bits
['OV32']] else 0
331 xer_zero
= not (so
or ov
or ov32
)
332 print("before: so/ov/32", so
, ov
, ov32
)
336 # ask the decoder to decode this binary data (endian'd)
338 yield pdecode2
.dec
.bigendian
.eq(bigendian
)
339 yield instruction
.eq(ins
) # raw binary instr.
341 fn_unit
= yield pdecode2
.e
.do
.fn_unit
342 self
.assertEqual(fn_unit
, Function
.DIV
.value
)
343 pia_inputs
= yield from set_alu_inputs(alu
, pdecode2
, isa_sim
)
345 # set valid for one cycle, propagate through pipeline...
346 yield alu
.p
.valid_i
.eq(1)
348 yield alu
.p
.valid_i
.eq(0)
350 opname
= code
.split(' ')[0]
352 fnname
= opname
.replace(".", "_")
353 print(f
"{fnname}({pia_inputs})")
354 pia_result
= getattr(
355 pia
, opname
.replace(".", "_"))(pia_inputs
)
356 print(f
"-> {pia_result}")
359 yield from isa_sim
.call(opname
)
360 index
= isa_sim
.pc
.CIA
.value
//4
362 vld
= yield alu
.n
.valid_o
366 vld
= yield alu
.n
.valid_o
367 # bug #425 investigation
368 do
= alu
.pipe_end
.div_out
370 is_32bit
= yield ctx_op
.is_32bit
371 is_signed
= yield ctx_op
.is_signed
372 quotient_root
= yield do
.i
.core
.quotient_root
373 quotient_65
= yield do
.quotient_65
374 dive_abs_ov32
= yield do
.i
.dive_abs_ov32
375 div_by_zero
= yield do
.i
.div_by_zero
376 quotient_neg
= yield do
.quotient_neg
377 print("32bit", hex(is_32bit
))
378 print("signed", hex(is_signed
))
379 print("quotient_root", hex(quotient_root
))
380 print("quotient_65", hex(quotient_65
))
381 print("div_by_zero", hex(div_by_zero
))
382 print("dive_abs_ov32", hex(dive_abs_ov32
))
383 print("quotient_neg", hex(quotient_neg
))
388 print("time:", sim
._state
.timeline
.now
)
389 yield from self
.check_alu_outputs(alu
, pdecode2
, isa_sim
, code
, pia_result
)
391 sim
.add_sync_process(process
)
392 with sim
.write_vcd(f
"div_simulator_{div_pipe_kind.name}.vcd",
393 f
"div_simulator_{div_pipe_kind.name}.gtkw",
397 def check_alu_outputs(self
, alu
, dec2
, sim
, code
, pia_result
):
399 rc
= yield dec2
.e
.do
.rc
.data
400 cridx_ok
= yield dec2
.e
.write_cr
.ok
401 cridx
= yield dec2
.e
.write_cr
.data
403 print("check extra output", repr(code
), cridx_ok
, cridx
)
405 self
.assertEqual(cridx
, 0, code
)
410 yield from ALUHelpers
.get_cr_a(res
, alu
, dec2
)
411 yield from ALUHelpers
.get_xer_ov(res
, alu
, dec2
)
412 yield from ALUHelpers
.get_int_o(res
, alu
, dec2
)
413 yield from ALUHelpers
.get_xer_so(res
, alu
, dec2
)
415 print("res output", res
)
417 yield from ALUHelpers
.get_sim_int_o(sim_o
, sim
, dec2
)
418 yield from ALUHelpers
.get_wr_sim_cr_a(sim_o
, sim
, dec2
)
419 yield from ALUHelpers
.get_sim_xer_ov(sim_o
, sim
, dec2
)
420 yield from ALUHelpers
.get_sim_xer_so(sim_o
, sim
, dec2
)
422 print("sim output", sim_o
)
424 print("power-instruction-analyzer result:")
426 if pia_result
is not None:
427 with self
.subTest(check
="pia", sim_o
=sim_o
, pia_result
=str(pia_result
)):
428 pia_o
= pia_result_to_output(pia_result
)
430 ALUHelpers
.check_int_o(self
, res
, pia_o
, code
)
431 ALUHelpers
.check_cr_a(self
, res
, pia_o
,
432 "CR%d %s" % (cridx
, code
))
433 ALUHelpers
.check_xer_ov(self
, res
, pia_o
, code
)
434 ALUHelpers
.check_xer_so(self
, res
, pia_o
, code
)
436 with self
.subTest(check
="sim", sim_o
=sim_o
, pia_result
=str(pia_result
)):
437 ALUHelpers
.check_int_o(self
, res
, sim_o
, code
)
438 ALUHelpers
.check_cr_a(self
, res
, sim_o
, "CR%d %s" % (cridx
, code
))
439 ALUHelpers
.check_xer_ov(self
, res
, sim_o
, code
)
440 ALUHelpers
.check_xer_so(self
, res
, sim_o
, code
)
442 oe
= yield dec2
.e
.do
.oe
.oe
443 oe_ok
= yield dec2
.e
.do
.oe
.ok
444 print("oe, oe_ok", oe
, oe_ok
)
445 if not oe
or not oe_ok
:
446 # if OE not enabled, XER SO and OV must not be activated
447 so_ok
= yield alu
.n
.data_o
.xer_so
.ok
448 ov_ok
= yield alu
.n
.data_o
.xer_ov
.ok
449 print("so, ov", so_ok
, ov_ok
)
450 self
.assertEqual(ov_ok
, False, code
)
451 self
.assertEqual(so_ok
, False, code
)
453 def test_run_div_pipe_core(self
):
454 self
.run_all(DivPipeKind
.DivPipeCore
)
456 def test_run_fsm_div_core(self
):
457 self
.run_all(DivPipeKind
.FSMDivCore
)
459 def test_run_sim_only(self
):
460 self
.run_all(DivPipeKind
.SimOnly
)
463 if __name__
== "__main__":