add operators test class
[riscv-isa-sim.git] / id_regs.py
1 #!/usr/bin/env python
2 # Copyright (C) 2018 Luke Kenneth Casson Leighton <lkcl@lkcl.net>
3
4 """ identify registers used in riscv/insns/*.h and create code
5 that can be used in spike at runtime
6
7 the design of spike assumes that once an opcode is identified,
8 the role of decoding the instruction is implicitly rolled into
9 and included inside the function that emulates that opcode.
10
11 however there may be circumstances where the behaviour of an
12 instruction has to change depending on "tags" associated with
13 the registers (security extensions, simple-v extension).
14
15 therefore this code walks the instruction implementations
16 in riscv/insns/*.h looking for register usage patterns.
17 the resultant table can be used *prior* to the emulation,
18 without having to manually maintain such a table.
19 """
20
21 import os
22 import sys
23
24 insns_dir = "./riscv/insns"
25 def list_insns():
26 if len(sys.argv) == 2:
27 fullfname = sys.argv[1]
28 pth, fname = os.path.split(fullfname)
29 insn = fname[:-2]
30 return [(fullfname, insn)]
31
32 res = []
33 for fname in os.listdir(insns_dir):
34 if not fname.endswith(".h"):
35 continue
36 if fname.startswith("regs_"):
37 continue
38 insn = fname[:-2]
39 res.append((os.path.join(insns_dir, fname), insn))
40 return res
41
42 cintpatterns = [ 'WRITE_RVC_RS1S', 'WRITE_RVC_RS2S',
43 'RVC_RS1', 'RVC_RS2', 'RVC_RS1S', 'RVC_RS2S', ]
44 cfloatpatterns = [ 'WRITE_RVC_FRS2S', 'RVC_FRS2 ', 'RVC_FRS2S ']
45 intpatterns = ['WRITE_RD' , 'RS1', 'RS2', 'RS3']
46 floatpatterns = ['WRITE_FRD', 'FRS1', 'FRS2', 'FRS3']
47 patterns = intpatterns + floatpatterns
48 patterns += cintpatterns
49 patterns += cfloatpatterns
50
51 allfloats = floatpatterns + cfloatpatterns
52 floatmask = (1<<len(allfloats)-1)
53 allints = intpatterns + cintpatterns[2:]
54
55 skip = '#define USING_NOREGS\n' \
56 '#define REGS_PATTERN 0x0\n'
57
58 # this matches the order of the 4 predication arguments to
59 drlookup = { 'rd': 0, 'frd': 0, 'rs1': 1, 'rs2': 2, 'rs3': 3,
60 'rvc_rs1': 1, 'rvc_rs1s': 1,
61 'rvc_rs2': 2, 'rvc_rs2s': 2,
62 'rvc_frs2': 2, 'rvc_frs2s': 2,
63 }
64
65 def find_registers(fname, insn, twin_predication, immed_offset, is_branch):
66 # HACK! macro-skipping of instructions too painful
67 for notparallel in ['csr', 'lui', 'c_j', 'wfi', 'auipc',
68 'dret', 'uret', 'mret', 'sret',
69 'lr_d', 'lr_w', 'sc_d', 'sc_w',
70 'c_addi4spn', 'c_addi16sp']:
71 if notparallel in fname:
72 return skip
73 res = []
74 regs = []
75 isintfloat = 0x0 + floatmask << len(allints)
76 with open(fname) as f:
77 f = f.read()
78 dest_reg = None
79 for pattern in patterns:
80 x = f.find(pattern)
81 if x == -1:
82 continue
83 if pattern.startswith('R') and x != 0 and f[x-1] == 'F':
84 # botch-job/hack: RS1 also matches against FRS1 (etc.)
85 # check letter before match: if "F", skip it.
86 continue
87 if pattern.startswith('R') and x != 0 and f[x-1] == '_':
88 # RS1 also matches against RVC_RS1 (etc.)
89 # check letter before match: if "_", skip it.
90 continue
91 if pattern.startswith('FR') and x != 0 and f[x-1] == '_':
92 # RS1 also matches against RVC_FRS1 (etc.)
93 # check letter before match: if "_", skip it.
94 continue
95 if 'RVC_' in pattern and f[x+len(pattern)] == 'S':
96 # RVC_RS2S also matches against RVC_RS2 (etc.)
97 # check letter at end of match: if "S", skip it.
98 continue
99 p = pattern
100 if p.startswith('WRITE_'):
101 p = p[6:]
102 dest_reg = p
103 if pattern in allints:
104 idx = allints.index(pattern)
105 isintfloat += 1 << idx
106 if pattern in allfloats:
107 idx = allfloats.index(pattern)
108 isintfloat &= ~(1 << (idx+len(allints)))
109 regs.append(p)
110 res.append('#define USING_REG_%s' % p)
111 if dest_reg:
112 dr = dest_reg
113 fdest = False
114 if dest_reg.startswith('RVC_F'):
115 fdest = True
116 dr = 'RVC_' + dest_reg[5:]
117 if dest_reg == 'FRD':
118 fdest = True
119 dr = 'RD'
120 dridx = drlookup[dest_reg.lower()]
121 res.append('#define DEST_REG %s' % dr.lower())
122 res.append('#define _DEST_REG _%s' % dr.lower())
123 res.append('#define DEST_PREDINT %d' % (0 if fdest else 1))
124 if not res:
125 return skip
126 res.append('#define REGS_PATTERN 0x%x' % isintfloat)
127
128 predargs = ['dest_pred'] * 4
129 if immed_offset: # C.LWSP
130 if immed_offset == 'LD':
131 predargs.append('&src_pred')
132 else:
133 predargs.append('&dest_pred')
134 fsrc = insn in ['c_flwsp', 'c_fldsp']
135 c_sp_width = {'c_lwsp': 4, 'c_ldsp': 8, 'c_lqsp': 16,
136 'c_flwsp': 4, 'c_fldsp': 8,
137 'c_swsp': 4, 'c_sdsp': 8, 'c_sqsp': 16,
138 'c_fswsp': 4, 'c_fsdsp': 8}
139 iwidth = c_sp_width[insn]
140 res.append('#define IMMEDWIDTH %d' % (iwidth))
141 if immed_offset == 'LD':
142 res.append('#define SRC_PREDINT %d' % (0 if fsrc else 1))
143 else:
144 res.append('#define DEST_PREDINT %d' % (0 if fsrc else 1))
145
146 if twin_predication:
147 found = None
148 for search in ['rs1', 'rs2', 'rs3', 'rvc_rs1', 'rvc_rs1s',
149 'rvc_rs2', 'rvc_rs2s',
150 'frs1', 'frs2', 'frs3',
151 'rvc_frs2', 'rvc_frs2s']:
152 if search.upper() in regs:
153 found = search
154 if found:
155 predargs[drlookup[found]] = 'src_pred'
156 fsrc = 'f' in found
157 found = found.replace('f', '')
158 res.append('#define SRC_PREDINT %d' % (0 if fsrc else 1))
159 res.append('#define SRC_REG %s' % found)
160
161 if len(predargs) == 4:
162 predargs.append('NULL')
163
164 res.append('#define PRED_ARGS %s' % ','.join(predargs))
165 offsargs = []
166 for i in range(len(predargs)):
167 offsargs.append(predargs[i].replace('pred', 'offs').replace("&", ''))
168 res.append('#define OFFS_ARGS %s' % ','.join(offsargs))
169
170 return '\n'.join(res)
171
172 if __name__ == '__main__':
173 files = list_insns()
174 for (fname, insn) in files:
175 regsname = "regs_%s.h" % insn
176 regsname = os.path.join(insns_dir, regsname)
177 twin_predication = False
178 immed_offset = False
179 is_branch = False
180 with open(regsname, "w") as f:
181 txt = "\n#define INSN_%s\n" % insn.upper()
182 # help identify type of register
183 if insn in ['beq', 'bne', 'blt', 'bltu', 'bge', 'bgeu']:
184 txt += "#define INSN_TYPE_BRANCH\n"
185 is_branch = 'STD' # standard branch
186 if insn in ['lb', 'lbu', 'lw', 'lwu', 'ld', 'ldu']:
187 twin_predication = True
188 txt += "#define INSN_TYPE_LOAD\n"
189 elif insn in ['c_lwsp', 'c_ldsp', 'c_lqsp', 'c_flwsp', 'c_fldsp']:
190 twin_predication = True
191 immed_offset = 'LD'
192 txt += "\n#define INSN_TYPE_C_STACK_LD\n"
193 elif insn in ['c_swsp', 'c_sdsp', 'c_sqsp', 'c_fswsp', 'c_fsdsp']:
194 twin_predication = True
195 immed_offset = 'ST'
196 txt += "\n#define INSN_TYPE_C_STACK_ST\n"
197 elif insn in ['c_lw', 'c_ld', 'c_lq', 'c_flw', 'c_fld']:
198 txt += "\n#define INSN_TYPE_C_LD\n"
199 twin_predication = True
200 elif insn in ['c_sw', 'c_sd', 'c_sq', 'c_fsw', 'c_fsd']:
201 txt += "\n#define INSN_TYPE_C_ST\n"
202 twin_predication = True
203 elif insn in ['c_beqz', 'c_bnez']:
204 txt += "\n#define INSN_TYPE_C_BRANCH\n"
205 elif insn in ['c_mv']:
206 twin_predication = True
207 elif insn.startswith("c_"):
208 txt += "#define INSN_TYPE_C\n"
209 elif insn.startswith("fmv") or \
210 insn.startswith("fcvt") or \
211 insn.startswith("fsgn"):
212 txt += "#define INSN_TYPE_FP_DUALOP\n"
213 elif insn.startswith("feq") or \
214 insn.startswith("flt") or \
215 insn.startswith("fle"):
216 txt += "#define INSN_TYPE_FP_BRANCH\n"
217 if twin_predication:
218 txt += "\n#define INSN_CATEGORY_TWINPREDICATION\n"
219 txt += find_registers(fname, insn, twin_predication,
220 immed_offset, is_branch)
221 f.write(txt)