]>
Commit | Line | Data |
---|---|---|
b9249c46 MF |
1 | /* RISC-V simulator. |
2 | ||
1d506c26 | 3 | Copyright (C) 2005-2024 Free Software Foundation, Inc. |
b9249c46 MF |
4 | Contributed by Mike Frysinger. |
5 | ||
6 | This file is part of simulators. | |
7 | ||
8 | This program is free software; you can redistribute it and/or modify | |
9 | it under the terms of the GNU General Public License as published by | |
10 | the Free Software Foundation; either version 3 of the License, or | |
11 | (at your option) any later version. | |
12 | ||
13 | This program is distributed in the hope that it will be useful, | |
14 | but WITHOUT ANY WARRANTY; without even the implied warranty of | |
15 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
16 | GNU General Public License for more details. | |
17 | ||
18 | You should have received a copy of the GNU General Public License | |
19 | along with this program. If not, see <http://www.gnu.org/licenses/>. */ | |
20 | ||
21 | /* This file contains the main simulator decoding logic. i.e. everything that | |
22 | is architecture specific. */ | |
23 | ||
6df01ab8 MF |
24 | /* This must come before any other includes. */ |
25 | #include "defs.h" | |
b9249c46 MF |
26 | |
27 | #include <inttypes.h> | |
28 | #include <time.h> | |
29 | ||
30 | #include "sim-main.h" | |
1fef66b0 | 31 | #include "sim-signal.h" |
b9249c46 MF |
32 | #include "sim-syscall.h" |
33 | ||
34 | #include "opcode/riscv.h" | |
35 | ||
d026e67e | 36 | #include "sim/sim-riscv.h" |
f3e1a3e6 MF |
37 | |
38 | #include "riscv-sim.h" | |
b9249c46 MF |
39 | \f |
40 | #define TRACE_REG(cpu, reg) \ | |
41 | TRACE_REGISTER (cpu, "wrote %s = %#" PRIxTW, riscv_gpr_names_abi[reg], \ | |
5409cab7 | 42 | RISCV_SIM_CPU (cpu)->regs[reg]) |
b9249c46 MF |
43 | \f |
44 | static const struct riscv_opcode *riscv_hash[OP_MASK_OP + 1]; | |
45 | #define OP_HASH_IDX(i) ((i) & (riscv_insn_length (i) == 2 ? 0x3 : 0x7f)) | |
46 | ||
47 | #define RISCV_ASSERT_RV32(cpu, fmt, args...) \ | |
48 | do { \ | |
49 | if (RISCV_XLEN (cpu) != 32) \ | |
50 | { \ | |
b9249c46 | 51 | TRACE_INSN (cpu, "RV32I-only " fmt, ## args); \ |
f0fcc327 MF |
52 | sim_engine_halt (CPU_STATE (cpu), cpu, NULL, sim_pc_get (cpu), \ |
53 | sim_signalled, SIM_SIGILL); \ | |
b9249c46 MF |
54 | } \ |
55 | } while (0) | |
56 | ||
57 | #define RISCV_ASSERT_RV64(cpu, fmt, args...) \ | |
58 | do { \ | |
59 | if (RISCV_XLEN (cpu) != 64) \ | |
60 | { \ | |
b9249c46 | 61 | TRACE_INSN (cpu, "RV64I-only " fmt, ## args); \ |
f0fcc327 MF |
62 | sim_engine_halt (CPU_STATE (cpu), cpu, NULL, sim_pc_get (cpu), \ |
63 | sim_signalled, SIM_SIGILL); \ | |
b9249c46 MF |
64 | } \ |
65 | } while (0) | |
66 | ||
67 | static INLINE void | |
68 | store_rd (SIM_CPU *cpu, int rd, unsigned_word val) | |
69 | { | |
5409cab7 MF |
70 | struct riscv_sim_cpu *riscv_cpu = RISCV_SIM_CPU (cpu); |
71 | ||
b9249c46 MF |
72 | if (rd) |
73 | { | |
5409cab7 | 74 | riscv_cpu->regs[rd] = val; |
b9249c46 MF |
75 | TRACE_REG (cpu, rd); |
76 | } | |
77 | } | |
78 | ||
79 | static INLINE unsigned_word | |
80 | fetch_csr (SIM_CPU *cpu, const char *name, int csr, unsigned_word *reg) | |
81 | { | |
82 | /* Handle pseudo registers. */ | |
83 | switch (csr) | |
84 | { | |
85 | /* Allow certain registers only in respective modes. */ | |
86 | case CSR_CYCLEH: | |
87 | case CSR_INSTRETH: | |
88 | case CSR_TIMEH: | |
89 | RISCV_ASSERT_RV32 (cpu, "CSR: %s", name); | |
90 | break; | |
91 | } | |
92 | ||
93 | return *reg; | |
94 | } | |
95 | ||
96 | static INLINE void | |
97 | store_csr (SIM_CPU *cpu, const char *name, int csr, unsigned_word *reg, | |
98 | unsigned_word val) | |
99 | { | |
5409cab7 MF |
100 | struct riscv_sim_cpu *riscv_cpu = RISCV_SIM_CPU (cpu); |
101 | ||
b9249c46 MF |
102 | switch (csr) |
103 | { | |
104 | /* These are pseudo registers that modify sub-fields of fcsr. */ | |
105 | case CSR_FRM: | |
106 | val &= 0x7; | |
107 | *reg = val; | |
5409cab7 | 108 | riscv_cpu->csr.fcsr = (riscv_cpu->csr.fcsr & ~0xe0) | (val << 5); |
b9249c46 MF |
109 | break; |
110 | case CSR_FFLAGS: | |
111 | val &= 0x1f; | |
112 | *reg = val; | |
5409cab7 | 113 | riscv_cpu->csr.fcsr = (riscv_cpu->csr.fcsr & ~0x1f) | val; |
b9249c46 MF |
114 | break; |
115 | /* Keep the sub-fields in sync. */ | |
116 | case CSR_FCSR: | |
117 | *reg = val; | |
5409cab7 MF |
118 | riscv_cpu->csr.frm = (val >> 5) & 0x7; |
119 | riscv_cpu->csr.fflags = val & 0x1f; | |
b9249c46 MF |
120 | break; |
121 | ||
122 | /* Allow certain registers only in respective modes. */ | |
123 | case CSR_CYCLEH: | |
124 | case CSR_INSTRETH: | |
125 | case CSR_TIMEH: | |
126 | RISCV_ASSERT_RV32 (cpu, "CSR: %s", name); | |
05b9feff | 127 | ATTRIBUTE_FALLTHROUGH; |
b9249c46 MF |
128 | |
129 | /* All the rest are immutable. */ | |
130 | default: | |
131 | val = *reg; | |
132 | break; | |
133 | } | |
134 | ||
135 | TRACE_REGISTER (cpu, "wrote CSR %s = %#" PRIxTW, name, val); | |
136 | } | |
137 | ||
138 | static inline unsigned_word | |
139 | ashiftrt (unsigned_word val, unsigned_word shift) | |
140 | { | |
5a33ead2 | 141 | uint32_t sign = (val & 0x80000000) ? ~(0xfffffffful >> shift) : 0; |
b9249c46 MF |
142 | return (val >> shift) | sign; |
143 | } | |
144 | ||
145 | static inline unsigned_word | |
146 | ashiftrt64 (unsigned_word val, unsigned_word shift) | |
147 | { | |
5a33ead2 | 148 | uint64_t sign = |
b9249c46 MF |
149 | (val & 0x8000000000000000ull) ? ~(0xffffffffffffffffull >> shift) : 0; |
150 | return (val >> shift) | sign; | |
151 | } | |
152 | ||
153 | static sim_cia | |
154 | execute_i (SIM_CPU *cpu, unsigned_word iw, const struct riscv_opcode *op) | |
155 | { | |
156 | SIM_DESC sd = CPU_STATE (cpu); | |
5409cab7 | 157 | struct riscv_sim_cpu *riscv_cpu = RISCV_SIM_CPU (cpu); |
b9249c46 MF |
158 | int rd = (iw >> OP_SH_RD) & OP_MASK_RD; |
159 | int rs1 = (iw >> OP_SH_RS1) & OP_MASK_RS1; | |
160 | int rs2 = (iw >> OP_SH_RS2) & OP_MASK_RS2; | |
161 | const char *rd_name = riscv_gpr_names_abi[rd]; | |
162 | const char *rs1_name = riscv_gpr_names_abi[rs1]; | |
163 | const char *rs2_name = riscv_gpr_names_abi[rs2]; | |
164 | unsigned int csr = (iw >> OP_SH_CSR) & OP_MASK_CSR; | |
165 | unsigned_word i_imm = EXTRACT_ITYPE_IMM (iw); | |
5a33ead2 | 166 | unsigned_word u_imm = EXTRACT_UTYPE_IMM ((uint64_t) iw); |
b9249c46 | 167 | unsigned_word s_imm = EXTRACT_STYPE_IMM (iw); |
5a9f5403 | 168 | unsigned_word sb_imm = EXTRACT_BTYPE_IMM (iw); |
b9249c46 MF |
169 | unsigned_word shamt_imm = ((iw >> OP_SH_SHAMT) & OP_MASK_SHAMT); |
170 | unsigned_word tmp; | |
5409cab7 | 171 | sim_cia pc = riscv_cpu->pc + 4; |
b9249c46 MF |
172 | |
173 | TRACE_EXTRACT (cpu, | |
174 | "rd:%-2i:%-4s " | |
175 | "rs1:%-2i:%-4s %0*" PRIxTW " " | |
176 | "rs2:%-2i:%-4s %0*" PRIxTW " " | |
177 | "match:%#x mask:%#x", | |
178 | rd, rd_name, | |
5409cab7 MF |
179 | rs1, rs1_name, (int) sizeof (unsigned_word) * 2, |
180 | riscv_cpu->regs[rs1], | |
181 | rs2, rs2_name, (int) sizeof (unsigned_word) * 2, | |
182 | riscv_cpu->regs[rs2], | |
b9249c46 MF |
183 | (unsigned) op->match, (unsigned) op->mask); |
184 | ||
185 | switch (op->match) | |
186 | { | |
187 | case MATCH_ADD: | |
188 | TRACE_INSN (cpu, "add %s, %s, %s; // %s = %s + %s", | |
189 | rd_name, rs1_name, rs2_name, rd_name, rs1_name, rs2_name); | |
5409cab7 | 190 | store_rd (cpu, rd, riscv_cpu->regs[rs1] + riscv_cpu->regs[rs2]); |
b9249c46 MF |
191 | break; |
192 | case MATCH_ADDW: | |
193 | TRACE_INSN (cpu, "addw %s, %s, %s; // %s = %s + %s", | |
194 | rd_name, rs1_name, rs2_name, rd_name, rs1_name, rs2_name); | |
195 | RISCV_ASSERT_RV64 (cpu, "insn: %s", op->name); | |
5409cab7 MF |
196 | store_rd (cpu, rd, |
197 | EXTEND32 (riscv_cpu->regs[rs1] + riscv_cpu->regs[rs2])); | |
b9249c46 MF |
198 | break; |
199 | case MATCH_ADDI: | |
200 | TRACE_INSN (cpu, "addi %s, %s, %#" PRIxTW "; // %s = %s + %#" PRIxTW, | |
201 | rd_name, rs1_name, i_imm, rd_name, rs1_name, i_imm); | |
5409cab7 | 202 | store_rd (cpu, rd, riscv_cpu->regs[rs1] + i_imm); |
b9249c46 MF |
203 | break; |
204 | case MATCH_ADDIW: | |
205 | TRACE_INSN (cpu, "addiw %s, %s, %#" PRIxTW "; // %s = %s + %#" PRIxTW, | |
206 | rd_name, rs1_name, i_imm, rd_name, rs1_name, i_imm); | |
207 | RISCV_ASSERT_RV64 (cpu, "insn: %s", op->name); | |
5409cab7 | 208 | store_rd (cpu, rd, EXTEND32 (riscv_cpu->regs[rs1] + i_imm)); |
b9249c46 MF |
209 | break; |
210 | case MATCH_AND: | |
211 | TRACE_INSN (cpu, "and %s, %s, %s; // %s = %s & %s", | |
212 | rd_name, rs1_name, rs2_name, rd_name, rs1_name, rs2_name); | |
5409cab7 | 213 | store_rd (cpu, rd, riscv_cpu->regs[rs1] & riscv_cpu->regs[rs2]); |
b9249c46 MF |
214 | break; |
215 | case MATCH_ANDI: | |
216 | TRACE_INSN (cpu, "andi %s, %s, %" PRIiTW "; // %s = %s & %#" PRIxTW, | |
217 | rd_name, rs1_name, i_imm, rd_name, rs1_name, i_imm); | |
5409cab7 | 218 | store_rd (cpu, rd, riscv_cpu->regs[rs1] & i_imm); |
b9249c46 MF |
219 | break; |
220 | case MATCH_OR: | |
221 | TRACE_INSN (cpu, "or %s, %s, %s; // %s = %s | %s", | |
222 | rd_name, rs1_name, rs2_name, rd_name, rs1_name, rs2_name); | |
5409cab7 | 223 | store_rd (cpu, rd, riscv_cpu->regs[rs1] | riscv_cpu->regs[rs2]); |
b9249c46 MF |
224 | break; |
225 | case MATCH_ORI: | |
226 | TRACE_INSN (cpu, "ori %s, %s, %" PRIiTW "; // %s = %s | %#" PRIxTW, | |
227 | rd_name, rs1_name, i_imm, rd_name, rs1_name, i_imm); | |
5409cab7 | 228 | store_rd (cpu, rd, riscv_cpu->regs[rs1] | i_imm); |
b9249c46 MF |
229 | break; |
230 | case MATCH_XOR: | |
231 | TRACE_INSN (cpu, "xor %s, %s, %s; // %s = %s ^ %s", | |
232 | rd_name, rs1_name, rs2_name, rd_name, rs1_name, rs2_name); | |
5409cab7 | 233 | store_rd (cpu, rd, riscv_cpu->regs[rs1] ^ riscv_cpu->regs[rs2]); |
b9249c46 MF |
234 | break; |
235 | case MATCH_XORI: | |
236 | TRACE_INSN (cpu, "xori %s, %s, %" PRIiTW "; // %s = %s ^ %#" PRIxTW, | |
237 | rd_name, rs1_name, i_imm, rd_name, rs1_name, i_imm); | |
5409cab7 | 238 | store_rd (cpu, rd, riscv_cpu->regs[rs1] ^ i_imm); |
b9249c46 MF |
239 | break; |
240 | case MATCH_SUB: | |
241 | TRACE_INSN (cpu, "sub %s, %s, %s; // %s = %s - %s", | |
242 | rd_name, rs1_name, rs2_name, rd_name, rs1_name, rs2_name); | |
5409cab7 | 243 | store_rd (cpu, rd, riscv_cpu->regs[rs1] - riscv_cpu->regs[rs2]); |
b9249c46 MF |
244 | break; |
245 | case MATCH_SUBW: | |
246 | TRACE_INSN (cpu, "subw %s, %s, %s; // %s = %s - %s", | |
247 | rd_name, rs1_name, rs2_name, rd_name, rs1_name, rs2_name); | |
248 | RISCV_ASSERT_RV64 (cpu, "insn: %s", op->name); | |
5409cab7 MF |
249 | store_rd (cpu, rd, |
250 | EXTEND32 (riscv_cpu->regs[rs1] - riscv_cpu->regs[rs2])); | |
b9249c46 MF |
251 | break; |
252 | case MATCH_LUI: | |
253 | TRACE_INSN (cpu, "lui %s, %#" PRIxTW ";", rd_name, u_imm); | |
254 | store_rd (cpu, rd, u_imm); | |
255 | break; | |
256 | case MATCH_SLL: | |
257 | TRACE_INSN (cpu, "sll %s, %s, %s; // %s = %s << %s", | |
258 | rd_name, rs1_name, rs2_name, rd_name, rs1_name, rs2_name); | |
259 | u_imm = RISCV_XLEN (cpu) == 32 ? 0x1f : 0x3f; | |
5409cab7 MF |
260 | store_rd (cpu, rd, |
261 | riscv_cpu->regs[rs1] << (riscv_cpu->regs[rs2] & u_imm)); | |
b9249c46 MF |
262 | break; |
263 | case MATCH_SLLW: | |
264 | TRACE_INSN (cpu, "sllw %s, %s, %s; // %s = %s << %s", | |
265 | rd_name, rs1_name, rs2_name, rd_name, rs1_name, rs2_name); | |
266 | RISCV_ASSERT_RV64 (cpu, "insn: %s", op->name); | |
267 | store_rd (cpu, rd, EXTEND32 ( | |
5409cab7 | 268 | (uint32_t) riscv_cpu->regs[rs1] << (riscv_cpu->regs[rs2] & 0x1f))); |
b9249c46 MF |
269 | break; |
270 | case MATCH_SLLI: | |
271 | TRACE_INSN (cpu, "slli %s, %s, %" PRIiTW "; // %s = %s << %#" PRIxTW, | |
272 | rd_name, rs1_name, shamt_imm, rd_name, rs1_name, shamt_imm); | |
273 | if (RISCV_XLEN (cpu) == 32 && shamt_imm > 0x1f) | |
5409cab7 MF |
274 | sim_engine_halt (sd, cpu, NULL, riscv_cpu->pc, sim_signalled, |
275 | SIM_SIGILL); | |
276 | store_rd (cpu, rd, riscv_cpu->regs[rs1] << shamt_imm); | |
b9249c46 MF |
277 | break; |
278 | case MATCH_SLLIW: | |
279 | TRACE_INSN (cpu, "slliw %s, %s, %" PRIiTW "; // %s = %s << %#" PRIxTW, | |
280 | rd_name, rs1_name, shamt_imm, rd_name, rs1_name, shamt_imm); | |
281 | RISCV_ASSERT_RV64 (cpu, "insn: %s", op->name); | |
5409cab7 MF |
282 | store_rd (cpu, rd, |
283 | EXTEND32 ((uint32_t) riscv_cpu->regs[rs1] << shamt_imm)); | |
b9249c46 MF |
284 | break; |
285 | case MATCH_SRL: | |
286 | TRACE_INSN (cpu, "srl %s, %s, %s; // %s = %s >> %s", | |
287 | rd_name, rs1_name, rs2_name, rd_name, rs1_name, rs2_name); | |
288 | u_imm = RISCV_XLEN (cpu) == 32 ? 0x1f : 0x3f; | |
5409cab7 MF |
289 | store_rd (cpu, rd, |
290 | riscv_cpu->regs[rs1] >> (riscv_cpu->regs[rs2] & u_imm)); | |
b9249c46 MF |
291 | break; |
292 | case MATCH_SRLW: | |
293 | TRACE_INSN (cpu, "srlw %s, %s, %s; // %s = %s >> %s", | |
294 | rd_name, rs1_name, rs2_name, rd_name, rs1_name, rs2_name); | |
295 | RISCV_ASSERT_RV64 (cpu, "insn: %s", op->name); | |
296 | store_rd (cpu, rd, EXTEND32 ( | |
5409cab7 | 297 | (uint32_t) riscv_cpu->regs[rs1] >> (riscv_cpu->regs[rs2] & 0x1f))); |
b9249c46 MF |
298 | break; |
299 | case MATCH_SRLI: | |
300 | TRACE_INSN (cpu, "srli %s, %s, %" PRIiTW "; // %s = %s >> %#" PRIxTW, | |
301 | rd_name, rs1_name, shamt_imm, rd_name, rs1_name, shamt_imm); | |
302 | if (RISCV_XLEN (cpu) == 32 && shamt_imm > 0x1f) | |
5409cab7 MF |
303 | sim_engine_halt (sd, cpu, NULL, riscv_cpu->pc, sim_signalled, |
304 | SIM_SIGILL); | |
305 | store_rd (cpu, rd, riscv_cpu->regs[rs1] >> shamt_imm); | |
b9249c46 MF |
306 | break; |
307 | case MATCH_SRLIW: | |
308 | TRACE_INSN (cpu, "srliw %s, %s, %" PRIiTW "; // %s = %s >> %#" PRIxTW, | |
309 | rd_name, rs1_name, shamt_imm, rd_name, rs1_name, shamt_imm); | |
310 | RISCV_ASSERT_RV64 (cpu, "insn: %s", op->name); | |
5409cab7 MF |
311 | store_rd (cpu, rd, |
312 | EXTEND32 ((uint32_t) riscv_cpu->regs[rs1] >> shamt_imm)); | |
b9249c46 MF |
313 | break; |
314 | case MATCH_SRA: | |
315 | TRACE_INSN (cpu, "sra %s, %s, %s; // %s = %s >>> %s", | |
316 | rd_name, rs1_name, rs2_name, rd_name, rs1_name, rs2_name); | |
317 | if (RISCV_XLEN (cpu) == 32) | |
5409cab7 | 318 | tmp = ashiftrt (riscv_cpu->regs[rs1], riscv_cpu->regs[rs2] & 0x1f); |
b9249c46 | 319 | else |
5409cab7 | 320 | tmp = ashiftrt64 (riscv_cpu->regs[rs1], riscv_cpu->regs[rs2] & 0x3f); |
b9249c46 MF |
321 | store_rd (cpu, rd, tmp); |
322 | break; | |
323 | case MATCH_SRAW: | |
324 | TRACE_INSN (cpu, "sraw %s, %s, %s; // %s = %s >>> %s", | |
325 | rd_name, rs1_name, rs2_name, rd_name, rs1_name, rs2_name); | |
326 | RISCV_ASSERT_RV64 (cpu, "insn: %s", op->name); | |
327 | store_rd (cpu, rd, EXTEND32 ( | |
5409cab7 MF |
328 | ashiftrt ((int32_t) riscv_cpu->regs[rs1], |
329 | riscv_cpu->regs[rs2] & 0x1f))); | |
b9249c46 MF |
330 | break; |
331 | case MATCH_SRAI: | |
332 | TRACE_INSN (cpu, "srai %s, %s, %" PRIiTW "; // %s = %s >>> %#" PRIxTW, | |
333 | rd_name, rs1_name, shamt_imm, rd_name, rs1_name, shamt_imm); | |
334 | if (RISCV_XLEN (cpu) == 32) | |
335 | { | |
336 | if (shamt_imm > 0x1f) | |
5409cab7 MF |
337 | sim_engine_halt (sd, cpu, NULL, riscv_cpu->pc, sim_signalled, |
338 | SIM_SIGILL); | |
339 | tmp = ashiftrt (riscv_cpu->regs[rs1], shamt_imm); | |
b9249c46 MF |
340 | } |
341 | else | |
5409cab7 | 342 | tmp = ashiftrt64 (riscv_cpu->regs[rs1], shamt_imm); |
b9249c46 MF |
343 | store_rd (cpu, rd, tmp); |
344 | break; | |
345 | case MATCH_SRAIW: | |
346 | TRACE_INSN (cpu, "sraiw %s, %s, %" PRIiTW "; // %s = %s >>> %#" PRIxTW, | |
347 | rd_name, rs1_name, shamt_imm, rd_name, rs1_name, shamt_imm); | |
348 | RISCV_ASSERT_RV64 (cpu, "insn: %s", op->name); | |
349 | store_rd (cpu, rd, EXTEND32 ( | |
5409cab7 | 350 | ashiftrt ((int32_t) riscv_cpu->regs[rs1], shamt_imm))); |
b9249c46 MF |
351 | break; |
352 | case MATCH_SLT: | |
353 | TRACE_INSN (cpu, "slt"); | |
354 | store_rd (cpu, rd, | |
5409cab7 MF |
355 | !!((signed_word) riscv_cpu->regs[rs1] < |
356 | (signed_word) riscv_cpu->regs[rs2])); | |
b9249c46 MF |
357 | break; |
358 | case MATCH_SLTU: | |
359 | TRACE_INSN (cpu, "sltu"); | |
5409cab7 MF |
360 | store_rd (cpu, rd, !!((unsigned_word) riscv_cpu->regs[rs1] < |
361 | (unsigned_word) riscv_cpu->regs[rs2])); | |
b9249c46 MF |
362 | break; |
363 | case MATCH_SLTI: | |
364 | TRACE_INSN (cpu, "slti"); | |
5409cab7 | 365 | store_rd (cpu, rd, !!((signed_word) riscv_cpu->regs[rs1] < |
b9249c46 MF |
366 | (signed_word) i_imm)); |
367 | break; | |
368 | case MATCH_SLTIU: | |
369 | TRACE_INSN (cpu, "sltiu"); | |
5409cab7 | 370 | store_rd (cpu, rd, !!((unsigned_word) riscv_cpu->regs[rs1] < |
b9249c46 MF |
371 | (unsigned_word) i_imm)); |
372 | break; | |
373 | case MATCH_AUIPC: | |
374 | TRACE_INSN (cpu, "auipc %s, %" PRIiTW "; // %s = pc + %" PRIiTW, | |
375 | rd_name, u_imm, rd_name, u_imm); | |
5409cab7 | 376 | store_rd (cpu, rd, riscv_cpu->pc + u_imm); |
b9249c46 MF |
377 | break; |
378 | case MATCH_BEQ: | |
379 | TRACE_INSN (cpu, "beq %s, %s, %#" PRIxTW "; " | |
380 | "// if (%s == %s) goto %#" PRIxTW, | |
381 | rs1_name, rs2_name, sb_imm, rs1_name, rs2_name, sb_imm); | |
5409cab7 | 382 | if (riscv_cpu->regs[rs1] == riscv_cpu->regs[rs2]) |
b9249c46 | 383 | { |
5409cab7 | 384 | pc = riscv_cpu->pc + sb_imm; |
b9249c46 MF |
385 | TRACE_BRANCH (cpu, "to %#" PRIxTW, pc); |
386 | } | |
387 | break; | |
388 | case MATCH_BLT: | |
389 | TRACE_INSN (cpu, "blt %s, %s, %#" PRIxTW "; " | |
390 | "// if (%s < %s) goto %#" PRIxTW, | |
391 | rs1_name, rs2_name, sb_imm, rs1_name, rs2_name, sb_imm); | |
5409cab7 MF |
392 | if ((signed_word) riscv_cpu->regs[rs1] < |
393 | (signed_word) riscv_cpu->regs[rs2]) | |
b9249c46 | 394 | { |
5409cab7 | 395 | pc = riscv_cpu->pc + sb_imm; |
b9249c46 MF |
396 | TRACE_BRANCH (cpu, "to %#" PRIxTW, pc); |
397 | } | |
398 | break; | |
399 | case MATCH_BLTU: | |
400 | TRACE_INSN (cpu, "bltu %s, %s, %#" PRIxTW "; " | |
401 | "// if (%s < %s) goto %#" PRIxTW, | |
402 | rs1_name, rs2_name, sb_imm, rs1_name, rs2_name, sb_imm); | |
5409cab7 MF |
403 | if ((unsigned_word) riscv_cpu->regs[rs1] < |
404 | (unsigned_word) riscv_cpu->regs[rs2]) | |
b9249c46 | 405 | { |
5409cab7 | 406 | pc = riscv_cpu->pc + sb_imm; |
b9249c46 MF |
407 | TRACE_BRANCH (cpu, "to %#" PRIxTW, pc); |
408 | } | |
409 | break; | |
410 | case MATCH_BGE: | |
411 | TRACE_INSN (cpu, "bge %s, %s, %#" PRIxTW "; " | |
412 | "// if (%s >= %s) goto %#" PRIxTW, | |
413 | rs1_name, rs2_name, sb_imm, rs1_name, rs2_name, sb_imm); | |
5409cab7 MF |
414 | if ((signed_word) riscv_cpu->regs[rs1] >= |
415 | (signed_word) riscv_cpu->regs[rs2]) | |
b9249c46 | 416 | { |
5409cab7 | 417 | pc = riscv_cpu->pc + sb_imm; |
b9249c46 MF |
418 | TRACE_BRANCH (cpu, "to %#" PRIxTW, pc); |
419 | } | |
420 | break; | |
421 | case MATCH_BGEU: | |
422 | TRACE_INSN (cpu, "bgeu %s, %s, %#" PRIxTW "; " | |
423 | "// if (%s >= %s) goto %#" PRIxTW, | |
424 | rs1_name, rs2_name, sb_imm, rs1_name, rs2_name, sb_imm); | |
5409cab7 MF |
425 | if ((unsigned_word) riscv_cpu->regs[rs1] >= |
426 | (unsigned_word) riscv_cpu->regs[rs2]) | |
b9249c46 | 427 | { |
5409cab7 | 428 | pc = riscv_cpu->pc + sb_imm; |
b9249c46 MF |
429 | TRACE_BRANCH (cpu, "to %#" PRIxTW, pc); |
430 | } | |
431 | break; | |
432 | case MATCH_BNE: | |
433 | TRACE_INSN (cpu, "bne %s, %s, %#" PRIxTW "; " | |
434 | "// if (%s != %s) goto %#" PRIxTW, | |
435 | rs1_name, rs2_name, sb_imm, rs1_name, rs2_name, sb_imm); | |
5409cab7 | 436 | if (riscv_cpu->regs[rs1] != riscv_cpu->regs[rs2]) |
b9249c46 | 437 | { |
5409cab7 | 438 | pc = riscv_cpu->pc + sb_imm; |
b9249c46 MF |
439 | TRACE_BRANCH (cpu, "to %#" PRIxTW, pc); |
440 | } | |
441 | break; | |
442 | case MATCH_JAL: | |
443 | TRACE_INSN (cpu, "jal %s, %" PRIiTW ";", rd_name, | |
5a9f5403 | 444 | EXTRACT_JTYPE_IMM (iw)); |
5409cab7 MF |
445 | store_rd (cpu, rd, riscv_cpu->pc + 4); |
446 | pc = riscv_cpu->pc + EXTRACT_JTYPE_IMM (iw); | |
b9249c46 MF |
447 | TRACE_BRANCH (cpu, "to %#" PRIxTW, pc); |
448 | break; | |
449 | case MATCH_JALR: | |
450 | TRACE_INSN (cpu, "jalr %s, %s, %" PRIiTW ";", rd_name, rs1_name, i_imm); | |
5409cab7 | 451 | pc = riscv_cpu->regs[rs1] + i_imm; |
1c37b309 | 452 | store_rd (cpu, rd, riscv_cpu->pc + 4); |
b9249c46 MF |
453 | TRACE_BRANCH (cpu, "to %#" PRIxTW, pc); |
454 | break; | |
455 | ||
456 | case MATCH_LD: | |
457 | TRACE_INSN (cpu, "ld %s, %" PRIiTW "(%s);", | |
458 | rd_name, i_imm, rs1_name); | |
459 | RISCV_ASSERT_RV64 (cpu, "insn: %s", op->name); | |
460 | store_rd (cpu, rd, | |
5409cab7 MF |
461 | sim_core_read_unaligned_8 (cpu, riscv_cpu->pc, read_map, |
462 | riscv_cpu->regs[rs1] + i_imm)); | |
b9249c46 MF |
463 | break; |
464 | case MATCH_LW: | |
465 | TRACE_INSN (cpu, "lw %s, %" PRIiTW "(%s);", | |
466 | rd_name, i_imm, rs1_name); | |
467 | store_rd (cpu, rd, EXTEND32 ( | |
5409cab7 MF |
468 | sim_core_read_unaligned_4 (cpu, riscv_cpu->pc, read_map, |
469 | riscv_cpu->regs[rs1] + i_imm))); | |
b9249c46 MF |
470 | break; |
471 | case MATCH_LWU: | |
472 | TRACE_INSN (cpu, "lwu %s, %" PRIiTW "(%s);", | |
473 | rd_name, i_imm, rs1_name); | |
474 | store_rd (cpu, rd, | |
5409cab7 MF |
475 | sim_core_read_unaligned_4 (cpu, riscv_cpu->pc, read_map, |
476 | riscv_cpu->regs[rs1] + i_imm)); | |
b9249c46 MF |
477 | break; |
478 | case MATCH_LH: | |
479 | TRACE_INSN (cpu, "lh %s, %" PRIiTW "(%s);", | |
480 | rd_name, i_imm, rs1_name); | |
481 | store_rd (cpu, rd, EXTEND16 ( | |
5409cab7 MF |
482 | sim_core_read_unaligned_2 (cpu, riscv_cpu->pc, read_map, |
483 | riscv_cpu->regs[rs1] + i_imm))); | |
b9249c46 MF |
484 | break; |
485 | case MATCH_LHU: | |
486 | TRACE_INSN (cpu, "lbu %s, %" PRIiTW "(%s);", | |
487 | rd_name, i_imm, rs1_name); | |
488 | store_rd (cpu, rd, | |
5409cab7 MF |
489 | sim_core_read_unaligned_2 (cpu, riscv_cpu->pc, read_map, |
490 | riscv_cpu->regs[rs1] + i_imm)); | |
b9249c46 MF |
491 | break; |
492 | case MATCH_LB: | |
493 | TRACE_INSN (cpu, "lb %s, %" PRIiTW "(%s);", | |
494 | rd_name, i_imm, rs1_name); | |
495 | store_rd (cpu, rd, EXTEND8 ( | |
5409cab7 MF |
496 | sim_core_read_unaligned_1 (cpu, riscv_cpu->pc, read_map, |
497 | riscv_cpu->regs[rs1] + i_imm))); | |
b9249c46 MF |
498 | break; |
499 | case MATCH_LBU: | |
500 | TRACE_INSN (cpu, "lbu %s, %" PRIiTW "(%s);", | |
501 | rd_name, i_imm, rs1_name); | |
502 | store_rd (cpu, rd, | |
5409cab7 MF |
503 | sim_core_read_unaligned_1 (cpu, riscv_cpu->pc, read_map, |
504 | riscv_cpu->regs[rs1] + i_imm)); | |
b9249c46 MF |
505 | break; |
506 | case MATCH_SD: | |
507 | TRACE_INSN (cpu, "sd %s, %" PRIiTW "(%s);", | |
508 | rs2_name, s_imm, rs1_name); | |
509 | RISCV_ASSERT_RV64 (cpu, "insn: %s", op->name); | |
5409cab7 MF |
510 | sim_core_write_unaligned_8 (cpu, riscv_cpu->pc, write_map, |
511 | riscv_cpu->regs[rs1] + s_imm, | |
512 | riscv_cpu->regs[rs2]); | |
b9249c46 MF |
513 | break; |
514 | case MATCH_SW: | |
515 | TRACE_INSN (cpu, "sw %s, %" PRIiTW "(%s);", | |
516 | rs2_name, s_imm, rs1_name); | |
5409cab7 MF |
517 | sim_core_write_unaligned_4 (cpu, riscv_cpu->pc, write_map, |
518 | riscv_cpu->regs[rs1] + s_imm, | |
519 | riscv_cpu->regs[rs2]); | |
b9249c46 MF |
520 | break; |
521 | case MATCH_SH: | |
522 | TRACE_INSN (cpu, "sh %s, %" PRIiTW "(%s);", | |
523 | rs2_name, s_imm, rs1_name); | |
5409cab7 MF |
524 | sim_core_write_unaligned_2 (cpu, riscv_cpu->pc, write_map, |
525 | riscv_cpu->regs[rs1] + s_imm, | |
526 | riscv_cpu->regs[rs2]); | |
b9249c46 MF |
527 | break; |
528 | case MATCH_SB: | |
529 | TRACE_INSN (cpu, "sb %s, %" PRIiTW "(%s);", | |
530 | rs2_name, s_imm, rs1_name); | |
5409cab7 MF |
531 | sim_core_write_unaligned_1 (cpu, riscv_cpu->pc, write_map, |
532 | riscv_cpu->regs[rs1] + s_imm, | |
533 | riscv_cpu->regs[rs2]); | |
b9249c46 MF |
534 | break; |
535 | ||
536 | case MATCH_CSRRC: | |
537 | TRACE_INSN (cpu, "csrrc"); | |
538 | switch (csr) | |
539 | { | |
540 | #define DECLARE_CSR(name, num, ...) \ | |
541 | case num: \ | |
5409cab7 MF |
542 | store_rd (cpu, rd, \ |
543 | fetch_csr (cpu, #name, num, &riscv_cpu->csr.name)); \ | |
544 | store_csr (cpu, #name, num, &riscv_cpu->csr.name, \ | |
545 | riscv_cpu->csr.name & !riscv_cpu->regs[rs1]); \ | |
b9249c46 MF |
546 | break; |
547 | #include "opcode/riscv-opc.h" | |
548 | #undef DECLARE_CSR | |
549 | } | |
550 | break; | |
551 | case MATCH_CSRRS: | |
552 | TRACE_INSN (cpu, "csrrs"); | |
553 | switch (csr) | |
554 | { | |
555 | #define DECLARE_CSR(name, num, ...) \ | |
556 | case num: \ | |
5409cab7 MF |
557 | store_rd (cpu, rd, \ |
558 | fetch_csr (cpu, #name, num, &riscv_cpu->csr.name)); \ | |
559 | store_csr (cpu, #name, num, &riscv_cpu->csr.name, \ | |
560 | riscv_cpu->csr.name | riscv_cpu->regs[rs1]); \ | |
b9249c46 MF |
561 | break; |
562 | #include "opcode/riscv-opc.h" | |
563 | #undef DECLARE_CSR | |
564 | } | |
565 | break; | |
566 | case MATCH_CSRRW: | |
567 | TRACE_INSN (cpu, "csrrw"); | |
568 | switch (csr) | |
569 | { | |
570 | #define DECLARE_CSR(name, num, ...) \ | |
571 | case num: \ | |
5409cab7 MF |
572 | store_rd (cpu, rd, \ |
573 | fetch_csr (cpu, #name, num, &riscv_cpu->csr.name)); \ | |
574 | store_csr (cpu, #name, num, &riscv_cpu->csr.name, \ | |
575 | riscv_cpu->regs[rs1]); \ | |
b9249c46 MF |
576 | break; |
577 | #include "opcode/riscv-opc.h" | |
578 | #undef DECLARE_CSR | |
579 | } | |
580 | break; | |
581 | ||
582 | case MATCH_RDCYCLE: | |
583 | TRACE_INSN (cpu, "rdcycle %s;", rd_name); | |
5409cab7 MF |
584 | store_rd (cpu, rd, |
585 | fetch_csr (cpu, "cycle", CSR_CYCLE, &riscv_cpu->csr.cycle)); | |
b9249c46 MF |
586 | break; |
587 | case MATCH_RDCYCLEH: | |
588 | TRACE_INSN (cpu, "rdcycleh %s;", rd_name); | |
589 | RISCV_ASSERT_RV32 (cpu, "insn: %s", op->name); | |
590 | store_rd (cpu, rd, | |
5409cab7 | 591 | fetch_csr (cpu, "cycleh", CSR_CYCLEH, &riscv_cpu->csr.cycleh)); |
b9249c46 MF |
592 | break; |
593 | case MATCH_RDINSTRET: | |
594 | TRACE_INSN (cpu, "rdinstret %s;", rd_name); | |
595 | store_rd (cpu, rd, | |
5409cab7 MF |
596 | fetch_csr (cpu, "instret", CSR_INSTRET, |
597 | &riscv_cpu->csr.instret)); | |
b9249c46 MF |
598 | break; |
599 | case MATCH_RDINSTRETH: | |
600 | TRACE_INSN (cpu, "rdinstreth %s;", rd_name); | |
601 | RISCV_ASSERT_RV32 (cpu, "insn: %s", op->name); | |
602 | store_rd (cpu, rd, | |
5409cab7 MF |
603 | fetch_csr (cpu, "instreth", CSR_INSTRETH, |
604 | &riscv_cpu->csr.instreth)); | |
b9249c46 MF |
605 | break; |
606 | case MATCH_RDTIME: | |
607 | TRACE_INSN (cpu, "rdtime %s;", rd_name); | |
5409cab7 MF |
608 | store_rd (cpu, rd, |
609 | fetch_csr (cpu, "time", CSR_TIME, &riscv_cpu->csr.time)); | |
b9249c46 MF |
610 | break; |
611 | case MATCH_RDTIMEH: | |
612 | TRACE_INSN (cpu, "rdtimeh %s;", rd_name); | |
613 | RISCV_ASSERT_RV32 (cpu, "insn: %s", op->name); | |
5409cab7 MF |
614 | store_rd (cpu, rd, |
615 | fetch_csr (cpu, "timeh", CSR_TIMEH, &riscv_cpu->csr.timeh)); | |
b9249c46 MF |
616 | break; |
617 | ||
618 | case MATCH_FENCE: | |
619 | TRACE_INSN (cpu, "fence;"); | |
620 | break; | |
621 | case MATCH_FENCE_I: | |
622 | TRACE_INSN (cpu, "fence.i;"); | |
623 | break; | |
b9593cb7 TO |
624 | case MATCH_EBREAK: |
625 | TRACE_INSN (cpu, "ebreak;"); | |
626 | /* GDB expects us to step over EBREAK. */ | |
5409cab7 MF |
627 | sim_engine_halt (sd, cpu, NULL, riscv_cpu->pc + 4, sim_stopped, |
628 | SIM_SIGTRAP); | |
b9249c46 MF |
629 | break; |
630 | case MATCH_ECALL: | |
631 | TRACE_INSN (cpu, "ecall;"); | |
5409cab7 MF |
632 | riscv_cpu->a0 = sim_syscall (cpu, riscv_cpu->a7, riscv_cpu->a0, |
633 | riscv_cpu->a1, riscv_cpu->a2, riscv_cpu->a3); | |
b9249c46 MF |
634 | break; |
635 | default: | |
636 | TRACE_INSN (cpu, "UNHANDLED INSN: %s", op->name); | |
5409cab7 | 637 | sim_engine_halt (sd, cpu, NULL, riscv_cpu->pc, sim_signalled, SIM_SIGILL); |
b9249c46 MF |
638 | } |
639 | ||
640 | return pc; | |
641 | } | |
642 | ||
5a33ead2 MF |
643 | static uint64_t |
644 | mulhu (uint64_t a, uint64_t b) | |
b9249c46 | 645 | { |
79633c12 | 646 | #ifdef HAVE___INT128 |
b9249c46 MF |
647 | return ((__int128)a * b) >> 64; |
648 | #else | |
649 | uint64_t t; | |
650 | uint32_t y1, y2, y3; | |
651 | uint64_t a0 = (uint32_t)a, a1 = a >> 32; | |
652 | uint64_t b0 = (uint32_t)b, b1 = b >> 32; | |
653 | ||
654 | t = a1*b0 + ((a0*b0) >> 32); | |
655 | y1 = t; | |
656 | y2 = t >> 32; | |
657 | ||
658 | t = a0*b1 + y1; | |
659 | y1 = t; | |
660 | ||
661 | t = a1*b1 + y2 + (t >> 32); | |
662 | y2 = t; | |
663 | y3 = t >> 32; | |
664 | ||
665 | return ((uint64_t)y3 << 32) | y2; | |
666 | #endif | |
667 | } | |
668 | ||
5a33ead2 MF |
669 | static uint64_t |
670 | mulh (int64_t a, int64_t b) | |
b9249c46 MF |
671 | { |
672 | int negate = (a < 0) != (b < 0); | |
673 | uint64_t res = mulhu (a < 0 ? -a : a, b < 0 ? -b : b); | |
674 | return negate ? ~res + (a * b == 0) : res; | |
675 | } | |
676 | ||
5a33ead2 MF |
677 | static uint64_t |
678 | mulhsu (int64_t a, uint64_t b) | |
b9249c46 MF |
679 | { |
680 | int negate = a < 0; | |
681 | uint64_t res = mulhu (a < 0 ? -a : a, b); | |
682 | return negate ? ~res + (a * b == 0) : res; | |
683 | } | |
684 | ||
685 | static sim_cia | |
686 | execute_m (SIM_CPU *cpu, unsigned_word iw, const struct riscv_opcode *op) | |
687 | { | |
5409cab7 | 688 | struct riscv_sim_cpu *riscv_cpu = RISCV_SIM_CPU (cpu); |
b9249c46 MF |
689 | SIM_DESC sd = CPU_STATE (cpu); |
690 | int rd = (iw >> OP_SH_RD) & OP_MASK_RD; | |
691 | int rs1 = (iw >> OP_SH_RS1) & OP_MASK_RS1; | |
692 | int rs2 = (iw >> OP_SH_RS2) & OP_MASK_RS2; | |
693 | const char *rd_name = riscv_gpr_names_abi[rd]; | |
694 | const char *rs1_name = riscv_gpr_names_abi[rs1]; | |
695 | const char *rs2_name = riscv_gpr_names_abi[rs2]; | |
696 | unsigned_word tmp, dividend_max; | |
5409cab7 | 697 | sim_cia pc = riscv_cpu->pc + 4; |
b9249c46 MF |
698 | |
699 | dividend_max = -((unsigned_word) 1 << (WITH_TARGET_WORD_BITSIZE - 1)); | |
700 | ||
701 | switch (op->match) | |
702 | { | |
703 | case MATCH_DIV: | |
704 | TRACE_INSN (cpu, "div %s, %s, %s; // %s = %s / %s", | |
705 | rd_name, rs1_name, rs2_name, rd_name, rs1_name, rs2_name); | |
5409cab7 | 706 | if (riscv_cpu->regs[rs1] == dividend_max && riscv_cpu->regs[rs2] == -1) |
b9249c46 | 707 | tmp = dividend_max; |
5409cab7 MF |
708 | else if (riscv_cpu->regs[rs2]) |
709 | tmp = (signed_word) riscv_cpu->regs[rs1] / | |
710 | (signed_word) riscv_cpu->regs[rs2]; | |
b9249c46 MF |
711 | else |
712 | tmp = -1; | |
713 | store_rd (cpu, rd, tmp); | |
714 | break; | |
715 | case MATCH_DIVW: | |
716 | TRACE_INSN (cpu, "divw %s, %s, %s; // %s = %s / %s", | |
717 | rd_name, rs1_name, rs2_name, rd_name, rs1_name, rs2_name); | |
718 | RISCV_ASSERT_RV64 (cpu, "insn: %s", op->name); | |
5409cab7 | 719 | if (EXTEND32 (riscv_cpu->regs[rs2]) == -1) |
b9249c46 | 720 | tmp = 1 << 31; |
5409cab7 MF |
721 | else if (EXTEND32 (riscv_cpu->regs[rs2])) |
722 | tmp = EXTEND32 (riscv_cpu->regs[rs1]) / EXTEND32 (riscv_cpu->regs[rs2]); | |
b9249c46 MF |
723 | else |
724 | tmp = -1; | |
725 | store_rd (cpu, rd, EXTEND32 (tmp)); | |
726 | break; | |
727 | case MATCH_DIVU: | |
728 | TRACE_INSN (cpu, "divu %s, %s, %s; // %s = %s / %s", | |
729 | rd_name, rs1_name, rs2_name, rd_name, rs1_name, rs2_name); | |
5409cab7 MF |
730 | if (riscv_cpu->regs[rs2]) |
731 | store_rd (cpu, rd, (unsigned_word) riscv_cpu->regs[rs1] | |
732 | / (unsigned_word) riscv_cpu->regs[rs2]); | |
b9249c46 MF |
733 | else |
734 | store_rd (cpu, rd, -1); | |
735 | break; | |
736 | case MATCH_DIVUW: | |
737 | TRACE_INSN (cpu, "divuw %s, %s, %s; // %s = %s / %s", | |
738 | rd_name, rs1_name, rs2_name, rd_name, rs1_name, rs2_name); | |
739 | RISCV_ASSERT_RV64 (cpu, "insn: %s", op->name); | |
5409cab7 MF |
740 | if ((uint32_t) riscv_cpu->regs[rs2]) |
741 | tmp = (uint32_t) riscv_cpu->regs[rs1] / (uint32_t) riscv_cpu->regs[rs2]; | |
b9249c46 MF |
742 | else |
743 | tmp = -1; | |
744 | store_rd (cpu, rd, EXTEND32 (tmp)); | |
745 | break; | |
746 | case MATCH_MUL: | |
747 | TRACE_INSN (cpu, "mul %s, %s, %s; // %s = %s * %s", | |
748 | rd_name, rs1_name, rs2_name, rd_name, rs1_name, rs2_name); | |
5409cab7 | 749 | store_rd (cpu, rd, riscv_cpu->regs[rs1] * riscv_cpu->regs[rs2]); |
b9249c46 MF |
750 | break; |
751 | case MATCH_MULW: | |
752 | TRACE_INSN (cpu, "mulw %s, %s, %s; // %s = %s * %s", | |
753 | rd_name, rs1_name, rs2_name, rd_name, rs1_name, rs2_name); | |
754 | RISCV_ASSERT_RV64 (cpu, "insn: %s", op->name); | |
5409cab7 MF |
755 | store_rd (cpu, rd, EXTEND32 ((int32_t) riscv_cpu->regs[rs1] |
756 | * (int32_t) riscv_cpu->regs[rs2])); | |
b9249c46 MF |
757 | break; |
758 | case MATCH_MULH: | |
759 | TRACE_INSN (cpu, "mulh %s, %s, %s; // %s = %s * %s", | |
760 | rd_name, rs1_name, rs2_name, rd_name, rs1_name, rs2_name); | |
761 | if (RISCV_XLEN (cpu) == 32) | |
5409cab7 MF |
762 | store_rd (cpu, rd, |
763 | ((int64_t)(signed_word) riscv_cpu->regs[rs1] | |
764 | * (int64_t)(signed_word) riscv_cpu->regs[rs2]) >> 32); | |
b9249c46 | 765 | else |
5409cab7 | 766 | store_rd (cpu, rd, mulh (riscv_cpu->regs[rs1], riscv_cpu->regs[rs2])); |
b9249c46 MF |
767 | break; |
768 | case MATCH_MULHU: | |
769 | TRACE_INSN (cpu, "mulhu %s, %s, %s; // %s = %s * %s", | |
770 | rd_name, rs1_name, rs2_name, rd_name, rs1_name, rs2_name); | |
771 | if (RISCV_XLEN (cpu) == 32) | |
5409cab7 MF |
772 | store_rd (cpu, rd, ((uint64_t)riscv_cpu->regs[rs1] |
773 | * (uint64_t)riscv_cpu->regs[rs2]) >> 32); | |
b9249c46 | 774 | else |
5409cab7 | 775 | store_rd (cpu, rd, mulhu (riscv_cpu->regs[rs1], riscv_cpu->regs[rs2])); |
b9249c46 MF |
776 | break; |
777 | case MATCH_MULHSU: | |
778 | TRACE_INSN (cpu, "mulhsu %s, %s, %s; // %s = %s * %s", | |
779 | rd_name, rs1_name, rs2_name, rd_name, rs1_name, rs2_name); | |
780 | if (RISCV_XLEN (cpu) == 32) | |
5409cab7 MF |
781 | store_rd (cpu, rd, ((int64_t)(signed_word) riscv_cpu->regs[rs1] |
782 | * (uint64_t)riscv_cpu->regs[rs2]) >> 32); | |
b9249c46 | 783 | else |
5409cab7 | 784 | store_rd (cpu, rd, mulhsu (riscv_cpu->regs[rs1], riscv_cpu->regs[rs2])); |
b9249c46 MF |
785 | break; |
786 | case MATCH_REM: | |
787 | TRACE_INSN (cpu, "rem %s, %s, %s; // %s = %s %% %s", | |
788 | rd_name, rs1_name, rs2_name, rd_name, rs1_name, rs2_name); | |
5409cab7 | 789 | if (riscv_cpu->regs[rs1] == dividend_max && riscv_cpu->regs[rs2] == -1) |
b9249c46 | 790 | tmp = 0; |
5409cab7 MF |
791 | else if (riscv_cpu->regs[rs2]) |
792 | tmp = (signed_word) riscv_cpu->regs[rs1] | |
793 | % (signed_word) riscv_cpu->regs[rs2]; | |
b9249c46 | 794 | else |
5409cab7 | 795 | tmp = riscv_cpu->regs[rs1]; |
b9249c46 MF |
796 | store_rd (cpu, rd, tmp); |
797 | break; | |
798 | case MATCH_REMW: | |
799 | TRACE_INSN (cpu, "remw %s, %s, %s; // %s = %s %% %s", | |
800 | rd_name, rs1_name, rs2_name, rd_name, rs1_name, rs2_name); | |
801 | RISCV_ASSERT_RV64 (cpu, "insn: %s", op->name); | |
5409cab7 | 802 | if (EXTEND32 (riscv_cpu->regs[rs2]) == -1) |
b9249c46 | 803 | tmp = 0; |
5409cab7 MF |
804 | else if (EXTEND32 (riscv_cpu->regs[rs2])) |
805 | tmp = EXTEND32 (riscv_cpu->regs[rs1]) % EXTEND32 (riscv_cpu->regs[rs2]); | |
b9249c46 | 806 | else |
5409cab7 | 807 | tmp = riscv_cpu->regs[rs1]; |
b9249c46 MF |
808 | store_rd (cpu, rd, EXTEND32 (tmp)); |
809 | break; | |
810 | case MATCH_REMU: | |
811 | TRACE_INSN (cpu, "remu %s, %s, %s; // %s = %s %% %s", | |
812 | rd_name, rs1_name, rs2_name, rd_name, rs1_name, rs2_name); | |
5409cab7 MF |
813 | if (riscv_cpu->regs[rs2]) |
814 | store_rd (cpu, rd, riscv_cpu->regs[rs1] % riscv_cpu->regs[rs2]); | |
b9249c46 | 815 | else |
5409cab7 | 816 | store_rd (cpu, rd, riscv_cpu->regs[rs1]); |
b9249c46 MF |
817 | break; |
818 | case MATCH_REMUW: | |
819 | TRACE_INSN (cpu, "remuw %s, %s, %s; // %s = %s %% %s", | |
820 | rd_name, rs1_name, rs2_name, rd_name, rs1_name, rs2_name); | |
821 | RISCV_ASSERT_RV64 (cpu, "insn: %s", op->name); | |
5409cab7 MF |
822 | if ((uint32_t) riscv_cpu->regs[rs2]) |
823 | tmp = (uint32_t) riscv_cpu->regs[rs1] % (uint32_t) riscv_cpu->regs[rs2]; | |
b9249c46 | 824 | else |
5409cab7 | 825 | tmp = riscv_cpu->regs[rs1]; |
b9249c46 MF |
826 | store_rd (cpu, rd, EXTEND32 (tmp)); |
827 | break; | |
828 | default: | |
829 | TRACE_INSN (cpu, "UNHANDLED INSN: %s", op->name); | |
5409cab7 | 830 | sim_engine_halt (sd, cpu, NULL, riscv_cpu->pc, sim_signalled, SIM_SIGILL); |
b9249c46 MF |
831 | } |
832 | ||
833 | return pc; | |
834 | } | |
835 | ||
b9249c46 MF |
836 | static sim_cia |
837 | execute_a (SIM_CPU *cpu, unsigned_word iw, const struct riscv_opcode *op) | |
838 | { | |
5409cab7 | 839 | struct riscv_sim_cpu *riscv_cpu = RISCV_SIM_CPU (cpu); |
b9249c46 | 840 | SIM_DESC sd = CPU_STATE (cpu); |
10c23a2c | 841 | struct riscv_sim_state *state = RISCV_SIM_STATE (sd); |
b9249c46 MF |
842 | int rd = (iw >> OP_SH_RD) & OP_MASK_RD; |
843 | int rs1 = (iw >> OP_SH_RS1) & OP_MASK_RS1; | |
844 | int rs2 = (iw >> OP_SH_RS2) & OP_MASK_RS2; | |
845 | const char *rd_name = riscv_gpr_names_abi[rd]; | |
846 | const char *rs1_name = riscv_gpr_names_abi[rs1]; | |
847 | const char *rs2_name = riscv_gpr_names_abi[rs2]; | |
848 | struct atomic_mem_reserved_list *amo_prev, *amo_curr; | |
849 | unsigned_word tmp; | |
5409cab7 | 850 | sim_cia pc = riscv_cpu->pc + 4; |
b9249c46 MF |
851 | |
852 | /* Handle these two load/store operations specifically. */ | |
853 | switch (op->match) | |
854 | { | |
855 | case MATCH_LR_W: | |
856 | TRACE_INSN (cpu, "%s %s, (%s);", op->name, rd_name, rs1_name); | |
857 | store_rd (cpu, rd, | |
5409cab7 MF |
858 | sim_core_read_unaligned_4 (cpu, riscv_cpu->pc, read_map, |
859 | riscv_cpu->regs[rs1])); | |
b9249c46 MF |
860 | |
861 | /* Walk the reservation list to find an existing match. */ | |
10c23a2c | 862 | amo_curr = state->amo_reserved_list; |
b9249c46 MF |
863 | while (amo_curr) |
864 | { | |
5409cab7 | 865 | if (amo_curr->addr == riscv_cpu->regs[rs1]) |
b9249c46 MF |
866 | goto done; |
867 | amo_curr = amo_curr->next; | |
868 | } | |
869 | ||
870 | /* No reservation exists, so add one. */ | |
871 | amo_curr = xmalloc (sizeof (*amo_curr)); | |
5409cab7 | 872 | amo_curr->addr = riscv_cpu->regs[rs1]; |
10c23a2c MF |
873 | amo_curr->next = state->amo_reserved_list; |
874 | state->amo_reserved_list = amo_curr; | |
b9249c46 MF |
875 | goto done; |
876 | case MATCH_SC_W: | |
877 | TRACE_INSN (cpu, "%s %s, %s, (%s);", op->name, rd_name, rs2_name, | |
878 | rs1_name); | |
879 | ||
880 | /* Walk the reservation list to find a match. */ | |
10c23a2c | 881 | amo_curr = amo_prev = state->amo_reserved_list; |
b9249c46 MF |
882 | while (amo_curr) |
883 | { | |
5409cab7 | 884 | if (amo_curr->addr == riscv_cpu->regs[rs1]) |
b9249c46 MF |
885 | { |
886 | /* We found a reservation, so operate it. */ | |
5409cab7 MF |
887 | sim_core_write_unaligned_4 (cpu, riscv_cpu->pc, write_map, |
888 | riscv_cpu->regs[rs1], | |
889 | riscv_cpu->regs[rs2]); | |
b9249c46 | 890 | store_rd (cpu, rd, 0); |
10c23a2c MF |
891 | if (amo_curr == state->amo_reserved_list) |
892 | state->amo_reserved_list = amo_curr->next; | |
b9249c46 MF |
893 | else |
894 | amo_prev->next = amo_curr->next; | |
895 | free (amo_curr); | |
896 | goto done; | |
897 | } | |
898 | amo_prev = amo_curr; | |
899 | amo_curr = amo_curr->next; | |
900 | } | |
901 | ||
902 | /* If we're still here, then no reservation exists, so mark as failed. */ | |
903 | store_rd (cpu, rd, 1); | |
904 | goto done; | |
905 | } | |
906 | ||
907 | /* Handle the rest of the atomic insns with common code paths. */ | |
908 | TRACE_INSN (cpu, "%s %s, %s, (%s);", | |
909 | op->name, rd_name, rs2_name, rs1_name); | |
910 | if (op->xlen_requirement == 64) | |
5409cab7 MF |
911 | tmp = sim_core_read_unaligned_8 (cpu, riscv_cpu->pc, read_map, |
912 | riscv_cpu->regs[rs1]); | |
b9249c46 | 913 | else |
5409cab7 MF |
914 | tmp = EXTEND32 (sim_core_read_unaligned_4 (cpu, riscv_cpu->pc, read_map, |
915 | riscv_cpu->regs[rs1])); | |
b9249c46 MF |
916 | store_rd (cpu, rd, tmp); |
917 | ||
918 | switch (op->match) | |
919 | { | |
920 | case MATCH_AMOADD_D: | |
921 | case MATCH_AMOADD_W: | |
5409cab7 | 922 | tmp = riscv_cpu->regs[rd] + riscv_cpu->regs[rs2]; |
b9249c46 MF |
923 | break; |
924 | case MATCH_AMOAND_D: | |
925 | case MATCH_AMOAND_W: | |
5409cab7 | 926 | tmp = riscv_cpu->regs[rd] & riscv_cpu->regs[rs2]; |
b9249c46 MF |
927 | break; |
928 | case MATCH_AMOMAX_D: | |
929 | case MATCH_AMOMAX_W: | |
5409cab7 MF |
930 | tmp = max ((signed_word) riscv_cpu->regs[rd], |
931 | (signed_word) riscv_cpu->regs[rs2]); | |
b9249c46 MF |
932 | break; |
933 | case MATCH_AMOMAXU_D: | |
934 | case MATCH_AMOMAXU_W: | |
5409cab7 MF |
935 | tmp = max ((unsigned_word) riscv_cpu->regs[rd], |
936 | (unsigned_word) riscv_cpu->regs[rs2]); | |
b9249c46 MF |
937 | break; |
938 | case MATCH_AMOMIN_D: | |
939 | case MATCH_AMOMIN_W: | |
5409cab7 MF |
940 | tmp = min ((signed_word) riscv_cpu->regs[rd], |
941 | (signed_word) riscv_cpu->regs[rs2]); | |
b9249c46 MF |
942 | break; |
943 | case MATCH_AMOMINU_D: | |
944 | case MATCH_AMOMINU_W: | |
5409cab7 MF |
945 | tmp = min ((unsigned_word) riscv_cpu->regs[rd], |
946 | (unsigned_word) riscv_cpu->regs[rs2]); | |
b9249c46 MF |
947 | break; |
948 | case MATCH_AMOOR_D: | |
949 | case MATCH_AMOOR_W: | |
5409cab7 | 950 | tmp = riscv_cpu->regs[rd] | riscv_cpu->regs[rs2]; |
b9249c46 MF |
951 | break; |
952 | case MATCH_AMOSWAP_D: | |
953 | case MATCH_AMOSWAP_W: | |
5409cab7 | 954 | tmp = riscv_cpu->regs[rs2]; |
b9249c46 MF |
955 | break; |
956 | case MATCH_AMOXOR_D: | |
957 | case MATCH_AMOXOR_W: | |
5409cab7 | 958 | tmp = riscv_cpu->regs[rd] ^ riscv_cpu->regs[rs2]; |
b9249c46 MF |
959 | break; |
960 | default: | |
961 | TRACE_INSN (cpu, "UNHANDLED INSN: %s", op->name); | |
5409cab7 | 962 | sim_engine_halt (sd, cpu, NULL, riscv_cpu->pc, sim_signalled, SIM_SIGILL); |
b9249c46 MF |
963 | } |
964 | ||
965 | if (op->xlen_requirement == 64) | |
5409cab7 MF |
966 | sim_core_write_unaligned_8 (cpu, riscv_cpu->pc, write_map, |
967 | riscv_cpu->regs[rs1], tmp); | |
b9249c46 | 968 | else |
5409cab7 MF |
969 | sim_core_write_unaligned_4 (cpu, riscv_cpu->pc, write_map, |
970 | riscv_cpu->regs[rs1], tmp); | |
b9249c46 MF |
971 | |
972 | done: | |
973 | return pc; | |
974 | } | |
975 | ||
976 | static sim_cia | |
977 | execute_one (SIM_CPU *cpu, unsigned_word iw, const struct riscv_opcode *op) | |
978 | { | |
5409cab7 | 979 | struct riscv_sim_cpu *riscv_cpu = RISCV_SIM_CPU (cpu); |
b9249c46 MF |
980 | SIM_DESC sd = CPU_STATE (cpu); |
981 | ||
982 | if (op->xlen_requirement == 32) | |
983 | RISCV_ASSERT_RV32 (cpu, "insn: %s", op->name); | |
984 | else if (op->xlen_requirement == 64) | |
985 | RISCV_ASSERT_RV64 (cpu, "insn: %s", op->name); | |
986 | ||
987 | switch (op->insn_class) | |
988 | { | |
989 | case INSN_CLASS_A: | |
990 | return execute_a (cpu, iw, op); | |
991 | case INSN_CLASS_I: | |
992 | return execute_i (cpu, iw, op); | |
993 | case INSN_CLASS_M: | |
c6422d7b | 994 | case INSN_CLASS_ZMMUL: |
b9249c46 MF |
995 | return execute_m (cpu, iw, op); |
996 | default: | |
997 | TRACE_INSN (cpu, "UNHANDLED EXTENSION: %d", op->insn_class); | |
5409cab7 | 998 | sim_engine_halt (sd, cpu, NULL, riscv_cpu->pc, sim_signalled, SIM_SIGILL); |
b9249c46 MF |
999 | } |
1000 | ||
5409cab7 | 1001 | return riscv_cpu->pc + riscv_insn_length (iw); |
b9249c46 MF |
1002 | } |
1003 | ||
1004 | /* Decode & execute a single instruction. */ | |
1005 | void step_once (SIM_CPU *cpu) | |
1006 | { | |
5409cab7 | 1007 | struct riscv_sim_cpu *riscv_cpu = RISCV_SIM_CPU (cpu); |
b9249c46 MF |
1008 | SIM_DESC sd = CPU_STATE (cpu); |
1009 | unsigned_word iw; | |
1010 | unsigned int len; | |
5409cab7 | 1011 | sim_cia pc = riscv_cpu->pc; |
b9249c46 MF |
1012 | const struct riscv_opcode *op; |
1013 | int xlen = RISCV_XLEN (cpu); | |
1014 | ||
1015 | if (TRACE_ANY_P (cpu)) | |
1016 | trace_prefix (sd, cpu, NULL_CIA, pc, TRACE_LINENUM_P (cpu), | |
1017 | NULL, 0, " "); /* Use a space for gcc warnings. */ | |
1018 | ||
1019 | iw = sim_core_read_aligned_2 (cpu, pc, exec_map, pc); | |
1020 | ||
1021 | /* Reject non-32-bit opcodes first. */ | |
1022 | len = riscv_insn_length (iw); | |
1023 | if (len != 4) | |
1024 | { | |
1025 | sim_io_printf (sd, "sim: bad insn len %#x @ %#" PRIxTA ": %#" PRIxTW "\n", | |
1026 | len, pc, iw); | |
1027 | sim_engine_halt (sd, cpu, NULL, pc, sim_signalled, SIM_SIGILL); | |
1028 | } | |
1029 | ||
1030 | iw |= ((unsigned_word) sim_core_read_aligned_2 ( | |
1031 | cpu, pc, exec_map, pc + 2) << 16); | |
1032 | ||
1033 | TRACE_CORE (cpu, "0x%08" PRIxTW, iw); | |
1034 | ||
1035 | op = riscv_hash[OP_HASH_IDX (iw)]; | |
1036 | if (!op) | |
1037 | sim_engine_halt (sd, cpu, NULL, pc, sim_signalled, SIM_SIGILL); | |
1038 | ||
1039 | /* NB: Same loop logic as riscv_disassemble_insn. */ | |
1040 | for (; op->name; op++) | |
1041 | { | |
1042 | /* Does the opcode match? */ | |
1043 | if (! op->match_func (op, iw)) | |
1044 | continue; | |
1045 | /* Is this a pseudo-instruction and may we print it as such? */ | |
1046 | if (op->pinfo & INSN_ALIAS) | |
1047 | continue; | |
1048 | /* Is this instruction restricted to a certain value of XLEN? */ | |
1049 | if (op->xlen_requirement != 0 && op->xlen_requirement != xlen) | |
1050 | continue; | |
1051 | ||
1052 | /* It's a match. */ | |
1053 | pc = execute_one (cpu, iw, op); | |
1054 | break; | |
1055 | } | |
1056 | ||
1057 | /* TODO: Handle overflow into high 32 bits. */ | |
1058 | /* TODO: Try to use a common counter and only update on demand (reads). */ | |
5409cab7 MF |
1059 | ++riscv_cpu->csr.cycle; |
1060 | ++riscv_cpu->csr.instret; | |
b9249c46 | 1061 | |
5409cab7 | 1062 | riscv_cpu->pc = pc; |
b9249c46 MF |
1063 | } |
1064 | \f | |
1065 | /* Return the program counter for this cpu. */ | |
1066 | static sim_cia | |
1067 | pc_get (sim_cpu *cpu) | |
1068 | { | |
5409cab7 MF |
1069 | struct riscv_sim_cpu *riscv_cpu = RISCV_SIM_CPU (cpu); |
1070 | ||
1071 | return riscv_cpu->pc; | |
b9249c46 MF |
1072 | } |
1073 | ||
1074 | /* Set the program counter for this cpu to the new pc value. */ | |
1075 | static void | |
1076 | pc_set (sim_cpu *cpu, sim_cia pc) | |
1077 | { | |
5409cab7 MF |
1078 | struct riscv_sim_cpu *riscv_cpu = RISCV_SIM_CPU (cpu); |
1079 | ||
1080 | riscv_cpu->pc = pc; | |
b9249c46 MF |
1081 | } |
1082 | ||
04b4939b | 1083 | static int |
ee1cffd3 | 1084 | reg_fetch (sim_cpu *cpu, int rn, void *buf, int len) |
04b4939b | 1085 | { |
5409cab7 MF |
1086 | struct riscv_sim_cpu *riscv_cpu = RISCV_SIM_CPU (cpu); |
1087 | ||
04b4939b MF |
1088 | if (len <= 0 || len > sizeof (unsigned_word)) |
1089 | return -1; | |
1090 | ||
1091 | switch (rn) | |
1092 | { | |
1093 | case SIM_RISCV_ZERO_REGNUM: | |
1094 | memset (buf, 0, len); | |
1095 | return len; | |
1096 | case SIM_RISCV_RA_REGNUM ... SIM_RISCV_T6_REGNUM: | |
5409cab7 | 1097 | memcpy (buf, &riscv_cpu->regs[rn], len); |
04b4939b MF |
1098 | return len; |
1099 | case SIM_RISCV_FIRST_FP_REGNUM ... SIM_RISCV_LAST_FP_REGNUM: | |
5409cab7 | 1100 | memcpy (buf, &riscv_cpu->fpregs[rn - SIM_RISCV_FIRST_FP_REGNUM], len); |
04b4939b MF |
1101 | return len; |
1102 | case SIM_RISCV_PC_REGNUM: | |
5409cab7 | 1103 | memcpy (buf, &riscv_cpu->pc, len); |
04b4939b MF |
1104 | return len; |
1105 | ||
1106 | #define DECLARE_CSR(name, num, ...) \ | |
1107 | case SIM_RISCV_ ## num ## _REGNUM: \ | |
5409cab7 | 1108 | memcpy (buf, &riscv_cpu->csr.name, len); \ |
04b4939b MF |
1109 | return len; |
1110 | #include "opcode/riscv-opc.h" | |
1111 | #undef DECLARE_CSR | |
1112 | ||
1113 | default: | |
1114 | return -1; | |
1115 | } | |
1116 | } | |
1117 | ||
1118 | static int | |
ee1cffd3 | 1119 | reg_store (sim_cpu *cpu, int rn, const void *buf, int len) |
04b4939b | 1120 | { |
5409cab7 MF |
1121 | struct riscv_sim_cpu *riscv_cpu = RISCV_SIM_CPU (cpu); |
1122 | ||
04b4939b MF |
1123 | if (len <= 0 || len > sizeof (unsigned_word)) |
1124 | return -1; | |
1125 | ||
1126 | switch (rn) | |
1127 | { | |
1128 | case SIM_RISCV_ZERO_REGNUM: | |
1129 | /* Ignore writes. */ | |
1130 | return len; | |
1131 | case SIM_RISCV_RA_REGNUM ... SIM_RISCV_T6_REGNUM: | |
5409cab7 | 1132 | memcpy (&riscv_cpu->regs[rn], buf, len); |
04b4939b MF |
1133 | return len; |
1134 | case SIM_RISCV_FIRST_FP_REGNUM ... SIM_RISCV_LAST_FP_REGNUM: | |
5409cab7 | 1135 | memcpy (&riscv_cpu->fpregs[rn - SIM_RISCV_FIRST_FP_REGNUM], buf, len); |
04b4939b MF |
1136 | return len; |
1137 | case SIM_RISCV_PC_REGNUM: | |
5409cab7 | 1138 | memcpy (&riscv_cpu->pc, buf, len); |
04b4939b MF |
1139 | return len; |
1140 | ||
1141 | #define DECLARE_CSR(name, num, ...) \ | |
1142 | case SIM_RISCV_ ## num ## _REGNUM: \ | |
5409cab7 | 1143 | memcpy (&riscv_cpu->csr.name, buf, len); \ |
04b4939b MF |
1144 | return len; |
1145 | #include "opcode/riscv-opc.h" | |
1146 | #undef DECLARE_CSR | |
1147 | ||
1148 | default: | |
1149 | return -1; | |
1150 | } | |
1151 | } | |
1152 | ||
b9249c46 MF |
1153 | /* Initialize the state for a single cpu. Usuaully this involves clearing all |
1154 | registers back to their reset state. Should also hook up the fetch/store | |
1155 | helper functions too. */ | |
1156 | void | |
1157 | initialize_cpu (SIM_DESC sd, SIM_CPU *cpu, int mhartid) | |
1158 | { | |
5409cab7 | 1159 | struct riscv_sim_cpu *riscv_cpu = RISCV_SIM_CPU (cpu); |
b9249c46 MF |
1160 | const char *extensions; |
1161 | int i; | |
1162 | ||
5409cab7 | 1163 | memset (riscv_cpu->regs, 0, sizeof (riscv_cpu->regs)); |
b9249c46 MF |
1164 | |
1165 | CPU_PC_FETCH (cpu) = pc_get; | |
1166 | CPU_PC_STORE (cpu) = pc_set; | |
04b4939b MF |
1167 | CPU_REG_FETCH (cpu) = reg_fetch; |
1168 | CPU_REG_STORE (cpu) = reg_store; | |
b9249c46 MF |
1169 | |
1170 | if (!riscv_hash[0]) | |
1171 | { | |
1172 | const struct riscv_opcode *op; | |
1173 | ||
1174 | for (op = riscv_opcodes; op->name; op++) | |
1175 | if (!riscv_hash[OP_HASH_IDX (op->match)]) | |
1176 | riscv_hash[OP_HASH_IDX (op->match)] = op; | |
1177 | } | |
1178 | ||
5409cab7 | 1179 | riscv_cpu->csr.misa = 0; |
b9249c46 MF |
1180 | /* RV32 sets this field to 0, and we don't really support RV128 yet. */ |
1181 | if (RISCV_XLEN (cpu) == 64) | |
5409cab7 | 1182 | riscv_cpu->csr.misa |= (uint64_t)2 << 62; |
b9249c46 MF |
1183 | |
1184 | /* Skip the leading "rv" prefix and the two numbers. */ | |
1185 | extensions = MODEL_NAME (CPU_MODEL (cpu)) + 4; | |
1186 | for (i = 0; i < 26; ++i) | |
1187 | { | |
1188 | char ext = 'A' + i; | |
1189 | ||
1190 | if (ext == 'X') | |
1191 | continue; | |
1192 | else if (strchr (extensions, ext) != NULL) | |
1193 | { | |
1194 | if (ext == 'G') | |
5409cab7 | 1195 | riscv_cpu->csr.misa |= 0x1129; /* G = IMAFD. */ |
b9249c46 | 1196 | else |
5409cab7 | 1197 | riscv_cpu->csr.misa |= (1 << i); |
b9249c46 MF |
1198 | } |
1199 | } | |
1200 | ||
5409cab7 MF |
1201 | riscv_cpu->csr.mimpid = 0x8000; |
1202 | riscv_cpu->csr.mhartid = mhartid; | |
b9249c46 MF |
1203 | } |
1204 | \f | |
1205 | /* Some utils don't like having a NULL environ. */ | |
1206 | static const char * const simple_env[] = { "HOME=/", "PATH=/bin", NULL }; | |
1207 | ||
1208 | /* Count the number of arguments in an argv. */ | |
1209 | static int | |
1210 | count_argv (const char * const *argv) | |
1211 | { | |
1212 | int i; | |
1213 | ||
1214 | if (!argv) | |
1215 | return -1; | |
1216 | ||
1217 | for (i = 0; argv[i] != NULL; ++i) | |
1218 | continue; | |
1219 | return i; | |
1220 | } | |
1221 | ||
1222 | void | |
1223 | initialize_env (SIM_DESC sd, const char * const *argv, const char * const *env) | |
1224 | { | |
1225 | SIM_CPU *cpu = STATE_CPU (sd, 0); | |
5409cab7 | 1226 | struct riscv_sim_cpu *riscv_cpu = RISCV_SIM_CPU (cpu); |
b9249c46 MF |
1227 | int i; |
1228 | int argc, argv_flat; | |
1229 | int envc, env_flat; | |
1230 | address_word sp, sp_flat; | |
1231 | unsigned char null[8] = { 0, 0, 0, 0, 0, 0, 0, 0, }; | |
1232 | ||
1233 | /* Figure out how many bytes the argv strings take up. */ | |
1234 | argc = count_argv (argv); | |
1235 | if (argc == -1) | |
1236 | argc = 0; | |
1237 | argv_flat = argc; /* NUL bytes. */ | |
1238 | for (i = 0; i < argc; ++i) | |
1239 | argv_flat += strlen (argv[i]); | |
1240 | ||
1241 | /* Figure out how many bytes the environ strings take up. */ | |
1242 | if (!env) | |
1243 | env = simple_env; | |
1244 | envc = count_argv (env); | |
1245 | env_flat = envc; /* NUL bytes. */ | |
1246 | for (i = 0; i < envc; ++i) | |
1247 | env_flat += strlen (env[i]); | |
1248 | ||
1249 | /* Make space for the strings themselves. */ | |
1250 | sp_flat = (DEFAULT_MEM_SIZE - argv_flat - env_flat) & -sizeof (address_word); | |
1251 | /* Then the pointers to the strings. */ | |
1252 | sp = sp_flat - ((argc + 1 + envc + 1) * sizeof (address_word)); | |
1253 | /* Then the argc. */ | |
1254 | sp -= sizeof (unsigned_word); | |
1255 | ||
1256 | /* Set up the regs the libgloss crt0 expects. */ | |
5409cab7 MF |
1257 | riscv_cpu->a0 = argc; |
1258 | riscv_cpu->sp = sp; | |
b9249c46 MF |
1259 | |
1260 | /* First push the argc value. */ | |
5b94c380 | 1261 | sim_write (sd, sp, &argc, sizeof (unsigned_word)); |
b9249c46 MF |
1262 | sp += sizeof (unsigned_word); |
1263 | ||
1264 | /* Then the actual argv strings so we know where to point argv[]. */ | |
1265 | for (i = 0; i < argc; ++i) | |
1266 | { | |
1267 | unsigned len = strlen (argv[i]) + 1; | |
5b94c380 MF |
1268 | sim_write (sd, sp_flat, argv[i], len); |
1269 | sim_write (sd, sp, &sp_flat, sizeof (address_word)); | |
b9249c46 MF |
1270 | sp_flat += len; |
1271 | sp += sizeof (address_word); | |
1272 | } | |
1273 | sim_write (sd, sp, null, sizeof (address_word)); | |
1274 | sp += sizeof (address_word); | |
1275 | ||
1276 | /* Then the actual env strings so we know where to point env[]. */ | |
1277 | for (i = 0; i < envc; ++i) | |
1278 | { | |
1279 | unsigned len = strlen (env[i]) + 1; | |
5b94c380 MF |
1280 | sim_write (sd, sp_flat, env[i], len); |
1281 | sim_write (sd, sp, &sp_flat, sizeof (address_word)); | |
b9249c46 MF |
1282 | sp_flat += len; |
1283 | sp += sizeof (address_word); | |
1284 | } | |
1285 | } |