/* 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 <http://www.gnu.org/licenses/>. */
#include "config.h"
#include <unistd.h>
#include <signal.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#ifdef WIN32
#include <winsock.h>
#else
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <netdb.h>
#endif
#include <fcntl.h>
#include "sis.h"
#include <inttypes.h>
#include <sys/time.h>
/* 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);
}