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 set_alu_inputs(alu
, dec2
, sim
):
28 # TODO: see https://bugs.libre-soc.org/show_bug.cgi?id=305#c43
29 # detect the immediate here (with m.If(self.i.ctx.op.imm_data.imm_ok))
30 # and place it into data_i.b
32 reg1_ok
= yield dec2
.e
.read_reg1
.ok
34 data1
= yield dec2
.e
.read_reg1
.data
35 data1
= sim
.gpr(data1
).value
39 yield alu
.p
.data_i
.a
.eq(data1
)
41 # If there's an immediate, set the B operand to that
42 reg2_ok
= yield dec2
.e
.read_reg2
.ok
43 imm_ok
= yield dec2
.e
.imm_data
.imm_ok
45 data2
= yield dec2
.e
.imm_data
.imm
47 data2
= yield dec2
.e
.read_reg2
.data
48 data2
= sim
.gpr(data2
).value
51 yield alu
.p
.data_i
.b
.eq(data2
)
54 # This test bench is a bit different than is usual. Initially when I
55 # was writing it, I had all of the tests call a function to create a
56 # device under test and simulator, initialize the dut, run the
57 # simulation for ~2 cycles, and assert that the dut output what it
58 # should have. However, this was really slow, since it needed to
59 # create and tear down the dut and simulator for every test case.
61 # Now, instead of doing that, every test case in ALUTestCase puts some
62 # data into the test_data list below, describing the instructions to
63 # be tested and the initial state. Once all the tests have been run,
64 # test_data gets passed to TestRunner which then sets up the DUT and
65 # simulator once, runs all the data through it, and asserts that the
66 # results match the pseudocode sim at every cycle.
68 # By doing this, I've reduced the time it takes to run the test suite
69 # massively. Before, it took around 1 minute on my computer, now it
70 # takes around 3 seconds
75 class LogicalTestCase(FHDLTestCase
):
76 def __init__(self
, name
):
77 super().__init
__(name
)
80 def run_tst_program(self
, prog
, initial_regs
=[0] * 32, initial_sprs
={}):
81 tc
= TestCase(prog
, initial_regs
, initial_sprs
, self
.test_name
)
85 insns
= ["and", "or", "xor"]
87 choice
= random
.choice(insns
)
88 lst
= [f
"{choice} 3, 1, 2"]
89 initial_regs
= [0] * 32
90 initial_regs
[1] = random
.randint(0, (1 << 64)-1)
91 initial_regs
[2] = random
.randint(0, (1 << 64)-1)
92 self
.run_tst_program(Program(lst
), initial_regs
)
94 def test_rand_imm_logical(self
):
95 insns
= ["andi.", "andis.", "ori", "oris", "xori", "xoris"]
97 choice
= random
.choice(insns
)
98 imm
= random
.randint(0, (1 << 16)-1)
99 lst
= [f
"{choice} 3, 1, {imm}"]
101 initial_regs
= [0] * 32
102 initial_regs
[1] = random
.randint(0, (1 << 64)-1)
103 self
.run_tst_program(Program(lst
), initial_regs
)
106 insns
= ["cntlzd", "cnttzd", "cntlzw", "cnttzw"]
108 choice
= random
.choice(insns
)
109 lst
= [f
"{choice} 3, 1"]
111 initial_regs
= [0] * 32
112 initial_regs
[1] = random
.randint(0, (1 << 64)-1)
113 self
.run_tst_program(Program(lst
), initial_regs
)
115 def test_parity(self
):
116 insns
= ["prtyw", "prtyd"]
118 choice
= random
.choice(insns
)
119 lst
= [f
"{choice} 3, 1"]
121 initial_regs
= [0] * 32
122 initial_regs
[1] = random
.randint(0, (1 << 64)-1)
123 self
.run_tst_program(Program(lst
), initial_regs
)
125 def test_popcnt(self
):
126 insns
= ["popcntb", "popcntw", "popcntd"]
128 choice
= random
.choice(insns
)
129 lst
= [f
"{choice} 3, 1"]
131 initial_regs
= [0] * 32
132 initial_regs
[1] = random
.randint(0, (1 << 64)-1)
133 self
.run_tst_program(Program(lst
), initial_regs
)
135 def test_popcnt_edge(self
):
136 insns
= ["popcntb", "popcntw", "popcntd"]
138 lst
= [f
"{choice} 3, 1"]
139 initial_regs
= [0] * 32
141 self
.run_tst_program(Program(lst
), initial_regs
)
144 lst
= ["cmpb 3, 1, 2"]
145 initial_regs
= [0] * 32
146 initial_regs
[1] = 0xdeadbeefcafec0de
147 initial_regs
[2] = 0xd0adb0000afec1de
148 self
.run_tst_program(Program(lst
), initial_regs
)
150 def test_bpermd(self
):
151 lst
= ["bpermd 3, 1, 2"]
153 initial_regs
= [0] * 32
154 initial_regs
[1] = 1<<random
.randint(0,63)
155 initial_regs
[2] = 0xdeadbeefcafec0de
156 self
.run_tst_program(Program(lst
), initial_regs
)
158 def test_ilang(self
):
159 pspec
= LogicalPipeSpec(id_wid
=2)
160 alu
= LogicalBasePipe(pspec
)
161 vl
= rtlil
.convert(alu
, ports
=alu
.ports())
162 with
open("logical_pipeline.il", "w") as f
:
166 class TestRunner(FHDLTestCase
):
167 def __init__(self
, test_data
):
168 super().__init
__("run_all")
169 self
.test_data
= test_data
174 instruction
= Signal(32)
176 pdecode
= create_pdecode()
178 m
.submodules
.pdecode2
= pdecode2
= PowerDecode2(pdecode
)
180 pspec
= LogicalPipeSpec(id_wid
=2)
181 m
.submodules
.alu
= alu
= LogicalBasePipe(pspec
)
183 comb
+= alu
.p
.data_i
.ctx
.op
.eq_from_execute1(pdecode2
.e
)
184 comb
+= alu
.p
.valid_i
.eq(1)
185 comb
+= alu
.n
.ready_i
.eq(1)
186 comb
+= pdecode2
.dec
.raw_opcode_in
.eq(instruction
)
192 for test
in self
.test_data
:
194 program
= test
.program
195 self
.subTest(test
.name
)
196 simulator
= ISA(pdecode2
, test
.regs
, test
.sprs
, 0)
197 gen
= program
.generate_instructions()
198 instructions
= list(zip(gen
, program
.assembly
.splitlines()))
200 index
= simulator
.pc
.CIA
.value
//4
201 while index
< len(instructions
):
202 ins
, code
= instructions
[index
]
204 print("0x{:X}".format(ins
& 0xffffffff))
207 # ask the decoder to decode this binary data (endian'd)
208 yield pdecode2
.dec
.bigendian
.eq(0) # little / big?
209 yield instruction
.eq(ins
) # raw binary instr.
211 fn_unit
= yield pdecode2
.e
.fn_unit
212 self
.assertEqual(fn_unit
, Function
.LOGICAL
.value
, code
)
213 yield from set_alu_inputs(alu
, pdecode2
, simulator
)
215 opname
= code
.split(' ')[0]
216 yield from simulator
.call(opname
)
217 index
= simulator
.pc
.CIA
.value
//4
219 vld
= yield alu
.n
.valid_o
222 vld
= yield alu
.n
.valid_o
224 alu_out
= yield alu
.n
.data_o
.o
.data
225 out_reg_valid
= yield pdecode2
.e
.write_reg
.ok
227 write_reg_idx
= yield pdecode2
.e
.write_reg
.data
228 expected
= simulator
.gpr(write_reg_idx
).value
229 print(f
"expected {expected:x}, actual: {alu_out:x}")
230 self
.assertEqual(expected
, alu_out
, code
)
231 yield from self
.check_extra_alu_outputs(alu
, pdecode2
,
234 sim
.add_sync_process(process
)
235 with sim
.write_vcd("simulator.vcd", "simulator.gtkw",
239 def check_extra_alu_outputs(self
, alu
, dec2
, sim
, code
):
240 rc
= yield dec2
.e
.rc
.data
242 cr_expected
= sim
.crl
[0].get_range().value
243 cr_actual
= yield alu
.n
.data_o
.cr0
.data
244 self
.assertEqual(cr_expected
, cr_actual
, code
)
247 if __name__
== "__main__":
248 unittest
.main(exit
=False)
249 suite
= unittest
.TestSuite()
250 suite
.addTest(TestRunner(test_data
))
252 runner
= unittest
.TextTestRunner()