1 /* Simulator for Xilinx MicroBlaze processor 2 Copyright 2009-2024 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 <stdlib.h> 23 #include <string.h> 24 #include <unistd.h> 25 #include "bfd.h" 26 #include "sim/callback.h" 27 #include "libiberty.h" 28 #include "sim/sim.h" 29 30 #include "sim-main.h" 31 #include "sim-options.h" 32 #include "sim-signal.h" 33 #include "sim-syscall.h" 34 35 #include "microblaze-sim.h" 36 #include "opcodes/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 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 signed_4 inst; 123 enum microblaze_instr op; 124 int memops; 125 int bonus_cycles; 126 int insts; 127 unsigned_1 carry; 128 bool imm_unsigned; 129 short ra, rb, rd; 130 unsigned_4 oldpc, newpc; 131 short delay_slot_enable; 132 short branch_taken; 133 short num_delay_slot; /* UNUSED except as reqd parameter */ 134 enum microblaze_instr_type insn_type; 135 136 memops = 0; 137 bonus_cycles = 0; 138 insts = 0; 139 140 while (1) 141 { 142 /* Fetch the initial instructions that we'll decode. */ 143 inst = MEM_RD_WORD (PC & 0xFFFFFFFC); 144 145 op = get_insn_microblaze (inst, &imm_unsigned, &insn_type, 146 &num_delay_slot); 147 148 if (op == invalid_inst) 149 fprintf (stderr, "Unknown instruction 0x%04x", inst); 150 151 if (tracing) 152 fprintf (stderr, "%.4x: inst = %.4x ", PC, inst); 153 154 rd = GET_RD; 155 rb = GET_RB; 156 ra = GET_RA; 157 /* immword = IMM_W; */ 158 159 oldpc = PC; 160 delay_slot_enable = 0; 161 branch_taken = 0; 162 if (op == microblaze_brk) 163 sim_engine_halt (sd, NULL, NULL, NULL_CIA, sim_stopped, SIM_SIGTRAP); 164 else if (inst == MICROBLAZE_HALT_INST) 165 { 166 insts += 1; 167 bonus_cycles++; 168 TRACE_INSN (cpu, "HALT (%i)", RETREG); 169 sim_engine_halt (sd, NULL, NULL, NULL_CIA, sim_exited, RETREG); 170 } 171 else 172 { 173 switch(op) 174 { 175 #define INSTRUCTION(NAME, OPCODE, TYPE, ACTION) \ 176 case NAME: \ 177 TRACE_INSN (cpu, #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 { 288 if (op == brki && IMM == 8) 289 { 290 RETREG = sim_syscall (cpu, CPU.regs[12], CPU.regs[5], 291 CPU.regs[6], CPU.regs[7], 292 CPU.regs[8]); 293 PC = RD + INST_SIZE; 294 } 295 296 /* no delay slot: increment cycle count */ 297 bonus_cycles++; 298 } 299 } 300 } 301 302 if (tracing) 303 fprintf (stderr, "\n"); 304 305 if (sim_events_tick (sd)) 306 sim_events_process (sd); 307 } 308 309 /* Hide away the things we've cached while executing. */ 310 /* CPU.pc = pc; */ 311 CPU.insts += insts; /* instructions done ... */ 312 CPU.cycles += insts; /* and each takes a cycle */ 313 CPU.cycles += bonus_cycles; /* and extra cycles for branches */ 314 CPU.cycles += memops; /* and memop cycle delays */ 315 } 316 317 static int 318 microblaze_reg_store (SIM_CPU *cpu, int rn, const void *memory, int length) 319 { 320 if (rn < NUM_REGS + NUM_SPECIAL && rn >= 0) 321 { 322 if (length == 4) 323 { 324 /* misalignment safe */ 325 long ival = microblaze_extract_unsigned_integer (memory, 4); 326 if (rn < NUM_REGS) 327 CPU.regs[rn] = ival; 328 else 329 CPU.spregs[rn-NUM_REGS] = ival; 330 return 4; 331 } 332 else 333 return 0; 334 } 335 else 336 return 0; 337 } 338 339 static int 340 microblaze_reg_fetch (SIM_CPU *cpu, int rn, void *memory, int length) 341 { 342 long ival; 343 344 if (rn < NUM_REGS + NUM_SPECIAL && rn >= 0) 345 { 346 if (length == 4) 347 { 348 if (rn < NUM_REGS) 349 ival = CPU.regs[rn]; 350 else 351 ival = CPU.spregs[rn-NUM_REGS]; 352 353 /* misalignment-safe */ 354 microblaze_store_unsigned_integer (memory, 4, ival); 355 return 4; 356 } 357 else 358 return 0; 359 } 360 else 361 return 0; 362 } 363 364 void 365 sim_info (SIM_DESC sd, bool verbose) 366 { 367 SIM_CPU *cpu = STATE_CPU (sd, 0); 368 host_callback *callback = STATE_CALLBACK (sd); 369 370 callback->printf_filtered (callback, "\n\n# instructions executed %10d\n", 371 CPU.insts); 372 callback->printf_filtered (callback, "# cycles %10d\n", 373 (CPU.cycles) ? CPU.cycles+2 : 0); 374 } 375 376 static sim_cia 377 microblaze_pc_get (sim_cpu *cpu) 378 { 379 return MICROBLAZE_SIM_CPU (cpu)->spregs[0]; 380 } 381 382 static void 383 microblaze_pc_set (sim_cpu *cpu, sim_cia pc) 384 { 385 MICROBLAZE_SIM_CPU (cpu)->spregs[0] = pc; 386 } 387 388 static void 389 free_state (SIM_DESC sd) 390 { 391 if (STATE_MODULES (sd) != NULL) 392 sim_module_uninstall (sd); 393 sim_cpu_free_all (sd); 394 sim_state_free (sd); 395 } 396 397 SIM_DESC 398 sim_open (SIM_OPEN_KIND kind, host_callback *cb, 399 struct bfd *abfd, char * const *argv) 400 { 401 int i; 402 SIM_DESC sd = sim_state_alloc (kind, cb); 403 SIM_ASSERT (STATE_MAGIC (sd) == SIM_MAGIC_NUMBER); 404 405 /* The cpu data is kept in a separately allocated chunk of memory. */ 406 if (sim_cpu_alloc_all_extra (sd, 0, sizeof (struct microblaze_regset)) 407 != SIM_RC_OK) 408 { 409 free_state (sd); 410 return 0; 411 } 412 413 if (sim_pre_argv_init (sd, argv[0]) != SIM_RC_OK) 414 { 415 free_state (sd); 416 return 0; 417 } 418 419 /* The parser will print an error message for us, so we silently return. */ 420 if (sim_parse_args (sd, argv) != SIM_RC_OK) 421 { 422 free_state (sd); 423 return 0; 424 } 425 426 /* Check for/establish the a reference program image. */ 427 if (sim_analyze_program (sd, STATE_PROG_FILE (sd), abfd) != SIM_RC_OK) 428 { 429 free_state (sd); 430 return 0; 431 } 432 433 /* Configure/verify the target byte order and other runtime 434 configuration options. */ 435 if (sim_config (sd) != SIM_RC_OK) 436 { 437 sim_module_uninstall (sd); 438 return 0; 439 } 440 441 if (sim_post_argv_init (sd) != SIM_RC_OK) 442 { 443 /* Uninstall the modules to avoid memory leaks, 444 file descriptor leaks, etc. */ 445 sim_module_uninstall (sd); 446 return 0; 447 } 448 449 /* CPU specific initialization. */ 450 for (i = 0; i < MAX_NR_PROCESSORS; ++i) 451 { 452 SIM_CPU *cpu = STATE_CPU (sd, i); 453 454 CPU_REG_FETCH (cpu) = microblaze_reg_fetch; 455 CPU_REG_STORE (cpu) = microblaze_reg_store; 456 CPU_PC_FETCH (cpu) = microblaze_pc_get; 457 CPU_PC_STORE (cpu) = microblaze_pc_set; 458 459 set_initial_gprs (cpu); 460 } 461 462 /* Default to a 8 Mbyte (== 2^23) memory space. */ 463 sim_do_commandf (sd, "memory-size 0x800000"); 464 465 return sd; 466 } 467 468 SIM_RC 469 sim_create_inferior (SIM_DESC sd, struct bfd *prog_bfd, 470 char * const *argv, char * const *env) 471 { 472 SIM_CPU *cpu = STATE_CPU (sd, 0); 473 474 PC = bfd_get_start_address (prog_bfd); 475 476 return SIM_RC_OK; 477 } 478