embra.h 9.06 KB
/*
 * Copyright (C) 1996-1998 by the Board of Trustees
 *    of Leland Stanford Junior University.
 * 
 * This file is part of the SimOS distribution. 
 * See LICENSE file for terms of the license. 
 *
 */

#ifndef EMBRA_H
#define EMBRA_H

#include <sys/cdefs.h>
/* From embra - should this be shared? */
#include "c_port.h"
/* From the shared directory */
#include "mips_arch.h"
#include "addr_layout.h"
#include "cpu_state.h"
#include "cpu_interface.h"
#include "sim_error.h"
/* From simos */
#include "syslimits.h"

#ifndef _LANGUAGE_ASSEMBLY
#include "simmisc.h" 
#include "machine_params.h"



/* *****************************************************************
 * A form of PC that allows us to pass information as to whether we 
 * are in the delay slot 
 * ****************************************************************/

typedef VA  PC_BD;
#define IN_BD(_pc) ((_pc) & 0x1)
#define CLEAR_BD(_pc) ((_pc) & (~(0x1)))



/* Directory Entry.  Limits to 30 cpus, but ll/sc is word limited */
/* Could expand to 62 cpus with lld MIPS III opcode */
typedef uint Dir_Entry;



/* ****************************************************************
 * Virtual quick check status byte 
 * *****************************************************************/
typedef enum {MEM_D_EXCLUSIVE = 0x80, 
              MEM_I_EXCLUSIVE = 0xc0, 
              MEM_D_SHARED = 0x01, 
              MEM_I_SHARED = 0x41, 
              MEM_INVALID = 0x00} EmVQCMemState;

#define VQC_SHARED(_x)((int)(_x) &   0x00000001)
#define VQC_EXCL(_x)  ((int)(_x) &   0x00000080 )
#define VQC_INST(_x)  ((int)(_x) &   0x00000040 )
#define VQC_DATA(_x)  ((int)(_x) == MEM_D_EXCLUSIVE || (int)(_x) == MEM_D_SHARED)

#endif /* _LANGUAGE_ASSEMBLY */


/* ***********************************************************************
 * Still this BACKDOOR vs KSEG1 confusion. 
 * ***********************************************************************/

#define IS_BACKDOOR(_addr) (IS_KSEG1(_addr))
#if defined(SIM_MIPS32)
#define IS_USER(_pc) (!IS_KSEG0(_pc))
#else
#define IS_USER(_pc) (IS_XKUSEG(_pc))
#endif
/* Zero offset bits, and add a line */
#define ALIGN_IT( _val, _mod ) ( ((unsigned)(_val) & (~((unsigned)_mod-1))) +\
(unsigned)_mod )
#define HOST_SCACHE_LINE_SIZE (128)


/* *******************************************************************
 * Memory Address macros. Should in fact be somewhere else (not
 * embra specific)
 * ******************************************************************/

#define MA_TO_UINT(x)  ((uint)(x))
#define EMBRA_IS_MEMADDR(_m, x) (IS_VALID_MA((_m), (x)))
#define EMBRA_IS_PADDR(_m, x)   (IS_VALID_PA((_m), (x)))



#ifndef _LANGUAGE_ASSEMBLY

/* We may need extra state eventually, but right now, CPUState is sufficient*/
typedef CPUState EmbraState;
extern EmbraState* EMP;  /* allocated in driver.c */

typedef struct EmbraParams { 
   int sequential;          /* not really a param - for now 1 */
   int parallel;            /* not really a param - for now 0 */
   CPUType emode;           /* not really a param - EMBRA_CACHE | EMBRA_PAGE */
  
   int MPinUP;              /* never set for cpuNum==1 - yes */
   
   int useETLB;             /* boolean - yes */
   int useVQC;              /* boolean - yes */
   int stats;               /* boolean - yes */
   int inlineQC;            /* boolean - yes */
   int timeQuantum;         /* 1024 */
   int periodicAnnInterval; /* 32*1024*1024 */
   int statInterval;        /* 32*1024*1024 */
   int miscCheckInterval;   /* 50000 */
   int separateMMUs;
}  EmbraParams;


extern EmbraParams embra;


/* **********************************************************
 * Current EMP and cpu is determined by curEmp, updated
 * on context switches
 * *********************************************************
 */

extern int EmbraCurrentCpuNum(void);
#define CURR_CPU (curEmp->myNum)
extern EmbraState* curEmp;



/* Allocated in driver.c This is NOT shared*/
extern int quick_ASID[SIM_MAXCPUS];
/* Allocated in simos_interface.c - the number of CPUs in the simulation */

/* Here are a whole bunch of defines.  All of the registers, and how */
/* Embra uses them are here. */

#endif /* _LANGUAGE_ASSEMBLY */

/* DEFINE what registers simulator will use */



#ifndef _LANGUAGE_ASSEMBLY
typedef struct RegNoDummy {
   char nothing[1];
} RegNo;
#define REGNUM(_x) ((int)(_x))

#endif

#ifdef _LANGUAGE_ASSEMBLY
/* 
 * this is a work-around around the expansion rules of cpp.
 */

#if  defined(_ABIN32) 
#define REGISTER_NUMBER(_x) $/**/_x
#else
#define REGISTER_NUMBER2(_dollar,_x) _dollar##_x
#define REGISTER_NUMBER(_x) REGISTER_NUMBER2($,_x)
#endif
#else
#define REGISTER_NUMBER(_x) (_x)
#endif



