]> git.ipfire.org Git - thirdparty/binutils-gdb.git/blob - sim/microblaze/interp.c
e451f2d68d1454c6a8ae3dc3b4c7ecc8285cb471
[thirdparty/binutils-gdb.git] / sim / microblaze / interp.c
1 /* Simulator for Xilinx MicroBlaze processor
2 Copyright 2009-2021 Free Software Foundation, Inc.
3
4 This file is part of GDB, the GNU debugger.
5
6 This program is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 3 of the License, or
9 (at your option) any later version.
10
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with this program; if not, see <http://www.gnu.org/licenses/>. */
18
19 /* This must come before any other includes. */
20 #include "defs.h"
21
22 #include <signal.h>
23 #include <stdlib.h>
24 #include <string.h>
25 #include <unistd.h>
26 #include "bfd.h"
27 #include "sim/callback.h"
28 #include "libiberty.h"
29 #include "sim/sim.h"
30
31 #include "sim-main.h"
32 #include "sim-options.h"
33 #include "sim-syscall.h"
34
35 #include "microblaze-dis.h"
36
37 #define target_big_endian (CURRENT_TARGET_BYTE_ORDER == BFD_ENDIAN_BIG)
38
39 static unsigned long
40 microblaze_extract_unsigned_integer (unsigned char *addr, int len)
41 {
42 unsigned long retval;
43 unsigned char *p;
44 unsigned char *startaddr = (unsigned char *)addr;
45 unsigned char *endaddr = startaddr + len;
46
47 if (len > (int) sizeof (unsigned long))
48 printf ("That operation is not available on integers of more than "
49 "%zu bytes.", sizeof (unsigned long));
50
51 /* Start at the most significant end of the integer, and work towards
52 the least significant. */
53 retval = 0;
54
55 if (!target_big_endian)
56 {
57 for (p = endaddr; p > startaddr;)
58 retval = (retval << 8) | * -- p;
59 }
60 else
61 {
62 for (p = startaddr; p < endaddr;)
63 retval = (retval << 8) | * p ++;
64 }
65
66 return retval;
67 }
68
69 static void
70 microblaze_store_unsigned_integer (unsigned char *addr, int len,
71 unsigned long val)
72 {
73 unsigned char *p;
74 unsigned char *startaddr = (unsigned char *)addr;
75 unsigned char *endaddr = startaddr + len;
76
77 if (!target_big_endian)
78 {
79 for (p = startaddr; p < endaddr;)
80 {
81 *p++ = val & 0xff;
82 val >>= 8;
83 }
84 }
85 else
86 {
87 for (p = endaddr; p > startaddr;)
88 {
89 *--p = val & 0xff;
90 val >>= 8;
91 }
92 }
93 }
94
95 static void
96 set_initial_gprs (SIM_CPU *cpu)
97 {
98 int i;
99 long space;
100
101 /* Set up machine just out of reset. */
102 PC = 0;
103 MSR = 0;
104
105 /* Clean out the GPRs */
106 for (i = 0; i < 32; i++)
107 CPU.regs[i] = 0;
108 CPU.insts = 0;
109 CPU.cycles = 0;
110 CPU.imm_enable = 0;
111 }
112
113 static int tracing = 0;
114
115 void
116 sim_engine_run (SIM_DESC sd,
117 int next_cpu_nr, /* ignore */
118 int nr_cpus, /* ignore */
119 int siggnal) /* ignore */
120 {
121 SIM_CPU *cpu = STATE_CPU (sd, 0);
122 int needfetch;
123 word inst;
124 enum microblaze_instr op;
125 int memops;
126 int bonus_cycles;
127 int insts;
128 int w;
129 int cycs;
130 word WLhash;
131 ubyte carry;
132 bool imm_unsigned;
133 short ra, rb, rd;
134 long immword;
135 uword oldpc, newpc;
136 short delay_slot_enable;
137 short branch_taken;
138 short num_delay_slot; /* UNUSED except as reqd parameter */
139 enum microblaze_instr_type insn_type;
140
141 memops = 0;
142 bonus_cycles = 0;
143 insts = 0;
144
145 while (1)
146 {
147 /* Fetch the initial instructions that we'll decode. */
148 inst = MEM_RD_WORD (PC & 0xFFFFFFFC);
149
150 op = get_insn_microblaze (inst, &imm_unsigned, &insn_type,
151 &num_delay_slot);
152
153 if (op == invalid_inst)
154 fprintf (stderr, "Unknown instruction 0x%04x", inst);
155
156 if (tracing)
157 fprintf (stderr, "%.4x: inst = %.4x ", PC, inst);
158
159 rd = GET_RD;
160 rb = GET_RB;
161 ra = GET_RA;
162 /* immword = IMM_W; */
163
164 oldpc = PC;
165 delay_slot_enable = 0;
166 branch_taken = 0;
167 if (op == microblaze_brk)
168 sim_engine_halt (sd, NULL, NULL, NULL_CIA, sim_stopped, SIM_SIGTRAP);
169 else if (inst == MICROBLAZE_HALT_INST)
170 {
171 insts += 1;
172 bonus_cycles++;
173 TRACE_INSN (cpu, "HALT (%i)", RETREG);
174 sim_engine_halt (sd, NULL, NULL, NULL_CIA, sim_exited, RETREG);
175 }
176 else
177 {
178 switch(op)
179 {
180 #define INSTRUCTION(NAME, OPCODE, TYPE, ACTION) \
181 case NAME: \
182 TRACE_INSN (cpu, #NAME); \
183 ACTION; \
184 break;
185 #include "microblaze.isa"
186 #undef INSTRUCTION
187
188 default:
189 sim_engine_halt (sd, NULL, NULL, NULL_CIA, sim_signalled,
190 SIM_SIGILL);
191 fprintf (stderr, "ERROR: Unknown opcode\n");
192 }
193 /* Make R0 consistent */
194 CPU.regs[0] = 0;
195
196 /* Check for imm instr */
197 if (op == imm)
198 IMM_ENABLE = 1;
199 else
200 IMM_ENABLE = 0;
201
202 /* Update cycle counts */
203 insts ++;
204 if (insn_type == memory_store_inst || insn_type == memory_load_inst)
205 memops++;
206 if (insn_type == mult_inst)
207 bonus_cycles++;
208 if (insn_type == barrel_shift_inst)
209 bonus_cycles++;
210 if (insn_type == anyware_inst)
211 bonus_cycles++;
212 if (insn_type == div_inst)
213 bonus_cycles += 33;
214
215 if ((insn_type == branch_inst || insn_type == return_inst)
216 && branch_taken)
217 {
218 /* Add an extra cycle for taken branches */
219 bonus_cycles++;
220 /* For branch instructions handle the instruction in the delay slot */
221 if (delay_slot_enable)
222 {
223 newpc = PC;
224 PC = oldpc + INST_SIZE;
225 inst = MEM_RD_WORD (PC & 0xFFFFFFFC);
226 op = get_insn_microblaze (inst, &imm_unsigned, &insn_type,
227 &num_delay_slot);
228 if (op == invalid_inst)
229 fprintf (stderr, "Unknown instruction 0x%04x", inst);
230 if (tracing)
231 fprintf (stderr, "%.4x: inst = %.4x ", PC, inst);
232 rd = GET_RD;
233 rb = GET_RB;
234 ra = GET_RA;
235 /* immword = IMM_W; */
236 if (op == microblaze_brk)
237 {
238 if (STATE_VERBOSE_P (sd))
239 fprintf (stderr, "Breakpoint set in delay slot "
240 "(at address 0x%x) will not be honored\n", PC);
241 /* ignore the breakpoint */
242 }
243 else if (insn_type == branch_inst || insn_type == return_inst)
244 {
245 if (STATE_VERBOSE_P (sd))
246 fprintf (stderr, "Cannot have branch or return instructions "
247 "in delay slot (at address 0x%x)\n", PC);
248 sim_engine_halt (sd, NULL, NULL, NULL_CIA, sim_signalled,
249 SIM_SIGILL);
250 }
251 else
252 {
253 switch(op)
254 {
255 #define INSTRUCTION(NAME, OPCODE, TYPE, ACTION) \
256 case NAME: \
257 ACTION; \
258 break;
259 #include "microblaze.isa"
260 #undef INSTRUCTION
261
262 default:
263 sim_engine_halt (sd, NULL, NULL, NULL_CIA,
264 sim_signalled, SIM_SIGILL);
265 fprintf (stderr, "ERROR: Unknown opcode at 0x%x\n", PC);
266 }
267 /* Update cycle counts */
268 insts++;
269 if (insn_type == memory_store_inst
270 || insn_type == memory_load_inst)
271 memops++;
272 if (insn_type == mult_inst)
273 bonus_cycles++;
274 if (insn_type == barrel_shift_inst)
275 bonus_cycles++;
276 if (insn_type == anyware_inst)
277 bonus_cycles++;
278 if (insn_type == div_inst)
279 bonus_cycles += 33;
280 }
281 /* Restore the PC */
282 PC = newpc;
283 /* Make R0 consistent */
284 CPU.regs[0] = 0;
285 /* Check for imm instr */
286 if (op == imm)
287 IMM_ENABLE = 1;
288 else
289 IMM_ENABLE = 0;
290 }
291 else
292 {
293 if (op == brki && IMM == 8)
294 {
295 RETREG = sim_syscall (cpu, CPU.regs[12], CPU.regs[5],
296 CPU.regs[6], CPU.regs[7],
297 CPU.regs[8]);
298 PC = RD + INST_SIZE;
299 }
300
301 /* no delay slot: increment cycle count */
302 bonus_cycles++;
303 }
304 }
305 }
306
307 if (tracing)
308 fprintf (stderr, "\n");
309
310 if (sim_events_tick (sd))
311 sim_events_process (sd);
312 }
313
314 /* Hide away the things we've cached while executing. */
315 /* CPU.pc = pc; */
316 CPU.insts += insts; /* instructions done ... */
317 CPU.cycles += insts; /* and each takes a cycle */
318 CPU.cycles += bonus_cycles; /* and extra cycles for branches */
319 CPU.cycles += memops; /* and memop cycle delays */
320 }
321
322 static int
323 microblaze_reg_store (SIM_CPU *cpu, int rn, unsigned char *memory, int length)
324 {
325 if (rn < NUM_REGS + NUM_SPECIAL && rn >= 0)
326 {
327 if (length == 4)
328 {
329 /* misalignment safe */
330 long ival = microblaze_extract_unsigned_integer (memory, 4);
331 if (rn < NUM_REGS)
332 CPU.regs[rn] = ival;
333 else
334 CPU.spregs[rn-NUM_REGS] = ival;
335 return 4;
336 }
337 else
338 return 0;
339 }
340 else
341 return 0;
342 }
343
344 static int
345 microblaze_reg_fetch (SIM_CPU *cpu, int rn, unsigned char *memory, int length)
346 {
347 long ival;
348
349 if (rn < NUM_REGS + NUM_SPECIAL && rn >= 0)
350 {
351 if (length == 4)
352 {
353 if (rn < NUM_REGS)
354 ival = CPU.regs[rn];
355 else
356 ival = CPU.spregs[rn-NUM_REGS];
357
358 /* misalignment-safe */
359 microblaze_store_unsigned_integer (memory, 4, ival);
360 return 4;
361 }
362 else
363 return 0;
364 }
365 else
366 return 0;
367 }
368
369 void
370 sim_info (SIM_DESC sd, int verbose)
371 {
372 SIM_CPU *cpu = STATE_CPU (sd, 0);
373 host_callback *callback = STATE_CALLBACK (sd);
374
375 callback->printf_filtered (callback, "\n\n# instructions executed %10d\n",
376 CPU.insts);
377 callback->printf_filtered (callback, "# cycles %10d\n",
378 (CPU.cycles) ? CPU.cycles+2 : 0);
379 }
380
381 static sim_cia
382 microblaze_pc_get (sim_cpu *cpu)
383 {
384 return cpu->microblaze_cpu.spregs[0];
385 }
386
387 static void
388 microblaze_pc_set (sim_cpu *cpu, sim_cia pc)
389 {
390 cpu->microblaze_cpu.spregs[0] = pc;
391 }
392
393 static void
394 free_state (SIM_DESC sd)
395 {
396 if (STATE_MODULES (sd) != NULL)
397 sim_module_uninstall (sd);
398 sim_cpu_free_all (sd);
399 sim_state_free (sd);
400 }
401
402 SIM_DESC
403 sim_open (SIM_OPEN_KIND kind, host_callback *cb,
404 struct bfd *abfd, char * const *argv)
405 {
406 int i;
407 SIM_DESC sd = sim_state_alloc (kind, cb);
408 SIM_ASSERT (STATE_MAGIC (sd) == SIM_MAGIC_NUMBER);
409
410 /* The cpu data is kept in a separately allocated chunk of memory. */
411 if (sim_cpu_alloc_all (sd, 1) != SIM_RC_OK)
412 {
413 free_state (sd);
414 return 0;
415 }
416
417 if (sim_pre_argv_init (sd, argv[0]) != SIM_RC_OK)
418 {
419 free_state (sd);
420 return 0;
421 }
422
423 /* The parser will print an error message for us, so we silently return. */
424 if (sim_parse_args (sd, argv) != SIM_RC_OK)
425 {
426 free_state (sd);
427 return 0;
428 }
429
430 /* Check for/establish the a reference program image. */
431 if (sim_analyze_program (sd,
432 (STATE_PROG_ARGV (sd) != NULL
433 ? *STATE_PROG_ARGV (sd)
434 : NULL), abfd) != SIM_RC_OK)
435 {
436 free_state (sd);
437 return 0;
438 }
439
440 /* Configure/verify the target byte order and other runtime
441 configuration options. */
442 if (sim_config (sd) != SIM_RC_OK)
443 {
444 sim_module_uninstall (sd);
445 return 0;
446 }
447
448 if (sim_post_argv_init (sd) != SIM_RC_OK)
449 {
450 /* Uninstall the modules to avoid memory leaks,
451 file descriptor leaks, etc. */
452 sim_module_uninstall (sd);
453 return 0;
454 }
455
456 /* CPU specific initialization. */
457 for (i = 0; i < MAX_NR_PROCESSORS; ++i)
458 {
459 SIM_CPU *cpu = STATE_CPU (sd, i);
460
461 CPU_REG_FETCH (cpu) = microblaze_reg_fetch;
462 CPU_REG_STORE (cpu) = microblaze_reg_store;
463 CPU_PC_FETCH (cpu) = microblaze_pc_get;
464 CPU_PC_STORE (cpu) = microblaze_pc_set;
465
466 set_initial_gprs (cpu);
467 }
468
469 /* Default to a 8 Mbyte (== 2^23) memory space. */
470 sim_do_commandf (sd, "memory-size 0x800000");
471
472 return sd;
473 }
474
475 SIM_RC
476 sim_create_inferior (SIM_DESC sd, struct bfd *prog_bfd,
477 char * const *argv, char * const *env)
478 {
479 SIM_CPU *cpu = STATE_CPU (sd, 0);
480
481 PC = bfd_get_start_address (prog_bfd);
482
483 return SIM_RC_OK;
484 }