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 yield alu
.p
.data_i
.carry_in
.eq(carry
)
71 so
= 1 if sim
.spr
['XER'][XER_bits
['SO']] else 0
72 yield alu
.p
.data_i
.so
.eq(so
)
75 # This test bench is a bit different than is usual. Initially when I
76 # was writing it, I had all of the tests call a function to create a
77 # device under test and simulator, initialize the dut, run the
78 # simulation for ~2 cycles, and assert that the dut output what it
79 # should have. However, this was really slow, since it needed to
80 # create and tear down the dut and simulator for every test case.
82 # Now, instead of doing that, every test case in ALUTestCase puts some
83 # data into the test_data list below, describing the instructions to
84 # be tested and the initial state. Once all the tests have been run,
85 # test_data gets passed to TestRunner which then sets up the DUT and
86 # simulator once, runs all the data through it, and asserts that the
87 # results match the pseudocode sim at every cycle.
89 # By doing this, I've reduced the time it takes to run the test suite
90 # massively. Before, it took around 1 minute on my computer, now it
91 # takes around 3 seconds
96 class ALUTestCase(FHDLTestCase
):
97 def __init__(self
, name
):
98 super().__init
__(name
)
100 def run_tst_program(self
, prog
, initial_regs
=[0] * 32, initial_sprs
={}):
101 tc
= TestCase(prog
, initial_regs
, initial_sprs
, self
.test_name
)
105 insns
= ["add", "add.", "subf"]
107 choice
= random
.choice(insns
)
108 lst
= [f
"{choice} 3, 1, 2"]
109 initial_regs
= [0] * 32
110 initial_regs
[1] = random
.randint(0, (1<<64)-1)
111 initial_regs
[2] = random
.randint(0, (1<<64)-1)
112 self
.run_tst_program(Program(lst
), initial_regs
)
114 def test_rand_imm(self
):
115 insns
= ["addi", "addis", "subfic"]
117 choice
= random
.choice(insns
)
118 imm
= random
.randint(-(1<<15), (1<<15)-1)
119 lst
= [f
"{choice} 3, 1, {imm}"]
121 initial_regs
= [0] * 32
122 initial_regs
[1] = random
.randint(0, (1<<64)-1)
123 self
.run_tst_program(Program(lst
), initial_regs
)
126 lst
= ["adde. 5, 6, 7"]
127 initial_regs
= [0] * 32
128 initial_regs
[6] = random
.randint(0, (1<<64)-1)
129 initial_regs
[7] = random
.randint(0, (1<<64)-1)
131 xer
= SelectableInt(0, 64)
132 xer
[XER_bits
['CA']] = 1
133 initial_sprs
[special_sprs
['XER']] = xer
134 self
.run_tst_program(Program(lst
), initial_regs
, initial_sprs
)
137 lst
= ["subf. 1, 6, 7",
139 initial_regs
= [0] * 32
140 initial_regs
[6] = 0x10
141 initial_regs
[7] = 0x05
142 self
.run_tst_program(Program(lst
), initial_regs
, {})
144 def test_extsb(self
):
145 insns
= ["extsb", "extsh", "extsw"]
147 choice
= random
.choice(insns
)
148 lst
= [f
"{choice} 3, 1"]
150 initial_regs
= [0] * 32
151 initial_regs
[1] = random
.randint(0, (1<<64)-1)
152 self
.run_tst_program(Program(lst
), initial_regs
)
154 def test_cmpeqb(self
):
155 lst
= ["cmpeqb cr0, 1, 2"]
157 initial_regs
= [0] * 32
159 initial_regs
[2] = 0x01030507090b0d0f11
160 self
.run_tst_program(Program(lst
), initial_regs
, {})
162 def test_ilang(self
):
163 rec
= CompALUOpSubset()
165 pspec
= ALUPipeSpec(id_wid
=2, op_wid
=get_rec_width(rec
))
166 alu
= ALUBasePipe(pspec
)
167 vl
= rtlil
.convert(alu
, ports
=alu
.ports())
168 with
open("pipeline.il", "w") as f
:
172 class TestRunner(FHDLTestCase
):
173 def __init__(self
, test_data
):
174 super().__init
__("run_all")
175 self
.test_data
= test_data
180 instruction
= Signal(32)
182 pdecode
= create_pdecode()
184 m
.submodules
.pdecode2
= pdecode2
= PowerDecode2(pdecode
)
186 rec
= CompALUOpSubset()
188 pspec
= ALUPipeSpec(id_wid
=2, op_wid
=get_rec_width(rec
))
189 m
.submodules
.alu
= alu
= ALUBasePipe(pspec
)
191 comb
+= alu
.p
.data_i
.ctx
.op
.eq_from_execute1(pdecode2
.e
)
192 comb
+= alu
.p
.valid_i
.eq(1)
193 comb
+= alu
.n
.ready_i
.eq(1)
194 comb
+= pdecode2
.dec
.raw_opcode_in
.eq(instruction
)
199 for test
in self
.test_data
:
201 program
= test
.program
202 self
.subTest(test
.name
)
203 simulator
= ISA(pdecode2
, test
.regs
, test
.sprs
, 0)
204 gen
= program
.generate_instructions()
205 instructions
= list(zip(gen
, program
.assembly
.splitlines()))
207 index
= simulator
.pc
.CIA
.value
//4
208 while index
< len(instructions
):
209 ins
, code
= instructions
[index
]
211 print("0x{:X}".format(ins
& 0xffffffff))
214 # ask the decoder to decode this binary data (endian'd)
215 yield pdecode2
.dec
.bigendian
.eq(0) # little / big?
216 yield instruction
.eq(ins
) # raw binary instr.
218 fn_unit
= yield pdecode2
.e
.fn_unit
219 self
.assertEqual(fn_unit
, Function
.ALU
.value
)
220 yield from set_alu_inputs(alu
, pdecode2
, simulator
)
221 yield from set_extra_alu_inputs(alu
, pdecode2
, simulator
)
223 opname
= code
.split(' ')[0]
224 yield from simulator
.call(opname
)
225 index
= simulator
.pc
.CIA
.value
//4
227 vld
= yield alu
.n
.valid_o
230 vld
= yield alu
.n
.valid_o
232 alu_out
= yield alu
.n
.data_o
.o
233 out_reg_valid
= yield pdecode2
.e
.write_reg
.ok
235 write_reg_idx
= yield pdecode2
.e
.write_reg
.data
236 expected
= simulator
.gpr(write_reg_idx
).value
237 print(f
"expected {expected:x}, actual: {alu_out:x}")
238 self
.assertEqual(expected
, alu_out
)
239 yield from self
.check_extra_alu_outputs(alu
, pdecode2
,
242 sim
.add_sync_process(process
)
243 with sim
.write_vcd("simulator.vcd", "simulator.gtkw",
247 def check_extra_alu_outputs(self
, alu
, dec2
, sim
, code
):
248 rc
= yield dec2
.e
.rc
.data
250 cr_expected
= sim
.crl
[0].get_range().value
251 cr_actual
= yield alu
.n
.data_o
.cr0
252 self
.assertEqual(cr_expected
, cr_actual
, code
)
254 op
= yield dec2
.e
.insn_type
255 if op
== InternalOp
.OP_CMP
.value
or \
256 op
== InternalOp
.OP_CMPEQB
.value
:
257 bf
= yield dec2
.dec
.BF
258 cr_actual
= yield alu
.n
.data_o
.cr0
259 cr_expected
= sim
.crl
[bf
].get_range().value
260 self
.assertEqual(cr_expected
, cr_actual
, code
)
264 if __name__
== "__main__":
265 unittest
.main(exit
=False)
266 suite
= unittest
.TestSuite()
267 suite
.addTest(TestRunner(test_data
))
269 runner
= unittest
.TextTestRunner()