/* *********************************************************************
 * new register map:
 * *********************************************************************

 *  0  zero   :
 *  1  at     : shadow
 *  2  v0     : used to return stuff (tc internal)
 *  3  v1     : shadow
 *  4  a0-a3  : parameter passing (tc internal + callout)
 *  8  t0-t2  : shadow
 *  11 t3-t6  : tc internal
 *  15 t7     : shadow
 *  24 t8-t9  : shadow
 *     s0-s8  : common variables

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


/* *********************************************************************
 * Simulator working registers 
 *  SIM_T1 & SIM_T2 are used by ALU_OPS and L/S generally as
 * rs == SIM_T2, rt == SIM_T1, rd = SIM_T2
 * 
 * BRANCHREG holds either the branch condition or the target address if
 * its a register indirect jump.  Therefore it must be saved on
 * callouts because if we callout in a delay slot, we don't want to
 * lose our branch information
 *
 * XXX bugnion(fix this. should be a callee saved register)
 *
 * SIM_T4 Used for Quick Check and link address in branch and link 
 * instructions
 * 
 ************************************************************************/



#define SIM_T1 REGISTER_NUMBER(REG_T3)
#define SIM_T2 REGISTER_NUMBER(REG_T4)
#define SIM_T3 unused-register
#define SIM_T4 REGISTER_NUMBER(REG_T6)



/* *******************************************************************
 * Simulator permanent registers.
 * These registers are used to cache the most frequently accessed
 * variables from within the translation cache.
 * All registers are callee saved, therefore do not need to 
 * explicitely saved on callouts.
 *
 * VSS_BASE == curEmp == &EMP[CURR_CPU]
 * QC_REG   == curEmp->qc_v, (cache mode only)
 * MMU_REG  == curEmp->mmu: base of relocation array
 * PC_REG   == curEmp->PC (must spill on callouts!)
 * CLOCK_REG== curEmp->XXX
 * MMUMASK_REG == 0x7fffffff (page mode only)
 * 
 * IHIT     == number of instructions executed
 * DHIT     == number of references
 * 
 * *******************************************************************/


/* common variables: reconstruct after callout 
 * Either have these variables callee saved or recompute them
 * after returning from the callout
 */


#define VSS_BASE     REGISTER_NUMBER(REG_S8)
#define QC_REG       REGISTER_NUMBER(REG_T1)
#define PA_REG       QC_REG
#define MMU_REG      REGISTER_NUMBER(REG_T0)
#define MMUMASK_REG  REGISTER_NUMBER(REG_V1)

/* volatile registers: save to EMP before callout. Restore from EMP
 * Note that the clock and ihit must be adjusted. 
 * Ideally, these registers are caller saved. 
 */
#define CLOCK_REG    REGISTER_NUMBER(REG_T7)
#define IHIT_REG     REGISTER_NUMBER(REG_T8)
#define DHIT_REG     REGISTER_NUMBER(REG_T9)


/* internal registers: save to stack if caller saved
 * 
 * As non-intuitive as this might seem, PC_REG and curEmp->PC
 * MUST BE KEPT incoherent, the first walue corresponds
 * to the start ot the BB while the second one is updated on
 * callouts !!!!
 * Note that there is an incentive of having them callee saved.
 * If we support mid-translation context switches, we must 
 * save the BRANCHREG and the PC_REG in the saveArea. 
 */
#define BRANCHREG    REGISTER_NUMBER(REG_T5)
#define PC_REG       REGISTER_NUMBER(REG_S5)


#define UNUSED       REGISTER_NUMBER(REG_T2)

/* shadow registers: save to stack if caller saved
 * Again, there is an incentive of having them callee
 * saved. 
 */
#define SHADOW0      REGISTER_NUMBER(REG_S0)
#define SHADOW1      REGISTER_NUMBER(REG_S1)
#define SHADOW2      REGISTER_NUMBER(REG_S2)
#define SHADOW3      REGISTER_NUMBER(REG_S3)
#define SHADOW4      REGISTER_NUMBER(REG_S4)
#define SHADOW5      REGISTER_NUMBER(REG_S6)
#define SHADOW6      REGISTER_NUMBER(REG_S7)

/* DON'T USE $AT!!! VCODE CAN USE $AT and this
 * CAN HOSE US BIG TIME. Should fix vcode, but for now
 * play it safe. XXX -BL */

/* #define SHADOW7      REGISTER_NUMBER(REG_AT) */


#define REGALLOC_LIST {SHADOW0,SHADOW1,SHADOW2, \
                       SHADOW3,SHADOW4,SHADOW5, \
                       SHADOW6,0}

#ifdef OBSOLETE

/* This is used to hold the old PC, so we can see if we are */
/* transfering to code on the same page */
#define OLD_PC         REGISTER_NUMBER(REG_S3)

/* In MP_IN_UP, we store the old PC directly in the state structure, */
/* so we use it to provide guarantees on interleaving between */
/* processors It holds the threshold which, with CLOCK_REG falls under */
/* this value, we context switch*/
#define  TQUANTUM_REG     REGISTER_NUMBER(REG_S3)


#endif



#endif /* EMBRA_H */