riscv-arch-test系列之riscof使用范例-从汇编文件生成elf文件

本片文章主要介绍下面命令的执行过程,来说明从汇编文件生成elf文件时如何进行的。

执行命令:

cd /riscv-arch-test-3.10.0/riscof_work/rv32i_m/I/src/cand-01.S/ref;riscv32-unknown-elf-gcc -march=rv32ic -static -mcmodel=medany -fvisibility=hidden -nostdlib -nostartfiles -T /riscv-arch-test-3.10.0/sail_cSim/env/link.ld -I /riscv-arch-test-3.10.0/sail_cSim/env/ -I /riscv-arch-test-3.10.0/riscv-test-suite/env -mabi=ilp32 /riscv-arch-test-3.10.0/riscv-test-suite/rv32i_m/I/src/cand-01.S -o ref.elf -DTEST_CASE_1=True -DXLEN=32;riscv32-unknown-elf-objdump -D ref.elf > ref.disass;riscv_sim_RV32 --test-signature=/riscv-arch-test-3.10.0/riscof_work/rv32i_m/I/src/cand-01.S/ref/Reference-sail_c_simulator.signature ref.elf > cand-01.log 2>&1;

分析该命令:

  1. cd /riscv-arch-test-3.10.0/riscof_work/rv32i_m/I/src/cand-01.S/ref;

    • 切换到包含参考文件的目录。
  2. riscv32-unknown-elf-gcc -march=rv32ic -static -mcmodel=medany -fvisibility=hidden -nostdlib -nostartfiles -T /riscv-arch-test-3.10.0/sail_cSim/env/link.ld -I /riscv-arch-test-3.10.0/sail_cSim/env/ -I /riscv-arch-test-3.10.0/riscv-test-suite/env -mabi=ilp32 /riscv-arch-test-3.10.0/riscv-test-suite/rv32i_m/I/src/cand-01.S -o ref.elf -DTEST_CASE_1=True -DXLEN=32;

    • 使用riscv32-unknown-elf-gcc编译器编译cand-01.S文件。
    • -march=rv32ic: 指定目标架构。
    • -static: 静态链接。
    • -mcmodel=medany: 指定代码模型。
    • -fvisibility=hidden: 隐藏符号。
    • -nostdlib -nostartfiles: 不使用标准库和启动文件。
    • -T link.ld: 使用指定的链接脚本。
    • -I: 指定包含路径。
    • -mabi=ilp32: 指定ABI。
    • -DTEST_CASE_1=True -DXLEN=32: 定义宏。
    • 输出文件为ref.elf
  3. riscv32-unknown-elf-objdump -D ref.elf > ref.disass;

    • 使用riscv32-unknown-elf-objdump反汇编生成的ELF文件,输出到ref.disass
  4. riscv_sim_RV32 --test-signature=/riscv-arch-test-3.10.0/riscof_work/rv32i_m/I/src/cand-01.S/ref/Reference-sail_c_simulator.signature ref.elf > cand-01.log 2>&1;

    • 使用riscv_sim_RV32模拟器运行生成的ELF文件。
    • --test-signature指定测试签名文件。
    • 输出日志到cand-01.log

该命令依次进行编译、反汇编和模拟运行,以验证汇编测试文件的正确性。

汇编文件


// -----------
// This file was generated by riscv_ctg (https://gitlab.com/incoresemi/riscv-compliance/riscv_ctg)
// version   : 0.4.1
// timestamp : Tue Dec 15 15:36:11 2020 GMT
// usage     : riscv_ctg \
//                  -- cgf ('/scratch/git-repo/incoresemi/riscv-compliance/riscv_ctg/sample_cgfs/dataset.cgf', '/scratch/git-repo/incoresemi/riscv-compliance/riscv_ctg/sample_cgfs/rv32i.cgf') \
//                  -- xlen 32 \
// -----------
//
// -----------
// Copyright (c) 2020. RISC-V International. All rights reserved.
// SPDX-License-Identifier: BSD-3-Clause
// -----------
//
// This assembly file tests the add instruction of the RISC-V I extension for the add covergroup.
// 
#include "model_test.h"
#include "arch_test.h"
RVTEST_ISA("RV32I")

.section .text.init
.globl rvtest_entry_point
rvtest_entry_point:
RVMODEL_BOOT
RVTEST_CODE_BEGIN

#ifdef TEST_CASE_1

RVTEST_CASE(0,"//check ISA:=regex(.*32.*);check ISA:=regex(.*I.*);def TEST_CASE_1=True;",add)

RVTEST_SIGBASE( x3,signature_x3_1)

inst_0:
// rs2 == rd != rs1, rs1==x4, rs2==x24, rd==x24, rs1_val > 0 and rs2_val > 0, rs2_val == 1, rs1_val == (2**(xlen-1)-1), rs1_val != rs2_val, rs1_val == 2147483647
// opcode: add ; op1:x4; op2:x24; dest:x24; op1val:0x7fffffff;  op2val:0x1
TEST_RR_OP(add, x24, x4, x24, 0x80000000, 0x7fffffff, 0x1, x3, 0, x18)

inst_1:
// rs1 == rs2 != rd, rs1==x10, rs2==x10, rd==x28, rs1_val > 0 and rs2_val < 0, rs2_val == -257, rs1_val == 131072
// opcode: add ; op1:x10; op2:x10; dest:x28; op1val:0x20000;  op2val:0x20000
TEST_RR_OP(add, x28, x10, x10, 0x40000, 0x20000, 0x20000, x3, 4, x18)

