/**************************************************************************
 * psim.c - Pipelined Y86-64 simulator
 * 
 * Copyright (c) 2010, 2015. Bryant and D. O'Hallaron, All rights reserved.
 * May not be used, modified, or copied without permission.
 **************************************************************************/ 

#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <unistd.h>
#include <string.h>

#include "isa.h"
#include "pipeline.h"
#include "stages.h"
#include "sim.h"

#define MAXBUF 1024
#define DEFAULTNAME "Y86-64 Simulator: "

#ifdef HAS_GUI
#include <tk.h>
#endif /* HAS_GUI */

#define MAXARGS 128
#define MAXBUF 1024
#define TKARGS 3


/***************
 * Begin Globals
 ***************/

/* Simulator name defined and initialized by the compiled HCL file */
/* according to the -n argument supplied to hcl2c */
extern  char simname[];

/* Parameters modifed by the command line */
int gui_mode = FALSE;    /* Run in GUI mode instead of TTY mode? (-g) */
char *object_filename;   /* The input object file name. */
FILE *object_file;       /* Input file handle */
bool_t verbosity = 2;    /* Verbosity level [TTY only] (-v) */ 
word_t instr_limit = 10000; /* Instruction limit [TTY only] (-l) */
bool_t do_check = FALSE; /* Test with ISA simulator? [TTY only] (-t) */

/************* 
 * End Globals 
 *************/


/***************************
 * Begin function prototypes 
 ***************************/

word_t sim_run_pipe(word_t max_instr, word_t max_cycle, byte_t *statusp, cc_t *ccp);
static void usage(char *name);           /* Print helpful usage message */
static void run_tty_sim();               /* Run simulator in TTY mode */

#ifdef HAS_GUI
void addAppCommands(Tcl_Interp *interp); /* Add application-dependent commands */
#endif /* HAS_GUI */

/*************************
 * End function prototypes
 *************************/


/*******************************************************************
 * Part 1: This part is the initial entry point that handles general
 * initialization. It parses the command line and does any necessary
 * setup to run in either TTY or GUI mode, and then starts the
 * simulation.
 *******************************************************************/

/* 
 * sim_main - main simulator routine. This function is called from the
 * main() routine in the HCL file.
 */
int sim_main(int argc, char **argv)
{
    int i;
    int c;
    char *myargv[MAXARGS];
    
    /* Parse the command line arguments */
    while ((c = getopt(argc, argv, "htgl:v:")) != -1) {
	switch(c) {
	case 'h':
	    usage(argv[0]);
	    break;
	case 'l':
	    instr_limit = atoll(optarg);
	    break;
	case 'v':
	    verbosity = atoi(optarg);
	    if (verbosity < 0 || verbosity > 2) {
		printf("Invalid verbosity %d\n", verbosity);
		usage(argv[0]);
	    }
	    break;
	case 't':
	    do_check = TRUE;
	    break;
	case 'g':
	    gui_mode = TRUE;
	    break;
	default:
	    printf("Invalid option '%c'\n", c);
	    usage(argv[0]);
	    break;
	}
    }


    /* Do we have too many arguments? */
    if (optind < argc - 1) {
	printf("Too many command line arguments:");
	for (i = optind; i < argc; i++)
	    printf(" %s", argv[i]);
	printf("\n");
	usage(argv[0]);
    }


    /* The single unflagged argument should be the object file name */
    object_filename = NULL;
    object_file = NULL;
    if (optind < argc) {
	object_filename = argv[optind];
	object_file = fopen(object_filename, "r");
	if (!object_file) {
	    fprintf(stderr, "Couldn't open object file %s\n", object_filename);
	    exit(1);
	}
    }


    /* Run the simulator in GUI mode (-g flag) */
    if (gui_mode) {

#ifndef HAS_GUI
	printf("To run in GUI mode, you must recompile with the HAS_GUI constant defined.\n");
	exit(1);
#endif /* HAS_GUI */

	/* In GUI mode, we must specify the object file on command line */ 
	if (!object_file) {
	    printf("Missing object file argument in GUI mode\n");
	    usage(argv[0]);
	}

	/* Build the command line for the GUI simulator */
	for (i = 0; i < TKARGS; i++) {
	    if ((myargv[i] = malloc(MAXBUF*sizeof(char))) == NULL) {
		perror("malloc error");
		exit(1);
	    }
	}
	strcpy(myargv[0], argv[0]);
	strcpy(myargv[1], "pipe.tcl");
	strcpy(myargv[2], object_filename);
	myargv[3] = NULL;

	/* Start the GUI simulator */
#ifdef HAS_GUI
	Tk_Main(TKARGS, myargv, Tcl_AppInit);
#endif /* HAS_GUI */
	exit(0);
    }

    /* Otherwise, run the simulator in TTY mode (no -g flag) */
    run_tty_sim();

    exit(0);
}

/* 
 * run_tty_sim - Run the simulator in TTY mode
 */
static void run_tty_sim() 
{
    word_t icount = 0;
    byte_t run_status = STAT_AOK;
    cc_t result_cc = 0;
    word_t byte_cnt = 0;
    mem_t mem0, reg0;
    state_ptr isa_state = NULL;


    /* In TTY mode, the default object file comes from stdin */
    if (!object_file) {
	object_file = stdin;
    }

    if (verbosity >= 2)
	sim_set_dumpfile(stdout);
    sim_init();

    /* Emit simulator name */
    if (verbosity >= 2)
	printf("%s\n", simname);

    byte_cnt = load_mem(mem, object_file, 1);
    if (byte_cnt == 0) {
	fprintf(stderr, "No lines of code found\n");
	exit(1);
    } else if (verbosity >= 2) {
	printf("%lld bytes of code read\n", byte_cnt);
    }
    fclose(object_file);
    if (do_check) {
	isa_state = new_state(0);
	free_mem(isa_state->r);
	free_mem(isa_state->m);
	isa_state->m = copy_mem(mem);
	isa_state->r = copy_mem(reg);
	isa_state->cc = cc;
    }

    mem0 = copy_mem(mem);
    reg0 = copy_mem(reg);
    
    icount = sim_run_pipe(instr_limit, 5*instr_limit, &run_status, &result_cc);
    if (verbosity > 0) {
	printf("%lld instructions executed\n", icount);
	printf("Status = %s\n", stat_name(run_status));
	printf("Condition Codes: %s\n", cc_name(result_cc));
	printf("Changed Register State:\n");
	diff_reg(reg0, reg, stdout);
	printf("Changed Memory State:\n");
	diff_mem(mem0, mem, stdout);
    }
    if (do_check) {
	byte_t e = STAT_AOK;
	word_t step;
	bool_t match = TRUE;

	for (step = 0; step < instr_limit && e == STAT_AOK; step++) {
	    e = step_state(isa_state, stdout);
	}

	if (diff_reg(isa_state->r, reg, NULL)) {
	    match = FALSE;
	    if (verbosity > 0) {
		printf("ISA Register != Pipeline Register File\n");
		diff_reg(isa_state->r, reg, stdout);
	    }
	}
	if (diff_mem(isa_state->m, mem, NULL)) {
	    match = FALSE;
	    if (verbosity > 0) {
		printf("ISA Memory != Pipeline Memory\n");
		diff_mem(isa_state->m, mem, stdout);
	    }
	}
	if (isa_state->cc != result_cc) {
	    match = FALSE;
	    if (verbosity > 0) {
		printf("ISA Cond. Codes (%s) != Pipeline Cond. Codes (%s)\n",
		       cc_name(isa_state->cc), cc_name(result_cc));
	    }
	}
	if (match) {
	    printf("ISA Check Succeeds\n");
	} else {
	    printf("ISA Check Fails\n");
	}
    }

    /* Emit CPI statistics */
    {
	double cpi = instructions > 0 ? (double) cycles/instructions : 1.0;
	printf("CPI: %lld cycles/%lld instructions = %.2f\n",
	       cycles, instructions, cpi);
    }

}

