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
18 def log_rand(n
, min_val
=1):
19 logrange
= random
.randint(1, n
)
20 return random
.randint(min_val
, (1 << logrange
)-1)
23 def get_cu_inputs(dec2
, sim
):
24 """naming (res) must conform to DivFunctionUnit input regspec
28 yield from ALUHelpers
.get_sim_int_ra(res
, sim
, dec2
) # RA
29 yield from ALUHelpers
.get_sim_int_rb(res
, sim
, dec2
) # RB
30 yield from ALUHelpers
.get_sim_xer_so(res
, sim
, dec2
) # XER.so
32 print("alu get_cu_inputs", res
)
37 def set_alu_inputs(alu
, dec2
, sim
):
38 # TODO: see https://bugs.libre-soc.org/show_bug.cgi?id=305#c43
39 # detect the immediate here (with m.If(self.i.ctx.op.imm_data.imm_ok))
40 # and place it into data_i.b
42 inp
= yield from get_cu_inputs(dec2
, sim
)
43 yield from ALUHelpers
.set_int_ra(alu
, dec2
, inp
)
44 yield from ALUHelpers
.set_int_rb(alu
, dec2
, inp
)
46 yield from ALUHelpers
.set_xer_so(alu
, dec2
, inp
)
49 # This test bench is a bit different than is usual. Initially when I
50 # was writing it, I had all of the tests call a function to create a
51 # device under test and simulator, initialize the dut, run the
52 # simulation for ~2 cycles, and assert that the dut output what it
53 # should have. However, this was really slow, since it needed to
54 # create and tear down the dut and simulator for every test case.
56 # Now, instead of doing that, every test case in DivTestCase puts some
57 # data into the test_data list below, describing the instructions to
58 # be tested and the initial state. Once all the tests have been run,
59 # test_data gets passed to TestRunner which then sets up the DUT and
60 # simulator once, runs all the data through it, and asserts that the
61 # results match the pseudocode sim at every cycle.
63 # By doing this, I've reduced the time it takes to run the test suite
64 # massively. Before, it took around 1 minute on my computer, now it
65 # takes around 3 seconds
68 class DivRunner(unittest
.TestCase
):
69 def __init__(self
, test_data
, div_pipe_kind
=None):
70 print ("DivRunner", test_data
, div_pipe_kind
)
71 super().__init
__("run_all")
72 self
.test_data
= test_data
73 self
.div_pipe_kind
= div_pipe_kind
75 def write_ilang(self
):
76 pspec
= DivPipeSpec(id_wid
=2, div_pipe_kind
=self
.div_pipe_kind
)
77 alu
= DivBasePipe(pspec
)
78 vl
= rtlil
.convert(alu
, ports
=alu
.ports())
79 with
open(f
"div_pipeline_{div_pipe_kind.name}.il", "w") as f
:
82 def test_write_ilang(self
):
83 self
.write_ilang(self
.div_pipe_kind
)
86 # *sigh* this is a mess. unit test gets added by code-walking
87 # (unittest module) and picked up with a test name.
88 # we don't want that: we want it explicitly called
89 # (see div test_pipe_caller.py) - don't know what to do,
90 # so "fix" it by adding default param and returning here
91 if self
.div_pipe_kind
is None:
96 instruction
= Signal(32)
98 pdecode
= create_pdecode()
100 m
.submodules
.pdecode2
= pdecode2
= PowerDecode2(pdecode
)
102 pspec
= DivPipeSpec(id_wid
=2, div_pipe_kind
=self
.div_pipe_kind
)
103 m
.submodules
.alu
= alu
= DivBasePipe(pspec
)
105 comb
+= alu
.p
.data_i
.ctx
.op
.eq_from_execute1(pdecode2
.e
)
106 comb
+= alu
.n
.ready_i
.eq(1)
107 comb
+= pdecode2
.dec
.raw_opcode_in
.eq(instruction
)
113 for test
in self
.test_data
:
116 with self
.subTest(test
.name
):
117 isa_sim
= ISA(pdecode2
, test
.regs
, test
.sprs
, test
.cr
,
120 gen
= prog
.generate_instructions()
121 instructions
= list(zip(gen
, prog
.assembly
.splitlines()))
124 index
= isa_sim
.pc
.CIA
.value
//4
125 while index
< len(instructions
):
126 ins
, code
= instructions
[index
]
128 print("instruction: 0x{:X}".format(ins
& 0xffffffff))
132 so
= 1 if spr
['XER'][XER_bits
['SO']] else 0
133 ov
= 1 if spr
['XER'][XER_bits
['OV']] else 0
134 ov32
= 1 if spr
['XER'][XER_bits
['OV32']] else 0
135 print("before: so/ov/32", so
, ov
, ov32
)
137 # ask the decoder to decode this binary data (endian'd)
139 yield pdecode2
.dec
.bigendian
.eq(bigendian
)
140 yield instruction
.eq(ins
) # raw binary instr.
142 fn_unit
= yield pdecode2
.e
.do
.fn_unit
143 self
.assertEqual(fn_unit
, Function
.DIV
.value
)
144 yield from set_alu_inputs(alu
, pdecode2
, isa_sim
)
146 # set valid for one cycle, propagate through pipeline..
147 # note that it is critically important to do this
148 # for DIV otherwise it starts trying to produce
150 yield alu
.p
.valid_i
.eq(1)
152 yield alu
.p
.valid_i
.eq(0)
154 opname
= code
.split(' ')[0]
155 yield from isa_sim
.call(opname
)
156 index
= isa_sim
.pc
.CIA
.value
//4
158 vld
= yield alu
.n
.valid_o
162 vld
= yield alu
.n
.valid_o
163 # bug #425 investigation
164 do
= alu
.pipe_end
.div_out
166 is_32bit
= yield ctx_op
.is_32bit
167 is_signed
= yield ctx_op
.is_signed
168 quotient_root
= yield do
.i
.core
.quotient_root
169 quotient_65
= yield do
.quotient_65
170 dive_abs_ov32
= yield do
.i
.dive_abs_ov32
171 div_by_zero
= yield do
.i
.div_by_zero
172 quotient_neg
= yield do
.quotient_neg
173 print("32bit", hex(is_32bit
))
174 print("signed", hex(is_signed
))
175 print("quotient_root", hex(quotient_root
))
176 print("quotient_65", hex(quotient_65
))
177 print("div_by_zero", hex(div_by_zero
))
178 print("dive_abs_ov32", hex(dive_abs_ov32
))
179 print("quotient_neg", hex(quotient_neg
))
184 # XXX sim._state is an internal variable
185 # and timeline does not exist
186 # AttributeError: '_SimulatorState' object
187 # has no attribute 'timeline'
188 # TODO: raise bugreport with whitequark
189 # requesting a public API to access this "officially"
190 # XXX print("time:", sim._state.timeline.now)
191 msg
= "%s: %s" % (self
.div_pipe_kind
.name
, code
)
192 msg
+= " %s" % (repr(prog
.assembly
))
193 msg
+= " %s" % (repr(test
.regs
))
194 yield from self
.check_alu_outputs(alu
, pdecode2
,
197 sim
.add_sync_process(process
)
198 with sim
.write_vcd(f
"div_simulator_{self.div_pipe_kind.name}.vcd"):
201 def check_alu_outputs(self
, alu
, dec2
, sim
, code
):
203 rc
= yield dec2
.e
.do
.rc
.data
204 cridx_ok
= yield dec2
.e
.write_cr
.ok
205 cridx
= yield dec2
.e
.write_cr
.data
207 print("check extra output", repr(code
), cridx_ok
, cridx
)
209 self
.assertEqual(cridx
, 0, code
)
214 yield from ALUHelpers
.get_cr_a(res
, alu
, dec2
)
215 yield from ALUHelpers
.get_xer_ov(res
, alu
, dec2
)
216 yield from ALUHelpers
.get_int_o(res
, alu
, dec2
)
217 yield from ALUHelpers
.get_xer_so(res
, alu
, dec2
)
219 print("res output", res
)
221 yield from ALUHelpers
.get_sim_int_o(sim_o
, sim
, dec2
)
222 yield from ALUHelpers
.get_wr_sim_cr_a(sim_o
, sim
, dec2
)
223 yield from ALUHelpers
.get_sim_xer_ov(sim_o
, sim
, dec2
)
224 yield from ALUHelpers
.get_sim_xer_so(sim_o
, sim
, dec2
)
226 print("sim output", sim_o
)
228 ALUHelpers
.check_int_o(self
, res
, sim_o
, code
)
229 ALUHelpers
.check_cr_a(self
, res
, sim_o
, "CR%d %s" % (cridx
, code
))
230 ALUHelpers
.check_xer_ov(self
, res
, sim_o
, code
)
231 ALUHelpers
.check_xer_so(self
, res
, sim_o
, code
)
233 oe
= yield dec2
.e
.do
.oe
.oe
234 oe_ok
= yield dec2
.e
.do
.oe
.ok
235 print("oe, oe_ok", oe
, oe_ok
)
236 if not oe
or not oe_ok
:
237 # if OE not enabled, XER SO and OV must not be activated
238 so_ok
= yield alu
.n
.data_o
.xer_so
.ok
239 ov_ok
= yield alu
.n
.data_o
.xer_ov
.ok
240 print("so, ov", so_ok
, ov_ok
)
241 self
.assertEqual(ov_ok
, False, code
)
242 self
.assertEqual(so_ok
, False, code
)