inst_2:
// rs1 == rs2 == rd, rs1==x21, rs2==x21, rd==x21, rs1_val < 0 and rs2_val < 0, rs1_val == -16777217
// opcode: add ; op1:x21; op2:x21; dest:x21; op1val:-0x1000001;  op2val:-0x1000001
TEST_RR_OP(add, x21, x21, x21, 0xfdfffffe, -0x1000001, -0x1000001, x3, 8, x18)

inst_3:
// rs1 == rd != rs2, rs1==x22, rs2==x31, rd==x22, rs1_val < 0 and rs2_val > 0, rs1_val == -2, rs2_val == 262144
// opcode: add ; op1:x22; op2:x31; dest:x22; op1val:-0x2;  op2val:0x40000
TEST_RR_OP(add, x22, x22, x31, 0x3fffe, -0x2, 0x40000, x3, 12, x18)

inst_4:
// rs1 != rs2  and rs1 != rd and rs2 != rd, rs1==x12, rs2==x6, rd==x11, rs1_val == rs2_val, rs1_val==1431655766 and rs2_val==1431655766
// opcode: add ; op1:x12; op2:x6; dest:x11; op1val:0x55555556;  op2val:0x55555556
TEST_RR_OP(add, x11, x12, x6, 0xaaaaaaac, 0x55555556, 0x55555556, x3, 16, x18)

inst_5:
// rs1==x29, rs2==x13, rd==x10, rs2_val == (-2**(xlen-1)), rs2_val == -2147483648, rs1_val == 2
// opcode: add ; op1:x29; op2:x13; dest:x10; op1val:0x2;  op2val:-0x80000000
TEST_RR_OP(add, x10, x29, x13, 0x80000002, 0x2, -0x80000000, x3, 20, x18)

inst_6:
// rs1==x31, rs2==x5, rd==x26, rs2_val == 0, rs1_val == -17
// opcode: add ; op1:x31; op2:x5; dest:x26; op1val:-0x11;  op2val:0x0
TEST_RR_OP(add, x26, x31, x5, 0xffffffef, -0x11, 0x0, x3, 24, x18)

inst_7:
// rs1==x2, rs2==x1, rd==x7, rs2_val == (2**(xlen-1)-1), rs2_val == 2147483647
// opcode: add ; op1:x2; op2:x1; dest:x7; op1val:0x66666666;  op2val:0x7fffffff
TEST_RR_OP(add, x7, x2, x1, 0xe6666665, 0x66666666, 0x7fffffff, x3, 28, x18)

inst_8:
// rs1==x8, rs2==x25, rd==x14, rs1_val == (-2**(xlen-1)), rs1_val == -2147483648, rs2_val == -1431655766
// opcode: add ; op1:x8; op2:x25; dest:x14; op1val:-0x80000000;  op2val:-0x55555556
TEST_RR_OP(add, x14, x8, x25, 0x2aaaaaaa, -0x80000000, -0x55555556, x3, 32, x18)

inst_9:
// rs1==x13, rs2==x8, rd==x1, rs1_val == 0, rs2_val == -33554433
// opcode: add ; op1:x13; op2:x8; dest:x1; op1val:0x0;  op2val:-0x2000001
TEST_RR_OP(add, x1, x13, x8, 0xfdffffff, 0x0, -0x2000001, x3, 36, x18)

inst_10:
// rs1==x28, rs2==x9, rd==x0, rs1_val == 1, rs2_val == 8388608
// opcode: add ; op1:x28; op2:x9; dest:x0; op1val:0x1;  op2val:0x800000
TEST_RR_OP(add, x0, x28, x9, 0, 0x1, 0x800000, x3, 40, x18)

inst_11:
// rs1==x14, rs2==x4, rd==x20, rs2_val == 2, 
// opcode: add ; op1:x14; op2:x4; dest:x20; op1val:0x7;  op2val:0x2
TEST_RR_OP(add, x20, x14, x4, 0x9, 0x7, 0x2, x3, 44, x18)


inst_587:
// rs2_val == 4096, rs1_val == 4
// opcode: add ; op1:x10; op2:x11; dest:x12; op1val:0x4;  op2val:0x1000
TEST_RR_OP(add, x12, x10, x11, 0x1004, 0x4, 0x1000, x1, 168, x2)
#endif


RVTEST_CODE_END
RVMODEL_HALT

RVTEST_DATA_BEGIN
.align 4

rvtest_data:
.word 0xbabecafe
.word 0xbabecafe
.word 0xbabecafe
.word 0xbabecafe
RVTEST_DATA_END


RVMODEL_DATA_BEGIN
rvtest_sig_begin:
sig_begin_canary:
CANARY;


signature_x3_0:
    .fill 0*(XLEN/32),4,0xdeadbeef


signature_x3_1:
    .fill 17*(XLEN/32),4,0xdeadbeef


signature_x8_0:
    .fill 16*(XLEN/32),4,0xdeadbeef


signature_x1_0:
    .fill 512*(XLEN/32),4,0xdeadbeef


signature_x1_1:
    .fill 43*(XLEN/32),4,0xdeadbeef

#ifdef rvtest_mtrap_routine

tsig_begin_canary:
CANARY;
mtrap_sigptr:
    .fill 64*(XLEN/32),4,0xdeadbeef
tsig_end_canary:
CANARY;

#endif

#ifdef rvtest_gpr_save

gpr_save:
    .fill 32*(XLEN/32),4,0xdeadbeef

#endif

sig_end_canary:
CANARY;
rvtest_sig_end:
RVMODEL_DATA_END

汇编文件include文件

#include "model_test.h"
#include "arch_test.h"

model_test.h

备注:根据编译命令,model_test.h需要在编译的include文件中查找,即

