8517dc741312b8fbd0c7f588ae17423f5b4af7e0
1 from nmigen
import Module
, Signal
2 from nmigen
.back
.pysim
import Simulator
, Delay
, Settle
3 from nmigen
.test
.utils
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
, InternalOp
)
10 from soc
.decoder
.selectable_int
import SelectableInt
11 from soc
.simulator
.program
import Program
12 from soc
.decoder
.isa
.all
import ISA
15 from soc
.fu
.alu
.pipeline
import ALUBasePipe
16 from soc
.fu
.alu
.pipe_data
import ALUPipeSpec
20 def __init__(self
, program
, regs
, sprs
, name
):
21 self
.program
= program
27 def set_alu_inputs(alu
, dec2
, sim
):
28 # TODO: see https://bugs.libre-soc.org/show_bug.cgi?id=305#c43
29 # detect the immediate here (with m.If(self.i.ctx.op.imm_data.imm_ok))
30 # and place it into data_i.b
32 reg1_ok
= yield dec2
.e
.read_reg1
.ok
34 data1
= yield dec2
.e
.read_reg1
.data
35 data1
= sim
.gpr(data1
).value
39 yield alu
.p
.data_i
.a
.eq(data1
)
41 # If there's an immediate, set the B operand to that
42 reg2_ok
= yield dec2
.e
.read_reg2
.ok
43 imm_ok
= yield dec2
.e
.imm_data
.imm_ok
45 data2
= yield dec2
.e
.imm_data
.imm
47 data2
= yield dec2
.e
.read_reg2
.data
48 data2
= sim
.gpr(data2
).value
51 yield alu
.p
.data_i
.b
.eq(data2
)
55 def set_extra_alu_inputs(alu
, dec2
, sim
):
56 carry
= 1 if sim
.spr
['XER'][XER_bits
['CA']] else 0
57 carry32
= 1 if sim
.spr
['XER'][XER_bits
['CA32']] else 0
58 yield alu
.p
.data_i
.xer_ca
[0].eq(carry
)
59 yield alu
.p
.data_i
.xer_ca
[1].eq(carry32
)
60 so
= 1 if sim
.spr
['XER'][XER_bits
['SO']] else 0
61 yield alu
.p
.data_i
.xer_so
.eq(so
)
64 # This test bench is a bit different than is usual. Initially when I
65 # was writing it, I had all of the tests call a function to create a
66 # device under test and simulator, initialize the dut, run the
67 # simulation for ~2 cycles, and assert that the dut output what it
68 # should have. However, this was really slow, since it needed to
69 # create and tear down the dut and simulator for every test case.
71 # Now, instead of doing that, every test case in ALUTestCase puts some
72 # data into the test_data list below, describing the instructions to
73 # be tested and the initial state. Once all the tests have been run,
74 # test_data gets passed to TestRunner which then sets up the DUT and
75 # simulator once, runs all the data through it, and asserts that the
76 # results match the pseudocode sim at every cycle.
78 # By doing this, I've reduced the time it takes to run the test suite
79 # massively. Before, it took around 1 minute on my computer, now it
80 # takes around 3 seconds
85 class ALUTestCase(FHDLTestCase
):
86 def __init__(self
, name
):
87 super().__init
__(name
)
89 def run_tst_program(self
, prog
, initial_regs
=[0] * 32, initial_sprs
={}):
90 tc
= TestCase(prog
, initial_regs
, initial_sprs
, self
.test_name
)
94 insns
= ["add", "add.", "subf"]
96 choice
= random
.choice(insns
)
97 lst
= [f
"{choice} 3, 1, 2"]
98 initial_regs
= [0] * 32
99 initial_regs
[1] = random
.randint(0, (1<<64)-1)
100 initial_regs
[2] = random
.randint(0, (1<<64)-1)
101 self
.run_tst_program(Program(lst
), initial_regs
)
103 def test_rand_imm(self
):
104 insns
= ["addi", "addis", "subfic"]
106 choice
= random
.choice(insns
)
107 imm
= random
.randint(-(1<<15), (1<<15)-1)
108 lst
= [f
"{choice} 3, 1, {imm}"]
110 initial_regs
= [0] * 32
111 initial_regs
[1] = random
.randint(0, (1<<64)-1)
112 self
.run_tst_program(Program(lst
), initial_regs
)
115 lst
= ["adde. 5, 6, 7"]
117 initial_regs
= [0] * 32
118 initial_regs
[6] = random
.randint(0, (1<<64)-1)
119 initial_regs
[7] = random
.randint(0, (1<<64)-1)
121 xer
= SelectableInt(0, 64)
122 xer
[XER_bits
['CA']] = 1
123 initial_sprs
[special_sprs
['XER']] = xer
124 self
.run_tst_program(Program(lst
), initial_regs
, initial_sprs
)
127 lst
= ["subf. 1, 6, 7",
129 initial_regs
= [0] * 32
130 initial_regs
[6] = 0x10
131 initial_regs
[7] = 0x05
132 self
.run_tst_program(Program(lst
), initial_regs
, {})
134 def test_extsb(self
):
135 insns
= ["extsb", "extsh", "extsw"]
137 choice
= random
.choice(insns
)
138 lst
= [f
"{choice} 3, 1"]
140 initial_regs
= [0] * 32
141 initial_regs
[1] = random
.randint(0, (1<<64)-1)
142 self
.run_tst_program(Program(lst
), initial_regs
)
144 def test_cmpeqb(self
):
145 lst
= ["cmpeqb cr1, 1, 2"]
147 initial_regs
= [0] * 32
149 initial_regs
[2] = 0x0001030507090b0f
150 self
.run_tst_program(Program(lst
), initial_regs
, {})
152 def test_ilang(self
):
153 pspec
= ALUPipeSpec(id_wid
=2)
154 alu
= ALUBasePipe(pspec
)
155 vl
= rtlil
.convert(alu
, ports
=alu
.ports())
156 with
open("alu_pipeline.il", "w") as f
:
160 class TestRunner(FHDLTestCase
):
161 def __init__(self
, test_data
):
162 super().__init
__("run_all")
163 self
.test_data
= test_data
168 instruction
= Signal(32)
170 pdecode
= create_pdecode()
172 m
.submodules
.pdecode2
= pdecode2
= PowerDecode2(pdecode
)
174 pspec
= ALUPipeSpec(id_wid
=2)
175 m
.submodules
.alu
= alu
= ALUBasePipe(pspec
)
177 comb
+= alu
.p
.data_i
.ctx
.op
.eq_from_execute1(pdecode2
.e
)
178 comb
+= alu
.p
.valid_i
.eq(1)
179 comb
+= alu
.n
.ready_i
.eq(1)
180 comb
+= pdecode2
.dec
.raw_opcode_in
.eq(instruction
)
185 for test
in self
.test_data
:
187 program
= test
.program
188 self
.subTest(test
.name
)
189 simulator
= ISA(pdecode2
, test
.regs
, test
.sprs
, 0)
190 gen
= program
.generate_instructions()
191 instructions
= list(zip(gen
, program
.assembly
.splitlines()))
193 index
= simulator
.pc
.CIA
.value
//4
194 while index
< len(instructions
):
195 ins
, code
= instructions
[index
]
197 print("0x{:X}".format(ins
& 0xffffffff))
200 # ask the decoder to decode this binary data (endian'd)
201 yield pdecode2
.dec
.bigendian
.eq(0) # little / big?
202 yield instruction
.eq(ins
) # raw binary instr.
204 fn_unit
= yield pdecode2
.e
.fn_unit
205 self
.assertEqual(fn_unit
, Function
.ALU
.value
)
206 yield from set_alu_inputs(alu
, pdecode2
, simulator
)
207 yield from set_extra_alu_inputs(alu
, pdecode2
, simulator
)
209 opname
= code
.split(' ')[0]
210 yield from simulator
.call(opname
)
211 index
= simulator
.pc
.CIA
.value
//4
213 vld
= yield alu
.n
.valid_o
216 vld
= yield alu
.n
.valid_o
218 alu_out
= yield alu
.n
.data_o
.o
.data
219 out_reg_valid
= yield pdecode2
.e
.write_reg
.ok
221 write_reg_idx
= yield pdecode2
.e
.write_reg
.data
222 expected
= simulator
.gpr(write_reg_idx
).value
223 print(f
"expected {expected:x}, actual: {alu_out:x}")
224 self
.assertEqual(expected
, alu_out
, code
)
225 yield from self
.check_extra_alu_outputs(alu
, pdecode2
,
228 sim
.add_sync_process(process
)
229 with sim
.write_vcd("simulator.vcd", "simulator.gtkw",
233 def check_extra_alu_outputs(self
, alu
, dec2
, sim
, code
):
234 rc
= yield dec2
.e
.rc
.data
235 op
= yield dec2
.e
.insn_type
236 cridx_ok
= yield dec2
.e
.write_cr
.ok
237 cridx
= yield dec2
.e
.write_cr
.data
239 print ("check extra output", repr(code
), cridx_ok
, cridx
)
241 self
.assertEqual(cridx
, 0, code
)
244 cr_expected
= sim
.crl
[cridx
].get_range().value
245 cr_actual
= yield alu
.n
.data_o
.cr0
.data
246 print ("CR", cridx
, cr_expected
, cr_actual
)
247 self
.assertEqual(cr_expected
, cr_actual
, "CR%d %s" % (cridx
, code
))
249 cry_out
= yield dec2
.e
.output_carry
251 expected_carry
= 1 if sim
.spr
['XER'][XER_bits
['CA']] else 0
252 real_carry
= yield alu
.n
.data_o
.xer_ca
.data
[0] # XXX CO not CO32
253 self
.assertEqual(expected_carry
, real_carry
, code
)
254 expected_carry32
= 1 if sim
.spr
['XER'][XER_bits
['CA32']] else 0
255 real_carry32
= yield alu
.n
.data_o
.xer_ca
.data
[1] # XXX CO32
256 self
.assertEqual(expected_carry32
, real_carry32
, code
)
260 if __name__
== "__main__":
261 unittest
.main(exit
=False)
262 suite
= unittest
.TestSuite()
263 suite
.addTest(TestRunner(test_data
))
265 runner
= unittest
.TextTestRunner()