vu.c 7.6 KB

/**************************************************************************
 *                                                                        *
 *               Copyright (C) 1994, Silicon Graphics, Inc.               *
 *                                                                        *
 *  These coded instructions, statements, and computer programs  contain  *
 *  unpublished  proprietary  information of Silicon Graphics, Inc., and  *
 *  are protected by Federal copyright  law.  They  may not be disclosed  *
 *  to  third  parties  or copied or duplicated in any form, in whole or  *
 *  in part, without the prior written consent of Silicon Graphics, Inc.  *
 *                                                                        *
 *************************************************************************/

/*
 * File:	vu.c
 * Creator:	hsa@sgi.com
 * Create Date:	Tue Feb  8 11:51:11 PST 1994
 *
 * This file is the top of the vector unit for the RSP simulator.
 *
 */

#include <stdio.h>
#include <math.h>	/* for rand() */
#include "rsp.h"
#include "rspctl.h"
#include "memory.h"
#include "opcode.h"
#include "vu.h"
#include "i128.h"

int VUZeroPipe;

/* VU registers: */
i128	rsp_VUR[32];	/* 64-bit VU GPR */
i64	rsp_ACC[8];	/* 48-bit accumulator(s) */
u16	rsp_VCO; 	/* carry in/out */
u16 	rsp_VCC;	/* vector compare code */
u8       rsp_VCE;       /* control used by double precision vch/vcl*/


/* tables for fancy element encoding (see sketch): */
int	cmask_tab[] = 
{ 
    0x07, 			/* vector */
    0x0e, 			/* scalar quarter */
    0x0c, 0x0c, 		/* scalar half */
    0x00, 0x00, 0x00, 0x00	/* scalar whole */
};

int	emask_tab[] = 
{ 
    0x00, 			/* vector */
    0x01, 			/* scalar quarter */
    0x03, 0x03, 		/* scalar half */
    0x07, 0x07, 0x07, 0x07	/* scalar whole */
};


/* pipeline stall machinery */
boolean	rsp_VUStalled;

typedef struct
{
    boolean	busy;
    u32		pc;	/* address of instruction who's busy */
    u32		waiting[rsp_VUPIPEDEPTH+1];
    int		wait_cnt;
} rsp_vuRegState_t;

rsp_vuRegState_t	rsp_vuRegState[32];


/*
 * a per-clock function that looks to see if we can clear
 * a stall condition
 */
static boolean
rsp_VUCheckStalled(void)
{
    if (!rsp_VUIsStalled())
	return TRUE;

    rsp_VUStalled = FALSE;

    if (rsp_VUMultCheckStall() ||
	rsp_VUDivCheckStall()  ||
	rsp_VUAddCheckStall()  ||
	rsp_VULogicalCheckStall()  ||
	rsp_VUSelectCheckStall()  ||
	rsp_VUPackCheckStall() ||
	rsp_VULoadStoreCheckStall()) {
	rsp_VUStalled = TRUE;
    }

    /* check all the datapath pipelines: */
    if (!rsp_VUStalled) {
	rsp_Verbose(stderr,"VU un-stalled.\n");
    }
}

/*
 * schedules execution of a VU computation instruction:
 */
void
rsp_VUExec(u32 inst, u32 pc)
{
    int		opKey;

    opKey = ExtractBits(inst, 5, 3);	/* upper opcode bits */

    switch (opKey) {
      case 0x00:	/* MULT instruction */
      case 0x01:
	rsp_VUMultInstall(inst, pc);
	break;

      case 0x02:	/* ADD instruction */
      case 0x03:
	rsp_VUAddInstall(inst, pc);
	break;
	
      case 0x04:	/* SELECT instruction */
	rsp_VUSelectInstall(inst, pc);
	break;
	
      case 0x05:	/* logical instruction */
	rsp_VULogicalInstall(inst, pc);
	break;
	
      case 0x06:	/* DIV instruction */
	rsp_VUDivInstall(inst, pc);
	break;
	
      case 0x07:	/* PACK instruction */
	rsp_VUPackInstall(inst, pc);
	break;
	
      default:		/* RESERVED */
	rsp_eprintf(stderr,"ERROR : reserved VU instruction.\n");
	break;
    }
}

/*
 * answers query
 */
boolean
rsp_VUIsStalled(void)
{
    return(rsp_VUStalled);
}

/*
 * These routines implement processor stall support by 'lock' and 'unlock' 
 * registers during instruction execution, and provide access to test the 
 * lock status of a register.
 *
 * We must keep track of not only which registers are 'busy', but who
 * is waiting on them. We have to include instructions that are they
 * themselves stalled, and make sure we don't wait on our own registers.
 * The serial nature of the instruction stream is assumed here, that is,
 * unlocks happen in the same order as locks.
 */