-I /riscv-arch-test-3.10.0/sail_cSim/env/ -I /riscv-arch-test-3.10.0/riscv-test-suite/env

/riscv-arch-test-3.10.0/sail_cSim/env是通过riscof命令生成的,见文章<riscv-arch-test系列之riscof使用范例>
/riscv-arch-test-3.10.0/riscv-test-suite/env见github仓库

/riscv-arch-test-3.10.0/sail_cSim/env/ model_test.h

文件内容

model_test.h 文件定义了一些用于RISC-V测试合规性的宏,具体说明如下:

#ifndef _COMPLIANCE_MODEL_H
#define _COMPLIANCE_MODEL_H

// 数据段定义
#define RVMODEL_DATA_SECTION \
        .pushsection .tohost,"aw",@progbits;                            \
        .align 8; .global tohost; tohost: .dword 0;                     \
        .align 8; .global fromhost; fromhost: .dword 0;                 \
        .popsection;                                                    \
        .align 8; .global begin_regstate; begin_regstate:               \
        .word 128;                                                      \
        .align 8; .global end_regstate; end_regstate:                   \
        .word 4;

// 停机指令
#define RVMODEL_HALT                                              \
  li x1, 1;                                                                   \
  write_tohost:                                                               \
    sw x1, tohost, t5;                                                        \
    j write_tohost;

// 启动指令
#define RVMODEL_BOOT

// 数据段开始
#define RVMODEL_DATA_BEGIN                                              \
  RVMODEL_DATA_SECTION                                                        \
  .align 4;\
  .global begin_signature; begin_signature:

// 数据段结束
#define RVMODEL_DATA_END                                                      \
  .align 4; .global end_signature; end_signature:  

// I/O初始化
#define RVMODEL_IO_INIT

// 输出字符串
#define RVMODEL_IO_WRITE_STR(_R, _STR)

// I/O检查
#define RVMODEL_IO_CHECK()

// 断言通用寄存器相等
#define RVMODEL_IO_ASSERT_GPR_EQ(_S, _R, _I)

// 断言单精度浮点寄存器相等
#define RVMODEL_IO_ASSERT_SFPR_EQ(_F, _R, _I)

// 断言双精度浮点寄存器相等
#define RVMODEL_IO_ASSERT_DFPR_EQ(_D, _R, _I)

// 设置机器软件中断
#define RVMODEL_SET_MSW_INT

// 清除机器软件中断
#define RVMODEL_CLEAR_MSW_INT

// 清除机器定时器中断
#define RVMODEL_CLEAR_MTIMER_INT

// 清除机器外部中断
#define RVMODEL_CLEAR_MEXT_INT

#endif // _COMPLIANCE_MODEL_H
主要宏定义解释:
  • RVMODEL_DATA_SECTION: 定义了.tohostfromhost段,用于主机与目标之间的数据交换。
  • RVMODEL_HALT: 定义了一个无限循环的停机指令,通过写入tohost来通知主机。
  • RVMODEL_BOOT: 启动指令的占位符。
  • RVMODEL_DATA_BEGIN: 数据段开始标记。
  • RVMODEL_DATA_END: 数据段结束标记。
  • RVMODEL_IO_INIT: 初始化I/O操作的占位符。
  • RVMODEL_IO_WRITE_STR: 输出字符串的占位符。
  • RVMODEL_IO_CHECK: I/O检查的占位符。
  • RVMODEL_IO_ASSERT_GPR_EQ: 断言通用寄存器值相等的占位符。
  • RVMODEL_IO_ASSERT_SFPR_EQ: 断言单精度浮点寄存器值相等的占位符。
  • RVMODEL_IO_ASSERT_DFPR_EQ: 断言双精度浮点寄存器值相等的占位符。
  • RVMODEL_SET_MSW_INT: 设置机器软件中断的占位符。
  • RVMODEL_CLEAR_MSW_INT: 清除机器软件中断的占位符。
  • RVMODEL_CLEAR_MTIMER_INT: 清除机器定时器中断的占位符。
  • RVMODEL_CLEAR_MEXT_INT: 清除机器外部中断的占位符。

这些宏用于配置和控制RISC-V测试环境,确保测试的合规性和正确性。

arch_test.h

文件内容
// ***DELETEME** note to self
// this version adds instret counts to the signature
// this version modifies the LA macro to skip generation if rd=x0 (or X0)
// this version detects ECALL cause even in CLIC mode

// -----------
// Copyright (c) 2020-2023. RISC-V International. All rights reserved.
// SPDX-License-Identifier: BSD-3-Clause
// -----------

        //********************************************************************************
        //********** FIXME: these comments are now completely out of order****************
        //********************************************************************************

// 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
//**************************************
//*****(trap handler data is here)******
//**************************************
//
//**************************************
//*****(Ld/St test data is here)********
//**************************************
//
//    rvtest_trap_sig:   [ptr toglobal 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 pte entry permision bits for all permissions.
#define RVTEST_ALLPERMS ( PTE_G | PTE_U | PTE_X | PTE_W | PTE_R | PTE_V)
//this is pte entry permision bits for no permissions.
#define RVTEST_NOACC    ( 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

// this is the position of the last level PPN in each root page table PTE
  #define ROOT_PPN_LSB 10
    #if XLEN==32
      #define PPN_SZ   10
      #define LVLS     2
    #else
      #define PPN_SZ   9
      #define LVLS   ((_ADDR_SZ_-4)/PPN_SZ)
    #endif

