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