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
.logical
.pipeline
import LogicalBasePipe
15 from soc
.fu
.logical
.pipe_data
import LogicalPipeSpec
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
)
66 # This test bench is a bit different than is usual. Initially when I
67 # was writing it, I had all of the tests call a function to create a
68 # device under test and simulator, initialize the dut, run the
69 # simulation for ~2 cycles, and assert that the dut output what it
70 # should have. However, this was really slow, since it needed to
71 # create and tear down the dut and simulator for every test case.
73 # Now, instead of doing that, every test case in ALUTestCase puts some
74 # data into the test_data list below, describing the instructions to
75 # be tested and the initial state. Once all the tests have been run,
76 # test_data gets passed to TestRunner which then sets up the DUT and
77 # simulator once, runs all the data through it, and asserts that the
78 # results match the pseudocode sim at every cycle.
80 # By doing this, I've reduced the time it takes to run the test suite
81 # massively. Before, it took around 1 minute on my computer, now it
82 # takes around 3 seconds
87 class LogicalTestCase(FHDLTestCase
):
88 def __init__(self
, name
):
89 super().__init
__(name
)
92 def run_tst_program(self
, prog
, initial_regs
=[0] * 32, initial_sprs
={}):
93 tc
= TestCase(prog
, initial_regs
, initial_sprs
, self
.test_name
)
97 insns
= ["and", "or", "xor"]
99 choice
= random
.choice(insns
)
100 lst
= [f
"{choice} 3, 1, 2"]
101 initial_regs
= [0] * 32
102 initial_regs
[1] = random
.randint(0, (1 << 64)-1)
103 initial_regs
[2] = random
.randint(0, (1 << 64)-1)
104 self
.run_tst_program(Program(lst
), initial_regs
)
106 def test_rand_imm_logical(self
):
107 insns
= ["andi.", "andis.", "ori", "oris", "xori", "xoris"]
109 choice
= random
.choice(insns
)
110 imm
= random
.randint(0, (1 << 16)-1)
111 lst
= [f
"{choice} 3, 1, {imm}"]
113 initial_regs
= [0] * 32
114 initial_regs
[1] = random
.randint(0, (1 << 64)-1)
115 self
.run_tst_program(Program(lst
), initial_regs
)
118 insns
= ["cntlzd", "cnttzd", "cntlzw", "cnttzw"]
120 choice
= random
.choice(insns
)
121 lst
= [f
"{choice} 3, 1"]
123 initial_regs
= [0] * 32
124 initial_regs
[1] = random
.randint(0, (1 << 64)-1)
125 self
.run_tst_program(Program(lst
), initial_regs
)
127 def test_parity(self
):
128 insns
= ["prtyw", "prtyd"]
130 choice
= random
.choice(insns
)
131 lst
= [f
"{choice} 3, 1"]
133 initial_regs
= [0] * 32
134 initial_regs
[1] = random
.randint(0, (1 << 64)-1)
135 self
.run_tst_program(Program(lst
), initial_regs
)
137 def test_popcnt(self
):
138 insns
= ["popcntb", "popcntw", "popcntd"]
140 choice
= random
.choice(insns
)
141 lst
= [f
"{choice} 3, 1"]
143 initial_regs
= [0] * 32
144 initial_regs
[1] = random
.randint(0, (1 << 64)-1)
145 self
.run_tst_program(Program(lst
), initial_regs
)
147 def test_popcnt_edge(self
):
148 insns
= ["popcntb", "popcntw", "popcntd"]
150 lst
= [f
"{choice} 3, 1"]
151 initial_regs
= [0] * 32
153 self
.run_tst_program(Program(lst
), initial_regs
)
156 lst
= ["cmpb 3, 1, 2"]
157 initial_regs
= [0] * 32
158 initial_regs
[1] = 0xdeadbeefcafec0de
159 initial_regs
[2] = 0xd0adb0000afec1de
160 self
.run_tst_program(Program(lst
), initial_regs
)
162 def test_bpermd(self
):
163 lst
= ["bpermd 3, 1, 2"]
165 initial_regs
= [0] * 32
166 initial_regs
[1] = 1<<random
.randint(0,63)
167 initial_regs
[2] = 0xdeadbeefcafec0de
168 self
.run_tst_program(Program(lst
), initial_regs
)
170 def test_ilang(self
):
171 pspec
= LogicalPipeSpec(id_wid
=2)
172 alu
= LogicalBasePipe(pspec
)
173 vl
= rtlil
.convert(alu
, ports
=alu
.ports())
174 with
open("logical_pipeline.il", "w") as f
:
178 class TestRunner(FHDLTestCase
):
179 def __init__(self
, test_data
):
180 super().__init
__("run_all")
181 self
.test_data
= test_data
186 instruction
= Signal(32)
188 pdecode
= create_pdecode()
190 m
.submodules
.pdecode2
= pdecode2
= PowerDecode2(pdecode
)
192 pspec
= LogicalPipeSpec(id_wid
=2)
193 m
.submodules
.alu
= alu
= LogicalBasePipe(pspec
)
195 comb
+= alu
.p
.data_i
.ctx
.op
.eq_from_execute1(pdecode2
.e
)
196 comb
+= alu
.p
.valid_i
.eq(1)
197 comb
+= alu
.n
.ready_i
.eq(1)
198 comb
+= pdecode2
.dec
.raw_opcode_in
.eq(instruction
)
204 for test
in self
.test_data
:
206 program
= test
.program
207 self
.subTest(test
.name
)
208 simulator
= ISA(pdecode2
, test
.regs
, test
.sprs
, 0)
209 gen
= program
.generate_instructions()
210 instructions
= list(zip(gen
, program
.assembly
.splitlines()))
212 index
= simulator
.pc
.CIA
.value
//4
213 while index
< len(instructions
):
214 ins
, code
= instructions
[index
]
216 print("0x{:X}".format(ins
& 0xffffffff))
219 # ask the decoder to decode this binary data (endian'd)
220 yield pdecode2
.dec
.bigendian
.eq(0) # little / big?
221 yield instruction
.eq(ins
) # raw binary instr.
223 fn_unit
= yield pdecode2
.e
.fn_unit
224 self
.assertEqual(fn_unit
, Function
.LOGICAL
.value
, code
)
225 yield from set_alu_inputs(alu
, pdecode2
, simulator
)
226 yield from set_extra_alu_inputs(alu
, pdecode2
, simulator
)
228 opname
= code
.split(' ')[0]
229 yield from simulator
.call(opname
)
230 index
= simulator
.pc
.CIA
.value
//4
232 vld
= yield alu
.n
.valid_o
235 vld
= yield alu
.n
.valid_o
237 alu_out
= yield alu
.n
.data_o
.o
.data
238 out_reg_valid
= yield pdecode2
.e
.write_reg
.ok
240 write_reg_idx
= yield pdecode2
.e
.write_reg
.data
241 expected
= simulator
.gpr(write_reg_idx
).value
242 print(f
"expected {expected:x}, actual: {alu_out:x}")
243 self
.assertEqual(expected
, alu_out
, code
)
244 yield from self
.check_extra_alu_outputs(alu
, pdecode2
,
247 sim
.add_sync_process(process
)
248 with sim
.write_vcd("simulator.vcd", "simulator.gtkw",
252 def check_extra_alu_outputs(self
, alu
, dec2
, sim
, code
):
253 rc
= yield dec2
.e
.rc
.data
255 cr_expected
= sim
.crl
[0].get_range().value
256 cr_actual
= yield alu
.n
.data_o
.cr0
.data
257 self
.assertEqual(cr_expected
, cr_actual
, code
)
260 if __name__
== "__main__":
261 unittest
.main(exit
=False)
262 suite
= unittest
.TestSuite()
263 suite
.addTest(TestRunner(test_data
))
265 runner
= unittest
.TextTestRunner()