// this defines a page of PTEs at top level (depending on _ADDR_SZ_) with named permissions
// for the largest size page and a common base (which is set to zero for identify mapping)
#define RVTEST_PTE_IDENT_MAP(PGBASE,LVLS,PERMS)                                 ;\
    .set ppn, 0                                                                 ;\
    .rept (4096 >> REGWIDTH)                                                    ;\
      .fill   1, REGWIDTH, (PGBASE | (ppn<<(10+(LVLS-1)*PPN_SZ)) | PERMS)       ;\
      .set ppn, (ppn+1)                                                         ;\
    .endr                                                                       ;\

// 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
#elif FLEN==128
    #define FLREG flq
    #define FSREG fsq
    #define FREGWIDTH 16
#endif

#if ZFINX==1
  #define FLREG ld
  #define FSREG sd
  #define FREGWIDTH 8
  #define FLEN 64
  #if XLEN==64
    #define SIGALIGN 8
  #else
      #define SIGALIGN 4
  #endif
#elif ZDINX==1
  #define FLREG LREG
  #define FSREG SREG
  #define FREGWIDTH 8
  #define FLEN 64
#elif ZHINX==1
  #define FLREG lw
  #define FSREG sw
  #define FREGWIDTH 4
  #define FLEN 32
#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)     ;\
    .ifnc(reg, X0)       ;\
        .option push    ;\
        .option rvc     ;\
        .align UNROLLSZ ;\
        .option norvc   ;\
        la reg,val      ;\
        .align UNROLLSZ ;\
        .option pop     ;\
    .endif
#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 ****/
/*****************************************************************/

.macro DBLSHIFT7 dstreg, oldreg
        srli  \dstreg\(), \oldreg\(), 7
        srli  x15   , \oldreg\(), XLEN-7
        or    \dstreg\(), \dstreg\(), x15
.endm
/* init regs, to ensure you catch any errors */
.macro RVTEST_INIT_GPRS
   #ifndef RVTEST_E
     LI (x16, (0x7D5BFDDB7D5BFDDB & MASK))
     DBLSHIFT7 x17, x16
     DBLSHIFT7 x18, x17
     DBLSHIFT7 x19, x18
     DBLSHIFT7 x20, x19
     DBLSHIFT7 x21, x20
     DBLSHIFT7 x22, x21
     DBLSHIFT7 x23, x22
     DBLSHIFT7 x24, x23
     DBLSHIFT7 x25, x24
     DBLSHIFT7 x26, x25
     DBLSHIFT7 x27, x26
     DBLSHIFT7 x28, x27
     DBLSHIFT7 x29, x28
     DBLSHIFT7 x30, x29
   #endif
     LI (x1,  (0xFEEDBEADFEEDBEAD & MASK))
     DBLSHIFT7 x2, x1
     DBLSHIFT7 x3, x2
     DBLSHIFT7 x4, x3
     DBLSHIFT7 x5, x4
     DBLSHIFT7 x6, x5
     DBLSHIFT7 x7, x6
     DBLSHIFT7 x8, x7
     DBLSHIFT7 x9, x8
     DBLSHIFT7 x10, x9
     DBLSHIFT7 x11, x10
     DBLSHIFT7 x12, x11
     DBLSHIFT7 x13, x12

