65aa0801e0b17157092dcda32e84c54821b676b3
1 from nmigen
import Module
, Signal
2 from nmigen
.back
.pysim
import Simulator
, Delay
, Settle
3 from nmutil
.formaltest
import FHDLTestCase
4 from nmigen
.cli
import rtlil
6 from soc
.decoder
.isa
.caller
import ISACaller
, special_sprs
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
, InternalOp
, CryIn
)
10 from soc
.decoder
.selectable_int
import SelectableInt
11 from soc
.simulator
.program
import Program
12 from soc
.decoder
.isa
.all
import ISA
13 from soc
.config
.endian
import bigendian
15 from soc
.fu
.test
.common
import (TestCase
, ALUHelpers
)
16 from soc
.fu
.div
.pipeline
import DIVBasePipe
17 from soc
.fu
.div
.pipe_data
import DIVPipeSpec
20 def log_rand(n
, min_val
=1):
21 logrange
= random
.randint(1, n
)
22 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
)
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
)
51 # This test bench is a bit different than is usual. Initially when I
52 # was writing it, I had all of the tests call a function to create a
53 # device under test and simulator, initialize the dut, run the
54 # simulation for ~2 cycles, and assert that the dut output what it
55 # should have. However, this was really slow, since it needed to
56 # create and tear down the dut and simulator for every test case.
58 # Now, instead of doing that, every test case in DIVTestCase puts some
59 # data into the test_data list below, describing the instructions to
60 # be tested and the initial state. Once all the tests have been run,
61 # test_data gets passed to TestRunner which then sets up the DUT and
62 # simulator once, runs all the data through it, and asserts that the
63 # results match the pseudocode sim at every cycle.
65 # By doing this, I've reduced the time it takes to run the test suite
66 # massively. Before, it took around 1 minute on my computer, now it
67 # takes around 3 seconds
70 class DIVTestCase(FHDLTestCase
):
73 def __init__(self
, name
):
74 super().__init
__(name
)
77 def run_tst_program(self
, prog
, initial_regs
=None, initial_sprs
=None):
78 tc
= TestCase(prog
, self
.test_name
, initial_regs
, initial_sprs
)
79 self
.test_data
.append(tc
)
81 def tst_0_regression(self
):
83 lst
= ["divwo 3, 1, 2"]
84 initial_regs
= [0] * 32
85 initial_regs
[1] = 0xbc716835f32ac00c
86 initial_regs
[2] = 0xcdf69a7f7042db66
87 self
.run_tst_program(Program(lst
, bigendian
), initial_regs
)
89 def tst_1_regression(self
):
90 lst
= ["divwo 3, 1, 2"]
91 initial_regs
= [0] * 32
92 initial_regs
[1] = 0x10000000000000000-4
93 initial_regs
[2] = 0x10000000000000000-2
94 self
.run_tst_program(Program(lst
, bigendian
), initial_regs
)
96 def tst_2_regression(self
):
97 lst
= ["divwo 3, 1, 2"]
98 initial_regs
= [0] * 32
99 initial_regs
[1] = 0xffffffffffff9321
100 initial_regs
[2] = 0xffffffffffff7012
101 self
.run_tst_program(Program(lst
, bigendian
), initial_regs
)
103 def tst_3_regression(self
):
104 lst
= ["divwo. 3, 1, 2"]
105 initial_regs
= [0] * 32
106 initial_regs
[1] = 0x1b8e32f2458746af
107 initial_regs
[2] = 0x6b8aee2ccf7d62e9
108 self
.run_tst_program(Program(lst
, bigendian
), initial_regs
)
110 def tst_4_regression(self
):
111 lst
= ["divw 3, 1, 2"]
112 initial_regs
= [0] * 32
113 initial_regs
[1] = 0x1c4e6c2f3aa4a05c
114 initial_regs
[2] = 0xe730c2eed6cc8dd7
115 self
.run_tst_program(Program(lst
, bigendian
), initial_regs
)
117 def tst_5_regression(self
):
118 lst
= ["divw 3, 1, 2",
120 initial_regs
= [0] * 32
121 initial_regs
[1] = 0x1c4e6c2f3aa4a05c
122 initial_regs
[2] = 0xe730c2eed6cc8dd7
123 initial_regs
[4] = 0x1b8e32f2458746af
124 initial_regs
[5] = 0x6b8aee2ccf7d62e9
125 self
.run_tst_program(Program(lst
, bigendian
), initial_regs
)
127 def tst_6_regression(self
):
128 # CR0 not getting set properly for this one
129 # turns out that overflow is not set correctly in
130 # fu/div/output_stage.py calc_overflow
131 # https://bugs.libre-soc.org/show_bug.cgi?id=425
132 lst
= ["divw. 3, 1, 2"]
133 initial_regs
= [0] * 32
134 initial_regs
[1] = 0x61c1cc3b80f2a6af
135 initial_regs
[2] = 0x9dc66a7622c32bc0
136 self
.run_tst_program(Program(lst
, bigendian
), initial_regs
)
138 def test_7_regression(self
):
139 # https://bugs.libre-soc.org/show_bug.cgi?id=425
140 lst
= ["divw. 3, 1, 2"]
141 initial_regs
= [0] * 32
142 initial_regs
[1] = 0xf1791627e05e8096
143 initial_regs
[2] = 0xffc868bf4573da0b
144 self
.run_tst_program(Program(lst
, bigendian
), initial_regs
)
146 def tst_divw_by_zero_1(self
):
147 lst
= ["divw. 3, 1, 2"]
148 initial_regs
= [0] * 32
149 initial_regs
[1] = 0x1
150 initial_regs
[2] = 0x0
151 self
.run_tst_program(Program(lst
, bigendian
), initial_regs
)
153 def tst_divw_overflow2(self
):
154 lst
= ["divw. 3, 1, 2"]
155 initial_regs
= [0] * 32
156 initial_regs
[1] = 0x80000000
157 initial_regs
[2] = 0xffffffffffffffff # top bits don't seem to matter
158 self
.run_tst_program(Program(lst
, bigendian
), initial_regs
)
160 def tst_divw_overflow3(self
):
161 lst
= ["divw. 3, 1, 2"]
162 initial_regs
= [0] * 32
163 initial_regs
[1] = 0x80000000
164 initial_regs
[2] = 0xffffffff
165 self
.run_tst_program(Program(lst
, bigendian
), initial_regs
)
167 def tst_rand_divw(self
):
168 insns
= ["divw", "divw.", "divwo", "divwo."]
170 choice
= random
.choice(insns
)
171 lst
= [f
"{choice} 3, 1, 2"]
172 initial_regs
= [0] * 32
173 initial_regs
[1] = log_rand(32)
174 initial_regs
[2] = log_rand(32)
175 self
.run_tst_program(Program(lst
, bigendian
), initial_regs
)
177 def tst_divwuo_regression_1(self
):
178 lst
= ["divwuo. 3, 1, 2"]
179 initial_regs
= [0] * 32
180 initial_regs
[1] = 0x7591a398c4e32b68
181 initial_regs
[2] = 0x48674ab432867d69
182 self
.run_tst_program(Program(lst
, bigendian
), initial_regs
)
184 def tst_divwuo_1(self
):
185 lst
= ["divwuo. 3, 1, 2"]
186 initial_regs
= [0] * 32
187 initial_regs
[1] = 0x50
188 initial_regs
[2] = 0x2
189 self
.run_tst_program(Program(lst
, bigendian
), initial_regs
)
191 def tst_rand_divwu(self
):
192 insns
= ["divwu", "divwu.", "divwuo", "divwuo."]
194 choice
= random
.choice(insns
)
195 lst
= [f
"{choice} 3, 1, 2"]
196 initial_regs
= [0] * 32
197 initial_regs
[1] = log_rand(32)
198 initial_regs
[2] = log_rand(32)
199 self
.run_tst_program(Program(lst
, bigendian
), initial_regs
)
202 pspec
= DIVPipeSpec(id_wid
=2)
203 alu
= DIVBasePipe(pspec
)
204 vl
= rtlil
.convert(alu
, ports
=alu
.ports())
205 with
open("div_pipeline.il", "w") as f
:
209 class TestRunner(FHDLTestCase
):
210 def __init__(self
, test_data
):
211 super().__init
__("run_all")
212 self
.test_data
= test_data
217 instruction
= Signal(32)
219 pdecode
= create_pdecode()
221 m
.submodules
.pdecode2
= pdecode2
= PowerDecode2(pdecode
)
223 pspec
= DIVPipeSpec(id_wid
=2)
224 m
.submodules
.alu
= alu
= DIVBasePipe(pspec
)
226 comb
+= alu
.p
.data_i
.ctx
.op
.eq_from_execute1(pdecode2
.e
)
227 comb
+= alu
.n
.ready_i
.eq(1)
228 comb
+= pdecode2
.dec
.raw_opcode_in
.eq(instruction
)
233 for test
in self
.test_data
:
235 program
= test
.program
236 self
.subTest(test
.name
)
237 sim
= ISA(pdecode2
, test
.regs
, test
.sprs
, test
.cr
,
240 gen
= program
.generate_instructions()
241 instructions
= list(zip(gen
, program
.assembly
.splitlines()))
244 index
= sim
.pc
.CIA
.value
//4
245 while index
< len(instructions
):
246 ins
, code
= instructions
[index
]
248 print("instruction: 0x{:X}".format(ins
& 0xffffffff))
251 so
= 1 if sim
.spr
['XER'][XER_bits
['SO']] else 0
252 ov
= 1 if sim
.spr
['XER'][XER_bits
['OV']] else 0
253 ov32
= 1 if sim
.spr
['XER'][XER_bits
['OV32']] else 0
254 print ("before: so/ov/32", so
, ov
, ov32
)
256 # ask the decoder to decode this binary data (endian'd)
257 yield pdecode2
.dec
.bigendian
.eq(bigendian
) # little / big?
258 yield instruction
.eq(ins
) # raw binary instr.
260 fn_unit
= yield pdecode2
.e
.do
.fn_unit
261 self
.assertEqual(fn_unit
, Function
.DIV
.value
)
262 yield from set_alu_inputs(alu
, pdecode2
, sim
)
264 # set valid for one cycle, propagate through pipeline...
265 yield alu
.p
.valid_i
.eq(1)
267 yield alu
.p
.valid_i
.eq(0)
269 opname
= code
.split(' ')[0]
270 yield from sim
.call(opname
)
271 index
= sim
.pc
.CIA
.value
//4
273 vld
= yield alu
.n
.valid_o
276 vld
= yield alu
.n
.valid_o
277 # bug #425 investigation
278 do
= alu
.pipe_end
.div_out
280 is_32bit
= yield ctx_op
.is_32bit
281 is_signed
= yield ctx_op
.is_signed
282 quotient_root
= yield do
.i
.core
.quotient_root
283 quotient_65
= yield do
.quotient_65
284 dive_abs_ov32
= yield do
.i
.dive_abs_ov32
285 div_by_zero
= yield do
.i
.div_by_zero
286 quotient_neg
= yield do
.quotient_neg
287 print ("32bit", hex(is_32bit
))
288 print ("signed", hex(is_signed
))
289 print ("quotient_root", hex(quotient_root
))
290 print ("quotient_65", hex(quotient_65
))
291 print ("div_by_zero", hex(div_by_zero
))
292 print ("dive_abs_ov32", hex(dive_abs_ov32
))
293 print ("quotient_neg", hex(quotient_neg
))
297 yield from self
.check_alu_outputs(alu
, pdecode2
, sim
, code
)
300 sim
.add_sync_process(process
)
301 with sim
.write_vcd("div_simulator.vcd", "div_simulator.gtkw",
305 def check_alu_outputs(self
, alu
, dec2
, sim
, code
):
307 rc
= yield dec2
.e
.do
.rc
.data
308 cridx_ok
= yield dec2
.e
.write_cr
.ok
309 cridx
= yield dec2
.e
.write_cr
.data
311 print ("check extra output", repr(code
), cridx_ok
, cridx
)
313 self
.assertEqual(cridx
, 0, code
)
318 yield from ALUHelpers
.get_cr_a(res
, alu
, dec2
)
319 yield from ALUHelpers
.get_xer_ov(res
, alu
, dec2
)
320 yield from ALUHelpers
.get_int_o(res
, alu
, dec2
)
321 yield from ALUHelpers
.get_xer_so(res
, alu
, dec2
)
323 print ("res output", res
)
325 yield from ALUHelpers
.get_sim_int_o(sim_o
, sim
, dec2
)
326 yield from ALUHelpers
.get_wr_sim_cr_a(sim_o
, sim
, dec2
)
327 yield from ALUHelpers
.get_sim_xer_ov(sim_o
, sim
, dec2
)
328 yield from ALUHelpers
.get_sim_xer_so(sim_o
, sim
, dec2
)
330 print ("sim output", sim_o
)
332 ALUHelpers
.check_int_o(self
, res
, sim_o
, code
)
333 ALUHelpers
.check_cr_a(self
, res
, sim_o
, "CR%d %s" % (cridx
, code
))
334 ALUHelpers
.check_xer_ov(self
, res
, sim_o
, code
)
335 ALUHelpers
.check_xer_so(self
, res
, sim_o
, code
)
337 oe
= yield dec2
.e
.do
.oe
.oe
338 oe_ok
= yield dec2
.e
.do
.oe
.ok
339 print ("oe, oe_ok", oe
, oe_ok
)
340 if not oe
or not oe_ok
:
341 # if OE not enabled, XER SO and OV must not be activated
342 so_ok
= yield alu
.n
.data_o
.xer_so
.ok
343 ov_ok
= yield alu
.n
.data_o
.xer_ov
.ok
344 print ("so, ov", so_ok
, ov_ok
)
345 self
.assertEqual(ov_ok
, False, code
)
346 self
.assertEqual(so_ok
, False, code
)
349 if __name__
== "__main__":
350 unittest
.main(exit
=False)
351 suite
= unittest
.TestSuite()
352 suite
.addTest(TestRunner(DIVTestCase
.test_data
))
354 runner
= unittest
.TextTestRunner()