/*
 * usage - print helpful diagnostic information
 */
static void usage(char *name)
{
    printf("Usage: %s [-htg] [-l m] [-v n] file.yo\n", name);
    printf("file.yo arg required in GUI mode, optional in TTY mode (default stdin)\n");
    printf("   -h     Print this message\n");
    printf("   -g     Run in GUI mode instead of TTY mode (default TTY)\n");  
    printf("   -l m   Set instruction limit to m [TTY mode only] (default %lld)\n", instr_limit);
    printf("   -v n   Set verbosity level to 0 <= n <= 2 [TTY mode only] (default %d)\n", verbosity);
    printf("   -t     Test result against ISA simulator [TTY mode only]\n");
    exit(0);
}


/*********************************************************
 * Part 2: This part contains the core simulator routines.
 *********************************************************/


/*****************
 *  Part 2 Globals
 *****************/

/* Performance monitoring */
/* How many cycles have been simulated? */
word_t cycles = 0;
/* How many instructions have passed through the WB stage? */
word_t instructions = 0;

/* Has simulator gotten past initial bubbles? */
static int starting_up = 1;



/* Both instruction and data memory */
mem_t mem;
word_t minAddr = 0;
word_t memCnt = 0;

/* Register file */
mem_t reg;
/* Condition code register */
cc_t cc;
/* Status code */
stat_t status;


/* Pending updates to state */
word_t cc_in = DEFAULT_CC;
word_t wb_destE = REG_NONE;
word_t wb_valE = 0;
word_t wb_destM = REG_NONE;
word_t wb_valM = 0;
word_t mem_addr = 0;
word_t mem_data = 0;
bool_t mem_write = FALSE;

/* EX Operand sources */
mux_source_t amux = MUX_NONE;
mux_source_t bmux = MUX_NONE;

/* Current and next states of all pipeline registers */
pc_ptr pc_curr;
if_id_ptr if_id_curr;
id_ex_ptr id_ex_curr;
ex_mem_ptr ex_mem_curr;
mem_wb_ptr mem_wb_curr;

pc_ptr pc_next;
if_id_ptr if_id_next;
id_ex_ptr id_ex_next;
ex_mem_ptr ex_mem_next;
mem_wb_ptr mem_wb_next;

/* Intermediate values */
word_t f_pc;
byte_t imem_icode;
byte_t imem_ifun;
bool_t imem_error;
bool_t instr_valid;
word_t d_regvala;
word_t d_regvalb;
word_t e_vala;
word_t e_valb;
bool_t e_bcond;
bool_t dmem_error;

/* The pipeline state */
pipe_ptr pc_state, if_id_state, id_ex_state, ex_mem_state, mem_wb_state;

/* Simulator operating mode */
sim_mode_t sim_mode = S_FORWARD;
/* Log file */
FILE *dumpfile = NULL;

/*****************************************************************************
 * reporting code
 *****************************************************************************/

#ifdef HAS_GUI
/* used for formatting instructions */
static char status_msg[128];

static char *format_pc(pc_ptr state)
{
    char pstring[17];
    wstring(state->pc, 4, 64, pstring);
    sprintf(status_msg, "%s %s", stat_name(state->status), pstring);
    return status_msg;
}

static char *format_if_id(if_id_ptr state)
{
    char valcstring[17];
    char valpstring[17];
    wstring(state->valc, 4, 64, valcstring);
    wstring(state->valp, 4, 64, valpstring);
    sprintf(status_msg, "%s %s %s %s %s %s",
	    stat_name(state->status),
	    iname(HPACK(state->icode,state->ifun)),
	    reg_name(state->ra),
	    reg_name(state->rb),
	    valcstring,
	    valpstring);
    return status_msg;
}

static char *format_id_ex(id_ex_ptr state)
{
    char valcstring[17];
    char valastring[17];
    char valbstring[17];
    wstring(state->valc, 4, 64, valcstring);
    wstring(state->vala, 4, 64, valastring);
    wstring(state->valb, 4, 64, valbstring);
    sprintf(status_msg, "%s %s %s %s %s %s %s %s %s",
	    stat_name(state->status),
	    iname(HPACK(state->icode, state->ifun)),
	    valcstring,
	    valastring,
	    valbstring,
	    reg_name(state->deste),
	    reg_name(state->destm),
	    reg_name(state->srca),
	    reg_name(state->srcb));
    return status_msg;
}

static char *format_ex_mem(ex_mem_ptr state)
{
    char valestring[17];
    char valastring[17];
    wstring(state->vale, 4, 64, valestring);
    wstring(state->vala, 4, 64, valastring);
    sprintf(status_msg, "%s %s %c %s %s %s %s",
	    stat_name(state->status),
	    iname(HPACK(state->icode, state->ifun)),
	    state->takebranch ? 'Y' : 'N',
	    valestring,
	    valastring,
	    reg_name(state->deste),
	    reg_name(state->destm));

    return status_msg;
}

static char *format_mem_wb(mem_wb_ptr state)
{
    char valestring[17];
    char valmstring[17];
    wstring(state->vale, 4, 64, valestring);
    wstring(state->valm, 4, 64, valmstring);
    sprintf(status_msg, "%s %s %s %s %s %s",
	    stat_name(state->status),
	    iname(HPACK(state->icode, state->ifun)),
	    valestring,
	    valmstring,
	    reg_name(state->deste),
	    reg_name(state->destm));

    return status_msg;
}
#endif /* HAS_GUI */

