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
28 def set_alu_inputs(alu
, dec2
, sim
):
29 # TODO: see https://bugs.libre-soc.org/show_bug.cgi?id=305#c43
30 # detect the immediate here (with m.If(self.i.ctx.op.imm_data.imm_ok))
31 # and place it into data_i.b
33 reg3_ok
= yield dec2
.e
.read_reg3
.ok
34 reg1_ok
= yield dec2
.e
.read_reg1
.ok
35 assert reg3_ok
!= reg1_ok
37 data1
= yield dec2
.e
.read_reg3
.data
38 data1
= sim
.gpr(data1
).value
40 data1
= yield dec2
.e
.read_reg1
.data
41 data1
= sim
.gpr(data1
).value
45 yield alu
.p
.data_i
.a
.eq(data1
)
47 # If there's an immediate, set the B operand to that
48 reg2_ok
= yield dec2
.e
.read_reg2
.ok
49 imm_ok
= yield dec2
.e
.imm_data
.imm_ok
51 data2
= yield dec2
.e
.imm_data
.imm
53 data2
= yield dec2
.e
.read_reg2
.data
54 data2
= sim
.gpr(data2
).value
57 yield alu
.p
.data_i
.b
.eq(data2
)
61 def set_extra_alu_inputs(alu
, dec2
, sim
):
62 carry
= 1 if sim
.spr
['XER'][XER_bits
['CA']] else 0
63 carry32
= 1 if sim
.spr
['XER'][XER_bits
['CA32']] else 0
64 yield alu
.p
.data_i
.xer_ca
[0].eq(carry
)
65 yield alu
.p
.data_i
.xer_ca
[1].eq(carry32
)
66 so
= 1 if sim
.spr
['XER'][XER_bits
['SO']] else 0
67 yield alu
.p
.data_i
.xer_so
.eq(so
)
70 # This test bench is a bit different than is usual. Initially when I
71 # was writing it, I had all of the tests call a function to create a
72 # device under test and simulator, initialize the dut, run the
73 # simulation for ~2 cycles, and assert that the dut output what it
74 # should have. However, this was really slow, since it needed to
75 # create and tear down the dut and simulator for every test case.
77 # Now, instead of doing that, every test case in ALUTestCase puts some
78 # data into the test_data list below, describing the instructions to
79 # be tested and the initial state. Once all the tests have been run,
80 # test_data gets passed to TestRunner which then sets up the DUT and
81 # simulator once, runs all the data through it, and asserts that the
82 # results match the pseudocode sim at every cycle.
84 # By doing this, I've reduced the time it takes to run the test suite
85 # massively. Before, it took around 1 minute on my computer, now it
86 # takes around 3 seconds
91 class ALUTestCase(FHDLTestCase
):
92 def __init__(self
, name
):
93 super().__init
__(name
)
95 def run_tst_program(self
, prog
, initial_regs
=[0] * 32, initial_sprs
={}):
96 tc
= TestCase(prog
, initial_regs
, initial_sprs
, self
.test_name
)
100 insns
= ["add", "add.", "subf"]
102 choice
= random
.choice(insns
)
103 lst
= [f
"{choice} 3, 1, 2"]
104 initial_regs
= [0] * 32
105 initial_regs
[1] = random
.randint(0, (1<<64)-1)
106 initial_regs
[2] = random
.randint(0, (1<<64)-1)
107 self
.run_tst_program(Program(lst
), initial_regs
)
109 def test_rand_imm(self
):
110 insns
= ["addi", "addis", "subfic"]
112 choice
= random
.choice(insns
)
113 imm
= random
.randint(-(1<<15), (1<<15)-1)
114 lst
= [f
"{choice} 3, 1, {imm}"]
116 initial_regs
= [0] * 32
117 initial_regs
[1] = random
.randint(0, (1<<64)-1)
118 self
.run_tst_program(Program(lst
), initial_regs
)
121 lst
= ["adde. 5, 6, 7"]
123 initial_regs
= [0] * 32
124 initial_regs
[6] = random
.randint(0, (1<<64)-1)
125 initial_regs
[7] = random
.randint(0, (1<<64)-1)
127 xer
= SelectableInt(0, 64)
128 xer
[XER_bits
['CA']] = 1
129 initial_sprs
[special_sprs
['XER']] = xer
130 self
.run_tst_program(Program(lst
), initial_regs
, initial_sprs
)
133 lst
= ["subf. 1, 6, 7",
135 initial_regs
= [0] * 32
136 initial_regs
[6] = 0x10
137 initial_regs
[7] = 0x05
138 self
.run_tst_program(Program(lst
), initial_regs
, {})
140 def test_extsb(self
):
141 insns
= ["extsb", "extsh", "extsw"]
143 choice
= random
.choice(insns
)
144 lst
= [f
"{choice} 3, 1"]
146 initial_regs
= [0] * 32
147 initial_regs
[1] = random
.randint(0, (1<<64)-1)
148 self
.run_tst_program(Program(lst
), initial_regs
)
150 def test_cmpeqb(self
):
151 lst
= ["cmpeqb cr0, 1, 2"]
153 initial_regs
= [0] * 32
155 initial_regs
[2] = 0x01030507090b0d0f11
156 self
.run_tst_program(Program(lst
), initial_regs
, {})
158 def test_ilang(self
):
159 pspec
= ALUPipeSpec(id_wid
=2)
160 alu
= ALUBasePipe(pspec
)
161 vl
= rtlil
.convert(alu
, ports
=alu
.ports())
162 with
open("alu_pipeline.il", "w") as f
:
166 class TestRunner(FHDLTestCase
):
167 def __init__(self
, test_data
):
168 super().__init
__("run_all")
169 self
.test_data
= test_data
174 instruction
= Signal(32)
176 pdecode
= create_pdecode()
178 m
.submodules
.pdecode2
= pdecode2
= PowerDecode2(pdecode
)
180 pspec
= ALUPipeSpec(id_wid
=2)
181 m
.submodules
.alu
= alu
= ALUBasePipe(pspec
)
183 comb
+= alu
.p
.data_i
.ctx
.op
.eq_from_execute1(pdecode2
.e
)
184 comb
+= alu
.p
.valid_i
.eq(1)
185 comb
+= alu
.n
.ready_i
.eq(1)
186 comb
+= pdecode2
.dec
.raw_opcode_in
.eq(instruction
)
191 for test
in self
.test_data
:
193 program
= test
.program
194 self
.subTest(test
.name
)
195 simulator
= ISA(pdecode2
, test
.regs
, test
.sprs
, 0)
196 gen
= program
.generate_instructions()
197 instructions
= list(zip(gen
, program
.assembly
.splitlines()))
199 index
= simulator
.pc
.CIA
.value
//4
200 while index
< len(instructions
):
201 ins
, code
= instructions
[index
]
203 print("0x{:X}".format(ins
& 0xffffffff))
206 # ask the decoder to decode this binary data (endian'd)
207 yield pdecode2
.dec
.bigendian
.eq(0) # little / big?
208 yield instruction
.eq(ins
) # raw binary instr.
210 fn_unit
= yield pdecode2
.e
.fn_unit
211 self
.assertEqual(fn_unit
, Function
.ALU
.value
)
212 yield from set_alu_inputs(alu
, pdecode2
, simulator
)
213 yield from set_extra_alu_inputs(alu
, pdecode2
, simulator
)
215 opname
= code
.split(' ')[0]
216 yield from simulator
.call(opname
)
217 index
= simulator
.pc
.CIA
.value
//4
219 vld
= yield alu
.n
.valid_o
222 vld
= yield alu
.n
.valid_o
224 alu_out
= yield alu
.n
.data_o
.o
225 out_reg_valid
= yield pdecode2
.e
.write_reg
.ok
227 write_reg_idx
= yield pdecode2
.e
.write_reg
.data
228 expected
= simulator
.gpr(write_reg_idx
).value
229 print(f
"expected {expected:x}, actual: {alu_out:x}")
230 self
.assertEqual(expected
, alu_out
, code
)
231 yield from self
.check_extra_alu_outputs(alu
, pdecode2
,
234 sim
.add_sync_process(process
)
235 with sim
.write_vcd("simulator.vcd", "simulator.gtkw",
239 def check_extra_alu_outputs(self
, alu
, dec2
, sim
, code
):
240 rc
= yield dec2
.e
.rc
.data
242 cr_expected
= sim
.crl
[0].get_range().value
243 cr_actual
= yield alu
.n
.data_o
.cr0
.data
244 self
.assertEqual(cr_expected
, cr_actual
, code
)
246 op
= yield dec2
.e
.insn_type
247 if op
== InternalOp
.OP_CMP
.value
or \
248 op
== InternalOp
.OP_CMPEQB
.value
:
249 bf
= yield dec2
.dec
.BF
250 cr_actual
= yield alu
.n
.data_o
.cr0
.data
251 cr_expected
= sim
.crl
[bf
].get_range().value
252 self
.assertEqual(cr_expected
, cr_actual
, code
)
254 cry_out
= yield dec2
.e
.output_carry
256 expected_carry
= 1 if sim
.spr
['XER'][XER_bits
['CA']] else 0
257 real_carry
= yield alu
.n
.data_o
.xer_ca
.data
[0] # XXX CO not CO32
258 self
.assertEqual(expected_carry
, real_carry
, code
)
259 expected_carry32
= 1 if sim
.spr
['XER'][XER_bits
['CA32']] else 0
260 real_carry32
= yield alu
.n
.data_o
.xer_ca
.data
[1] # XXX CO32
261 self
.assertEqual(expected_carry32
, real_carry32
, code
)
265 if __name__
== "__main__":
266 unittest
.main(exit
=False)
267 suite
= unittest
.TestSuite()
268 suite
.addTest(TestRunner(test_data
))
270 runner
= unittest
.TextTestRunner()