void
rsp_VURegLock(int reg, u32 pc)
{
    register rsp_vuRegState_t	*rs = &(rsp_vuRegState[0]);
    int		i;

    if (!rs[reg].busy) {
	rs[reg].busy = TRUE;
	rs[reg].pc = pc;
#ifdef TRACE
	rsp_fprintf(stderr,"inst %08x locking register %d\n", pc, reg);
#endif
    } else {
	if (rs[reg].pc == pc)
	    return;
	for (i=0; i<rs[reg].wait_cnt; i++) {
	    if (rs[reg].waiting[i] == pc)
		return;		/* only insert into queue once */
	}
	rs[reg].waiting[rs[reg].wait_cnt] = pc;
	rs[reg].wait_cnt++;
#ifdef TRACE
	rsp_fprintf(stderr,"inst %08x locking register %d\n", pc, reg);
#endif
    }
}

void
rsp_VURegUnLock(int reg, u32 pc)
{
    register rsp_vuRegState_t	*rs = &(rsp_vuRegState[0]);
    int i;

#ifdef TRACE
    rsp_fprintf(stderr,"inst %08x un-locking register %d\n", pc, reg);

    /*
     * We disable this error message. We declare the simulator to be
     * done debugging; we still see this error occasionly, but if you
     * trace it, you can see it doesn't matter.
     *
     * The problem is when two consecutive instructions lock the same rd, but
     * the first stalls, so the second unlocks rd first.
     *
     * Technically, this is a bug, but since the first instruction's use of rd
     * must be a "don't care", we'll turn off this message.
     *
     * Sun Jun  4 18:30:28 PDT 1995
     */
    if (!rs[reg].busy || rs[reg].pc != pc) {
	rsp_eprintf(stderr,
		"ERROR : VU register locking confused. v%d (%s) %08x != %08x\n",
		reg, ((rs[reg].busy) ? "busy" : "not busy"), rs[reg].pc, pc);
	rsp_controlReg = Flag(rsp_controlReg, rsp_CtlHaltMask);
	rsp_SuSpecialBreak(0x0);
	/* exit(-1); */
    }
#endif

    if (rs[reg].wait_cnt > 0) {
	rs[reg].pc = rs[reg].waiting[0];
	rs[reg].waiting[0] = 0xffffffff;
	for (i=1; i<rs[reg].wait_cnt; i++) {
	    rs[reg].waiting[i-1] = rs[reg].waiting[i];
	    rs[reg].waiting[i] = 0xffffffff;
	}
	rs[reg].wait_cnt--;
    } else {
	rs[reg].busy = FALSE;
	rs[reg].pc = 0xffffffff;
    }
}

boolean
rsp_VURegIsLocked(int reg, u32 pc)
{
    register rsp_vuRegState_t	*rs = &(rsp_vuRegState[0]);

    if (!rs[reg].busy)
	return(FALSE);		/* not busy */

    if (rs[reg].pc != pc) {
	return(TRUE);		/* busy, not me */
    }

    return(FALSE);		/* busy, me */
}

/*
 * init VU. Mainly installs per-clock routines
 */
void
rsp_VuInit(int init_regs)
{
    int		i, j;

    rsp_VUStalled = FALSE;

    for (i=0; i<32; i++) {
	rsp_vuRegState[i].busy = FALSE;
	rsp_vuRegState[i].pc = 0xffffffff;
	rsp_vuRegState[i].wait_cnt = 0;
	if (init_regs) {
	    for (j=0; j<16; j++)
		rsp_VUR[i].b[j] = 0;
	} else {
	    for (j=0; j<16; j++)
		rsp_VUR[i].b[j] = (char) rand();
	}
	for (j=0; j<rsp_VUPIPEDEPTH; j++)
	    rsp_vuRegState[i].waiting[j] = 0xffffffff;
    }

    /* install clock handlers for everything... */
    rsp_ProcessorStepInstall(rsp_VUCheckStalled);
    rsp_ProcessorStepInstall(rsp_VUMovePipeStep);
    rsp_ProcessorStepInstall(rsp_VUMultPipeStep);
    rsp_ProcessorStepInstall(rsp_VUAddPipeStep);
    rsp_ProcessorStepInstall(rsp_VULogicalPipeStep);
    rsp_ProcessorStepInstall(rsp_VUSelectPipeStep);
    rsp_ProcessorStepInstall(rsp_VUDivPipeStep);
    rsp_ProcessorStepInstall(rsp_VUPackPipeStep);
    rsp_ProcessorStepInstall(rsp_VULoadStorePipeStep);
}

/***************************************************************************/

/*
 * Debugging operations.
 */

i128
rsp_VURGet(i16 reg)
{
    i128	z;

    if (reg > 31) {
	rsp_eprintf(stderr,"ERROR : invalid register %d.\n",reg);
	Set128By64(&z, 0x0, 0);
	Set128By64(&z, 0x0, 1);
	return(z);
    }

    return(rsp_VUR[reg]);
}

void
rsp_VURSet(i16 reg, i128 value)
{
    if (reg > 31) {
	rsp_eprintf(stderr,"ERROR : invalid register %d.\n",reg);
    } else {
	Set128(&(rsp_VUR[reg]), &value);
    }
}