/* Report system state */
static void sim_report() 
{

#ifdef HAS_GUI
    if (gui_mode) {
	report_pc(f_pc, pc_curr->status != STAT_BUB,
		  if_id_curr->stage_pc, if_id_curr->status != STAT_BUB,
		  id_ex_curr->stage_pc, id_ex_curr->status != STAT_BUB,
		  ex_mem_curr->stage_pc, ex_mem_curr->status != STAT_BUB,
		  mem_wb_curr->stage_pc, mem_wb_curr->status != STAT_BUB);
	report_state("F", 0, format_pc(pc_next));
	report_state("F", 1, format_pc(pc_curr));
	report_state("D", 0, format_if_id(if_id_next));
	report_state("D", 1, format_if_id(if_id_curr));
	report_state("E", 0, format_id_ex(id_ex_next));
	report_state("E", 1, format_id_ex(id_ex_curr));
	report_state("M", 0, format_ex_mem(ex_mem_next));
	report_state("M", 1, format_ex_mem(ex_mem_curr));
	report_state("W", 0, format_mem_wb(mem_wb_next));
	report_state("W", 1, format_mem_wb(mem_wb_curr));
	/* signal_sources(); */
	show_cc(cc);
	show_stat(status);
	show_cpi();
    }
#endif

}

/*****************************************************************************
 * pipeline control
 * These functions can be used to handle hazards
 *****************************************************************************/

/* bubble stage (has effect at next update) */
void sim_bubble_stage(stage_id_t stage) 
{
    switch (stage)
	{
	case IF_STAGE : pc_state->op     = P_BUBBLE; break;
	case ID_STAGE : if_id_state->op  = P_BUBBLE; break;
	case EX_STAGE : id_ex_state->op  = P_BUBBLE; break;
	case MEM_STAGE: ex_mem_state->op = P_BUBBLE; break;
	case WB_STAGE : mem_wb_state->op = P_BUBBLE; break;
	}
}

/* stall stage (has effect at next update) */
void sim_stall_stage(stage_id_t stage) {
    switch (stage)
	{
	case IF_STAGE : pc_state->op     = P_STALL; break;
	case ID_STAGE : if_id_state->op  = P_STALL; break;
	case EX_STAGE : id_ex_state->op  = P_STALL; break;
	case MEM_STAGE: ex_mem_state->op = P_STALL; break;
	case WB_STAGE : mem_wb_state->op = P_STALL; break;
	}
}


static int initialized = 0;

void sim_init()
{
    /* Create memory and register files */
    initialized = 1;
    mem = init_mem(MEM_SIZE);
    reg = init_reg();
    
    /* create 5 pipe registers */
    pc_state     = new_pipe(sizeof(pc_ele), (void *) &bubble_pc);
    if_id_state  = new_pipe(sizeof(if_id_ele), (void *) &bubble_if_id);
    id_ex_state  = new_pipe(sizeof(id_ex_ele), (void *) &bubble_id_ex);
    ex_mem_state = new_pipe(sizeof(ex_mem_ele), (void *) &bubble_ex_mem);
    mem_wb_state = new_pipe(sizeof(mem_wb_ele), (void *) &bubble_mem_wb);
  
    /* connect them to the pipeline stages */
    pc_next   = pc_state->next;
    pc_curr   = pc_state->current;
  
    if_id_next = if_id_state->next;
    if_id_curr = if_id_state->current;

    id_ex_next = id_ex_state->next;
    id_ex_curr = id_ex_state->current;

    ex_mem_next = ex_mem_state->next;
    ex_mem_curr = ex_mem_state->current;

    mem_wb_next = mem_wb_state->next;
    mem_wb_curr = mem_wb_state->current;

    sim_reset();
    clear_mem(mem);
}

void sim_reset()
{
    if (!initialized)
	sim_init();
    clear_pipes();
    clear_mem(reg);
    minAddr = 0;
    memCnt = 0;
    starting_up = 1;
    cycles = instructions = 0;
    cc = DEFAULT_CC;
    status = STAT_AOK;

#ifdef HAS_GUI
    if (gui_mode) {
	signal_register_clear();
	create_memory_display();
    }
#endif

    amux = bmux = MUX_NONE;
    cc = cc_in = DEFAULT_CC;
    wb_destE = REG_NONE;
    wb_valE = 0;
    wb_destM = REG_NONE;
    wb_valM = 0;
    mem_addr = 0;
    mem_data = 0;
    mem_write = FALSE;
    sim_report();
}

/* Update state elements */
/* May need to disable updating of memory & condition codes */
static void update_state(bool_t update_mem, bool_t update_cc)
{
    /* Writeback(s):
       If either register is REG_NONE, write will have no effect .
       Order of two writes determines semantics of
       popl %rsp.  According to ISA, %rsp will get popped value
    */

    if (wb_destE != REG_NONE) {
	sim_log("\tWriteback: Wrote 0x%llx to register %s\n",
		wb_valE, reg_name(wb_destE));
	set_reg_val(reg, wb_destE, wb_valE);
    }
    if (wb_destM != REG_NONE) {
	sim_log("\tWriteback: Wrote 0x%llx to register %s\n",
		wb_valM, reg_name(wb_destM));
	set_reg_val(reg, wb_destM, wb_valM);
    }

    /* Memory write */
    if (mem_write && !update_mem) {
	sim_log("\tDisabled write of 0x%llx to address 0x%llx\n", mem_data, mem_addr);
    }
    if (update_mem && mem_write) {
	if (!set_word_val(mem, mem_addr, mem_data)) {
	    sim_log("\tCouldn't write to address 0x%llx\n", mem_addr);
	} else {
	    sim_log("\tWrote 0x%llx to address 0x%llx\n", mem_data, mem_addr);

#ifdef HAS_GUI
	    if (gui_mode) {
		if (mem_addr % 8 != 0) {
		    /* Just did a misaligned write.
		       Need to display both words */
		    word_t align_addr = mem_addr & ~0x3;
		    word_t val;
		    get_word_val(mem, align_addr, &val);
		    set_memory(align_addr, val);
		    align_addr+=8;
		    get_word_val(mem, align_addr, &val);
		    set_memory(align_addr, val);
		} else {
		    set_memory(mem_addr, mem_data);
		}
	    }
#endif

	}
    }
    if (update_cc)
	cc = cc_in;
}

