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 get_rec_width(rec
):
29 # 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
)
68 def set_extra_alu_inputs(alu
, dec2
, sim
):
69 carry
= 1 if sim
.spr
['XER'][XER_bits
['CA']] else 0
70 carry32
= 1 if sim
.spr
['XER'][XER_bits
['CA32']] else 0
71 yield alu
.p
.data_i
.xer_ca
[0].eq(carry
)
72 yield alu
.p
.data_i
.xer_ca
[1].eq(carry32
)
73 so
= 1 if sim
.spr
['XER'][XER_bits
['SO']] else 0
74 yield alu
.p
.data_i
.xer_so
.eq(so
)
77 # This test bench is a bit different than is usual. Initially when I
78 # was writing it, I had all of the tests call a function to create a
79 # device under test and simulator, initialize the dut, run the
80 # simulation for ~2 cycles, and assert that the dut output what it
81 # should have. However, this was really slow, since it needed to
82 # create and tear down the dut and simulator for every test case.
84 # Now, instead of doing that, every test case in ALUTestCase puts some
85 # data into the test_data list below, describing the instructions to
86 # be tested and the initial state. Once all the tests have been run,
87 # test_data gets passed to TestRunner which then sets up the DUT and
88 # simulator once, runs all the data through it, and asserts that the
89 # results match the pseudocode sim at every cycle.
91 # By doing this, I've reduced the time it takes to run the test suite
92 # massively. Before, it took around 1 minute on my computer, now it
93 # takes around 3 seconds
98 class LogicalTestCase(FHDLTestCase
):
99 def __init__(self
, name
):
100 super().__init
__(name
)
101 self
.test_name
= name
103 def run_tst_program(self
, prog
, initial_regs
=[0] * 32, initial_sprs
={}):
104 tc
= TestCase(prog
, initial_regs
, initial_sprs
, self
.test_name
)
108 insns
= ["and", "or", "xor"]
110 choice
= random
.choice(insns
)
111 lst
= [f
"{choice} 3, 1, 2"]
112 initial_regs
= [0] * 32
113 initial_regs
[1] = random
.randint(0, (1 << 64)-1)
114 initial_regs
[2] = random
.randint(0, (1 << 64)-1)
115 self
.run_tst_program(Program(lst
), initial_regs
)
117 def test_rand_imm_logical(self
):
118 insns
= ["andi.", "andis.", "ori", "oris", "xori", "xoris"]
120 choice
= random
.choice(insns
)
121 imm
= random
.randint(0, (1 << 16)-1)
122 lst
= [f
"{choice} 3, 1, {imm}"]
124 initial_regs
= [0] * 32
125 initial_regs
[1] = random
.randint(0, (1 << 64)-1)
126 self
.run_tst_program(Program(lst
), initial_regs
)
129 insns
= ["cntlzd", "cnttzd", "cntlzw", "cnttzw"]
131 choice
= random
.choice(insns
)
132 lst
= [f
"{choice} 3, 1"]
134 initial_regs
= [0] * 32
135 initial_regs
[1] = random
.randint(0, (1 << 64)-1)
136 self
.run_tst_program(Program(lst
), initial_regs
)
138 def test_parity(self
):
139 insns
= ["prtyw", "prtyd"]
141 choice
= random
.choice(insns
)
142 lst
= [f
"{choice} 3, 1"]
144 initial_regs
= [0] * 32
145 initial_regs
[1] = random
.randint(0, (1 << 64)-1)
146 self
.run_tst_program(Program(lst
), initial_regs
)
148 def test_popcnt(self
):
149 insns
= ["popcntb", "popcntw", "popcntd"]
151 choice
= random
.choice(insns
)
152 lst
= [f
"{choice} 3, 1"]
154 initial_regs
= [0] * 32
155 initial_regs
[1] = random
.randint(0, (1 << 64)-1)
156 self
.run_tst_program(Program(lst
), initial_regs
)
158 def test_popcnt_edge(self
):
159 insns
= ["popcntb", "popcntw", "popcntd"]
161 lst
= [f
"{choice} 3, 1"]
162 initial_regs
= [0] * 32
164 self
.run_tst_program(Program(lst
), initial_regs
)
167 lst
= ["cmpb 3, 1, 2"]
168 initial_regs
= [0] * 32
169 initial_regs
[1] = 0xdeadbeefcafec0de
170 initial_regs
[2] = 0xd0adb0000afec1de
171 self
.run_tst_program(Program(lst
), initial_regs
)
173 def test_bpermd(self
):
174 lst
= ["bpermd 3, 1, 2"]
176 initial_regs
= [0] * 32
177 initial_regs
[1] = 1<<random
.randint(0,63)
178 initial_regs
[2] = 0xdeadbeefcafec0de
179 self
.run_tst_program(Program(lst
), initial_regs
)
181 def test_ilang(self
):
182 rec
= LogicalPipeSpec
.opsubsetkls()
184 pspec
= LogicalPipeSpec(id_wid
=2, op_wid
=get_rec_width(rec
))
185 alu
= LogicalBasePipe(pspec
)
186 vl
= rtlil
.convert(alu
, ports
=alu
.ports())
187 with
open("logical_pipeline.il", "w") as f
:
191 class TestRunner(FHDLTestCase
):
192 def __init__(self
, test_data
):
193 super().__init
__("run_all")
194 self
.test_data
= test_data
199 instruction
= Signal(32)
201 pdecode
= create_pdecode()
203 m
.submodules
.pdecode2
= pdecode2
= PowerDecode2(pdecode
)
205 rec
= LogicalPipeSpec
.opsubsetkls()
207 pspec
= LogicalPipeSpec(id_wid
=2, op_wid
=get_rec_width(rec
))
208 m
.submodules
.alu
= alu
= LogicalBasePipe(pspec
)
210 comb
+= alu
.p
.data_i
.ctx
.op
.eq_from_execute1(pdecode2
.e
)
211 comb
+= alu
.p
.valid_i
.eq(1)
212 comb
+= alu
.n
.ready_i
.eq(1)
213 comb
+= pdecode2
.dec
.raw_opcode_in
.eq(instruction
)
219 for test
in self
.test_data
:
221 program
= test
.program
222 self
.subTest(test
.name
)
223 simulator
= ISA(pdecode2
, test
.regs
, test
.sprs
, 0)
224 gen
= program
.generate_instructions()
225 instructions
= list(zip(gen
, program
.assembly
.splitlines()))
227 index
= simulator
.pc
.CIA
.value
//4
228 while index
< len(instructions
):
229 ins
, code
= instructions
[index
]
231 print("0x{:X}".format(ins
& 0xffffffff))
234 # ask the decoder to decode this binary data (endian'd)
235 yield pdecode2
.dec
.bigendian
.eq(0) # little / big?
236 yield instruction
.eq(ins
) # raw binary instr.
238 fn_unit
= yield pdecode2
.e
.fn_unit
239 self
.assertEqual(fn_unit
, Function
.LOGICAL
.value
, code
)
240 yield from set_alu_inputs(alu
, pdecode2
, simulator
)
241 yield from set_extra_alu_inputs(alu
, pdecode2
, simulator
)
243 opname
= code
.split(' ')[0]
244 yield from simulator
.call(opname
)
245 index
= simulator
.pc
.CIA
.value
//4
247 vld
= yield alu
.n
.valid_o
250 vld
= yield alu
.n
.valid_o
252 alu_out
= yield alu
.n
.data_o
.o
253 out_reg_valid
= yield pdecode2
.e
.write_reg
.ok
255 write_reg_idx
= yield pdecode2
.e
.write_reg
.data
256 expected
= simulator
.gpr(write_reg_idx
).value
257 print(f
"expected {expected:x}, actual: {alu_out:x}")
258 self
.assertEqual(expected
, alu_out
, code
)
259 yield from self
.check_extra_alu_outputs(alu
, pdecode2
,
262 sim
.add_sync_process(process
)
263 with sim
.write_vcd("simulator.vcd", "simulator.gtkw",
267 def check_extra_alu_outputs(self
, alu
, dec2
, sim
, code
):
268 rc
= yield dec2
.e
.rc
.data
270 cr_expected
= sim
.crl
[0].get_range().value
271 cr_actual
= yield alu
.n
.data_o
.cr0
.data
272 self
.assertEqual(cr_expected
, cr_actual
, code
)
275 if __name__
== "__main__":
276 unittest
.main(exit
=False)
277 suite
= unittest
.TestSuite()
278 suite
.addTest(TestRunner(test_data
))
280 runner
= unittest
.TextTestRunner()