#ifdef RVTEST_ENAB_INSTRET_CNT
     csrr  x14, CSR_MSCRATCH
     csrr  x15, CSR_MINSTRET
     SREG  x15, tramp_sz+4*8(x14)               // this replaces initial canary val w/ instret counter val

     DBLSHIFT7 x14, x13
     LI (x15, (0xFAB7FBB6FAB7FBB6 & 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_GPRS(_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


/***********************************************************************************/
/**** 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
        LI(T4,(1<<(XLEN-1))+(1<<12 - 1<<2))     // make a mask of int bit and cause(11:2). This 
        and     T4, T4, T5                      // Keep int bit and cause[11:2]  NOTE: cause 10 is RSVD.  Sail will diverge, but buggy anyway  
        addi    T4, T4, -8                      // map cause 8..11 to 0.  Mmode should avoid ECALL 0
        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
  // lglmsk(vMPP,H,GVA)=(1x1,x01)=0x5D
  // +-------+------+-------+-------+---------+
  // | Hndlr | vMPP | H-ext | M.GVA | sv area |
  // | Mode  |  =3  |       |       |  delta  |
  // +-------+------+-------+-------+---------+
  // |   M   |   0  |   1   |   1   |   2     |
  // |   M   |   0  |   x   |   0   |   1     |
  // |   M   |   1  |   x   |   0   |   0     |
  // |   M   |   x  |   0   |   1   | illegal |
  // |   M   |   1  |   x   |   1   | illegal |
  // +-------+------+-------+-------+---------+
  // |       |      | H-ext | H.GVA | sv area |
  // +-------+------+-------+-------+---------+
  // | S/HS  |   0* |   1   |   1   |   1     |
  // | S/HS  |   0* |   1   |   0   |   0     |
  // | S/HS  |   0* |   0   |   *   |   0     |
  // +-------+------+-------+-------+---------+
  // |       |      | H-ext | noGVA | 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
        
  .ifc \__MODE__ ,  M
        csrr    T6, CSR_MSTATUS
// 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
        LREG    T6, mpp_sv_off(sp)      /* saved MPP, overwritten if MPRV=1     */
1:
// create a mask in T4[2:0] with (xMPP==3, H-ext, GVA)        
        srli    T4, T6, 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 0
    #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         /* reposition RV32 mstatush into bit1   */
      #else
        srli    T3, T4, GVA_LSB+32      /* reposition RV32 mstatus  into bit1   */
      #endif
        andi    T3, T3, 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, 0x5D)               /*lglmsk(vMPP,H,GVA)=(1x1,011,0x0)=0x5D */
        srl     T6, T6, T4
        andi    T6, T6, 1               /* extract lgl bit val & end test if 0  */
        beq     T6, x0, rvtest_\__MODE__\()endtest 
//determine sv offset multiplier
        LI(     T6, sv_area_sz)
        andi    T3, T4, 1               /* GVA indicates *2 or sll of 1         */
        sll     T6, T6, T3              /* mul by 2 if GVA else mul by 1        */ 
        slli    T3, T4, XLEN-3          /* but mul by 0 if bit2 (vMPP==3) == 1  */
        srai    T3, T3, XLEN-1          /* make 0s msk if vMMP==3 bit =1        */
        xori    T3, T3, -1
        and     T4, 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  */

#ifdef SKIP_MEPC
        addi T3, T3, 0
        j adj_\__MODE__\()epc
#endif

        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

#ifdef SKIP_MTVAL
        addi T3, T3, 0
        j adj_\__MODE__\()tval
#endif

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_entrypoint:
// RVMODEL_BOOT                        // Commenting this one as temporary fix
  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           ****/
/************************************************************************************/
/*                ----------------> test inserted here <----------------             */
/**************************************************************************************/
/**** 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
#ifdef RVTEST_ENAB_INSTRET_CNT
     csrr  x15, CSR_MINSTRET
     csrr  x14, CSR_MSCRATCH
     LREG  x13, tramp_sz+4*8(x14)       // initial instret point stored here
     sub   x15, x15, x13                // calc instret delta
     SREG  x13, tramp_sz+4*8(x14)       //put it back in the signature
#endif

//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
//RVMODEL_HALT
  .option pop
.endm                           // end of RVTEST_CODE_END

/*===================================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

/************************************************************************************/
/*            ----------------> test data inserted here <----------------           */
/************************************************************************************/

/************************************************************************************/
/**************** RVTEST_DATA_END macro; defines global label rvtest_data_end    ****/
/************************************************************************************/
.macro RVTEST_DATA_END
.global rvtest_data_end

/**** create identity mapped page tables here if mmu is present ****/
.align 12

#ifndef RVTEST_NO_IDENTY_MAP
  #ifdef rvtest_strap_routine
//this is a valid global pte entry w/ all permissions. IF at root level, it forms an identity map.
    rvtest_Sroot_pg_tbl:
    RVTEST_PTE_IDENT_MAP(0,LVLS,RVTEST_ALLPERMS)
        
    #ifdef rvtest_vtrap_routine
      rvtest_Vroot_pg_tbl: 
      RVTEST_PTE_IDENT_MAP(0,LVLS,RVTEST_ALLPERMS)
    #endif
  #endif
#endif
rvtest_data_end:
.endm

/********************* 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             ****/
/**********************************************************************/
.macro 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:
.endm

// 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 defining start of trap signature area              ****/
/**** defining rvtest_sig_end: label to enabling direct stores     ****/
/**** into the signature area to be properLY relocated             ****/
/**********************************************************************/
//.macro RVTEST_TSIG_BEGIN
.macro RVTEST_SIG_END
.global rvtest_tsig_begin     /* defines beginning of trap sig area   */

tsig_begin_canary:
   CANARY
mtrap_sigptr:
  #ifndef rvtest_mtrap_routine /* install dummy or dflt trap sig area */
    .fill  3*(XLEN/32),4,0xdeadbeef
  #else 
    .fill 64*(XLEN/32),4,0xdeadbeef
  #endif
tsig_end_canary:
   CANARY
//.endm

/********************* 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             ****/
/**********************************************************************/
//.macro 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                       */
.endm

汇编文件所有引用展开后代码

#include "model_test.h"
#include "arch_test.h"
RVTEST_ISA("RV32I")

.section .text.init
.globl rvtest_entry_point
rvtest_entry_point:
RVMODEL_BOOT
RVTEST_CODE_BEGIN

#ifdef TEST_CASE_1

RVTEST_CASE(0,"//check ISA:=regex(.*32.*);check ISA:=regex(.*I.*);def TEST_CASE_1=True;",add)

RVTEST_SIGBASE( x3,signature_x3_1)

inst_0:
// rs2 == rd != rs1, rs1==x4, rs2==x24, rd==x24, rs1_val > 0 and rs2_val > 0, rs2_val == 1, rs1_val == (2**(xlen-1)-1), rs1_val != rs2_val, rs1_val == 2147483647
// opcode: add ; op1:x4; op2:x24; dest:x24; op1val:0x7fffffff;  op2val:0x1
TEST_RR_OP(add, x24, x4, x24, 0x80000000, 0x7fffffff, 0x1, x3, 0, x18)

//  more other tests

RVTEST_CODE_END
RVMODEL_HALT

RVTEST_DATA_BEGIN
.align 4

rvtest_data:
.word 0xbabecafe
.word 0xbabecafe
.word 0xbabecafe
.word 0xbabecafe
RVTEST_DATA_END


RVMODEL_DATA_BEGIN
rvtest_sig_begin:
sig_begin_canary:
CANARY;


signature_x3_0:
    .fill 0*(XLEN/32),4,0xdeadbeef


signature_x3_1:
    .fill 17*(XLEN/32),4,0xdeadbeef


signature_x8_0:
    .fill 16*(XLEN/32),4,0xdeadbeef


signature_x1_0:
    .fill 512*(XLEN/32),4,0xdeadbeef


signature_x1_1:
    .fill 43*(XLEN/32),4,0xdeadbeef

#ifdef rvtest_mtrap_routine

tsig_begin_canary:
CANARY;
mtrap_sigptr:
    .fill 64*(XLEN/32),4,0xdeadbeef
tsig_end_canary:
CANARY;

#endif

#ifdef rvtest_gpr_save

gpr_save:
    .fill 32*(XLEN/32),4,0xdeadbeef

#endif

sig_end_canary:
CANARY;
rvtest_sig_end:
RVMODEL_DATA_END

这段代码是一个RISC-V指令测试程序的汇编代码,它包含测试例程的初始化、执行和数据处理部分。以下是对这段代码的详细分析:

包含头文件和ISA定义
#include "model_test.h"
#include "arch_test.h"
RVTEST_ISA("RV32I")
  • 包含了model_test.harch_test.h头文件,定义了一些宏和常量。
  • RVTEST_ISA("RV32I")用于指定测试的ISA(指令集架构)为RV32I。
代码段初始化
.section .text.init
.globl rvtest_entry_point
rvtest_entry_point:
RVMODEL_BOOT
RVTEST_CODE_BEGIN
  • 定义了.text.init段,并声明了全局标签rvtest_entry_point作为入口点。
  • 执行RVMODEL_BOOT宏进行启动初始化。
  • 执行RVTEST_CODE_BEGIN宏,表示测试代码的开始。
测试案例
#ifdef TEST_CASE_1

RVTEST_CASE(0,"//check ISA:=regex(.*32.*);check ISA:=regex(.*I.*);def TEST_CASE_1=True;",add)

RVTEST_SIGBASE( x3,signature_x3_1)

inst_0:
// rs2 == rd != rs1, rs1==x4, rs2==x24, rd==x24, rs1_val > 0 and rs2_val > 0, rs2_val == 1, rs1_val == (2**(xlen-1)-1), rs1_val != rs2_val, rs1_val == 2147483647
// opcode: add ; op1:x4; op2:x24; dest:x24; op1val:0x7fffffff;  op2val:0x1
TEST_RR_OP(add, x24, x4, x24, 0x80000000, 0x7fffffff, 0x1, x3, 0, x18)
  • 如果定义了TEST_CASE_1,则执行以下代码。
  • RVTEST_CASE宏用于定义测试案例。
  • RVTEST_SIGBASE宏用于指定签名基地址。
  • 定义了第一个测试指令inst_0,用于测试ADD指令。
  • TEST_RR_OP宏执行ADD指令测试,检查寄存器x24、x4和x24的值。

####代码段结束

RVTEST_CODE_END
RVMODEL_HALT
  • RVTEST_CODE_END宏表示测试代码的结束。
  • RVMODEL_HALT宏用于停止测试。
数据段
RVTEST_DATA_BEGIN
.align 4

rvtest_data:
.word 0xbabecafe
.word 0xbabecafe
.word 0xbabecafe
.word 0xbabecafe
RVTEST_DATA_END
  • RVTEST_DATA_BEGIN宏表示数据段的开始。
  • 定义了对齐为4字节的rvtest_data数据段,其中包含4个数据字。
  • RVTEST_DATA_END宏表示数据段的结束。
签名段
RVMODEL_DATA_BEGIN
rvtest_sig_begin:
sig_begin_canary:
CANARY;

signature_x3_0:
    .fill 0*(XLEN/32),4,0xdeadbeef

signature_x3_1:
    .fill 17*(XLEN/32),4,0xdeadbeef

signature_x8_0:
    .fill 16*(XLEN/32),4,0xdeadbeef

signature_x1_0:
    .fill 512*(XLEN/32),4,0xdeadbeef

signature_x1_1:
    .fill 43*(XLEN/32),4,0xdeadbeef
  • RVMODEL_DATA_BEGIN宏表示签名段的开始。
  • 定义了rvtest_sig_begin标签和CANARY占位符。
  • 定义了多个签名部分,每个部分使用.fill伪指令填充指定数量的0xdeadbeef值。
可选的陷阱签名和寄存器保存段
#ifdef rvtest_mtrap_routine

tsig_begin_canary:
CANARY;
mtrap_sigptr:
    .fill 64*(XLEN/32),4,0xdeadbeef
tsig_end_canary:
CANARY;

#endif

#ifdef rvtest_gpr_save

gpr_save:
    .fill 32*(XLEN/32),4,0xdeadbeef

#endif

sig_end_canary:
CANARY;
rvtest_sig_end:
RVMODEL_DATA_END
  • 定义了可选的陷阱签名段和寄存器保存段。
  • tsig_begin_canarymtrap_sigptrtsig_end_canary用于定义陷阱签名部分。
  • gpr_save用于保存通用寄存器的值。
  • sig_end_canaryrvtest_sig_end用于标记签名段的结束。
  • RVMODEL_DATA_END宏表示签名段的结束。

这段代码实现了一个简单的RISC-V指令测试,包括初始化、指令执行、数据处理和签名保存等步骤。

link.ld

riscv-dv/riscv-arch-test-3.10.0/sail_cSim/env

OUTPUT_ARCH( "riscv" )
ENTRY(rvtest_entry_point)

SECTIONS
{
  . = 0x80000000;
  .text.init : { *(.text.init) }
  . = ALIGN(0x1000);
  .tohost : { *(.tohost) }
  . = ALIGN(0x1000);
  .text : { *(.text) }
  . = ALIGN(0x1000);
  .data : { *(.data) }
  .data.string : { *(.data.string)}
  .bss : { *(.bss) }
  _end = .;
}


生成反汇编文件(debug)

;riscv32-unknown-elf-objdump -D ref.elf > ref.disass

文件示例


ref.elf:     file format elf32-littleriscv


Disassembly of section .text.init:

80000000 <rvtest_entry_point>:
80000000:	7d5c0837          	lui	a6,0x7d5c0
80000004:	ddb80813          	addi	a6,a6,-549 # 7d5bfddb <absimm+0x382cd8ac>
80000008:	00785893          	srli	a7,a6,0x7
8000000c:	01985793          	srli	a5,a6,0x19
80000010:	00f8e8b3          	or	a7,a7,a5
80000014:	0078d913          	srli	s2,a7,0x7
80000018:	0198d793          	srli	a5,a7,0x19
8000001c:	00f96933          	or	s2,s2,a5
80000020:	00795993          	srli	s3,s2,0x7
80000024:	01995793          	srli	a5,s2,0x19
80000028:	00f9e9b3          	or	s3,s3,a5
8000002c:	0079da13          	srli	s4,s3,0x7
80000030:	0199d793          	srli	a5,s3,0x19
80000034:	00fa6a33          	or	s4,s4,a5
80000038:	007a5a93          	srli	s5,s4,0x7
8000003c:	019a5793          	srli	a5,s4,0x19
80000040:	00faeab3          	or	s5,s5,a5
80000044:	007adb13          	srli	s6,s5,0x7
80000048:	019ad793          	srli	a5,s5,0x19
8000004c:	00fb6b33          	or	s6,s6,a5
80000050:	007b5b93          	srli	s7,s6,0x7
80000054:	019b5793          	srli	a5,s6,0x19
80000058:	00fbebb3          	or	s7,s7,a5
8000005c:	007bdc13          	srli	s8,s7,0x7
80000060:	019bd793          	srli	a5,s7,0x19
80000064:	00fc6c33          	or	s8,s8,a5
80000068:	007c5c93          	srli	s9,s8,0x7
8000006c:	019c5793          	srli	a5,s8,0x19
80000070:	00fcecb3          	or	s9,s9,a5
80000074:	007cdd13          	srli	s10,s9,0x7
80000078:	019cd793          	srli	a5,s9,0x19
8000007c:	00fd6d33          	or	s10,s10,a5
80000080:	007d5d93          	srli	s11,s10,0x7
80000084:	019d5793          	srli	a5,s10,0x19
80000088:	00fdedb3          	or	s11,s11,a5
8000008c:	007dde13          	srli	t3,s11,0x7
80000090:	019dd793          	srli	a5,s11,0x19
80000094:	00fe6e33          	or	t3,t3,a5
80000098:	007e5e93          	srli	t4,t3,0x7
8000009c:	019e5793          	srli	a5,t3,0x19
800000a0:	00feeeb3          	or	t4,t4,a5
800000a4:	007edf13          	srli	t5,t4,0x7
800000a8:	019ed793          	srli	a5,t4,0x19
800000ac:	00ff6f33          	or	t5,t5,a5
800000b0:	feedc0b7          	lui	ra,0xfeedc
800000b4:	ead08093          	addi	ra,ra,-339 # feedbead <immx+0x441ce3dd>
800000b8:	0070d113          	srli	sp,ra,0x7
800000bc:	0190d793          	srli	a5,ra,0x19
800000c0:	00f16133          	or	sp,sp,a5
800000c4:	00715193          	srli	gp,sp,0x7
800000c8:	01915793          	srli	a5,sp,0x19
800000cc:	00f1e1b3          	or	gp,gp,a5
800000d0:	0071d213          	srli	tp,gp,0x7
800000d4:	0191d793          	srli	a5,gp,0x19
800000d8:	00f26233          	or	tp,tp,a5
800000dc:	00725293          	srli	t0,tp,0x7
800000e0:	01925793          	srli	a5,tp,0x19
800000e4:	00f2e2b3          	or	t0,t0,a5
800000e8:	0072d313          	srli	t1,t0,0x7
800000ec:	0192d793          	srli	a5,t0,0x19
800000f0:	00f36333          	or	t1,t1,a5
800000f4:	00735393          	srli	t2,t1,0x7
800000f8:	01935793          	srli	a5,t1,0x19
800000fc:	00f3e3b3          	or	t2,t2,a5
80000100:	0073d413          	srli	s0,t2,0x7
80000104:	0193d793          	srli	a5,t2,0x19
80000108:	00f46433          	or	s0,s0,a5
8000010c:	00745493          	srli	s1,s0,0x7
80000110:	01945793          	srli	a5,s0,0x19
80000114:	00f4e4b3          	or	s1,s1,a5
80000118:	0074d513          	srli	a0,s1,0x7
8000011c:	0194d793          	srli	a5,s1,0x19
80000120:	00f56533          	or	a0,a0,a5
80000124:	00755593          	srli	a1,a0,0x7
80000128:	01955793          	srli	a5,a0,0x19
8000012c:	00f5e5b3          	or	a1,a1,a5
80000130:	0075d613          	srli	a2,a1,0x7
80000134:	0195d793          	srli	a5,a1,0x19
80000138:	00f66633          	or	a2,a2,a5
8000013c:	00765693          	srli	a3,a2,0x7
80000140:	01965793          	srli	a5,a2,0x19
80000144:	00f6e6b3          	or	a3,a3,a5

80000148 <rvtest_code_begin>:
80000148:	00000013          	nop
8000014c:	00000013          	nop
80000150:	00000013          	nop
80000154:	00000013          	nop
80000158:	00000013          	nop
8000015c:	00000013          	nop
80000160:	00006197          	auipc	gp,0x6
80000164:	fb418193          	addi	gp,gp,-76 # 80006114 <signature_x3_0>
80000168:	00000013          	nop
8000016c:	00000013          	nop
80000170:	00000013          	nop
80000174:	00000013          	nop
80000178:	00000013          	nop
8000017c:	00000013          	nop

80000180 <inst_0>:
80000180:	80000237          	lui	tp,0x80000
80000184:	fff20213          	addi	tp,tp,-1 # 7fffffff <absimm+0x3ad0dad0>
80000188:	00100c13          	li	s8,1
8000018c:	01820c33          	add	s8,tp,s8
80000190:	0181a023          	sw	s8,0(gp)

80000194 <inst_1>:
80000194:	00020537          	lui	a0,0x20
80000198:	00020537          	lui	a0,0x20
8000019c:	00a50e33          	add	t3,a0,a0
800001a0:	01c1a223          	sw	t3,4(gp)

800001a4 <inst_2>:
800001a4:	ff000ab7          	lui	s5,0xff000
800001a8:	fffa8a93          	addi	s5,s5,-1 # feffffff <immx+0x442f252f>
800001ac:	ff000ab7          	lui	s5,0xff000
800001b0:	fffa8a93          	addi	s5,s5,-1 # feffffff <immx+0x442f252f>
800001b4:	015a8ab3          	add	s5,s5,s5
800001b8:	0151a423          	sw	s5,8(gp)

800001bc <inst_3>:
800001bc:	ffe00b13          	li	s6,-2
800001c0:	00040fb7          	lui	t6,0x40
800001c4:	01fb0b33          	add	s6,s6,t6
800001c8:	0161a623          	sw	s6,12(gp)

800001cc <inst_4>:
800001cc:	55555637          	lui	a2,0x55555
800001d0:	55660613          	addi	a2,a2,1366 # 55555556 <absimm+0x10263027>
800001d4:	55555337          	lui	t1,0x55555
800001d8:	55630313          	addi	t1,t1,1366 # 55555556 <absimm+0x10263027>
800001dc:	006605b3          	add	a1,a2,t1
800001e0:	00b1a823          	sw	a1,16(gp)

800001e4 <inst_5>:
800001e4:	00200e93          	li	t4,2
800001e8:	800006b7          	lui	a3,0x80000
800001ec:	00de8533          	add	a0,t4,a3
800001f0:	00a1aa23          	sw	a0,20(gp)

800001f4 <inst_6>:
800001f4:	fef00f93          	li	t6,-17
800001f8:	00000293          	li	t0,0
800001fc:	005f8d33          	add	s10,t6,t0
80000200:	01a1ac23          	sw	s10,24(gp)

80000204 <inst_7>:
80000204:	66666137          	lui	sp,0x66666
80000208:	66610113          	addi	sp,sp,1638 # 66666666 <absimm+0x21374137>
8000020c:	800000b7          	lui	ra,0x80000
80000210:	fff08093          	addi	ra,ra,-1 # 7fffffff <absimm+0x3ad0dad0>
80000214:	001103b3          	add	t2,sp,ra
80000218:	0071ae23          	sw	t2,28(gp)

8000021c <inst_8>:
8000021c:	80000437          	lui	s0,0x80000
80000220:	aaaabcb7          	lui	s9,0xaaaab
80000224:	aaac8c93          	addi	s9,s9,-1366 # aaaaaaaa <_end+0x2aaa405a>
80000228:	01940733          	add	a4,s0,s9
8000022c:	02e1a023          	sw	a4,32(gp)

80000230 <inst_9>:
80000230:	00000693          	li	a3,0
80000234:	fe000437          	lui	s0,0xfe000
80000238:	fff40413          	addi	s0,s0,-1 # fdffffff <immx+0x432f252f>
8000023c:	008680b3          	add	ra,a3,s0
80000240:	0211a223          	sw	ra,36(gp)

80000244 <inst_10>:
80000244:	00100e13          	li	t3,1
80000248:	008004b7          	lui	s1,0x800
8000024c:	009e0033          	add	zero,t3,s1
80000250:	0201a423          	sw	zero,40(gp)

80000254 <inst_11>:
80000254:	00700713          	li	a4,7
80000258:	00200213          	li	tp,2
8000025c:	00470a33          	add	s4,a4,tp
80000260:	0341a623          	sw	s4,44(gp)

80000310 <inst_20>:
80000310:	00000013          	nop
80000314:	40000813          	li	a6,1024
80000318:	01000333          	add	t1,zero,a6
8000031c:	00642623          	sw	t1,12(s0)

80000320 <inst_21>:
80000320:	aaaab5b7          	lui	a1,0xaaaab
80000324:	aab58593          	addi	a1,a1,-1365 # aaaaaaab <_end+0x2aaa405b>
80000328:	000011b7          	lui	gp,0x1
8000032c:	80018193          	addi	gp,gp,-2048 # 800 <offset+0x754>
80000330:	00358fb3          	add	t6,a1,gp
80000334:	01f42823          	sw	t6,16(s0)

80000338 <inst_22>:
80000338:	00400913          	li	s2,4
8000033c:	00000013          	nop
80000340:	00090633          	add	a2,s2,zero
80000344:	00c42a23          	sw	a2,20(s0)

80000348 <inst_23>:
80000348:	fe000f37          	lui	t5,0xfe000
8000034c:	ffff0f13          	addi	t5,t5,-1 # fdffffff <immx+0x432f252f>
80000350:	000023b7          	lui	t2,0x2
80000354:	007f04b3          	add	s1,t5,t2
80000358:	00942c23          	sw	s1,24(s0)

8000035c <inst_24>:
8000035c:	0000b1b7          	lui	gp,0xb
80000360:	50318193          	addi	gp,gp,1283 # b503 <offset+0xb457>
80000364:	00004f37          	lui	t5,0x4
80000368:	01e182b3          	add	t0,gp,t5
8000036c:	00542e23          	sw	t0,28(s0)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值