Commit | Line | Data |
---|---|---|
bd30e45a | 1 | /* Simulator for Xilinx MicroBlaze processor |
32d0add0 | 2 | Copyright 2009-2015 Free Software Foundation, Inc. |
bd30e45a ME |
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 | |
3fd725ef | 8 | the Free Software Foundation; either version 3 of the License, or |
bd30e45a ME |
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 | |
51b318de | 17 | along with this program; if not, see <http://www.gnu.org/licenses/>. */ |
bd30e45a | 18 | |
8fe6640e | 19 | #include "config.h" |
bd30e45a | 20 | #include <signal.h> |
dc049bf4 MF |
21 | #include <stdlib.h> |
22 | #include <string.h> | |
dc049bf4 | 23 | #include <unistd.h> |
bd30e45a ME |
24 | #include "bfd.h" |
25 | #include "gdb/callback.h" | |
26 | #include "libiberty.h" | |
27 | #include "gdb/remote-sim.h" | |
2b4bc832 | 28 | |
bd30e45a | 29 | #include "sim-main.h" |
2b4bc832 MF |
30 | #include "sim-options.h" |
31 | ||
419c2fda | 32 | #include "microblaze-dis.h" |
bd30e45a | 33 | |
2b4bc832 | 34 | #define target_big_endian (CURRENT_TARGET_BYTE_ORDER == BIG_ENDIAN) |
bd30e45a | 35 | |
feb703b3 | 36 | static unsigned long |
bd30e45a ME |
37 | microblaze_extract_unsigned_integer (unsigned char *addr, int len) |
38 | { | |
39 | unsigned long retval; | |
40 | unsigned char *p; | |
41 | unsigned char *startaddr = (unsigned char *)addr; | |
42 | unsigned char *endaddr = startaddr + len; | |
43 | ||
44 | if (len > (int) sizeof (unsigned long)) | |
45 | printf ("That operation is not available on integers of more than " | |
3e95021c | 46 | "%zu bytes.", sizeof (unsigned long)); |
bd30e45a ME |
47 | |
48 | /* Start at the most significant end of the integer, and work towards | |
49 | the least significant. */ | |
50 | retval = 0; | |
51 | ||
52 | if (!target_big_endian) | |
53 | { | |
54 | for (p = endaddr; p > startaddr;) | |
55 | retval = (retval << 8) | * -- p; | |
56 | } | |
57 | else | |
58 | { | |
59 | for (p = startaddr; p < endaddr;) | |
60 | retval = (retval << 8) | * p ++; | |
61 | } | |
62 | ||
63 | return retval; | |
64 | } | |
65 | ||
feb703b3 | 66 | static void |
bd30e45a ME |
67 | microblaze_store_unsigned_integer (unsigned char *addr, int len, |
68 | unsigned long val) | |
69 | { | |
70 | unsigned char *p; | |
71 | unsigned char *startaddr = (unsigned char *)addr; | |
72 | unsigned char *endaddr = startaddr + len; | |
73 | ||
74 | if (!target_big_endian) | |
75 | { | |
76 | for (p = startaddr; p < endaddr;) | |
77 | { | |
78 | *p++ = val & 0xff; | |
79 | val >>= 8; | |
80 | } | |
81 | } | |
82 | else | |
83 | { | |
84 | for (p = endaddr; p > startaddr;) | |
85 | { | |
86 | *--p = val & 0xff; | |
87 | val >>= 8; | |
88 | } | |
89 | } | |
90 | } | |
91 | ||
bd30e45a | 92 | static void |
2b4bc832 | 93 | set_initial_gprs (SIM_CPU *cpu) |
bd30e45a ME |
94 | { |
95 | int i; | |
96 | long space; | |
bd30e45a ME |
97 | |
98 | /* Set up machine just out of reset. */ | |
99 | PC = 0; | |
100 | MSR = 0; | |
101 | ||
bd30e45a ME |
102 | /* Clean out the GPRs */ |
103 | for (i = 0; i < 32; i++) | |
104 | CPU.regs[i] = 0; | |
105 | CPU.insts = 0; | |
106 | CPU.cycles = 0; | |
107 | CPU.imm_enable = 0; | |
bd30e45a ME |
108 | } |
109 | ||
bd30e45a ME |
110 | static int tracing = 0; |
111 | ||
112 | void | |
113 | sim_resume (SIM_DESC sd, int step, int siggnal) | |
114 | { | |
2b4bc832 | 115 | SIM_CPU *cpu = STATE_CPU (sd, 0); |
bd30e45a ME |
116 | int needfetch; |
117 | word inst; | |
118 | enum microblaze_instr op; | |
bd30e45a ME |
119 | int memops; |
120 | int bonus_cycles; | |
121 | int insts; | |
122 | int w; | |
123 | int cycs; | |
124 | word WLhash; | |
125 | ubyte carry; | |
126 | int imm_unsigned; | |
127 | short ra, rb, rd; | |
128 | long immword; | |
129 | uword oldpc, newpc; | |
130 | short delay_slot_enable; | |
131 | short branch_taken; | |
132 | short num_delay_slot; /* UNUSED except as reqd parameter */ | |
133 | enum microblaze_instr_type insn_type; | |
134 | ||
bd30e45a ME |
135 | CPU.exception = step ? SIGTRAP : 0; |
136 | ||
137 | memops = 0; | |
138 | bonus_cycles = 0; | |
139 | insts = 0; | |
ba14f941 | 140 | |
bd30e45a ME |
141 | do |
142 | { | |
143 | /* Fetch the initial instructions that we'll decode. */ | |
c85fc610 | 144 | inst = MEM_RD_WORD (PC & 0xFFFFFFFC); |
bd30e45a | 145 | |
ba14f941 | 146 | op = get_insn_microblaze (inst, &imm_unsigned, &insn_type, |
bd30e45a ME |
147 | &num_delay_slot); |
148 | ||
149 | if (op == invalid_inst) | |
150 | fprintf (stderr, "Unknown instruction 0x%04x", inst); | |
151 | ||
152 | if (tracing) | |
153 | fprintf (stderr, "%.4x: inst = %.4x ", PC, inst); | |
154 | ||
155 | rd = GET_RD; | |
156 | rb = GET_RB; | |
157 | ra = GET_RA; | |
158 | /* immword = IMM_W; */ | |
159 | ||
160 | oldpc = PC; | |
161 | delay_slot_enable = 0; | |
162 | branch_taken = 0; | |
163 | if (op == microblaze_brk) | |
164 | CPU.exception = SIGTRAP; | |
165 | else if (inst == MICROBLAZE_HALT_INST) | |
166 | { | |
167 | CPU.exception = SIGQUIT; | |
168 | insts += 1; | |
169 | bonus_cycles++; | |
170 | } | |
171 | else | |
172 | { | |
173 | switch(op) | |
174 | { | |
175 | #define INSTRUCTION(NAME, OPCODE, TYPE, ACTION) \ | |
176 | case NAME: \ | |
177 | ACTION; \ | |
178 | break; | |
179 | #include "microblaze.isa" | |
180 | #undef INSTRUCTION | |
181 | ||
182 | default: | |
183 | CPU.exception = SIGILL; | |
184 | fprintf (stderr, "ERROR: Unknown opcode\n"); | |
185 | } | |
186 | /* Make R0 consistent */ | |
187 | CPU.regs[0] = 0; | |
188 | ||
189 | /* Check for imm instr */ | |
190 | if (op == imm) | |
191 | IMM_ENABLE = 1; | |
192 | else | |
193 | IMM_ENABLE = 0; | |
194 | ||
195 | /* Update cycle counts */ | |
196 | insts ++; | |
197 | if (insn_type == memory_store_inst || insn_type == memory_load_inst) | |
198 | memops++; | |
199 | if (insn_type == mult_inst) | |
200 | bonus_cycles++; | |
201 | if (insn_type == barrel_shift_inst) | |
202 | bonus_cycles++; | |
203 | if (insn_type == anyware_inst) | |
204 | bonus_cycles++; | |
205 | if (insn_type == div_inst) | |
206 | bonus_cycles += 33; | |
207 | ||
208 | if ((insn_type == branch_inst || insn_type == return_inst) | |
ba14f941 | 209 | && branch_taken) |
bd30e45a ME |
210 | { |
211 | /* Add an extra cycle for taken branches */ | |
212 | bonus_cycles++; | |
213 | /* For branch instructions handle the instruction in the delay slot */ | |
ba14f941 | 214 | if (delay_slot_enable) |
bd30e45a ME |
215 | { |
216 | newpc = PC; | |
217 | PC = oldpc + INST_SIZE; | |
c85fc610 | 218 | inst = MEM_RD_WORD (PC & 0xFFFFFFFC); |
bd30e45a ME |
219 | op = get_insn_microblaze (inst, &imm_unsigned, &insn_type, |
220 | &num_delay_slot); | |
221 | if (op == invalid_inst) | |
222 | fprintf (stderr, "Unknown instruction 0x%04x", inst); | |
223 | if (tracing) | |
224 | fprintf (stderr, "%.4x: inst = %.4x ", PC, inst); | |
225 | rd = GET_RD; | |
226 | rb = GET_RB; | |
227 | ra = GET_RA; | |
228 | /* immword = IMM_W; */ | |
229 | if (op == microblaze_brk) | |
230 | { | |
c85fc610 | 231 | if (STATE_VERBOSE_P (sd)) |
bd30e45a ME |
232 | fprintf (stderr, "Breakpoint set in delay slot " |
233 | "(at address 0x%x) will not be honored\n", PC); | |
234 | /* ignore the breakpoint */ | |
235 | } | |
236 | else if (insn_type == branch_inst || insn_type == return_inst) | |
237 | { | |
c85fc610 | 238 | if (STATE_VERBOSE_P (sd)) |
bd30e45a ME |
239 | fprintf (stderr, "Cannot have branch or return instructions " |
240 | "in delay slot (at address 0x%x)\n", PC); | |
241 | CPU.exception = SIGILL; | |
242 | } | |
243 | else | |
244 | { | |
245 | switch(op) | |
246 | { | |
247 | #define INSTRUCTION(NAME, OPCODE, TYPE, ACTION) \ | |
248 | case NAME: \ | |
249 | ACTION; \ | |
250 | break; | |
251 | #include "microblaze.isa" | |
252 | #undef INSTRUCTION | |
253 | ||
254 | default: | |
255 | CPU.exception = SIGILL; | |
256 | fprintf (stderr, "ERROR: Unknown opcode at 0x%x\n", PC); | |
257 | } | |
258 | /* Update cycle counts */ | |
259 | insts++; | |
260 | if (insn_type == memory_store_inst | |
ba14f941 | 261 | || insn_type == memory_load_inst) |
bd30e45a ME |
262 | memops++; |
263 | if (insn_type == mult_inst) | |
264 | bonus_cycles++; | |
265 | if (insn_type == barrel_shift_inst) | |
266 | bonus_cycles++; | |
267 | if (insn_type == anyware_inst) | |
268 | bonus_cycles++; | |
269 | if (insn_type == div_inst) | |
270 | bonus_cycles += 33; | |
271 | } | |
272 | /* Restore the PC */ | |
273 | PC = newpc; | |
274 | /* Make R0 consistent */ | |
275 | CPU.regs[0] = 0; | |
276 | /* Check for imm instr */ | |
277 | if (op == imm) | |
278 | IMM_ENABLE = 1; | |
279 | else | |
280 | IMM_ENABLE = 0; | |
281 | } | |
282 | else | |
283 | /* no delay slot: increment cycle count */ | |
284 | bonus_cycles++; | |
285 | } | |
286 | } | |
287 | ||
288 | if (tracing) | |
289 | fprintf (stderr, "\n"); | |
290 | } | |
291 | while (!CPU.exception); | |
292 | ||
293 | /* Hide away the things we've cached while executing. */ | |
294 | /* CPU.pc = pc; */ | |
295 | CPU.insts += insts; /* instructions done ... */ | |
296 | CPU.cycles += insts; /* and each takes a cycle */ | |
297 | CPU.cycles += bonus_cycles; /* and extra cycles for branches */ | |
298 | CPU.cycles += memops; /* and memop cycle delays */ | |
bd30e45a ME |
299 | } |
300 | ||
bd30e45a ME |
301 | int |
302 | sim_store_register (SIM_DESC sd, int rn, unsigned char *memory, int length) | |
303 | { | |
2b4bc832 MF |
304 | SIM_CPU *cpu = STATE_CPU (sd, 0); |
305 | ||
bd30e45a ME |
306 | if (rn < NUM_REGS + NUM_SPECIAL && rn >= 0) |
307 | { | |
308 | if (length == 4) | |
309 | { | |
310 | /* misalignment safe */ | |
311 | long ival = microblaze_extract_unsigned_integer (memory, 4); | |
312 | if (rn < NUM_REGS) | |
313 | CPU.regs[rn] = ival; | |
314 | else | |
315 | CPU.spregs[rn-NUM_REGS] = ival; | |
316 | return 4; | |
317 | } | |
318 | else | |
319 | return 0; | |
320 | } | |
321 | else | |
322 | return 0; | |
323 | } | |
324 | ||
325 | int | |
326 | sim_fetch_register (SIM_DESC sd, int rn, unsigned char *memory, int length) | |
327 | { | |
2b4bc832 | 328 | SIM_CPU *cpu = STATE_CPU (sd, 0); |
bd30e45a | 329 | long ival; |
2b4bc832 | 330 | |
bd30e45a ME |
331 | if (rn < NUM_REGS + NUM_SPECIAL && rn >= 0) |
332 | { | |
333 | if (length == 4) | |
334 | { | |
335 | if (rn < NUM_REGS) | |
336 | ival = CPU.regs[rn]; | |
337 | else | |
338 | ival = CPU.spregs[rn-NUM_REGS]; | |
339 | ||
340 | /* misalignment-safe */ | |
341 | microblaze_store_unsigned_integer (memory, 4, ival); | |
342 | return 4; | |
343 | } | |
344 | else | |
345 | return 0; | |
346 | } | |
347 | else | |
348 | return 0; | |
349 | } | |
350 | ||
bd30e45a ME |
351 | void |
352 | sim_stop_reason (SIM_DESC sd, enum sim_stop *reason, int *sigrc) | |
353 | { | |
2b4bc832 MF |
354 | SIM_CPU *cpu = STATE_CPU (sd, 0); |
355 | ||
bd30e45a ME |
356 | if (CPU.exception == SIGQUIT) |
357 | { | |
358 | *reason = sim_exited; | |
359 | *sigrc = RETREG; | |
360 | } | |
361 | else | |
362 | { | |
363 | *reason = sim_stopped; | |
364 | *sigrc = CPU.exception; | |
365 | } | |
366 | } | |
367 | ||
bd30e45a ME |
368 | void |
369 | sim_info (SIM_DESC sd, int verbose) | |
370 | { | |
2b4bc832 MF |
371 | SIM_CPU *cpu = STATE_CPU (sd, 0); |
372 | host_callback *callback = STATE_CALLBACK (sd); | |
373 | ||
bd30e45a ME |
374 | callback->printf_filtered (callback, "\n\n# instructions executed %10d\n", |
375 | CPU.insts); | |
376 | callback->printf_filtered (callback, "# cycles %10d\n", | |
377 | (CPU.cycles) ? CPU.cycles+2 : 0); | |
bd30e45a ME |
378 | } |
379 | ||
27b97b40 MF |
380 | static sim_cia |
381 | microblaze_pc_get (sim_cpu *cpu) | |
382 | { | |
383 | return cpu->microblaze_cpu.spregs[0]; | |
384 | } | |
385 | ||
386 | static void | |
387 | microblaze_pc_set (sim_cpu *cpu, sim_cia pc) | |
388 | { | |
389 | cpu->microblaze_cpu.spregs[0] = pc; | |
390 | } | |
391 | ||
2b4bc832 MF |
392 | static void |
393 | free_state (SIM_DESC sd) | |
394 | { | |
395 | if (STATE_MODULES (sd) != NULL) | |
396 | sim_module_uninstall (sd); | |
397 | sim_cpu_free_all (sd); | |
398 | sim_state_free (sd); | |
399 | } | |
400 | ||
bd30e45a ME |
401 | SIM_DESC |
402 | sim_open (SIM_OPEN_KIND kind, host_callback *cb, struct bfd *abfd, char **argv) | |
403 | { | |
2b4bc832 MF |
404 | int i; |
405 | SIM_DESC sd = sim_state_alloc (kind, cb); | |
406 | SIM_ASSERT (STATE_MAGIC (sd) == SIM_MAGIC_NUMBER); | |
407 | ||
408 | /* The cpu data is kept in a separately allocated chunk of memory. */ | |
409 | if (sim_cpu_alloc_all (sd, 1, /*cgen_cpu_max_extra_bytes ()*/0) != SIM_RC_OK) | |
410 | { | |
411 | free_state (sd); | |
412 | return 0; | |
413 | } | |
414 | ||
415 | if (sim_pre_argv_init (sd, argv[0]) != SIM_RC_OK) | |
416 | { | |
417 | free_state (sd); | |
418 | return 0; | |
419 | } | |
bd30e45a | 420 | |
2b4bc832 MF |
421 | /* getopt will print the error message so we just have to exit if this fails. |
422 | FIXME: Hmmm... in the case of gdb we need getopt to call | |
423 | print_filtered. */ | |
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 | } | |
bd30e45a | 455 | |
2b4bc832 MF |
456 | /* CPU specific initialization. */ |
457 | for (i = 0; i < MAX_NR_PROCESSORS; ++i) | |
458 | { | |
459 | SIM_CPU *cpu = STATE_CPU (sd, i); | |
2b4bc832 | 460 | |
27b97b40 MF |
461 | CPU_PC_FETCH (cpu) = microblaze_pc_get; |
462 | CPU_PC_STORE (cpu) = microblaze_pc_set; | |
463 | ||
2b4bc832 | 464 | set_initial_gprs (cpu); |
2b4bc832 | 465 | } |
bd30e45a | 466 | |
c85fc610 MF |
467 | /* Default to a 8 Mbyte (== 2^23) memory space. */ |
468 | sim_do_commandf (sd, "memory-size 0x800000"); | |
469 | ||
2b4bc832 | 470 | return sd; |
bd30e45a ME |
471 | } |
472 | ||
473 | void | |
474 | sim_close (SIM_DESC sd, int quitting) | |
475 | { | |
2b4bc832 | 476 | /* Do nothing. */ |
bd30e45a ME |
477 | } |
478 | ||
479 | SIM_RC | |
480 | sim_create_inferior (SIM_DESC sd, struct bfd *prog_bfd, char **argv, char **env) | |
481 | { | |
2b4bc832 | 482 | SIM_CPU *cpu = STATE_CPU (sd, 0); |
bd30e45a | 483 | |
bd30e45a ME |
484 | PC = bfd_get_start_address (prog_bfd); |
485 | ||
bd30e45a ME |
486 | return SIM_RC_OK; |
487 | } |