a09e6bad9b97b0d4df1770c9b4ea40a0cbf55b08
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
)
35 def set_alu_inputs(alu
, dec2
, sim
):
36 # TODO: see https://bugs.libre-soc.org/show_bug.cgi?id=305#c43
37 # detect the immediate here (with m.If(self.i.ctx.op.imm_data.imm_ok))
38 # and place it into data_i.b
40 inp
= yield from get_cu_inputs(dec2
, sim
)
41 print("set alu inputs", inp
)
42 yield from ALUHelpers
.set_int_ra(alu
, dec2
, inp
)
43 yield from ALUHelpers
.set_int_rb(alu
, dec2
, inp
)
45 yield from ALUHelpers
.set_xer_so(alu
, dec2
, inp
)
48 # This test bench is a bit different than is usual. Initially when I
49 # was writing it, I had all of the tests call a function to create a
50 # device under test and simulator, initialize the dut, run the
51 # simulation for ~2 cycles, and assert that the dut output what it
52 # should have. However, this was really slow, since it needed to
53 # create and tear down the dut and simulator for every test case.
55 # Now, instead of doing that, every test case in MulTestCase puts some
56 # data into the test_data list below, describing the instructions to
57 # be tested and the initial state. Once all the tests have been run,
58 # test_data gets passed to TestRunner which then sets up the DUT and
59 # simulator once, runs all the data through it, and asserts that the
60 # results match the pseudocode sim at every cycle.
62 # By doing this, I've reduced the time it takes to run the test suite
63 # massively. Before, it took around 1 minute on my computer, now it
64 # takes around 3 seconds
67 class MulTestCase(FHDLTestCase
):
70 def __init__(self
, name
):
71 super().__init
__(name
)
74 def run_tst_program(self
, prog
, initial_regs
=None, initial_sprs
=None):
75 tc
= TestCase(prog
, self
.test_name
, initial_regs
, initial_sprs
)
76 self
.test_data
.append(tc
)
78 def test_0_mullw(self
):
79 lst
= [f
"mullw 3, 1, 2"]
80 initial_regs
= [0] * 32
81 #initial_regs[1] = 0xffffffffffffffff
82 #initial_regs[2] = 0xffffffffffffffff
83 initial_regs
[1] = 0x2ffffffff
85 self
.run_tst_program(Program(lst
, bigendian
), initial_regs
)
87 def test_1_mullwo_(self
):
88 lst
= [f
"mullwo. 3, 1, 2"]
89 initial_regs
= [0] * 32
90 initial_regs
[1] = 0x3b34b06f
91 initial_regs
[2] = 0xfdeba998
92 self
.run_tst_program(Program(lst
, bigendian
), initial_regs
)
94 def test_2_mullwo(self
):
95 lst
= [f
"mullwo 3, 1, 2"]
96 initial_regs
= [0] * 32
97 initial_regs
[1] = 0xffffffffffffa988 # -5678
98 initial_regs
[2] = 0xffffffffffffedcc # -1234
99 self
.run_tst_program(Program(lst
, bigendian
), initial_regs
)
101 def test_3_mullw(self
):
102 lst
= ["mullw 3, 1, 2",
104 initial_regs
= [0] * 32
105 initial_regs
[1] = 0x6
106 initial_regs
[2] = 0xe
107 self
.run_tst_program(Program(lst
, bigendian
), initial_regs
)
109 def test_4_mullw_rand(self
):
111 lst
= ["mullw 3, 1, 2"]
112 initial_regs
= [0] * 32
113 initial_regs
[1] = random
.randint(0, (1 << 64)-1)
114 initial_regs
[2] = random
.randint(0, (1 << 64)-1)
115 self
.run_tst_program(Program(lst
, bigendian
), initial_regs
)
117 def test_4_mullw_nonrand(self
):
119 lst
= ["mullw 3, 1, 2"]
120 initial_regs
= [0] * 32
121 initial_regs
[1] = i
+1
122 initial_regs
[2] = i
+20
123 self
.run_tst_program(Program(lst
, bigendian
), initial_regs
)
125 def test_mulhw__regression_1(self
):
126 lst
= ["mulhw. 3, 1, 2"
128 initial_regs
= [0] * 32
129 initial_regs
[1] = 0x7745b36eca6646fa
130 initial_regs
[2] = 0x47dfba3a63834ba2
131 self
.run_tst_program(Program(lst
, bigendian
), initial_regs
)
133 def test_4_mullw_rand(self
):
135 lst
= ["mullw 3, 1, 2"]
136 initial_regs
= [0] * 32
137 initial_regs
[1] = random
.randint(0, (1 << 64)-1)
138 initial_regs
[2] = random
.randint(0, (1 << 64)-1)
139 self
.run_tst_program(Program(lst
, bigendian
), initial_regs
)
141 def test_rand_mul_lh(self
):
142 insns
= ["mulhw", "mulhw.", "mulhwu", "mulhwu."]
144 choice
= random
.choice(insns
)
145 lst
= [f
"{choice} 3, 1, 2"]
146 initial_regs
= [0] * 32
147 initial_regs
[1] = random
.randint(0, (1 << 64)-1)
148 initial_regs
[2] = random
.randint(0, (1 << 64)-1)
149 self
.run_tst_program(Program(lst
, bigendian
), initial_regs
)
151 def test_rand_mullw(self
):
152 insns
= ["mullw", "mullw.", "mullwo", "mullwo."]
154 choice
= random
.choice(insns
)
155 lst
= [f
"{choice} 3, 1, 2"]
156 initial_regs
= [0] * 32
157 initial_regs
[1] = random
.randint(0, (1 << 64)-1)
158 initial_regs
[2] = random
.randint(0, (1 << 64)-1)
159 self
.run_tst_program(Program(lst
, bigendian
), initial_regs
)
161 def test_rand_mulld(self
):
162 insns
= ["mulld", "mulld.", "mulldo", "mulldo."]
164 choice
= random
.choice(insns
)
165 lst
= [f
"{choice} 3, 1, 2"]
166 initial_regs
= [0] * 32
167 initial_regs
[1] = random
.randint(0, (1 << 64)-1)
168 initial_regs
[2] = random
.randint(0, (1 << 64)-1)
169 self
.run_tst_program(Program(lst
, bigendian
), initial_regs
)
171 def test_rand_mulhd(self
):
172 insns
= ["mulhd", "mulhd."]
174 choice
= random
.choice(insns
)
175 lst
= [f
"{choice} 3, 1, 2"]
176 initial_regs
= [0] * 32
177 initial_regs
[1] = random
.randint(0, (1 << 64)-1)
178 initial_regs
[2] = random
.randint(0, (1 << 64)-1)
179 self
.run_tst_program(Program(lst
, bigendian
), initial_regs
)
181 def test_ilang(self
):
182 pspec
= MulPipeSpec(id_wid
=2)
183 alu
= MulBasePipe(pspec
)
184 vl
= rtlil
.convert(alu
, ports
=alu
.ports())
185 with
open("mul_pipeline.il", "w") as f
:
189 class TestRunner(FHDLTestCase
):
190 def __init__(self
, test_data
):
191 super().__init
__("run_all")
192 self
.test_data
= test_data
197 instruction
= Signal(32)
199 pdecode
= create_pdecode()
201 m
.submodules
.pdecode2
= pdecode2
= PowerDecode2(pdecode
)
203 pspec
= MulPipeSpec(id_wid
=2)
204 m
.submodules
.alu
= alu
= MulBasePipe(pspec
)
206 comb
+= alu
.p
.data_i
.ctx
.op
.eq_from_execute1(pdecode2
.e
)
207 comb
+= alu
.n
.ready_i
.eq(1)
208 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()