1 from nmigen
import Module
, Signal
2 from nmigen
.back
.pysim
import Simulator
, Delay
, Settle
3 from nmutil
.formaltest
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
.test
.common
import TestCase
15 from soc
.fu
.logical
.pipeline
import LogicalBasePipe
16 from soc
.fu
.logical
.pipe_data
import LogicalPipeSpec
20 def get_cu_inputs(dec2
, sim
):
21 """naming (res) must conform to LogicalFunctionUnit input regspec
26 reg1_ok
= yield dec2
.e
.read_reg1
.ok
28 data1
= yield dec2
.e
.read_reg1
.data
29 res
['ra'] = sim
.gpr(data1
).value
32 reg2_ok
= yield dec2
.e
.read_reg2
.ok
33 #imm_ok = yield dec2.e.imm_data.imm_ok
35 data2
= yield dec2
.e
.read_reg2
.data
36 data2
= sim
.gpr(data2
).value
39 # data2 = yield dec2.e.imm_data.imm
45 def set_alu_inputs(alu
, dec2
, sim
):
46 # TODO: see https://bugs.libre-soc.org/show_bug.cgi?id=305#c43
47 # detect the immediate here (with m.If(self.i.ctx.op.imm_data.imm_ok))
48 # and place it into data_i.b
50 inp
= yield from get_cu_inputs(dec2
, sim
)
52 yield alu
.p
.data_i
.a
.eq(inp
['ra'])
54 yield alu
.p
.data_i
.b
.eq(inp
['rb'])
55 imm_ok
= yield dec2
.e
.imm_data
.imm_ok
57 data2
= yield dec2
.e
.imm_data
.imm
58 yield alu
.p
.data_i
.b
.eq(data2
)
61 # This test bench is a bit different than is usual. Initially when I
62 # was writing it, I had all of the tests call a function to create a
63 # device under test and simulator, initialize the dut, run the
64 # simulation for ~2 cycles, and assert that the dut output what it
65 # should have. However, this was really slow, since it needed to
66 # create and tear down the dut and simulator for every test case.
68 # Now, instead of doing that, every test case in ALUTestCase puts some
69 # data into the test_data list below, describing the instructions to
70 # be tested and the initial state. Once all the tests have been run,
71 # test_data gets passed to TestRunner which then sets up the DUT and
72 # simulator once, runs all the data through it, and asserts that the
73 # results match the pseudocode sim at every cycle.
75 # By doing this, I've reduced the time it takes to run the test suite
76 # massively. Before, it took around 1 minute on my computer, now it
77 # takes around 3 seconds
80 class LogicalTestCase(FHDLTestCase
):
82 def __init__(self
, name
):
83 super().__init
__(name
)
86 def run_tst_program(self
, prog
, initial_regs
=None, initial_sprs
=None):
87 tc
= TestCase(prog
, self
.test_name
, initial_regs
, initial_sprs
)
88 self
.test_data
.append(tc
)
91 insns
= ["and", "or", "xor"]
93 choice
= random
.choice(insns
)
94 lst
= [f
"{choice} 3, 1, 2"]
95 initial_regs
= [0] * 32
96 initial_regs
[1] = random
.randint(0, (1 << 64)-1)
97 initial_regs
[2] = random
.randint(0, (1 << 64)-1)
98 self
.run_tst_program(Program(lst
), initial_regs
)
100 def test_rand_imm_logical(self
):
101 insns
= ["andi.", "andis.", "ori", "oris", "xori", "xoris"]
103 choice
= random
.choice(insns
)
104 imm
= random
.randint(0, (1 << 16)-1)
105 lst
= [f
"{choice} 3, 1, {imm}"]
107 initial_regs
= [0] * 32
108 initial_regs
[1] = random
.randint(0, (1 << 64)-1)
109 self
.run_tst_program(Program(lst
), initial_regs
)
112 insns
= ["cntlzd", "cnttzd", "cntlzw", "cnttzw"]
114 choice
= random
.choice(insns
)
115 lst
= [f
"{choice} 3, 1"]
117 initial_regs
= [0] * 32
118 initial_regs
[1] = random
.randint(0, (1 << 64)-1)
119 self
.run_tst_program(Program(lst
), initial_regs
)
121 def test_parity(self
):
122 insns
= ["prtyw", "prtyd"]
124 choice
= random
.choice(insns
)
125 lst
= [f
"{choice} 3, 1"]
127 initial_regs
= [0] * 32
128 initial_regs
[1] = random
.randint(0, (1 << 64)-1)
129 self
.run_tst_program(Program(lst
), initial_regs
)
131 def test_popcnt(self
):
132 insns
= ["popcntb", "popcntw", "popcntd"]
134 choice
= random
.choice(insns
)
135 lst
= [f
"{choice} 3, 1"]
137 initial_regs
= [0] * 32
138 initial_regs
[1] = random
.randint(0, (1 << 64)-1)
139 self
.run_tst_program(Program(lst
), initial_regs
)
141 def test_popcnt_edge(self
):
142 insns
= ["popcntb", "popcntw", "popcntd"]
144 lst
= [f
"{choice} 3, 1"]
145 initial_regs
= [0] * 32
147 self
.run_tst_program(Program(lst
), initial_regs
)
150 lst
= ["cmpb 3, 1, 2"]
151 initial_regs
= [0] * 32
152 initial_regs
[1] = 0xdeadbeefcafec0de
153 initial_regs
[2] = 0xd0adb0000afec1de
154 self
.run_tst_program(Program(lst
), initial_regs
)
156 def test_bpermd(self
):
157 lst
= ["bpermd 3, 1, 2"]
159 initial_regs
= [0] * 32
160 initial_regs
[1] = 1<<random
.randint(0,63)
161 initial_regs
[2] = 0xdeadbeefcafec0de
162 self
.run_tst_program(Program(lst
), initial_regs
)
164 def test_ilang(self
):
165 pspec
= LogicalPipeSpec(id_wid
=2)
166 alu
= LogicalBasePipe(pspec
)
167 vl
= rtlil
.convert(alu
, ports
=alu
.ports())
168 with
open("logical_pipeline.il", "w") as f
:
172 class TestRunner(FHDLTestCase
):
173 def __init__(self
, test_data
):
174 super().__init
__("run_all")
175 self
.test_data
= test_data
180 instruction
= Signal(32)
182 pdecode
= create_pdecode()
184 m
.submodules
.pdecode2
= pdecode2
= PowerDecode2(pdecode
)
186 pspec
= LogicalPipeSpec(id_wid
=2)
187 m
.submodules
.alu
= alu
= LogicalBasePipe(pspec
)
189 comb
+= alu
.p
.data_i
.ctx
.op
.eq_from_execute1(pdecode2
.e
)
190 comb
+= alu
.p
.valid_i
.eq(1)
191 comb
+= alu
.n
.ready_i
.eq(1)
192 comb
+= pdecode2
.dec
.raw_opcode_in
.eq(instruction
)
198 for test
in self
.test_data
:
200 program
= test
.program
201 self
.subTest(test
.name
)
202 simulator
= ISA(pdecode2
, test
.regs
, test
.sprs
, test
.cr
,
204 gen
= program
.generate_instructions()
205 instructions
= list(zip(gen
, program
.assembly
.splitlines()))
207 index
= simulator
.pc
.CIA
.value
//4
208 while index
< len(instructions
):
209 ins
, code
= instructions
[index
]
211 print("0x{:X}".format(ins
& 0xffffffff))
214 # ask the decoder to decode this binary data (endian'd)
215 yield pdecode2
.dec
.bigendian
.eq(0) # little / big?
216 yield instruction
.eq(ins
) # raw binary instr.
218 fn_unit
= yield pdecode2
.e
.fn_unit
219 self
.assertEqual(fn_unit
, Function
.LOGICAL
.value
, code
)
220 yield from set_alu_inputs(alu
, pdecode2
, simulator
)
222 opname
= code
.split(' ')[0]
223 yield from simulator
.call(opname
)
224 index
= simulator
.pc
.CIA
.value
//4
226 vld
= yield alu
.n
.valid_o
229 vld
= yield alu
.n
.valid_o
231 alu_out
= yield alu
.n
.data_o
.o
.data
232 out_reg_valid
= yield pdecode2
.e
.write_reg
.ok
234 write_reg_idx
= yield pdecode2
.e
.write_reg
.data
235 expected
= simulator
.gpr(write_reg_idx
).value
236 print(f
"expected {expected:x}, actual: {alu_out:x}")
237 self
.assertEqual(expected
, alu_out
, code
)
238 yield from self
.check_extra_alu_outputs(alu
, pdecode2
,
241 sim
.add_sync_process(process
)
242 with sim
.write_vcd("simulator.vcd", "simulator.gtkw",
246 def check_extra_alu_outputs(self
, alu
, dec2
, sim
, code
):
247 rc
= yield dec2
.e
.rc
.data
249 cr_expected
= sim
.crl
[0].get_range().value
250 cr_actual
= yield alu
.n
.data_o
.cr0
.data
251 self
.assertEqual(cr_expected
, cr_actual
, code
)
254 if __name__
== "__main__":
255 unittest
.main(exit
=False)
256 suite
= unittest
.TestSuite()
257 suite
.addTest(TestRunner(LogicalTestCase
.test_data
))
259 runner
= unittest
.TextTestRunner()