/* Simulator for Xilinx MicroBlaze processor Copyright 2009-2015 Free Software Foundation, Inc. This file is part of GDB, the GNU debugger. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, see . */ #include "config.h" #include #include #include #include #include #include #include "bfd.h" #include "gdb/callback.h" #include "libiberty.h" #include "gdb/remote-sim.h" #include "run-sim.h" #include "sim-main.h" #include "sim-utils.h" #include "microblaze-dis.h" #ifndef NUM_ELEM #define NUM_ELEM(A) (sizeof (A) / sizeof (A)[0]) #endif static int target_big_endian = 1; static unsigned long heap_ptr = 0; static unsigned long stack_ptr = 0; host_callback *callback; static unsigned long microblaze_extract_unsigned_integer (unsigned char *addr, int len) { unsigned long retval; unsigned char *p; unsigned char *startaddr = (unsigned char *)addr; unsigned char *endaddr = startaddr + len; if (len > (int) sizeof (unsigned long)) printf ("That operation is not available on integers of more than " "%zu bytes.", sizeof (unsigned long)); /* Start at the most significant end of the integer, and work towards the least significant. */ retval = 0; if (!target_big_endian) { for (p = endaddr; p > startaddr;) retval = (retval << 8) | * -- p; } else { for (p = startaddr; p < endaddr;) retval = (retval << 8) | * p ++; } return retval; } static void microblaze_store_unsigned_integer (unsigned char *addr, int len, unsigned long val) { unsigned char *p; unsigned char *startaddr = (unsigned char *)addr; unsigned char *endaddr = startaddr + len; if (!target_big_endian) { for (p = startaddr; p < endaddr;) { *p++ = val & 0xff; val >>= 8; } } else { for (p = endaddr; p > startaddr;) { *--p = val & 0xff; val >>= 8; } } } struct sim_state microblaze_state; int memcycles = 1; static SIM_OPEN_KIND sim_kind; static char *myname; static int issue_messages = 0; static void /* INLINE */ wbat (word x, word v) { if (((uword)x) >= CPU.msize) { if (issue_messages) fprintf (stderr, "byte write to 0x%x outside memory range\n", x); CPU.exception = SIGSEGV; } else { unsigned char *p = CPU.memory + x; p[0] = v; } } static void /* INLINE */ wlat (word x, word v) { if (((uword)x) >= CPU.msize) { if (issue_messages) fprintf (stderr, "word write to 0x%x outside memory range\n", x); CPU.exception = SIGSEGV; } else { if ((x & 3) != 0) { if (issue_messages) fprintf (stderr, "word write to unaligned memory address: 0x%x\n", x); CPU.exception = SIGBUS; } else if (!target_big_endian) { unsigned char *p = CPU.memory + x; p[3] = v >> 24; p[2] = v >> 16; p[1] = v >> 8; p[0] = v; } else { unsigned char *p = CPU.memory + x; p[0] = v >> 24; p[1] = v >> 16; p[2] = v >> 8; p[3] = v; } } } static void /* INLINE */ what (word x, word v) { if (((uword)x) >= CPU.msize) { if (issue_messages) fprintf (stderr, "short write to 0x%x outside memory range\n", x); CPU.exception = SIGSEGV; } else { if ((x & 1) != 0) { if (issue_messages) fprintf (stderr, "short write to unaligned memory address: 0x%x\n", x); CPU.exception = SIGBUS; } else if (!target_big_endian) { unsigned char *p = CPU.memory + x; p[1] = v >> 8; p[0] = v; } else { unsigned char *p = CPU.memory + x; p[0] = v >> 8; p[1] = v; } } } /* Read functions. */ static int /* INLINE */ rbat (word x) { if (((uword)x) >= CPU.msize) { if (issue_messages) fprintf (stderr, "byte read from 0x%x outside memory range\n", x); CPU.exception = SIGSEGV; return 0; } else { unsigned char *p = CPU.memory + x; return p[0]; } } static int /* INLINE */ rlat (word x) { if (((uword) x) >= CPU.msize) { if (issue_messages) fprintf (stderr, "word read from 0x%x outside memory range\n", x); CPU.exception = SIGSEGV; return 0; } else { if ((x & 3) != 0) { if (issue_messages) fprintf (stderr, "word read from unaligned address: 0x%x\n", x); CPU.exception = SIGBUS; return 0; } else if (! target_big_endian) { unsigned char *p = CPU.memory + x; return (p[3] << 24) | (p[2] << 16) | (p[1] << 8) | p[0]; } else { unsigned char *p = CPU.memory + x; return (p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3]; } } } static int /* INLINE */ rhat (word x) { if (((uword)x) >= CPU.msize) { if (issue_messages) fprintf (stderr, "short read from 0x%x outside memory range\n", x); CPU.exception = SIGSEGV; return 0; } else { if ((x & 1) != 0) { if (issue_messages) fprintf (stderr, "short read from unaligned address: 0x%x\n", x); CPU.exception = SIGBUS; return 0; } else if (!target_big_endian) { unsigned char *p = CPU.memory + x; return (p[1] << 8) | p[0]; } else { unsigned char *p = CPU.memory + x; return (p[0] << 8) | p[1]; } } } /* Default to a 8 Mbyte (== 2^23) memory space. */ static int sim_memory_size = 1 << 23; #define MEM_SIZE_FLOOR 64 void sim_size (int size) { sim_memory_size = size; CPU.msize = sim_memory_size; if (CPU.memory) free (CPU.memory); CPU.memory = (unsigned char *) calloc (1, CPU.msize); if (!CPU.memory) { if (issue_messages) fprintf (stderr, "Not enough VM for simulation of %ld bytes of RAM\n", CPU.msize); CPU.msize = 1; CPU.memory = (unsigned char *) calloc (1, 1); } } static void init_pointers (void) { if (CPU.msize != (sim_memory_size)) sim_size (sim_memory_size); } static void set_initial_gprs (void) { int i; long space; unsigned long memsize; init_pointers (); /* Set up machine just out of reset. */ PC = 0; MSR = 0; memsize = CPU.msize / (1024 * 1024); if (issue_messages > 1) fprintf (stderr, "Simulated memory of %ld Mbytes (0x0 .. 0x%08lx)\n", memsize, CPU.msize - 1); /* Clean out the GPRs */ for (i = 0; i < 32; i++) CPU.regs[i] = 0; CPU.insts = 0; CPU.cycles = 0; CPU.imm_enable = 0; } #define WATCHFUNCTIONS 1 #ifdef WATCHFUNCTIONS #define MAXWL 80 word WL[MAXWL]; char *WLstr[MAXWL]; int ENDWL=0; int WLincyc; int WLcyc[MAXWL]; int WLcnts[MAXWL]; int WLmax[MAXWL]; int WLmin[MAXWL]; word WLendpc; int WLbcyc; int WLW; #endif static int tracing = 0; void sim_resume (SIM_DESC sd, int step, int siggnal) { int needfetch; word inst; enum microblaze_instr op; int memops; int bonus_cycles; int insts; int w; int cycs; word WLhash; ubyte carry; int imm_unsigned; short ra, rb, rd; long immword; uword oldpc, newpc; short delay_slot_enable; short branch_taken; short num_delay_slot; /* UNUSED except as reqd parameter */ enum microblaze_instr_type insn_type; CPU.exception = step ? SIGTRAP : 0; memops = 0; bonus_cycles = 0; insts = 0; do { /* Fetch the initial instructions that we'll decode. */ inst = rlat (PC & 0xFFFFFFFC); op = get_insn_microblaze (inst, &imm_unsigned, &insn_type, &num_delay_slot); if (op == invalid_inst) fprintf (stderr, "Unknown instruction 0x%04x", inst); if (tracing) fprintf (stderr, "%.4x: inst = %.4x ", PC, inst); rd = GET_RD; rb = GET_RB; ra = GET_RA; /* immword = IMM_W; */ oldpc = PC; delay_slot_enable = 0; branch_taken = 0; if (op == microblaze_brk) CPU.exception = SIGTRAP; else if (inst == MICROBLAZE_HALT_INST) { CPU.exception = SIGQUIT; insts += 1; bonus_cycles++; } else { switch(op) { #define INSTRUCTION(NAME, OPCODE, TYPE, ACTION) \ case NAME: \ ACTION; \ break; #include "microblaze.isa" #undef INSTRUCTION default: CPU.exception = SIGILL; fprintf (stderr, "ERROR: Unknown opcode\n"); } /* Make R0 consistent */ CPU.regs[0] = 0; /* Check for imm instr */ if (op == imm) IMM_ENABLE = 1; else IMM_ENABLE = 0; /* Update cycle counts */ insts ++; if (insn_type == memory_store_inst || insn_type == memory_load_inst) memops++; if (insn_type == mult_inst) bonus_cycles++; if (insn_type == barrel_shift_inst) bonus_cycles++; if (insn_type == anyware_inst) bonus_cycles++; if (insn_type == div_inst) bonus_cycles += 33; if ((insn_type == branch_inst || insn_type == return_inst) && branch_taken) { /* Add an extra cycle for taken branches */ bonus_cycles++; /* For branch instructions handle the instruction in the delay slot */ if (delay_slot_enable) { newpc = PC; PC = oldpc + INST_SIZE; inst = rlat (PC & 0xFFFFFFFC); op = get_insn_microblaze (inst, &imm_unsigned, &insn_type, &num_delay_slot); if (op == invalid_inst) fprintf (stderr, "Unknown instruction 0x%04x", inst); if (tracing) fprintf (stderr, "%.4x: inst = %.4x ", PC, inst); rd = GET_RD; rb = GET_RB; ra = GET_RA; /* immword = IMM_W; */ if (op == microblaze_brk) { if (issue_messages) fprintf (stderr, "Breakpoint set in delay slot " "(at address 0x%x) will not be honored\n", PC); /* ignore the breakpoint */ } else if (insn_type == branch_inst || insn_type == return_inst) { if (issue_messages) fprintf (stderr, "Cannot have branch or return instructions " "in delay slot (at address 0x%x)\n", PC); CPU.exception = SIGILL; } else { switch(op) { #define INSTRUCTION(NAME, OPCODE, TYPE, ACTION) \ case NAME: \ ACTION; \ break; #include "microblaze.isa" #undef INSTRUCTION default: CPU.exception = SIGILL; fprintf (stderr, "ERROR: Unknown opcode at 0x%x\n", PC); } /* Update cycle counts */ insts++; if (insn_type == memory_store_inst || insn_type == memory_load_inst) memops++; if (insn_type == mult_inst) bonus_cycles++; if (insn_type == barrel_shift_inst) bonus_cycles++; if (insn_type == anyware_inst) bonus_cycles++; if (insn_type == div_inst) bonus_cycles += 33; } /* Restore the PC */ PC = newpc; /* Make R0 consistent */ CPU.regs[0] = 0; /* Check for imm instr */ if (op == imm) IMM_ENABLE = 1; else IMM_ENABLE = 0; } else /* no delay slot: increment cycle count */ bonus_cycles++; } } if (tracing) fprintf (stderr, "\n"); } while (!CPU.exception); /* Hide away the things we've cached while executing. */ /* CPU.pc = pc; */ CPU.insts += insts; /* instructions done ... */ CPU.cycles += insts; /* and each takes a cycle */ CPU.cycles += bonus_cycles; /* and extra cycles for branches */ CPU.cycles += memops; /* and memop cycle delays */ } int sim_write (SIM_DESC sd, SIM_ADDR addr, const unsigned char *buffer, int size) { int i; init_pointers (); memcpy (&CPU.memory[addr], buffer, size); return size; } int sim_read (SIM_DESC sd, SIM_ADDR addr, unsigned char *buffer, int size) { int i; init_pointers (); memcpy (buffer, &CPU.memory[addr], size); return size; } int sim_store_register (SIM_DESC sd, int rn, unsigned char *memory, int length) { init_pointers (); if (rn < NUM_REGS + NUM_SPECIAL && rn >= 0) { if (length == 4) { /* misalignment safe */ long ival = microblaze_extract_unsigned_integer (memory, 4); if (rn < NUM_REGS) CPU.regs[rn] = ival; else CPU.spregs[rn-NUM_REGS] = ival; return 4; } else return 0; } else return 0; } int sim_fetch_register (SIM_DESC sd, int rn, unsigned char *memory, int length) { long ival; init_pointers (); if (rn < NUM_REGS + NUM_SPECIAL && rn >= 0) { if (length == 4) { if (rn < NUM_REGS) ival = CPU.regs[rn]; else ival = CPU.spregs[rn-NUM_REGS]; /* misalignment-safe */ microblaze_store_unsigned_integer (memory, 4, ival); return 4; } else return 0; } else return 0; } int sim_trace (SIM_DESC sd) { tracing = 1; sim_resume (sd, 0, 0); tracing = 0; return 1; } void sim_stop_reason (SIM_DESC sd, enum sim_stop *reason, int *sigrc) { if (CPU.exception == SIGQUIT) { *reason = sim_exited; *sigrc = RETREG; } else { *reason = sim_stopped; *sigrc = CPU.exception; } } int sim_stop (SIM_DESC sd) { CPU.exception = SIGINT; return 1; } void sim_info (SIM_DESC sd, int verbose) { #ifdef WATCHFUNCTIONS int w, wcyc; #endif callback->printf_filtered (callback, "\n\n# instructions executed %10d\n", CPU.insts); callback->printf_filtered (callback, "# cycles %10d\n", (CPU.cycles) ? CPU.cycles+2 : 0); #ifdef WATCHFUNCTIONS callback->printf_filtered (callback, "\nNumber of watched functions: %d\n", ENDWL); wcyc = 0; for (w = 1; w <= ENDWL; w++) { callback->printf_filtered (callback, "WL = %s %8x\n",WLstr[w],WL[w]); callback->printf_filtered (callback, " calls = %d, cycles = %d\n", WLcnts[w],WLcyc[w]); if (WLcnts[w] != 0) callback->printf_filtered (callback, " maxcpc = %d, mincpc = %d, avecpc = %d\n", WLmax[w],WLmin[w],WLcyc[w]/WLcnts[w]); wcyc += WLcyc[w]; } callback->printf_filtered (callback, "Total cycles for watched functions: %d\n",wcyc); #endif } struct aout { unsigned char sa_machtype[2]; unsigned char sa_magic[2]; unsigned char sa_tsize[4]; unsigned char sa_dsize[4]; unsigned char sa_bsize[4]; unsigned char sa_syms[4]; unsigned char sa_entry[4]; unsigned char sa_trelo[4]; unsigned char sa_drelo[4]; } aout; #define LONG(x) (((x)[0]<<24)|((x)[1]<<16)|((x)[2]<<8)|(x)[3]) #define SHORT(x) (((x)[0]<<8)|(x)[1]) SIM_DESC sim_open (SIM_OPEN_KIND kind, host_callback *cb, struct bfd *abfd, char **argv) { /* SIM_DESC sd = sim_state_alloc(kind, alloc);*/ int osize = sim_memory_size; myname = argv[0]; callback = cb; if (kind == SIM_OPEN_STANDALONE) issue_messages = 1; /* Discard and reacquire memory -- start with a clean slate. */ sim_size (1); /* small */ sim_size (osize); /* and back again */ set_initial_gprs (); /* Reset the GPR registers. */ return ((SIM_DESC) 1); } void sim_close (SIM_DESC sd, int quitting) { if (CPU.memory) { free(CPU.memory); CPU.memory = NULL; CPU.msize = 0; } } SIM_RC sim_load (SIM_DESC sd, const char *prog, bfd *abfd, int from_tty) { /* Do the right thing for ELF executables; this turns out to be just about the right thing for any object format that: - we crack using BFD routines - follows the traditional UNIX text/data/bss layout - calls the bss section ".bss". */ extern bfd *sim_load_file (); /* ??? Don't know where this should live. */ bfd *prog_bfd; { bfd *handle; asection *s; int found_loadable_section = 0; bfd_vma max_addr = 0; handle = bfd_openr (prog, 0); if (!handle) { printf("``%s'' could not be opened.\n", prog); return SIM_RC_FAIL; } /* Makes sure that we have an object file, also cleans gets the section headers in place. */ if (!bfd_check_format (handle, bfd_object)) { /* wasn't an object file */ bfd_close (handle); printf ("``%s'' is not appropriate object file.\n", prog); return SIM_RC_FAIL; } for (s = handle->sections; s; s = s->next) { if (s->flags & SEC_ALLOC) { bfd_vma vma = 0; int size = bfd_get_section_size (s); if (size > 0) { vma = bfd_section_vma (handle, s); if (vma >= max_addr) { max_addr = vma + size; } } if (s->flags & SEC_LOAD) found_loadable_section = 1; } } if (!found_loadable_section) { /* No loadable sections */ bfd_close(handle); printf("No loadable sections in file %s\n", prog); return SIM_RC_FAIL; } sim_memory_size = (unsigned long) max_addr; /* Clean up after ourselves. */ bfd_close (handle); } /* from sh -- dac */ prog_bfd = sim_load_file (sd, myname, callback, prog, abfd, /* sim_kind == SIM_OPEN_DEBUG, */ 0, 0, sim_write); if (prog_bfd == NULL) return SIM_RC_FAIL; target_big_endian = bfd_big_endian (prog_bfd); PC = bfd_get_start_address (prog_bfd); if (abfd == NULL) bfd_close (prog_bfd); return SIM_RC_OK; } SIM_RC sim_create_inferior (SIM_DESC sd, struct bfd *prog_bfd, char **argv, char **env) { char **avp; int nargs = 0; int nenv = 0; int s_length; int l; unsigned long strings; unsigned long pointers; unsigned long hi_stack; /* Set the initial register set. */ l = issue_messages; issue_messages = 0; set_initial_gprs (); issue_messages = l; hi_stack = CPU.msize - 4; PC = bfd_get_start_address (prog_bfd); /* For now ignore all parameters to the program */ return SIM_RC_OK; } void sim_do_command (SIM_DESC sd, const char *cmd) { /* Nothing there yet; it's all an error. */ if (cmd != NULL) { char ** simargv = buildargv (cmd); if (strcmp (simargv[0], "watch") == 0) { if ((simargv[1] == NULL) || (simargv[2] == NULL)) { fprintf (stderr, "Error: missing argument to watch cmd.\n"); freeargv (simargv); return; } ENDWL++; WL[ENDWL] = strtol (simargv[2], NULL, 0); WLstr[ENDWL] = strdup (simargv[1]); fprintf (stderr, "Added %s (%x) to watchlist, #%d\n",WLstr[ENDWL], WL[ENDWL], ENDWL); } else if (strcmp (simargv[0], "dumpmem") == 0) { unsigned char * p; FILE * dumpfile; if (simargv[1] == NULL) fprintf (stderr, "Error: missing argument to dumpmem cmd.\n"); fprintf (stderr, "Writing dumpfile %s...",simargv[1]); dumpfile = fopen (simargv[1], "w"); p = CPU.memory; fwrite (p, CPU.msize-1, 1, dumpfile); fclose (dumpfile); fprintf (stderr, "done.\n"); } else if (strcmp (simargv[0], "clearstats") == 0) { CPU.cycles = 0; CPU.insts = 0; ENDWL = 0; } else if (strcmp (simargv[0], "verbose") == 0) { issue_messages = 2; } else { fprintf (stderr,"Error: \"%s\" is not a valid M.CORE simulator command.\n", cmd); } freeargv (simargv); } else { fprintf (stderr, "M.CORE sim commands: \n"); fprintf (stderr, " watch \n"); fprintf (stderr, " dumpmem \n"); fprintf (stderr, " clearstats\n"); fprintf (stderr, " verbose\n"); } } void sim_set_callbacks (host_callback *ptr) { callback = ptr; } char ** sim_complete_command (SIM_DESC sd, const char *text, const char *word) { return NULL; }