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
, Settle
7 from nmigen
.cli
import rtlil
9 from openpower
.decoder
.isa
.caller
import ISACaller
, special_sprs
10 from openpower
.decoder
.power_decoder
import (create_pdecode
)
11 from openpower
.decoder
.power_decoder2
import (PowerDecode2
)
12 from openpower
.decoder
.power_enums
import (XER_bits
, Function
)
13 from openpower
.decoder
.selectable_int
import SelectableInt
14 from openpower
.simulator
.program
import Program
15 from openpower
.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
.test
.common
import mask_extend
20 from soc
.fu
.cr
.pipeline
import CRBasePipe
21 from soc
.fu
.cr
.pipe_data
import CRPipeSpec
25 # This test bench is a bit different than is usual. Initially when I
26 # was writing it, I had all of the tests call a function to create a
27 # device under test and simulator, initialize the dut, run the
28 # simulation for ~2 cycles, and assert that the dut output what it
29 # should have. However, this was really slow, since it needed to
30 # create and tear down the dut and simulator for every test case.
32 # Now, instead of doing that, every test case in ALUTestCase puts some
33 # data into the test_data list below, describing the instructions to
34 # be tested and the initial state. Once all the tests have been run,
35 # test_data gets passed to TestRunner which then sets up the DUT and
36 # simulator once, runs all the data through it, and asserts that the
37 # results match the pseudocode sim at every cycle.
39 # By doing this, I've reduced the time it takes to run the test suite
40 # massively. Before, it took around 1 minute on my computer, now it
41 # takes around 3 seconds
44 class CRTestCase(TestAccumulatorBase
):
47 insns
= ["crand", "cror", "crnand", "crnor", "crxor", "creqv",
50 choice
= random
.choice(insns
)
51 ba
= random
.randint(0, 31)
52 bb
= random
.randint(0, 31)
53 bt
= random
.randint(0, 31)
54 lst
= [f
"{choice} {ba}, {bb}, {bt}"]
55 cr
= random
.randint(0, (1 << 32)-1)
56 self
.add_case(Program(lst
, bigendian
), initial_cr
=cr
)
60 lst
= ["crand 0, 11, 13"]
61 cr
= random
.randint(0, (1 << 32)-1)
62 self
.add_case(Program(lst
, bigendian
), initial_cr
=cr
)
64 def case_1_mcrf(self
):
66 src
= random
.randint(0, 7)
67 dst
= random
.randint(0, 7)
68 lst
= [f
"mcrf {src}, {dst}"]
69 cr
= random
.randint(0, (1 << 32)-1)
70 self
.add_case(Program(lst
, bigendian
), initial_cr
=cr
)
72 def case_0_mcrf(self
):
74 lst
= [f
"mcrf 5, {i}"]
76 self
.add_case(Program(lst
, bigendian
), initial_cr
=cr
)
80 mask
= random
.randint(0, 255)
81 lst
= [f
"mtcrf {mask}, 2"]
82 cr
= random
.randint(0, (1 << 32)-1)
83 initial_regs
= [0] * 32
84 initial_regs
[2] = random
.randint(0, (1 << 32)-1)
85 self
.add_case(Program(lst
, bigendian
), initial_regs
=initial_regs
,
88 def case_mtocrf(self
):
90 mask
= 1 << random
.randint(0, 7)
91 lst
= [f
"mtocrf {mask}, 2"]
92 cr
= random
.randint(0, (1 << 32)-1)
93 initial_regs
= [0] * 32
94 initial_regs
[2] = random
.randint(0, (1 << 32)-1)
95 self
.add_case(Program(lst
, bigendian
), initial_regs
=initial_regs
,
101 cr
= random
.randint(0, (1 << 32)-1)
102 self
.add_case(Program(lst
, bigendian
), initial_cr
=cr
)
104 def case_cror_regression(self
):
107 dis
= ["cror 28, 5, 11"]
108 lst
= bytes([0x83, 0x5b, 0x75, 0x4f]) # 4f855b83
110 p
= Program(lst
, bigendian
)
111 p
.assembly
= '\n'.join(dis
)+'\n'
112 self
.add_case(p
, initial_cr
=cr
)
114 def case_mfocrf_regression(self
):
115 """bit of a bad hack. comes from microwatt 1.bin instruction 0x106d0
116 as the mask is non-standard, gnu-as barfs. so we fake it up directly
120 dis
= [f
"mfocrf 2, {mask}"]
121 lst
= bytes([0x26, 0x78, 0xb8, 0x7c]) # 0x7cb87826
123 p
= Program(lst
, bigendian
)
124 p
.assembly
= '\n'.join(dis
)+'\n'
125 self
.add_case(p
, initial_cr
=cr
)
127 def case_mtocrf_regression(self
):
128 """microwatt 1.bin regression, same hack as above.
129 106b4: 21 d9 96 7d .long 0x7d96d921 # mtocrf 12, 0b01101101
132 dis
= [f
"mtocrf 12, {mask}"]
133 lst
= bytes([0x21, 0xd9, 0x96, 0x7d]) # 0x7d96d921
135 initial_regs
= [0] * 32
136 initial_regs
[12] = 0xffffffffffffffff
137 p
= Program(lst
, bigendian
)
138 p
.assembly
= '\n'.join(dis
)+'\n'
139 self
.add_case(p
, initial_regs
=initial_regs
, initial_cr
=cr
)
141 def case_mtocrf_regression_2(self
):
142 """microwatt 1.bin regression, zero fxm
143 mtocrf 0,16 14928: 21 09 10 7e .long 0x7e100921
145 dis
= ["mtocrf 16, 0"]
146 lst
= bytes([0x21, 0x09, 0x10, 0x7e]) # 0x7e100921
148 initial_regs
= [0] * 32
149 initial_regs
[16] = 0x0001C020
150 p
= Program(lst
, bigendian
)
151 p
.assembly
= '\n'.join(dis
)+'\n'
152 self
.add_case(p
, initial_regs
=initial_regs
, initial_cr
=cr
)
154 def case_mfocrf_1(self
):
155 lst
= [f
"mfocrf 2, 1"]
157 self
.add_case(Program(lst
, bigendian
), initial_cr
=cr
)
159 def case_mfocrf(self
):
161 mask
= 1 << random
.randint(0, 7)
162 lst
= [f
"mfocrf 2, {mask}"]
163 cr
= random
.randint(0, (1 << 32)-1)
164 self
.add_case(Program(lst
, bigendian
), initial_cr
=cr
)
166 def case_isel_0(self
):
167 lst
= [ "isel 4, 1, 2, 31"
169 initial_regs
= [0] * 32
170 initial_regs
[1] = 0x1004
171 initial_regs
[2] = 0x1008
173 self
.add_case(Program(lst
, bigendian
),
174 initial_regs
=initial_regs
, initial_cr
=cr
)
176 def case_isel_1(self
):
177 lst
= [ "isel 4, 1, 2, 30"
179 initial_regs
= [0] * 32
180 initial_regs
[1] = 0x1004
181 initial_regs
[2] = 0x1008
183 self
.add_case(Program(lst
, bigendian
),
184 initial_regs
=initial_regs
, initial_cr
=cr
)
186 def case_isel_2(self
):
187 lst
= [ "isel 4, 1, 2, 2"
189 initial_regs
= [0] * 32
190 initial_regs
[1] = 0x1004
191 initial_regs
[2] = 0x1008
193 self
.add_case(Program(lst
, bigendian
),
194 initial_regs
=initial_regs
, initial_cr
=cr
)
196 def case_isel_3(self
):
197 lst
= [ "isel 1, 2, 3, 13"
199 initial_regs
= [0] * 32
200 initial_regs
[2] = 0x1004
201 initial_regs
[3] = 0x1008
202 cr
= 0x5d677571b8229f1
204 self
.add_case(Program(lst
, bigendian
),
205 initial_regs
=initial_regs
, initial_cr
=cr
)
209 bc
= random
.randint(0, 31)
210 lst
= [f
"isel 1, 2, 3, {bc}"]
211 cr
= random
.randint(0, (1 << 64)-1)
212 initial_regs
= [0] * 32
213 #initial_regs[2] = random.randint(0, (1 << 64)-1)
214 #initial_regs[3] = random.randint(0, (1 << 64)-1)
215 initial_regs
[2] = i
*2+1
216 initial_regs
[3] = i
*2+2
217 self
.add_case(Program(lst
, bigendian
),
218 initial_regs
=initial_regs
, initial_cr
=cr
)
222 bfa
= random
.randint(0, 7)
223 lst
= [f
"setb 1, {bfa}"]
224 cr
= random
.randint(0, (1 << 32)-1)
225 self
.add_case(Program(lst
, bigendian
), initial_cr
=cr
)
227 def case_regression_setb(self
):
229 cr
= random
.randint(0, 0x66f6b106)
230 self
.add_case(Program(lst
, bigendian
), initial_cr
=cr
)
232 def case_ilang(self
):
233 pspec
= CRPipeSpec(id_wid
=2)
234 alu
= CRBasePipe(pspec
)
235 vl
= rtlil
.convert(alu
, ports
=alu
.ports())
236 with
open("cr_pipeline.il", "w") as f
:
240 def get_cu_inputs(dec2
, sim
):
241 """naming (res) must conform to CRFunctionUnit input regspec
244 full_reg
= yield dec2
.dec_cr_in
.whole_reg
.data
245 full_reg_ok
= yield dec2
.dec_cr_in
.whole_reg
.ok
246 full_cr_mask
= mask_extend(full_reg
, 8, 4)
251 res
['full_cr'] = sim
.cr
.value
& full_cr_mask
253 yield from ALUHelpers
.get_sim_cr_a(res
, sim
, dec2
) # CR A
254 yield from ALUHelpers
.get_sim_cr_b(res
, sim
, dec2
) # CR B
255 yield from ALUHelpers
.get_sim_cr_c(res
, sim
, dec2
) # CR C
257 yield from ALUHelpers
.get_sim_int_ra(res
, sim
, dec2
) # RA
258 yield from ALUHelpers
.get_sim_int_rb(res
, sim
, dec2
) # RB
260 print("get inputs", res
)
264 class TestRunner(unittest
.TestCase
):
265 def __init__(self
, test_data
):
266 super().__init
__("run_all")
267 self
.test_data
= test_data
269 def set_inputs(self
, alu
, dec2
, simulator
):
270 inp
= yield from get_cu_inputs(dec2
, simulator
)
271 yield from ALUHelpers
.set_full_cr(alu
, dec2
, inp
)
272 yield from ALUHelpers
.set_cr_a(alu
, dec2
, inp
)
273 yield from ALUHelpers
.set_cr_b(alu
, dec2
, inp
)
274 yield from ALUHelpers
.set_cr_c(alu
, dec2
, inp
)
275 yield from ALUHelpers
.set_int_ra(alu
, dec2
, inp
)
276 yield from ALUHelpers
.set_int_rb(alu
, dec2
, inp
)
278 def assert_outputs(self
, alu
, dec2
, simulator
, code
):
279 whole_reg_ok
= yield dec2
.dec_cr_out
.whole_reg
.ok
280 whole_reg_data
= yield dec2
.dec_cr_out
.whole_reg
.data
281 full_cr_mask
= mask_extend(whole_reg_data
, 8, 4)
283 cr_en
= yield dec2
.e
.write_cr
.ok
285 full_cr
= yield alu
.n
.data_o
.full_cr
.data
& full_cr_mask
286 expected_cr
= simulator
.cr
.value
287 print("CR whole: expected %x, actual: %x mask: %x" % \
288 (expected_cr
, full_cr
, full_cr_mask
))
289 # HACK: only look at the bits that we expected to change
290 self
.assertEqual(expected_cr
& full_cr_mask
, full_cr
, code
)
292 cr_sel
= yield dec2
.e
.write_cr
.data
293 expected_cr
= simulator
.cr
.value
294 print(f
"CR whole: {expected_cr:x}, sel {cr_sel}")
295 expected_cr
= simulator
.crl
[cr_sel
].get_range().value
296 real_cr
= yield alu
.n
.data_o
.cr
.data
297 print(f
"CR part: expected {expected_cr:x}, actual: {real_cr:x}")
298 self
.assertEqual(expected_cr
, real_cr
, code
)
299 alu_out
= yield alu
.n
.data_o
.o
.data
300 out_reg_valid
= yield dec2
.e
.write_reg
.ok
302 write_reg_idx
= yield dec2
.e
.write_reg
.data
303 expected
= simulator
.gpr(write_reg_idx
).value
304 print(f
"expected {expected:x}, actual: {alu_out:x}")
305 self
.assertEqual(expected
, alu_out
, code
)
307 def execute(self
, alu
, instruction
, pdecode2
, test
):
308 program
= test
.program
309 sim
= ISA(pdecode2
, test
.regs
, test
.sprs
, test
.cr
, test
.mem
,
312 gen
= program
.generate_instructions()
313 instructions
= list(zip(gen
, program
.assembly
.splitlines()))
315 index
= sim
.pc
.CIA
.value
//4
316 while index
< len(instructions
):
317 ins
, code
= instructions
[index
]
319 print("0x{:X}".format(ins
& 0xffffffff))
322 # ask the decoder to decode this binary data (endian'd)
323 yield pdecode2
.dec
.bigendian
.eq(bigendian
) # little / big?
324 yield instruction
.eq(ins
) # raw binary instr.
326 yield from self
.set_inputs(alu
, pdecode2
, sim
)
327 yield alu
.p
.valid_i
.eq(1)
328 fn_unit
= yield pdecode2
.e
.do
.fn_unit
329 self
.assertEqual(fn_unit
, Function
.CR
.value
, code
)
331 opname
= code
.split(' ')[0]
332 yield from sim
.call(opname
)
333 index
= sim
.pc
.CIA
.value
//4
335 vld
= yield alu
.n
.valid_o
338 vld
= yield alu
.n
.valid_o
340 yield from self
.assert_outputs(alu
, pdecode2
, sim
, code
)
345 instruction
= Signal(32)
348 opkls
= CRPipeSpec
.opsubsetkls
350 m
.submodules
.pdecode2
= pdecode2
= PowerDecode2(None, opkls
, fn_name
)
351 pdecode
= pdecode2
.dec
353 pspec
= CRPipeSpec(id_wid
=2)
354 m
.submodules
.alu
= alu
= CRBasePipe(pspec
)
356 comb
+= alu
.p
.data_i
.ctx
.op
.eq_from_execute1(pdecode2
.do
)
357 comb
+= alu
.n
.ready_i
.eq(1)
358 comb
+= pdecode2
.dec
.raw_opcode_in
.eq(instruction
)
364 for test
in self
.test_data
:
366 with self
.subTest(test
.name
):
367 yield from self
.execute(alu
, instruction
, pdecode2
, test
)
369 sim
.add_sync_process(process
)
370 with sim
.write_vcd("cr_simulator.vcd"):
374 if __name__
== "__main__":
375 unittest
.main(exit
=False)
376 suite
= unittest
.TestSuite()
377 suite
.addTest(TestRunner(CRTestCase().test_data
))
379 runner
= unittest
.TextTestRunner()