Add a -printsimstats switch.

Start working in a "extra" mode for debugging and other features
that we don't want on the main path. As a demonstration of what we
can do with this extra mode, print out a bunch of simulation info
that can help us optimize offline.
This commit is contained in:
Tyler Stachecki 2014-10-27 19:23:34 -04:00
parent 4ec470fed7
commit d45cab877a
6 changed files with 125 additions and 7 deletions

27
cen64.c
View file

@ -17,7 +17,7 @@ static int load_roms(const char *pifrom_path, const char *cart_path,
struct rom_file *pifrom, struct rom_file *cart);
static int setup_and_run_device(struct cen64_device *device,
const char *pifrom_path, const char *cart_path);
const char *pifrom_path, const char *cart_path, bool extra_mode);
// Called when a simulation instance is terminating.
void cen64_cleanup(struct cen64_device *device) {
@ -28,7 +28,8 @@ void cen64_cleanup(struct cen64_device *device) {
// Called when another simulation instance is desired.
int cen64_main(struct cen64_device *device, int argc, const char *argv[]) {
struct gl_window_hints hints;
int status;
bool extra_mode = false;
int status, i;
// Prevent debugging tools from raising warnings
// about uninitialized memory being read, etc.
@ -36,7 +37,11 @@ int cen64_main(struct cen64_device *device, int argc, const char *argv[]) {
get_default_gl_window_hints(&hints);
if (argc < 3) {
printf("%s <pifrom.bin> <rom>\n", argv[0]);
printf("%s <pifrom.bin> <rom>\n\n"
"Options:\n"
" -printsimstats : Print simulation statistics at exit.\n",
argv[0]);
return 255;
}
@ -45,8 +50,13 @@ int cen64_main(struct cen64_device *device, int argc, const char *argv[]) {
return 1;
}
// Parse arguments.
for (i = 1; i < argc - 2; i++)
if (!strcmp(argv[i], "-printsimstats"))
extra_mode = true;
// Start simulation.
if ((status = setup_and_run_device(device, argv[1], argv[2])))
if ((status = setup_and_run_device(device, argv[i], argv[i + 1], extra_mode)))
destroy_gl_window(&device->vi.gl_window);
return status;
@ -79,7 +89,7 @@ int load_roms(const char *pifrom_path, const char *cart_path,
// Create a device, Load ROM images, etc. and run.
int setup_and_run_device(struct cen64_device *device,
const char *pifrom_path, const char *cart_path) {
const char *pifrom_path, const char *cart_path, bool extra_mode) {
struct rom_file pifrom, cart;
int status;
@ -90,8 +100,11 @@ int setup_and_run_device(struct cen64_device *device,
device->cart_size = cart.size;
// Create a device and roceed to the main application loop.
if (!(status = device_create(device) == NULL))
status = device_run(device);
if (!(status = device_create(device) == NULL)) {
status = unlikely(extra_mode)
? device_run_extra(device)
: device_run(device);
}
close_rom_file(&cart);
close_rom_file(&pifrom);

View file

@ -118,3 +118,28 @@ int device_run(struct cen64_device *device) {
return 0;
}
// Kicks off threads and starts the device.
int device_run_extra(struct cen64_device *device) {
unsigned i;
if (setjmp(device->bus.unwind_data)) {
vr4300_print_summary(&device->vr4300_stats);
return 0;
}
while (1) {
for (i = 0; i < 2; i++) {
vi_cycle(&device->vi);
rsp_cycle(&device->rsp);
vr4300_cycle(&device->vr4300);
vr4300_cycle_extra(&device->vr4300, &device->vr4300_stats);
}
vr4300_cycle(&device->vr4300);
vr4300_cycle_extra(&device->vr4300, &device->vr4300_stats);
}
return 0;
}

View file

@ -45,11 +45,16 @@ struct cen64_device {
const uint8_t *pifrom;
const uint8_t *cart;
// Debugging/statistical data.
struct vr4300_stats vr4300_stats;
};
struct cen64_device *device_create(struct cen64_device *device);
void device_destroy(struct cen64_device *device);
int device_run(struct cen64_device *device);
int device_run_extra(struct cen64_device *device);
#endif

View file

@ -48,3 +48,51 @@ int vr4300_init(struct vr4300 *vr4300, struct bus_controller *bus) {
return 0;
}
// Prints out simulation information to stdout.
void vr4300_print_summary(struct vr4300_stats *stats) {
unsigned i, j;
float secs;
float cpi;
// Print banner.
printf("###############################\n"
" NEC VR4300 Simulation Summary\n"
"###############################\n"
"\n"
);
// Print configuration, summary, whatever.
secs = stats->total_cycles / 93750000.0f;
printf(" %16s: %.1f sec.\n"
"\n\n",
"Actual runtime", secs
);
// Print performance statistics.
cpi = (float) stats->executed_instructions / stats->total_cycles;
printf(" * Performance statistics:\n\n"
" %16s: %llu\n"
" %16s: %llu\n"
" %16s: %1.2f\n"
"\n\n",
"Elapsed pcycles", stats->total_cycles,
"Insns executed", stats->executed_instructions,
"Average CPI", cpi
);
// Print executed opcode counts.
printf(" * Executed instruction counts:\n\n");
for (i = 1; i < NUM_VR4300_OPCODES; i += 2) {
for (j = 0; i + j < NUM_VR4300_OPCODES && j < 2; j++)
printf(" %16s: %16llu\t", vr4300_opcode_mnemonics[i + j],
stats->opcode_counts[i + j]);
printf("\n");
}
}

View file

@ -15,6 +15,7 @@
#include "vr4300/cp1.h"
#include "vr4300/dcache.h"
#include "vr4300/icache.h"
#include "vr4300/opcodes.h"
#include "vr4300/pipeline.h"
struct bus_controller;
@ -110,8 +111,18 @@ struct vr4300 {
};
struct vr4300_stats {
unsigned long long executed_instructions;
unsigned long long total_cycles;
unsigned long long opcode_counts[NUM_VR4300_OPCODES];
};
void vr4300_cycle(struct vr4300 *vr4300);
void vr4300_cycle_extra(struct vr4300 *vr4300, struct vr4300_stats *stats);
int vr4300_init(struct vr4300 *vr4300, struct bus_controller *bus);
void vr4300_print_summary(struct vr4300_stats *stats);
#endif

View file

@ -460,6 +460,22 @@ void vr4300_cycle(struct vr4300 *vr4300) {
return;
}
// Collects additional information about the pipeline each cycle.
void vr4300_cycle_extra(struct vr4300 *vr4300, struct vr4300_stats *stats) {
struct vr4300_dcwb_latch *dcwb_latch = &vr4300->pipeline.dcwb_latch;
struct vr4300_rfex_latch *rfex_latch = &vr4300->pipeline.rfex_latch;
// Collect information for CPI.
stats->executed_instructions +=
!dcwb_latch->common.fault &&
!vr4300->pipeline.cycles_to_stall;
stats->total_cycles++;
// Collect information about executed instructions.
stats->opcode_counts[rfex_latch->opcode.id]++;
}
// Initializes the pipeline with default values.
void vr4300_pipeline_init(struct vr4300_pipeline *pipeline) {
pipeline->icrf_latch.segment = get_default_segment();