/* Text representation of status */
void tty_report(word_t cyc) {
  sim_log("\nCycle %lld. CC=%s, Stat=%s\n", cyc, cc_name(cc), stat_name(status));

  sim_log("F: predPC = 0x%llx\n", pc_curr->pc);

  sim_log("D: instr = %s, rA = %s, rB = %s, valC = 0x%llx, valP = 0x%llx, Stat = %s\n",
	  iname(HPACK(if_id_curr->icode, if_id_curr->ifun)),
	  reg_name(if_id_curr->ra), reg_name(if_id_curr->rb),
	  if_id_curr->valc, if_id_curr->valp,
	  stat_name(if_id_curr->status));

  sim_log("E: instr = %s, valC = 0x%llx, valA = 0x%llx, valB = 0x%llx\n   srcA = %s, srcB = %s, dstE = %s, dstM = %s, Stat = %s\n",
	  iname(HPACK(id_ex_curr->icode, id_ex_curr->ifun)),
	  id_ex_curr->valc, id_ex_curr->vala, id_ex_curr->valb,
	  reg_name(id_ex_curr->srca), reg_name(id_ex_curr->srcb),
	  reg_name(id_ex_curr->deste), reg_name(id_ex_curr->destm),
	  stat_name(id_ex_curr->status));

  sim_log("M: instr = %s, Cnd = %d, valE = 0x%llx, valA = 0x%llx\n   dstE = %s, dstM = %s, Stat = %s\n",
	  iname(HPACK(ex_mem_curr->icode, ex_mem_curr->ifun)),
	  ex_mem_curr->takebranch,
	  ex_mem_curr->vale, ex_mem_curr->vala,
	  reg_name(ex_mem_curr->deste), reg_name(ex_mem_curr->destm),
	  stat_name(ex_mem_curr->status));

  sim_log("W: instr = %s, valE = 0x%llx, valM = 0x%llx, dstE = %s, dstM = %s, Stat = %s\n",
	  iname(HPACK(mem_wb_curr->icode, mem_wb_curr->ifun)),
	  mem_wb_curr->vale, mem_wb_curr->valm,
	  reg_name(mem_wb_curr->deste), reg_name(mem_wb_curr->destm),
	  stat_name(mem_wb_curr->status));
}

/* Run pipeline for one cycle */
/* Return status of processor */
/* Max_instr indicates maximum number of instructions that
   want to complete during this simulation run.  */
static byte_t sim_step_pipe(word_t max_instr, word_t ccount)
{
    byte_t wb_status = mem_wb_curr->status;
    byte_t mem_status = mem_wb_next->status;
    /* How many instructions are ahead of one in wb / ex? */
    int ahead_mem = (wb_status != STAT_BUB);
    int ahead_ex = ahead_mem + (mem_status != STAT_BUB);
    bool_t update_mem = ahead_mem < max_instr;
    bool_t update_cc = ahead_ex < max_instr;

    /* Update program-visible state */
    update_state(update_mem, update_cc);
    /* Update pipe registers */
    update_pipes();
    tty_report(ccount);
    if (pc_state->op == P_ERROR)
	pc_curr->status = STAT_PIP;
    if (if_id_state->op == P_ERROR)
	if_id_curr->status = STAT_PIP;
    if (id_ex_state->op == P_ERROR)
	id_ex_curr->status = STAT_PIP;
    if (ex_mem_state->op == P_ERROR)
	ex_mem_curr->status = STAT_PIP;
    if (mem_wb_state->op == P_ERROR)
	mem_wb_curr->status = STAT_PIP;
    
    /* Need to do decode after execute & memory stages,
       and memory stage before execute, in order to propagate
       forwarding values properly */
    do_if_stage();
    do_mem_stage();
    do_ex_stage();
    do_id_wb_stages();

    do_stall_check();
#if 0
    /* This doesn't seem necessary */
    if (id_ex_curr->status != STAT_AOK
	&& id_ex_curr->status != STAT_BUB) {
	if_id_state->op = P_BUBBLE;
	id_ex_state->op = P_BUBBLE;
    }
#endif

    /* Performance monitoring */
    if (mem_wb_curr->status != STAT_BUB && mem_wb_curr->icode != I_POP2) {
	starting_up = 0;
	instructions++;
	cycles++;
    } else {
	if (!starting_up)
	    cycles++;
    }
    
    sim_report();
    return status;
}

/*
  Run pipeline until one of following occurs:
  - An error status is encountered in WB.
  - max_instr instructions have completed through WB
  - max_cycle cycles have been simulated

  Return number of instructions executed.
  if statusp nonnull, then will be set to status of final instruction
  if ccp nonnull, then will be set to condition codes of final instruction
*/
word_t sim_run_pipe(word_t max_instr, word_t max_cycle, byte_t *statusp, cc_t *ccp)
{
    word_t icount = 0;
    word_t ccount = 0;
    byte_t run_status = STAT_AOK;
    while (icount < max_instr && ccount < max_cycle) {
        run_status = sim_step_pipe(max_instr-icount, ccount);
	if (run_status != STAT_BUB)
	    icount++;
	if (run_status != STAT_AOK && run_status != STAT_BUB)
	    break;
	ccount++;
    }
    if (statusp)
	*statusp = run_status;
    if (ccp)
	*ccp = cc;
    return icount;
}

/* If dumpfile set nonNULL, lots of status info printed out */
void sim_set_dumpfile(FILE *df)
{
    dumpfile = df;
}

/*
 * sim_log dumps a formatted string to the dumpfile, if it exists
 * accepts variable argument list
 */
void sim_log( const char *format, ... ) {
    if (dumpfile) {
	va_list arg;
	va_start( arg, format );
	vfprintf( dumpfile, format, arg );
	va_end( arg );
    }
}


/*************************************************************
 * Part 3: This part contains support for the GUI simulator
 *************************************************************/

#ifdef HAS_GUI

/**********************
 * Begin Part 3 globals	
 **********************/

/* Hack for SunOS */
#ifdef __sun
extern int matherr();
int *tclDummyMathPtr = (int *) matherr;
#endif

static char tcl_msg[256];

/* Keep track of the TCL Interpreter */
static Tcl_Interp *sim_interp = NULL;

static mem_t post_load_mem;

static void set_tcl_result(Tcl_Interp *interp, const char *msg)
{
    Tcl_SetResult(interp, (char *) msg, TCL_VOLATILE);
}

/**********************
 * End Part 3 globals	
 **********************/


/******************************************************************************
 *	function declarations
 ******************************************************************************/

int simResetCmd(ClientData clientData, Tcl_Interp *interp,
		int argc, char *argv[]);

int simLoadCodeCmd(ClientData clientData, Tcl_Interp *interp,
		   int argc, char *argv[]);

int simLoadDataCmd(ClientData clientData, Tcl_Interp *interp,
		   int argc, char *argv[]);

int simRunCmd(ClientData clientData, Tcl_Interp *interp,
	      int argc, char *argv[]);

int simModeCmd(ClientData clientData, Tcl_Interp *interp,
	       int argc, char *argv[]);

void addAppCommands(Tcl_Interp *interp);


/******************************************************************************
 *	tcl command definitions
 ******************************************************************************/

