3 from nmigen
import Module
, Signal
4 from nmigen
.back
.pysim
import Simulator
, Delay
5 from nmigen
.cli
import rtlil
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
9 from soc
.simulator
.program
import Program
10 from soc
.decoder
.isa
.all
import ISA
11 from soc
.config
.endian
import bigendian
13 from soc
.fu
.test
.common
import ALUHelpers
14 from soc
.fu
.div
.pipeline
import DivBasePipe
15 from soc
.fu
.div
.pipe_data
import DivPipeSpec
, DivPipeKind
17 import power_instruction_analyzer
as pia
19 def log_rand(n
, min_val
=1):
20 logrange
= random
.randint(1, n
)
21 return random
.randint(min_val
, (1 << logrange
)-1)
24 def get_cu_inputs(dec2
, sim
):
25 """naming (res) must conform to DivFunctionUnit input regspec
29 yield from ALUHelpers
.get_sim_int_ra(res
, sim
, dec2
) # RA
30 yield from ALUHelpers
.get_sim_int_rb(res
, sim
, dec2
) # RB
31 yield from ALUHelpers
.get_sim_xer_so(res
, sim
, dec2
) # XER.so
33 print("alu get_cu_inputs", res
)
38 def pia_res_to_output(pia_res
):
40 if pia_res
.rt
is not None:
41 retval
["o"] = pia_res
.rt
42 if pia_res
.cr0
is not None:
54 if pia_res
.overflow
is not None:
55 overflow
= pia_res
.overflow
62 retval
["xer_so"] = overflow
.so
69 def set_alu_inputs(alu
, dec2
, sim
):
70 # TODO: see https://bugs.libre-soc.org/show_bug.cgi?id=305#c43
71 # detect the immediate here (with m.If(self.i.ctx.op.imm_data.imm_ok))
72 # and place it into data_i.b
74 inp
= yield from get_cu_inputs(dec2
, sim
)
75 yield from ALUHelpers
.set_int_ra(alu
, dec2
, inp
)
76 yield from ALUHelpers
.set_int_rb(alu
, dec2
, inp
)
78 yield from ALUHelpers
.set_xer_so(alu
, dec2
, inp
)
79 return pia
.InstructionInput(ra
=inp
["ra"], rb
=inp
["rb"], rc
=0)
82 # This test bench is a bit different than is usual. Initially when I
83 # was writing it, I had all of the tests call a function to create a
84 # device under test and simulator, initialize the dut, run the
85 # simulation for ~2 cycles, and assert that the dut output what it
86 # should have. However, this was really slow, since it needed to
87 # create and tear down the dut and simulator for every test case.
89 # Now, instead of doing that, every test case in DivTestCase puts some
90 # data into the test_data list below, describing the instructions to
91 # be tested and the initial state. Once all the tests have been run,
92 # test_data gets passed to TestRunner which then sets up the DUT and
93 # simulator once, runs all the data through it, and asserts that the
94 # results match the pseudocode sim at every cycle.
96 # By doing this, I've reduced the time it takes to run the test suite
97 # massively. Before, it took around 1 minute on my computer, now it
98 # takes around 3 seconds
101 class DivRunner(unittest
.TestCase
):
102 def __init__(self
, test_data
, div_pipe_kind
=None):
103 print ("DivRunner", test_data
, div_pipe_kind
)
104 super().__init
__("run_all")
105 self
.test_data
= test_data
106 self
.div_pipe_kind
= div_pipe_kind
108 def write_ilang(self
):
109 pspec
= DivPipeSpec(id_wid
=2, div_pipe_kind
=self
.div_pipe_kind
)
110 alu
= DivBasePipe(pspec
)
111 vl
= rtlil
.convert(alu
, ports
=alu
.ports())
112 with
open(f
"div_pipeline_{div_pipe_kind.name}.il", "w") as f
:
115 def test_write_ilang(self
):
116 self
.write_ilang(self
.div_pipe_kind
)
119 # *sigh* this is a mess. unit test gets added by code-walking
120 # (unittest module) and picked up with a test name.
121 # we don't want that: we want it explicitly called
122 # (see div test_pipe_caller.py) - don't know what to do,
123 # so "fix" it by adding default param and returning here
124 if self
.div_pipe_kind
is None:
129 instruction
= Signal(32)
131 pdecode
= create_pdecode()
133 m
.submodules
.pdecode2
= pdecode2
= PowerDecode2(pdecode
)
135 pspec
= DivPipeSpec(id_wid
=2, div_pipe_kind
=self
.div_pipe_kind
)
136 m
.submodules
.alu
= alu
= DivBasePipe(pspec
)
138 comb
+= alu
.p
.data_i
.ctx
.op
.eq_from_execute1(pdecode2
.e
)
139 comb
+= alu
.n
.ready_i
.eq(1)
140 comb
+= pdecode2
.dec
.raw_opcode_in
.eq(instruction
)
146 for test
in self
.test_data
:
149 with self
.subTest(test
.name
):
150 isa_sim
= ISA(pdecode2
, test
.regs
, test
.sprs
, test
.cr
,
153 gen
= prog
.generate_instructions()
154 instructions
= list(zip(gen
, prog
.assembly
.splitlines()))
157 index
= isa_sim
.pc
.CIA
.value
//4
158 while index
< len(instructions
):
159 ins
, code
= instructions
[index
]
161 print("instruction: 0x{:X}".format(ins
& 0xffffffff))
165 so
= 1 if spr
['XER'][XER_bits
['SO']] else 0
166 ov
= 1 if spr
['XER'][XER_bits
['OV']] else 0
167 ov32
= 1 if spr
['XER'][XER_bits
['OV32']] else 0
168 xer_zero
= not (so
or ov
or ov32
)
169 print("before: so/ov/32", so
, ov
, ov32
)
173 # ask the decoder to decode this binary data (endian'd)
175 yield pdecode2
.dec
.bigendian
.eq(bigendian
)
176 yield instruction
.eq(ins
) # raw binary instr.
178 fn_unit
= yield pdecode2
.e
.do
.fn_unit
179 self
.assertEqual(fn_unit
, Function
.DIV
.value
)
180 pia_inputs
= yield from set_alu_inputs(alu
, pdecode2
,
183 # set valid for one cycle, propagate through pipeline..
184 # note that it is critically important to do this
185 # for DIV otherwise it starts trying to produce
187 yield alu
.p
.valid_i
.eq(1)
189 yield alu
.p
.valid_i
.eq(0)
191 opname
= code
.split(' ')[0]
193 fnname
= opname
.replace(".", "_")
194 print(f
"{fnname}({pia_inputs})")
196 pia
, opname
.replace(".", "_"))(pia_inputs
)
197 print(f
"-> {pia_res}")
201 yield from isa_sim
.call(opname
)
202 index
= isa_sim
.pc
.CIA
.value
//4
204 vld
= yield alu
.n
.valid_o
208 vld
= yield alu
.n
.valid_o
209 # bug #425 investigation
210 do
= alu
.pipe_end
.div_out
212 is_32bit
= yield ctx_op
.is_32bit
213 is_signed
= yield ctx_op
.is_signed
214 quotient_root
= yield do
.i
.core
.quotient_root
215 quotient_65
= yield do
.quotient_65
216 dive_abs_ov32
= yield do
.i
.dive_abs_ov32
217 div_by_zero
= yield do
.i
.div_by_zero
218 quotient_neg
= yield do
.quotient_neg
219 print("32bit", hex(is_32bit
))
220 print("signed", hex(is_signed
))
221 print("quotient_root", hex(quotient_root
))
222 print("quotient_65", hex(quotient_65
))
223 print("div_by_zero", hex(div_by_zero
))
224 print("dive_abs_ov32", hex(dive_abs_ov32
))
225 print("quotient_neg", hex(quotient_neg
))
230 # XXX sim._state is an internal variable
231 # and timeline does not exist
232 # AttributeError: '_SimulatorState' object
233 # has no attribute 'timeline'
234 # TODO: raise bugreport with whitequark
235 # requesting a public API to access this "officially"
236 # XXX print("time:", sim._state.timeline.now)
237 msg
= "%s: %s" % (self
.div_pipe_kind
.name
, code
)
238 msg
+= " %s" % (repr(prog
.assembly
))
239 msg
+= " %s" % (repr(test
.regs
))
240 yield from self
.check_alu_outputs(alu
, pdecode2
,
244 sim
.add_sync_process(process
)
245 with sim
.write_vcd(f
"div_simulator_{self.div_pipe_kind.name}.vcd"):
248 def check_alu_outputs(self
, alu
, dec2
, sim
, code
, pia_res
):
250 rc
= yield dec2
.e
.do
.rc
.data
251 cridx_ok
= yield dec2
.e
.write_cr
.ok
252 cridx
= yield dec2
.e
.write_cr
.data
254 print("check extra output", repr(code
), cridx_ok
, cridx
)
256 self
.assertEqual(cridx
, 0, code
)
261 yield from ALUHelpers
.get_cr_a(res
, alu
, dec2
)
262 yield from ALUHelpers
.get_xer_ov(res
, alu
, dec2
)
263 yield from ALUHelpers
.get_int_o(res
, alu
, dec2
)
264 yield from ALUHelpers
.get_xer_so(res
, alu
, dec2
)
266 print("res output", res
)
268 yield from ALUHelpers
.get_sim_int_o(sim_o
, sim
, dec2
)
269 yield from ALUHelpers
.get_wr_sim_cr_a(sim_o
, sim
, dec2
)
270 yield from ALUHelpers
.get_sim_xer_ov(sim_o
, sim
, dec2
)
271 yield from ALUHelpers
.get_sim_xer_so(sim_o
, sim
, dec2
)
273 print("sim output", sim_o
)
275 print("power-instruction-analyzer result:")
277 if pia_res
is not None:
278 with self
.subTest(check
="pia", sim_o
=sim_o
, pia_res
=str(pia_res
)):
279 pia_o
= pia_res_to_output(pia_res
)
280 ALUHelpers
.check_int_o(self
, res
, pia_o
, code
)
281 ALUHelpers
.check_cr_a(self
, res
, pia_o
, code
)
282 ALUHelpers
.check_xer_ov(self
, res
, pia_o
, code
)
283 ALUHelpers
.check_xer_so(self
, res
, pia_o
, code
)
285 with self
.subTest(check
="sim", sim_o
=sim_o
, pia_res
=str(pia_res
)):
286 ALUHelpers
.check_int_o(self
, res
, sim_o
, code
)
287 ALUHelpers
.check_cr_a(self
, res
, sim_o
, code
)
288 ALUHelpers
.check_xer_ov(self
, res
, sim_o
, code
)
289 ALUHelpers
.check_xer_so(self
, res
, sim_o
, code
)
291 oe
= yield dec2
.e
.do
.oe
.oe
292 oe_ok
= yield dec2
.e
.do
.oe
.ok
293 print("oe, oe_ok", oe
, oe_ok
)
294 if not oe
or not oe_ok
:
295 # if OE not enabled, XER SO and OV must not be activated
296 so_ok
= yield alu
.n
.data_o
.xer_so
.ok
297 ov_ok
= yield alu
.n
.data_o
.xer_ov
.ok
298 print("so, ov", so_ok
, ov_ok
)
299 self
.assertEqual(ov_ok
, False, code
)
300 self
.assertEqual(so_ok
, False, code
)