d414997e2e96a3e6b341814228ff662d3eea9e64
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
.alu_input_record
import CompALUOpSubset
17 from soc
.fu
.alu
.pipe_data
import ALUPipeSpec
22 def __init__(self
, program
, regs
, sprs
, name
):
23 self
.program
= program
28 def get_rec_width(rec
):
30 # Setup random inputs for dut.op
36 def set_alu_inputs(alu
, dec2
, sim
):
37 # TODO: see https://bugs.libre-soc.org/show_bug.cgi?id=305#c43
38 # detect the immediate here (with m.If(self.i.ctx.op.imm_data.imm_ok))
39 # and place it into data_i.b
41 reg3_ok
= yield dec2
.e
.read_reg3
.ok
42 reg1_ok
= yield dec2
.e
.read_reg1
.ok
43 assert reg3_ok
!= reg1_ok
45 data1
= yield dec2
.e
.read_reg3
.data
46 data1
= sim
.gpr(data1
).value
48 data1
= yield dec2
.e
.read_reg1
.data
49 data1
= sim
.gpr(data1
).value
53 yield alu
.p
.data_i
.a
.eq(data1
)
55 # If there's an immediate, set the B operand to that
56 reg2_ok
= yield dec2
.e
.read_reg2
.ok
57 imm_ok
= yield dec2
.e
.imm_data
.imm_ok
59 data2
= yield dec2
.e
.imm_data
.imm
61 data2
= yield dec2
.e
.read_reg2
.data
62 data2
= sim
.gpr(data2
).value
65 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 yield alu
.p
.data_i
.carry_in
.eq(carry
)
72 so
= 1 if sim
.spr
['XER'][XER_bits
['SO']] else 0
73 yield alu
.p
.data_i
.so
.eq(so
)
76 # This test bench is a bit different than is usual. Initially when I
77 # was writing it, I had all of the tests call a function to create a
78 # device under test and simulator, initialize the dut, run the
79 # simulation for ~2 cycles, and assert that the dut output what it
80 # should have. However, this was really slow, since it needed to
81 # create and tear down the dut and simulator for every test case.
83 # Now, instead of doing that, every test case in ALUTestCase puts some
84 # data into the test_data list below, describing the instructions to
85 # be tested and the initial state. Once all the tests have been run,
86 # test_data gets passed to TestRunner which then sets up the DUT and
87 # simulator once, runs all the data through it, and asserts that the
88 # results match the pseudocode sim at every cycle.
90 # By doing this, I've reduced the time it takes to run the test suite
91 # massively. Before, it took around 1 minute on my computer, now it
92 # takes around 3 seconds
97 class LogicalTestCase(FHDLTestCase
):
98 def __init__(self
, name
):
99 super().__init
__(name
)
100 self
.test_name
= name
101 def run_tst_program(self
, prog
, initial_regs
=[0] * 32, initial_sprs
={}):
102 tc
= TestCase(prog
, initial_regs
, initial_sprs
, self
.test_name
)
106 insns
= ["and", "or", "xor"]
108 choice
= random
.choice(insns
)
109 lst
= [f
"{choice} 3, 1, 2"]
110 initial_regs
= [0] * 32
111 initial_regs
[1] = random
.randint(0, (1<<64)-1)
112 initial_regs
[2] = random
.randint(0, (1<<64)-1)
113 self
.run_tst_program(Program(lst
), initial_regs
)
115 def test_rand_imm_logical(self
):
116 insns
= ["andi.", "andis.", "ori", "oris", "xori", "xoris"]
118 choice
= random
.choice(insns
)
119 imm
= random
.randint(0, (1<<16)-1)
120 lst
= [f
"{choice} 3, 1, {imm}"]
122 initial_regs
= [0] * 32
123 initial_regs
[1] = random
.randint(0, (1<<64)-1)
124 self
.run_tst_program(Program(lst
), initial_regs
)
126 @unittest.skip("broken")
128 insns
= ["cntlzd", "cnttzd"]
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_parity(self
):
138 insns
= ["prtyw", "prtyd"]
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(self
):
148 insns
= ["popcntb", "popcntw", "popcntd"]
150 choice
= random
.choice(insns
)
151 lst
= [f
"{choice} 3, 1"]
153 initial_regs
= [0] * 32
154 initial_regs
[1] = random
.randint(0, (1<<64)-1)
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_ilang(self
):
165 rec
= CompALUOpSubset()
167 pspec
= ALUPipeSpec(id_wid
=2, op_wid
=get_rec_width(rec
))
168 alu
= LogicalBasePipe(pspec
)
169 vl
= rtlil
.convert(alu
, ports
=alu
.ports())
170 with
open("logical_pipeline.il", "w") as f
:
174 class TestRunner(FHDLTestCase
):
175 def __init__(self
, test_data
):
176 super().__init
__("run_all")
177 self
.test_data
= test_data
182 instruction
= Signal(32)
184 pdecode
= create_pdecode()
186 m
.submodules
.pdecode2
= pdecode2
= PowerDecode2(pdecode
)
188 rec
= CompALUOpSubset()
190 pspec
= ALUPipeSpec(id_wid
=2, op_wid
=get_rec_width(rec
))
191 m
.submodules
.alu
= alu
= LogicalBasePipe(pspec
)
193 comb
+= alu
.p
.data_i
.ctx
.op
.eq_from_execute1(pdecode2
.e
)
194 comb
+= alu
.p
.valid_i
.eq(1)
195 comb
+= alu
.n
.ready_i
.eq(1)
196 comb
+= pdecode2
.dec
.raw_opcode_in
.eq(instruction
)
201 for test
in self
.test_data
:
203 program
= test
.program
204 self
.subTest(test
.name
)
205 simulator
= ISA(pdecode2
, test
.regs
, test
.sprs
, 0)
206 gen
= program
.generate_instructions()
207 instructions
= list(zip(gen
, program
.assembly
.splitlines()))
209 index
= simulator
.pc
.CIA
.value
//4
210 while index
< len(instructions
):
211 ins
, code
= instructions
[index
]
213 print("0x{:X}".format(ins
& 0xffffffff))
216 # ask the decoder to decode this binary data (endian'd)
217 yield pdecode2
.dec
.bigendian
.eq(0) # little / big?
218 yield instruction
.eq(ins
) # raw binary instr.
220 fn_unit
= yield pdecode2
.e
.fn_unit
221 self
.assertEqual(fn_unit
, Function
.LOGICAL
.value
, code
)
222 yield from set_alu_inputs(alu
, pdecode2
, simulator
)
223 yield from set_extra_alu_inputs(alu
, pdecode2
, simulator
)
225 opname
= code
.split(' ')[0]
226 yield from simulator
.call(opname
)
227 index
= simulator
.pc
.CIA
.value
//4
229 vld
= yield alu
.n
.valid_o
232 vld
= yield alu
.n
.valid_o
234 alu_out
= yield alu
.n
.data_o
.o
235 out_reg_valid
= yield pdecode2
.e
.write_reg
.ok
237 write_reg_idx
= yield pdecode2
.e
.write_reg
.data
238 expected
= simulator
.gpr(write_reg_idx
).value
239 print(f
"expected {expected:x}, actual: {alu_out:x}")
240 self
.assertEqual(expected
, alu_out
, code
)
241 yield from self
.check_extra_alu_outputs(alu
, pdecode2
,
244 sim
.add_sync_process(process
)
245 with sim
.write_vcd("simulator.vcd", "simulator.gtkw",
248 def check_extra_alu_outputs(self
, alu
, dec2
, sim
):
249 rc
= yield dec2
.e
.rc
.data
251 cr_expected
= sim
.crl
[0].get_range().value
252 cr_actual
= yield alu
.n
.data_o
.cr0
253 self
.assertEqual(cr_expected
, cr_actual
)
256 if __name__
== "__main__":
257 unittest
.main(exit
=False)
258 suite
= unittest
.TestSuite()
259 suite
.addTest(TestRunner(test_data
))
261 runner
= unittest
.TextTestRunner()