/* Implement command versions of the simulation functions */
int simResetCmd(ClientData clientData, Tcl_Interp *interp,
		int argc, char *argv[])
{
    sim_interp = interp;
    if (argc != 1) {
	set_tcl_result(interp, "No arguments allowed");
	return TCL_ERROR;
    }
    sim_reset();
    if (post_load_mem) {
	free_mem(mem);
	mem = copy_mem(post_load_mem);
    }
    set_tcl_result(interp, stat_name(STAT_AOK));
    return TCL_OK;
}

int simLoadCodeCmd(ClientData clientData, Tcl_Interp *interp,
		   int argc, char *argv[])
{
    FILE *code_file;
    word_t code_count;
    sim_interp = interp;
    if (argc != 2) {
	set_tcl_result(interp, "One argument required");
	return TCL_ERROR;
    }
    code_file = fopen(argv[1], "r");
    if (!code_file) {
	sprintf(tcl_msg, "Couldn't open code file '%s'", argv[1]);
	set_tcl_result(interp, tcl_msg);
	return TCL_ERROR;
    }
    sim_reset();
    code_count = load_mem(mem, code_file, 0);
    post_load_mem = copy_mem(mem);
    sprintf(tcl_msg, "%lld", code_count);
    set_tcl_result(interp, tcl_msg);
    fclose(code_file);
    return TCL_OK;
}

int simLoadDataCmd(ClientData clientData, Tcl_Interp *interp,
		   int argc, char *argv[])
{
    FILE *data_file;
    word_t word_count = 0;
    set_tcl_result(interp, "Not implemented");
    return TCL_ERROR;


    sim_interp = interp;
    if (argc != 2) {
	set_tcl_result(interp, "One argument required");
	return TCL_ERROR;
    }
    data_file = fopen(argv[1], "r");
    if (!data_file) {
	sprintf(tcl_msg, "Couldn't open data file '%s'", argv[1]);
	set_tcl_result(interp, tcl_msg);
	return TCL_ERROR;
    }
    sprintf(tcl_msg, "%lld", word_count);
    set_tcl_result(interp, tcl_msg);
    fclose(data_file);
    return TCL_OK;
}


int simRunCmd(ClientData clientData, Tcl_Interp *interp,
	      int argc, char *argv[])
{
    word_t cycle_limit = 1;
    byte_t status;
    cc_t cc;
    sim_interp = interp;
    if (argc > 2) {
	set_tcl_result(interp, "At most one argument allowed");
	return TCL_ERROR;
    }
    if (argc >= 2 &&
	(sscanf(argv[1], "%lld", &cycle_limit) != 1 ||
	 cycle_limit < 0)) {
	sprintf(tcl_msg, "Cannot run for '%s' cycles!", argv[1]);
	set_tcl_result(interp, tcl_msg);
	return TCL_ERROR;
    }
    sim_run_pipe(cycle_limit + 5, cycle_limit, &status, &cc);
    set_tcl_result(interp, stat_name(status));
    return TCL_OK;
}

int simModeCmd(ClientData clientData, Tcl_Interp *interp,
	       int argc, char *argv[])
{
    sim_interp = interp;
    if (argc != 2) {
	set_tcl_result(interp, "One argument required");
	return TCL_ERROR;
    }
    set_tcl_result(interp, argv[1]);
    if (strcmp(argv[1], "wedged") == 0)
	sim_mode = S_WEDGED;
    else if (strcmp(argv[1], "stall") == 0)
	sim_mode = S_STALL;
    else if (strcmp(argv[1], "forward") == 0)
	sim_mode = S_FORWARD;
    else {
	sprintf(tcl_msg, "Unknown mode '%s'", argv[1]);
	set_tcl_result(interp, tcl_msg);
	return TCL_ERROR;
    }
    return TCL_OK;
}


/******************************************************************************
 *	registering the commands with tcl
 ******************************************************************************/

void addAppCommands(Tcl_Interp *interp)
{
    sim_interp = interp;
    Tcl_CreateCommand(interp, "simReset", (Tcl_CmdProc *) simResetCmd,
		      (ClientData) NULL, (Tcl_CmdDeleteProc *) NULL);
    Tcl_CreateCommand(interp, "simCode", (Tcl_CmdProc *) simLoadCodeCmd,
		      (ClientData) NULL, (Tcl_CmdDeleteProc *) NULL);
    Tcl_CreateCommand(interp, "simData", (Tcl_CmdProc *) simLoadDataCmd,
		      (ClientData) NULL, (Tcl_CmdDeleteProc *) NULL);
    Tcl_CreateCommand(interp, "simRun", (Tcl_CmdProc *) simRunCmd,
		      (ClientData) NULL, (Tcl_CmdDeleteProc *) NULL);
    Tcl_CreateCommand(interp, "setSimMode", (Tcl_CmdProc *) simModeCmd,
		      (ClientData) NULL, (Tcl_CmdDeleteProc *) NULL);
} 

/******************************************************************************
 *	tcl functionality called from within C
 ******************************************************************************/

/* Provide mechanism for simulator to update register display */
void signal_register_update(reg_id_t r, word_t val) {
    int code;
    sprintf(tcl_msg, "setReg %d %lld 1", (int) r, (word_t) val);
    code = Tcl_Eval(sim_interp, tcl_msg);
    if (code != TCL_OK) {
	fprintf(stderr, "Failed to signal register set\n");
	fprintf(stderr, "Error Message was '%s'\n", Tcl_GetStringResult(sim_interp));
    }
}

/* Provide mechanism for simulator to generate memory display */
void create_memory_display() {
    int code;
    sprintf(tcl_msg, "createMem %lld %lld", minAddr, memCnt);
    code = Tcl_Eval(sim_interp, tcl_msg);
    if (code != TCL_OK) {
	fprintf(stderr, "Command '%s' failed\n", tcl_msg);
	fprintf(stderr, "Error Message was '%s'\n", Tcl_GetStringResult(sim_interp));
    } else {
	word_t i;
	for (i = 0; i < memCnt && code == TCL_OK; i+=8) {
	    word_t addr = minAddr+i;
	    word_t val;
	    if (!get_word_val(mem, addr, &val)) {
		fprintf(stderr, "Out of bounds memory display\n");
		return;
	    }
	    sprintf(tcl_msg, "setMem %lld %lld", addr, val);
	    code = Tcl_Eval(sim_interp, tcl_msg);
	}
	if (code != TCL_OK) {
	    fprintf(stderr, "Couldn't set memory value\n");
	    fprintf(stderr, "Error Message was '%s'\n", Tcl_GetStringResult(sim_interp));
	}
    }
}

