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 reg3_ok
= yield dec2
.e
.read_reg3
.ok
33 reg1_ok
= yield dec2
.e
.read_reg1
.ok
34 assert reg3_ok
!= reg1_ok
36 data1
= yield dec2
.e
.read_reg3
.data
37 data1
= sim
.gpr(data1
).value
39 data1
= yield dec2
.e
.read_reg1
.data
40 data1
= sim
.gpr(data1
).value
44 yield alu
.p
.data_i
.a
.eq(data1
)
46 # If there's an immediate, set the B operand to that
47 reg2_ok
= yield dec2
.e
.read_reg2
.ok
48 imm_ok
= yield dec2
.e
.imm_data
.imm_ok
50 data2
= yield dec2
.e
.imm_data
.imm
52 data2
= yield dec2
.e
.read_reg2
.data
53 data2
= sim
.gpr(data2
).value
56 yield alu
.p
.data_i
.b
.eq(data2
)
60 def set_extra_alu_inputs(alu
, dec2
, sim
):
61 carry
= 1 if sim
.spr
['XER'][XER_bits
['CA']] else 0
62 carry32
= 1 if sim
.spr
['XER'][XER_bits
['CA32']] else 0
63 yield alu
.p
.data_i
.xer_ca
[0].eq(carry
)
64 yield alu
.p
.data_i
.xer_ca
[1].eq(carry32
)
65 so
= 1 if sim
.spr
['XER'][XER_bits
['SO']] else 0
66 yield alu
.p
.data_i
.xer_so
.eq(so
)
69 # This test bench is a bit different than is usual. Initially when I
70 # was writing it, I had all of the tests call a function to create a
71 # device under test and simulator, initialize the dut, run the
72 # simulation for ~2 cycles, and assert that the dut output what it
73 # should have. However, this was really slow, since it needed to
74 # create and tear down the dut and simulator for every test case.
76 # Now, instead of doing that, every test case in ALUTestCase puts some
77 # data into the test_data list below, describing the instructions to
78 # be tested and the initial state. Once all the tests have been run,
79 # test_data gets passed to TestRunner which then sets up the DUT and
80 # simulator once, runs all the data through it, and asserts that the
81 # results match the pseudocode sim at every cycle.
83 # By doing this, I've reduced the time it takes to run the test suite
84 # massively. Before, it took around 1 minute on my computer, now it
85 # takes around 3 seconds
90 class ALUTestCase(FHDLTestCase
):
91 def __init__(self
, name
):
92 super().__init
__(name
)
94 def run_tst_program(self
, prog
, initial_regs
=[0] * 32, initial_sprs
={}):
95 tc
= TestCase(prog
, initial_regs
, initial_sprs
, self
.test_name
)
99 insns
= ["add", "add.", "subf"]
101 choice
= random
.choice(insns
)
102 lst
= [f
"{choice} 3, 1, 2"]
103 initial_regs
= [0] * 32
104 initial_regs
[1] = random
.randint(0, (1<<64)-1)
105 initial_regs
[2] = random
.randint(0, (1<<64)-1)
106 self
.run_tst_program(Program(lst
), initial_regs
)
108 def test_rand_imm(self
):
109 insns
= ["addi", "addis", "subfic"]
111 choice
= random
.choice(insns
)
112 imm
= random
.randint(-(1<<15), (1<<15)-1)
113 lst
= [f
"{choice} 3, 1, {imm}"]
115 initial_regs
= [0] * 32
116 initial_regs
[1] = random
.randint(0, (1<<64)-1)
117 self
.run_tst_program(Program(lst
), initial_regs
)
120 lst
= ["adde. 5, 6, 7"]
122 initial_regs
= [0] * 32
123 initial_regs
[6] = random
.randint(0, (1<<64)-1)
124 initial_regs
[7] = random
.randint(0, (1<<64)-1)
126 xer
= SelectableInt(0, 64)
127 xer
[XER_bits
['CA']] = 1
128 initial_sprs
[special_sprs
['XER']] = xer
129 self
.run_tst_program(Program(lst
), initial_regs
, initial_sprs
)
132 lst
= ["subf. 1, 6, 7",
134 initial_regs
= [0] * 32
135 initial_regs
[6] = 0x10
136 initial_regs
[7] = 0x05
137 self
.run_tst_program(Program(lst
), initial_regs
, {})
139 def test_extsb(self
):
140 insns
= ["extsb", "extsh", "extsw"]
142 choice
= random
.choice(insns
)
143 lst
= [f
"{choice} 3, 1"]
145 initial_regs
= [0] * 32
146 initial_regs
[1] = random
.randint(0, (1<<64)-1)
147 self
.run_tst_program(Program(lst
), initial_regs
)
149 def test_cmpeqb(self
):
150 lst
= ["cmpeqb cr0, 1, 2"]
152 initial_regs
= [0] * 32
154 initial_regs
[2] = 0x01030507090b0d0f11
155 self
.run_tst_program(Program(lst
), initial_regs
, {})
157 def test_ilang(self
):
158 pspec
= ALUPipeSpec(id_wid
=2)
159 alu
= ALUBasePipe(pspec
)
160 vl
= rtlil
.convert(alu
, ports
=alu
.ports())
161 with
open("alu_pipeline.il", "w") as f
:
165 class TestRunner(FHDLTestCase
):
166 def __init__(self
, test_data
):
167 super().__init
__("run_all")
168 self
.test_data
= test_data
173 instruction
= Signal(32)
175 pdecode
= create_pdecode()
177 m
.submodules
.pdecode2
= pdecode2
= PowerDecode2(pdecode
)
179 pspec
= ALUPipeSpec(id_wid
=2)
180 m
.submodules
.alu
= alu
= ALUBasePipe(pspec
)
182 comb
+= alu
.p
.data_i
.ctx
.op
.eq_from_execute1(pdecode2
.e
)
183 comb
+= alu
.p
.valid_i
.eq(1)
184 comb
+= alu
.n
.ready_i
.eq(1)
185 comb
+= pdecode2
.dec
.raw_opcode_in
.eq(instruction
)
190 for test
in self
.test_data
:
192 program
= test
.program
193 self
.subTest(test
.name
)
194 simulator
= ISA(pdecode2
, test
.regs
, test
.sprs
, 0)
195 gen
= program
.generate_instructions()
196 instructions
= list(zip(gen
, program
.assembly
.splitlines()))
198 index
= simulator
.pc
.CIA
.value
//4
199 while index
< len(instructions
):
200 ins
, code
= instructions
[index
]
202 print("0x{:X}".format(ins
& 0xffffffff))
205 # ask the decoder to decode this binary data (endian'd)
206 yield pdecode2
.dec
.bigendian
.eq(0) # little / big?
207 yield instruction
.eq(ins
) # raw binary instr.
209 fn_unit
= yield pdecode2
.e
.fn_unit
210 self
.assertEqual(fn_unit
, Function
.ALU
.value
)
211 yield from set_alu_inputs(alu
, pdecode2
, simulator
)
212 yield from set_extra_alu_inputs(alu
, pdecode2
, simulator
)
214 opname
= code
.split(' ')[0]
215 yield from simulator
.call(opname
)
216 index
= simulator
.pc
.CIA
.value
//4
218 vld
= yield alu
.n
.valid_o
221 vld
= yield alu
.n
.valid_o
223 alu_out
= yield alu
.n
.data_o
.o
.data
224 out_reg_valid
= yield pdecode2
.e
.write_reg
.ok
226 write_reg_idx
= yield pdecode2
.e
.write_reg
.data
227 expected
= simulator
.gpr(write_reg_idx
).value
228 print(f
"expected {expected:x}, actual: {alu_out:x}")
229 self
.assertEqual(expected
, alu_out
, code
)
230 yield from self
.check_extra_alu_outputs(alu
, pdecode2
,
233 sim
.add_sync_process(process
)
234 with sim
.write_vcd("simulator.vcd", "simulator.gtkw",
238 def check_extra_alu_outputs(self
, alu
, dec2
, sim
, code
):
239 rc
= yield dec2
.e
.rc
.data
241 cr_expected
= sim
.crl
[0].get_range().value
242 cr_actual
= yield alu
.n
.data_o
.cr0
.data
243 self
.assertEqual(cr_expected
, cr_actual
, code
)
245 op
= yield dec2
.e
.insn_type
246 if op
== InternalOp
.OP_CMP
.value
or \
247 op
== InternalOp
.OP_CMPEQB
.value
:
248 bf
= yield dec2
.dec
.BF
249 cr_actual
= yield alu
.n
.data_o
.cr0
.data
250 cr_expected
= sim
.crl
[bf
].get_range().value
251 self
.assertEqual(cr_expected
, cr_actual
, code
)
253 cry_out
= yield dec2
.e
.output_carry
255 expected_carry
= 1 if sim
.spr
['XER'][XER_bits
['CA']] else 0
256 real_carry
= yield alu
.n
.data_o
.xer_ca
.data
[0] # XXX CO not CO32
257 self
.assertEqual(expected_carry
, real_carry
, code
)
258 expected_carry32
= 1 if sim
.spr
['XER'][XER_bits
['CA32']] else 0
259 real_carry32
= yield alu
.n
.data_o
.xer_ca
.data
[1] # XXX CO32
260 self
.assertEqual(expected_carry32
, real_carry32
, code
)
264 if __name__
== "__main__":
265 unittest
.main(exit
=False)
266 suite
= unittest
.TestSuite()
267 suite
.addTest(TestRunner(test_data
))
269 runner
= unittest
.TextTestRunner()