1 /* Simulator for Xilinx MicroBlaze processor 2 Copyright 2009-2023 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-signal.h" 34 #include "sim-syscall.h" 35 36 #include "microblaze-dis.h" 37 38 #define target_big_endian (CURRENT_TARGET_BYTE_ORDER == BFD_ENDIAN_BIG) 39 40 static unsigned long 41 microblaze_extract_unsigned_integer (const 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 " 50 "%zu bytes.", sizeof (unsigned long)); 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 70 static void 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 96 static void 97 set_initial_gprs (SIM_CPU *cpu) 98 { 99 int i; 100 long space; 101 102 /* Set up machine just out of reset. */ 103 PC = 0; 104 MSR = 0; 105 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; 112 } 113 114 static int tracing = 0; 115 116 void 117 sim_engine_run (SIM_DESC sd, 118 int next_cpu_nr, /* ignore */ 119 int nr_cpus, /* ignore */ 120 int siggnal) /* ignore */ 121 { 122 SIM_CPU *cpu = STATE_CPU (sd, 0); 123 int needfetch; 124 signed_4 inst; 125 enum microblaze_instr op; 126 int memops; 127 int bonus_cycles; 128 int insts; 129 int w; 130 int cycs; 131 signed_4 WLhash; 132 unsigned_1 carry; 133 bool imm_unsigned; 134 short ra, rb, rd; 135 long immword; 136 unsigned_4 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 142 memops = 0; 143 bonus_cycles = 0; 144 insts = 0; 145 146 while (1) 147 { 148 /* Fetch the initial instructions that we'll decode. */ 149 inst = MEM_RD_WORD (PC & 0xFFFFFFFC); 150 151 op = get_insn_microblaze (inst, &imm_unsigned, &insn_type, 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) 169 sim_engine_halt (sd, NULL, NULL, NULL_CIA, sim_stopped, SIM_SIGTRAP); 170 else if (inst == MICROBLAZE_HALT_INST) 171 { 172 insts += 1; 173 bonus_cycles++; 174 TRACE_INSN (cpu, "HALT (%i)", RETREG); 175 sim_engine_halt (sd, NULL, NULL, NULL_CIA, sim_exited, RETREG); 176 } 177 else 178 { 179 switch(op) 180 { 181 #define INSTRUCTION(NAME, OPCODE, TYPE, ACTION) \ 182 case NAME: \ 183 TRACE_INSN (cpu, #NAME); \ 184 ACTION; \ 185 break; 186 #include "microblaze.isa" 187 #undef INSTRUCTION 188 189 default: 190 sim_engine_halt (sd, NULL, NULL, NULL_CIA, sim_signalled, 191 SIM_SIGILL); 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) 217 && branch_taken) 218 { 219 /* Add an extra cycle for taken branches */ 220 bonus_cycles++; 221 /* For branch instructions handle the instruction in the delay slot */ 222 if (delay_slot_enable) 223 { 224 newpc = PC; 225 PC = oldpc + INST_SIZE; 226 inst = MEM_RD_WORD (PC & 0xFFFFFFFC); 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 { 239 if (STATE_VERBOSE_P (sd)) 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 { 246 if (STATE_VERBOSE_P (sd)) 247 fprintf (stderr, "Cannot have branch or return instructions " 248 "in delay slot (at address 0x%x)\n", PC); 249 sim_engine_halt (sd, NULL, NULL, NULL_CIA, sim_signalled, 250 SIM_SIGILL); 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: 264 sim_engine_halt (sd, NULL, NULL, NULL_CIA, 265 sim_signalled, SIM_SIGILL); 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 271 || insn_type == memory_load_inst) 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 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 } 305 } 306 } 307 308 if (tracing) 309 fprintf (stderr, "\n"); 310 311 if (sim_events_tick (sd)) 312 sim_events_process (sd); 313 } 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 */ 321 } 322 323 static int 324 microblaze_reg_store (SIM_CPU *cpu, int rn, const void *memory, int length) 325 { 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 345 static int 346 microblaze_reg_fetch (SIM_CPU *cpu, int rn, void *memory, int length) 347 { 348 long ival; 349 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 370 void 371 sim_info (SIM_DESC sd, int verbose) 372 { 373 SIM_CPU *cpu = STATE_CPU (sd, 0); 374 host_callback *callback = STATE_CALLBACK (sd); 375 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); 380 } 381 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 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 403 SIM_DESC 404 sim_open (SIM_OPEN_KIND kind, host_callback *cb, 405 struct bfd *abfd, char * const *argv) 406 { 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. */ 412 if (sim_cpu_alloc_all (sd, 1) != SIM_RC_OK) 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 } 423 424 /* The parser will print an error message for us, so we silently return. */ 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, STATE_PROG_FILE (sd), abfd) != SIM_RC_OK) 433 { 434 free_state (sd); 435 return 0; 436 } 437 438 /* Configure/verify the target byte order and other runtime 439 configuration options. */ 440 if (sim_config (sd) != SIM_RC_OK) 441 { 442 sim_module_uninstall (sd); 443 return 0; 444 } 445 446 if (sim_post_argv_init (sd) != SIM_RC_OK) 447 { 448 /* Uninstall the modules to avoid memory leaks, 449 file descriptor leaks, etc. */ 450 sim_module_uninstall (sd); 451 return 0; 452 } 453 454 /* CPU specific initialization. */ 455 for (i = 0; i < MAX_NR_PROCESSORS; ++i) 456 { 457 SIM_CPU *cpu = STATE_CPU (sd, i); 458 459 CPU_REG_FETCH (cpu) = microblaze_reg_fetch; 460 CPU_REG_STORE (cpu) = microblaze_reg_store; 461 CPU_PC_FETCH (cpu) = microblaze_pc_get; 462 CPU_PC_STORE (cpu) = microblaze_pc_set; 463 464 set_initial_gprs (cpu); 465 } 466 467 /* Default to a 8 Mbyte (== 2^23) memory space. */ 468 sim_do_commandf (sd, "memory-size 0x800000"); 469 470 return sd; 471 } 472 473 SIM_RC 474 sim_create_inferior (SIM_DESC sd, struct bfd *prog_bfd, 475 char * const *argv, char * const *env) 476 { 477 SIM_CPU *cpu = STATE_CPU (sd, 0); 478 479 PC = bfd_get_start_address (prog_bfd); 480 481 return SIM_RC_OK; 482 } 483