/* Provide mechanism for simulator to update memory value */
void set_memory(word_t addr, word_t val) {
    int code;
    word_t nminAddr = minAddr;
    word_t nmemCnt = memCnt;

    /* First see if we need to expand memory range */
    if (memCnt == 0) {
	nminAddr = addr;
	nmemCnt = 8;
    } else if (addr < minAddr) {
	nminAddr = addr;
	nmemCnt = minAddr + memCnt - addr;
    } else if (addr >= minAddr+memCnt) {
	nmemCnt = addr-minAddr+8;
    }
    /* Now make sure nminAddr & nmemCnt are multiples of 16 */
    nmemCnt = ((nminAddr & 0xF) + nmemCnt + 0xF) & ~0xF;
    nminAddr = nminAddr & ~0xF;

    if (nminAddr != minAddr || nmemCnt != memCnt) {
	minAddr = nminAddr;
	memCnt = nmemCnt;
	create_memory_display();
    } else {
	sprintf(tcl_msg, "setMem %lld %lld", addr, val);
	code = Tcl_Eval(sim_interp, tcl_msg);
	if (code != TCL_OK) {
	    fprintf(stderr, "Couldn't set memory value 0x%llx to 0x%llx\n",
		    addr, val);
	    fprintf(stderr, "Error Message was '%s'\n", Tcl_GetStringResult(sim_interp));
	}
    }
}

/* Provide mechanism for simulator to update condition code display */
void show_cc(cc_t cc)
{
    int code;
    sprintf(tcl_msg, "setCC %d %d %d",
	    GET_ZF(cc), GET_SF(cc), GET_OF(cc));
    code = Tcl_Eval(sim_interp, tcl_msg);
    if (code != TCL_OK) {
	fprintf(stderr, "Failed to display condition codes\n");
	fprintf(stderr, "Error Message was '%s'\n", Tcl_GetStringResult(sim_interp));
    }
}

/* Provide mechanism for simulator to update status display */
void show_stat(stat_t stat)
{
    int code;
    sprintf(tcl_msg, "showStat %s", stat_name(stat));
    code = Tcl_Eval(sim_interp, tcl_msg);
    if (code != TCL_OK) {
	fprintf(stderr, "Failed to display status\n");
	fprintf(stderr, "Error Message was '%s'\n", Tcl_GetStringResult(sim_interp));
    }
}



/* Provide mechanism for simulator to update performance information */
void show_cpi() {
    int code;
    double cpi = instructions > 0 ? (double) cycles/instructions : 1.0;
    sprintf(tcl_msg, "showCPI %lld %lld %.2f",
	    cycles, instructions, (double) cpi);
    code = Tcl_Eval(sim_interp, tcl_msg);
    if (code != TCL_OK) {
	fprintf(stderr, "Failed to display CPI\n");
	fprintf(stderr, "Error Message was '%s'\n", Tcl_GetStringResult(sim_interp));
    }
}

char *rname[] = {"none", "ea", "eb", "me", "wm", "we"};

/* provide mechanism for simulator to specify source registers */
void signal_sources() {
    int code;
    sprintf(tcl_msg, "showSources %s %s",
	    rname[amux], rname[bmux]);
    code = Tcl_Eval(sim_interp, tcl_msg);
    if (code != TCL_OK) {
	fprintf(stderr, "Failed to signal forwarding sources\n");
	fprintf(stderr, "Error Message was '%s'\n", Tcl_GetStringResult(sim_interp));
    }
}

/* Provide mechanism for simulator to clear register display */
void signal_register_clear() {
    int code;
    code = Tcl_Eval(sim_interp, "clearReg");
    if (code != TCL_OK) {
	fprintf(stderr, "Failed to signal register clear\n");
	fprintf(stderr, "Error Message was '%s'\n", Tcl_GetStringResult(sim_interp));
    }
}

/* Provide mechanism for simulator to report instructions as they are 
   read in
*/

void report_line(word_t line_no, word_t addr, char *hex, char *text) {
    int code;
    sprintf(tcl_msg, "addCodeLine %lld %lld {%s} {%s}", line_no, addr, hex, text);
    code = Tcl_Eval(sim_interp, tcl_msg);
    if (code != TCL_OK) {
	fprintf(stderr, "Failed to report code line 0x%llx\n", addr);
	fprintf(stderr, "Error Message was '%s'\n", Tcl_GetStringResult(sim_interp));
    }
}


/* Provide mechanism for simulator to report which instructions are in
   which stages */
void report_pc(unsigned fpc, unsigned char fpcv,
	       unsigned dpc, unsigned char dpcv,
	       unsigned epc, unsigned char epcv,
	       unsigned mpc, unsigned char mpcv,
	       unsigned wpc, unsigned char wpcv)
{
    int status;
    char addr[10];
    char code[12];
    Tcl_DString cmd;
    Tcl_DStringInit(&cmd);
    Tcl_DStringAppend(&cmd, "simLabel ", -1);
    Tcl_DStringStartSublist(&cmd);
    if (fpcv) {
	sprintf(addr, "%u", fpc);
	Tcl_DStringAppendElement(&cmd, addr);
    }
    if (dpcv) {
	sprintf(addr, "%u", dpc);
	Tcl_DStringAppendElement(&cmd, addr);
    }
    if (epcv) {
	sprintf(addr, "%u", epc);
	Tcl_DStringAppendElement(&cmd, addr);
    }
    if (mpcv) {
	sprintf(addr, "%u", mpc);
	Tcl_DStringAppendElement(&cmd, addr);
    }
    if (wpcv) {
	sprintf(addr, "%u", wpc);
	Tcl_DStringAppendElement(&cmd, addr);
    }
    Tcl_DStringEndSublist(&cmd);
    Tcl_DStringStartSublist(&cmd);
    sprintf(code, "%s %s %s %s %s",
	    fpcv ? "F" : "",
	    dpcv ? "D" : "",
	    epcv ? "E" : "",
	    mpcv ? "M" : "",
	    wpcv ? "W" : "");
    Tcl_DStringAppend(&cmd, code, -1);
    Tcl_DStringEndSublist(&cmd);
    /* Debug 
       fprintf(stderr, "Code '%s'\n", Tcl_DStringValue(&cmd));
    */
    status = Tcl_Eval(sim_interp, Tcl_DStringValue(&cmd));
    if (status != TCL_OK) {
	fprintf(stderr, "Failed to report pipe code '%s'\n", code);
	fprintf(stderr, "Error Message was '%s'\n", Tcl_GetStringResult(sim_interp));
    }
}

/* Report single line of pipeline state */
void report_state(char *id, word_t current, char *txt)
{
    int status;
    sprintf(tcl_msg, "updateStage %s %lld {%s}", id, current,txt);
    status = Tcl_Eval(sim_interp, tcl_msg);
    if (status != TCL_OK) {
	fprintf(stderr, "Failed to report pipe status\n");
	fprintf(stderr, "\tStage %s.%s, status '%s'\n",
		id, current ? "current" : "next", txt);
	fprintf(stderr, "\tError Message was '%s'\n", Tcl_GetStringResult(sim_interp));
    }
}


