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
.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
26 def get_cu_inputs(dec2
, sim
):
27 """naming (res) must conform to LogicalFunctionUnit input regspec
32 reg1_ok
= yield dec2
.e
.read_reg1
.ok
34 data1
= yield dec2
.e
.read_reg1
.data
35 res
['ra'] = sim
.gpr(data1
).value
38 reg2_ok
= yield dec2
.e
.read_reg2
.ok
39 #imm_ok = yield dec2.e.imm_data.imm_ok
41 data2
= yield dec2
.e
.read_reg2
.data
42 data2
= sim
.gpr(data2
).value
45 # data2 = yield dec2.e.imm_data.imm
51 def set_alu_inputs(alu
, dec2
, sim
):
52 # TODO: see https://bugs.libre-soc.org/show_bug.cgi?id=305#c43
53 # detect the immediate here (with m.If(self.i.ctx.op.imm_data.imm_ok))
54 # and place it into data_i.b
56 inp
= yield from get_cu_inputs(dec2
, sim
)
58 yield alu
.p
.data_i
.a
.eq(inp
['ra'])
60 yield alu
.p
.data_i
.b
.eq(inp
['rb'])
61 imm_ok
= yield dec2
.e
.imm_data
.imm_ok
63 data2
= yield dec2
.e
.imm_data
.imm
64 yield alu
.p
.data_i
.b
.eq(data2
)
67 # This test bench is a bit different than is usual. Initially when I
68 # was writing it, I had all of the tests call a function to create a
69 # device under test and simulator, initialize the dut, run the
70 # simulation for ~2 cycles, and assert that the dut output what it
71 # should have. However, this was really slow, since it needed to
72 # create and tear down the dut and simulator for every test case.
74 # Now, instead of doing that, every test case in ALUTestCase puts some
75 # data into the test_data list below, describing the instructions to
76 # be tested and the initial state. Once all the tests have been run,
77 # test_data gets passed to TestRunner which then sets up the DUT and
78 # simulator once, runs all the data through it, and asserts that the
79 # results match the pseudocode sim at every cycle.
81 # By doing this, I've reduced the time it takes to run the test suite
82 # massively. Before, it took around 1 minute on my computer, now it
83 # takes around 3 seconds
88 class LogicalTestCase(FHDLTestCase
):
89 def __init__(self
, name
):
90 super().__init
__(name
)
93 def run_tst_program(self
, prog
, initial_regs
=[0] * 32, initial_sprs
={}):
94 tc
= TestCase(prog
, initial_regs
, initial_sprs
, self
.test_name
)
98 insns
= ["and", "or", "xor"]
100 choice
= random
.choice(insns
)
101 lst
= [f
"{choice} 3, 1, 2"]
102 initial_regs
= [0] * 32
103 initial_regs
[1] = random
.randint(0, (1 << 64)-1)
104 initial_regs
[2] = random
.randint(0, (1 << 64)-1)
105 self
.run_tst_program(Program(lst
), initial_regs
)
107 def test_rand_imm_logical(self
):
108 insns
= ["andi.", "andis.", "ori", "oris", "xori", "xoris"]
110 choice
= random
.choice(insns
)
111 imm
= random
.randint(0, (1 << 16)-1)
112 lst
= [f
"{choice} 3, 1, {imm}"]
114 initial_regs
= [0] * 32
115 initial_regs
[1] = random
.randint(0, (1 << 64)-1)
116 self
.run_tst_program(Program(lst
), initial_regs
)
119 insns
= ["cntlzd", "cnttzd", "cntlzw", "cnttzw"]
121 choice
= random
.choice(insns
)
122 lst
= [f
"{choice} 3, 1"]
124 initial_regs
= [0] * 32
125 initial_regs
[1] = random
.randint(0, (1 << 64)-1)
126 self
.run_tst_program(Program(lst
), initial_regs
)
128 def test_parity(self
):
129 insns
= ["prtyw", "prtyd"]
131 choice
= random
.choice(insns
)
132 lst
= [f
"{choice} 3, 1"]
134 initial_regs
= [0] * 32
135 initial_regs
[1] = random
.randint(0, (1 << 64)-1)
136 self
.run_tst_program(Program(lst
), initial_regs
)
138 def test_popcnt(self
):
139 insns
= ["popcntb", "popcntw", "popcntd"]
141 choice
= random
.choice(insns
)
142 lst
= [f
"{choice} 3, 1"]
144 initial_regs
= [0] * 32
145 initial_regs
[1] = random
.randint(0, (1 << 64)-1)
146 self
.run_tst_program(Program(lst
), initial_regs
)
148 def test_popcnt_edge(self
):
149 insns
= ["popcntb", "popcntw", "popcntd"]
151 lst
= [f
"{choice} 3, 1"]
152 initial_regs
= [0] * 32
154 self
.run_tst_program(Program(lst
), initial_regs
)
157 lst
= ["cmpb 3, 1, 2"]
158 initial_regs
= [0] * 32
159 initial_regs
[1] = 0xdeadbeefcafec0de
160 initial_regs
[2] = 0xd0adb0000afec1de
161 self
.run_tst_program(Program(lst
), initial_regs
)
163 def test_bpermd(self
):
164 lst
= ["bpermd 3, 1, 2"]
166 initial_regs
= [0] * 32
167 initial_regs
[1] = 1<<random
.randint(0,63)
168 initial_regs
[2] = 0xdeadbeefcafec0de
169 self
.run_tst_program(Program(lst
), initial_regs
)
171 def test_ilang(self
):
172 pspec
= LogicalPipeSpec(id_wid
=2)
173 alu
= LogicalBasePipe(pspec
)
174 vl
= rtlil
.convert(alu
, ports
=alu
.ports())
175 with
open("logical_pipeline.il", "w") as f
:
179 class TestRunner(FHDLTestCase
):
180 def __init__(self
, test_data
):
181 super().__init
__("run_all")
182 self
.test_data
= test_data
187 instruction
= Signal(32)
189 pdecode
= create_pdecode()
191 m
.submodules
.pdecode2
= pdecode2
= PowerDecode2(pdecode
)
193 pspec
= LogicalPipeSpec(id_wid
=2)
194 m
.submodules
.alu
= alu
= LogicalBasePipe(pspec
)
196 comb
+= alu
.p
.data_i
.ctx
.op
.eq_from_execute1(pdecode2
.e
)
197 comb
+= alu
.p
.valid_i
.eq(1)
198 comb
+= alu
.n
.ready_i
.eq(1)
199 comb
+= pdecode2
.dec
.raw_opcode_in
.eq(instruction
)
205 for test
in self
.test_data
:
207 program
= test
.program
208 self
.subTest(test
.name
)
209 simulator
= ISA(pdecode2
, test
.regs
, test
.sprs
, 0)
210 gen
= program
.generate_instructions()
211 instructions
= list(zip(gen
, program
.assembly
.splitlines()))
213 index
= simulator
.pc
.CIA
.value
//4
214 while index
< len(instructions
):
215 ins
, code
= instructions
[index
]
217 print("0x{:X}".format(ins
& 0xffffffff))
220 # ask the decoder to decode this binary data (endian'd)
221 yield pdecode2
.dec
.bigendian
.eq(0) # little / big?
222 yield instruction
.eq(ins
) # raw binary instr.
224 fn_unit
= yield pdecode2
.e
.fn_unit
225 self
.assertEqual(fn_unit
, Function
.LOGICAL
.value
, code
)
226 yield from set_alu_inputs(alu
, pdecode2
, simulator
)
228 opname
= code
.split(' ')[0]
229 yield from simulator
.call(opname
)
230 index
= simulator
.pc
.CIA
.value
//4
232 vld
= yield alu
.n
.valid_o
235 vld
= yield alu
.n
.valid_o
237 alu_out
= yield alu
.n
.data_o
.o
.data
238 out_reg_valid
= yield pdecode2
.e
.write_reg
.ok
240 write_reg_idx
= yield pdecode2
.e
.write_reg
.data
241 expected
= simulator
.gpr(write_reg_idx
).value
242 print(f
"expected {expected:x}, actual: {alu_out:x}")
243 self
.assertEqual(expected
, alu_out
, code
)
244 yield from self
.check_extra_alu_outputs(alu
, pdecode2
,
247 sim
.add_sync_process(process
)
248 with sim
.write_vcd("simulator.vcd", "simulator.gtkw",
252 def check_extra_alu_outputs(self
, alu
, dec2
, sim
, code
):
253 rc
= yield dec2
.e
.rc
.data
255 cr_expected
= sim
.crl
[0].get_range().value
256 cr_actual
= yield alu
.n
.data_o
.cr0
.data
257 self
.assertEqual(cr_expected
, cr_actual
, code
)
260 if __name__
== "__main__":
261 unittest
.main(exit
=False)
262 suite
= unittest
.TestSuite()
263 suite
.addTest(TestRunner(test_data
))
265 runner
= unittest
.TextTestRunner()