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
, InternalOp
, CryIn
)
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
.test
.common
import TestCase
16 from soc
.fu
.alu
.pipeline
import ALUBasePipe
17 from soc
.fu
.alu
.pipe_data
import ALUPipeSpec
21 def get_cu_inputs(dec2
, sim
):
22 """naming (res) must conform to ALUFunctionUnit input regspec
27 reg1_ok
= yield dec2
.e
.read_reg1
.ok
29 data1
= yield dec2
.e
.read_reg1
.data
30 res
['ra'] = sim
.gpr(data1
).value
33 reg2_ok
= yield dec2
.e
.read_reg2
.ok
35 data2
= yield dec2
.e
.read_reg2
.data
36 res
['rb'] = sim
.gpr(data2
).value
39 cry_in
= yield dec2
.e
.input_carry
40 if cry_in
== CryIn
.CA
.value
:
41 carry
= 1 if sim
.spr
['XER'][XER_bits
['CA']] else 0
42 carry32
= 1 if sim
.spr
['XER'][XER_bits
['CA32']] else 0
43 res
['xer_ca'] = carry |
(carry32
<<1)
46 oe
= yield dec2
.e
.oe
.data
[0] & dec2
.e
.oe
.ok
48 so
= 1 if sim
.spr
['XER'][XER_bits
['SO']] else 0
55 def set_alu_inputs(alu
, dec2
, sim
):
56 # TODO: see https://bugs.libre-soc.org/show_bug.cgi?id=305#c43
57 # detect the immediate here (with m.If(self.i.ctx.op.imm_data.imm_ok))
58 # and place it into data_i.b
60 inp
= yield from get_cu_inputs(dec2
, sim
)
62 yield alu
.p
.data_i
.a
.eq(inp
['ra'])
64 yield alu
.p
.data_i
.b
.eq(inp
['rb'])
66 # If there's an immediate, set the B operand to that
67 imm_ok
= yield dec2
.e
.imm_data
.imm_ok
69 data2
= yield dec2
.e
.imm_data
.imm
70 yield alu
.p
.data_i
.b
.eq(data2
)
73 yield alu
.p
.data_i
.xer_ca
.eq(inp
['xer_ca'])
74 print ("extra inputs: CA/32", bin(inp
['xer_ca']))
77 print ("extra inputs: so", so
)
78 yield alu
.p
.data_i
.xer_so
.eq(so
)
81 # This test bench is a bit different than is usual. Initially when I
82 # was writing it, I had all of the tests call a function to create a
83 # device under test and simulator, initialize the dut, run the
84 # simulation for ~2 cycles, and assert that the dut output what it
85 # should have. However, this was really slow, since it needed to
86 # create and tear down the dut and simulator for every test case.
88 # Now, instead of doing that, every test case in ALUTestCase puts some
89 # data into the test_data list below, describing the instructions to
90 # be tested and the initial state. Once all the tests have been run,
91 # test_data gets passed to TestRunner which then sets up the DUT and
92 # simulator once, runs all the data through it, and asserts that the
93 # results match the pseudocode sim at every cycle.
95 # By doing this, I've reduced the time it takes to run the test suite
96 # massively. Before, it took around 1 minute on my computer, now it
97 # takes around 3 seconds
100 class ALUTestCase(FHDLTestCase
):
103 def __init__(self
, name
):
104 super().__init
__(name
)
105 self
.test_name
= name
106 def run_tst_program(self
, prog
, initial_regs
=None, initial_sprs
=None):
107 tc
= TestCase(prog
, self
.test_name
, initial_regs
, initial_sprs
)
108 self
.test_data
.append(tc
)
111 insns
= ["add", "add.", "subf"]
113 choice
= random
.choice(insns
)
114 lst
= [f
"{choice} 3, 1, 2"]
115 initial_regs
= [0] * 32
116 initial_regs
[1] = random
.randint(0, (1<<64)-1)
117 initial_regs
[2] = random
.randint(0, (1<<64)-1)
118 self
.run_tst_program(Program(lst
), initial_regs
)
120 def test_rand_imm(self
):
121 insns
= ["addi", "addis", "subfic"]
123 choice
= random
.choice(insns
)
124 imm
= random
.randint(-(1<<15), (1<<15)-1)
125 lst
= [f
"{choice} 3, 1, {imm}"]
127 initial_regs
= [0] * 32
128 initial_regs
[1] = random
.randint(0, (1<<64)-1)
129 self
.run_tst_program(Program(lst
), initial_regs
)
132 lst
= ["adde. 5, 6, 7"]
134 initial_regs
= [0] * 32
135 initial_regs
[6] = random
.randint(0, (1<<64)-1)
136 initial_regs
[7] = random
.randint(0, (1<<64)-1)
138 xer
= SelectableInt(0, 64)
139 xer
[XER_bits
['CA']] = 1
140 initial_sprs
[special_sprs
['XER']] = xer
141 self
.run_tst_program(Program(lst
), initial_regs
, initial_sprs
)
144 lst
= ["subf. 1, 6, 7",
146 initial_regs
= [0] * 32
147 initial_regs
[6] = 0x10
148 initial_regs
[7] = 0x05
149 self
.run_tst_program(Program(lst
), initial_regs
, {})
151 def test_extsb(self
):
152 insns
= ["extsb", "extsh", "extsw"]
154 choice
= random
.choice(insns
)
155 lst
= [f
"{choice} 3, 1"]
157 initial_regs
= [0] * 32
158 initial_regs
[1] = random
.randint(0, (1<<64)-1)
159 self
.run_tst_program(Program(lst
), initial_regs
)
161 def test_cmpeqb(self
):
162 lst
= ["cmpeqb cr1, 1, 2"]
164 initial_regs
= [0] * 32
166 initial_regs
[2] = 0x0001030507090b0f
167 self
.run_tst_program(Program(lst
), initial_regs
, {})
169 def test_ilang(self
):
170 pspec
= ALUPipeSpec(id_wid
=2)
171 alu
= ALUBasePipe(pspec
)
172 vl
= rtlil
.convert(alu
, ports
=alu
.ports())
173 with
open("alu_pipeline.il", "w") as f
:
177 class TestRunner(FHDLTestCase
):
178 def __init__(self
, test_data
):
179 super().__init
__("run_all")
180 self
.test_data
= test_data
185 instruction
= Signal(32)
187 pdecode
= create_pdecode()
189 m
.submodules
.pdecode2
= pdecode2
= PowerDecode2(pdecode
)
191 pspec
= ALUPipeSpec(id_wid
=2)
192 m
.submodules
.alu
= alu
= ALUBasePipe(pspec
)
194 comb
+= alu
.p
.data_i
.ctx
.op
.eq_from_execute1(pdecode2
.e
)
195 comb
+= alu
.p
.valid_i
.eq(1)
196 comb
+= alu
.n
.ready_i
.eq(1)
197 comb
+= pdecode2
.dec
.raw_opcode_in
.eq(instruction
)
202 for test
in self
.test_data
:
204 program
= test
.program
205 self
.subTest(test
.name
)
206 simulator
= ISA(pdecode2
, test
.regs
, test
.sprs
, test
.cr
,
208 gen
= program
.generate_instructions()
209 instructions
= list(zip(gen
, program
.assembly
.splitlines()))
211 index
= simulator
.pc
.CIA
.value
//4
212 while index
< len(instructions
):
213 ins
, code
= instructions
[index
]
215 print("0x{:X}".format(ins
& 0xffffffff))
218 # ask the decoder to decode this binary data (endian'd)
219 yield pdecode2
.dec
.bigendian
.eq(0) # little / big?
220 yield instruction
.eq(ins
) # raw binary instr.
222 fn_unit
= yield pdecode2
.e
.fn_unit
223 self
.assertEqual(fn_unit
, Function
.ALU
.value
)
224 yield from set_alu_inputs(alu
, pdecode2
, simulator
)
226 opname
= code
.split(' ')[0]
227 yield from simulator
.call(opname
)
228 index
= simulator
.pc
.CIA
.value
//4
230 vld
= yield alu
.n
.valid_o
233 vld
= yield alu
.n
.valid_o
235 alu_out
= yield alu
.n
.data_o
.o
.data
236 out_reg_valid
= yield pdecode2
.e
.write_reg
.ok
238 write_reg_idx
= yield pdecode2
.e
.write_reg
.data
239 expected
= simulator
.gpr(write_reg_idx
).value
240 print(f
"expected {expected:x}, actual: {alu_out:x}")
241 self
.assertEqual(expected
, alu_out
, code
)
242 yield from self
.check_extra_alu_outputs(alu
, pdecode2
,
245 sim
.add_sync_process(process
)
246 with sim
.write_vcd("simulator.vcd", "simulator.gtkw",
250 def check_extra_alu_outputs(self
, alu
, dec2
, sim
, code
):
251 rc
= yield dec2
.e
.rc
.data
252 op
= yield dec2
.e
.insn_type
253 cridx_ok
= yield dec2
.e
.write_cr
.ok
254 cridx
= yield dec2
.e
.write_cr
.data
256 print ("check extra output", repr(code
), cridx_ok
, cridx
)
258 self
.assertEqual(cridx
, 0, code
)
261 cr_expected
= sim
.crl
[cridx
].get_range().value
262 cr_actual
= yield alu
.n
.data_o
.cr0
.data
263 print ("CR", cridx
, cr_expected
, cr_actual
)
264 self
.assertEqual(cr_expected
, cr_actual
, "CR%d %s" % (cridx
, code
))
266 cry_out
= yield dec2
.e
.output_carry
268 expected_carry
= 1 if sim
.spr
['XER'][XER_bits
['CA']] else 0
269 real_carry
= yield alu
.n
.data_o
.xer_ca
.data
[0] # XXX CO not CO32
270 self
.assertEqual(expected_carry
, real_carry
, code
)
271 expected_carry32
= 1 if sim
.spr
['XER'][XER_bits
['CA32']] else 0
272 real_carry32
= yield alu
.n
.data_o
.xer_ca
.data
[1] # XXX CO32
273 self
.assertEqual(expected_carry32
, real_carry32
, code
)
277 if __name__
== "__main__":
278 unittest
.main(exit
=False)
279 suite
= unittest
.TestSuite()
280 suite
.addTest(TestRunner(ALUTestCase
.test_data
))
282 runner
= unittest
.TextTestRunner()