/*
 * Tcl_AppInit - Called by TCL to perform application-specific initialization.
 */
int Tcl_AppInit(Tcl_Interp *interp)
{
    /* Tell TCL about the name of the simulator so it can  */
    /* use it as the title of the main window */
    Tcl_SetVar(interp, "simname", simname, TCL_GLOBAL_ONLY);

    if (Tcl_Init(interp) == TCL_ERROR)
	return TCL_ERROR;
    if (Tk_Init(interp) == TCL_ERROR)
	return TCL_ERROR;
    Tcl_StaticPackage(interp, "Tk", Tk_Init, Tk_SafeInit);

    /* Call procedure to add new commands */
    addAppCommands(interp);

    /*
     * Specify a user-specific startup file to invoke if the application
     * is run interactively.  Typically the startup file is "~/.apprc"
     * where "app" is the name of the application.  If this line is deleted
     * then no user-specific startup file will be run under any conditions.
     */
    Tcl_SetVar(interp, "tcl_rcFileName", "~/.wishrc", TCL_GLOBAL_ONLY);
    return TCL_OK;

}

#endif /* HAS_GUI */


/**************************************************************
 * Part 4: Code for implementing pipelined processor simulators
 *************************************************************/

/******************************************************************************
 *	defines
 ******************************************************************************/

#define MAX_STAGE 10

/******************************************************************************
 *	static variables
 ******************************************************************************/

static pipe_ptr pipes[MAX_STAGE];
static int pipe_count = 0;

/******************************************************************************
 *	function definitions
 ******************************************************************************/

/* Create new pipe with count bytes of state */
/* bubble_val indicates state corresponding to pipeline bubble */
pipe_ptr new_pipe(int count, void *bubble_val)
{
  pipe_ptr result = (pipe_ptr) malloc(sizeof(pipe_ele));
  result->current = malloc(count);
  result->next = malloc(count);
  memcpy(result->current, bubble_val, count);
  memcpy(result->next, bubble_val, count);
  result->count = count;
  result->op = P_LOAD;
  result->bubble_val = bubble_val;
  pipes[pipe_count++] = result; 
  return result;
}

/* Update all pipes */
void update_pipes()
{
  int s;
  for (s = 0; s < pipe_count; s++) {
    pipe_ptr p = pipes[s];
    switch (p->op)
      {
      case P_BUBBLE:
      	/* insert a bubble into the next stage */
      	memcpy(p->current, p->bubble_val, p->count);
      	break;
      
      case P_LOAD:
      	/* copy calculated state from previous stage */
      	memcpy(p->current, p->next, p->count);
      	break;
      case P_ERROR:
	  /* Like a bubble, but insert error condition */
      	memcpy(p->current, p->bubble_val, p->count);
      	break;
      case P_STALL:
      default:
      	/* do nothing: next stage gets same instr again */
      	;
      }
    if (p->op != P_ERROR)
	p->op = P_LOAD;
  }
}

/* Set all pipes to bubble values */
void clear_pipes()
{
  int s;
  for (s = 0; s < pipe_count; s++) {
    pipe_ptr p = pipes[s];
    memcpy(p->current, p->bubble_val, p->count);
    memcpy(p->next, p->bubble_val, p->count);
    p->op = P_LOAD;
  }
}

/******************** Utility Code *************************/

/* Representations of digits */
static char digits[16] =
   {'0', '1', '2', '3', '4', '5', '6', '7',
    '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'};

/* Print hex/oct/binary format with leading zeros */
/* bpd denotes bits per digit  Should be in range 1-4,
   pbw denotes bits per word.*/
void wprint(uword_t x, int bpd, int bpw, FILE *fp)
{
  int digit;
  uword_t mask = ((uword_t) 1 << bpd) - 1;
  for (digit = (bpw-1)/bpd; digit >= 0; digit--) {
    uword_t val = (x >> (digit * bpd)) & mask;
    putc(digits[val], fp);
  }
}

/* Create string in hex/oct/binary format with leading zeros */
/* bpd denotes bits per digit  Should be in range 1-4,
   pbw denotes bits per word.*/
void wstring(uword_t x, int bpd, int bpw, char *str)
{
  int digit;
  uword_t mask = ((uword_t) 1 << bpd) - 1;
  for (digit = (bpw-1)/bpd; digit >= 0; digit--) {
    uword_t val = (x >> (digit * bpd)) & mask;
    *str++ = digits[val];
  }
  *str = '\0';
}


/********************************
 * Part 5: Stage implementations
 *********************************/

/*************** Bubbled version of stages *************/

pc_ele bubble_pc = {0,STAT_AOK};
if_id_ele bubble_if_id = { I_NOP, 0, REG_NONE,REG_NONE,
			   0, 0, STAT_BUB, 0};
id_ex_ele bubble_id_ex = { I_NOP, 0, 0, 0, 0,
			   REG_NONE, REG_NONE, REG_NONE, REG_NONE,
			   STAT_BUB, 0};

ex_mem_ele bubble_ex_mem = { I_NOP, 0, FALSE, 0, 0,
			     REG_NONE, REG_NONE, STAT_BUB, 0};

mem_wb_ele bubble_mem_wb = { I_NOP, 0, 0, 0, REG_NONE, REG_NONE,
			     STAT_BUB, 0};

/*************** Stage Implementations *****************/

word_t gen_f_pc();
word_t gen_need_regids();
word_t gen_need_valC();
word_t gen_instr_valid();
word_t gen_f_predPC();
word_t gen_f_icode();
word_t gen_f_ifun();
word_t gen_f_stat();
word_t gen_instr_valid();

void do_if_stage()
{
    byte_t instr = HPACK(I_NOP, F_NONE);
    byte_t regids = HPACK(REG_NONE, REG_NONE);
    word_t valc = 0;
    word_t valp = f_pc = gen_f_pc();

    /* Ready to fetch instruction.  Speculatively fetch register byte
       and immediate word
    */
    imem_error = !get_byte_val(mem, valp, &instr);
    imem_icode = HI4(instr);
    imem_ifun = LO4(instr);
    if (!imem_error) {
      byte_t junk;
      /* Make sure can read maximum length instruction */
      imem_error = !get_byte_val(mem, valp+5, &junk);
    }
    if_id_next->icode = gen_f_icode();
    if_id_next->ifun  = gen_f_ifun();
    if (!imem_error) {
	sim_log("\tFetch: f_pc = 0x%llx, imem_instr = %s, f_instr = %s\n",
		f_pc, iname(instr),
		iname(HPACK(if_id_next->icode, if_id_next->ifun)));
    }

    instr_valid = gen_instr_valid();
    if (!instr_valid) 
      sim_log("\tFetch: Instruction code 0x%llx invalid\n", instr);
    if_id_next->status = gen_f_stat();

    valp++;
    if (gen_need_regids()) {
	get_byte_val(mem, valp, &regids);
	valp ++;
    }
    if_id_next->ra = HI4(regids);
    if_id_next->rb = LO4(regids);
    if (gen_need_valC()) {
	get_word_val(mem, valp, &valc);
	valp+= 8;
    }
    if_id_next->valp = valp;
    if_id_next->valc = valc;

    pc_next->pc = gen_f_predPC();

    pc_next->status = (if_id_next->status == STAT_AOK) ? STAT_AOK : STAT_BUB;

    if_id_next->stage_pc = f_pc;
}

