c14f6a08402f6a89b4aac83ce3f3763ba697c1b5
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
15 from soc
.fu
.logical
.pipeline
import LogicalBasePipe
16 from soc
.fu
.alu
.pipe_data
import LogicalPipeSpec
21 def __init__(self
, program
, regs
, sprs
, name
):
22 self
.program
= program
28 def get_rec_width(rec
):
30 # Setup random inputs for dut.op
37 def set_alu_inputs(alu
, dec2
, sim
):
38 # TODO: see https://bugs.libre-soc.org/show_bug.cgi?id=305#c43
39 # detect the immediate here (with m.If(self.i.ctx.op.imm_data.imm_ok))
40 # and place it into data_i.b
42 reg3_ok
= yield dec2
.e
.read_reg3
.ok
43 reg1_ok
= yield dec2
.e
.read_reg1
.ok
44 assert reg3_ok
!= reg1_ok
46 data1
= yield dec2
.e
.read_reg3
.data
47 data1
= sim
.gpr(data1
).value
49 data1
= yield dec2
.e
.read_reg1
.data
50 data1
= sim
.gpr(data1
).value
54 yield alu
.p
.data_i
.a
.eq(data1
)
56 # If there's an immediate, set the B operand to that
57 reg2_ok
= yield dec2
.e
.read_reg2
.ok
58 imm_ok
= yield dec2
.e
.imm_data
.imm_ok
60 data2
= yield dec2
.e
.imm_data
.imm
62 data2
= yield dec2
.e
.read_reg2
.data
63 data2
= sim
.gpr(data2
).value
66 yield alu
.p
.data_i
.b
.eq(data2
)
69 def set_extra_alu_inputs(alu
, dec2
, sim
):
70 carry
= 1 if sim
.spr
['XER'][XER_bits
['CA']] else 0
71 carry32
= 1 if sim
.spr
['XER'][XER_bits
['CA32']] else 0
72 yield alu
.p
.data_i
.xer_ca
[0].eq(carry
)
73 yield alu
.p
.data_i
.xer_ca
[1].eq(carry32
)
74 so
= 1 if sim
.spr
['XER'][XER_bits
['SO']] else 0
75 yield alu
.p
.data_i
.xer_so
.eq(so
)
78 # This test bench is a bit different than is usual. Initially when I
79 # was writing it, I had all of the tests call a function to create a
80 # device under test and simulator, initialize the dut, run the
81 # simulation for ~2 cycles, and assert that the dut output what it
82 # should have. However, this was really slow, since it needed to
83 # create and tear down the dut and simulator for every test case.
85 # Now, instead of doing that, every test case in ALUTestCase puts some
86 # data into the test_data list below, describing the instructions to
87 # be tested and the initial state. Once all the tests have been run,
88 # test_data gets passed to TestRunner which then sets up the DUT and
89 # simulator once, runs all the data through it, and asserts that the
90 # results match the pseudocode sim at every cycle.
92 # By doing this, I've reduced the time it takes to run the test suite
93 # massively. Before, it took around 1 minute on my computer, now it
94 # takes around 3 seconds
99 class LogicalTestCase(FHDLTestCase
):
100 def __init__(self
, name
):
101 super().__init
__(name
)
102 self
.test_name
= name
104 def run_tst_program(self
, prog
, initial_regs
=[0] * 32, initial_sprs
={}):
105 tc
= TestCase(prog
, initial_regs
, initial_sprs
, self
.test_name
)
109 insns
= ["and", "or", "xor"]
111 choice
= random
.choice(insns
)
112 lst
= [f
"{choice} 3, 1, 2"]
113 initial_regs
= [0] * 32
114 initial_regs
[1] = random
.randint(0, (1 << 64)-1)
115 initial_regs
[2] = random
.randint(0, (1 << 64)-1)
116 self
.run_tst_program(Program(lst
), initial_regs
)
118 def test_rand_imm_logical(self
):
119 insns
= ["andi.", "andis.", "ori", "oris", "xori", "xoris"]
121 choice
= random
.choice(insns
)
122 imm
= random
.randint(0, (1 << 16)-1)
123 lst
= [f
"{choice} 3, 1, {imm}"]
125 initial_regs
= [0] * 32
126 initial_regs
[1] = random
.randint(0, (1 << 64)-1)
127 self
.run_tst_program(Program(lst
), initial_regs
)
130 insns
= ["cntlzd", "cnttzd", "cntlzw", "cnttzw"]
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_parity(self
):
140 insns
= ["prtyw", "prtyd"]
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(self
):
150 insns
= ["popcntb", "popcntw", "popcntd"]
152 choice
= random
.choice(insns
)
153 lst
= [f
"{choice} 3, 1"]
155 initial_regs
= [0] * 32
156 initial_regs
[1] = random
.randint(0, (1 << 64)-1)
157 self
.run_tst_program(Program(lst
), initial_regs
)
159 def test_popcnt_edge(self
):
160 insns
= ["popcntb", "popcntw", "popcntd"]
162 lst
= [f
"{choice} 3, 1"]
163 initial_regs
= [0] * 32
165 self
.run_tst_program(Program(lst
), initial_regs
)
168 lst
= ["cmpb 3, 1, 2"]
169 initial_regs
= [0] * 32
170 initial_regs
[1] = 0xdeadbeefcafec0de
171 initial_regs
[2] = 0xd0adb0000afec1de
172 self
.run_tst_program(Program(lst
), initial_regs
)
174 def test_bpermd(self
):
175 lst
= ["bpermd 3, 1, 2"]
177 initial_regs
= [0] * 32
178 initial_regs
[1] = 1<<random
.randint(0,63)
179 initial_regs
[2] = 0xdeadbeefcafec0de
180 self
.run_tst_program(Program(lst
), initial_regs
)
182 def test_ilang(self
):
183 rec
= LogicalPipeSpec
.opsubsetkls()
185 pspec
= LogicalPipeSpec(id_wid
=2, op_wid
=get_rec_width(rec
))
186 alu
= LogicalBasePipe(pspec
)
187 vl
= rtlil
.convert(alu
, ports
=alu
.ports())
188 with
open("logical_pipeline.il", "w") as f
:
192 class TestRunner(FHDLTestCase
):
193 def __init__(self
, test_data
):
194 super().__init
__("run_all")
195 self
.test_data
= test_data
200 instruction
= Signal(32)
202 pdecode
= create_pdecode()
204 m
.submodules
.pdecode2
= pdecode2
= PowerDecode2(pdecode
)
206 rec
= LogicalPipeSpec
.opsubsetkls()
208 pspec
= LogicalPipeSpec(id_wid
=2, op_wid
=get_rec_width(rec
))
209 m
.submodules
.alu
= alu
= LogicalBasePipe(pspec
)
211 comb
+= alu
.p
.data_i
.ctx
.op
.eq_from_execute1(pdecode2
.e
)
212 comb
+= alu
.p
.valid_i
.eq(1)
213 comb
+= alu
.n
.ready_i
.eq(1)
214 comb
+= pdecode2
.dec
.raw_opcode_in
.eq(instruction
)
220 for test
in self
.test_data
:
222 program
= test
.program
223 self
.subTest(test
.name
)
224 simulator
= ISA(pdecode2
, test
.regs
, test
.sprs
, 0)
225 gen
= program
.generate_instructions()
226 instructions
= list(zip(gen
, program
.assembly
.splitlines()))
228 index
= simulator
.pc
.CIA
.value
//4
229 while index
< len(instructions
):
230 ins
, code
= instructions
[index
]
232 print("0x{:X}".format(ins
& 0xffffffff))
235 # ask the decoder to decode this binary data (endian'd)
236 yield pdecode2
.dec
.bigendian
.eq(0) # little / big?
237 yield instruction
.eq(ins
) # raw binary instr.
239 fn_unit
= yield pdecode2
.e
.fn_unit
240 self
.assertEqual(fn_unit
, Function
.LOGICAL
.value
, code
)
241 yield from set_alu_inputs(alu
, pdecode2
, simulator
)
242 yield from set_extra_alu_inputs(alu
, pdecode2
, simulator
)
244 opname
= code
.split(' ')[0]
245 yield from simulator
.call(opname
)
246 index
= simulator
.pc
.CIA
.value
//4
248 vld
= yield alu
.n
.valid_o
251 vld
= yield alu
.n
.valid_o
253 alu_out
= yield alu
.n
.data_o
.o
254 out_reg_valid
= yield pdecode2
.e
.write_reg
.ok
256 write_reg_idx
= yield pdecode2
.e
.write_reg
.data
257 expected
= simulator
.gpr(write_reg_idx
).value
258 print(f
"expected {expected:x}, actual: {alu_out:x}")
259 self
.assertEqual(expected
, alu_out
, code
)
260 yield from self
.check_extra_alu_outputs(alu
, pdecode2
,
263 sim
.add_sync_process(process
)
264 with sim
.write_vcd("simulator.vcd", "simulator.gtkw",
268 def check_extra_alu_outputs(self
, alu
, dec2
, sim
, code
):
269 rc
= yield dec2
.e
.rc
.data
271 cr_expected
= sim
.crl
[0].get_range().value
272 cr_actual
= yield alu
.n
.data_o
.cr0
.data
273 self
.assertEqual(cr_expected
, cr_actual
, code
)
276 if __name__
== "__main__":
277 unittest
.main(exit
=False)
278 suite
= unittest
.TestSuite()
279 suite
.addTest(TestRunner(test_data
))
281 runner
= unittest
.TextTestRunner()