/* libtoplevel.c -- Top level simulator library source file
|
/* libtoplevel.c -- Top level simulator library source file
|
|
|
Copyright (C) 1999 Damjan Lampret, lampret@opencores.org
|
Copyright (C) 1999 Damjan Lampret, lampret@opencores.org
|
Copyright (C) 2008 Embecosm Limited
|
Copyright (C) 2008 Embecosm Limited
|
|
|
Contributor Jeremy Bennett <jeremy.bennett@embecosm.com>
|
Contributor Jeremy Bennett <jeremy.bennett@embecosm.com>
|
|
|
This file is part of OpenRISC 1000 Architectural Simulator.
|
This file is part of OpenRISC 1000 Architectural Simulator.
|
|
|
This program is free software; you can redistribute it and/or modify it
|
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
|
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)
|
Software Foundation; either version 3 of the License, or (at your option)
|
any later version.
|
any later version.
|
|
|
This program is distributed in the hope that it will be useful, but WITHOUT
|
This program is distributed in the hope that it will be useful, but WITHOUT
|
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
more details.
|
more details.
|
|
|
You should have received a copy of the GNU General Public License along
|
You should have received a copy of the GNU General Public License along
|
with this program. If not, see <http://www.gnu.org/licenses/>. */
|
with this program. If not, see <http://www.gnu.org/licenses/>. */
|
|
|
/* This program is commented throughout in a fashion suitable for processing
|
/* This program is commented throughout in a fashion suitable for processing
|
with Doxygen. */
|
with Doxygen. */
|
|
|
|
|
/* Autoconf and/or portability configuration */
|
/* Autoconf and/or portability configuration */
|
#include "config.h"
|
#include "config.h"
|
|
|
/* System includes */
|
/* System includes */
|
#include <stdlib.h>
|
#include <stdlib.h>
|
#include <unistd.h>
|
#include <unistd.h>
|
#include <signal.h>
|
#include <signal.h>
|
|
|
/* Package includes */
|
/* Package includes */
|
#include "or1ksim.h"
|
#include "or1ksim.h"
|
#include "sim-config.h"
|
#include "sim-config.h"
|
#include "toplevel-support.h"
|
#include "toplevel-support.h"
|
#include "sched.h"
|
#include "sched.h"
|
#include "execute.h"
|
#include "execute.h"
|
#include "pic.h"
|
#include "pic.h"
|
#include "jtag.h"
|
#include "jtag.h"
|
|
|
|
/* Indices of GDB registers that are not GPRs. Must match GDB settings! */
|
|
#define MAX_GPRS 32 /*!< Maximum GPRs */
|
|
#define PPC_REGNUM (MAX_GPRS + 0) /*!< Previous PC */
|
|
#define NPC_REGNUM (MAX_GPRS + 1) /*!< Next PC */
|
|
#define SR_REGNUM (MAX_GPRS + 2) /*!< Supervision Register */
|
|
|
|
|
/*---------------------------------------------------------------------------*/
|
/*---------------------------------------------------------------------------*/
|
/*!Initialize the simulator.
|
/*!Initialize the simulator.
|
|
|
Allows specification of an (optional) config file and an image file. Builds
|
Allows specification of an (optional) config file and an image file. Builds
|
up dummy argc/argv to pass to the existing argument parser.
|
up dummy argc/argv to pass to the existing argument parser.
|
|
|
@param[in] config_file Or1ksim configuration file name
|
@param[in] config_file Or1ksim configuration file name
|
@param[in] image_file The program image to execute
|
@param[in] image_file The program image to execute
|
@param[in] class_ptr Pointer to a C++ class instance (for use when
|
@param[in] class_ptr Pointer to a C++ class instance (for use when
|
called by C++)
|
called by C++)
|
@param[in] upr Upcall routine for reads
|
@param[in] upr Upcall routine for reads
|
@param[in] upw Upcall routine for writes
|
@param[in] upw Upcall routine for writes
|
|
|
@return 0 on success and an error code on failure */
|
@return 0 on success and an error code on failure */
|
/*---------------------------------------------------------------------------*/
|
/*---------------------------------------------------------------------------*/
|
int
|
int
|
or1ksim_init (const char *config_file,
|
or1ksim_init (const char *config_file,
|
const char *image_file,
|
const char *image_file,
|
void *class_ptr,
|
void *class_ptr,
|
int (*upr) (void *class_ptr,
|
int (*upr) (void *class_ptr,
|
unsigned long int addr,
|
unsigned long int addr,
|
unsigned char mask[],
|
unsigned char mask[],
|
unsigned char rdata[],
|
unsigned char rdata[],
|
int data_len),
|
int data_len),
|
int (*upw) (void *class_ptr,
|
int (*upw) (void *class_ptr,
|
unsigned long int addr,
|
unsigned long int addr,
|
unsigned char mask[],
|
unsigned char mask[],
|
unsigned char wdata[],
|
unsigned char wdata[],
|
int data_len))
|
int data_len))
|
{
|
{
|
int dummy_argc;
|
int dummy_argc;
|
char *dummy_argv[4];
|
char *dummy_argv[4];
|
|
|
/* Dummy argv array. Varies depending on whether an image file is
|
/* Dummy argv array. Varies depending on whether an image file is
|
specified. */
|
specified. */
|
dummy_argv[0] = "libsim";
|
dummy_argv[0] = "libsim";
|
dummy_argv[1] = "-f";
|
dummy_argv[1] = "-f";
|
dummy_argv[2] = (char *) ((NULL != config_file) ? config_file : "sim.cfg");
|
dummy_argv[2] = (char *) ((NULL != config_file) ? config_file : "sim.cfg");
|
dummy_argv[3] = (char *) image_file;
|
dummy_argv[3] = (char *) image_file;
|
|
|
dummy_argc = (NULL == image_file) ? 3 : 4;
|
dummy_argc = (NULL == image_file) ? 3 : 4;
|
|
|
/* Initialization copied from existing main() */
|
/* Initialization copied from existing main() */
|
srand (getpid ());
|
srand (getpid ());
|
init_defconfig ();
|
init_defconfig ();
|
reg_config_secs ();
|
reg_config_secs ();
|
|
|
if (parse_args (dummy_argc, dummy_argv))
|
if (parse_args (dummy_argc, dummy_argv))
|
{
|
{
|
return OR1KSIM_RC_BADINIT;
|
return OR1KSIM_RC_BADINIT;
|
}
|
}
|
|
|
|
config.sim.is_library = 1; /* Library operation */
|
config.sim.profile = 0; /* No profiling */
|
config.sim.profile = 0; /* No profiling */
|
config.sim.mprofile = 0;
|
config.sim.mprofile = 0;
|
|
|
config.ext.class_ptr = class_ptr; /* SystemC linkage */
|
config.ext.class_ptr = class_ptr; /* SystemC linkage */
|
config.ext.read_up = upr;
|
config.ext.read_up = upr;
|
config.ext.write_up = upw;
|
config.ext.write_up = upw;
|
|
|
print_config (); /* Will go eventually */
|
print_config (); /* Will go eventually */
|
signal (SIGINT, ctrl_c); /* Not sure we want this really */
|
signal (SIGINT, ctrl_c); /* Not sure we want this really */
|
|
|
runtime.sim.hush = 1; /* Not sure if this is needed */
|
runtime.sim.hush = 1; /* Not sure if this is needed */
|
do_stats = config.cpu.superscalar ||
|
do_stats = config.cpu.superscalar ||
|
config.cpu.dependstats ||
|
config.cpu.dependstats ||
|
config.sim.history ||
|
config.sim.history ||
|
config.sim.exe_log;
|
config.sim.exe_log;
|
|
|
sim_init ();
|
sim_init ();
|
|
|
runtime.sim.ext_int_set = 0; /* No interrupts pending to be set */
|
runtime.sim.ext_int_set = 0; /* No interrupts pending to be set */
|
runtime.sim.ext_int_clr = 0; /* No interrupts pending to be cleared */
|
runtime.sim.ext_int_clr = 0; /* No interrupts pending to be cleared */
|
|
|
return OR1KSIM_RC_OK;
|
return OR1KSIM_RC_OK;
|
|
|
} /* or1ksim_init() */
|
} /* or1ksim_init () */
|
|
|
|
|
/*---------------------------------------------------------------------------*/
|
/*---------------------------------------------------------------------------*/
|
/*!Run the simulator
|
/*!Run the simulator
|
|
|
The argument is a time in seconds, which is converted to a number of
|
The argument is a time in seconds, which is converted to a number of
|
cycles, if positive. A negative value means "run for ever".
|
cycles, if positive. A negative value means "run for ever".
|
|
|
With the JTAG interface, it is possible to stall the processor between
|
With the JTAG interface, it is possible to stall the processor between
|
calls of this function (but not during upcalls). In which case we return
|
calls of this function (but not during upcalls). In which case we return
|
immediately.
|
immediately.
|
|
|
@todo Is it possible (or desirable) to permit JTAG activity during upcalls,
|
@todo Is it possible (or desirable) to permit JTAG activity during upcalls,
|
in which case we could stall mid-run.
|
in which case we could stall mid-run.
|
|
|
@todo Should the JTAG functionality require enabling?
|
@todo Should the JTAG functionality require enabling?
|
|
|
The semantics are that the duration for which the run may occur may be
|
The semantics are that the duration for which the run may occur may be
|
changed mid-run by a call to or1ksim_reset_duration(). This is to allow for
|
changed mid-run by a call to or1ksim_reset_duration(). This is to allow for
|
the upcalls to generic components adding time, and reducing the time
|
the upcalls to generic components adding time, and reducing the time
|
permitted for ISS execution before synchronization of the parent SystemC
|
permitted for ISS execution before synchronization of the parent SystemC
|
wrapper.
|
wrapper.
|
|
|
This is over-ridden if the call was for a negative duration, which means
|
This is over-ridden if the call was for a negative duration, which means
|
run forever!
|
run forever!
|
|
|
Uses a simplified version of the old main program loop. Returns success if
|
Uses a simplified version of the old main program loop. Returns success if
|
the requested number of cycles were run and an error code otherwise.
|
the requested number of cycles were run and an error code otherwise.
|
|
|
@param[in] duration Time to execute for (seconds)
|
@param[in] duration Time to execute for (seconds)
|
|
|
@return OR1KSIM_RC_OK if we run to completion, OR1KSIM_RC_BRKPT if we hit
|
@return OR1KSIM_RC_OK if we run to completion, OR1KSIM_RC_BRKPT if we hit
|
a breakpoint (not clear how this can be set without CLI access) */
|
a breakpoint (not clear how this can be set without CLI access) */
|
/*---------------------------------------------------------------------------*/
|
/*---------------------------------------------------------------------------*/
|
int
|
int
|
or1ksim_run (double duration)
|
or1ksim_run (double duration)
|
{
|
{
|
const int num_ints = sizeof (runtime.sim.ext_int_set) * 8;
|
const int num_ints = sizeof (runtime.sim.ext_int_set) * 8;
|
|
|
/* If we are stalled we can't do anything. We treat this as hitting a
|
/* If we are stalled we can't do anything. We treat this as hitting a
|
breakpoint. */
|
breakpoint or halting. */
|
if(runtime.cpu.stalled)
|
if(runtime.cpu.stalled)
|
{
|
{
|
return OR1KSIM_RC_BRKPT;
|
return runtime.cpu.halted ? OR1KSIM_RC_HALTED : OR1KSIM_RC_BRKPT;
|
}
|
}
|
|
|
/* Reset the duration */
|
/* Reset the duration */
|
or1ksim_reset_duration (duration);
|
or1ksim_reset_duration (duration);
|
|
|
/* Loop until we have done enough cycles (or forever if we had a negative
|
/* Loop until we have done enough cycles (or forever if we had a negative
|
duration) */
|
duration) */
|
while (duration < 0.0 || (runtime.sim.cycles < runtime.sim.end_cycles))
|
while (duration < 0.0 || (runtime.sim.cycles < runtime.sim.end_cycles))
|
{
|
{
|
long long int time_start = runtime.sim.cycles;
|
long long int time_start = runtime.sim.cycles;
|
int i; /* Interrupt # */
|
int i; /* Interrupt # */
|
|
|
/* Each cycle has counter of mem_cycles; this value is joined with cycles
|
/* Each cycle has counter of mem_cycles; this value is joined with cycles
|
* at the end of the cycle; no sim originated memory accesses should be
|
* at the end of the cycle; no sim originated memory accesses should be
|
* performed in between. */
|
* performed in between. */
|
runtime.sim.mem_cycles = 0;
|
runtime.sim.mem_cycles = 0;
|
|
|
if (cpu_clock ())
|
if (cpu_clock ())
|
{
|
{
|
return OR1KSIM_RC_BRKPT; /* Hit a breakpoint */
|
/* This is probably wrong. This is an Or1ksim breakpoint, not a GNU
|
|
one. */
|
|
return runtime.cpu.halted ? OR1KSIM_RC_HALTED : OR1KSIM_RC_BRKPT;
|
|
}
|
|
|
|
/* If we are stalled we can't do anything. We treat this as hitting a
|
|
breakpoint or halting. */
|
|
if(runtime.cpu.stalled)
|
|
{
|
|
return runtime.cpu.halted ? OR1KSIM_RC_HALTED : OR1KSIM_RC_BRKPT;
|
}
|
}
|
|
|
runtime.sim.cycles += runtime.sim.mem_cycles;
|
runtime.sim.cycles += runtime.sim.mem_cycles;
|
|
|
/* Take any external interrupts. Outer test is for the common case for
|
/* Take any external interrupts. Outer test is for the common case for
|
efficiency. */
|
efficiency. */
|
if (0 != runtime.sim.ext_int_set)
|
if (0 != runtime.sim.ext_int_set)
|
{
|
{
|
for (i = 0; i < num_ints; i++)
|
for (i = 0; i < num_ints; i++)
|
{
|
{
|
if (0x1 == ((runtime.sim.ext_int_set >> i) & 0x1))
|
if (0x1 == ((runtime.sim.ext_int_set >> i) & 0x1))
|
{
|
{
|
report_interrupt (i);
|
report_interrupt (i);
|
runtime.sim.ext_int_set &= ~(1 << i); /* Clear req flag */
|
runtime.sim.ext_int_set &= ~(1 << i); /* Clear req flag */
|
}
|
}
|
}
|
}
|
}
|
}
|
|
|
/* Clear any interrupts as requested. For edge triggered interrupts this
|
/* Clear any interrupts as requested. For edge triggered interrupts this
|
will happen in the same cycle. For level triggered, it must be an
|
will happen in the same cycle. For level triggered, it must be an
|
explicit call. */
|
explicit call. */
|
if (0 != runtime.sim.ext_int_clr)
|
if (0 != runtime.sim.ext_int_clr)
|
{
|
{
|
for (i = 0; i < num_ints; i++)
|
for (i = 0; i < num_ints; i++)
|
{
|
{
|
/* Only clear interrupts that have been explicitly cleared */
|
/* Only clear interrupts that have been explicitly cleared */
|
if(0x1 == ((runtime.sim.ext_int_clr >> i) & 0x1))
|
if(0x1 == ((runtime.sim.ext_int_clr >> i) & 0x1))
|
{
|
{
|
clear_interrupt(i);
|
clear_interrupt(i);
|
runtime.sim.ext_int_clr &= ~(1 << i); /* Clear clr flag */
|
runtime.sim.ext_int_clr &= ~(1 << i); /* Clear clr flag */
|
}
|
}
|
}
|
}
|
}
|
}
|
|
|
/* Update the scheduler queue */
|
/* Update the scheduler queue */
|
scheduler.job_queue->time -= (runtime.sim.cycles - time_start);
|
scheduler.job_queue->time -= (runtime.sim.cycles - time_start);
|
|
|
if (scheduler.job_queue->time <= 0)
|
if (scheduler.job_queue->time <= 0)
|
{
|
{
|
do_scheduler ();
|
do_scheduler ();
|
}
|
}
|
}
|
}
|
|
|
return OR1KSIM_RC_OK;
|
return OR1KSIM_RC_OK;
|
|
|
} /* or1ksim_run() */
|
} /* or1ksim_run () */
|
|
|
|
|
/*---------------------------------------------------------------------------*/
|
/*---------------------------------------------------------------------------*/
|
|
/*!Step the simulator
|
|
|
|
This is just a wrapper for the run function, specifying a time
|
|
corresponding to a single cycle. This will in fact mean that a single
|
|
instruction is executed, even if takes more than one cycle to execute.
|
|
|
|
@todo What happens if an event is triggered - that may mean multiple
|
|
instructions.
|
|
|
|
@return OR1KSIM_RC_OK if we step to completion, OR1KSIM_RC_BRKPT if we hit
|
|
a breakpoint (not clear how this can be set without CLI access) */
|
|
/*---------------------------------------------------------------------------*/
|
|
int
|
|
or1ksim_step ()
|
|
{
|
|
return or1ksim_run ((double) config.sim.clkcycle_ps / 1e12);
|
|
|
|
} /* or1ksim_step () */
|
|
|
|
|
|
/*---------------------------------------------------------------------------*/
|
/*!Reset the run-time simulation end point
|
/*!Reset the run-time simulation end point
|
|
|
Reset the time for which the simulation should run to the specified duration
|
Reset the time for which the simulation should run to the specified duration
|
from NOW (i.e. NOT from when the run started).
|
from NOW (i.e. NOT from when the run started).
|
|
|
@param[in] duration Time to run for in seconds */
|
@param[in] duration Time to run for in seconds */
|
/*---------------------------------------------------------------------------*/
|
/*---------------------------------------------------------------------------*/
|
void
|
void
|
or1ksim_reset_duration (double duration)
|
or1ksim_reset_duration (double duration)
|
{
|
{
|
runtime.sim.end_cycles =
|
runtime.sim.end_cycles =
|
runtime.sim.cycles +
|
runtime.sim.cycles +
|
(long long int) (duration * 1.0e12 / (double) config.sim.clkcycle_ps);
|
(long long int) (duration * 1.0e12 / (double) config.sim.clkcycle_ps);
|
|
|
} /* or1ksim_reset_duration() */
|
} /* or1ksim_reset_duration () */
|
|
|
|
|
/*---------------------------------------------------------------------------*/
|
/*---------------------------------------------------------------------------*/
|
/*!Return time executed so far
|
/*!Return time executed so far
|
|
|
Internal utility to return the time executed so far. Note that this is a
|
Internal utility to return the time executed so far. Note that this is a
|
re-entrant routine.
|
re-entrant routine.
|
|
|
@return Time executed so far in seconds */
|
@return Time executed so far in seconds */
|
/*---------------------------------------------------------------------------*/
|
/*---------------------------------------------------------------------------*/
|
static double
|
static double
|
internal_or1ksim_time ()
|
internal_or1ksim_time ()
|
{
|
{
|
return (double) runtime.sim.cycles * (double) config.sim.clkcycle_ps /
|
return (double) runtime.sim.cycles * (double) config.sim.clkcycle_ps /
|
1.0e12;
|
1.0e12;
|
|
|
} // or1ksim_cycle_count()
|
} // or1ksim_cycle_count()
|
|
|
|
|
/*---------------------------------------------------------------------------*/
|
/*---------------------------------------------------------------------------*/
|
/*!Mark a time point in the simulation
|
/*!Mark a time point in the simulation
|
|
|
Sets the internal parameter recording this point in the simulation */
|
Sets the internal parameter recording this point in the simulation */
|
/*---------------------------------------------------------------------------*/
|
/*---------------------------------------------------------------------------*/
|
void
|
void
|
or1ksim_set_time_point ()
|
or1ksim_set_time_point ()
|
{
|
{
|
runtime.sim.time_point = internal_or1ksim_time ();
|
runtime.sim.time_point = internal_or1ksim_time ();
|
|
|
} /* or1ksim_set_time_point() */
|
} /* or1ksim_set_time_point () */
|
|
|
|
|
/*---------------------------------------------------------------------------*/
|
/*---------------------------------------------------------------------------*/
|
/*!Return the time since the time point was set
|
/*!Return the time since the time point was set
|
|
|
Get the value from the internal parameter */
|
Get the value from the internal parameter */
|
/*---------------------------------------------------------------------------*/
|
/*---------------------------------------------------------------------------*/
|
double
|
double
|
or1ksim_get_time_period ()
|
or1ksim_get_time_period ()
|
{
|
{
|
return internal_or1ksim_time () - runtime.sim.time_point;
|
return internal_or1ksim_time () - runtime.sim.time_point;
|
|
|
} /* or1ksim_get_time_period() */
|
} /* or1ksim_get_time_period () */
|
|
|
|
|
/*---------------------------------------------------------------------------*/
|
/*---------------------------------------------------------------------------*/
|
/*!Return the endianism of the model
|
/*!Return the endianism of the model
|
|
|
Note that this is a re-entrant routine.
|
Note that this is a re-entrant routine.
|
|
|
@return 1 if the model is little endian, 0 otherwise. */
|
@return 1 if the model is little endian, 0 otherwise. */
|
/*---------------------------------------------------------------------------*/
|
/*---------------------------------------------------------------------------*/
|
int
|
int
|
or1ksim_is_le ()
|
or1ksim_is_le ()
|
{
|
{
|
#ifdef OR32_BIG_ENDIAN
|
#ifdef OR32_BIG_ENDIAN
|
return 0;
|
return 0;
|
#else
|
#else
|
return 1;
|
return 1;
|
#endif
|
#endif
|
|
|
} /* or1ksim_is_le() */
|
} /* or1ksim_is_le () */
|
|
|
|
|
/*---------------------------------------------------------------------------*/
|
/*---------------------------------------------------------------------------*/
|
/*!Return the clock rate
|
/*!Return the clock rate
|
|
|
Value is part of the configuration
|
Value is part of the configuration
|
|
|
@return Clock rate in Hz. */
|
@return Clock rate in Hz. */
|
/*---------------------------------------------------------------------------*/
|
/*---------------------------------------------------------------------------*/
|
unsigned long int
|
unsigned long int
|
or1ksim_clock_rate ()
|
or1ksim_clock_rate ()
|
{
|
{
|
return (unsigned long int) (1000000000000ULL /
|
return (unsigned long int) (1000000000000ULL /
|
(unsigned long long int) (config.sim.
|
(unsigned long long int) (config.sim.
|
clkcycle_ps));
|
clkcycle_ps));
|
} /* or1ksim_clock_rate() */
|
} /* or1ksim_clock_rate () */
|
|
|
|
|
/*---------------------------------------------------------------------------*/
|
/*---------------------------------------------------------------------------*/
|
/*!Trigger an edge triggered interrupt
|
/*!Trigger an edge triggered interrupt
|
|
|
This function is appropriate for edge triggered interrupts, which are taken
|
This function is appropriate for edge triggered interrupts, which are taken
|
and then immediately cleared.
|
and then immediately cleared.
|
|
|
@note There is no check that the specified interrupt number is reasonable
|
@note There is no check that the specified interrupt number is reasonable
|
(i.e. <= 31).
|
(i.e. <= 31).
|
|
|
@param[in] i The interrupt number */
|
@param[in] i The interrupt number */
|
/*---------------------------------------------------------------------------*/
|
/*---------------------------------------------------------------------------*/
|
void
|
void
|
or1ksim_interrupt (int i)
|
or1ksim_interrupt (int i)
|
{
|
{
|
if (!config.pic.edge_trigger)
|
if (!config.pic.edge_trigger)
|
{
|
{
|
fprintf (stderr, "Warning: or1ksim_interrupt should not be used for "
|
fprintf (stderr, "Warning: or1ksim_interrupt should not be used for "
|
"level triggered interrupts. Ignored\n");
|
"level triggered interrupts. Ignored\n");
|
}
|
}
|
else
|
else
|
{
|
{
|
runtime.sim.ext_int_set |= 1 << i; // Better not be > 31!
|
runtime.sim.ext_int_set |= 1 << i; // Better not be > 31!
|
runtime.sim.ext_int_clr |= 1 << i; // Better not be > 31!
|
runtime.sim.ext_int_clr |= 1 << i; // Better not be > 31!
|
}
|
}
|
} /* or1ksim_interrupt() */
|
} /* or1ksim_interrupt () */
|
|
|
|
|
/*---------------------------------------------------------------------------*/
|
/*---------------------------------------------------------------------------*/
|
/*!Set a level triggered interrupt
|
/*!Set a level triggered interrupt
|
|
|
This function is appropriate for level triggered interrupts, which must be
|
This function is appropriate for level triggered interrupts, which must be
|
explicitly cleared in a separate call.
|
explicitly cleared in a separate call.
|
|
|
@note There is no check that the specified interrupt number is reasonable
|
@note There is no check that the specified interrupt number is reasonable
|
(i.e. <= 31).
|
(i.e. <= 31).
|
|
|
@param[in] i The interrupt number to set */
|
@param[in] i The interrupt number to set */
|
/*---------------------------------------------------------------------------*/
|
/*---------------------------------------------------------------------------*/
|
void
|
void
|
or1ksim_interrupt_set (int i)
|
or1ksim_interrupt_set (int i)
|
{
|
{
|
if (config.pic.edge_trigger)
|
if (config.pic.edge_trigger)
|
{
|
{
|
fprintf (stderr, "Warning: or1ksim_interrupt_set should not be used for "
|
fprintf (stderr, "Warning: or1ksim_interrupt_set should not be used for "
|
"edge triggered interrupts. Ignored\n");
|
"edge triggered interrupts. Ignored\n");
|
}
|
}
|
else
|
else
|
{
|
{
|
runtime.sim.ext_int_set |= 1 << i; // Better not be > 31!
|
runtime.sim.ext_int_set |= 1 << i; // Better not be > 31!
|
}
|
}
|
} /* or1ksim_interrupt() */
|
} /* or1ksim_interrupt () */
|
|
|
|
|
/*---------------------------------------------------------------------------*/
|
/*---------------------------------------------------------------------------*/
|
/*!Clear a level triggered interrupt
|
/*!Clear a level triggered interrupt
|
|
|
This function is appropriate for level triggered interrupts, which must be
|
This function is appropriate for level triggered interrupts, which must be
|
explicitly set first in a separate call.
|
explicitly set first in a separate call.
|
|
|
@note There is no check that the specified interrupt number is reasonable
|
@note There is no check that the specified interrupt number is reasonable
|
(i.e. <= 31).
|
(i.e. <= 31).
|
|
|
@param[in] i The interrupt number to clear */
|
@param[in] i The interrupt number to clear */
|
/*---------------------------------------------------------------------------*/
|
/*---------------------------------------------------------------------------*/
|
void
|
void
|
or1ksim_interrupt_clear (int i)
|
or1ksim_interrupt_clear (int i)
|
{
|
{
|
if (config.pic.edge_trigger)
|
if (config.pic.edge_trigger)
|
{
|
{
|
fprintf (stderr, "Warning: or1ksim_interrupt_clear should not be used "
|
fprintf (stderr, "Warning: or1ksim_interrupt_clear should not be used "
|
"for edge triggered interrupts. Ignored\n");
|
"for edge triggered interrupts. Ignored\n");
|
}
|
}
|
else
|
else
|
{
|
{
|
runtime.sim.ext_int_clr |= 1 << i; // Better not be > 31!
|
runtime.sim.ext_int_clr |= 1 << i; // Better not be > 31!
|
}
|
}
|
} /* or1ksim_interrupt() */
|
} /* or1ksim_interrupt () */
|
|
|
|
|
/*---------------------------------------------------------------------------*/
|
/*---------------------------------------------------------------------------*/
|
/*!Reset the JTAG interface
|
/*!Reset the JTAG interface
|
|
|
@note Like all the JTAG interface functions, this must not be called
|
@note Like all the JTAG interface functions, this must not be called
|
re-entrantly while a call to any other function (e.g. or1kim_run ())
|
re-entrantly while a call to any other function (e.g. or1kim_run ())
|
is in progress. It is the responsibility of the caller to ensure this
|
is in progress. It is the responsibility of the caller to ensure this
|
constraint is met, for example by use of a SystemC mutex.
|
constraint is met, for example by use of a SystemC mutex.
|
|
|
@return The time in seconds which the reset took. */
|
@return The time in seconds which the reset took. */
|
/*---------------------------------------------------------------------------*/
|
/*---------------------------------------------------------------------------*/
|
double
|
double
|
or1ksim_jtag_reset ()
|
or1ksim_jtag_reset ()
|
{
|
{
|
/* Number of JTAG clock cycles a reset sequence takes */
|
/* Number of JTAG clock cycles a reset sequence takes */
|
const double JTAG_RESET_CYCLES = 5.0;
|
const double JTAG_RESET_CYCLES = 5.0;
|
|
|
jtag_reset ();
|
jtag_reset ();
|
|
|
return JTAG_RESET_CYCLES * (double) config.debug.jtagcycle_ps / 1.0e12;
|
return JTAG_RESET_CYCLES * (double) config.debug.jtagcycle_ps / 1.0e12;
|
|
|
} /* or1ksim_jtag_reset () */
|
} /* or1ksim_jtag_reset () */
|
|
|
|
|
/*---------------------------------------------------------------------------*/
|
/*---------------------------------------------------------------------------*/
|
/*!Shift a JTAG instruction register
|
/*!Shift a JTAG instruction register
|
|
|
@note Like all the JTAG interface functions, this must not be called
|
@note Like all the JTAG interface functions, this must not be called
|
re-entrantly while a call to any other function (e.g. or1kim_run ())
|
re-entrantly while a call to any other function (e.g. or1kim_run ())
|
is in progress. It is the responsibility of the caller to ensure this
|
is in progress. It is the responsibility of the caller to ensure this
|
constraint is met, for example by use of a SystemC mutex.
|
constraint is met, for example by use of a SystemC mutex.
|
|
|
The register is represented as a vector of bytes, with the byte at offset
|
The register is represented as a vector of bytes, with the byte at offset
|
zero being shifted first, and the least significant bit in each byte being
|
zero being shifted first, and the least significant bit in each byte being
|
shifted first. Where the register will not fit in an exact number of bytes,
|
shifted first. Where the register will not fit in an exact number of bytes,
|
the odd bits are in the highest numbered byte, shifted to the low end.
|
the odd bits are in the highest numbered byte, shifted to the low end.
|
|
|
The only JTAG instruction for which we have any significant behavior in
|
The only JTAG instruction for which we have any significant behavior in
|
this model is DEBUG. For completeness the register is parsed and a warning
|
this model is DEBUG. For completeness the register is parsed and a warning
|
given if any register other than DEBUG is shifted.
|
given if any register other than DEBUG is shifted.
|
|
|
@param[in,out] jreg The register to shift in, and the register shifted
|
@param[in,out] jreg The register to shift in, and the register shifted
|
back out.
|
back out.
|
@param[in] num_bits The number of bits in the register. Just for
|
@param[in] num_bits The number of bits in the register. Just for
|
sanity check (it should always be 4).
|
sanity check (it should always be 4).
|
|
|
@return The time in seconds which the shift took. */
|
@return The time in seconds which the shift took. */
|
/*---------------------------------------------------------------------------*/
|
/*---------------------------------------------------------------------------*/
|
double
|
double
|
or1ksim_jtag_shift_ir (unsigned char *jreg,
|
or1ksim_jtag_shift_ir (unsigned char *jreg,
|
int num_bits)
|
int num_bits)
|
{
|
{
|
jtag_shift_ir (jreg, num_bits);
|
jtag_shift_ir (jreg, num_bits);
|
|
|
return (double) num_bits * (double) config.debug.jtagcycle_ps / 1.0e12;
|
return (double) num_bits * (double) config.debug.jtagcycle_ps / 1.0e12;
|
|
|
} /* or1ksim_jtag_shift_ir () */
|
} /* or1ksim_jtag_shift_ir () */
|
|
|
|
|
/*---------------------------------------------------------------------------*/
|
/*---------------------------------------------------------------------------*/
|
/*!Shift a JTAG data register
|
/*!Shift a JTAG data register
|
|
|
@note Like all the JTAG interface functions, this must not be called
|
@note Like all the JTAG interface functions, this must not be called
|
re-entrantly while a call to any other function (e.g. or1kim_run ())
|
re-entrantly while a call to any other function (e.g. or1kim_run ())
|
is in progress. It is the responsibility of the caller to ensure this
|
is in progress. It is the responsibility of the caller to ensure this
|
constraint is met, for example by use of a SystemC mutex.
|
constraint is met, for example by use of a SystemC mutex.
|
|
|
The register is represented as a vector of bytes, with the byte at offset
|
The register is represented as a vector of bytes, with the byte at offset
|
zero being shifted first, and the least significant bit in each byte being
|
zero being shifted first, and the least significant bit in each byte being
|
shifted first. Where the register will not fit in an exact number of bytes,
|
shifted first. Where the register will not fit in an exact number of bytes,
|
the odd bits are in the highest numbered byte, shifted to the low end.
|
the odd bits are in the highest numbered byte, shifted to the low end.
|
|
|
The register is parsed to determine which of the six possible register
|
The register is parsed to determine which of the six possible register
|
types it could be.
|
types it could be.
|
- MODULE_SELECT
|
- MODULE_SELECT
|
- WRITE_COMMNAND
|
- WRITE_COMMNAND
|
- READ_COMMAND
|
- READ_COMMAND
|
- GO_COMMAND
|
- GO_COMMAND
|
- WRITE_CONTROL
|
- WRITE_CONTROL
|
- READ_CONTROL
|
- READ_CONTROL
|
|
|
@note In practice READ_COMMAND is not used. However the functionality is
|
@note In practice READ_COMMAND is not used. However the functionality is
|
provided for future compatibility.
|
provided for future compatibility.
|
|
|
@param[in,out] jreg The register to shift in, and the register shifted
|
@param[in,out] jreg The register to shift in, and the register shifted
|
back out.
|
back out.
|
@param[in] num_bits The number of bits in the register. This is
|
@param[in] num_bits The number of bits in the register. This is
|
essential to prevent bugs where the size of
|
essential to prevent bugs where the size of
|
register supplied is incorrect.
|
register supplied is incorrect.
|
|
|
@return The time in seconds which the shift took. */
|
@return The time in seconds which the shift took. */
|
/*---------------------------------------------------------------------------*/
|
/*---------------------------------------------------------------------------*/
|
double
|
double
|
or1ksim_jtag_shift_dr (unsigned char *jreg,
|
or1ksim_jtag_shift_dr (unsigned char *jreg,
|
int num_bits)
|
int num_bits)
|
{
|
{
|
jtag_shift_dr (jreg, num_bits);
|
jtag_shift_dr (jreg, num_bits);
|
|
|
return (double) num_bits * (double) config.debug.jtagcycle_ps / 1.0e12;
|
return (double) num_bits * (double) config.debug.jtagcycle_ps / 1.0e12;
|
|
|
} /* or1ksim_jtag_shift_dr () */
|
} /* or1ksim_jtag_shift_dr () */
|
|
|
No newline at end of file
|
No newline at end of file
|
|
|
|
/*---------------------------------------------------------------------------*/
|
|
/*!Read a block of memory.
|
|
|
|
@param[out] buf Where to put the data.
|
|
@param[in] addr The address to read from.
|
|
@param[in] len The number of bytes to read.
|
|
|
|
@return Number of bytes read, or zero if error. */
|
|
/*---------------------------------------------------------------------------*/
|
|
int
|
|
or1ksim_read_mem (unsigned char *buf,
|
|
unsigned int addr,
|
|
int len)
|
|
{
|
|
int off; /* Offset into the memory */
|
|
|
|
/* Fill the buffer with data */
|
|
for (off = 0; off < len; off++)
|
|
{
|
|
/* Check memory area is valid */
|
|
if (NULL == verify_memoryarea (addr + off))
|
|
{
|
|
/* Fail silently - others can raise any error message. */
|
|
return 0;
|
|
}
|
|
else
|
|
{
|
|
/* Get the memory direct - no translation. */
|
|
buf[off] = eval_direct8 (addr + off, 0, 0);
|
|
}
|
|
}
|
|
|
|
return len;
|
|
|
|
} /* or1ksim_read_mem () */
|
|
|
|
|
|
/*---------------------------------------------------------------------------*/
|
|
/*!Write a block of memory.
|
|
|
|
@param[in] buf Where to get the data from.
|
|
@param[in] addr The address to write to.
|
|
@param[in] len The number of bytes to write.
|
|
|
|
@return Number of bytes written, or zero if error. */
|
|
/*---------------------------------------------------------------------------*/
|
|
int
|
|
or1ksim_write_mem (unsigned char *buf,
|
|
unsigned int addr,
|
|
int len)
|
|
{
|
|
int off; /* Offset into the memory */
|
|
|
|
/* Write the bytes to memory */
|
|
for (off = 0; off < len; off++)
|
|
{
|
|
if (NULL == verify_memoryarea (addr + off))
|
|
{
|
|
/* Fail silently - others can raise any error message. */
|
|
return 0;
|
|
}
|
|
else
|
|
{
|
|
/* circumvent the read-only check usually done for mem accesses data
|
|
is in host order, because that's what set_direct32 needs */
|
|
set_program8 (addr + off, buf[off]);
|
|
}
|
|
}
|
|
|
|
return len;
|
|
|
|
} /* or1ksim_write_mem () */
|
|
|
|
|
|
/*---------------------------------------------------------------------------*/
|
|
/*!Read a single register
|
|
|
|
The registers follow the GDB sequence for OR1K: GPR0 through GPR31, PC
|
|
(i.e. SPR NPC) and SR (i.e. SPR SR).
|
|
|
|
@param[out] buf Where to put the data.
|
|
@param[in] regnum The register to read.
|
|
@param[in] len Size of the register in bytes
|
|
|
|
@return Size of the register, or zero if error. */
|
|
/*---------------------------------------------------------------------------*/
|
|
int
|
|
or1ksim_read_reg (unsigned char *buf,
|
|
int regnum,
|
|
int len)
|
|
{
|
|
unsigned long int *regbuf = (unsigned long *) buf;
|
|
|
|
if (4 != len)
|
|
{
|
|
return 0; /* Not 32-bit reg */
|
|
}
|
|
|
|
/* Get the relevant register */
|
|
if (regnum < MAX_GPRS)
|
|
{
|
|
*regbuf = cpu_state.reg[regnum];
|
|
}
|
|
else if (PPC_REGNUM == regnum)
|
|
{
|
|
*regbuf = cpu_state.sprs[SPR_PPC];
|
|
}
|
|
else if (NPC_REGNUM == regnum)
|
|
{
|
|
*regbuf = cpu_state.pc;
|
|
}
|
|
else if (SR_REGNUM == regnum)
|
|
{
|
|
*regbuf = cpu_state.sprs[SPR_SR];
|
|
}
|
|
else
|
|
{
|
|
/* Silent error response if we don't know the register */
|
|
return 0;
|
|
}
|
|
|
|
return len;
|
|
|
|
} /* or1ksim_read_reg () */
|
|
|
|
|
|
/*---------------------------------------------------------------------------*/
|
|
/*!Write a single register
|
|
|
|
The registers follow the GDB sequence for OR1K: GPR0 through GPR31, PC
|
|
(i.e. SPR NPC) and SR (i.e. SPR SR). The register is specified as a
|
|
sequence of bytes in target endian order.
|
|
|
|
Each byte is packed as a pair of hex digits.
|
|
|
|
@param[in] buf Where to get the data from.
|
|
@param[in] regnum The register to write.
|
|
@param[in] len Size of the register in bytes
|
|
|
|
@return Size of the register, or zero if error. */
|
|
/*---------------------------------------------------------------------------*/
|
|
int
|
|
or1ksim_write_reg (unsigned char *buf,
|
|
int regnum,
|
|
int len)
|
|
{
|
|
unsigned long int *regbuf = (unsigned long *) buf;
|
|
unsigned long int regval = *regbuf;
|
|
|
|
if (4 != len)
|
|
{
|
|
return 0; /* Not 32-bit reg */
|
|
}
|
|
|
|
/* Set the relevant register */
|
|
if (regnum < MAX_GPRS)
|
|
{
|
|
cpu_state.reg[regnum] =regval;
|
|
}
|
|
else if (PPC_REGNUM == regnum)
|
|
{
|
|
cpu_state.sprs[SPR_PPC] = regval;
|
|
}
|
|
else if (NPC_REGNUM == regnum)
|
|
{
|
|
if (cpu_state.pc != regval)
|
|
{
|
|
cpu_state.pc = regval;
|
|
cpu_state.delay_insn = 0;
|
|
pcnext = regval + 4;
|
|
}
|
|
}
|
|
else if (SR_REGNUM == regnum)
|
|
{
|
|
cpu_state.sprs[SPR_SR] = regval;
|
|
}
|
|
else
|
|
{
|
|
/* Silent error response if we don't know the register */
|
|
return 0;
|
|
}
|
|
|
|
return len;
|
|
|
|
} /* or1ksim_write_reg () */
|
|
|
|
|
|
|
No newline at end of file
|
No newline at end of file
|