word_t gen_d_srcA();
word_t gen_d_srcB();
word_t gen_d_dstE();
word_t gen_d_dstM();
word_t gen_d_valA();
word_t gen_d_valB();
word_t gen_w_dstE();
word_t gen_w_valE();
word_t gen_w_dstM();
word_t gen_w_valM();
word_t gen_Stat();

/* Implements both ID and WB */
void do_id_wb_stages()
{
    /* Set up write backs.  Don't occur until end of cycle */
    wb_destE = gen_w_dstE();
    wb_valE = gen_w_valE();
    wb_destM = gen_w_dstM();
    wb_valM = gen_w_valM();

    /* Update processor status */
    status = gen_Stat();

    id_ex_next->srca = gen_d_srcA();
    id_ex_next->srcb = gen_d_srcB();
    id_ex_next->deste = gen_d_dstE();
    id_ex_next->destm = gen_d_dstM();

    /* Read the registers */
    d_regvala = get_reg_val(reg, id_ex_next->srca);
    d_regvalb = get_reg_val(reg, id_ex_next->srcb);

    /* Do forwarding and valA selection */
    id_ex_next->vala = gen_d_valA();
    id_ex_next->valb = gen_d_valB();

    id_ex_next->icode = if_id_curr->icode;
    id_ex_next->ifun = if_id_curr->ifun;
    id_ex_next->valc = if_id_curr->valc;
    id_ex_next->stage_pc = if_id_curr->stage_pc;
    id_ex_next->status = if_id_curr->status;
}

word_t gen_alufun();
word_t gen_set_cc();
word_t gen_Bch();
word_t gen_aluA();
word_t gen_aluB();
word_t gen_e_valA();
word_t gen_e_dstE();

void do_ex_stage()
{
    alu_t alufun = gen_alufun();
    bool_t setcc = gen_set_cc();
    word_t alua, alub;

    alua = gen_aluA();
    alub = gen_aluB();

    e_bcond = 	cond_holds(cc, id_ex_curr->ifun);
    
    ex_mem_next->takebranch = e_bcond;

    if (id_ex_curr->icode == I_JMP)
      sim_log("\tExecute: instr = %s, cc = %s, branch %staken\n",
	      iname(HPACK(id_ex_curr->icode, id_ex_curr->ifun)),
	      cc_name(cc),
	      ex_mem_next->takebranch ? "" : "not ");
    
    /* Perform the ALU operation */
    word_t aluout = compute_alu(alufun, alua, alub);
    ex_mem_next->vale = aluout;
    sim_log("\tExecute: ALU: %c 0x%llx 0x%llx --> 0x%llx\n",
	    op_name(alufun), alua, alub, aluout);

    if (setcc) {
	cc_in = compute_cc(alufun, alua, alub);
	sim_log("\tExecute: New cc = %s\n", cc_name(cc_in));
    }

    ex_mem_next->icode = id_ex_curr->icode;
    ex_mem_next->ifun = id_ex_curr->ifun;
    ex_mem_next->vala = gen_e_valA();
    ex_mem_next->deste = gen_e_dstE();
    ex_mem_next->destm = id_ex_curr->destm;
    ex_mem_next->srca = id_ex_curr->srca;
    ex_mem_next->status = id_ex_curr->status;
    ex_mem_next->stage_pc = id_ex_curr->stage_pc;
}

/* Functions defined using HCL */
word_t gen_mem_addr();
word_t gen_mem_read();
word_t gen_mem_write();
word_t gen_m_stat();

void do_mem_stage()
{
    bool_t read = gen_mem_read();

    word_t valm = 0;

    mem_addr = gen_mem_addr();
    mem_data = ex_mem_curr->vala;
    mem_write = gen_mem_write();
    dmem_error = FALSE;

    if (read) {
	dmem_error = dmem_error || !get_word_val(mem, mem_addr, &valm);
	if (!dmem_error)
	  sim_log("\tMemory: Read 0x%llx from 0x%llx\n",
		  valm, mem_addr);
    }
    if (mem_write) {
	word_t sink;
	/* Do a read of address just to check validity */
	dmem_error = dmem_error || !get_word_val(mem, mem_addr, &sink);
	if (dmem_error)
	  sim_log("\tMemory: Invalid address 0x%llx\n",
		  mem_addr);
    }
    mem_wb_next->icode = ex_mem_curr->icode;
    mem_wb_next->ifun = ex_mem_curr->ifun;
    mem_wb_next->vale = ex_mem_curr->vale;
    mem_wb_next->valm = valm;
    mem_wb_next->deste = ex_mem_curr->deste;
    mem_wb_next->destm = ex_mem_curr->destm;
    mem_wb_next->status = gen_m_stat();
    mem_wb_next->stage_pc = ex_mem_curr->stage_pc;
}

/* Set stalling conditions for different stages */

word_t gen_F_stall(), gen_F_bubble();
word_t gen_D_stall(), gen_D_bubble();
word_t gen_E_stall(), gen_E_bubble();
word_t gen_M_stall(), gen_M_bubble();
word_t gen_W_stall(), gen_W_bubble();

p_stat_t pipe_cntl(char *name, word_t stall, word_t bubble)
{
    if (stall) {
	if (bubble) {
	    sim_log("%s: Conflicting control signals for pipe register\n",
		    name);
	    return P_ERROR;
	} else 
	    return P_STALL;
    } else {
	return bubble ? P_BUBBLE : P_LOAD;
    }
}

void do_stall_check()
{
    pc_state->op = pipe_cntl("PC", gen_F_stall(), gen_F_bubble());
    if_id_state->op = pipe_cntl("ID", gen_D_stall(), gen_D_bubble());
    id_ex_state->op = pipe_cntl("EX", gen_E_stall(), gen_E_bubble());
    ex_mem_state->op = pipe_cntl("MEM", gen_M_stall(), gen_M_bubble());
    mem_wb_state->op = pipe_cntl("WB", gen_W_stall(), gen_W_bubble());
}

