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"
178 def case_ilang(self
):
179 pspec
= MulPipeSpec(id_wid
=2)
180 alu
= MulBasePipe(pspec
)
181 vl
= rtlil
.convert(alu
, ports
=alu
.ports())
182 with
open("mul_pipeline.il", "w") as f
:
186 class TestRunner(unittest
.TestCase
):
187 def __init__(self
, test_data
):
188 super().__init
__("run_all")
189 self
.test_data
= test_data
194 instruction
= Signal(32)
197 opkls
= MulPipeSpec
.opsubsetkls
199 m
.submodules
.pdecode2
= pdecode2
= PowerDecode2(None, opkls
, fn_name
)
200 pdecode
= pdecode2
.dec
202 pspec
= MulPipeSpec(id_wid
=2)
203 m
.submodules
.alu
= alu
= MulBasePipe(pspec
)
205 comb
+= alu
.p
.data_i
.ctx
.op
.eq_from_execute1(pdecode2
.do
)
206 comb
+= alu
.n
.ready_i
.eq(1)
207 comb
+= pdecode2
.dec
.raw_opcode_in
.eq(instruction
)
213 for test
in self
.test_data
:
215 program
= test
.program
216 self
.subTest(test
.name
)
217 sim
= ISA(pdecode2
, test
.regs
, test
.sprs
, test
.cr
,
220 gen
= program
.generate_instructions()
221 instructions
= list(zip(gen
, program
.assembly
.splitlines()))
224 index
= sim
.pc
.CIA
.value
//4
225 while index
< len(instructions
):
226 ins
, code
= instructions
[index
]
228 print("instruction: 0x{:X}".format(ins
& 0xffffffff))
231 so
= 1 if sim
.spr
['XER'][XER_bits
['SO']] else 0
232 ov
= 1 if sim
.spr
['XER'][XER_bits
['OV']] else 0
233 ov32
= 1 if sim
.spr
['XER'][XER_bits
['OV32']] else 0
234 print("before: so/ov/32", so
, ov
, ov32
)
236 # ask the decoder to decode this binary data (endian'd)
237 yield pdecode2
.dec
.bigendian
.eq(bigendian
) # little / big?
238 yield instruction
.eq(ins
) # raw binary instr.
240 fn_unit
= yield pdecode2
.e
.do
.fn_unit
241 self
.assertEqual(fn_unit
, Function
.MUL
.value
)
242 yield from set_alu_inputs(alu
, pdecode2
, sim
)
244 # set valid for one cycle, propagate through pipeline...
245 yield alu
.p
.valid_i
.eq(1)
247 yield alu
.p
.valid_i
.eq(0)
249 opname
= code
.split(' ')[0]
250 yield from sim
.call(opname
)
251 index
= sim
.pc
.CIA
.value
//4
253 # ...wait for valid to pop out the end
254 vld
= yield alu
.n
.valid_o
257 vld
= yield alu
.n
.valid_o
260 yield from self
.check_alu_outputs(alu
, pdecode2
, sim
, code
)
263 sim
.add_sync_process(process
)
264 with sim
.write_vcd("mul_simulator.vcd", "mul_simulator.gtkw",
268 def check_alu_outputs(self
, alu
, dec2
, sim
, code
):
270 rc
= yield dec2
.e
.do
.rc
.rc
271 cridx_ok
= yield dec2
.e
.write_cr
.ok
272 cridx
= yield dec2
.e
.write_cr
.data
274 print("check extra output", repr(code
), cridx_ok
, cridx
)
276 self
.assertEqual(cridx
, 0, code
)
278 oe
= yield dec2
.e
.do
.oe
.oe
279 oe_ok
= yield dec2
.e
.do
.oe
.ok
280 if not oe
or not oe_ok
:
281 # if OE not enabled, XER SO and OV must correspondingly be false
282 so_ok
= yield alu
.n
.data_o
.xer_so
.ok
283 ov_ok
= yield alu
.n
.data_o
.xer_ov
.ok
284 self
.assertEqual(so_ok
, False, code
)
285 self
.assertEqual(ov_ok
, False, code
)
290 yield from ALUHelpers
.get_cr_a(res
, alu
, dec2
)
291 yield from ALUHelpers
.get_xer_ov(res
, alu
, dec2
)
292 yield from ALUHelpers
.get_int_o(res
, alu
, dec2
)
293 yield from ALUHelpers
.get_xer_so(res
, alu
, dec2
)
295 yield from ALUHelpers
.get_sim_int_o(sim_o
, sim
, dec2
)
296 yield from ALUHelpers
.get_wr_sim_cr_a(sim_o
, sim
, dec2
)
297 yield from ALUHelpers
.get_sim_xer_ov(sim_o
, sim
, dec2
)
298 yield from ALUHelpers
.get_sim_xer_so(sim_o
, sim
, dec2
)
300 ALUHelpers
.check_int_o(self
, res
, sim_o
, code
)
301 ALUHelpers
.check_xer_ov(self
, res
, sim_o
, code
)
302 ALUHelpers
.check_xer_so(self
, res
, sim_o
, code
)
303 ALUHelpers
.check_cr_a(self
, res
, sim_o
, "CR%d %s" % (cridx
, code
))
306 if __name__
== "__main__":
307 unittest
.main(exit
=False)
308 suite
= unittest
.TestSuite()
309 suite
.addTest(TestRunner(MulTestCase().test_data
))
311 runner
= unittest
.TextTestRunner()