mirror of
https://github.com/openhwgroup/cvw
synced 2025-02-03 02:05:21 +00:00
1997 lines
104 KiB
C
1997 lines
104 KiB
C
// -----------
|
||
// Copyright (c) 2020-2023. RISC-V International. All rights reserved.
|
||
// SPDX-License-Identifier: BSD-3-Clause
|
||
// -----------
|
||
|
||
|
||
// This file is divided into the following sections:
|
||
// RV Arch Test Constants
|
||
// general test and helper macros, required, optional, or just useful
|
||
// _ARGn, SIG[BASE/UPD[_F/ID]],BASEUPD,BIT,LA ,LI,RVTEST_[INIT/SAVE]_GPRS, XCSR_RENAME
|
||
// RV ARCH Test Interrupt Macros ****FIXME:spec which regs must not be altered
|
||
// primary macros used by handle: RVTEST_TRAP{_PROLOG/_HANDLER/_EPILOG/SAVEAREA}
|
||
// required test format spec macros: RVTEST_{Code/DATA/SIG}{_BEGIN/_END}
|
||
// macros from Andrew Waterman's risc-v test macros
|
||
// deprecated macro name aliases, just for migration ease
|
||
|
||
// The resulting memory layout of the trap handler is (MACRO_NAME, label [function])
|
||
//****************************************************************
|
||
// (code section)
|
||
// RVMODEL_BOOT
|
||
// rvtest_entry_point: [boot code]
|
||
// RVTEST_CODE_BEGIN
|
||
// rvtest_init: [TRAP_PROLOG] (m, ms, or msv)
|
||
// [INIT_GPRS]
|
||
// rvtest_code_begin:
|
||
//*****************************
|
||
//********(body of tests)******
|
||
//*****************************
|
||
// RVTEST_CODE_END
|
||
// rvtest_code_end: [*optional* SAVE_GPRS routine]
|
||
// [RVTEST_GOTO_MMODE ] **FIXME** this won't work if MMU enabled unless VA=PA
|
||
// cleanup_epilogs [TRAP_EPILOG (m, ms, or msv)] (jump to exit_cleanup)
|
||
// [TRAP_HANDLER (m, ms, or msv)]
|
||
// exit_cleanup: [RVMODEL_HALT macro or a branch to it.]
|
||
//
|
||
//--------------------------------this could start a new section--------------------------------
|
||
// (Data section) - align to 4K boundary
|
||
// RVTEST_DATA_BEGIN
|
||
//**************************************
|
||
//*****(Ld/St test data is here)********
|
||
//**************************************
|
||
//
|
||
//**************************************
|
||
//*****(trap handler data is here)******
|
||
//**************************************
|
||
//
|
||
// rvtest_trap_sig: [global trap signature start (shared by all modes) inited to mtrap_sigptr] **FIXME: needs VA=PA
|
||
// RVTEST_TRAP_SAVEAREA [handler sv area(m, ms, or msv) temp reg save, CSRs, tramp table, ptrs]
|
||
// rvtest_data_begin: [input data (shared by all modes)]
|
||
// RVTEST_DATA_END
|
||
// rvtest_data_end:
|
||
// RVTEST_ROOT_PG_TBL [sets up identity map (VA=PA)
|
||
// sroot_pg_tbl: (if smode)
|
||
// vroot_pg_tbl: (if hypervisor)
|
||
//--------------------------------this could start a new section--------------------------------
|
||
// RVTEST_SIG_BEGIN
|
||
// RVMODEL_DATA_BEGIN
|
||
// rvtest_sig_begin: [beginning of signature, used by signature dump, can be used by tests]
|
||
// mtrap_sigptr: [global trap signature start (shared by all modes)] - defined by tests
|
||
// gpr_save: [gpr save area (optional, enabled if rvtest_gpr_save is defined)]
|
||
// RVTEST_SIG_END
|
||
// rvtest_sig_end: [global test end signature (shared by all modes)] (shouldn't matter what RVMODEL_DATA_END does)
|
||
// RVMODEL_DATA_END
|
||
//--------------------------------end of test--------------------------------
|
||
|
||
/* The following macros are optional if interrupt tests are enabled (defaulted if not defined):
|
||
RVMODEL_SET_[M/V/S]_[SW]_INT
|
||
RVMODEL_CLR_[M/V/S]_[SW/TIMTER/EXT]_INT
|
||
rvtest_[M/V/S]trap_routine
|
||
GOTO_[M/S/U]MODE, INSTANTIATE_MODE_MACRO (prolog/handler/epilog/savearea)
|
||
The following macro is optional, and defaults to fence.i if not defined
|
||
RVMODEL.FENCEI
|
||
The following variables are used if interrupt tests are enabled (defaulted if not defined):
|
||
NUM_SPECD_INTCAUSES
|
||
The following variables are optional if exception tests are enabled (defaulted if not defined):
|
||
DATA_REL_TVAL_MSK CODE_REL_TVAL_MSK
|
||
The following variables are optional:
|
||
rvtest_gpr_save: if defined, stores GPR contents into signature at test end (for debug)
|
||
The following labels are required and defined by required macros:
|
||
rvtest_code_begin: defined by RVTEST_CODE_BEGIN macro (boot code can precede this)
|
||
rvtest_code_end: defined by RVTEST_CODE_END macro (trap handlers follow this)
|
||
rvtest_data_begin: defined by RVTEST_DATA_BEGIN macro
|
||
rvtest_data_end: defined by RVTEST_DATA_END macro
|
||
rvtest_sig_begin: defined by RVTEST_SIG_BEGIN macro (after RVMODEL_DATA_BEGIN) defines signature begin
|
||
rvtest_sig_end: defined by RVTEST_SIG_END macro (before RVMODEL_DATA_END) defines signature end
|
||
rvtest_Sroot_pg_tbl: defined by RVTEST_PTE_IDENT_MAP macro inside RVTEST_DATA_BEGIN if Smode implemented
|
||
rvtest_Vroot_pg_tbl: defined by RVTEST_PTE_IDENT_MAP macro inside RVTEST_DATA_BEGIN if VSmode implemented
|
||
labels/variables that must be defined by the DUT in model specific macros or #defines
|
||
mtrap_sigptr: defined by test if traps are possible, else is defaulted
|
||
*/
|
||
// don't put C-style macros (#define xxx) inside assembly macros; C-style is evaluated before assembly
|
||
|
||
#include "encoding.h"
|
||
#include "test_macros.h"
|
||
#define RVTEST_ISA(_STR) //empty macro used by framework
|
||
|
||
#define T1 x6
|
||
#define T2 x7
|
||
#define T3 x8
|
||
#define T4 x9
|
||
#define T5 x10
|
||
#define T6 x11
|
||
|
||
#define MIN(a,b) (((a)<(b))?(a):(b))
|
||
#define MAX(a,b) (((a)>(b))?(a):(b))
|
||
#define BIT(addr, bit) (((addr)>>(bit))&1)
|
||
#define MASK (((1<<(XLEN-1))-1) + (1<<(XLEN-1))) // XLEN bits of 1s
|
||
#define MASK_XLEN(val) val&MASK // shortens 64b values to XLEN when XLEN==32
|
||
|
||
#define REGWIDTH (XLEN>>3) // in units of #bytes
|
||
|
||
#define WDSZ 32
|
||
#define WDSGN ( WDSZ -1)
|
||
#define WDMSK ( (1 << WDSZ) -1)
|
||
#define SEXT_WRD(x) ((x & WDMSK) | (-BIT((x), WRDSGN)<< WRDSZ))
|
||
|
||
#define IMMSZ 12
|
||
#define IMMSGN (IMMSZ -1)
|
||
#define IMMMSK ( (1 << IMMSZ)-1)
|
||
#define SEXT_IMM(x) ((x & IMMMSK) | (-BIT((x), IMMSGN)<< IMMSZ))
|
||
|
||
#define LIMMSZ (WDSZ - IMMSZ)
|
||
#define LIMMSGN (LIMMSZ -1)
|
||
#define LIMMMSK ( (1 <<LIMMSZ)-1)
|
||
#define SEXT_LIMM(x) ((x &LIMMMSK) | (-BIT((x),LIMMSGN)<<LIMMSZ))
|
||
|
||
#define WDBYTSZ (WDSZ >> 3) // in units of #bytes
|
||
#define WDBYTMSK (WDBYTSZ-1)
|
||
|
||
#define ALIGNSZ ((XLEN>>5)+2) // log2(XLEN): 2,3,4 for XLEN 32,64,128
|
||
#if XLEN>FLEN
|
||
#define SIGALIGN REGWIDTH
|
||
#else
|
||
#define SIGALIGN FREGWIDTH
|
||
#endif
|
||
|
||
#ifndef RVMODEL_MTVEC_ALIGN
|
||
#define MTVEC_ALIGN 6 // ensure that a trampoline is on a typical cacheline boundary, just in case
|
||
#else
|
||
#define MTVEC_ALIGN RVMODEL_MTVEC_ALIGN //Let the model defined value be used for required trap handler alignment based on implemented MTVEC
|
||
#endif
|
||
|
||
//==============================================================================
|
||
// this section has RV Arch Test Constants, mostly YAML based.
|
||
// It ensures they're defined & defaulted if necessary)
|
||
//==============================================================================
|
||
|
||
// set defaults
|
||
#ifndef NUM_SPECD_INTCAUSES
|
||
#define NUM_SPECD_INTCAUSES 16
|
||
#define INT_CAUSE_MSK ((1<<4)-1)
|
||
#endif
|
||
|
||
// set defaults
|
||
#ifndef NUM_SPECD_EXCPTCAUSES
|
||
#define NUM_SPECD_EXCPTCAUSES 16
|
||
#define EXCPT_CAUSE_MSK ((1<<4)-1)
|
||
#endif
|
||
|
||
// set defaults
|
||
#ifndef RVMODEL_CBZ_BLOCKSIZE
|
||
#define RVMODEL_CBZ_BLOCKSIZE 64
|
||
#endif
|
||
|
||
// set defaults
|
||
#ifndef RVMODEL_CMO_BLOCKSIZE
|
||
#define RVMODEL_CMO_BLOCKSIZE 64
|
||
#endif
|
||
|
||
//==========================================================================================
|
||
// By default, ZIFENCE is defined as nop for the implementation that does not support Zifencei
|
||
// Implementations that support Zifencei may use the fence.i instruction.
|
||
// This only gets executed if xTVEC is not writable to point to the trap trampoline,
|
||
// and if it isn't writable, the model better have the zifencei extension implemented.
|
||
//==========================================================================================
|
||
|
||
#ifndef RVMODEL_FENCEI
|
||
#ifndef ZIFENCE
|
||
#define RVMODEL_FENCEI nop // make sure ifetches get new code
|
||
#else
|
||
#define RVMODEL_FENCEI fence.i
|
||
#endif
|
||
#endif
|
||
|
||
#ifndef UNROLLSZ
|
||
#define UNROLLSZ 5
|
||
#endif
|
||
|
||
// **Note** that this is different that previous DATA_REL_TVAL_MASK! This is the OR of Code_Rel+Data_Rel
|
||
// if xTVAL is set to zero for some cause, then the corresponding bit in SET_REL_TVAL_MSK should be cleared
|
||
|
||
#ifndef SET_REL_TVAL_MSK
|
||
#define SET_REL_TVAL_MSK ((1<<CAUSE_MISALIGNED_FETCH | 1<<CAUSE_FETCH_ACCESS | 1<<CAUSE_BREAKPOINT | \
|
||
1<<CAUSE_MISALIGNED_LOAD | 1<<CAUSE_LOAD_ACCESS | 1<<CAUSE_MISALIGNED_STORE | 1<<CAUSE_STORE_ACCESS | \
|
||
1<<CAUSE_FETCH_PAGE_FAULT | 1<<CAUSE_LOAD_PAGE_FAULT | 1<<CAUSE_STORE_PAGE_FAULT) \
|
||
& 0xFFFFFFFF)
|
||
#endif
|
||
#ifndef SET_ABS_TVAL_MSK
|
||
#define SET_ABS_TVAL_MSK ((1<<CAUSE_ILLEGAL_INSTRUCTION) & 0xFFFFFFFF)
|
||
#endif
|
||
|
||
#ifndef GOTO_M_OP
|
||
#define GOTO_M_OP ecall
|
||
#endif
|
||
|
||
//this is a valid global pte entry with all permissions. IF at the root entry, it forms an identity map.
|
||
#define RVTEST_PTE_IDENT_MAP .fill 4096/REGWIDTH, REGWIDTH, (PTE_G | PTE_U | PTE_X | PTE_W | PTE_R | PTE_V)
|
||
#define RVTEST_PTE_NOACC_MAP .fill 4096/REGWIDTH, REGWIDTH, (PTE_G | PTE_U )
|
||
|
||
//_ADDR_SZ_ is a global variable extracted from YAML; set a default if it isn't defined
|
||
// This should be the MAX(phy_addr_size, VADDR_SZ) from YAML, where VADDR_SZ is derived from SATP.mode at reset
|
||
#ifndef _ADDR_SZ_
|
||
#if XLEN==64
|
||
#define _ADDR_SZ_ 57
|
||
#else
|
||
#define _ADDR_SZ_ 32
|
||
#endif
|
||
#endif
|
||
|
||
// define a bunch of XLEN dependent constants
|
||
#if XLEN==32
|
||
#define SREG sw
|
||
#define LREG lw
|
||
#define XLEN_WIDTH 5
|
||
#define LREGWU lw
|
||
#elif XLEN==64
|
||
#define SREG sd
|
||
#define LREG ld
|
||
#define XLEN_WIDTH 6
|
||
#define LREGWU lwu
|
||
#else
|
||
#define SREG sq
|
||
#define LREG lq
|
||
#define XLEN_WIDTH 7
|
||
#endif
|
||
|
||
#if FLEN==32
|
||
#define FLREG flw
|
||
#define FSREG fsw
|
||
#define FREGWIDTH 4
|
||
#elif FLEN==64
|
||
#define FLREG fld
|
||
#define FSREG fsd
|
||
#define FREGWIDTH 8
|
||
#else
|
||
#define FLREG flq
|
||
#define FSREG fsq
|
||
#define FREGWIDTH 16
|
||
#endif
|
||
|
||
|
||
#if SIGALIGN==8
|
||
#define CANARY \
|
||
.dword 0x6F5CA309E7D4B281
|
||
#else
|
||
#define CANARY \
|
||
.word 0x6F5CA309
|
||
#endif
|
||
|
||
//---------------------------mode encoding definitions-----------------------------
|
||
.set MMODE_SIG, 3
|
||
.set SMODE_SIG, 1
|
||
.set VMODE_SIG, 2
|
||
/* these macros need to be defined because mode is uppercase in mode specific macros */
|
||
/* note that vs mode uses smode return */
|
||
|
||
#define GVA_LSB 6 //bit pos of LSB of the hstatus.GVA field
|
||
#define MPP_LSB 11 //bit pos of LSB of the mstatus.MPP field
|
||
#define MPRV_LSB 17 //bit pos of LSB of the mstatus.MPRV field
|
||
#define MPV_LSB 7 // bit pos of prev vmod mstatush.MPV in either mstatush or mstatus upper
|
||
#define MPP_SMODE (1<<MPP_LSB)
|
||
//define sizes
|
||
#define actual_tramp_sz ((XLEN + 3* NUM_SPECD_INTCAUSES + 5) * 4) // 5 is added ops before common entry pt
|
||
#define tramp_sz ((actual_tramp_sz+4) & -8) // round up to keep aligment for sv area alloc
|
||
#define ptr_sv_sz (16*8)
|
||
#define reg_sv_sz ( 8*REGWIDTH)
|
||
#define sv_area_sz (tramp_sz + ptr_sv_sz + reg_sv_sz) // force dblword alignment
|
||
#define int_hndlr_tblsz (XLEN*2*WDBYTSZ)
|
||
/*
|
||
//#define sv_area_sz (Msv_area_end-Mtramptbl_sv) //sv_area start with aligned tramp_tbl
|
||
//#define tramp_sz (((common_Mentry-Mtrampoline)+4)& -8) // #ops from Mend..Mentry, forced to dblword size
|
||
*/
|
||
//define a fixed offsets into the save area
|
||
#define tramp_sv_off ( 0*8) // (Mtramptbl_sv -Mtrapreg_sv) algned to dblwd
|
||
|
||
#define code_bgn_off (tramp_sz+ 0*8) // (Mcode_bgn_ptr -Mtrapreg_sv)
|
||
#define code_seg_siz (tramp_sz+ 1*8) // (Mcode_seg_siz -Mtrapreg_sv)
|
||
#define data_bgn_off (tramp_sz+ 2*8) // (Mdata_bgn_ptr -Mtrapreg_sv) <--update on mapping chg
|
||
#define data_seg_siz (tramp_sz+ 3*8) // (Mdata_seg_siz -Mtrapreg_sv)
|
||
#define sig_bgn_off (tramp_sz+ 4*8) // ( Msig_bgn_ptr -Mtrapreg_sv) <--update on mapping chg
|
||
#define sig_seg_siz (tramp_sz+ 5*8) // ( Msig_seg_siz -Mtrapreg_sv)
|
||
#define vmem_bgn_off (tramp_sz+ 6*8) // (Mvmem_bgn_ptr -Mtrapreg_sv) <--update on mapping chg
|
||
#define vmem_seg_siz (tramp_sz+ 7*8) // (Mvmem_seg_siz -Mtrapreg_sv)
|
||
|
||
#define mpp_sv_off (sv_area_sz+tramp_sz+8*8) // (Strap_sig -Mtrapreg_sv)
|
||
#define trapsig_ptr_off (tramp_sz+ 8*8) // (Mtrap_sig -Mtrapreg_sv)
|
||
#define xsatp_sv_off (tramp_sz+ 9*8) // (Msatp_sv -Mtrapreg_sv)
|
||
#define trampend_off (tramp_sz+10*8) // (Mtrampend_sv -Mtrapreg_sv)
|
||
#define tentry_addr (tramp_sz+11*8) // (Mtentry_sv -Mtrapreg_sv) <--update on mapping chg
|
||
#define xedeleg_sv_off (tramp_sz+12*8) // (Medeleg_sv -Mtrapreg_sv)
|
||
#define xtvec_new_off (tramp_sz+13*8) // (tvec_new -Mtrapreg_sv)
|
||
#define xtvec_sav_off (tramp_sz+14*8) // (tvec_save -Mtrapreg_sv)
|
||
#define xscr_save_off (tramp_sz+15*8) // (scratch_save -Mtrapreg_sv)
|
||
#define trap_sv_off (tramp_sz+16*8) // (trapreg_sv -Mtrapreg_sv) 8 registers long
|
||
|
||
//==============================================================================
|
||
// this section has general test helper macros, required, optional, or just useful
|
||
//==============================================================================
|
||
|
||
#define _ARG5(_1ST,_2ND, _3RD,_4TH,_5TH,...) _5TH
|
||
#define _ARG4(_1ST,_2ND, _3RD,_4TH,...) _4TH
|
||
#define _ARG3(_1ST,_2ND, _3RD, ...) _3RD
|
||
#define _ARG2(_1ST,_2ND, ...) _2ND
|
||
#define _ARG1(_1ST,...) _1ST
|
||
#define NARG(...) _ARG5(__VA_OPT__(__VA_ARGS__,)4,3,2,1,0)
|
||
#define RVTEST_CASE(_PNAME,_DSTR,...)
|
||
|
||
//-----------------------------------------------------------------------
|
||
//Fixed length la, li macros; # of ops is ADDR_SZ dependent, not data dependent
|
||
//-----------------------------------------------------------------------
|
||
|
||
// this generates a constants using the standard addi or lui/addi sequences
|
||
// but also handles cases that are contiguous bit masks in any position,
|
||
// and also constants handled with the addi/lui/addi but are shifted left
|
||
|
||
/**** fixed length LI macro ****/
|
||
#if (XLEN<64)
|
||
#define LI(reg, imm) ;\
|
||
.set immx, (imm & MASK) /* trim to XLEN (noeffect on RV64) */ ;\
|
||
.set absimm, ((immx^(-BIT(immx,XLEN-1)))&MASK) /* cvt to posnum to simplify code */ ;\
|
||
.set cry, (BIT(imm, IMMSGN)) ;\
|
||
.set imm12, (SEXT_IMM(immx)) ;\
|
||
.if ((absimm>>IMMSGN)==0) /* fits 12b signed imm (properly sgnext)? */ ;\
|
||
li reg, imm12 /* yes, <= 12bit, will be simple li */ ;\
|
||
.else ;\
|
||
lui reg, (((immx>>IMMSZ)+cry) & LIMMMSK) /* <= 32b, use lui/addi */ ;\
|
||
.if ((imm&IMMMSK)!=0) /* but skip this if lower bits are zero */ ;\
|
||
addi reg, reg, imm12 ;\
|
||
.endif ;\
|
||
.endif
|
||
#else
|
||
#define LI(reg, imm) ;\
|
||
.option push ;\
|
||
.option norvc ;\
|
||
.set immx, (imm & MASK) /* trim to XLEN (noeffect on RV64) */ ;\
|
||
/***************** used in loop that detects bitmasks */ ;\
|
||
.set edge1, 1 /* 1st "1" bit pos scanning r to l */ ;\
|
||
.set edge2, 0 /* 1st "0" bit pos scanning r to l */ ;\
|
||
.set fnd1, -1 /* found 1st "1" bit pos scanning r to l */ ;\
|
||
.set fnd2, -1 /* found 1st "0" bit pos scanning r to l */ ;\
|
||
.set imme, ((immx^(-BIT(immx,0 )))&MASK) /* cvt to even, cvt back at end */ ;\
|
||
.set pos, 0 ;\
|
||
/***************** used in code that checks for 32b immediates */ ;\
|
||
.set absimm, ((immx^(-BIT(immx,XLEN-1)))&MASK) /* cvt to posnum to simplify code */ ;\
|
||
.set cry, (BIT(immx, IMMSGN)) ;\
|
||
.set imm12, (SEXT_IMM(immx)) ;\
|
||
/***************** used in code that gnerates bitmasks */ ;\
|
||
.set even, (1-BIT(imm, 0)) /* imm has at least 1 trailing zero */ ;\
|
||
.set cryh, (BIT(immx, IMMSGN+32)) ;\
|
||
/******** loop finding rising/falling edge fm LSB-MSB given even operand ****/ ;\
|
||
.rept XLEN ;\
|
||
.if (fnd1<0) /* looking for first edge? */ ;\
|
||
.if (BIT(imme,pos)==1) /* look for falling edge[pos] */ ;\
|
||
.set edge1,pos /* fnd falling edge, don’t chk for more */ ;\
|
||
.set fnd1,0 ;\
|
||
.endif ;\
|
||
.elseif (fnd2<0) /* looking for second edge? */ ;\
|
||
.if (BIT(imme,pos)==0) /* yes, found rising edge[pos]? */ ;\
|
||
.set edge2, pos /* fnd rising edge, don’t chk for more */ ;\
|
||
.set fnd2,0 ;\
|
||
.endif ;\
|
||
.endif ;\
|
||
.set pos, pos+1 /* keep looking (even if already found) */ ;\
|
||
.endr ;\
|
||
/***************** used in code that generates shifted 32b values */ ;\
|
||
.set immxsh, (immx>>edge1) /* *sh variables only used if positive */ ;\
|
||
.set imm12sh,(SEXT_IMM(immxsh))/* look @1st 12b of shifted imm val */ ;\
|
||
.set crysh, (BIT(immxsh, IMMSGN)) ;\
|
||
.set absimmsh, immxsh /* pos, no inversion needed, just shift */ ;\
|
||
/*******does it fit into std li or lui+li sequence****************************/ ;\
|
||
.if ((absimm>>IMMSGN)==0) /* fits 12b signed imm (properly sgnext)? */ ;\
|
||
li reg, imm12 /* yes, <= 12bit, will be simple li */ ;\
|
||
.elseif ((absimm+ (cry << IMMSZ) >> WDSGN)==0)/*fits 32b sgnimm?(w/ sgnext)?*/;\
|
||
lui reg, (((immx>>IMMSZ)+cry) & LIMMMSK) /* <= 32b, use lui/addi */ ;\
|
||
.if ((imm&IMMMSK)!=0) /* but skip this if lower bits are zero */ ;\
|
||
addi reg, reg, imm12 ;\
|
||
.endif ;\
|
||
/*********** look for 0->1->0 masks, or inverse sgl/multbit *************/ ;\
|
||
.elseif ( even && (fnd2<0)) /* only rising edge, so 111000 */ ;\
|
||
li reg, -1 ;\
|
||
slli reg, reg, edge1 /* make 111s --> 000s mask */ ;\
|
||
.elseif (!even && (fnd2<0)) /* only falling edge, so 000111 */ ;\
|
||
li reg, -1 ;\
|
||
srli reg, reg, XLEN-edge1 /* make 000s --> 111s mask */ ;\
|
||
.elseif (imme == (1<<edge1)) /* check for single bit case */ ;\
|
||
li reg, 1 ;\
|
||
slli reg, reg, edge1 /* make 0001000 sgl bit mask */ ;\
|
||
.if (!even) ;\
|
||
xori reg, reg, -1 /* orig odd, cvt to 1110111 mask */ ;\
|
||
.endif ;\
|
||
.elseif (imme == ((1<<edge2) - (1<<edge1))) /* chk for multibit case */ ;\
|
||
li reg, -1 ;\
|
||
srli reg, reg, XLEN-(edge2-edge1) /* make multibit 1s mask */ ;\
|
||
slli reg, reg, edge1 /* and put it into position */ ;\
|
||
.if (!even) ;\
|
||
xori reg, reg, -1 /* orig odd, cvt to 1110111 mask */ ;\
|
||
.endif ;\
|
||
/************** look for 12b or 32b imms with trailing zeroes ***********/ ;\
|
||
.elseif ((immx==imme)&&((absimmsh>>IMMSGN)==0))/* fits 12b after shift? */ ;\
|
||
li reg, imm12sh /* <= 12bit, will be simple li */ ;\
|
||
slli reg, reg, edge1 /* add trailing zeros */ ;\
|
||
.elseif ((immx==imme)&&(((absimmsh>>WDSGN)+crysh)==0)) /* fits 32 <<shft? */ ;\
|
||
lui reg, ((immxsh>>IMMSZ)+crysh)&LIMMMSK /* <=32b, use lui/addi */ ;\
|
||
.if ((imm12sh&IMMMSK)!=0) /* but skip this if low bits ==0 */ ;\
|
||
addi reg, reg, imm12sh ;\
|
||
.endif ;\
|
||
slli reg, reg, edge1 /* add trailing zeros */ ;\
|
||
.else /* give up, use fixed 8op sequence*/ ;\
|
||
/******* TBD add sp case of zero short imms, rmv add/merge shifts ******/ ;\
|
||
lui reg, ((immx>>(XLEN-LIMMSZ))+cryh)&LIMMMSK /* 1st 20b (63:44) */ ;\
|
||
addi reg, reg, SEXT_IMM(immx>>32) /* nxt 12b (43:32) */ ;\
|
||
slli reg, reg, 11 /* following are <12b, don't need SEXT */ ;\
|
||
addi reg, reg, (immx>>21) & (IMMMSK>>1) /* nxt 11b (31:21) */ ;\
|
||
slli reg, reg, 11 /* mk room for 11b */ ;\
|
||
addi reg, reg, (immx>>10) & (IMMMSK>>1) /* nxt 11b (20:10) */ ;\
|
||
slli reg, reg, 10 /* mk room for 10b */ ;\
|
||
.if ((imm&(IMMMSK>>2))!=0) /* but skip this if lower bits are zero */ ;\
|
||
addi reg, reg, (immx) & (IMMMSK>>2) /* lst 10b (09:00) */ ;\
|
||
.endif ;\
|
||
.if (XLEN==32) ;\
|
||
.warning "Should never get here for RV32" ;\
|
||
.endif ;\
|
||
.endif ;\
|
||
.option pop
|
||
#endif
|
||
|
||
/**** fixed length LA macro; alignment and rvc/norvc unknown before execution ****/
|
||
#define LA(reg,val) ;\
|
||
.option push ;\
|
||
.option rvc ;\
|
||
.align UNROLLSZ ;\
|
||
.option norvc ;\
|
||
la reg,val ;\
|
||
.align UNROLLSZ ;\
|
||
.option pop
|
||
|
||
#define ADDI(dst, src, imm) /* helper*/ ;\
|
||
.if ((imm<=2048) & (imm>=-2048)) ;\
|
||
addi dst, src, imm ;\
|
||
.else ;\
|
||
LI( dst, imm) ;\
|
||
addi dst, src, dst ;\
|
||
.endif
|
||
|
||
/*****************************************************************/
|
||
/**** initialize regs, just to make sure you catch any errors ****/
|
||
/*****************************************************************/
|
||
|
||
/* init regs, to ensure you catch any errors */
|
||
.macro RVTEST_INIT_GPRS
|
||
LI (x1, (0xFEEDBEADFEEDBEAD & MASK))
|
||
LI (x2, (0xFF76DF56FF76DF56 & MASK))
|
||
LI (x3, (0x7FBB6FAB7FBB6FAB & MASK))
|
||
LI (x4, (0xBFDDB7D5BFDDB7D5 & MASK))
|
||
LI (x5, (0xDFEEDBEADFEEDBEA & MASK))
|
||
LI (x6, (0x6FF76DF56FF76DF5 & MASK))
|
||
LI (x7, (0xB7FBB6FAB7FBB6FA & MASK))
|
||
LI (x8, (0x5BFDDB7D5BFDDB7D & MASK))
|
||
LI (x9, (0xADFEEDBEADFEEDBE & MASK))
|
||
LI (x10, (0x56FF76DF56FF76DF & MASK))
|
||
LI (x11, (0xAB7FBB6FAB7FBB6F & MASK))
|
||
LI (x12, (0xD5BFDDB7D5BFDDB7 & MASK))
|
||
LI (x13, (0xEADFEEDBEADFEEDB & MASK))
|
||
LI (x14, (0xF56FF76DF56FF76D & MASK))
|
||
LI (x15, (0xFAB7FBB6FAB7FBB6 & MASK))
|
||
#ifndef RVTEST_E
|
||
LI (x16, (0x7D5BFDDB7D5BFDDB & MASK))
|
||
LI (x17, (0xBEADFEEDBEADFEED & MASK))
|
||
LI (x18, (0xDF56FF76DF56FF76 & MASK))
|
||
LI (x19, (0x6FAB7FBB6FAB7FBB & MASK))
|
||
LI (x20, (0xB7D5BFDDB7D5BFDD & MASK))
|
||
LI (x21, (0xDBEADFEEDBEADFEE & MASK))
|
||
LI (x22, (0x6DF56FF76DF56FF7 & MASK))
|
||
LI (x23, (0xB6FAB7FBB6FAB7FB & MASK))
|
||
LI (x24, (0xDB7D5BFDDB7D5BFD & MASK))
|
||
LI (x25, (0xEDBEADFEEDBEADFE & MASK))
|
||
LI (x26, (0x76DF56FF76DF56FF & MASK))
|
||
LI (x27, (0xBB6FAB7FBB6FAB7F & MASK))
|
||
LI (x28, (0xDDB7D5BFDDB7D5BF & MASK))
|
||
LI (x29, (0xEEDBEADFEEDBEADF & MASK))
|
||
LI (x30, (0xF76DF56FF76DF56F & MASK))
|
||
LI (x31, (0xFBB6FAB7FBB6FAB7 & MASK))
|
||
#endif
|
||
.endm
|
||
/******************************************************************************/
|
||
/**** this is a helper macro that conditionally instantiates the macros ****/
|
||
/**** PROLOG/HANDLER/EPILOG/SAVEAREA depending on test type & mode support ****/
|
||
/******************************************************************************/
|
||
.macro INSTANTIATE_MODE_MACRO MACRO_NAME
|
||
#ifdef rvtest_mtrap_routine
|
||
\MACRO_NAME M // actual m-mode prolog/epilog/handler code
|
||
|
||
#ifdef rvtest_strap_routine
|
||
\MACRO_NAME S // actual s-mode prolog/epilog/handler code
|
||
|
||
#ifdef rvtest_vtrap_routine
|
||
\MACRO_NAME V // actual v-mode prolog/epilog/handler code
|
||
#endif
|
||
#endif
|
||
#endif
|
||
.endm
|
||
|
||
/**************************************************************************/
|
||
/**** this is a helper macro defaulting the int macro if its undefined ****/
|
||
/**** It builds the macro name from arguments prefix, mode, and type ****/
|
||
/**** The macro names are RV_MODEL_SET_[M/S/V][SW/TMR,EXT] ****/
|
||
/**** and RV_MODEL_CLR_[M/S/V][SW] ****/
|
||
/**************************************************************************/
|
||
|
||
.macro DFLT_INT_MACRO MACRO_NAME
|
||
.set MACRO_NAME_, \MACRO_NAME
|
||
.ifndef MACRO_NAME_
|
||
.warning "MACRO_NAME_ is not defined by target. Executing this will end test."
|
||
#define MACRO_NAME_ j cleanup_epilogs
|
||
.endif
|
||
.endm
|
||
|
||
/******************************************************************************/
|
||
/**** These macros enable parameterization of trap handlers for each mode ****/
|
||
/******************************************************************************/
|
||
|
||
.macro _XCSR_RENAME_V
|
||
.set CSR_XSTATUS, CSR_VSSTATUS /****FIXME? is the right substitution? ****/
|
||
.set CSR_XEDELEG, CSR_HEDELEG /****FIXME? is the right substitution? ****/
|
||
.set CSR_XIE, CSR_HIE
|
||
.set CSR_XIP, CSR_HIP
|
||
.set CSR_XCAUSE, CSR_VSCAUSE
|
||
.set CSR_XEPC, CSR_VSEPC
|
||
.set CSR_XSATP, CSR_VSATP
|
||
.set CSR_XSCRATCH,CSR_VSSCRATCH
|
||
.set CSR_XTVAL, CSR_VSTVAL
|
||
.set CSR_XTVEC, CSR_VSTVEC
|
||
.endm
|
||
|
||
.macro _XCSR_RENAME_S
|
||
.set CSR_XSTATUS, CSR_SSTATUS
|
||
.set CSR_XEDELEG, CSR_SEDELEG
|
||
.set CSR_XIE, CSR_SIE
|
||
.set CSR_XIP, CSR_SIP
|
||
.set CSR_XCAUSE, CSR_SCAUSE
|
||
.set CSR_XEPC, CSR_SEPC
|
||
.set CSR_XSATP, CSR_SATP
|
||
.set CSR_XSCRATCH,CSR_SSCRATCH
|
||
.set CSR_XTVAL, CSR_STVAL
|
||
.set CSR_XTVEC, CSR_STVEC
|
||
.endm
|
||
|
||
.macro _XCSR_RENAME_M
|
||
.set CSR_XSTATUS, CSR_MSTATUS
|
||
.set CSR_XEDELEG, CSR_MEDELEG
|
||
.set CSR_XIE, CSR_MIE
|
||
.set CSR_XIP, CSR_MIP
|
||
.set CSR_XCAUSE, CSR_MCAUSE
|
||
.set CSR_XEPC, CSR_MEPC
|
||
.set CSR_XSATP, CSR_SATP
|
||
.set CSR_XSCRATCH,CSR_MSCRATCH
|
||
.set CSR_XTVAL, CSR_MTVAL
|
||
.set CSR_XTVEC, CSR_MTVEC
|
||
.endm
|
||
|
||
/******************************************************************************/
|
||
/**** this is a helper macro that creates CSR aliases so code that ****/
|
||
/**** accesses CSRs when V=1 in different modes can share the code ****/
|
||
/******************************************************************************/
|
||
|
||
.macro XCSR_RENAME __MODE__ // enable CSR names to be parameterized, V,S merged
|
||
.ifc \__MODE__ , M
|
||
_XCSR_RENAME_M
|
||
.endif
|
||
.ifc \__MODE__ , S
|
||
_XCSR_RENAME_S
|
||
.endif
|
||
.ifc \__MODE__ , V
|
||
_XCSR_RENAME_S
|
||
.endif
|
||
.endm
|
||
|
||
/******************************************************************************/
|
||
/**** this is a helper macro that creates CSR aliases so code that ****/
|
||
/**** accesses CSRs when V=1 in different modes can share the code ****/
|
||
/**** this verasion treats Vmodes separately as opposed to XCSR_RENAME ****/
|
||
/**** this is used when the using it is run from Mmode ****/
|
||
/******************************************************************************/
|
||
|
||
.macro XCSR_VRENAME __MODE__ // enable CSR names to be parameterized, V,S separate
|
||
.ifc \__MODE__ , M
|
||
_XCSR_RENAME_M
|
||
.endif
|
||
.ifc \__MODE__ , S
|
||
_XCSR_RENAME_S
|
||
.endif
|
||
.ifc \__MODE__ , V
|
||
_XCSR_RENAME_V
|
||
.endif
|
||
.endm
|
||
|
||
////////////////////////////////////////////////////////////////////////////////////////
|
||
//**** This is a helper macro that saves GPRs. Normally used only inside CODE_END ****//
|
||
//**** Note: this needs a temp scratch register, & there isn't anything that will ****//
|
||
//**** will work, so we always trash some register, determined by macro param ****//
|
||
//**** NOTE: Only be use for debug! Xregs containing addresses won't be relocated ****//
|
||
////////////////////////////////////////////////////////////////////////////////////////
|
||
|
||
#define RVTEST_SAVE_GPRSM(_BR, _LBL, ...) ;\
|
||
.option push ;\
|
||
.option norvc ;\
|
||
.set __SV_MASK__, -1 /* default to save all */ ;\
|
||
.if NARG(__VA_ARGS__) == 1 ;\
|
||
.set __SV_MASK__, _ARG1(__VA_OPT__(__VA_ARGS__,0)) ;\
|
||
.endif ;\
|
||
.set offset, 0 ;\
|
||
LA(_BR, _LBL) ;\
|
||
.if (__SV_MASK__ & (0x2)) == 0x2 ;\
|
||
RVTEST_SIGUPD(_BR, x1) ;\
|
||
.endif ;\
|
||
.if (__SV_MASK__ & (0x4)) == 0x4 ;\
|
||
RVTEST_SIGUPD(_BR, x2) ;\
|
||
.endif ;\
|
||
.if (__SV_MASK__ & (0x8)) == 0x8 ;\
|
||
RVTEST_SIGUPD(_BR, x3) ;\
|
||
.endif ;\
|
||
.if (__SV_MASK__ & (0x10)) == 0x10 ;\
|
||
RVTEST_SIGUPD(_BR, x4) ;\
|
||
.endif ;\
|
||
.if (__SV_MASK__ & (0x20)) == 0x20 ;\
|
||
RVTEST_SIGUPD(_BR, x5) ;\
|
||
.endif ;\
|
||
.if (__SV_MASK__ & (0x40)) == 0x40 ;\
|
||
RVTEST_SIGUPD(_BR, x6) ;\
|
||
.endif ;\
|
||
.if (__SV_MASK__ & (0x80)) == 0x80 ;\
|
||
RVTEST_SIGUPD(_BR, x7) ;\
|
||
.endif ;\
|
||
.if (__SV_MASK__ & (0x100)) == 0x100 ;\
|
||
RVTEST_SIGUPD(_BR, x8) ;\
|
||
.endif ;\
|
||
.if (__SV_MASK__ & (0x200)) == 0x200 ;\
|
||
RVTEST_SIGUPD(_BR, x9) ;\
|
||
.endif ;\
|
||
.if (__SV_MASK__ & (0x400)) == 0x400 ;\
|
||
RVTEST_SIGUPD(_BR, x10) ;\
|
||
.endif ;\
|
||
.if (__SV_MASK__ & (0x800)) == 0x800 ;\
|
||
RVTEST_SIGUPD(_BR, x11) ;\
|
||
.endif ;\
|
||
.if (__SV_MASK__ & (0x1000)) == 0x1000 ;\
|
||
RVTEST_SIGUPD(_BR, x12) ;\
|
||
.endif ;\
|
||
.if (__SV_MASK__ & (0x2000)) == 0x2000 ;\
|
||
RVTEST_SIGUPD(_BR, x13) ;\
|
||
.endif ;\
|
||
.if (__SV_MASK__ & (0x4000)) == 0x4000 ;\
|
||
RVTEST_SIGUPD(_BR, x14) ;\
|
||
.endif ;\
|
||
.if (__SV_MASK__ & (0x8000)) == 0x8000 ;\
|
||
RVTEST_SIGUPD(_BR, x15) ;\
|
||
.endif ;\
|
||
#ifndef RVTEST_E ;\
|
||
.if (__SV_MASK__ & (0x10000)) == 0x10000 ;\
|
||
RVTEST_SIGUPD(_BR, x16) ;\
|
||
.endif ;\
|
||
.if (__SV_MASK__ & (0x20000)) == 0x20000 ;\
|
||
RVTEST_SIGUPD(_BR, x17) ;\
|
||
.endif ;\
|
||
.if (__SV_MASK__ & (0x40000)) == 0x40000 ;\
|
||
RVTEST_SIGUPD(_BR, x18) ;\
|
||
.endif ;\
|
||
.if (__SV_MASK__ & (0x80000)) == 0x80000 ;\
|
||
RVTEST_SIGUPD(_BR, x19) ;\
|
||
.endif ;\
|
||
.if (__SV_MASK__ & (0x100000)) == 0x100000 ;\
|
||
RVTEST_SIGUPD(_BR, x20) ;\
|
||
.endif ;\
|
||
.if (__SV_MASK__ & (0x200000)) == 0x200000 ;\
|
||
RVTEST_SIGUPD(_BR, x21) ;\
|
||
.endif ;\
|
||
.if (__SV_MASK__ & (0x400000)) == 0x400000 ;\
|
||
RVTEST_SIGUPD(_BR, x22) ;\
|
||
.endif ;\
|
||
.if (__SV_MASK__ & (0x800000)) == 0x800000 ;\
|
||
RVTEST_SIGUPD(_BR, x23) ;\
|
||
.endif ;\
|
||
.if (__SV_MASK__ & (0x1000000)) == 0x1000000 ;\
|
||
RVTEST_SIGUPD(_BR, x24) ;\
|
||
.endif ;\
|
||
.if (__SV_MASK__ & (0x2000000)) == 0x2000000 ;\
|
||
RVTEST_SIGUPD(_BR, x25) ;\
|
||
.endif ;\
|
||
.if (__SV_MASK__ & (0x4000000)) == 0x4000000 ;\
|
||
RVTEST_SIGUPD(_BR, x26) ;\
|
||
.endif ;\
|
||
.if (__SV_MASK__ & (0x8000000)) == 0x8000000 ;\
|
||
RVTEST_SIGUPD(_BR, x27) ;\
|
||
.endif ;\
|
||
.if (__SV_MASK__ & (0x10000000)) == 0x10000000 ;\
|
||
RVTEST_SIGUPD(_BR, x28) ;\
|
||
.endif ;\
|
||
.if (__SV_MASK__ & (0x20000000)) == 0x20000000 ;\
|
||
RVTEST_SIGUPD(_BR, x29) ;\
|
||
.endif ;\
|
||
.if (__SV_MASK__ & (0x40000000)) == 0x40000000 ;\
|
||
RVTEST_SIGUPD(_BR, x30) ;\
|
||
.endif ;\
|
||
.if (__SV_MASK__ & (0x80000000)) == 0x80000000 ;\
|
||
RVTEST_SIGUPD(_BR, x31) ;\
|
||
.endif ;\
|
||
.option pop ;\
|
||
#endif
|
||
|
||
/********************* REQUIRED FOR NEW TESTS *************************/
|
||
/**** new macro encapsulating RVMODEL_DATA_BEGIN (signature area) ****/
|
||
/**** defining rvtest_sig_begin: label to enabling direct stores ****/
|
||
/**** into the signature area to be properly relocated ****/
|
||
/**********************************************************************/
|
||
#define RVTEST_SIG_BEGIN ;\
|
||
.global rvtest_sig_begin /* defines beginning of signature area */ ;\
|
||
RVMODEL_DATA_BEGIN /* model specific stuff */ ;\
|
||
sig_begin_canary: ;\
|
||
CANARY ;\
|
||
rvtest_sig_begin:
|
||
|
||
// Tests allocate normal signature space here, then define
|
||
// the mtrap_sigptr: label to separate normal and trap
|
||
// signature space, then allocate trap signature space
|
||
|
||
/********************* REQUIRED FOR NEW TESTS *************************/
|
||
/**** new macro definong start of trap signature area ****/
|
||
/**** defining rvtest_sig_end: label to enabling direct stores ****/
|
||
/**** into the signature area to be properLY relocated ****/
|
||
/**********************************************************************/
|
||
#define RVTEST_TSIG_BEGIN ;\
|
||
.global rvtest_tsig_begin /* defines beginning of trap sig area */ ;\
|
||
;\
|
||
tsig_begin_canary: ;\
|
||
CANARY ;\
|
||
mtrap_sigptr: .fill 3*(XLEN/32),4,0xdeadbeef ;\
|
||
tsig_end_canary: ;\
|
||
CANARY
|
||
|
||
/********************* REQUIRED FOR NEW TESTS *************************/
|
||
/**** new macro encapsulating RVMODEL_SIG_END (signature area) ****/
|
||
/**** defining rvtest_sig_end: label to enabling direct stores ****/
|
||
/**** into the signature area to be properLY relocated ****/
|
||
/**********************************************************************/
|
||
#define RVTEST_SIG_END ;\
|
||
.global rvtest_sig_end /* defines beginning of trap sig area */ ;\
|
||
;\
|
||
#ifdef rvtest_gpr_save ;\
|
||
gpr_save: ;\
|
||
.fill 32*(XLEN/32),4,0xdeadbeef ;\
|
||
#endif ;\
|
||
;\
|
||
sig_end_canary: ;\
|
||
CANARY ;\
|
||
CANARY /* add one extra word of guardband */ ;\
|
||
rvtest_sig_end: ;\
|
||
RVMODEL_DATA_END /* model specific stuff */
|
||
|
||
|
||
/***********************************************************************************/
|
||
/**** At end of test, this code is entered. It sets a register x2 to 0 and by ****/
|
||
/**** default executes an ecall. The handler checks if the cause of the trap ****/
|
||
/**** was ecall, w/ x2=0, and divert a special rtn_fm_mmode handler. That code ****/
|
||
/**** determines the caller's mode, uses it to select it's CODE_BEGIN, and uses ****/
|
||
/**** to calculate it offset from Mmode's CODE_BEGIN, adjusts MEPC by that amt ****/
|
||
/**** to convert it to an Mmode address, restores saved regs, and branches to ****/
|
||
/**** the relocated addr+4, immediately following the ECALL, but now in Mmode ****/
|
||
/**** **NOTE**: this destroys T2 and clears x2 (AKA sp) ****/
|
||
/**** **NOTE**: this works from any mode but MUST not be used if ****/
|
||
/**** medeleg[<GOTO_M_OP_cause>]==1 to prevent infinite delegation loops. ****/
|
||
/**** **NOTE: tests that set medeleg[GOTO_M_OP_cause] must replace GOTO_M_OP ****/
|
||
/**** with an op that causes a different exception cause that isn't delegated. ****/
|
||
/***********************************************************************************/
|
||
|
||
.macro RVTEST_GOTO_MMODE
|
||
.option push
|
||
.option norvc
|
||
#ifdef rvtest_mtrap_routine /**** this can be empty if no Umode ****/
|
||
li x2, 0 /* Ecall w/x2=0 is handled specially to rtn here */
|
||
// Note that if illegal op trap is delegated , this may infinite loop
|
||
// The solution is either for test to disable delegation, or to
|
||
// redefine the GOTO_M_OP to be an op that will trap to mmode
|
||
|
||
GOTO_M_OP /* ECALL: traps always, but returns immediately to */
|
||
/* the next op if x2=0, else handles trap normally */
|
||
|
||
#endif
|
||
.option pop
|
||
.endm
|
||
|
||
|
||
/**** This is a helper macro that causes harts to transition from ****/
|
||
/**** M-mode to a lower priv mode at the instruction that follows ****/
|
||
/**** the macro invocation. Legal params are VS,HS,VU,HU,S,U. ****/
|
||
/**** The H,U variations leave V unchanged. This uses T4 only. ****/
|
||
/**** NOTE: this MUST be executed in M-mode. Precede with GOTO_MMODE ****/
|
||
/**** FIXME - SATP & VSATP must point to the identity map page table ****/
|
||
|
||
#define HSmode 0x9
|
||
#define HUmode 0x8
|
||
#define VUmode 0x4
|
||
#define VSmode 0x5
|
||
#define Smode 0x1
|
||
#define Umode 0x0
|
||
|
||
.macro RVTEST_GOTO_LOWER_MODE LMODE
|
||
.option push
|
||
.option norvc
|
||
|
||
// first, clear MSTATUS.PP (and .MPV if it will be changed_
|
||
// then set them to the values that represent the lower mode
|
||
#if (XLEN==32)
|
||
.if ((\LMODE\()==VUmode) | (\LMODE\()==VSmode))
|
||
csrsi CSR_MSTATUS, MSTATUS_MPV /* set V */
|
||
.elseif ((\LMODE\()==HUmode) | (\LMODE\()==HSmode))
|
||
csrci CSR_MSTATUS, MSTATUS_MPV /* clr V */
|
||
.endif /* lv V unchged for S or U */
|
||
|
||
LI( T4, MSTATUS_MPP)
|
||
csrc CSR_MSTATUS, T4 /* clr PP always */
|
||
|
||
.if ((\LMODE\()==VSmode) || (\LMODE\()==HSmode) || (\LMODE\()==Smode))
|
||
LI( T4, MPP_SMODE) /* val for Smode */
|
||
csrs CSR_MSTATUS, T4 /* set in PP */
|
||
.endif
|
||
// do the same if XLEN=64
|
||
#else /* XLEN=64, maybe 128? FIXME for 128 */
|
||
.if ((\LMODE\()==Smode) || (\LMODE\()==Umode)) /* lv V unchanged here */
|
||
LI( T4, MSTATUS_MPP) /* but always clear PP */
|
||
.else
|
||
LI( T4, (MSTATUS_MPP | MSTATUS_MPV)) /* clr V and P */
|
||
.endif
|
||
csrc CSR_MSTATUS, T4 /* clr PP to umode & maybe Vmode */
|
||
|
||
.if (!((\LMODE\()==HUmode) || (\LMODE\()==Umode))) /* lv pp unchged, v=0 or unchged */
|
||
.if (\LMODE\()==VSmode)
|
||
LI( T4, (MPP_SMODE | MSTATUS_MPV)) /* val for pp & v */
|
||
.elseif ((\LMODE\()==HSmode) || (\LMODE\()==Smode))
|
||
LI( T4, (MPP_SMODE)) /* val for pp only */
|
||
.else /* only VU left; set MPV only */
|
||
li T4, 1 /* optimize for single bit */
|
||
slli T4, T4, 32+MPV_LSB /* val for v only */
|
||
.endif
|
||
csrs CSR_MSTATUS, T4 /* set correct mode and Vbit */
|
||
.endif
|
||
#endif
|
||
csrr sp, CSR_MSCRATCH /* ensure sp points to Mmode datae area */
|
||
/**** mstatus MPV and PP now set up to desired mode ****/
|
||
/**** set MEPC to mret+4; requires relocating the pc ****/
|
||
.if (\LMODE\() == Vmode) // get trapsig_ptr & init val up 2 save areas (M<-S<-V)
|
||
LREG T1, code_bgn_off + 2*sv_area_sz(sp)
|
||
.elseif (\LMODE\() == Smode || \LMODE\() == Umode) // get trapsig_ptr & init val up 1 save areas (M<-S)
|
||
LREG T1, code_bgn_off + 1*sv_area_sz(sp)
|
||
.else // get trapsig ptr & init val for this Mmode, (M)
|
||
LREG T1, code_bgn_off + 0*sv_area_sz(sp)
|
||
.endif
|
||
LREG T4, code_bgn_off(sp)
|
||
sub T1, T1,T4 /* calc addr delta between this mode (M) and lower mode code */
|
||
addi T1, T1, 4*WDBYTSZ /* bias by # ops after auipc continue executing at mret+4 */
|
||
auipc T4, 0
|
||
add T4, T4, T1 /* calc addr after mret in LMODE's VM */
|
||
csrrw T4, CSR_MEPC, T4 /* set rtn addr to mret+4 in LMODE's VM */
|
||
mret /* transition to desired mode */
|
||
.option pop
|
||
.endm // end of RVTEST_GOTO_LOWER_MODE
|
||
|
||
//==============================================================================
|
||
// Helper macro to set defaults for undefined interrupt set/clear
|
||
// macros. This is used to populated the interrupt vector table.
|
||
// These are only used during interrupt testing, so it is safe to
|
||
// define them as empty macros if and only if that particular interrupt
|
||
// isn't being tested
|
||
//==============================================================================
|
||
//****************************************************************
|
||
#define RVTEST_DFLT_INT_HNDLR j cleanup_epilogs
|
||
//Mmode interrupts
|
||
#ifndef RVMODEL_SET_MSW_INT
|
||
//.warning "RVMODEL_SET_MSW_INT not defined. Executing this will end test. Define an empty macro to suppress this warning"
|
||
#define RVMODEL_SET_MSW_INT RVTEST_DFLT_INT_HNDLR
|
||
#endif
|
||
#ifndef RVMODEL_CLR_MSW_INT
|
||
//.warning "RVMODEL_CLR_MSW_INT not defined. Executing this will end test. Define an empty macro to suppress this warning"
|
||
#define RVMODEL_CLR_MSW_INT RVTEST_DFLT_INT_HNDLR
|
||
#endif
|
||
#ifndef RVMODEL_CLR_MTIMER_INT
|
||
//.warning "RVMODEL_CLR_MTIMER_INT not defined. Executing this will end test. Define an empty macro to suppress this warning"
|
||
#define RVMODEL_CLR_MTIMER_INT RVTEST_DFLT_INT_HNDLR
|
||
#endif
|
||
#ifndef RVMODEL_CLR_MEXT_INT
|
||
//.warning "RVMODEL_CLR_MEXT_INT not defined. Executing this will end test. Define an empty macro to suppress this warning"
|
||
#define RVMODEL_CLR_MEXT_INT RVTEST_DFLT_INT_HNDLR
|
||
#endif
|
||
//Smode interrupts
|
||
#ifndef RVMODEL_SET_SSW_INT
|
||
//.warning "RVMODEL_SET_SSW_INT not defined. Executing this will end test. Define an empty macro to suppress this warning"
|
||
#define RVMODEL_SET_SSW_INT RVTEST_DFLT_INT_HNDLR
|
||
#endif
|
||
#ifndef RVMODEL_CLR_SSW_INT
|
||
//.warning "RVMODEL_CLR_SSW_INT not defined. Executing this will end test. Define an empty macro to suppress this warning"
|
||
#define RVMODEL_CLR_SSW_INT RVTEST_DFLT_INT_HNDLR
|
||
#endif
|
||
#ifndef RVMODEL_CLR_STIMER_INT
|
||
//.warning "RVMODEL_CLR_STIMER_INT not defined. Executing this will end test. Define an empty macro to suppress this warning"
|
||
#define RVMODEL_CLR_STIMER_INT RVTEST_DFLT_INT_HNDLR
|
||
#endif
|
||
#ifndef RVMODEL_CLR_SEXT_INT
|
||
//.warning "RVMODEL_CLR_SEXT_INT not defined. Executing this will end test. Define an empty macro to suppress this warning"
|
||
#define RVMODEL_CLR_SEXT_INT RVTEST_DFLT_INT_HNDLR
|
||
#endif
|
||
//Vmode interrupts
|
||
#ifndef RVMODEL_SET_VSW_INT
|
||
//.warning "RVMODEL_SET_VSW_INT not defined. Executing this will end test. Define an empty macro to suppress this warning"
|
||
#define RVMODEL_SET_VSW_INT RVTEST_DFLT_INT_HNDLR
|
||
#endif
|
||
#ifndef RVMODEL_CLR_VSW_INT
|
||
//.warning "RVMODEL_CLR_VSW_INT not defined. Executing this will end test. Define an empty macro to suppress this warning"
|
||
#define RVMODEL_CLR_VSW_INT RVTEST_DFLT_INT_HNDLR
|
||
#endif
|
||
#ifndef RVMODEL_CLR_VTIMER_INT
|
||
//.warning "RVMODEL_CLR_VTIMER_INT not defined. Executing this will end test. Define an empty macro to suppress this warning"
|
||
#define RVMODEL_CLR_VTIMER_INT RVTEST_DFLT_INT_HNDLR
|
||
#endif
|
||
#ifndef RVMODEL_CLR_VEXT_INT
|
||
//.warning "RVMODEL_CLR_VEXT_INT not defined. Executing this will end test. Define an empty macro to suppress this warning"
|
||
#define RVMODEL_CLR_VEXT_INT RVTEST_DFLT_INT_HNDLR
|
||
#endif
|
||
|
||
//==============================================================================
|
||
// This section defines macros used by these required macros:
|
||
// RVTEST_TRAP_PROLOG, RVTEST_TRAP_HANDLER, RVTEST_TRAP_EPILOG
|
||
// These are macros instead of inline because they need to be replicated per mode
|
||
// These are passed the privmode as an argument to properly rename labels
|
||
// The helper INSTANTIATE_MODE_MACRO actually handles the replication
|
||
//==============================================================================
|
||
|
||
.macro RVTEST_TRAP_PROLOG __MODE__
|
||
.option push
|
||
.option norvc
|
||
/******************************************************************************/
|
||
/**** this is a mode-configured version of the prolog, which either saves and */
|
||
/**** replaces xtvec, or saves and replaces the code located at xtvec if it */
|
||
/**** it xtvec isn't arbitrarily writable. If not writable, restore & exit */
|
||
/******************************************************************************/
|
||
|
||
/******************************************************************************/
|
||
/**** Prolog, to be run before any tests ****/
|
||
/**** #include 1 copy of this per mode in rvmodel_boot code? ****/
|
||
/**** ------------------------------------------------------------------- ****/
|
||
/**** if xTVEC isn't completely RW, then we need to change the code at its ****/
|
||
/**** target. The entire trap trampoline and mtrap handler replaces the ****/
|
||
/**** area pointed to by mtvec, after saving its original contents first. ****/
|
||
/**** If it isn't possible to fully write that area, restore and fail. ****/
|
||
/******************************************************************************/
|
||
|
||
// RVTEST_TRAP_PROLOG trap_handler_prolog; enter with T1..T6 available; define specific handler
|
||
// sp will immediately point to the current mode's save area and must not be touched
|
||
//NOTE: this is run in M-mode, so can't use aliased S,V CSR names
|
||
|
||
.global \__MODE__\()trampoline
|
||
//.global mtrap_sigptr
|
||
|
||
XCSR_VRENAME \__MODE__ //retarget XCSR names to this modes CSRs, separate V/S copies
|
||
|
||
LA( T1, \__MODE__\()tramptbl_sv) // get ptr to save area (will be stored in xSCRATCH)
|
||
//----------------------------------------------------------------------
|
||
init_\__MODE__\()scratch:
|
||
csrrw T3, CSR_XSCRATCH, T1 // swap xscratch with save area ptr (will be used by handler)
|
||
SREG T3, xscr_save_off(T1) // save old mscratch in xscratch_save
|
||
//----------------------------------------------------------------------
|
||
init_\__MODE__\()edeleg:
|
||
li T2, 0 // save and clear edeleg so we can exit to Mmode
|
||
.ifc \__MODE__ , V
|
||
csrrw T2, CSR_VEDELEG, T2 // special case: VS EDELEG available from Vmode
|
||
.else
|
||
.ifc \__MODE__ , M
|
||
#ifdef rvtest_strap_routine
|
||
csrrw T2, CSR_XEDELEG, T2 // this handles M mode save, but only if Smode exists
|
||
#endif
|
||
.else
|
||
//FIXME: if N-extension or anything like it is implemented, uncomment the following
|
||
// csrrw T2, CSR_XEDELEG, T2 // this handles S mode
|
||
.endif
|
||
.endif
|
||
SREG T2, xedeleg_sv_off(T1) // now do the save
|
||
//----------------------------------------------------------------------
|
||
init_\__MODE__\()satp:
|
||
.ifnc \__MODE__ , M // if S or VS mode **FIXME: fixed offset frm trapreg_sv?
|
||
LA( T4, rvtest_\__MODE__\()root_pg_tbl) // rplc xsatp w/ identity-mapped pg table
|
||
srli T4, T4, 12
|
||
#if (XLEN==32)
|
||
LI(T3, SATP32_MODE)
|
||
#else
|
||
LI(T3, (SATP64_MODE) & (SATP_MODE_SV39 << 60))
|
||
#endif
|
||
or T4, T4, T3
|
||
csrrw T4, CSR_XSATP, T4
|
||
SREG T4, xsatp_sv_off(T1)
|
||
.endif
|
||
//----------------------------------------------------------------------
|
||
init_\__MODE__\()tvec:
|
||
// LA( T4, \__MODE__\()trampoline) //this is a code-relative pointer
|
||
// ADDI( T3, T4, actual_tramp_sz)
|
||
// SREG T3, tentry_addr(T1) // initialize to original common entry point
|
||
csrr T3, CSR_XTVEC
|
||
SREG T3, xtvec_sav_off(T1) // save orig mtvec+mode in tvec_save
|
||
andi T2, T3, WDBYTMSK // extract mode bits
|
||
LREG T4, tentry_addr(T1)
|
||
addi T4, T4, -actual_tramp_sz// load trampoline addr (common entry pt) avoiding an LA()
|
||
or T2, T4, T2 // merge .mode & tramp ptr and store to both XTVEC, tvec_new
|
||
SREG T2, xtvec_new_off(T1)
|
||
csrw CSR_XTVEC, T2 // write xtvec with trap_trampoline+mode, so trap will go to the trampoline
|
||
|
||
csrr T5, CSR_XTVEC // now read new_mtval back & make sure we could write it
|
||
#ifndef HANDLER_TESTCODE_ONLY
|
||
beq T5, T2, rvtest_\__MODE__\()prolog_done // if mtvec==trap_trampoline, mtvec is writable, continue
|
||
#endif
|
||
csrw CSR_XTVEC, T3 // xTVEC not completely writable, restore old value & exit if uninitialized
|
||
beqz T3, abort\__MODE__\()test
|
||
SREG T3, xtvec_new_off(T1) // else update tvect_new with orig mtvec
|
||
|
||
/*****************************************************************/
|
||
/**** fixed mtvec, can't move it so move trampoline instead ****/
|
||
/**** T1=tramp sv, T2=orig tvec, T3=sv end, T4=tramp ****/
|
||
/*****************************************************************/
|
||
|
||
init_\__MODE__\()tramp: /**** copy trampoline at mtvec tgt; T4->T2->T1 T3=end of save ****/
|
||
andi T2, T3, ~WDBYTMSK // calc bgn of orig tramp area by clring mode bits
|
||
addi T3, T2, actual_tramp_sz // calc end of orig tramp area (+4 maybe so bldwd aligned)
|
||
// addi T1, T1, tramp_sv_off // calc bgn of tramp save area <--buggy!!
|
||
//----------------------------------------------------------------------
|
||
overwt_tt_\__MODE__\()loop: // now build new tramp table w/ local offsets
|
||
lw T6, 0(T2) // move original mtvec target to save area
|
||
sw T6, 0(T1)
|
||
lw T5, 0(T4) // move traphandler trampoline into orig mtvec target
|
||
sw T5, 0(T2)
|
||
lw T6, 0(T2) // rd it back to make sure it was written
|
||
bne T6, T5, endcopy_\__MODE__\()tramp // table isn't fully writable, restore and give up
|
||
#ifdef HANDLER_TESTCODE_ONLY
|
||
csrr T5, CSR_XSCRATCH // load trapreg_sv from scratch
|
||
addi T5, T5,256 // calculate some offset into the save area
|
||
bgt T5, T1, endcopy_\__MODE__\()tramp // and pretend if couldnt be written
|
||
#endif
|
||
addi T2, T2, WDBYTSZ // next tvec inst. index
|
||
addi T1, T1, WDBYTSZ // next save inst. index
|
||
addi T4, T4, WDBYTSZ // next tramp inst. index
|
||
bne T3, T2, overwt_tt_\__MODE__\()loop // haven't reached end of save area, loop
|
||
//----------------------------------------------------------------------
|
||
endcopy_\__MODE__\()tramp: // vector table not writeable, restore
|
||
RVMODEL_FENCEI // By default it is defined as nop. See the definition above
|
||
csrr T1, CSR_XSCRATCH // reload trapreg_sv from scratch
|
||
SREG T2, trampend_off(T1) // save copy progress; used to restore orig tramp
|
||
SREG T4, tentry_addr(T1) // this is common entry point address, end of orig trampoline
|
||
beq T3,T2, rvtest_\__MODE__\()prolog_done //full loop, don't exit
|
||
abort\__MODE__\()test:
|
||
LA( T6, exit_\__MODE__\()cleanup) // trampoline rplc failure **FIXME: precalc& put into savearea?
|
||
jalr x0, T6 // this branch may be too far away, so longjmp
|
||
|
||
rvtest_\__MODE__\()prolog_done:
|
||
|
||
.option pop
|
||
.endm //end of PROLOG
|
||
/*******************************************************************************/
|
||
/*************** end of prolog macro ************/
|
||
/*******************************************************************************/
|
||
|
||
.macro RVTEST_TRAP_HANDLER __MODE__
|
||
.option push
|
||
.option rvc // temporarily allow compress to allow c.nop alignment
|
||
.align MTVEC_ALIGN // ensure that a trampoline is on a model defined or reasonable boundary
|
||
.option pop
|
||
|
||
/**********************************************************************/
|
||
/**** This is the entry point for all x-modetraps, vectored or not.****/
|
||
/**** xtvec should either point here, or trampoline code does and ****/
|
||
/**** trampoline code was copied to whereever xtvec pointed to. ****/
|
||
/**** At entry, xscratch will contain a pointer to a scratch area. ****/
|
||
/**** This is an array of branches at 4B intevals that spreads out ****/
|
||
/**** to an array of 12B xhandler stubs for specd int causes, and ****/
|
||
/**** to a return for anything above that (which causes a mismatch)****/
|
||
/**********************************************************************/
|
||
|
||
XCSR_RENAME \__MODE__ //retarget XCSR names to this modes CSRs
|
||
|
||
.global \__MODE__\()trampoline // define the label and make it available
|
||
.global common_\__MODE__\()entry
|
||
.option push
|
||
.option norvc
|
||
|
||
\__MODE__\()trampoline: //****GLOBAL:*****
|
||
.set value, 0
|
||
.rept NUM_SPECD_INTCAUSES // located at each possible int vectors
|
||
j trap_\__MODE__\()handler+ value // offset < +/- 1MB
|
||
.set value, value + 12 // length of xhandler trampoline spreader code
|
||
.endr
|
||
|
||
.rept XLEN-NUM_SPECD_INTCAUSES // fill at each impossible entry
|
||
j rvtest_\__MODE__\()endtest // end test if this happens
|
||
.endr
|
||
|
||
/*********************************************************************/
|
||
/**** this is spreader stub array; it saves enough info (sp & ****/
|
||
/**** vec-offset) to enable branch to common routine to save rest ****/
|
||
/*********************************************************************/
|
||
/**** !!CSR_xSCRATCH is preloaded w/ xtrapreg_sv in init_xscratch:****/
|
||
|
||
trap_\__MODE__\()handler: // on exit sp swapped w/ save ptr, T6 is vector addr
|
||
.rept NUM_SPECD_INTCAUSES
|
||
csrrw sp, CSR_XSCRATCH, sp // save sp, replace w/trapreg_sv regtmp save ptr
|
||
SREG T6, trap_sv_off+6*REGWIDTH(sp) // save T6 in temp save area offset 6
|
||
jal T6, common_\__MODE__\()handler // jmp to common code, saving vector in T6
|
||
.endr
|
||
|
||
/*********************************************************************/
|
||
/**** common code for all ints & exceptions, will fork to handle ****/
|
||
/**** each separately. The common handler first stores trap mode+ ****/
|
||
/**** vector, & mcause signatures. Most traps have 4wd sigs, but ****/
|
||
/**** sw and timer ints only store 3 of the 4, & some hypervisor ****/
|
||
/**** traps will set store 6 ops ****/
|
||
/**** sig offset Exception ExtInt SWInt TimerInt ****/
|
||
/**** 0: <--------------------- Vect+mode ----------> ****/
|
||
/**** 4: <---------------------- xcause -------------> ****/
|
||
/**** 8: xepc <------------- xip --------------> ****/
|
||
/**** 12: tval IntID <---- x ----------------> ****/
|
||
/**** 16: tval2/x * <-------------- x ----------------> ****/
|
||
/**** 20: tinst/x * <-------------- x ----------------> ****/
|
||
/**** * only loaded for Mmode traps when hypervisor implemented ****/
|
||
/*********************************************************************/
|
||
/* in general, CSRs loaded in T2, addresses into T3 */
|
||
|
||
//If we can distinguish between HS and S mode, we can share S and V code.
|
||
//except for prolog code which needs to initialize CSRs, and the save area
|
||
//To do this, we need to read one of the CSRs (e.g. xSCRATCH) and compare
|
||
//it to either Strapreg_sv or Vtrapreg_sv to determine which it is.
|
||
|
||
common_\__MODE__\()handler: // enter with vector addr in T6 (orig T6 is at offset 6*REGWIDTH)
|
||
SREG T5, trap_sv_off+5*REGWIDTH(sp) // x30 save remaining regs, starting with T5
|
||
csrrw T5, CSR_XSCRATCH, sp // restore ptr to reg sv area, and get old sp
|
||
SREG T5, trap_sv_off+7*REGWIDTH(sp) // save old sp
|
||
LREG T5, tentry_addr(sp) // get the address of the common entry point
|
||
jr T5 // needed if trampoline gets moved elsewhere, else it's effectively a noop
|
||
|
||
common_\__MODE__\()entry:
|
||
SREG T4, trap_sv_off+4*REGWIDTH(sp) //x29
|
||
SREG T3, trap_sv_off+3*REGWIDTH(sp) //x28
|
||
SREG T2, trap_sv_off+2*REGWIDTH(sp) //x7
|
||
SREG T1, trap_sv_off+1*REGWIDTH(sp) //x6 save other temporaries
|
||
|
||
//spcl case handling for ECALL in GOTO_MMODE mode,) ****tests can't use ECALL T2=0****
|
||
spcl_\__MODE__\()2mmode_test:
|
||
csrr T5, CSR_XCAUSE
|
||
addi T4, T5, -8 // is cause 8..11? Mmode should avoid ECALL 0
|
||
andi T4, T4, -4 // NOTE: cause 10 is RSVD. Sail will diverge, but buggy anyway
|
||
bnez T4, \__MODE__\()trapsig_ptr_upd // no, not in special mode, just continue
|
||
LREG T2, trap_sv_off+7*REGWIDTH(sp) // get test x2 (which is sp, which has been saved in the trap_sv area
|
||
beqz T2, rtn2mmode // spcl code 0 in T2 means spcl ECALL goto_mmode, just rtn after ECALL
|
||
//------pre-update trap_sig pointer so handlers can themselves trap-----
|
||
\__MODE__\()trapsig_ptr_upd: // calculate entry size based on int vs. excpt, int type, and h mode
|
||
li T2, 4*REGWIDTH // standard entry length
|
||
bgez T5, \__MODE__\()xcpt_sig_sv // Keep std length if cause is an exception for now (MSB==0)
|
||
\__MODE__\()int_sig_sv:
|
||
slli T3, T5, 1 // remove MSB, cause<<1
|
||
addi T3, T3, -(IRQ_M_TIMER)<<1 // is cause (w/o MSB) an extint or larger? ( (cause<<1) > (8<<1) )?
|
||
bgez T3, \__MODE__\()trap_sig_sv // yes, keep std length
|
||
li T2, 3*REGWIDTH // no, its a timer or swint, overrride preinc to 3*regsz
|
||
j \__MODE__\()trap_sig_sv
|
||
|
||
/**********************************************************************/
|
||
/**** FIXME: could this simply instantiate RVMODEL_HALT instead of ****/
|
||
/**** branching to it? might need to instantiate GOTO_MMODE here ****/
|
||
/**** to take care of VM issues that RVMODEL_HALT can't deal with ****/
|
||
/**********************************************************************/
|
||
|
||
rvtest_\__MODE__\()endtest: // target may be too far away, so longjmp
|
||
LA( T1, rvtest_\__MODE__\()end) // FIXME: must be identity mapped if its a VA
|
||
jalr x0, T1
|
||
|
||
\__MODE__\()xcpt_sig_sv:
|
||
.ifc \__MODE__ , M // exception case, don't adjust if hypervisor mode disabled
|
||
csrr T1, CSR_MISA
|
||
slli T1, T1, XLEN-8 // shift H bit into msb
|
||
bgez T1, \__MODE__\()trap_sig_sv // no hypervisor mode, keep std width
|
||
li T2, 6*REGWIDTH // Hmode implemented & Mmode trap, override preinc to be 6*regsz
|
||
.endif
|
||
|
||
\__MODE__\()trap_sig_sv:
|
||
// This replaces an LA(rvtest_trap_sig) calculating initial_Xtrap_sigptr +
|
||
// + (Mtrap_sigptr-initial_Mtrap-sigptr)
|
||
// The delta between Mmode_sigptr and Xmode_sigptr are constants
|
||
// Xtrap_sigptr (current priv mode) are in the save area ponted to by sp
|
||
// ****FIXME - this breaks if the signature area cross a page boundary and the mapping isn't contiguous
|
||
|
||
.set sv_area_off, (-0*sv_area_sz) // get trapsig ptr val offset for Mmode, (M)
|
||
.ifc \__MODE__ , S
|
||
.set sv_area_off, (-1*sv_area_sz) // get trapsig_ptr val up 1 save areas (M<-S)
|
||
.else
|
||
.ifc \__MODE__ , V
|
||
.set sv_area_off, (-2*sv_area_sz) // get trapsig ptr val up 2 save areas, (M<-S<-V))
|
||
.endif
|
||
.endif
|
||
//------this should be atomic-------------------------------------
|
||
LREG T1, trapsig_ptr_off+sv_area_off(sp)
|
||
add T4, T1, T2
|
||
SREG T4, trapsig_ptr_off+sv_area_off(sp)
|
||
|
||
//------end atomic------------------------------------------------
|
||
// convert mtrap_sigptr to curr_mode trap_sigptr
|
||
LREG T3, sig_bgn_off+sv_area_off(sp) // load Mmode sig begin addr
|
||
sub T1, T1, T3 // cvt to offset from sig begin
|
||
LREG T3, sig_bgn_off+ 0(sp) // load <currmode>sig begin addr
|
||
add T1, T1, T3 // add offset from sig_begin to curr sig_begin addr
|
||
|
||
LREG T3, xtvec_new_off(sp) // get pointer to actual tramp table
|
||
//----------------------------------------------------------------
|
||
|
||
/*************************************************************************/
|
||
/**** This first entry has this format. ****/
|
||
/**** The #entries is useful for parsing and is really #bytes/entry ****/
|
||
/**** +---------------+-----------+----------+------+ ****/
|
||
/**** | XLEN-1 16 | 15 6 | 5 2 | 1 0 | ****/
|
||
/**** +---------------+-----------+----------+------+ ****/
|
||
/**** | zeroes | vector | #entries | mode | ****/
|
||
/**** +---------------+-----------+----------+------+ ****/
|
||
/*************************************************************************/
|
||
|
||
sv_\__MODE__\()vect: // **FIXME?: breaks if tramp crosses pg && MMU enabled
|
||
sub T6, T6, T3 // cvt spreader-addr to vector offset fm top of tramptable
|
||
slli T6, T6, 4 // make room for 4 bits; vector is 10b max **FIXME: broken for SV64!)
|
||
or T6, T6, T2 // insert entry size into bits 5:2
|
||
addi T6, T6, \__MODE__\()MODE_SIG // insert mode# into 1:0
|
||
SREG T6, 0*REGWIDTH(T1) // save 1st sig value, (vec-offset, entrysz, trapmode)
|
||
//----------------------------------------------------------------
|
||
sv_\__MODE__\()cause:
|
||
SREG T5, 1*REGWIDTH(T1) // save 2nd sig value, (mcause)
|
||
//----------------------------------------------------------------
|
||
bltz T5, common_\__MODE__\()int_handler // split off if this is an interrupt
|
||
|
||
/*******************************************************************************/
|
||
/**** This is exception specific code, storing relative mepc & tval sigs ****/
|
||
/**** The mepc sig is relocated by data or code start, depending on whether ****/
|
||
/**** on whether it's in the data area or not, & restored bumped by 2..6B ****/
|
||
/**** depending op alignment so trapped op isn't re-executed ****/
|
||
/*******************************************************************************/
|
||
|
||
common_\__MODE__\()excpt_handler:
|
||
|
||
//********************************************************************************
|
||
// calculate the delta between trap mode and handler mode sv areas & add to sp
|
||
// This code calculates this table: (H-ext is determined by Vtrap_routine variable
|
||
|
||
// +-------+-------+-------+-------+---------+
|
||
// | Hndlr | vMPP | M.GVA | H-ext | sv area |
|
||
// | Mode | =3 | | | delta |
|
||
// +-------+------+-------+-------+---------+
|
||
// | M | 0 | 1 | 1 | 2 |
|
||
// | M | 0 | 0 | x | 1 |
|
||
// | M | 1 | 0 | x | 0 |
|
||
// | M | x | 1 | 0 | illegal |
|
||
// | M | 1 | 1 | x | illegal |
|
||
// +-------+------+-------+-------+---------+
|
||
// | | | H.GVA | H-ext | sv area |
|
||
// +-------+------+-------+-------+---------+
|
||
// | S/HS | 0* | 1 | 1 | 1 |
|
||
// | S/HS | 0* | 0 | 1 | 0 |
|
||
// | S/HS | 0* | * | 0 | 0 |
|
||
// +-------+------+-------+-------+---------+
|
||
// | | | noGVA | H-ext | sv area | |
|
||
// +-------+------+-------+-------+---------+
|
||
// | VS | 0* | - | 1* | 0 |
|
||
// +-------+------+-------+-------+---------+
|
||
// where vMPP is
|
||
// +-------+-------+-------+-------+------+
|
||
// | Hndlr | | | sved | |
|
||
// | Mode | MPRV | MPP=3 | MPP=3 | vMPP |
|
||
// +-------+-------+-------+-------+------+
|
||
// | M | 0 | 1 | x | 1 |
|
||
// | M | 0 | 0 | x | 0 |
|
||
// | M | 1 | 1 | 0 | 1 |
|
||
// | M | 1 | 1 | 1 | 0 |
|
||
// | M | 1 | 0 | x |illegl|
|
||
// +-------+-------+-------+-------+------+
|
||
// |S/HS/VS| 0* | 1* | x | 1 |
|
||
// +-------+-------+-------+-------+------+
|
||
|
||
// * means can't be read, but must or would have value indicated
|
||
// all other values are illegal
|
||
// lvs result in T4 to be used during relocation, (so doesn't touch sp)
|
||
// can use T3, T6 because relocation will overwrite them
|
||
//********************************************************************************
|
||
|
||
// create an index from these values: vMPP, x.GVA , H-ext
|
||
// where vMPP = m.PRV ? svedMPP : m.MPP & svedMPP
|
||
|
||
.ifc \__MODE__ , M
|
||
csrr T6, CSR_MSTATUS
|
||
LREG T4, mpp_sv_off(sp) /* saved MPP, overwritten if MPRV=1 */
|
||
// extract MPRV into bit0. Note that only Mmode cares; all other modes can have garbage
|
||
slli T3, T6, XLEN-MPRV_LSB-1 /* put MPRV into sign bit & test */
|
||
bge T3, x0, 1f
|
||
and T4, T4, T6 /* MPP=11 if MPRV=0, so AND w/ prevMPP */
|
||
1:
|
||
// FIXME: add code here to end test if MPRV=1 & MPP<3
|
||
// e.g. rt justify, extract, add mprv, end if <4
|
||
// now convert 2 bit xMPP field into a single bit 2
|
||
srli T4, T4, MPP_LSB /* now cvt MPP (in its natural position)*/
|
||
andi T4, T4, 3 /* to a single bit in bit2 iff ==3 */
|
||
addi T4, T4, 1
|
||
andi T4, T4, 4
|
||
// extract GVA into bit 1
|
||
#if (rvtest_vtrap_routine)
|
||
#if (XLEN==32)
|
||
csrr T3, CSR_MSTATUSH /* get CSR with GVA bit, but only H-ext */
|
||
srli T3, T3, GVA_LSB-1 /* reposition RV32 mstatush into bit1 */
|
||
#else
|
||
srli T3, T6, GVA_LSB-1+32 /* reposition RV32 mstatus into bit1 */
|
||
#endif
|
||
andi T3, T3, 1<<1
|
||
or T4, T4, T3 /* extract GVA in bit1, insert into msk */
|
||
// put H-extension implemented into bit 0
|
||
ori T4, T4, 1 /* set LSB if H-ext present */
|
||
//****FIXME: this doesn't work if misa.H is RW but set to zero ****/
|
||
#endif
|
||
// chk for illegal combination
|
||
LI( T6, 0x3B) /*lgl msk(vMPP,GVA,H)= 011,00x,10x=0x3B */
|
||
srl T6, T6, T4
|
||
andi T6, T6, 1
|
||
beq T6, x0, rvtest_\__MODE__\()endtest /* illegal combination */
|
||
//determine sv offset multiplier
|
||
LI( T6, sv_area_sz)
|
||
andi T3, T4, 2
|
||
srli T3, T3, 1 /* extract GVA & move to bito cases */
|
||
srl T6, T6, T3 /* mul by 2 if GVAelse mul by 1 */
|
||
slli T3, T4, XLEN-3
|
||
srai T3, T3, XLEN-1 /* sg ext vMPP, user it to clr delta */
|
||
and T6, T6, T3
|
||
|
||
.else // do it again, but from VS or HS mode
|
||
.ifc \__MODE__ , S
|
||
// vMPP cannot be 11 because you it cannot handle at a lower mode than trap mode
|
||
// MPRV cannot be 1 because that only applies to Mmode
|
||
// GVA can only exist if there is H-ext
|
||
#if rvtest_vtrap_routine
|
||
LI( T6, sv_area_sz)
|
||
csrr T3, CSR_HSTATUS /* get CSR with GVA bit, but only H-ext */
|
||
slli T3, T3, XLEN-1-GVA_LSB /* sign extend rt justified GVA bit */
|
||
slri T3, T3, XLEN-1
|
||
and T4, T3, T6 /* clr delta if GVA=0 */
|
||
#else
|
||
li T4,0 /* clr delta if no H-ext */
|
||
#endif
|
||
.else // handler is in VS mode, vtrap_routine must be defined, offset must be 0
|
||
li T4,0
|
||
.endif
|
||
.endif
|
||
|
||
//********************************************************************************
|
||
|
||
vmem_adj_\__MODE__\()epc:
|
||
add T4, T4, sp /* calc address of correct sv_area */
|
||
csrr T2, CSR_XEPC /* T4 now pts to trapping sv_area mode */
|
||
|
||
LREG T3, vmem_bgn_off(T4) // see if epc is in the vmem area
|
||
LREG T6, vmem_seg_siz(T4)
|
||
add T6, T6, T3 // construct vmem seg end
|
||
bgeu T2, T6, code_adj_\__MODE__\()epc// epc > rvtest_vmem_end, try data adj
|
||
bgeu T2, T3, adj_\__MODE__\()epc// epc >=rvtest_vmem_begin, adj and save
|
||
|
||
code_adj_\__MODE__\()epc:
|
||
LREG T3, code_bgn_off(T4) // see if epc is in the code area
|
||
LREG T6, code_seg_siz(T4)
|
||
add T6, T6, T3 // construct code seg end
|
||
bgeu T2, T6, data_adj_\__MODE__\()epc// epc > rvtest_code_end, try data adj
|
||
bgeu T2, T3, adj_\__MODE__\()epc// epc >=rvtest_code_begin, adj and save
|
||
|
||
data_adj_\__MODE__\()epc:
|
||
LREG T3, data_bgn_off(T4) // see if epc is in the data area
|
||
LREG T6, data_seg_siz(T4)
|
||
add T6, T6, T3 // construct data seg end
|
||
bgeu T2, T6, cleanup_epilogs // mepc > rvtest_code_end, (outside data seg), abort
|
||
bltu T2, T3, cleanup_epilogs // mepc < rvtest_code_begin (outside data seg), abort
|
||
|
||
adj_\__MODE__\()epc:
|
||
sub T3, T2, T3 // Offset adjustment
|
||
|
||
sv_\__MODE__\()epc:
|
||
SREG T3, 2*REGWIDTH(T1) // save 3rd sig value, (rel mepc) into trap sig area
|
||
|
||
adj_\__MODE__\()epc_rtn: // adj mepc so there is at least 4B of padding after op
|
||
andi T6, T2, ~WDBYTMSK // adjust mepc to prev 4B alignment (if 2B aligned)
|
||
addi T6, T6, 2*WDBYTSZ // adjust mepc so it skips past op, has padding & 4B aligned
|
||
csrw CSR_XEPC, T6 // restore adjusted value, w/ 2,4 or 6B of padding
|
||
|
||
/****WARNING needs updating when insts>32b are ratified, only 4 or 6B of padding;
|
||
for 64b insts, 2B or 4B of padding ****/
|
||
|
||
/******************************************************************************/
|
||
/* Relocate mtval if it’s an addr (by sig, data or code regions) else by zero */
|
||
/* error if exception address isn't inside code, data or signature segments */
|
||
/* Enter with rvtest_code_begin (which is start of actual test) in T3 */
|
||
/* FUTURE FIXME: this may need to be updated to handle 48 or 64b opcodes */
|
||
/* This uses offset sp in T4 from epc relocation */
|
||
/******************************************************************************/
|
||
|
||
/**** FIXME: if in Mmode and mode!=bare & MPRV=1, then T4 be altered to point to
|
||
the mode of the mstatus.mpp that is stored in Xtrampend_sv ****/
|
||
|
||
csrr T2, CSR_XTVAL
|
||
|
||
chk_\__MODE__\()tval:
|
||
andi T5, T5, EXCPT_CAUSE_MSK // ensures shift amt will be within range
|
||
LI( T3, SET_REL_TVAL_MSK) // now check if code or data (or sig) region adjustment
|
||
srl T3, T3, T5 // put mcause bit# into LSB
|
||
slli T3, T3, XLEN-1 // put mcause bit# into MSB
|
||
bge T3, x0, sv_\__MODE__\()tval // if MSB=0, no adj, sv to ensure tval was cleared
|
||
|
||
vmem_adj_\__MODE__\()tval: /* T4 still points to sv area of trapping mode */
|
||
LREG T3, vmem_bgn_off(T4) // fetch sig_begin addr
|
||
LREG T6, vmem_seg_siz(T4)
|
||
add T6, T6, T3 // construct vmem seg end
|
||
bgeu T2, T6, sig_adj_\__MODE__\()tval// tval > rvtest_sig_end, chk code seg
|
||
bgeu T2, T3, adj_\__MODE__\()tval// tval >=rvtest_sig_begin, adj & save
|
||
|
||
sig_adj_\__MODE__\()tval:
|
||
LREG T3, sig_bgn_off(T4) // fetch sig_begin addr
|
||
LREG T6, sig_seg_siz(T4)
|
||
add T6, T6, T3 // construct sig seg end
|
||
bgeu T2, T6, code_adj_\__MODE__\()tval// tval > rvtest_sig_end, chk code seg
|
||
bgeu T2, T3, adj_\__MODE__\()tval// tval >=rvtest_sig_begin, adj & save
|
||
|
||
code_adj_\__MODE__\()tval:
|
||
LREG T3, code_bgn_off(T4) // fetch code_begin addr
|
||
LREG T6, code_seg_siz(T4)
|
||
add T6, T6, T3 // construct code seg end
|
||
bgeu T2, T6, data_adj_\__MODE__\()tval// tval > rvtest_code_end, chk data seg
|
||
bgeu T2, T3, adj_\__MODE__\()tval// tval >=rvtest_code_begin, adj & save
|
||
|
||
data_adj_\__MODE__\()tval:
|
||
LREG T3, data_bgn_off(T4) // fetch data_begin addr
|
||
LREG T6, data_seg_siz(T4)
|
||
add T6, T6, T3 // construct data seg end
|
||
bgeu T2, T6, cleanup_epilogs // tval > rvtest_data_end, (outside data seg), abort
|
||
bltu T2, T3, cleanup_epilogs // tval < rvtest_data_begin (outside data seg), abort
|
||
|
||
adj_\__MODE__\()tval:
|
||
sub T3, T2, T3 // perform mtval adjust by either code, data, or sig position in T3
|
||
|
||
sv_\__MODE__\()tval:
|
||
SREG T3, 3*REGWIDTH(T1) // save 4th sig value, (rel tval)
|
||
|
||
skp_\__MODE__\()tval:
|
||
|
||
.ifc \__MODE__ , M
|
||
.ifdef __H_EXT__
|
||
csrr T2, CSR_MTVAL2 // **** FIXME: does this need reloc also? Its a guest phys addr
|
||
SREG T2, 4*REGWIDTH(T1) // store 5th sig value, only if mmode handler and VS mode exists
|
||
csrr T2, CSR_MTINST
|
||
SREG T2, 5*REGWIDTH(T1) // store 6th sig value, only if mmode handler and VS mode exists
|
||
.endif
|
||
.endif
|
||
|
||
chk_\__MODE__\()trapsig_overrun: // sv_area_off is defined above at Xtrap_sig_sv:
|
||
//This is the same code used at xtrap_sig_sv to get the shared copy of trap signature pointer
|
||
LREG T4, sv_area_off+trapsig_ptr_off(sp)
|
||
LREG T2, sv_area_off+sig_bgn_off(sp)
|
||
LREG T1, sv_area_off+sig_seg_siz(sp)
|
||
|
||
// now see if the pointer has overrun sig_end
|
||
add T1, T1, T2 // construct segment end address
|
||
bgtu T4, T1, cleanup_epilogs // abort test if pre-incremented value overruns
|
||
|
||
/**** vector to exception special handling routines ****/
|
||
li T2, int_hndlr_tblsz // offset of exception dispatch table base
|
||
j spcl_\__MODE__\()handler // jump to shared int/excpt spcl handling dispatcher
|
||
|
||
/**** common return code for both interrupts and exceptions ****/
|
||
resto_\__MODE__\()rtn: // restore and return
|
||
LREG T1, trap_sv_off+1*REGWIDTH(sp)
|
||
LREG T2, trap_sv_off+2*REGWIDTH(sp)
|
||
LREG T3, trap_sv_off+3*REGWIDTH(sp)
|
||
LREG T4, trap_sv_off+4*REGWIDTH(sp)
|
||
LREG T5, trap_sv_off+5*REGWIDTH(sp)
|
||
LREG T6, trap_sv_off+6*REGWIDTH(sp)
|
||
LREG sp, trap_sv_off+7*REGWIDTH(sp) // restore temporaries
|
||
|
||
\__MODE__\()RET // return to test, after padding adjustment (macro to handle case)
|
||
|
||
/***************************************************/
|
||
/**** This is the interrupt specific code. It ****/
|
||
/**** clears the int and saves int-specific CSRS****/
|
||
/***************************************************/
|
||
common_\__MODE__\()int_handler: // T1 has sig ptr, T5 has mcause, sp has save area
|
||
li T3, 1
|
||
//**FIXME** - make sure this is kept up-to-date with fast int extension and others
|
||
andi T2, T5, INT_CAUSE_MSK // clr INT & unarched arched bits (**NOTE expand if future extns use them)
|
||
sll T3, T3, T2 // create mask 1<<xcause **NOTE**: that MSB is ignored in shift amt
|
||
csrrc T4, CSR_XIE, T3 // read, then attempt to clear int enable bit??
|
||
csrrc T4, CSR_XIP, T3 // read, then attempt to clear int pend bit
|
||
sv_\__MODE__\()ip: // note: clear has no effect on MxIP
|
||
SREG T4, 2*REGWIDTH(T1) // save 3rd sig value, (xip)
|
||
|
||
li T2, 0 // index of interrupt dispatch table base
|
||
|
||
/**************************************************************/
|
||
/**** spcl int/excp dispatcher. T5 has mcause, T2 ****/
|
||
/**** holds int table (0) or excpt tbl (int_tbl_sz) offset ****/
|
||
/**** this loads an entry @ table_base+table_off+mcause<<8 ****/
|
||
/**** if entry=0, it should never be taken, error return ****/
|
||
/**** if entry is odd, it has cause<<1, skip disptaching ****/
|
||
/**** otherwise if even & >0, it is the handler address ****/
|
||
/**** There is an optional check that cause==mcause ****/
|
||
/**************************************************************/
|
||
|
||
spcl_\__MODE__\()handler: // case table branch to special handler code, depending on mcause
|
||
auipc T3, 0 // shortcut for LA(clrint_\__MODE__\()tbl) (might be 4 too large)
|
||
addi T3, T3, 15*4 // shortcut to avoid LA clrint_xtbl - this is might be 4 too large
|
||
add T3, T3, T2 // offset into the correct int/excpt dispatch table
|
||
slli T2, T5, 3 // index into 8b aligned dispatch entry and jump through it
|
||
add T3, T3, T2
|
||
andi T3, T3, -8 // make sure this is dblwd aligned, correct if it is 4 too large
|
||
LREG T3, 0(T3)
|
||
spcl_\__MODE__\()dispatch_hndling:
|
||
beqz T3, abort_tests // if address is 0, this is an error, exit test
|
||
slli T2, T3, XLEN-1 // look at LSB and dispatch if even
|
||
bge T2, x0, spcl_\__MODE__\()dispatch
|
||
srli T3, T3,1 //odd entry>0, remove LSB, normalizing to cause range
|
||
beq T5, T3, resto_\__MODE__\()rtn // case range matches, not an error, just noop
|
||
j abort_tests //FIXME: this needs to report an error somehow
|
||
|
||
spcl_\__MODE__\()dispatch:
|
||
jr T3 // not a default, jump to handler
|
||
|
||
/**** this is the table of interrupt clearing routine pointers ****/
|
||
/**** They could include special handlers ****/
|
||
/**** They default to model supplied RVMODEL macros above, ****/
|
||
/**** Note that the external interrupt routines are expected to ****/
|
||
/**** return with an interrupt ID in T3 ****/
|
||
|
||
.align 3 //make sure this is a dblwd boundary
|
||
clrint_\__MODE__\()tbl: //this code should only touch T2..T6
|
||
#ifdef rvtest_vtrap_routine // M/S/V/U
|
||
.dword 0 // int cause 0 is reserved, error
|
||
.dword \__MODE__\()clr_Ssw_int // int cause 1 Smode SW int
|
||
.dword \__MODE__\()clr_Vsw_int // int cause 2 Vmode SW int
|
||
.dword \__MODE__\()clr_Msw_int // int cause 3 Mmode SW int
|
||
//****************************************************************
|
||
.dword 0 // int cause 4 is reserved, error
|
||
.dword \__MODE__\()clr_Stmr_int // int cause 5 Smode Tmr int
|
||
.dword \__MODE__\()clr_Vtmr_int // int cause 6 Vmode Tmr int
|
||
.dword \__MODE__\()clr_Mtmr_int // int cause 7 Mmode Tmr int
|
||
//****************************************************************
|
||
.dword 0 // int cause 8 is reserved, error
|
||
.dword \__MODE__\()clr_Sext_int // int cause 9 Smode Ext int
|
||
.dword \__MODE__\()clr_Vext_int // int cause A Vmode Ext int
|
||
.dword \__MODE__\()clr_Mext_int // int cause B Mmode Ext int
|
||
//****************************************************************
|
||
#elseif rvtest_dtrap_routine // M/S/U only
|
||
.dword 0 // int cause 0 is reserved, error
|
||
.dword \__MODE__\()clr_Ssw_int // int cause 1 Smode SW int
|
||
.dword 1 // int cause 2 no Vmode
|
||
.dword \__MODE__\()clr_Msw_int // int cause 3 Mmode SW int
|
||
//****************************************************************
|
||
.dword 0 // int cause 4 is reserved, error
|
||
.dword \__MODE__\()clr_Stmr_int // int cause 5 Smode Tmr int
|
||
.dword 1 // int cause 6 no vmode
|
||
.dword \__MODE__\()clr_Mtmr_int // int cause 7 Mmode Tmr int
|
||
//****************************************************************
|
||
.dword 0 // int cause 8 is reserved, error
|
||
.dword \__MODE__\()clr_Sext_int // int cause 9 Smode Ext int
|
||
.dword 1 // int cause A no vmode
|
||
.dword \__MODE__\()clr_Mext_int // int cause B Mmode Ext int
|
||
//****************************************************************
|
||
#else // M(/U)mode only
|
||
.dword 0 // int cause 0 is reserved, error
|
||
.dword 1 // int cause 1 no Smode
|
||
.dword 1 // int cause 2 no Vmode
|
||
.dword \__MODE__\()clr_Msw_int // int cause 3 Mmode SW int
|
||
//****************************************************************
|
||
.dword 0 // int cause 4 is reserved, error
|
||
.dword 1 // int cause 5 no Smode
|
||
.dword 1 // int cause 6 no vmode
|
||
.dword \__MODE__\()clr_Mtmr_int // int cause 7 Mmode Tmr int
|
||
//****************************************************************
|
||
.dword 0 // int cause 8 is reserved, error
|
||
.dword 1 // int cause 9 no Smode
|
||
.dword 1 // int cause A no vmode
|
||
.dword \__MODE__\()clr_Mext_int // int cause B Mmode Ext int
|
||
//****************************************************************
|
||
#endif
|
||
.rept NUM_SPECD_INTCAUSES-0xC
|
||
.dword 1 // int cause c..NUM_SPECD_INTCAUSES is reserved, just return
|
||
.endr
|
||
.rept XLEN-NUM_SPECD_INTCAUSES
|
||
.dword 0 // impossible, quit test by jumping to epilogs
|
||
.endr
|
||
//****************************************************************
|
||
|
||
/**** this is the table of exception handling routine pointers, which ****/
|
||
/**** could include special handlers. They default to the rtn code ****/
|
||
excpt_\__MODE__\()hndlr_tbl: // handler code should only touch T2..T6 ****<<--must be speced!****
|
||
.set causeidx, 0
|
||
.rept NUM_SPECD_EXCPTCAUSES
|
||
.dword causeidx*2+1 // default, marked by @*cause+2just return
|
||
.set causeidx, causeidx+1
|
||
.endr
|
||
.rept XLEN-NUM_SPECD_EXCPTCAUSES
|
||
.dword 0 // impossible, quit test by jumping to epilogs
|
||
.endr
|
||
|
||
/**** These are invocations of the model supplied interrupt clearing macros ****/
|
||
/**** Note there is a copy per mode, though they could all be the same code ****/
|
||
/**** !!! Note: These macros should only touch T2..T6, unless test is aware ****/
|
||
/**** of other modified registers and knows they are dead- ****/
|
||
/**** but T1 must not be modified under any circumstances ****/
|
||
/**** !!! Note: the ext interrupt clearing macros must leave intID in T3 !!!****/
|
||
// **FIXME** : the spec needs to be updated with the per/mode versions, not just one
|
||
// **FIXME**: move these outside the handler so it can copied per mode using INSTANTIATE_MODE_MACRO
|
||
|
||
//------------- MMode----------------
|
||
\__MODE__\()clr_Msw_int: // int 3 default to just return if not defined
|
||
RVMODEL_CLR_MSW_INT
|
||
j resto_\__MODE__\()rtn
|
||
|
||
\__MODE__\()clr_Mtmr_int: // int 7 default to just return
|
||
RVMODEL_CLR_MTIMER_INT
|
||
j resto_\__MODE__\()rtn
|
||
|
||
\__MODE__\()clr_Mext_int: // inT11 default to just return after saving IntID in T3
|
||
RVMODEL_CLR_MEXT_INT
|
||
SREG T3, 3*REGWIDTH(T1) // save 4rd sig value, (intID)
|
||
j resto_\__MODE__\()rtn
|
||
|
||
//------------- SMode----------------
|
||
\__MODE__\()clr_Ssw_int: // int 1 default to just return if not defined
|
||
RVMODEL_CLR_SSW_INT
|
||
j resto_\__MODE__\()rtn
|
||
|
||
\__MODE__\()clr_Stmr_int: // int 5 default to just return
|
||
RVMODEL_CLR_STIMER_INT
|
||
j resto_\__MODE__\()rtn
|
||
|
||
\__MODE__\()clr_Sext_int: // int 9 default to just return after saving IntID in T3
|
||
RVMODEL_CLR_SEXT_INT
|
||
SREG T3, 3*REGWIDTH(T1) // save 4rd sig value, (intID)
|
||
j resto_\__MODE__\()rtn
|
||
|
||
//------------- VSmode----------------
|
||
\__MODE__\()clr_Vsw_int: // int 2 default to just return if not defined
|
||
RVMODEL_CLR_VSW_INT
|
||
j resto_\__MODE__\()rtn
|
||
|
||
\__MODE__\()clr_Vtmr_int: // int 6 default to just return
|
||
RVMODEL_CLR_VTIMER_INT
|
||
j resto_\__MODE__\()rtn
|
||
|
||
\__MODE__\()clr_Vext_int: // int 8 default to just return after saving IntID in T3
|
||
RVMODEL_CLR_VEXT_INT
|
||
SREG T3, 3*REGWIDTH(T1) // save 4rd sig value, (intID)
|
||
j resto_\__MODE__\()rtn
|
||
|
||
.ifc \__MODE__ , M
|
||
|
||
/*************** Spcl handler for returning from GOTO_MMODE. ********/
|
||
/*************** Only gets executed if GOTO_MMODE not called from Mmode ********/
|
||
/*************** Executed in M-mode. Enter w/ T1=ptr to Mregsave, T2=0 ********/
|
||
/*************** NOTE: Ecall must NOT delegate when T2=0 or this fails ********/
|
||
|
||
rtn2mmode:
|
||
addi T4,T5, -CAUSE_MACHINE_ECALL
|
||
beqz T4, rtn_fm_mmode /* shortcut if called from Mmode */
|
||
#if (rvtest_vtrap_routine)
|
||
#if (XLEN==32)
|
||
csrr T2, CSR_MSTATUSH /* find out originating mode if RV32 */
|
||
#else
|
||
csrr T2, CSR_MSTATUS /* find out originating mode if RV64/128*/
|
||
#endif
|
||
slli T2, T2, WDSZ-MPV_LSB-1 /* but V into MSB ****FIXME if RV128 */
|
||
#endif
|
||
LREG T6, code_bgn_off+1*sv_area_sz(sp) /* get U/S mode code begin */
|
||
bgez T2, from_u_s /* V==0, not virtualized, *1 offset */
|
||
from_v:
|
||
LREG T6, code_bgn_off+2*sv_area_sz(sp)/* get VU/VS mode code begin */
|
||
from_u_s: /* get u/s modes CODE_BEGIN */
|
||
LREG T4, code_bgn_off+0*sv_area_sz(sp) /* get M mode code begin */
|
||
sub T4, T4, T6 /* calc relocation amount */
|
||
rtn_fm_mmode:
|
||
csrr T2, CSR_MEPC /* get return address in orig mode's VM */
|
||
add T2, T2, T4 /* calc rtn_addr in Mmode VM */
|
||
|
||
LREG T1, trap_sv_off+1*REGWIDTH(sp)
|
||
// LREG T2, trap_sv_off+2*REGWIDTH(sp) /*this holds the return address */
|
||
LREG T3, trap_sv_off+3*REGWIDTH(sp)
|
||
LREG T4, trap_sv_off+4*REGWIDTH(sp)
|
||
LREG T5, trap_sv_off+5*REGWIDTH(sp)
|
||
LREG T6, trap_sv_off+6*REGWIDTH(sp)
|
||
LREG sp, trap_sv_off+7*REGWIDTH(sp) // restore temporaries
|
||
jr 4(T2) /* return after GOTO_MMODE in M-mode */
|
||
.endif
|
||
.option pop
|
||
.endm // end of HANDLER
|
||
|
||
/*******************************************************************************/
|
||
/*************** end of handler macro ************/
|
||
/*******************************************************************************/
|
||
/*******************************************************************************/
|
||
/**************** cleanup code; restore xtvec or where it points to ************/
|
||
/********* Assumption: in M-mode, because GOTO_MMODE always ends tests *********/
|
||
/********* Assumption: XSCRATCH pnts to save area for appropriate mode *********/
|
||
/*******************************************************************************/
|
||
|
||
.macro RVTEST_TRAP_EPILOG __MODE__
|
||
.option push
|
||
.option norvc
|
||
|
||
XCSR_VRENAME \__MODE__ // retarget XCSR names to this modes CSRs, no V/S aiasing
|
||
|
||
exit_\__MODE__\()cleanup:
|
||
csrr T1, mscratch // pointer to save area
|
||
.ifc \__MODE__ , S
|
||
addi T1, T1, 1*sv_area_sz
|
||
.else
|
||
.ifc \__MODE__ , V
|
||
addi T1, T1, 2*sv_area_sz
|
||
.endif
|
||
.endif
|
||
|
||
resto_\__MODE__\()edeleg:
|
||
LREG T2, xedeleg_sv_off(T1) // get saved xedeleg at offset -32
|
||
|
||
.ifc \__MODE__ , V
|
||
csrw CSR_VEDELEG, T2 //special case: VS EDELEG available from Vmode
|
||
.else
|
||
.ifc \__MODE__ , M
|
||
#ifdef rvtest_strap_routine
|
||
csrw CSR_XEDELEG, T2 //this handles M mode restore, but only if Smode exists
|
||
#endif
|
||
.else
|
||
//FIXME: if Umode-int-extension or anything like it is implemented, uncomment the following
|
||
// csrw CSR_XEDELEG, T2 //this handles S mode restore
|
||
.endif
|
||
.endif
|
||
|
||
.ifnc \__MODE__ , M
|
||
resto_\__MODE__\()satp:
|
||
LREG T2, xsatp_sv_off(T1) // restore saved xsatp
|
||
csrw CSR_XSATP, T2
|
||
.endif
|
||
resto_\__MODE__\()scratch:
|
||
LREG T5, xscr_save_off(T1) // restore saved xscratch
|
||
csrw CSR_XSCRATCH, T5
|
||
resto_\__MODE__\()xtvec:
|
||
LREG T4, xtvec_sav_off(T1) // restore orig xtvec addr & load current one
|
||
csrrw T2, CSR_XTVEC, T4
|
||
andi T4, T4, ~WDBYTMSK // remove mode, so both word aligned
|
||
andi T2, T2, ~WDBYTMSK
|
||
bne T4, T2, 1f // if saved!=curr mtvec, done, else need to restore tramp
|
||
|
||
resto_\__MODE__\()tramp: // T2 now contains where to restore to
|
||
addi T4, T1, tramp_sv_off // T4 now contains where to restore from
|
||
LREG T3, trampend_off(T1) // T3 tracks how much to restore
|
||
|
||
resto_\__MODE__\()loop:
|
||
lw T6, 0(T4) // read saved tramp entry
|
||
sw T6, 0(T2) // restore original tramp entry
|
||
addi T2, T2, WDBYTSZ // next tgt index
|
||
addi T4, T4, WDBYTSZ // next save index
|
||
blt T2, T3, resto_\__MODE__\()loop // didn't get to end, continue
|
||
1:
|
||
.global rvtest_\__MODE__\()end
|
||
rvtest_\__MODE__\()end:
|
||
|
||
#ifdef HANDLER_TESTCODE_ONLY
|
||
//**FIXME**: add conditional code to compare original trampoline with
|
||
// restored trampoline and store the deltas in the trap signature region
|
||
// as an added check? must work for each mode
|
||
#endif
|
||
.option pop
|
||
.endm //end of EPILOG
|
||
/*******************************************************************************/
|
||
/**** end epilog cleanup code; should fall from V->S->M into RVMODEL_HALT ******/
|
||
/*******************************************************************************/
|
||
|
||
/*******************************************************************************/
|
||
/**** This macro defines per/mode save areas for mmode for each mode ****/
|
||
/**** note that it is the code area, not the data area, and ****/
|
||
/**** must be mulitple of 8B, so multiple instantiations stay aligned ****/
|
||
/**** This is preceded by the current signature pointer, (@Mtrpreg_sv -64? ****/
|
||
/*******************************************************************************/
|
||
.macro RVTEST_TRAP_SAVEAREA __MODE__
|
||
|
||
.option push
|
||
.option norvc
|
||
.global \__MODE__\()tramptbl_sv
|
||
|
||
//****ASSERT: this should be a 64B boundary******//
|
||
\__MODE__\()tramptbl_sv: // save area of existing trampoline table, // also stored in XSCRATCH!!!
|
||
.rept (tramp_sz>>2) // size in words (technically, length of j op) padded to be 8B aligned
|
||
j .+0 // prototype jump instruction, offset to be filled in
|
||
.endr
|
||
|
||
\__MODE__\()code_bgn_ptr:
|
||
.dword rvtest_code_begin // ptr to code bgn area using this mode's mapping trampsvend+0*8
|
||
\__MODE__\()code_seg_sz:
|
||
.dword rvtest_code_end-rvtest_code_begin // code seg size in any mode trampsvend+1*8
|
||
\__MODE__\()data_bgn_ptr:
|
||
.dword rvtest_data_begin // ptr to data bgn area using this mode's mapping trampsvend+2*8
|
||
\__MODE__\()data_seg_sz:
|
||
.dword rvtest_data_end-rvtest_data_begin // code seg size in any mode trampsvend+3*8
|
||
\__MODE__\()sig_bgn_ptr:
|
||
.dword rvtest_sig_begin // ptr to sig bgn area using this mode's mapping trampsvend+4*8
|
||
\__MODE__\()sig_seg_sz:
|
||
.dword rvtest_sig_end-rvtest_sig_begin // code seg size in any mode trampsvend+5*8
|
||
\__MODE__\()vmem_bgn_ptr:
|
||
.dword rvtest_code_begin // default to code bgn area w/ this mode's mapping trampsvend+6*8
|
||
\__MODE__\()vmem_seg_sz:
|
||
.dword rvtest_code_end-rvtest_code_begin // vmem seg size in any mode trampsvend+7*8
|
||
|
||
\__MODE__\()mpp_sv:
|
||
// save mpp=3<<1 during test for mprv spcl case,***only Smode vers
|
||
\__MODE__\()trap_sig:
|
||
.dword mtrap_sigptr // ptr to next trapsig ***GLBL(only Mmode ver. used) trampsvend+8*8
|
||
\__MODE__\()satp_sv:
|
||
.dword 0 // save area for incoming xsatp trampsvend+9*8
|
||
\__MODE__\()trampend_sv:
|
||
.dword 0 // save loc of end of sved trampoline prolog/epilog trampsvend+10*8
|
||
\__MODE__\()tentry_sv:
|
||
.dword \__MODE__\()trampoline + actual_tramp_sz // save comm entry loc pt trampsvend+11*8
|
||
\__MODE__\()edeleg_sv:
|
||
.dword 0 // save loc for edeleg CSR trampsvend+12*8:
|
||
\__MODE__\()tvec_new:
|
||
.dword 0 // points to in-use tvec, actual tramp table used trampsvend+13*8
|
||
\__MODE__\()tvec_save:
|
||
.dword 0 // save area for incoming mtvec trampsvend+14*8
|
||
\__MODE__\()scratch_save:
|
||
.dword 0 // save area for incoming mscratch trampsvend+15*8
|
||
//****GLOBAL:***** onlyMMode version used
|
||
\__MODE__\()trapreg_sv: // hndler regsave area, T1..T6,sp+spare keep dbl algn trampsvend+16*8
|
||
.fill 8, REGWIDTH, 0xdeadbeef
|
||
|
||
\__MODE__\()sv_area_end: // used to calc size, which is used to avoid CSR read trampsvend+24/32+8
|
||
|
||
.option pop
|
||
.endm // end of TRAP_SAVEAREA
|
||
|
||
//==============================================================================
|
||
// This section defines the required test format spec macros:
|
||
// RVTEST_[CODE/DATA/SIG]_[BEGIN/END]
|
||
//==============================================================================
|
||
|
||
|
||
/**************************** CODE BEGIN w/ TRAP HANDLER START *********************/
|
||
/**** instantiate prologs using RVTEST_TRAP_PROLOG() if rvtests_xtrap_routine is ****/
|
||
/**** is defined, then initializes regs & defines rvtest_code_begin global label ****/
|
||
/************************************************************************************/
|
||
.macro RVTEST_CODE_BEGIN
|
||
.option push
|
||
.option rvc
|
||
.align UNROLLSZ
|
||
.option norvc
|
||
.section .text.init
|
||
.globl rvtest_init
|
||
.global rvtest_code_begin //define the label and make it available
|
||
|
||
rvtest_init: //instantiate prologs here
|
||
INSTANTIATE_MODE_MACRO RVTEST_TRAP_PROLOG
|
||
RVTEST_INIT_GPRS // 0xF0E1D2C3B4A59687
|
||
rvtest_code_begin:
|
||
.option pop
|
||
.endm //end of RVTEST_CODE_BEGIN
|
||
/*********************** end of RVTEST_CODE_BEGIN ***********************************/
|
||
|
||
/************************************************************************************/
|
||
/**** The above is instantiated at the start of the actual test ****/
|
||
/**** So the test is here ****/
|
||
/**** the below is instantiated at the end of the actual test ****/
|
||
/************************************************************************************/
|
||
|
||
/**************************************************************************************/
|
||
/**** RVTEST_CODE_END macro defines end of test code: saves regs, transitions to ****/
|
||
/**** Mmode, & instantiates epilog using RVTEST_TRAP_EPILOG() macros. Test code ****/
|
||
/**** falls through to this else must branch to label rvtest_code_end. This must ****/
|
||
/**** branch to a RVMODEL_HALT macro at the end. The actual trap handlers for each ****/
|
||
/**** mode are instantiated immediately following with RVTEST_TRAP_HANDLER() macro ****/
|
||
/**************************************************************************************/
|
||
|
||
.macro RVTEST_CODE_END // test is ended, but in no particular mode
|
||
.option push
|
||
.option norvc
|
||
.global rvtest_code_end // define the label and make it available
|
||
.global cleanup_epilogs // ****ALERT: tests must populate x1 with a point to the end of regular sig area
|
||
/**** MPRV must be clear here !!! ****/
|
||
rvtest_code_end: // RVMODEL_HALT should get here
|
||
#ifdef rvtest_gpr_save // gpr_save area is instantiated at end of signature
|
||
RVTEST_SAVE_GPRS x1 gpr_save
|
||
#endif
|
||
RVTEST_GOTO_MMODE // if only Mmode used by tests, this has no effect
|
||
cleanup_epilogs: // jump here to quit, will restore state for each mode
|
||
|
||
//restore xTVEC, trampoline, regs for each mode in opposite order that they were saved
|
||
#ifdef rvtest_mtrap_routine
|
||
#ifdef rvtest_strap_routine
|
||
#ifdef rvtest_vtrap_routine
|
||
RVTEST_TRAP_EPILOG V // actual v-mode prolog/epilog/handler code
|
||
#endif
|
||
RVTEST_TRAP_EPILOG S // actual s-mode prolog/epilog/handler code
|
||
#endif
|
||
RVTEST_TRAP_EPILOG M // actual m-mode prolog/epilog/handler code
|
||
#endif
|
||
|
||
/************* test done, epilog has restored everying, jump to halt ****************/
|
||
j exit_cleanup //skip around handlers, go to RVMODEL_HALT
|
||
|
||
abort_tests:
|
||
LREG T4, sig_bgn_off(sp) // calculate Mmode sig_end addr in handler's mode
|
||
LREG T1, sig_seg_siz(sp)
|
||
add T1, T1, T4 // construct sig seg end
|
||
LI( T1, 0xBAD0DAD0) // early abort signature value at sig_end, independent of mtrap_sigptr
|
||
SREG T1, -4(T4) // save into last signature canary
|
||
j exit_cleanup // skip around handlers, go to RVMODEL_HALT
|
||
/********************** trap handlers inserted here ***********************************/
|
||
|
||
INSTANTIATE_MODE_MACRO RVTEST_TRAP_HANDLER
|
||
|
||
exit_cleanup: // *** RVMODEL_HALT MUST follow this***, then data
|
||
|
||
.option pop
|
||
.endm // end of RVTEST_CODE_END
|
||
|
||
/************************************************************************************/
|
||
/**** RVTEST_CODE_END macros must fall thru or jump to an RVMODEL_HALT macro here ***/
|
||
/************************************************************************************/
|
||
|
||
/*===================================data section starts here========================*/
|
||
|
||
/************************************************************************************/
|
||
/**** RVTEST_DATA_BEGIN macro defines end of input data & rvtest_data_end label ****/
|
||
/**** this is a data area, so we instantiate trap save areas for each mode here ****/
|
||
/************************************************************************************/
|
||
|
||
.macro RVTEST_DATA_BEGIN
|
||
.data
|
||
|
||
.align 4 //ensure dbl alignment
|
||
/**************************************************************************************/
|
||
/**** this is the pointer to the current trap signature part of the signature area ****/
|
||
/**** it is shared by all trap modes, but shouldn't be instantiated unless at least****/
|
||
/**** 1 trap mode is defined (which is covered if m-mode trap handlers are defined ****/
|
||
/**************************************************************************************/
|
||
|
||
/**** now instantiate separate save areas for each modes state ****/
|
||
/**** strictly speaking, should only be needed for reentrant traps ****/
|
||
|
||
INSTANTIATE_MODE_MACRO RVTEST_TRAP_SAVEAREA
|
||
|
||
/************************************************************************************/
|
||
/**************** end of RVTEST_DATA_BEGIN; input data should follow ****************/
|
||
/************************************************************************************/
|
||
|
||
.global rvtest_data_begin
|
||
rvtest_data_begin:
|
||
.endm
|
||
|
||
/************************************************************************************/
|
||
/**************** RVTEST_DATA_END macro; defines global label rvtest_data_end ****/
|
||
/************************************************************************************/
|
||
.macro RVTEST_DATA_END
|
||
.global rvtest_data_end
|
||
#ifndef rvtest_mtrap_routine
|
||
mtrap_sigptr:
|
||
.fill 2,4,0xdeadbeef
|
||
#endif
|
||
|
||
/**** create identity mapped page tables here if mmu is present ****/
|
||
.align 12
|
||
|
||
#ifndef RVTEST_NO_IDENTY_MAP
|
||
#ifdef rvtest_strap_routine
|
||
rvtest_Sroot_pg_tbl:
|
||
RVTEST_PTE_IDENT_MAP
|
||
|
||
#ifdef rvtest_vtrap_routine
|
||
rvtest_Vroot_pg_tbl:
|
||
RVTEST_PTE_IDENT_MAP
|
||
#endif
|
||
#endif
|
||
#endif
|
||
rvtest_data_end:
|
||
.endm
|