shorten variables, add comments
[soc.git] / src / soc / decoder / test / test_decoder_gas.py
1 from nmigen import Module, Signal
2 from nmigen.back.pysim import Simulator, Delay
3 from nmigen.test.utils import FHDLTestCase
4 import unittest
5 from soc.decoder.power_decoder import (create_pdecode)
6 from soc.decoder.power_enums import (Function, InternalOp,
7 In1Sel, In2Sel, In3Sel,
8 OutSel, RC, LdstLen, CryIn,
9 single_bit_flags, Form, SPR,
10 get_signal_name, get_csv)
11 from soc.decoder.power_decoder2 import (PowerDecode2)
12 import tempfile
13 import subprocess
14 import struct
15 import random
16
17
18 class Register:
19 def __init__(self, num):
20 self.num = num
21
22
23 class RegRegOp:
24 def __init__(self):
25 self.ops = {
26 "add": InternalOp.OP_ADD,
27 "and": InternalOp.OP_AND,
28 "or": InternalOp.OP_OR,
29 "add.": InternalOp.OP_ADD,
30 "lwzx": InternalOp.OP_LOAD,
31 "stwx": InternalOp.OP_STORE,
32 }
33 self.opcodestr = random.choice(list(self.ops.keys()))
34 self.opcode = self.ops[self.opcodestr]
35 self.r1 = Register(random.randrange(32))
36 self.r2 = Register(random.randrange(32))
37 self.r3 = Register(random.randrange(32))
38
39 def generate_instruction(self):
40 string = "{} {}, {}, {}\n".format(self.opcodestr,
41 self.r1.num,
42 self.r2.num,
43 self.r3.num)
44 return string
45
46 def check_results(self, pdecode2):
47 if self.opcode == InternalOp.OP_STORE:
48 r1sel = yield pdecode2.e.read_reg3.data
49 else:
50 r1sel = yield pdecode2.e.write_reg.data
51
52 r3sel = yield pdecode2.e.read_reg2.data
53
54 # For some reason r2 gets decoded either in read_reg1
55 # or read_reg3
56 out_sel = yield pdecode2.dec.op.out_sel
57 if out_sel == OutSel.RA.value:
58 r2sel = yield pdecode2.e.read_reg3.data
59 else:
60 r2sel = yield pdecode2.e.read_reg1.data
61 assert(r1sel == self.r1.num)
62 assert(r3sel == self.r3.num)
63 assert(r2sel == self.r2.num)
64
65 opc_out = yield pdecode2.dec.op.internal_op
66 assert(opc_out == self.opcode.value)
67 # check RC value (the dot in the instruction)
68 rc = yield pdecode2.e.rc.data
69 if '.' in self.opcodestr:
70 assert(rc == 1)
71 else:
72 assert(rc == 0)
73
74
75 class RegImmOp:
76 def __init__(self):
77 self.ops = {
78 "addi": InternalOp.OP_ADD,
79 "addis": InternalOp.OP_ADD,
80 "andi.": InternalOp.OP_AND,
81 "ori": InternalOp.OP_OR,
82 }
83 self.opcodestr = random.choice(list(self.ops.keys()))
84 self.opcode = self.ops[self.opcodestr]
85 self.r1 = Register(random.randrange(32))
86 self.r2 = Register(random.randrange(32))
87 self.imm = random.randrange(32767)
88
89 def generate_instruction(self):
90 string = "{} {}, {}, {}\n".format(self.opcodestr,
91 self.r1.num,
92 self.r2.num,
93 self.imm)
94 return string
95
96 def check_results(self, pdecode2):
97 print("Check")
98 r1sel = yield pdecode2.e.write_reg.data
99 # For some reason r2 gets decoded either in read_reg1
100 # or read_reg3
101 out_sel = yield pdecode2.dec.op.out_sel
102 if out_sel == OutSel.RA.value:
103 r2sel = yield pdecode2.e.read_reg3.data
104 else:
105 r2sel = yield pdecode2.e.read_reg1.data
106 assert(r1sel == self.r1.num)
107 assert(r2sel == self.r2.num)
108
109 imm = yield pdecode2.e.imm_data.data
110 in2_sel = yield pdecode2.dec.op.in2_sel
111 if in2_sel in [In2Sel.CONST_SI_HI.value, In2Sel.CONST_UI_HI.value]:
112 assert(imm == (self.imm << 16))
113 else:
114 assert(imm == self.imm)
115
116 rc = yield pdecode2.e.rc.data
117 if '.' in self.opcodestr:
118 assert(rc == 1)
119 else:
120 assert(rc == 0)
121
122
123 class LdStOp:
124 def __init__(self):
125 self.ops = {
126 "lwz": InternalOp.OP_LOAD,
127 "stw": InternalOp.OP_STORE,
128 "lwzu": InternalOp.OP_LOAD,
129 "stwu": InternalOp.OP_STORE,
130 "lbz": InternalOp.OP_LOAD,
131 "lhz": InternalOp.OP_LOAD,
132 "stb": InternalOp.OP_STORE,
133 "sth": InternalOp.OP_STORE,
134 }
135 self.opcodestr = random.choice(list(self.ops.keys()))
136 self.opcode = self.ops[self.opcodestr]
137 self.r1 = Register(random.randrange(32))
138 self.r2 = Register(random.randrange(1, 32))
139 self.imm = random.randrange(32767)
140
141 def generate_instruction(self):
142 string = "{} {}, {}({})\n".format(self.opcodestr,
143 self.r1.num,
144 self.imm,
145 self.r2.num)
146 return string
147
148 def check_results(self, pdecode2):
149 print("Check")
150 r2sel = yield pdecode2.e.read_reg1.data
151 if self.opcode == InternalOp.OP_STORE:
152 r1sel = yield pdecode2.e.read_reg3.data
153 else:
154 r1sel = yield pdecode2.e.write_reg.data
155 assert(r1sel == self.r1.num)
156 assert(r2sel == self.r2.num)
157
158 imm = yield pdecode2.e.imm_data.data
159 assert(imm == self.imm)
160
161 update = yield pdecode2.e.update
162 if "u" in self.opcodestr:
163 assert(update == 1)
164 else:
165 assert(update == 0)
166
167 size = yield pdecode2.e.data_len
168 if "w" in self.opcodestr:
169 assert(size == 4)
170 elif "h" in self.opcodestr:
171 assert(size == 2)
172 elif "b" in self.opcodestr:
173 assert(size == 1)
174 else:
175 assert(False)
176
177
178 class CmpRegOp:
179 def __init__(self):
180 self.ops = {
181 "cmp": InternalOp.OP_CMP,
182 }
183 self.opcodestr = random.choice(list(self.ops.keys()))
184 self.opcode = self.ops[self.opcodestr]
185 self.r1 = Register(random.randrange(32))
186 self.r2 = Register(random.randrange(32))
187 self.cr = Register(random.randrange(8))
188
189 def generate_instruction(self):
190 string = "{} {}, 0, {}, {}\n".format(self.opcodestr,
191 self.cr.num,
192 self.r1.num,
193 self.r2.num)
194 return string
195
196 def check_results(self, pdecode2):
197 r1sel = yield pdecode2.e.read_reg1.data
198 r2sel = yield pdecode2.e.read_reg2.data
199 crsel = yield pdecode2.dec.BF[0:-1]
200
201 assert(r1sel == self.r1.num)
202 assert(r2sel == self.r2.num)
203 assert(crsel == self.cr.num)
204
205
206 class RotateOp:
207 def __init__(self):
208 self.ops = {
209 "rlwinm": InternalOp.OP_CMP,
210 "rlwnm": InternalOp.OP_CMP,
211 "rlwimi": InternalOp.OP_CMP,
212 "rlwinm.": InternalOp.OP_CMP,
213 "rlwnm.": InternalOp.OP_CMP,
214 "rlwimi.": InternalOp.OP_CMP,
215 }
216 self.opcodestr = random.choice(list(self.ops.keys()))
217 self.opcode = self.ops[self.opcodestr]
218 self.r1 = Register(random.randrange(32))
219 self.r2 = Register(random.randrange(32))
220 self.shift = random.randrange(32)
221 self.mb = random.randrange(32)
222 self.me = random.randrange(32)
223
224 def generate_instruction(self):
225 string = "{} {},{},{},{},{}\n".format(self.opcodestr,
226 self.r1.num,
227 self.r2.num,
228 self.shift,
229 self.mb,
230 self.me)
231 return string
232
233 def check_results(self, pdecode2):
234 r1sel = yield pdecode2.e.write_reg.data
235 r2sel = yield pdecode2.e.read_reg3.data
236 dec = pdecode2.dec
237
238 if "i" in self.opcodestr:
239 shift = yield dec.SH[0:-1]
240 else:
241 shift = yield pdecode2.e.read_reg2.data
242 mb = yield dec.MB[0:-1]
243 me = yield dec.ME[0:-1]
244
245 assert(r1sel == self.r1.num)
246 assert(r2sel == self.r2.num)
247 assert(shift == self.shift)
248 assert(mb == self.mb)
249 assert(me == self.me)
250
251 rc = yield pdecode2.e.rc.data
252 if '.' in self.opcodestr:
253 assert(rc == 1)
254 else:
255 assert(rc == 0)
256
257
258 class Branch:
259 def __init__(self):
260 self.ops = {
261 "b": InternalOp.OP_B,
262 "bl": InternalOp.OP_B,
263 "ba": InternalOp.OP_B,
264 "bla": InternalOp.OP_B,
265 }
266 self.opcodestr = random.choice(list(self.ops.keys()))
267 self.opcode = self.ops[self.opcodestr]
268 self.addr = random.randrange(2**23) * 4
269
270 def generate_instruction(self):
271 string = "{} {}\n".format(self.opcodestr,
272 self.addr)
273 return string
274
275 def check_results(self, pdecode2):
276 imm = yield pdecode2.e.imm_data.data
277
278 assert(imm == self.addr)
279 lk = yield pdecode2.e.lk
280 if "l" in self.opcodestr:
281 assert(lk == 1)
282 else:
283 assert(lk == 0)
284 aa = yield pdecode2.dec.AA[0:-1]
285 if "a" in self.opcodestr:
286 assert(aa == 1)
287 else:
288 assert(aa == 0)
289
290
291 class BranchCond:
292 def __init__(self):
293 self.ops = {
294 "bc": InternalOp.OP_B,
295 "bcl": InternalOp.OP_B,
296 "bca": InternalOp.OP_B,
297 "bcla": InternalOp.OP_B,
298 }
299 # Given in Figure 40 "BO field encodings" in section 2.4, page
300 # 33 of the Power ISA v3.0B manual
301 self.branchops = [0b00000, 0b00010, 0b00100, 0b01000, 0b01010,
302 0b01100, 0b10000, 0b10100]
303 self.opcodestr = random.choice(list(self.ops.keys()))
304 self.opcode = self.ops[self.opcodestr]
305 self.addr = random.randrange(2**13) * 4
306 self.bo = random.choice(self.branchops)
307 self.bi = random.randrange(32)
308
309 def generate_instruction(self):
310 string = "{} {},{},{}\n".format(self.opcodestr,
311 self.bo,
312 self.bi,
313 self.addr)
314 return string
315
316 def check_results(self, pdecode2):
317 imm = yield pdecode2.e.imm_data.data
318 bo = yield pdecode2.dec.BO[0:-1]
319 bi = yield pdecode2.dec.BI[0:-1]
320
321 assert(imm == self.addr)
322 assert(bo == self.bo)
323 assert(bi == self.bi)
324 lk = yield pdecode2.e.lk
325 if "l" in self.opcodestr:
326 assert(lk == 1)
327 else:
328 assert(lk == 0)
329 aa = yield pdecode2.dec.AA[0:-1]
330 if "a" in self.opcodestr:
331 assert(aa == 1)
332 else:
333 assert(aa == 0)
334
335
336 class BranchRel:
337 def __init__(self):
338 self.ops = {
339 "bclr": InternalOp.OP_B,
340 "bcctr": InternalOp.OP_B,
341 "bclrl": InternalOp.OP_B,
342 "bcctrl": InternalOp.OP_B,
343 }
344 # Given in Figure 40 "BO field encodings" in section 2.4, page
345 # 33 of the Power ISA v3.0B manual
346 self.branchops = [0b00100, 0b01100, 0b10100]
347 self.opcodestr = random.choice(list(self.ops.keys()))
348 self.opcode = self.ops[self.opcodestr]
349 self.bh = random.randrange(4)
350 self.bo = random.choice(self.branchops)
351 self.bi = random.randrange(32)
352
353 def generate_instruction(self):
354 string = "{} {},{},{}\n".format(self.opcodestr,
355 self.bo,
356 self.bi,
357 self.bh)
358 return string
359
360 def check_results(self, pdecode2):
361 bo = yield pdecode2.dec.BO[0:-1]
362 bi = yield pdecode2.dec.BI[0:-1]
363
364 assert(bo == self.bo)
365 assert(bi == self.bi)
366
367 spr = yield pdecode2.e.read_spr2.data
368 if "lr" in self.opcodestr:
369 assert(spr == SPR.LR.value)
370 else:
371 assert(spr == SPR.CTR.value)
372
373 lk = yield pdecode2.e.lk
374 if self.opcodestr[-1] == 'l':
375 assert(lk == 1)
376 else:
377 assert(lk == 0)
378
379
380 class DecoderTestCase(FHDLTestCase):
381
382 def get_assembled_instruction(self, instruction, bigendian=False):
383 if bigendian:
384 endian_fmt = "elf64-big"
385 obj_fmt = "-be"
386 else:
387 endian_fmt = "elf64-little"
388 obj_fmt = "-le"
389 with tempfile.NamedTemporaryFile(suffix=".o") as outfile:
390 args = ["powerpc64-linux-gnu-as",
391 obj_fmt,
392 "-o",
393 outfile.name]
394 p = subprocess.Popen(args, stdin=subprocess.PIPE)
395 p.communicate(instruction.encode('utf-8'))
396 assert(p.wait() == 0)
397
398 with tempfile.NamedTemporaryFile(suffix=".bin") as binfile:
399 args = ["powerpc64-linux-gnu-objcopy",
400 "-I", endian_fmt,
401 "-O", "binary",
402 outfile.name,
403 binfile.name]
404 subprocess.check_output(args)
405 binary = struct.unpack('>i', binfile.read(4))[0]
406 return binary
407
408 def run_tst(self, kls, name):
409 m = Module()
410 comb = m.d.comb
411 instruction = Signal(32)
412
413 pdecode = create_pdecode()
414
415 m.submodules.pdecode2 = pdecode2 = PowerDecode2(pdecode)
416 comb += pdecode2.dec.raw_opcode_in.eq(instruction)
417 sim = Simulator(m)
418
419 def process():
420 for i in range(20):
421 checker = kls()
422 ins = checker.generate_instruction()
423 print("instr", ins.strip())
424 for mode in [0, 1]:
425
426 # turn the instruction into binary data (endian'd)
427 ibin = self.get_assembled_instruction(ins, mode)
428 print("code", mode, hex(ibin), bin(ibin))
429
430 # ask the decoder to decode this binary data (endian'd)
431 yield pdecode2.dec.bigendian.eq(endian) # little / big?
432 yield instruction.eq(ibin) # raw binary instr.
433 yield Delay(1e-6)
434
435 yield from checker.check_results(pdecode2)
436
437 sim.add_process(process)
438 with sim.write_vcd("%s.vcd" % name, "%s.gtkw" % name,
439 traces=[pdecode2.ports()]):
440 sim.run()
441
442 def test_reg_reg(self):
443 self.run_tst(RegRegOp, "reg_reg")
444
445 def test_reg_imm(self):
446 self.run_tst(RegImmOp, "reg_imm")
447
448 def test_ldst_imm(self):
449 self.run_tst(LdStOp, "ldst_imm")
450
451 def test_cmp_reg(self):
452 self.run_tst(CmpRegOp, "cmp_reg")
453
454 def test_rot(self):
455 self.run_tst(RotateOp, "rot")
456
457 def test_branch(self):
458 self.run_tst(Branch, "branch")
459
460 def test_branch_cond(self):
461 self.run_tst(BranchCond, "branch_cond")
462
463 def test_branch_rel(self):
464 self.run_tst(BranchRel, "branch_rel")
465
466
467 if __name__ == "__main__":
468 unittest.main()