56d3fed6961abf07dc4c0774d28c733baebfc17c
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
, MicrOp
, 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
.mul
.pipeline
import MulBasePipe
17 from soc
.fu
.mul
.pipe_data
import MulPipeSpec
21 def get_cu_inputs(dec2
, sim
):
22 """naming (res) must conform to MulFunctionUnit input regspec
26 yield from ALUHelpers
.get_sim_int_ra(res
, sim
, dec2
) # RA
27 yield from ALUHelpers
.get_sim_int_rb(res
, sim
, dec2
) # RB
28 yield from ALUHelpers
.get_sim_xer_so(res
, sim
, dec2
) # XER.so
30 print ("alu get_cu_inputs", res
)
36 def set_alu_inputs(alu
, dec2
, sim
):
37 # TODO: see https://bugs.libre-soc.org/show_bug.cgi?id=305#c43
38 # detect the immediate here (with m.If(self.i.ctx.op.imm_data.imm_ok))
39 # and place it into data_i.b
41 inp
= yield from get_cu_inputs(dec2
, sim
)
42 print ("set alu inputs", inp
)
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 MulTestCase 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 MulTestCase(FHDLTestCase
):
71 def __init__(self
, name
):
72 super().__init
__(name
)
75 def run_tst_program(self
, prog
, initial_regs
=None, initial_sprs
=None):
76 tc
= TestCase(prog
, self
.test_name
, initial_regs
, initial_sprs
)
77 self
.test_data
.append(tc
)
79 def test_0_mullw(self
):
80 lst
= [f
"mullw 3, 1, 2"]
81 initial_regs
= [0] * 32
82 #initial_regs[1] = 0xffffffffffffffff
83 #initial_regs[2] = 0xffffffffffffffff
84 initial_regs
[1] = 0x2ffffffff
86 self
.run_tst_program(Program(lst
, bigendian
), initial_regs
)
88 def test_1_mullwo_(self
):
89 lst
= [f
"mullwo. 3, 1, 2"]
90 initial_regs
= [0] * 32
91 initial_regs
[1] = 0x3b34b06f
92 initial_regs
[2] = 0xfdeba998
93 self
.run_tst_program(Program(lst
, bigendian
), initial_regs
)
95 def test_2_mullwo(self
):
96 lst
= [f
"mullwo 3, 1, 2"]
97 initial_regs
= [0] * 32
98 initial_regs
[1] = 0xffffffffffffa988 # -5678
99 initial_regs
[2] = 0xffffffffffffedcc # -1234
100 self
.run_tst_program(Program(lst
, bigendian
), initial_regs
)
102 def test_3_mullw(self
):
103 lst
= ["mullw 3, 1, 2",
105 initial_regs
= [0] * 32
106 initial_regs
[1] = 0x6
107 initial_regs
[2] = 0xe
108 self
.run_tst_program(Program(lst
, bigendian
), initial_regs
)
110 def test_4_mullw_rand(self
):
112 lst
= ["mullw 3, 1, 2"]
113 initial_regs
= [0] * 32
114 initial_regs
[1] = random
.randint(0, (1<<64)-1)
115 initial_regs
[2] = random
.randint(0, (1<<64)-1)
116 self
.run_tst_program(Program(lst
, bigendian
), initial_regs
)
118 def test_4_mullw_nonrand(self
):
120 lst
= ["mullw 3, 1, 2"]
121 initial_regs
= [0] * 32
122 initial_regs
[1] = i
+1
123 initial_regs
[2] = i
+20
124 self
.run_tst_program(Program(lst
, bigendian
), initial_regs
)
126 def test_mulhw__regression_1(self
):
127 lst
= ["mulhw. 3, 1, 2"
129 initial_regs
= [0] * 32
130 initial_regs
[1] = 0x7745b36eca6646fa
131 initial_regs
[2] = 0x47dfba3a63834ba2
132 self
.run_tst_program(Program(lst
, bigendian
), initial_regs
)
134 def test_4_mullw_rand(self
):
136 lst
= ["mullw 3, 1, 2"]
137 initial_regs
= [0] * 32
138 initial_regs
[1] = random
.randint(0, (1<<64)-1)
139 initial_regs
[2] = random
.randint(0, (1<<64)-1)
140 self
.run_tst_program(Program(lst
, bigendian
), initial_regs
)
142 def test_rand_mul_lh(self
):
143 insns
= ["mulhw", "mulhw.", "mulhwu", "mulhwu."]
145 choice
= random
.choice(insns
)
146 lst
= [f
"{choice} 3, 1, 2"]
147 initial_regs
= [0] * 32
148 initial_regs
[1] = random
.randint(0, (1<<64)-1)
149 initial_regs
[2] = random
.randint(0, (1<<64)-1)
150 self
.run_tst_program(Program(lst
, bigendian
), initial_regs
)
152 def test_rand_mullw(self
):
153 insns
= ["mullw", "mullw.", "mullwo", "mullwo."]
155 choice
= random
.choice(insns
)
156 lst
= [f
"{choice} 3, 1, 2"]
157 initial_regs
= [0] * 32
158 initial_regs
[1] = random
.randint(0, (1<<64)-1)
159 initial_regs
[2] = random
.randint(0, (1<<64)-1)
160 self
.run_tst_program(Program(lst
, bigendian
), initial_regs
)
162 def test_rand_mulld(self
):
163 insns
= ["mulld", "mulld.", "mulldo", "mulldo."]
165 choice
= random
.choice(insns
)
166 lst
= [f
"{choice} 3, 1, 2"]
167 initial_regs
= [0] * 32
168 initial_regs
[1] = random
.randint(0, (1<<64)-1)
169 initial_regs
[2] = random
.randint(0, (1<<64)-1)
170 self
.run_tst_program(Program(lst
, bigendian
), initial_regs
)
172 def test_rand_mulhd(self
):
173 insns
= ["mulhd", "mulhd."]
175 choice
= random
.choice(insns
)
176 lst
= [f
"{choice} 3, 1, 2"]
177 initial_regs
= [0] * 32
178 initial_regs
[1] = random
.randint(0, (1<<64)-1)
179 initial_regs
[2] = random
.randint(0, (1<<64)-1)
180 self
.run_tst_program(Program(lst
, bigendian
), initial_regs
)
182 def test_ilang(self
):
183 pspec
= MulPipeSpec(id_wid
=2)
184 alu
= MulBasePipe(pspec
)
185 vl
= rtlil
.convert(alu
, ports
=alu
.ports())
186 with
open("mul_pipeline.il", "w") as f
:
190 class TestRunner(FHDLTestCase
):
191 def __init__(self
, test_data
):
192 super().__init
__("run_all")
193 self
.test_data
= test_data
198 instruction
= Signal(32)
200 pdecode
= create_pdecode()
202 m
.submodules
.pdecode2
= pdecode2
= PowerDecode2(pdecode
)
204 pspec
= MulPipeSpec(id_wid
=2)
205 m
.submodules
.alu
= alu
= MulBasePipe(pspec
)
207 comb
+= alu
.p
.data_i
.ctx
.op
.eq_from_execute1(pdecode2
.e
)
208 comb
+= alu
.n
.ready_i
.eq(1)
209 comb
+= pdecode2
.dec
.raw_opcode_in
.eq(instruction
)
214 for test
in self
.test_data
:
216 program
= test
.program
217 self
.subTest(test
.name
)
218 sim
= ISA(pdecode2
, test
.regs
, test
.sprs
, test
.cr
,
221 gen
= program
.generate_instructions()
222 instructions
= list(zip(gen
, program
.assembly
.splitlines()))
225 index
= sim
.pc
.CIA
.value
//4
226 while index
< len(instructions
):
227 ins
, code
= instructions
[index
]
229 print("instruction: 0x{:X}".format(ins
& 0xffffffff))
232 so
= 1 if sim
.spr
['XER'][XER_bits
['SO']] else 0
233 ov
= 1 if sim
.spr
['XER'][XER_bits
['OV']] else 0
234 ov32
= 1 if sim
.spr
['XER'][XER_bits
['OV32']] else 0
235 print ("before: so/ov/32", so
, ov
, ov32
)
237 # ask the decoder to decode this binary data (endian'd)
238 yield pdecode2
.dec
.bigendian
.eq(bigendian
) # little / big?
239 yield instruction
.eq(ins
) # raw binary instr.
241 fn_unit
= yield pdecode2
.e
.do
.fn_unit
242 self
.assertEqual(fn_unit
, Function
.MUL
.value
)
243 yield from set_alu_inputs(alu
, pdecode2
, sim
)
245 # set valid for one cycle, propagate through pipeline...
246 yield alu
.p
.valid_i
.eq(1)
248 yield alu
.p
.valid_i
.eq(0)
250 opname
= code
.split(' ')[0]
251 yield from sim
.call(opname
)
252 index
= sim
.pc
.CIA
.value
//4
254 # ...wait for valid to pop out the end
255 vld
= yield alu
.n
.valid_o
258 vld
= yield alu
.n
.valid_o
261 yield from self
.check_alu_outputs(alu
, pdecode2
, sim
, code
)
264 sim
.add_sync_process(process
)
265 with sim
.write_vcd("mul_simulator.vcd", "mul_simulator.gtkw",
269 def check_alu_outputs(self
, alu
, dec2
, sim
, code
):
271 rc
= yield dec2
.e
.do
.rc
.data
272 cridx_ok
= yield dec2
.e
.write_cr
.ok
273 cridx
= yield dec2
.e
.write_cr
.data
275 print ("check extra output", repr(code
), cridx_ok
, cridx
)
277 self
.assertEqual(cridx
, 0, code
)
279 oe
= yield dec2
.e
.do
.oe
.oe
280 oe_ok
= yield dec2
.e
.do
.oe
.ok
281 if not oe
or not oe_ok
:
282 # if OE not enabled, XER SO and OV must correspondingly be false
283 so_ok
= yield alu
.n
.data_o
.xer_so
.ok
284 ov_ok
= yield alu
.n
.data_o
.xer_ov
.ok
285 self
.assertEqual(so_ok
, False, code
)
286 self
.assertEqual(ov_ok
, False, code
)
291 yield from ALUHelpers
.get_cr_a(res
, alu
, dec2
)
292 yield from ALUHelpers
.get_xer_ov(res
, alu
, dec2
)
293 yield from ALUHelpers
.get_int_o(res
, alu
, dec2
)
294 yield from ALUHelpers
.get_xer_so(res
, alu
, dec2
)
296 yield from ALUHelpers
.get_sim_int_o(sim_o
, sim
, dec2
)
297 yield from ALUHelpers
.get_wr_sim_cr_a(sim_o
, sim
, dec2
)
298 yield from ALUHelpers
.get_sim_xer_ov(sim_o
, sim
, dec2
)
299 yield from ALUHelpers
.get_sim_xer_so(sim_o
, sim
, dec2
)
301 ALUHelpers
.check_int_o(self
, res
, sim_o
, code
)
302 ALUHelpers
.check_xer_ov(self
, res
, sim_o
, code
)
303 ALUHelpers
.check_xer_so(self
, res
, sim_o
, code
)
304 ALUHelpers
.check_cr_a(self
, res
, sim_o
, "CR%d %s" % (cridx
, code
))
307 if __name__
== "__main__":
308 unittest
.main(exit
=False)
309 suite
= unittest
.TestSuite()
310 suite
.addTest(TestRunner(MulTestCase
.test_data
))
312 runner
= unittest
.TextTestRunner()