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
13 from soc
.config
.endian
import bigendian
16 from soc
.fu
.test
.common
import TestAccumulatorBase
, TestCase
, ALUHelpers
17 from soc
.fu
.logical
.pipeline
import LogicalBasePipe
18 from soc
.fu
.logical
.pipe_data
import LogicalPipeSpec
22 def get_cu_inputs(dec2
, sim
):
23 """naming (res) must conform to LogicalFunctionUnit input regspec
27 yield from ALUHelpers
.get_sim_int_ra(res
, sim
, dec2
) # RA
28 yield from ALUHelpers
.get_sim_int_rb(res
, sim
, dec2
) # RB
29 yield from ALUHelpers
.get_sim_xer_so(res
, sim
, dec2
) # XER.so
31 print("alu get_cu_inputs", res
)
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 inp
= yield from get_cu_inputs(dec2
, sim
)
42 print ("set alu inputs", inp
)
43 yield from ALUHelpers
.set_int_ra(alu
, dec2
, inp
)
44 yield from ALUHelpers
.set_int_rb(alu
, dec2
, inp
)
45 yield from ALUHelpers
.set_xer_so(alu
, dec2
, inp
)
48 # This test bench is a bit different than is usual. Initially when I
49 # was writing it, I had all of the tests call a function to create a
50 # device under test and simulator, initialize the dut, run the
51 # simulation for ~2 cycles, and assert that the dut output what it
52 # should have. However, this was really slow, since it needed to
53 # create and tear down the dut and simulator for every test case.
55 # Now, instead of doing that, every test case in ALUTestCase puts some
56 # data into the test_data list below, describing the instructions to
57 # be tested and the initial state. Once all the tests have been run,
58 # test_data gets passed to TestRunner which then sets up the DUT and
59 # simulator once, runs all the data through it, and asserts that the
60 # results match the pseudocode sim at every cycle.
62 # By doing this, I've reduced the time it takes to run the test suite
63 # massively. Before, it took around 1 minute on my computer, now it
64 # takes around 3 seconds
67 class LogicalTestCase(TestAccumulatorBase
):
69 def case_complement(self
):
70 insns
= ["andc", "orc", "nand", "nor"]
72 choice
= random
.choice(insns
)
73 lst
= [f
"{choice} 3, 1, 2"]
74 initial_regs
= [0] * 32
75 initial_regs
[1] = random
.randint(0, (1 << 64)-1)
76 initial_regs
[2] = random
.randint(0, (1 << 64)-1)
77 self
.add_case(Program(lst
, bigendian
), initial_regs
)
80 insns
= ["and", "or", "xor", "eqv"]
82 choice
= random
.choice(insns
)
83 lst
= [f
"{choice} 3, 1, 2"]
84 initial_regs
= [0] * 32
85 initial_regs
[1] = random
.randint(0, (1 << 64)-1)
86 initial_regs
[2] = random
.randint(0, (1 << 64)-1)
87 self
.add_case(Program(lst
, bigendian
), initial_regs
)
90 insns
= ["and.", "or.", "xor.", "eqv.", "andc.",
91 "orc.", "nand.", "nor."]
92 for XER
in [0, 0xe00c0000]:
94 choice
= random
.choice(insns
)
95 lst
= [f
"{choice} 3, 1, 2"]
96 initial_regs
= [0] * 32
97 initial_regs
[1] = random
.randint(0, (1 << 64)-1)
98 initial_regs
[2] = random
.randint(0, (1 << 64)-1)
99 self
.add_case(Program(lst
, bigendian
), initial_regs
,
100 initial_sprs
= {'XER': XER
})
102 def case_rand_imm_so(self
):
103 insns
= ["andi.", "andis."]
105 choice
= random
.choice(insns
)
106 imm
= random
.randint(0, (1 << 16)-1)
107 lst
= [f
"{choice} 3, 1, {imm}"]
109 initial_regs
= [0] * 32
110 initial_regs
[1] = random
.randint(0, (1 << 64)-1)
111 initial_sprs
= {'XER': 0xe00c0000}
113 self
.add_case(Program(lst
, bigendian
), initial_regs
,
114 initial_sprs
=initial_sprs
)
116 def case_rand_imm_logical(self
):
117 insns
= ["andi.", "andis.", "ori", "oris", "xori", "xoris"]
119 choice
= random
.choice(insns
)
120 imm
= random
.randint(0, (1 << 16)-1)
121 lst
= [f
"{choice} 3, 1, {imm}"]
123 initial_regs
= [0] * 32
124 initial_regs
[1] = random
.randint(0, (1 << 64)-1)
125 self
.add_case(Program(lst
, bigendian
), initial_regs
)
128 insns
= ["cntlzd", "cnttzd", "cntlzw", "cnttzw"]
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
.add_case(Program(lst
, bigendian
), initial_regs
)
137 def case_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
.add_case(Program(lst
, bigendian
), initial_regs
)
147 def case_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
.add_case(Program(lst
, bigendian
), initial_regs
)
157 def case_popcnt_edge(self
):
158 insns
= ["popcntb", "popcntw", "popcntd"]
160 lst
= [f
"{choice} 3, 1"]
161 initial_regs
= [0] * 32
163 self
.add_case(Program(lst
, bigendian
), initial_regs
)
166 lst
= ["cmpb 3, 1, 2"]
167 initial_regs
= [0] * 32
168 initial_regs
[1] = 0xdeadbeefcafec0de
169 initial_regs
[2] = 0xd0adb0000afec1de
170 self
.add_case(Program(lst
, bigendian
), initial_regs
)
172 def case_bpermd(self
):
173 lst
= ["bpermd 3, 1, 2"]
175 initial_regs
= [0] * 32
176 initial_regs
[1] = 1 << random
.randint(0, 63)
177 initial_regs
[2] = 0xdeadbeefcafec0de
178 self
.add_case(Program(lst
, bigendian
), initial_regs
)
180 def case_ilang(self
):
181 pspec
= LogicalPipeSpec(id_wid
=2)
182 alu
= LogicalBasePipe(pspec
)
183 vl
= rtlil
.convert(alu
, ports
=alu
.ports())
184 with
open("logical_pipeline.il", "w") as f
:
188 class TestRunner(FHDLTestCase
):
189 def __init__(self
, test_data
):
190 super().__init
__("run_all")
191 self
.test_data
= test_data
193 def execute(self
, alu
,instruction
, pdecode2
, test
):
195 program
= test
.program
196 self
.subTest(test
.name
)
197 simulator
= ISA(pdecode2
, test
.regs
, test
.sprs
, test
.cr
,
200 gen
= program
.generate_instructions()
201 instructions
= list(zip(gen
, program
.assembly
.splitlines()))
203 index
= simulator
.pc
.CIA
.value
//4
204 while index
< len(instructions
):
205 ins
, code
= instructions
[index
]
207 print("0x{:X}".format(ins
& 0xffffffff))
210 # ask the decoder to decode this binary data (endian'd)
211 yield pdecode2
.dec
.bigendian
.eq(bigendian
) # little / big?
212 yield instruction
.eq(ins
) # raw binary instr.
214 fn_unit
= yield pdecode2
.e
.do
.fn_unit
215 self
.assertEqual(fn_unit
, Function
.LOGICAL
.value
, code
)
216 yield from set_alu_inputs(alu
, pdecode2
, simulator
)
218 # set valid for one cycle, propagate through pipeline...
219 yield alu
.p
.valid_i
.eq(1)
221 yield alu
.p
.valid_i
.eq(0)
223 opname
= code
.split(' ')[0]
224 yield from simulator
.call(opname
)
225 index
= simulator
.pc
.CIA
.value
//4
227 vld
= yield alu
.n
.valid_o
230 vld
= yield alu
.n
.valid_o
233 yield from self
.check_alu_outputs(alu
, pdecode2
,
240 instruction
= Signal(32)
242 pdecode
= create_pdecode()
244 m
.submodules
.pdecode2
= pdecode2
= PowerDecode2(pdecode
)
246 pspec
= LogicalPipeSpec(id_wid
=2)
247 m
.submodules
.alu
= alu
= LogicalBasePipe(pspec
)
249 comb
+= alu
.p
.data_i
.ctx
.op
.eq_from_execute1(pdecode2
.e
)
250 comb
+= alu
.n
.ready_i
.eq(1)
251 comb
+= pdecode2
.dec
.raw_opcode_in
.eq(instruction
)
257 for test
in self
.test_data
:
259 program
= test
.program
260 with self
.subTest(test
.name
):
261 yield from self
.execute(alu
, instruction
, pdecode2
, test
)
263 sim
.add_sync_process(process
)
264 with sim
.write_vcd("logical_simulator.vcd", "logical_simulator.gtkw",
268 def check_alu_outputs(self
, alu
, dec2
, sim
, code
):
270 rc
= yield dec2
.e
.do
.rc
.data
271 cridx_ok
= yield dec2
.e
.write_cr
.ok
272 cridx
= yield dec2
.e
.write_cr
.data
274 print("check extra output", repr(code
), cridx_ok
, cridx
)
276 self
.assertEqual(cridx
, 0, code
)
281 yield from ALUHelpers
.get_cr_a(res
, alu
, dec2
)
282 yield from ALUHelpers
.get_int_o(res
, alu
, dec2
)
284 yield from ALUHelpers
.get_sim_int_o(sim_o
, sim
, dec2
)
285 yield from ALUHelpers
.get_wr_sim_cr_a(sim_o
, sim
, dec2
)
287 ALUHelpers
.check_cr_a(self
, res
, sim_o
, "CR%d %s" % (cridx
, code
))
288 ALUHelpers
.check_xer_ca(self
, res
, sim_o
, code
)
289 ALUHelpers
.check_int_o(self
, res
, sim_o
, code
)
292 if __name__
== "__main__":
293 unittest
.main(exit
=False)
294 suite
= unittest
.TestSuite()
295 suite
.addTest(TestRunner(LogicalTestCase().test_data
))
297 runner
= unittest
.TextTestRunner()