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
)
127 insns
= ["cntlzd", "cnttzd", "cntlzw", "cnttzw"]
129 choice
= random
.choice(insns
)
130 lst
= [f
"{choice} 3, 1"]
132 initial_regs
= [0] * 32
133 initial_regs
[1] = random
.randint(0, (1<<64)-1)
134 self
.run_tst_program(Program(lst
), initial_regs
)
136 def test_parity(self
):
137 insns
= ["prtyw", "prtyd"]
139 choice
= random
.choice(insns
)
140 lst
= [f
"{choice} 3, 1"]
142 initial_regs
= [0] * 32
143 initial_regs
[1] = random
.randint(0, (1<<64)-1)
144 self
.run_tst_program(Program(lst
), initial_regs
)
146 def test_popcnt(self
):
147 insns
= ["popcntb", "popcntw", "popcntd"]
149 choice
= random
.choice(insns
)
150 lst
= [f
"{choice} 3, 1"]
152 initial_regs
= [0] * 32
153 initial_regs
[1] = random
.randint(0, (1<<64)-1)
154 self
.run_tst_program(Program(lst
), initial_regs
)
157 lst
= ["cmpb 3, 1, 2"]
158 initial_regs
= [0] * 32
159 initial_regs
[1] = 0xdeadbeefcafec0de
160 initial_regs
[2] = 0xd0adb0000afec1de
161 self
.run_tst_program(Program(lst
), initial_regs
)
163 def test_ilang(self
):
164 rec
= CompALUOpSubset()
166 pspec
= ALUPipeSpec(id_wid
=2, op_wid
=get_rec_width(rec
))
167 alu
= LogicalBasePipe(pspec
)
168 vl
= rtlil
.convert(alu
, ports
=alu
.ports())
169 with
open("logical_pipeline.il", "w") as f
:
173 class TestRunner(FHDLTestCase
):
174 def __init__(self
, test_data
):
175 super().__init
__("run_all")
176 self
.test_data
= test_data
181 instruction
= Signal(32)
183 pdecode
= create_pdecode()
185 m
.submodules
.pdecode2
= pdecode2
= PowerDecode2(pdecode
)
187 rec
= CompALUOpSubset()
189 pspec
= ALUPipeSpec(id_wid
=2, op_wid
=get_rec_width(rec
))
190 m
.submodules
.alu
= alu
= LogicalBasePipe(pspec
)
192 comb
+= alu
.p
.data_i
.ctx
.op
.eq_from_execute1(pdecode2
.e
)
193 comb
+= alu
.p
.valid_i
.eq(1)
194 comb
+= alu
.n
.ready_i
.eq(1)
195 comb
+= pdecode2
.dec
.raw_opcode_in
.eq(instruction
)
200 for test
in self
.test_data
:
202 program
= test
.program
203 self
.subTest(test
.name
)
204 simulator
= ISA(pdecode2
, test
.regs
, test
.sprs
, 0)
205 gen
= program
.generate_instructions()
206 instructions
= list(zip(gen
, program
.assembly
.splitlines()))
208 index
= simulator
.pc
.CIA
.value
//4
209 while index
< len(instructions
):
210 ins
, code
= instructions
[index
]
212 print("0x{:X}".format(ins
& 0xffffffff))
215 # ask the decoder to decode this binary data (endian'd)
216 yield pdecode2
.dec
.bigendian
.eq(0) # little / big?
217 yield instruction
.eq(ins
) # raw binary instr.
219 fn_unit
= yield pdecode2
.e
.fn_unit
220 self
.assertEqual(fn_unit
, Function
.LOGICAL
.value
, code
)
221 yield from set_alu_inputs(alu
, pdecode2
, simulator
)
222 yield from set_extra_alu_inputs(alu
, pdecode2
, simulator
)
224 opname
= code
.split(' ')[0]
225 yield from simulator
.call(opname
)
226 index
= simulator
.pc
.CIA
.value
//4
228 vld
= yield alu
.n
.valid_o
231 vld
= yield alu
.n
.valid_o
233 alu_out
= yield alu
.n
.data_o
.o
234 out_reg_valid
= yield pdecode2
.e
.write_reg
.ok
236 write_reg_idx
= yield pdecode2
.e
.write_reg
.data
237 expected
= simulator
.gpr(write_reg_idx
).value
238 print(f
"expected {expected:x}, actual: {alu_out:x}")
239 self
.assertEqual(expected
, alu_out
, code
)
240 yield from self
.check_extra_alu_outputs(alu
, pdecode2
,
243 sim
.add_sync_process(process
)
244 with sim
.write_vcd("simulator.vcd", "simulator.gtkw",
247 def check_extra_alu_outputs(self
, alu
, dec2
, sim
, code
):
248 rc
= yield dec2
.e
.rc
.data
250 cr_expected
= sim
.crl
[0].get_range().value
251 cr_actual
= yield alu
.n
.data_o
.cr0
.data
252 self
.assertEqual(cr_expected
, cr_actual
, code
)
255 if __name__
== "__main__":
256 unittest
.main(exit
=False)
257 suite
= unittest
.TestSuite()
258 suite
.addTest(TestRunner(test_data
))
260 runner
= unittest
.TextTestRunner()