/* This file is part of SIS (SPARC instruction simulator) Copyright (C) 1995-2017 Free Software Foundation, Inc. Contributed by Jiri Gaisler, European Space Agency 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 #ifdef WIN32 #include #else #include #include #include #include #endif #include #include "sis.h" #include #include /* set if UART device cannot handle attributes, terminal oriented IO by default */ int dumbio = 0; /* set if UARTs are connected to a tty, enable by default */ int tty_setup = 1; struct pstate sregs[NCPU]; struct estate ebase; struct evcell evbuf[MAX_EVENT]; int ctrl_c = 0; int sis_verbose = 0; char *sis_version = PACKAGE_VERSION; int nfp = 0; int ift = 0; int wrp = 0; int rom8 = 0; int uben = 0; int termsave; char uart_dev1[128] = ""; char uart_dev2[128] = ""; uint32 last_load_addr = 0; int nouartrx = 0; int port = 1234; int sim_run = 0; int sync_rt = 0; char bridge[32] = ""; /* RAM and ROM for all systems */ char romb[MAX_ROM_SIZE]; char ramb[MAX_RAM_SIZE]; const struct memsys *ms; int cputype = 0; int archtype = 0; int sis_gdb_break; int cpu = 0; /* active cpu */ int ncpu = 1; /* number of cpus to emulate */ int delta = 50; /* time slice for MP simulation */ const struct cpu_arch *arch = &sparc32; uint32 daddr = 0; /* static bfd *abfd; static asymbol **asymbols; static int symsize = 0; static int symcount = 0; */ /* Forward declarations */ static int batch (struct pstate *sregs, char *fname); static void init_event (void); static void disp_mem (uint32 addr, uint32 len); static ssize_t mygetline (char **lineptr, size_t * n, FILE * stream); static void symprint (); static uint32 symtoaddr (char *s); static int batch (sregs, fname) struct pstate *sregs; char *fname; { FILE *fp; char *lbuf = NULL; size_t len = 0; size_t slen; if ((fp = fopen (fname, "r")) == NULL) { fprintf (stderr, "couldn't open batch file %s\n", fname); return 0; } while (mygetline (&lbuf, &len, fp) > -1) { slen = strlen (lbuf); if (slen && (lbuf[slen - 1] == '\n')) { lbuf[slen - 1] = 0; printf ("sis> %s\n", lbuf); exec_cmd (lbuf); } } free (lbuf); fclose (fp); return 1; } static uint64 limcalc (freq) float32 freq; { uint64 unit, lim; double flim; char *cmd1, *cmd2; unit = 1; lim = -1; if ((cmd1 = strtok (NULL, " \t\n\r")) != NULL) { lim = VAL (cmd1); if ((cmd2 = strtok (NULL, " \t\n\r")) != NULL) { if (strcmp (cmd2, "us") == 0) unit = 1; if (strcmp (cmd2, "ms") == 0) unit = 1000; if (strcmp (cmd2, "s") == 0) unit = 1000000; } flim = (double) lim *(double) unit *(double) freq + (double) ebase.simtime; if (flim > ebase.simtime) { lim = (uint64) flim; } else { printf ("error in expression\n"); lim = 0; } } return lim; } int exec_cmd (const char *cmd) { char *cmd1, *cmd2; int32 stat, i; uint32 len, clen, j; char *cmdsave, *cmdsave2 = NULL; stat = OK; if (!cmd) return stat; cmdsave = strdup (cmd); cmdsave2 = strdup (cmd); if ((cmd1 = strtok (cmdsave2, " \t")) != NULL) { clen = strlen (cmd1); if (strncmp (cmd1, "bp", clen) == 0) { for (i = 0; i < ebase.bptnum; i++) { printf (" %d : 0x%08x\n", i + 1, ebase.bpts[i]); } } else if ((strncmp (cmd1, "+bp", clen) == 0) || (strncmp (cmd1, "break", clen) == 0)) { if ((cmd1 = strtok (NULL, " \t\n\r")) != NULL) { if (isdigit (cmd1[0])) len = VAL (cmd1); /* else len = symtoaddr(cmd1); */ if (len) { if (sim_set_watchpoint (len & ~1, 4, 1)) printf ("added breakpoint %d at 0x%08x\n", ebase.bptnum, ebase.bpts[ebase.bptnum - 1]); } } else { for (i = 0; i < ebase.bptnum; i++) { printf (" %d : 0x%08x\n", i + 1, ebase.bpts[i]); } } } else if ((strncmp (cmd1, "-bp", clen) == 0) || (strncmp (cmd1, "delete", clen) == 0)) { if ((cmd1 = strtok (NULL, " \t\n\r")) != NULL) { i = VAL (cmd1) - 1; if ((i >= 0) && (i < ebase.bptnum)) { printf ("deleted breakpoint %d at 0x%08x\n", i + 1, ebase.bpts[i]); for (; i < ebase.bptnum - 1; i++) { ebase.bpts[i] = ebase.bpts[i + 1]; } ebase.bptnum -= 1; } } } else if (strncmp (cmd1, "batch", clen) == 0) { if ((cmd1 = strtok (NULL, " \t\n\r")) == NULL) { printf ("no file specified\n"); } else { batch (sregs, cmd1); } } else if (strncmp (cmd1, "cont", clen) == 0) { if ((cmd1 = strtok (NULL, " \t\n\r")) == NULL) { stat = run_sim (UINT64_MAX / 2, 0); } else { stat = run_sim (VAL (cmd1), 0); } daddr = sregs->pc; ms->sim_halt (); } else if (strncmp (cmd1, "debug", clen) == 0) { if ((cmd1 = strtok (NULL, " \t\n\r")) != NULL) { sis_verbose = VAL (cmd1); } printf ("Debug level = %d\n", sis_verbose); } else if (strncmp (cmd1, "disas", clen) == 0) { if ((cmd1 = strtok (NULL, " \t\n\r")) != NULL) { daddr = VAL (cmd1); } if ((cmd2 = strtok (NULL, " \t\n\r")) != NULL) { len = VAL (cmd2); } else len = 16; printf ("\n"); daddr = dis_mem (daddr, len); printf ("\n"); } else if (strncmp (cmd1, "echo", clen) == 0) { if ((cmd1 = strtok (NULL, " \t\n\r")) != NULL) { printf ("%s\n", (&cmdsave[clen + 1])); } } else if (strncmp (cmd1, "float", clen) == 0) { arch->display_fpu (sregs); } else if (strncmp (cmd1, "go", clen) == 0) { if ((cmd1 = strtok (NULL, " \t\n\r")) == NULL) { len = last_load_addr; } else { len = VAL (cmd1); } for (i = 0; i < ncpu; i++) { sregs[i].pc = len & ~1; sregs[i].npc = sregs->pc + 4; } if (ebase.simtime == 0) ms->boot_init (); printf ("resuming at 0x%08x\n", sregs->pc); if ((cmd2 = strtok (NULL, " \t\n\r")) != NULL) { stat = run_sim (VAL (cmd2), 0); } else { stat = run_sim (UINT64_MAX / 2, 0); } daddr = sregs->pc; ms->sim_halt (); } else if (strncmp (cmd1, "gdb", clen) == 0) { if ((cmd1 = strtok (NULL, " \t\n\r")) != NULL) { port = VAL (cmd1); if (port < 1024) port = 1024; } gdb_remote (port); } else if (strncmp (cmd1, "help", clen) == 0) { gen_help (); } else if (strncmp (cmd1, "history", clen) == 0) { if ((cmd1 = strtok (NULL, " \t\n\r")) != NULL) { ebase.histlen = VAL (cmd1); for (i = 0; i < ncpu; i++) { if (sregs[i].histbuf != NULL) free (sregs[i].histbuf); sregs[i].histbuf = (struct histype *) calloc (ebase.histlen, sizeof (struct histype)); sregs[i].histind = 0; } printf ("trace history length = %d\n\r", ebase.histlen); } else { j = sregs[cpu].histind; for (i = 0; i < ebase.histlen; i++) { if (j >= ebase.histlen) j = 0; printf (" %8" PRIu64 " ", sregs[cpu].histbuf[j].time); dis_mem (sregs[cpu].histbuf[j].addr, 1); j++; } } } else if (strncmp (cmd1, "load", clen) == 0) { if ((cmd1 = strtok (NULL, " \t\n\r")) != NULL) { last_load_addr = elf_load (cmd1, 1); daddr = last_load_addr; while ((cmd1 = strtok (NULL, " \t\n\r")) != NULL) last_load_addr = elf_load (cmd1, 1); } else { printf ("load: no file specified\n"); } } else if (strncmp (cmd1, "mem", clen) == 0) { if ((cmd1 = strtok (NULL, " \t\n\r")) != NULL) daddr = VAL (cmd1); if ((cmd2 = strtok (NULL, " \t\n\r")) != NULL) len = VAL (cmd2); else len = 64; disp_mem (daddr, len); daddr += len; } else if (strncmp (cmd1, "cpu", clen) == 0) { if ((cmd1 = strtok (NULL, " \t\n\r")) != NULL) { cpu = VAL (cmd1); if (cpu > NCPU) cpu = NCPU; } printf ("active cpu: %d\n", cpu); } else if (strncmp (cmd1, "ncpu", clen) == 0) { if ((cmd1 = strtok (NULL, " \t\n\r")) != NULL) { ncpu = VAL (cmd1); if (ncpu > NCPU) ncpu = NCPU; } printf ("number of online cpus: %d\n", ncpu); } else if (strncmp (cmd1, "wmem", clen) == 0) { if ((cmd1 = strtok (NULL, " \t\n\r")) != NULL) daddr = VAL (cmd1); if ((cmd2 = strtok (NULL, " \t\n\r")) != NULL) len = VAL (cmd2); ms->sis_memory_write (daddr, (char *) &len, 4); } else if (strncmp (cmd1, "perf", clen) == 0) { cmd1 = strtok (NULL, " \t\n\r"); if ((cmd1 != NULL) && (strncmp (cmd1, "reset", strlen (cmd1)) == 0)) { reset_stat (sregs); } else show_stat (sregs); } else if (strncmp (cmd1, "quit", clen) == 0) { stat = QUIT; } else if (strncmp (cmd1, "csr", clen) == 0) { arch->display_special (&sregs[cpu]); } else if (strncmp (cmd1, "reg", clen) == 0) { cmd1 = strtok (NULL, " \t\n\r"); cmd2 = strtok (NULL, " \t\n\r"); if (cmd2 != NULL) arch->set_register (&sregs[cpu], cmd1, VAL (cmd2), 0); /* else if (cmd1 != NULL) disp_reg(&sregs[cpu], cmd1); */ else { arch->display_registers (&sregs[cpu]); arch->display_ctrl (&sregs[cpu]); } } else if (strncmp (cmd1, "reset", clen) == 0) { ebase.simtime = 0; ebase.simstart = 0; reset_all (); reset_stat (sregs); } else if (strncmp (cmd1, "run", clen) == 0) { ebase.simtime = 0; ebase.simstart = 0; reset_all (); reset_stat (sregs); if (last_load_addr != 0) { for (i = 0; i < ncpu; i++) { sregs[i].pc = last_load_addr & ~3; sregs[i].npc = sregs[i].pc + 4; } } if (ebase.simtime == 0) ms->boot_init (); if ((cmd1 = strtok (NULL, " \t\n\r")) == NULL) { stat = run_sim (UINT64_MAX / 2, 0); } else { stat = run_sim (VAL (cmd1), 0); } daddr = sregs->pc; ms->sim_halt (); } else if (strncmp (cmd1, "shell", clen) == 0) { if ((cmd1 = strtok (NULL, " \t\n\r")) != NULL) { if (system (&cmdsave[clen])) { /* Silence unused return value warning. */ } } /* } else if (strncmp(cmd1, "sym", clen) == 0) { symprint (); */ } else if (strncmp (cmd1, "step", clen) == 0) { stat = run_sim (1, 1); daddr = sregs->pc; ms->sim_halt (); } else if (strncmp (cmd1, "tcont", clen) == 0) { ebase.tlimit = limcalc (ebase.freq); stat = run_sim (UINT64_MAX / 2, 0); daddr = sregs->pc; ms->sim_halt (); } else if (strncmp (cmd1, "tgo", clen) == 0) { if ((cmd1 = strtok (NULL, " \t\n\r")) == NULL) { len = last_load_addr; } else { len = VAL (cmd1); ebase.tlimit = limcalc (ebase.freq); } sregs->pc = len & ~1; sregs->npc = sregs->pc + 4; printf ("resuming at 0x%08x\n", sregs->pc); stat = run_sim (UINT64_MAX / 2, 0); daddr = sregs->pc; ms->sim_halt (); } else if (strncmp (cmd1, "tlimit", clen) == 0) { ebase.tlimit = limcalc (ebase.freq); if (ebase.tlimit != (uint32) - 1) if (sis_verbose) printf ("simulation limit = %u (%.3f ms)\n", (uint32) ebase.tlimit, ebase.tlimit / ebase.freq / 1000); } else if (strncmp (cmd1, "tra", clen) == 0) { if ((cmd1 = strtok (NULL, " \t\n\r")) == NULL) { stat = run_sim (UINT64_MAX / 2, 1); } else { stat = run_sim (VAL (cmd1), 1); } printf ("\n"); daddr = sregs->pc; ms->sim_halt (); } else if (strncmp (cmd1, "trun", clen) == 0) { ebase.simtime = 0; ebase.simstart = 0; reset_all (); reset_stat (sregs); if (last_load_addr != 0) { for (i = 0; i < ncpu; i++) { sregs[i].pc = last_load_addr & ~3; sregs[i].npc = sregs[i].pc + 4; } } if ((sregs->pc != 0) && (ebase.simtime == 0)) ms->boot_init (); ebase.tlimit = limcalc (ebase.freq); stat = run_sim (UINT64_MAX / 2, 0); daddr = sregs->pc; ms->sim_halt (); } else if (strncmp (cmd1, "wp", clen) == 0) { for (i = 0; i < ebase.wprnum; i++) { printf (" %d : 0x%08x (read)\n", i + 1, ebase.wprs[i]); } for (i = 0; i < ebase.wpwnum; i++) { printf (" %d : 0x%08x (write)\n", i + 1, ebase.wpws[i]); } } else if ((strncmp (cmd1, "+wpr", clen) == 0) || (strncmp (cmd1, "rwatch", clen) == 0)) { if ((cmd1 = strtok (NULL, " \t\n\r")) != NULL) { if (sim_set_watchpoint (VAL (cmd1) & ~0x3, 4, 3)) printf ("added read watchpoint %d at 0x%08x\n", ebase.wprnum, ebase.wprs[ebase.wprnum - 1]); } } else if (strncmp (cmd1, "-wpr", clen) == 0) { if ((cmd1 = strtok (NULL, " \t\n\r")) != NULL) { i = VAL (cmd1) - 1; if ((i >= 0) && (i < ebase.wprnum)) { printf ("deleted read watchpoint %d at 0x%08x\n", i + 1, ebase.wprs[i]); for (; i < ebase.wprnum - 1; i++) { ebase.wprs[i] = ebase.wprs[i + 1]; } ebase.wprnum -= 1; } } } else if ((strncmp (cmd1, "+wpw", clen) == 0) || (strncmp (cmd1, "watch", clen) == 0)) { if ((cmd1 = strtok (NULL, " \t\n\r")) != NULL) { if (sim_set_watchpoint (VAL (cmd1) & ~0x3, 4, 2)) printf ("added write watchpoint %d at 0x%08x\n", ebase.wpwnum, ebase.wpws[ebase.wpwnum - 1]); } else { for (i = 0; i < ebase.wpwnum; i++) { printf (" %d : 0x%08x (write)\n", i + 1, ebase.wpws[i]); } } } else if (strncmp (cmd1, "-wpw", clen) == 0) { if ((cmd1 = strtok (NULL, " \t\n\r")) != NULL) { i = VAL (cmd1) - 1; if ((i >= 0) && (i < ebase.wpwnum)) { printf ("deleted write watchpoint %d at 0x%08x\n", i + 1, ebase.wpws[i]); for (; i < ebase.wpwnum - 1; i++) { ebase.wpws[i] = ebase.wpws[i + 1]; } ebase.wpwnum -= 1; } } } else printf ("syntax error\n"); } if (cmdsave2 != NULL) free (cmdsave2); if (cmdsave != NULL) free (cmdsave); return stat; } void reset_stat (sregs) struct pstate *sregs; { ebase.tottime = 0.0; sregs->pwdtime = 0; sregs->ninst = 0; sregs->fholdt = 0; sregs->holdt = 0; sregs->icntt = 0; sregs->finst = 0; sregs->nstore = 0; sregs->nload = 0; sregs->nbranch = 0; ebase.simstart = ebase.simtime; sregs->l1imiss = 0; sregs->l1dmiss = 0; } void show_stat (sregs) struct pstate *sregs; { uint64 iinst, ninst, pwdtime; uint64 stime, atime; int i; ninst = 0; pwdtime = 0; atime = 0; if (ebase.tottime == 0.0) ebase.tottime += 1E-6; for (i = 0; i < ncpu; i++) { if (sregs[i].pwd_mode) { sregs[i].pwdtime += sregs[i].simtime - sregs[i].pwdstart; sregs[i].pwdstart = sregs[i].simtime; } ninst += sregs[i].ninst; pwdtime += sregs[i].pwdtime; } stime = ebase.simtime - ebase.simstart; /* Total simulated time */ printf ("\n Frequency : %4.1f MHz\n", ebase.freq); printf (" Cycles : %" PRIu64 "\n", stime); printf (" Instructions : %" PRIu64 "\n", ninst); printf (" Simulated time : %.2f s\n", (double) (stime) / 1000000.0 / ebase.freq); printf (" System perf. : %.2f MOPS\n", (double) ninst / ((double) (stime) / ebase.freq)); printf (" Real-time perf. : %.2f %%\n", 100.0 / (ebase.tottime / ((double) (stime) / (ebase.freq * 1.0E6)))); printf (" Simulator perf. : %.2f MIPS\n", (double) (ninst / ebase.tottime / 1E6)); printf (" Wall time : %.2f s\n\n", ebase.tottime); printf (" Core MIPS MFLOPS CPI Util" #ifdef ENABLE_L1CACHE " IHit DHit" #endif "\n"); for (i = 0; i < ncpu; i++) { #ifdef STAT iinst = sregs[i].ninst - sregs[i].finst - sregs[i].nload - sregs[i].nstore - sregs[i].nbranch; #endif stime = sregs[i].simtime - ebase.simstart + 1; /* Core simulated time */ printf (" %d %5.2f %5.2f %5.2f %5.2f%%" #ifdef ENABLE_L1CACHE " %5.2f%% %5.2f%%" #endif "\n", i, ebase.freq * (double) (sregs[i].ninst - sregs[i].finst) / (double) (stime - sregs[i].pwdtime), ebase.freq * (double) sregs[i].finst / (double) (stime - sregs [i].pwdtime), (double) (stime - sregs[i].pwdtime) / (double) (sregs[i].ninst + 1), 100.0 * (1.0 - ((double) sregs[i].pwdtime / (double) stime)) #ifdef ENABLE_L1CACHE , (double) (sregs[i].ninst - sregs[i].l1imiss + 1) / (double) (sregs[i].ninst + 1) * 100.0, (double) (sregs[i].nload + sregs[i].nstore - sregs[i].l1dmiss + 1) / (double) (sregs[i].nload + sregs[i].nstore + 1) * 100.0 #endif ); } #ifdef STAT printf (" integer : %9.2f %%\n", 100.0 * (double) iinst / (double) sregs[i].ninst); printf (" load : %9.2f %%\n", 100.0 * (double) sregs->nload / (double) sregs[i].ninst); printf (" store : %9.2f %%\n", 100.0 * (double) sregs->nstore / (double) sregs[i].ninst); printf (" branch : %9.2f %%\n", 100.0 * (double) sregs->nbranch / (double) sregs[i].ninst); printf (" float : %9.2f %%\n", 100.0 * (double) sregs->finst / (double) sregs[i].ninst); printf (" Integer CPI : %9.2f\n", ((double) (stime - sregs[i].pwdtime - sregs[i].fholdt - sregs[i].finst)) / (double) (sregs[i].ninst - sregs[i].finst)); printf (" Float CPI : %9.2f\n", ((double) sregs[i].fholdt / (double) sregs[i].finst) + 1.0); #endif printf ("\n"); } void init_bpt (sregs) struct pstate *sregs; { int i; ebase.bptnum = 0; ebase.wprnum = 0; ebase.wpwnum = 0; ebase.histlen = 0; for (i = 0; i < ncpu; i++) { sregs[i].histind = 0; sregs[i].histbuf = NULL; } ebase.tlimit = 0; } /* support for catching ctrl-c */ #ifdef WIN32 BOOL WINAPI ConsoleHandler (DWORD); void init_signals () { if (!SetConsoleCtrlHandler ((PHANDLER_ROUTINE) ConsoleHandler, TRUE)) { fprintf (stderr, "Unable to install ctrl-c handler!\n"); } } BOOL WINAPI ConsoleHandler (DWORD dwType) { switch (dwType) { case CTRL_C_EVENT: case CTRL_BREAK_EVENT: ctrl_c = 1; break; default: break; } return TRUE; } #else void int_handler (int sig) { int count; switch (sig) { case SIGINT: ctrl_c = 1; if (!sim_run) { if (new_socket > 0) close (new_socket); else exit (0); } break; default: printf ("\n\n Signal handler error (%d)\n\n", sig); } } void init_signals () { typedef void (*PFI) (); static PFI int_tab[2]; int_tab[0] = signal (SIGTERM, int_handler); int_tab[1] = signal (SIGINT, int_handler); } #endif void print_insn_sis (uint32 addr) { char i[4]; ms->sis_memory_read (addr, i, 4); arch->disas (addr); } static void disp_mem (addr, len) uint32 addr; uint32 len; { uint32 i; union { char u8[4]; uint32 u32; } data; uint32 mem[4], j; char *p; for (i = addr & ~3; i < ((addr + len) & ~3); i += 16) { printf ("\n %8X ", i); for (j = 0; j < 4; j++) { ms->sis_memory_read ((i + (j * 4)), data.u8, 4); printf ("%08x ", data.u32); mem[j] = data.u32; } printf (" "); p = (char *) mem; for (j = 0; j < 16; j++) { if (isprint (p[j ^ arch->bswap])) putchar (p[j ^ arch->bswap]); else putchar ('.'); } } printf ("\n\n"); } uint32 dis_mem (addr, len) uint32 addr; uint32 len; { uint32 i, data; for (i = 0; i < len; i++) { ms->sis_memory_read (addr, (char *) &data, 4); if ((cputype == CPU_RISCV) && ((data & 3) != 3)) { data &= 0x0ffff; printf (" %08x: %04x ", addr, data); } else printf (" %08x: %08x ", addr, data); print_insn_sis (addr); if (i >= 0xfffffffc) break; printf ("\n"); if ((cputype == CPU_RISCV) && ((data & 3) != 3)) addr += 2; else addr += 4; } return addr; } /* Add event to event queue */ void event (cfunc, arg, delta) void (*cfunc) (); int32 arg; uint64 delta; { struct evcell *ev1, *evins; if (ebase.freeq == NULL) { printf ("Error, too many events in event queue\n"); return; } ev1 = &ebase.eq; delta += ebase.simtime; while ((ev1->nxt != NULL) && (ev1->nxt->time <= delta)) { ev1 = ev1->nxt; } if (ev1->nxt == NULL) { ev1->nxt = ebase.freeq; ebase.freeq = ebase.freeq->nxt; ev1->nxt->nxt = NULL; } else { evins = ebase.freeq; ebase.freeq = ebase.freeq->nxt; evins->nxt = ev1->nxt; ev1->nxt = evins; } ev1->nxt->time = delta; ev1->nxt->cfunc = cfunc; ev1->nxt->arg = arg; ebase.evtime = ebase.eq.nxt->time; } /* remove event from event queue */ void remove_event (cfunc, arg) void (*cfunc) (); int32 arg; { struct evcell *ev1, *evdel; ev1 = &ebase.eq; while (ev1->nxt != NULL) { if ((ev1->nxt->cfunc == cfunc) && ((arg == ev1->nxt->arg) || (arg < 0))) { evdel = ev1->nxt; ev1->nxt = ev1->nxt->nxt; evdel->nxt = ebase.freeq; ebase.freeq = evdel; } ev1 = ev1->nxt; } ebase.evtime = ebase.eq.nxt->time; } static void last_event (int32 arg) { printf ("Warning: end of time ... exiting!\n"); exit (0); } void init_event () { int32 i; ebase.eq.nxt = NULL; ebase.freeq = evbuf; for (i = 0; i < MAX_EVENT; i++) { evbuf[i].nxt = &evbuf[i + 1]; } evbuf[MAX_EVENT - 1].nxt = NULL; event (last_event, 0, UINT64_MAX); } /* Advance simulator time */ void advance_time (endtime) uint64 endtime; { struct evcell *evrem; void (*cfunc) (); uint32 arg; while (ebase.evtime <= endtime) { ebase.simtime = ebase.eq.nxt->time; cfunc = ebase.eq.nxt->cfunc; arg = ebase.eq.nxt->arg; evrem = ebase.eq.nxt; ebase.eq.nxt = ebase.eq.nxt->nxt; ebase.evtime = ebase.eq.nxt->time; evrem->nxt = ebase.freeq; ebase.freeq = evrem; cfunc (arg); } ebase.simtime = endtime; } uint32 now () { return (uint32) ebase.simtime; } void pwd_enter (struct pstate *sregs) { sregs->pwd_mode = 1; sregs->pwdstart = sregs->simtime; sregs->hold += delta; } void rt_sync () { double walltime, realtime, dtime; int64 stime; stime = ebase.simtime - ebase.simstart; /* Total simulated time */ realtime = (double) ((stime) / 1000000.0 / ebase.freq); walltime = ebase.tottime + get_time () - ebase.starttime; dtime = (realtime - walltime); if (dtime > 0.001) { if (dtime > 1.0) dtime = 0.1; usleep ((useconds_t) (dtime * 1E6)); } } int check_bpt (sregs) struct pstate *sregs; { int32 i; if (sregs->bphit) { sregs->bphit = 0; return 0; } for (i = 0; i < (int32) ebase.bptnum; i++) { if (sregs->pc == ebase.bpts[i]) return BPT_HIT; } return 0; } int check_wpr (struct pstate *sregs, int32 address, unsigned char mask) { int32 i, msk; for (i = 0; i < ebase.wprnum; i++) { msk = ~(mask | ebase.wprm[i]); if (((address ^ ebase.wprs[i]) & msk) == 0) { ebase.wpaddress = address; if (ebase.wphit) return (0); ebase.wptype = 3; return (WPT_HIT); } } return (0); } int check_wpw (struct pstate *sregs, int32 address, unsigned char mask) { int32 i, msk; for (i = 0; i < ebase.wpwnum; i++) { msk = ~(mask | ebase.wpwm[i]); if (((address ^ ebase.wpws[i]) & msk) == 0) { ebase.wpaddress = ebase.wpws[i]; if (ebase.wphit) return (0); ebase.wptype = 2; return (WPT_HIT); } } return (0); } void reset_all () { init_event (); /* Clear event queue */ init_regs (sregs); ms->reset (); } void sys_reset () { reset_all (); sregs[0].trap = 256; /* Force fake reset trap */ } void sys_halt () { sregs[0].trap = 257; /* Force fake halt trap */ } /* simulate one core instruction-wise */ static int run_sim_un (sregs, icount, dis) struct pstate *sregs; uint64 icount; int dis; { int irq, mexc, deb; uint32 *inst; if (sregs->err_mode) icount = 0; deb = dis || ebase.histlen || ebase.bptnum; mexc = irq = 0; while (icount > 0) { if (sregs->pwd_mode) { sregs->simtime = ebase.evtime; /* skip forward to next event */ if (ext_irl[sregs->cpu]) irq = arch->check_interrupts (sregs); } else { sregs->icnt = 1; sregs->fhold = 0; if (ext_irl[sregs->cpu]) irq = arch->check_interrupts (sregs); if (!irq) { mexc = ms->memory_iread (sregs->pc, &sregs->inst, &sregs->hold); if (mexc) { sregs->trap = I_ACC_EXC; } else { if (deb) { if ((ebase.bptnum) && (sregs->bphit = check_bpt (sregs))) icount = 0; else { if (ebase.histlen) { sregs->histbuf[sregs->histind].addr = sregs->pc; sregs->histbuf[sregs->histind].time = ebase.simtime; sregs->histind++; if (sregs->histind >= ebase.histlen) sregs->histind = 0; } if (dis) { printf (" %8" PRIu64 " ", ebase.simtime); dis_mem (sregs->pc, 1); } arch->dispatch_instruction (sregs); icount--; advance_time (sregs->simtime); } } else { arch->dispatch_instruction (sregs); icount--; } } } else sregs->trap = irq + 16; if (sregs->trap) { irq = 0; if ((sregs->err_mode = arch->execute_trap (sregs)) == WPT_HIT) { sregs->err_mode = 0; sregs->trap = 0; icount = 0; ebase.bpcpu = sregs->cpu; if (ebase.histlen) { sregs->histind--; if (sregs->histind >= ebase.histlen) sregs->histind = ebase.histlen - 1; } } if (sregs->err_mode) { ms->error_mode (sregs->pc); icount = 0; ebase.bpcpu = sregs->cpu; } } #ifdef STAT sregs->fholdt += sregs->fhold; sregs->holdt += sregs->hold; sregs->icntt += sregs->icnt; #endif sregs->simtime += sregs->icnt + sregs->hold + sregs->fhold; } if (sregs->simtime >= ebase.evtime) advance_time (sregs->simtime); if (ctrl_c) { icount = 0; } } advance_time (sregs->simtime); if (sregs->err_mode) return ERROR_MODE; if (sregs->bphit) return (BPT_HIT); if (ebase.wphit) return (WPT_HIT); if (ctrl_c) { return CTRL_C; } return TIME_OUT; } /* stop simulation after specified time */ static void sim_timeout (int32 arg) { ctrl_c = arg; } /* simulate one core time-wise */ static void run_sim_core (sregs, ntime, deb, dis) struct pstate *sregs; uint64 ntime; int deb; int dis; { int mexc, irq; mexc = irq = 0; if (sregs->pwd_mode == 0) while (ntime > sregs->simtime) { if (ext_irl[sregs->cpu]) irq = arch->check_interrupts (sregs); else irq = 0; sregs->icnt = 1; mexc = ms->memory_iread (sregs->pc, &sregs->inst, &sregs->hold); #ifdef ENABLE_L1CACHE if (sregs->l1itags[(sregs->pc >> L1ILINEBITS) & L1IMASK] != (sregs->pc >> L1ILINEBITS)) { sregs->hold = T_L1IMISS; sregs->l1itags[(sregs->pc >> L1ILINEBITS) & L1IMASK] = (sregs->pc >> L1ILINEBITS); sregs->l1imiss++; } #endif sregs->fhold = 0; if (!irq) { if (mexc) { sregs->trap = I_ACC_EXC; } else { if (deb) { if ((ebase.bptnum) && (sregs->bphit = check_bpt (sregs))) { ntime = sregs->simtime; ctrl_c = 1; ebase.bpcpu = sregs->cpu; break; } if (ebase.histlen) { sregs->histbuf[sregs->histind].addr = sregs->pc; sregs->histbuf[sregs->histind].time = sregs->simtime; sregs->histind++; if (sregs->histind >= ebase.histlen) sregs->histind = 0; } if (dis) { printf ("cpu %d %8" PRIu64 " ", sregs->cpu, sregs->simtime); dis_mem (sregs->pc, 1); } arch->dispatch_instruction (sregs); } else { arch->dispatch_instruction (sregs); } } } else sregs->trap = irq + 16; if (sregs->trap) { irq = 0; if ((sregs->err_mode = arch->execute_trap (sregs)) == WPT_HIT) { sregs->err_mode = 0; sregs->trap = 0; ntime = sregs->simtime; ctrl_c = 1; ebase.bpcpu = sregs->cpu; if (ebase.histlen) { sregs->histind--; if (sregs->histind >= ebase.histlen) sregs->histind = ebase.histlen - 1; } break; } if (sregs->err_mode) { ms->error_mode (sregs->pc); sregs->pwd_mode = 1; sregs->pwdstart = sregs->simtime; sregs->simtime = ntime; ctrl_c = 1; ebase.bpcpu = sregs->cpu; break; } } #ifdef STAT sregs->fholdt += sregs->fhold; sregs->holdt += sregs->hold; sregs->icntt += sregs->icnt; #endif sregs->simtime += sregs->icnt + sregs->hold + sregs->fhold; } else { sregs->simtime = ntime; if (ext_irl[sregs->cpu]) irq = arch->check_interrupts (sregs); } sregs->lrq = 0; } /* time slice simulation of cpu cores in MP system */ static int run_sim_mp (icount, dis) uint64 icount; int dis; { uint64 ntime, etime; int deb, i, j; int err_mode, bphit, wphit, oldcpu; err_mode = bphit = wphit = 0; icount += ebase.simtime; for (i = 0; i < ncpu; i++) { if (sregs[i].err_mode) { icount = 0; err_mode = 1; } } while (icount > ebase.simtime) { ntime = ebase.simtime + delta; if (ntime > icount) ntime = icount; if (ntime > ebase.evtime) ntime = ebase.evtime; for (i = 0; i < ncpu; i++) { deb = dis || ebase.histlen || ebase.bptnum; etime = ntime; run_sim_core (&sregs[i], ntime, deb, dis); err_mode |= sregs[i].err_mode; bphit |= sregs[i].bphit; wphit |= ebase.wphit; if (sregs[i].simtime < etime) etime = sregs[i].simtime; } advance_time (etime); if (ctrl_c) { icount = 0; } } oldcpu = cpu; cpu = ebase.bpcpu; if (err_mode == NULL_HIT) return NULL_HIT; if (err_mode) return ERROR_MODE; if (bphit) return (BPT_HIT); if (wphit) return (WPT_HIT); if (ctrl_c) { return CTRL_C; } cpu = oldcpu; return TIME_OUT; } int run_sim (icount, dis) uint64 icount; int dis; { int res; ctrl_c = 0; sim_run = 1; ebase.starttime = get_time (); ms->init_stdio (); if (ebase.tlimit > ebase.simtime) event (sim_timeout, 2, ebase.tlimit - ebase.simtime); if (ebase.coven) cov_start (sregs[0].pc); if ((ncpu == 1) || (icount == 1)) res = run_sim_un (&sregs[cpu], icount, dis); else res = run_sim_mp (icount, dis); remove_event (sim_timeout, -1); ebase.tottime += get_time () - ebase.starttime; ms->restore_stdio (); if ((res == CTRL_C) && (ctrl_c == 2)) printf ("\nTime-out limit reached\n"); sim_run = 0; return res; } double get_time (void) { double usec; struct timeval tm; gettimeofday (&tm, NULL); usec = ((double) tm.tv_sec) * 1E6 + ((double) tm.tv_usec); return usec / 1E6; } /* Local version of getline() since not all systems supports it */ static const int line_size = 128; static ssize_t mygetdelim (char **lineptr, size_t * n, int delim, FILE * stream) { int indx = 0; int c; /* Sanity checks. */ if (lineptr == NULL || n == NULL || stream == NULL) return -1; /* Allocate the line the first time. */ if (*lineptr == NULL) { *lineptr = malloc (line_size); if (*lineptr == NULL) return -1; *n = line_size; } /* Clear the line. */ memset (*lineptr, '\0', *n); while ((c = getc (stream)) != EOF) { /* Check if more memory is needed. */ if (indx >= *n) { *lineptr = realloc (*lineptr, *n + line_size); if (*lineptr == NULL) { return -1; } /* Clear the rest of the line. */ memset (*lineptr + *n, '\0', line_size); *n += line_size; } /* Push the result in the line. */ (*lineptr)[indx++] = c; /* Bail out. */ if (c == delim) { break; } } return (c == EOF) ? -1 : indx; } static ssize_t mygetline (char **lineptr, size_t * n, FILE * stream) { return mygetdelim (lineptr, n, '\n', stream); } /* Coverage support */ #define COV_EXEC 1 #define COV_START 2 #define COV_JMP 4 #define COV_BT 8 #define COV_BNT 16 unsigned char covram[MAX_RAM_SIZE / 4]; void cov_start (int address) { covram[(address >> 2) & MAX_RAM_MASK] |= (COV_START | COV_EXEC); } void cov_exec (int address) { covram[(address >> 2) & MAX_RAM_MASK] |= COV_EXEC; } void cov_bt (int address1, int address2) { covram[(address1 >> 2) & MAX_RAM_MASK] |= (COV_BT | COV_EXEC); covram[(address2 >> 2) & MAX_RAM_MASK] |= (COV_START | COV_EXEC); } void cov_bnt (int address) { covram[(address >> 2) & MAX_RAM_MASK] |= (COV_BNT | COV_EXEC); } void cov_jmp (int address1, int address2) { covram[(address1 >> 2) & MAX_RAM_MASK] |= (COV_JMP | COV_EXEC); covram[(address2 >> 2) & MAX_RAM_MASK] |= (COV_START | COV_EXEC); } void cov_save (char *name) { FILE *fp; char filename[1024]; int i, j, k, state; strcpy (filename, name); strcat (filename, ".cov"); fp = fopen (filename, "w"); state = 0; for (i = 0; i < MAX_RAM_SIZE / 4; i += 32) { k = 0; for (j = 0; j < 32; j++) { k |= covram[i + j]; } if (k || state) { fprintf (fp, "%08x : ", ebase.ramstart + i * 4); for (j = 0; j < 32; j++) { if (state) covram[i + j] |= COV_EXEC; if (covram[i + j] & COV_START) state = 1; if (covram[i + j] & (COV_JMP | COV_BT | COV_BNT)) state = 0; if ((ebase.ramstart + (i + j) * 4) == sregs[0].pc) state = 0; covram[i + j] &= ~0x6; fprintf (fp, "%x ", covram[i + j]); } fprintf (fp, "\n"); } } printf ("\nsaved code coverage to %s\n", filename); fclose (fp); }