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
.alu_input_record
import CompALUOpSubset
17 from soc
.fu
.alu
.pipe_data
import ALUPipeSpec
21 def __init__(self
, program
, regs
, sprs
, name
):
22 self
.program
= program
27 def get_rec_width(rec
):
29 # Setup random inputs for dut.op
35 def set_alu_inputs(alu
, dec2
, sim
):
36 # TODO: see https://bugs.libre-soc.org/show_bug.cgi?id=305#c43
37 # detect the immediate here (with m.If(self.i.ctx.op.imm_data.imm_ok))
38 # and place it into data_i.b
40 reg3_ok
= yield dec2
.e
.read_reg3
.ok
41 reg1_ok
= yield dec2
.e
.read_reg1
.ok
42 assert reg3_ok
!= reg1_ok
44 data1
= yield dec2
.e
.read_reg3
.data
45 data1
= sim
.gpr(data1
).value
47 data1
= yield dec2
.e
.read_reg1
.data
48 data1
= sim
.gpr(data1
).value
52 yield alu
.p
.data_i
.a
.eq(data1
)
54 # If there's an immediate, set the B operand to that
55 reg2_ok
= yield dec2
.e
.read_reg2
.ok
56 imm_ok
= yield dec2
.e
.imm_data
.imm_ok
58 data2
= yield dec2
.e
.imm_data
.imm
60 data2
= yield dec2
.e
.read_reg2
.data
61 data2
= sim
.gpr(data2
).value
64 yield alu
.p
.data_i
.b
.eq(data2
)
68 def set_extra_alu_inputs(alu
, dec2
, sim
):
69 carry
= 1 if sim
.spr
['XER'][XER_bits
['CA']] else 0
70 carry32
= 1 if sim
.spr
['XER'][XER_bits
['CA32']] else 0
71 yield alu
.p
.data_i
.xer_ca
[0].eq(carry
)
72 yield alu
.p
.data_i
.xer_ca
[1].eq(carry32
)
73 so
= 1 if sim
.spr
['XER'][XER_bits
['SO']] else 0
74 yield alu
.p
.data_i
.xer_so
.eq(so
)
77 # This test bench is a bit different than is usual. Initially when I
78 # was writing it, I had all of the tests call a function to create a
79 # device under test and simulator, initialize the dut, run the
80 # simulation for ~2 cycles, and assert that the dut output what it
81 # should have. However, this was really slow, since it needed to
82 # create and tear down the dut and simulator for every test case.
84 # Now, instead of doing that, every test case in ALUTestCase puts some
85 # data into the test_data list below, describing the instructions to
86 # be tested and the initial state. Once all the tests have been run,
87 # test_data gets passed to TestRunner which then sets up the DUT and
88 # simulator once, runs all the data through it, and asserts that the
89 # results match the pseudocode sim at every cycle.
91 # By doing this, I've reduced the time it takes to run the test suite
92 # massively. Before, it took around 1 minute on my computer, now it
93 # takes around 3 seconds
98 class ALUTestCase(FHDLTestCase
):
99 def __init__(self
, name
):
100 super().__init
__(name
)
101 self
.test_name
= name
102 def run_tst_program(self
, prog
, initial_regs
=[0] * 32, initial_sprs
={}):
103 tc
= TestCase(prog
, initial_regs
, initial_sprs
, self
.test_name
)
107 insns
= ["add", "add.", "subf"]
109 choice
= random
.choice(insns
)
110 lst
= [f
"{choice} 3, 1, 2"]
111 initial_regs
= [0] * 32
112 initial_regs
[1] = random
.randint(0, (1<<64)-1)
113 initial_regs
[2] = random
.randint(0, (1<<64)-1)
114 self
.run_tst_program(Program(lst
), initial_regs
)
116 def test_rand_imm(self
):
117 insns
= ["addi", "addis", "subfic"]
119 choice
= random
.choice(insns
)
120 imm
= random
.randint(-(1<<15), (1<<15)-1)
121 lst
= [f
"{choice} 3, 1, {imm}"]
123 initial_regs
= [0] * 32
124 initial_regs
[1] = random
.randint(0, (1<<64)-1)
125 self
.run_tst_program(Program(lst
), initial_regs
)
128 lst
= ["adde. 5, 6, 7"]
130 initial_regs
= [0] * 32
131 initial_regs
[6] = random
.randint(0, (1<<64)-1)
132 initial_regs
[7] = random
.randint(0, (1<<64)-1)
134 xer
= SelectableInt(0, 64)
135 xer
[XER_bits
['CA']] = 1
136 initial_sprs
[special_sprs
['XER']] = xer
137 self
.run_tst_program(Program(lst
), initial_regs
, initial_sprs
)
140 lst
= ["subf. 1, 6, 7",
142 initial_regs
= [0] * 32
143 initial_regs
[6] = 0x10
144 initial_regs
[7] = 0x05
145 self
.run_tst_program(Program(lst
), initial_regs
, {})
147 def test_extsb(self
):
148 insns
= ["extsb", "extsh", "extsw"]
150 choice
= random
.choice(insns
)
151 lst
= [f
"{choice} 3, 1"]
153 initial_regs
= [0] * 32
154 initial_regs
[1] = random
.randint(0, (1<<64)-1)
155 self
.run_tst_program(Program(lst
), initial_regs
)
157 def test_cmpeqb(self
):
158 lst
= ["cmpeqb cr0, 1, 2"]
160 initial_regs
= [0] * 32
162 initial_regs
[2] = 0x01030507090b0d0f11
163 self
.run_tst_program(Program(lst
), initial_regs
, {})
165 def test_ilang(self
):
166 rec
= ALUPipeSpec
.opsubsetkls()
168 pspec
= ALUPipeSpec(id_wid
=2, op_wid
=get_rec_width(rec
))
169 alu
= ALUBasePipe(pspec
)
170 vl
= rtlil
.convert(alu
, ports
=alu
.ports())
171 with
open("alu_pipeline.il", "w") as f
:
175 class TestRunner(FHDLTestCase
):
176 def __init__(self
, test_data
):
177 super().__init
__("run_all")
178 self
.test_data
= test_data
183 instruction
= Signal(32)
185 pdecode
= create_pdecode()
187 m
.submodules
.pdecode2
= pdecode2
= PowerDecode2(pdecode
)
189 rec
= ALUPipeSpec
.opsubsetkls()
191 pspec
= ALUPipeSpec(id_wid
=2, op_wid
=get_rec_width(rec
))
192 m
.submodules
.alu
= alu
= ALUBasePipe(pspec
)
194 comb
+= alu
.p
.data_i
.ctx
.op
.eq_from_execute1(pdecode2
.e
)
195 comb
+= alu
.p
.valid_i
.eq(1)
196 comb
+= alu
.n
.ready_i
.eq(1)
197 comb
+= pdecode2
.dec
.raw_opcode_in
.eq(instruction
)
202 for test
in self
.test_data
:
204 program
= test
.program
205 self
.subTest(test
.name
)
206 simulator
= ISA(pdecode2
, test
.regs
, test
.sprs
, 0)
207 gen
= program
.generate_instructions()
208 instructions
= list(zip(gen
, program
.assembly
.splitlines()))
210 index
= simulator
.pc
.CIA
.value
//4
211 while index
< len(instructions
):
212 ins
, code
= instructions
[index
]
214 print("0x{:X}".format(ins
& 0xffffffff))
217 # ask the decoder to decode this binary data (endian'd)
218 yield pdecode2
.dec
.bigendian
.eq(0) # little / big?
219 yield instruction
.eq(ins
) # raw binary instr.
221 fn_unit
= yield pdecode2
.e
.fn_unit
222 self
.assertEqual(fn_unit
, Function
.ALU
.value
)
223 yield from set_alu_inputs(alu
, pdecode2
, simulator
)
224 yield from set_extra_alu_inputs(alu
, pdecode2
, simulator
)
226 opname
= code
.split(' ')[0]
227 yield from simulator
.call(opname
)
228 index
= simulator
.pc
.CIA
.value
//4
230 vld
= yield alu
.n
.valid_o
233 vld
= yield alu
.n
.valid_o
235 alu_out
= yield alu
.n
.data_o
.o
236 out_reg_valid
= yield pdecode2
.e
.write_reg
.ok
238 write_reg_idx
= yield pdecode2
.e
.write_reg
.data
239 expected
= simulator
.gpr(write_reg_idx
).value
240 print(f
"expected {expected:x}, actual: {alu_out:x}")
241 self
.assertEqual(expected
, alu_out
, code
)
242 yield from self
.check_extra_alu_outputs(alu
, pdecode2
,
245 sim
.add_sync_process(process
)
246 with sim
.write_vcd("simulator.vcd", "simulator.gtkw",
250 def check_extra_alu_outputs(self
, alu
, dec2
, sim
, code
):
251 rc
= yield dec2
.e
.rc
.data
253 cr_expected
= sim
.crl
[0].get_range().value
254 cr_actual
= yield alu
.n
.data_o
.cr0
.data
255 self
.assertEqual(cr_expected
, cr_actual
, code
)
257 op
= yield dec2
.e
.insn_type
258 if op
== InternalOp
.OP_CMP
.value
or \
259 op
== InternalOp
.OP_CMPEQB
.value
:
260 bf
= yield dec2
.dec
.BF
261 cr_actual
= yield alu
.n
.data_o
.cr0
.data
262 cr_expected
= sim
.crl
[bf
].get_range().value
263 self
.assertEqual(cr_expected
, cr_actual
, code
)
265 cry_out
= yield dec2
.e
.output_carry
267 expected_carry
= 1 if sim
.spr
['XER'][XER_bits
['CA']] else 0
268 real_carry
= yield alu
.n
.data_o
.xer_ca
.data
[0] # XXX CO not CO32
269 self
.assertEqual(expected_carry
, real_carry
, code
)
270 expected_carry32
= 1 if sim
.spr
['XER'][XER_bits
['CA32']] else 0
271 real_carry32
= yield alu
.n
.data_o
.xer_ca
.data
[1] # XXX CO32
272 self
.assertEqual(expected_carry32
, real_carry32
, code
)
276 if __name__
== "__main__":
277 unittest
.main(exit
=False)
278 suite
= unittest
.TestSuite()
279 suite
.addTest(TestRunner(test_data
))
281 runner
= unittest
.TextTestRunner()