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
.alu
.pipeline
import ALUBasePipe
16 from soc
.fu
.alu
.pipe_data
import ALUPipeSpec
20 def __init__(self
, program
, regs
, sprs
, name
):
21 self
.program
= program
27 def get_cu_inputs(dec2
, sim
):
28 """naming (res) must conform to ALUFunctionUnit input regspec
33 reg1_ok
= yield dec2
.e
.read_reg1
.ok
35 data1
= yield dec2
.e
.read_reg1
.data
36 res
['ra'] = sim
.gpr(data1
).value
39 reg2_ok
= yield dec2
.e
.read_reg2
.ok
41 data2
= yield dec2
.e
.read_reg2
.data
42 res
['rb'] = sim
.gpr(data2
).value
45 cry_in
= yield dec2
.e
.input_carry
46 if cry_in
== CryIn
.CA
.value
:
47 carry
= 1 if sim
.spr
['XER'][XER_bits
['CA']] else 0
48 carry32
= 1 if sim
.spr
['XER'][XER_bits
['CA32']] else 0
49 res
['xer_ca'] = carry |
(carry32
<<1)
52 oe
= yield dec2
.e
.oe
.data
[0] & dec2
.e
.oe
.ok
54 so
= 1 if sim
.spr
['XER'][XER_bits
['SO']] else 0
61 def set_alu_inputs(alu
, dec2
, sim
):
62 # TODO: see https://bugs.libre-soc.org/show_bug.cgi?id=305#c43
63 # detect the immediate here (with m.If(self.i.ctx.op.imm_data.imm_ok))
64 # and place it into data_i.b
66 inp
= yield from get_cu_inputs(dec2
, sim
)
68 yield alu
.p
.data_i
.a
.eq(inp
['ra'])
70 yield alu
.p
.data_i
.b
.eq(inp
['rb'])
72 # If there's an immediate, set the B operand to that
73 imm_ok
= yield dec2
.e
.imm_data
.imm_ok
75 data2
= yield dec2
.e
.imm_data
.imm
76 yield alu
.p
.data_i
.b
.eq(data2
)
79 yield alu
.p
.data_i
.xer_ca
.eq(inp
['xer_ca'])
80 print ("extra inputs: CA/32", bin(inp
['xer_ca']))
83 print ("extra inputs: so", so
)
84 yield alu
.p
.data_i
.xer_so
.eq(so
)
87 # This test bench is a bit different than is usual. Initially when I
88 # was writing it, I had all of the tests call a function to create a
89 # device under test and simulator, initialize the dut, run the
90 # simulation for ~2 cycles, and assert that the dut output what it
91 # should have. However, this was really slow, since it needed to
92 # create and tear down the dut and simulator for every test case.
94 # Now, instead of doing that, every test case in ALUTestCase puts some
95 # data into the test_data list below, describing the instructions to
96 # be tested and the initial state. Once all the tests have been run,
97 # test_data gets passed to TestRunner which then sets up the DUT and
98 # simulator once, runs all the data through it, and asserts that the
99 # results match the pseudocode sim at every cycle.
101 # By doing this, I've reduced the time it takes to run the test suite
102 # massively. Before, it took around 1 minute on my computer, now it
103 # takes around 3 seconds
108 class ALUTestCase(FHDLTestCase
):
109 def __init__(self
, name
):
110 super().__init
__(name
)
111 self
.test_name
= name
112 def run_tst_program(self
, prog
, initial_regs
=[0] * 32, initial_sprs
={}):
113 tc
= TestCase(prog
, initial_regs
, initial_sprs
, self
.test_name
)
117 insns
= ["add", "add.", "subf"]
119 choice
= random
.choice(insns
)
120 lst
= [f
"{choice} 3, 1, 2"]
121 initial_regs
= [0] * 32
122 initial_regs
[1] = random
.randint(0, (1<<64)-1)
123 initial_regs
[2] = random
.randint(0, (1<<64)-1)
124 self
.run_tst_program(Program(lst
), initial_regs
)
126 def test_rand_imm(self
):
127 insns
= ["addi", "addis", "subfic"]
129 choice
= random
.choice(insns
)
130 imm
= random
.randint(-(1<<15), (1<<15)-1)
131 lst
= [f
"{choice} 3, 1, {imm}"]
133 initial_regs
= [0] * 32
134 initial_regs
[1] = random
.randint(0, (1<<64)-1)
135 self
.run_tst_program(Program(lst
), initial_regs
)
138 lst
= ["adde. 5, 6, 7"]
140 initial_regs
= [0] * 32
141 initial_regs
[6] = random
.randint(0, (1<<64)-1)
142 initial_regs
[7] = random
.randint(0, (1<<64)-1)
144 xer
= SelectableInt(0, 64)
145 xer
[XER_bits
['CA']] = 1
146 initial_sprs
[special_sprs
['XER']] = xer
147 self
.run_tst_program(Program(lst
), initial_regs
, initial_sprs
)
150 lst
= ["subf. 1, 6, 7",
152 initial_regs
= [0] * 32
153 initial_regs
[6] = 0x10
154 initial_regs
[7] = 0x05
155 self
.run_tst_program(Program(lst
), initial_regs
, {})
157 def test_extsb(self
):
158 insns
= ["extsb", "extsh", "extsw"]
160 choice
= random
.choice(insns
)
161 lst
= [f
"{choice} 3, 1"]
163 initial_regs
= [0] * 32
164 initial_regs
[1] = random
.randint(0, (1<<64)-1)
165 self
.run_tst_program(Program(lst
), initial_regs
)
167 def test_cmpeqb(self
):
168 lst
= ["cmpeqb cr1, 1, 2"]
170 initial_regs
= [0] * 32
172 initial_regs
[2] = 0x0001030507090b0f
173 self
.run_tst_program(Program(lst
), initial_regs
, {})
175 def test_ilang(self
):
176 pspec
= ALUPipeSpec(id_wid
=2)
177 alu
= ALUBasePipe(pspec
)
178 vl
= rtlil
.convert(alu
, ports
=alu
.ports())
179 with
open("alu_pipeline.il", "w") as f
:
183 class TestRunner(FHDLTestCase
):
184 def __init__(self
, test_data
):
185 super().__init
__("run_all")
186 self
.test_data
= test_data
191 instruction
= Signal(32)
193 pdecode
= create_pdecode()
195 m
.submodules
.pdecode2
= pdecode2
= PowerDecode2(pdecode
)
197 pspec
= ALUPipeSpec(id_wid
=2)
198 m
.submodules
.alu
= alu
= ALUBasePipe(pspec
)
200 comb
+= alu
.p
.data_i
.ctx
.op
.eq_from_execute1(pdecode2
.e
)
201 comb
+= alu
.p
.valid_i
.eq(1)
202 comb
+= alu
.n
.ready_i
.eq(1)
203 comb
+= pdecode2
.dec
.raw_opcode_in
.eq(instruction
)
208 for test
in self
.test_data
:
210 program
= test
.program
211 self
.subTest(test
.name
)
212 simulator
= ISA(pdecode2
, test
.regs
, test
.sprs
, 0)
213 gen
= program
.generate_instructions()
214 instructions
= list(zip(gen
, program
.assembly
.splitlines()))
216 index
= simulator
.pc
.CIA
.value
//4
217 while index
< len(instructions
):
218 ins
, code
= instructions
[index
]
220 print("0x{:X}".format(ins
& 0xffffffff))
223 # ask the decoder to decode this binary data (endian'd)
224 yield pdecode2
.dec
.bigendian
.eq(0) # little / big?
225 yield instruction
.eq(ins
) # raw binary instr.
227 fn_unit
= yield pdecode2
.e
.fn_unit
228 self
.assertEqual(fn_unit
, Function
.ALU
.value
)
229 yield from set_alu_inputs(alu
, pdecode2
, simulator
)
231 opname
= code
.split(' ')[0]
232 yield from simulator
.call(opname
)
233 index
= simulator
.pc
.CIA
.value
//4
235 vld
= yield alu
.n
.valid_o
238 vld
= yield alu
.n
.valid_o
240 alu_out
= yield alu
.n
.data_o
.o
.data
241 out_reg_valid
= yield pdecode2
.e
.write_reg
.ok
243 write_reg_idx
= yield pdecode2
.e
.write_reg
.data
244 expected
= simulator
.gpr(write_reg_idx
).value
245 print(f
"expected {expected:x}, actual: {alu_out:x}")
246 self
.assertEqual(expected
, alu_out
, code
)
247 yield from self
.check_extra_alu_outputs(alu
, pdecode2
,
250 sim
.add_sync_process(process
)
251 with sim
.write_vcd("simulator.vcd", "simulator.gtkw",
255 def check_extra_alu_outputs(self
, alu
, dec2
, sim
, code
):
256 rc
= yield dec2
.e
.rc
.data
257 op
= yield dec2
.e
.insn_type
258 cridx_ok
= yield dec2
.e
.write_cr
.ok
259 cridx
= yield dec2
.e
.write_cr
.data
261 print ("check extra output", repr(code
), cridx_ok
, cridx
)
263 self
.assertEqual(cridx
, 0, code
)
266 cr_expected
= sim
.crl
[cridx
].get_range().value
267 cr_actual
= yield alu
.n
.data_o
.cr0
.data
268 print ("CR", cridx
, cr_expected
, cr_actual
)
269 self
.assertEqual(cr_expected
, cr_actual
, "CR%d %s" % (cridx
, code
))
271 cry_out
= yield dec2
.e
.output_carry
273 expected_carry
= 1 if sim
.spr
['XER'][XER_bits
['CA']] else 0
274 real_carry
= yield alu
.n
.data_o
.xer_ca
.data
[0] # XXX CO not CO32
275 self
.assertEqual(expected_carry
, real_carry
, code
)
276 expected_carry32
= 1 if sim
.spr
['XER'][XER_bits
['CA32']] else 0
277 real_carry32
= yield alu
.n
.data_o
.xer_ca
.data
[1] # XXX CO32
278 self
.assertEqual(expected_carry32
, real_carry32
, code
)
282 if __name__
== "__main__":
283 unittest
.main(exit
=False)
284 suite
= unittest
.TestSuite()
285 suite
.addTest(TestRunner(test_data
))
287 runner
= unittest
.TextTestRunner()