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