1 from nmigen
import Module
, Signal
3 # NOTE: to use cxxsim, export NMIGEN_SIM_MODE=cxxsim from the shell
4 # Also, check out the cxxsim nmigen branch, and latest yosys from git
5 from nmutil
.sim_tmp_alternative
import Simulator
, Delay
, Settle
7 from nmigen
.cli
import rtlil
9 from soc
.decoder
.isa
.caller
import ISACaller
, special_sprs
10 from soc
.decoder
.power_decoder
import (create_pdecode
)
11 from soc
.decoder
.power_decoder2
import (PowerDecode2
)
12 from soc
.decoder
.power_enums
import (XER_bits
, Function
, MicrOp
, CryIn
)
13 from soc
.decoder
.selectable_int
import SelectableInt
14 from soc
.simulator
.program
import Program
15 from soc
.decoder
.isa
.all
import ISA
16 from soc
.config
.endian
import bigendian
18 from soc
.fu
.test
.common
import (TestAccumulatorBase
, TestCase
, ALUHelpers
)
19 from soc
.fu
.mul
.pipeline
import MulBasePipe
20 from soc
.fu
.mul
.pipe_data
import MulPipeSpec
24 def get_cu_inputs(dec2
, sim
):
25 """naming (res) must conform to MulFunctionUnit 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 set_alu_inputs(alu
, dec2
, sim
):
39 # TODO: see https://bugs.libre-soc.org/show_bug.cgi?id=305#c43
40 # detect the immediate here (with m.If(self.i.ctx.op.imm_data.imm_ok))
41 # and place it into data_i.b
43 inp
= yield from get_cu_inputs(dec2
, sim
)
44 print("set alu inputs", inp
)
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 MulTestCase 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 MulTestCase(TestAccumulatorBase
):
72 def case_0_mullw(self
):
73 lst
= [f
"mullw 3, 1, 2"]
74 initial_regs
= [0] * 32
75 #initial_regs[1] = 0xffffffffffffffff
76 #initial_regs[2] = 0xffffffffffffffff
77 initial_regs
[1] = 0x2ffffffff
79 self
.add_case(Program(lst
, bigendian
), initial_regs
)
81 def case_1_mullwo_(self
):
82 lst
= [f
"mullwo. 3, 1, 2"]
83 initial_regs
= [0] * 32
84 initial_regs
[1] = 0x3b34b06f
85 initial_regs
[2] = 0xfdeba998
86 self
.add_case(Program(lst
, bigendian
), initial_regs
)
88 def case_2_mullwo(self
):
89 lst
= [f
"mullwo 3, 1, 2"]
90 initial_regs
= [0] * 32
91 initial_regs
[1] = 0xffffffffffffa988 # -5678
92 initial_regs
[2] = 0xffffffffffffedcc # -1234
93 self
.add_case(Program(lst
, bigendian
), initial_regs
)
95 def case_3_mullw(self
):
96 lst
= ["mullw 3, 1, 2",
98 initial_regs
= [0] * 32
100 initial_regs
[2] = 0xe
101 self
.add_case(Program(lst
, bigendian
), initial_regs
)
103 def case_4_mullw_rand(self
):
105 lst
= ["mullw 3, 1, 2"]
106 initial_regs
= [0] * 32
107 initial_regs
[1] = random
.randint(0, (1 << 64)-1)
108 initial_regs
[2] = random
.randint(0, (1 << 64)-1)
109 self
.add_case(Program(lst
, bigendian
), initial_regs
)
111 def case_4_mullw_nonrand(self
):
113 lst
= ["mullw 3, 1, 2"]
114 initial_regs
= [0] * 32
115 initial_regs
[1] = i
+1
116 initial_regs
[2] = i
+20
117 self
.add_case(Program(lst
, bigendian
), initial_regs
)
119 def case_mulhw__regression_1(self
):
120 lst
= ["mulhw. 3, 1, 2"
122 initial_regs
= [0] * 32
123 initial_regs
[1] = 0x7745b36eca6646fa
124 initial_regs
[2] = 0x47dfba3a63834ba2
125 self
.add_case(Program(lst
, bigendian
), initial_regs
)
127 def case_rand_mul_lh(self
):
128 insns
= ["mulhw", "mulhw.", "mulhwu", "mulhwu."]
130 choice
= random
.choice(insns
)
131 lst
= [f
"{choice} 3, 1, 2"]
132 initial_regs
= [0] * 32
133 initial_regs
[1] = random
.randint(0, (1 << 64)-1)
134 initial_regs
[2] = random
.randint(0, (1 << 64)-1)
135 self
.add_case(Program(lst
, bigendian
), initial_regs
)
137 def case_rand_mullw(self
):
138 insns
= ["mullw", "mullw.", "mullwo", "mullwo."]
140 choice
= random
.choice(insns
)
141 lst
= [f
"{choice} 3, 1, 2"]
142 initial_regs
= [0] * 32
143 initial_regs
[1] = random
.randint(0, (1 << 64)-1)
144 initial_regs
[2] = random
.randint(0, (1 << 64)-1)
145 self
.add_case(Program(lst
, bigendian
), initial_regs
)
147 def case_rand_mulld(self
):
148 insns
= ["mulld", "mulld.", "mulldo", "mulldo."]
150 choice
= random
.choice(insns
)
151 lst
= [f
"{choice} 3, 1, 2"]
152 initial_regs
= [0] * 32
153 initial_regs
[1] = random
.randint(0, (1 << 64)-1)
154 initial_regs
[2] = random
.randint(0, (1 << 64)-1)
155 self
.add_case(Program(lst
, bigendian
), initial_regs
)
157 def case_rand_mulhd(self
):
158 insns
= ["mulhd", "mulhd."]
160 choice
= random
.choice(insns
)
161 lst
= [f
"{choice} 3, 1, 2"]
162 initial_regs
= [0] * 32
163 initial_regs
[1] = random
.randint(0, (1 << 64)-1)
164 initial_regs
[2] = random
.randint(0, (1 << 64)-1)
165 self
.add_case(Program(lst
, bigendian
), initial_regs
)
167 def case_0_mullhw_regression(self
):
168 lst
= [f
"mulhwu 3, 1, 2"]
169 initial_regs
= [0] * 32
170 initial_regs
[1] = 0x4000000000000000
171 initial_regs
[2] = 0x0000000000000002
172 self
.add_case(Program(lst
, bigendian
), initial_regs
)
174 # TODO add test case for these 3 operand cases (madd
175 # needs to be implemented)
176 # "maddhd","maddhdu","maddld"
179 class TestRunner(unittest
.TestCase
):
180 def __init__(self
, test_data
):
181 super().__init
__("run_all")
182 self
.test_data
= test_data
187 instruction
= Signal(32)
190 opkls
= MulPipeSpec
.opsubsetkls
192 m
.submodules
.pdecode2
= pdecode2
= PowerDecode2(None, opkls
, fn_name
)
193 pdecode
= pdecode2
.dec
195 pspec
= MulPipeSpec(id_wid
=2)
196 m
.submodules
.alu
= alu
= MulBasePipe(pspec
)
198 comb
+= alu
.p
.data_i
.ctx
.op
.eq_from_execute1(pdecode2
.do
)
199 comb
+= alu
.n
.ready_i
.eq(1)
200 comb
+= pdecode2
.dec
.raw_opcode_in
.eq(instruction
)
206 for test
in self
.test_data
:
208 program
= test
.program
209 self
.subTest(test
.name
)
210 sim
= ISA(pdecode2
, test
.regs
, test
.sprs
, test
.cr
,
213 gen
= program
.generate_instructions()
214 instructions
= list(zip(gen
, program
.assembly
.splitlines()))
217 index
= sim
.pc
.CIA
.value
//4
218 while index
< len(instructions
):
219 ins
, code
= instructions
[index
]
221 print("instruction: 0x{:X}".format(ins
& 0xffffffff))
224 so
= 1 if sim
.spr
['XER'][XER_bits
['SO']] else 0
225 ov
= 1 if sim
.spr
['XER'][XER_bits
['OV']] else 0
226 ov32
= 1 if sim
.spr
['XER'][XER_bits
['OV32']] else 0
227 print("before: so/ov/32", so
, ov
, ov32
)
229 # ask the decoder to decode this binary data (endian'd)
230 yield pdecode2
.dec
.bigendian
.eq(bigendian
) # little / big?
231 yield instruction
.eq(ins
) # raw binary instr.
233 fn_unit
= yield pdecode2
.e
.do
.fn_unit
234 self
.assertEqual(fn_unit
, Function
.MUL
.value
)
235 yield from set_alu_inputs(alu
, pdecode2
, sim
)
237 # set valid for one cycle, propagate through pipeline...
238 yield alu
.p
.valid_i
.eq(1)
240 yield alu
.p
.valid_i
.eq(0)
242 opname
= code
.split(' ')[0]
243 yield from sim
.call(opname
)
244 index
= sim
.pc
.CIA
.value
//4
246 # ...wait for valid to pop out the end
247 vld
= yield alu
.n
.valid_o
250 vld
= yield alu
.n
.valid_o
253 yield from self
.check_alu_outputs(alu
, pdecode2
, sim
, code
)
256 sim
.add_sync_process(process
)
257 with sim
.write_vcd("mul_simulator.vcd", "mul_simulator.gtkw",
261 def check_alu_outputs(self
, alu
, dec2
, sim
, code
):
263 rc
= yield dec2
.e
.do
.rc
.rc
264 cridx_ok
= yield dec2
.e
.write_cr
.ok
265 cridx
= yield dec2
.e
.write_cr
.data
267 print("check extra output", repr(code
), cridx_ok
, cridx
)
269 self
.assertEqual(cridx
, 0, code
)
271 oe
= yield dec2
.e
.do
.oe
.oe
272 oe_ok
= yield dec2
.e
.do
.oe
.ok
273 if not oe
or not oe_ok
:
274 # if OE not enabled, XER SO and OV must correspondingly be false
275 so_ok
= yield alu
.n
.data_o
.xer_so
.ok
276 ov_ok
= yield alu
.n
.data_o
.xer_ov
.ok
277 self
.assertEqual(so_ok
, False, code
)
278 self
.assertEqual(ov_ok
, False, code
)
283 yield from ALUHelpers
.get_cr_a(res
, alu
, dec2
)
284 yield from ALUHelpers
.get_xer_ov(res
, alu
, dec2
)
285 yield from ALUHelpers
.get_int_o(res
, alu
, dec2
)
286 yield from ALUHelpers
.get_xer_so(res
, alu
, dec2
)
288 yield from ALUHelpers
.get_sim_int_o(sim_o
, sim
, dec2
)
289 yield from ALUHelpers
.get_wr_sim_cr_a(sim_o
, sim
, dec2
)
290 yield from ALUHelpers
.get_sim_xer_ov(sim_o
, sim
, dec2
)
291 yield from ALUHelpers
.get_sim_xer_so(sim_o
, sim
, dec2
)
293 ALUHelpers
.check_int_o(self
, res
, sim_o
, code
)
294 ALUHelpers
.check_xer_ov(self
, res
, sim_o
, code
)
295 ALUHelpers
.check_xer_so(self
, res
, sim_o
, code
)
296 ALUHelpers
.check_cr_a(self
, res
, sim_o
, "CR%d %s" % (cridx
, code
))
299 if __name__
== "__main__":
300 unittest
.main(exit
=False)
301 suite
= unittest
.TestSuite()
302 suite
.addTest(TestRunner(MulTestCase().test_data
))
304 runner
= unittest
.TextTestRunner()