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
)
10 from soc
.decoder
.selectable_int
import SelectableInt
11 from soc
.simulator
.program
import Program
12 from soc
.decoder
.isa
.all
import ISA
14 from soc
.fu
.div
.pipeline
import DivBasePipe
15 from soc
.fu
.div
.pipe_data
import DivPipeSpec
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
)
59 def set_extra_alu_inputs(alu
, dec2
, sim
):
60 carry
= 1 if sim
.spr
['XER'][XER_bits
['CA']] else 0
61 carry32
= 1 if sim
.spr
['XER'][XER_bits
['CA32']] else 0
62 yield alu
.p
.data_i
.xer_ca
[0].eq(carry
)
63 yield alu
.p
.data_i
.xer_ca
[1].eq(carry32
)
64 so
= 1 if sim
.spr
['XER'][XER_bits
['SO']] else 0
65 yield alu
.p
.data_i
.xer_so
.eq(so
)
68 # This test bench is a bit different than is usual. Initially when I
69 # was writing it, I had all of the tests call a function to create a
70 # device under test and simulator, initialize the dut, run the
71 # simulation for ~2 cycles, and assert that the dut output what it
72 # should have. However, this was really slow, since it needed to
73 # create and tear down the dut and simulator for every test case.
75 # Now, instead of doing that, every test case in ALUTestCase puts some
76 # data into the test_data list below, describing the instructions to
77 # be tested and the initial state. Once all the tests have been run,
78 # test_data gets passed to TestRunner which then sets up the DUT and
79 # simulator once, runs all the data through it, and asserts that the
80 # results match the pseudocode sim at every cycle.
82 # By doing this, I've reduced the time it takes to run the test suite
83 # massively. Before, it took around 1 minute on my computer, now it
84 # takes around 3 seconds
89 class DivTestCase(FHDLTestCase
):
90 def __init__(self
, name
):
91 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
= ["and", "or", "xor"]
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_logical(self
):
109 insns
= ["andi.", "andis.", "ori", "oris", "xori", "xoris"]
111 choice
= random
.choice(insns
)
112 imm
= random
.randint(0, (1 << 16)-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 insns
= ["cntlzd", "cnttzd", "cntlzw", "cnttzw"]
122 choice
= random
.choice(insns
)
123 lst
= [f
"{choice} 3, 1"]
125 initial_regs
= [0] * 32
126 initial_regs
[1] = random
.randint(0, (1 << 64)-1)
127 self
.run_tst_program(Program(lst
), initial_regs
)
129 def test_parity(self
):
130 insns
= ["prtyw", "prtyd"]
132 choice
= random
.choice(insns
)
133 lst
= [f
"{choice} 3, 1"]
135 initial_regs
= [0] * 32
136 initial_regs
[1] = random
.randint(0, (1 << 64)-1)
137 self
.run_tst_program(Program(lst
), initial_regs
)
139 def test_popcnt(self
):
140 insns
= ["popcntb", "popcntw", "popcntd"]
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_popcnt_edge(self
):
150 insns
= ["popcntb", "popcntw", "popcntd"]
152 lst
= [f
"{choice} 3, 1"]
153 initial_regs
= [0] * 32
155 self
.run_tst_program(Program(lst
), initial_regs
)
158 lst
= ["cmpb 3, 1, 2"]
159 initial_regs
= [0] * 32
160 initial_regs
[1] = 0xdeadbeefcafec0de
161 initial_regs
[2] = 0xd0adb0000afec1de
162 self
.run_tst_program(Program(lst
), initial_regs
)
164 def test_bpermd(self
):
165 lst
= ["bpermd 3, 1, 2"]
167 initial_regs
= [0] * 32
168 initial_regs
[1] = 1<<random
.randint(0,63)
169 initial_regs
[2] = 0xdeadbeefcafec0de
170 self
.run_tst_program(Program(lst
), initial_regs
)
172 def test_ilang(self
):
173 pspec
= DivPipeSpec(id_wid
=2)
174 alu
= DivBasePipe(pspec
)
175 vl
= rtlil
.convert(alu
, ports
=alu
.ports())
176 with
open("logical_pipeline.il", "w") as f
:
180 class TestRunner(FHDLTestCase
):
181 def __init__(self
, test_data
):
182 super().__init
__("run_all")
183 self
.test_data
= test_data
188 instruction
= Signal(32)
190 pdecode
= create_pdecode()
192 m
.submodules
.pdecode2
= pdecode2
= PowerDecode2(pdecode
)
194 pspec
= DivPipeSpec(id_wid
=2)
195 m
.submodules
.alu
= alu
= DivBasePipe(pspec
)
197 comb
+= alu
.p
.data_i
.ctx
.op
.eq_from_execute1(pdecode2
.e
)
198 comb
+= alu
.p
.valid_i
.eq(1)
199 comb
+= alu
.n
.ready_i
.eq(1)
200 comb
+= pdecode2
.dec
.raw_opcode_in
.eq(instruction
)
206 for test
in self
.test_data
:
208 program
= test
.program
209 self
.subTest(test
.name
)
210 simulator
= ISA(pdecode2
, test
.regs
, test
.sprs
, 0)
211 gen
= program
.generate_instructions()
212 instructions
= list(zip(gen
, program
.assembly
.splitlines()))
214 index
= simulator
.pc
.CIA
.value
//4
215 while index
< len(instructions
):
216 ins
, code
= instructions
[index
]
218 print("0x{:X}".format(ins
& 0xffffffff))
221 # ask the decoder to decode this binary data (endian'd)
222 yield pdecode2
.dec
.bigendian
.eq(0) # little / big?
223 yield instruction
.eq(ins
) # raw binary instr.
225 fn_unit
= yield pdecode2
.e
.fn_unit
226 self
.assertEqual(fn_unit
, Function
.LOGICAL
.value
, code
)
227 yield from set_alu_inputs(alu
, pdecode2
, simulator
)
228 yield from set_extra_alu_inputs(alu
, pdecode2
, simulator
)
230 opname
= code
.split(' ')[0]
231 yield from simulator
.call(opname
)
232 index
= simulator
.pc
.CIA
.value
//4
234 vld
= yield alu
.n
.valid_o
237 vld
= yield alu
.n
.valid_o
239 alu_out
= yield alu
.n
.data_o
.o
240 out_reg_valid
= yield pdecode2
.e
.write_reg
.ok
242 write_reg_idx
= yield pdecode2
.e
.write_reg
.data
243 expected
= simulator
.gpr(write_reg_idx
).value
244 print(f
"expected {expected:x}, actual: {alu_out:x}")
245 self
.assertEqual(expected
, alu_out
, code
)
246 yield from self
.check_extra_alu_outputs(alu
, pdecode2
,
249 sim
.add_sync_process(process
)
250 with sim
.write_vcd("simulator.vcd", "simulator.gtkw",
254 def check_extra_alu_outputs(self
, alu
, dec2
, sim
, code
):
255 rc
= yield dec2
.e
.rc
.data
257 cr_expected
= sim
.crl
[0].get_range().value
258 cr_actual
= yield alu
.n
.data_o
.cr0
.data
259 self
.assertEqual(cr_expected
, cr_actual
, code
)
262 if __name__
== "__main__":
263 unittest
.main(exit
=False)
264 suite
= unittest
.TestSuite()
265 suite
.addTest(TestRunner(test_data
))
267 runner
= unittest
.TextTestRunner()