/////////////////////////////////////////// // // WALLY-TEST-LIB-64.S // // Author: Kip Macsai-Goren // // Created 2021-07-19 // // Copyright (C) 2021 Harvey Mudd College & Oklahoma State University // // Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation // files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, // modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software // is furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES // OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS // BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT // OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. /////////////////////////////////////////// #include "model_test.h" #include "arch_test.h" .macro INIT_TESTS // RVTEST_ISA("RV64I") .section .text.init .globl rvtest_entry_point rvtest_entry_point: RVMODEL_BOOT RVTEST_CODE_BEGIN // --------------------------------------------------------------------------------------------- // Initialization Overview: // // Initialize t1 as a virtual pointer to the test results // Initialize a6 as a physical pointer to the test results // Set up stack pointer, mscratch, sscratch // // --------------------------------------------------------------------------------------------- // address for test results la t1, test_1_res la a6, test_1_res // a6 reserved for the physical address equivalent of t1 to be used in trap handlers // any time either is used, both must be updated. // address for normal user stack, mscratch stack, and sscratch stack la sp, mscratch_top csrw mscratch, sp la sp, sscratch_top csrw sscratch, sp la sp, stack_top .endm // Code to trigger traps goes here so we have consistent mtvals for instruction adresses // Even if more tests are added. .macro CAUSE_TRAP_TRIGGERS j end_trap_triggers // The following tests involve causing many of the interrupts and exceptions that are easily done in a few lines // This effectively includes everything that isn't to do with page faults (virtual memory) // // INPUTS: a3 (x13): the number of times one of the infinitely looping interrupt causes should loop before giving up and continuing without the interrupt firing. // cause_instr_addr_misaligned: // cause a misaligned address trap auipc t3, 0 // get current PC, which is aligned addi t3, t3, 0x2 // add 2 to pc to create misaligned address (Assumes compressed instructions are disabled) jr t3 // cause instruction address midaligned trap ret cause_instr_access: sd ra, -8(sp) // push the return adress onto the stack addi sp, sp, -8 jalr zero // cause instruction access trap (address zero is an address with no memory) ld ra, 0(sp) // pop return adress back from the stack addi sp, sp, 8 ret cause_illegal_instr: .word 0x00000000 // 32 bit zero is an illegal instruction ret cause_breakpnt: ebreak ret cause_load_addr_misaligned: auipc t3, 0 // get current PC, which is aligned addi t3, t3, 1 lw t4, 0(t3) // load from a misaligned address ret cause_load_acc: lw t4, 0(zero) // load from unimplemented address ( zero) ret cause_store_addr_misaligned: auipc t3, 0 // get current PC, which is aligned addi t3, t3, 1 sw t4, 0(t3) // store to a misaligned address ret cause_store_acc: sw t4, 0(zero) // store to unimplemented address (zero) ret cause_ecall: // ASSUMES you have already gone to the mode you need to call this from. ecall ret cause_m_time_interrupt: // The following code works for both RV32 and RV64. // RV64 alone would be easier using double-word adds and stores li t3, 0x30 // Desired offset from the present time mv a3, t3 // copy value in to know to stop waiting for interrupt after this many cycles la t4, 0x02004000 // MTIMECMP register in CLINT la t5, 0x0200BFF8 // MTIME register in CLINT lw t2, 0(t5) // low word of MTIME lw t6, 4(t5) // high word of MTIME add t3, t2, t3 // add desired offset to the current time bgtu t3, t2, nowrap // check new time exceeds current time (no wraparound) addi t6, t6, 1 // if wrap, increment most significant word sw t6,4(t4) // store into most significant word of MTIMECMP nowrap: sw t3, 0(t4) // store into least significant word of MTIMECMP time_loop: addi a3, a3, -1 bnez a3, time_loop // go through this loop for [a3 value] iterations before returning without performing interrupt ret cause_s_time_interrupt: li t3, 0x20 csrs mip, t3 // set supervisor time interrupt pending. nop // added extra nops in so the csrs can get through the pipeline before returning. ret cause_m_soft_interrupt: la t3, 0x02000000 // MSIP register in CLINT li t4, 1 // 1 in the lsb sw t4, 0(t3) // Write MSIP bit ret cause_s_soft_interrupt: li t3, 0x2 csrs sip, t3 // set supervisor software interrupt pending. SIP is a subset of MIP, so writing this should also change MIP. ret cause_m_ext_interrupt: // ========== Configure PLIC ========== li a3, 0x40 // m priority threshold = 0 li t3, 0xC200000 li t4, 0 sw t4, 0(t3) // s priority threshold = 7 li t3, 0xC201000 li t4, 7 sw t4, 0(t3) // source 3 (GPIO) priority = 1 li t3, 0xC000000 li t4, 1 sw t4, 0x0C(t3) // enable source 3 in M Mode li t3, 0x0C002000 li t4, 0b1000 sw t4, 0(t3) li t3, 0x10060000 // load base GPIO memory location li t4, 0x1 sw t4, 0x08(t3) // enable the first pin as an output sw t4, 0x04(t3) // enable the first pin as an input as well to cause the interrupt to fire sw zero, 0x1C(t3) // clear rise_ip sw zero, 0x24(t3) // clear fall_ip sw zero, 0x2C(t3) // clear high_ip sw zero, 0x34(t3) // clear low_ip sw t4, 0x28(t3) // set first pin to interrupt on a rising value sw t4, 0x0C(t3) // write a 1 to the first output pin (cause interrupt) m_ext_loop: addi a3, a3, -1 bnez a3, m_ext_loop // go through this loop for [a3 value] iterations before returning without performing interrupt ret cause_s_ext_interrupt_GPIO: // ========== Configure PLIC ========== li a3, 0x40 // s priority threshold = 0 li t3, 0xC201000 li t4, 0 sw t4, 0(t3) // m priority threshold = 7 li t3, 0xC200000 li t4, 7 sw t4, 0(t3) // source 3 (GPIO) priority = 1 li t3, 0xC000000 li t4, 1 sw t4, 0x0C(t3) // enable source 3 in S mode li t3, 0x0C002080 li t4, 0b1000 sw t4, 0(t3) li t3, 0x10060000 // load base GPIO memory location li t4, 0x1 sw t4, 0x08(t3) // enable the first pin as an output sw t4, 0x04(t3) // enable the first pin as an input as well to cause the interrupt to fire sw zero, 0x1C(t3) // clear rise_ip sw zero, 0x24(t3) // clear fall_ip sw zero, 0x2C(t3) // clear high_ip sw zero, 0x34(t3) // clear low_ip sw t4, 0x28(t3) // set first pin to interrupt on a rising value sw t4, 0x0C(t3) // write a 1 to the first output pin (cause interrupt) s_ext_loop: addi a3, a3, -1 bnez a3, s_ext_loop // go through this loop for [a3 value] iterations before returning without performing interrupt ret end_trap_triggers: .endm .macro TRAP_HANDLER MODE, VECTORED=1, EXT_SIGNATURE=0 // MODE decides which mode this trap handler will be taken in (M or S mode) // Vectored decides whether interrupts are handled with the vector table at trap_handler_MODE (1) // vs Using the non-vector approach the rest of the trap handler takes (0) // EXT_SIGNATURE decides whether we will print mtval a string with status.mpie, status.mie, and status.mpp to the signature (1) // vs not saving that info to the signature (0) // Set up the exception Handler, keeping the original handler in tp. la ra, trap_handler_\MODE\() ori ra, ra, \VECTORED // set mode field of tvec to VECTORED, which will force vectored interrupts if it's 1. .if (\MODE\() == m) csrrw tp, \MODE\()tvec, ra // tp reserved for "default" trap handler address that needs to be restored before halting this test. .else csrw \MODE\()tvec, ra // we only neet save the machine trap handler and this if statement ensures it isn't overwritten .endif li a0, 0 li a1, 0 li a2, 0 // reset trap handler inputs to zero la t4, 0x02004000 // MTIMECMP register in CLINT li t5, 0xFFFFFFFF sd t5, 0(t4) // set mtimecmp to 0xFFFFFFFF to really make sure time interrupts don't go off immediately after being enabled j trap_handler_end_\MODE\() // skip the trap handler when it is being defined. // --------------------------------------------------------------------------------------------- // General traps Handler // // Handles traps by branching to different behaviors based on mcause. // // Note that allowing the exception handler to change mode for a program is a huge security // hole, but this is an expedient way of writing tests that need different modes // // input parameters: // // a0 (x10): // 0: halt program with no failures // 1: halt program with failure in x11 = a1 // 2: go to machine mode // 3: go to supervisor mode // 4: go to user mode // others: do nothing // // a1 (x11): // VPN for return address after changing privilege mode. // This should be the base VPN with no offset. // 0x0 : defaults to next instruction on the same page the trap was called on. // // a2 (x12): // Pagetype of the current address VPN before changing privilge mode // Used so that we can know how many bits of the adress are the offset. // Ignored if a1 == 0x0 // 0: Kilopage // 1: Megapage // 2: Gigapage // 3: Terapage // // -------------------------------------------------------------------------------------------- .align 3 trap_handler_\MODE\(): j trap_unvectored_\MODE\() // for the unvectored implimentation: jump past this table of addresses into the actual handler // *** ASSUMES that a cause value of 0 for an interrupt is unimplemented // otherwise, a vectored interrupt handler should jump to trap_handler_\MODE\() + 4 * Interrupt cause code // No matter the value of VECTORED, exceptions (not interrupts) are handled in an unvecotred way j s_soft_vector_\MODE\() // 1: instruction access fault // the zero spot is taken up by the instruction to skip this table. j segfault_\MODE\() // 2: reserved j m_soft_vector_\MODE\() // 3: breakpoint j segfault_\MODE\() // 4: reserved j s_time_vector_\MODE\() // 5: load access fault j segfault_\MODE\() // 6: reserved j m_time_vector_\MODE\() // 7: store access fault j segfault_\MODE\() // 8: reserved j s_ext_vector_\MODE\() // 9: ecall from S-mode j segfault_\MODE\() // 10: reserved j m_ext_vector_\MODE\() // 11: ecall from M-mode // 12 through >=16 are reserved or designated for platform use trap_unvectored_\MODE\(): csrrw sp, \MODE\()scratch, sp // swap sp and scratch so we can use the scratch stack in the trap hanler without messing up sp's value or the stack itself. // *** NOTE: this means that nested traps will be screwed up but they shouldn't happen in any of these tests trap_stack_saved_\MODE\(): // jump here after handling vectored interupt since we already switch sp and scratch there // save registers on stack before using sd ra, -8(sp) sd t0, -16(sp) sd t2, -24(sp) // Record trap csrr ra, \MODE\()cause // record the mcause sd ra, 0(a6) addi t1, t1, 8 addi a6, a6, 8 // update pointers for logging results .if (\EXT_SIGNATURE\() == 1) // record extra information (MTVAL, some status bits) about traps csrr ra, \MODE\()tval sd ra, 0(a6) addi t1, t1, 8 addi a6, a6, 8 csrr ra, \MODE\()status .if (\MODE\() == m) // Taking traps in different modes means we want to get different bits from the status register. li t0, 0x1888 // mask bits to select MPP, MPIE, and MIE. .else li t0, 0x122 // mask bits to select SPP, SPIE, and SIE. .endif and t0, t0, ra sd t0, 0(a6) // store masked out status bits to the output addi t1, t1, 8 addi a6, a6, 8 .endif // Respond to trap based on cause // All interrupts should return after being logged csrr ra, \MODE\()cause li t0, 0x8000000000000000 // if msb is set, it is an interrupt and t0, t0, ra bnez t0, interrupt_handler_\MODE\() // Other trap handling is specified in the vector Table la t0, exception_vector_table_\MODE\() slli ra, ra, 3 // multiply cause by 8 to get offset in vector Table add t0, t0, ra // compute address of vector in Table ld t0, 0(t0) // fectch address of handler from vector Table jr t0 // and jump to the handler interrupt_handler_\MODE\(): la t0, interrupt_vector_table_\MODE\() // NOTE THIS IS NOT THE SAME AS VECTORED INTERRUPTS!!! slli ra, ra, 3 // multiply cause by 8 to get offset in vector Table add t0, t0, ra // compute address of vector in Table ld t0, 0(t0) // fectch address of handler from vector Table jr t0 // and jump to the handler segfault_\MODE\(): ld t2, -24(sp) // restore registers from stack before faulting ld t0, -16(sp) ld ra, -8(sp) j terminate_test // halt program. trapreturn_\MODE\(): csrr ra, \MODE\()epc addi ra, ra, 4 // return to the address AFTER the trapping instruction trapreturn_specified_\MODE\(): // reset the necessary pointers and registers (ra, t0, t1, and the return address going to mepc) // note that we don't need to change t2 since it was a temporary register with no important address in it. // so that when we return to a new virtual address, they're all in the right spot as well. beqz a1, trapreturn_finished_\MODE\() // either update values, of go to default return address. la t0, trap_return_pagetype_table_\MODE\() slli a2, a2, 3 add t0, t0, a2 ld a2, 0(t0) // a2 = number of offset bits in current page type li t0, 1 sll t0, t0, a2 addi t0, t0, -1 // t0 = mask bits for offset into current pagetype // reset the top of the stack, which will be put into ra ld t2, -8(sp) and t2, t0, t2 // t2 = offset for ra add t2, t2, a1 // t2 = new address for ra sd t2, -8(sp) // reset the second spot in the stack, which will be put into t0 ld t2, -16(sp) and t2, t0, t2 // t2 = offset for t0 add t2, t2, a1 // t2 = new address for t0 sd t2, -16(sp) // reset t1, the pointer for the virtual address of the output of the tests and t2, t0, t1 // t2 = offset for t1 add t1, t2, a1 // t1 = new address for the result pointer // reset ra, which temporarily holds the return address that will be written to mepc. and ra, t0, ra // ra = offset for the return address add ra, ra, a1 // ra = new return address. li a1, 0 li a2, 0 // reset trapreturn inputs to the trap handler trapreturn_finished_\MODE\(): csrw \MODE\()epc, ra // update the epc with address of next instruction ld t2, -24(sp) // restore registers from stack before returning ld t0, -16(sp) ld ra, -8(sp) csrrw sp, \MODE\()scratch, sp // switch sp and scratch stack back to restore the non-trap stack pointer \MODE\()ret // return from trap // specific exception handlers ecallhandler_\MODE\(): // Check input parameter a0. encoding above. li t0, 2 // case 2: change to machine mode beq a0, t0, ecallhandler_changetomachinemode_\MODE\() li t0, 3 // case 3: change to supervisor mode beq a0, t0, ecallhandler_changetosupervisormode_\MODE\() li t0, 4 // case 4: change to user mode beq a0, t0, ecallhandler_changetousermode_\MODE\() // unsupported ecalls should segfault j segfault_\MODE\() ecallhandler_changetomachinemode_\MODE\(): // Force status.MPP (bits 12:11) to 11 to enter machine mode after mret // note that it is impossible to return to M mode after a trap delegated to S mode li ra, 0b1100000000000 csrs \MODE\()status, ra j trapreturn_\MODE\() ecallhandler_changetosupervisormode_\MODE\(): // Force status.MPP (bits 12:11) and status.SPP (bit 8) to 01 to enter supervisor mode after (m/s)ret li ra, 0b1000000000000 csrc \MODE\()status, ra li ra, 0b0100100000000 csrs \MODE\()status, ra j trapreturn_\MODE\() ecallhandler_changetousermode_\MODE\(): // Force status.MPP (bits 12:11) and status.SPP (bit 8) to 00 to enter user mode after (m/s)ret li ra, 0b1100100000000 csrc \MODE\()status, ra j trapreturn_\MODE\() instrpagefault_\MODE\(): ld ra, -8(sp) // load return address from stack into ra (the address AFTER the jal to the faulting address) j trapreturn_finished_\MODE\() // puts ra into mepc, restores stack and returns to program (outside of faulting page) instrfault_\MODE\(): ld ra, -8(sp) // load return address from stack into ra (the address AFTER the jal to the faulting address) j trapreturn_finished_\MODE\() // return to the code at ra value from before trap illegalinstr_\MODE\(): j trapreturn_\MODE\() // return to the code after recording the mcause accessfault_\MODE\(): j trapreturn_\MODE\() addr_misaligned_\MODE\(): j trapreturn_\MODE\() breakpt_\MODE\(): j trapreturn_\MODE\() // Vectored interrupt handlers: record the fact that the handler went to the correct vector and then continue to handling // note: does not mess up any registers, saves and restores them to the stack instead. s_soft_vector_\MODE\(): csrrw sp, \MODE\()scratch, sp // swap sp and scratch so we can use the scratch stack in the trap hanler without messing up sp's value or the stack itself. sd t0, -8(sp) // put t0 on the scratch stack before messing with it li t0, 0x7EC01 // write 0x7ec01 (for "VEC"tored and 01 for the interrupt code) j vectored_int_end_\MODE\() m_soft_vector_\MODE\(): csrrw sp, \MODE\()scratch, sp // swap sp and scratch so we can use the scratch stack in the trap hanler without messing up sp's value or the stack itself. sd t0, -8(sp) // put t0 on the scratch stack before messing with it li t0, 0x7EC03 // write 0x7ec03 (for "VEC"tored and 03 for the interrupt code) j vectored_int_end_\MODE\() s_time_vector_\MODE\(): csrrw sp, \MODE\()scratch, sp // swap sp and scratch so we can use the scratch stack in the trap hanler without messing up sp's value or the stack itself. sd t0, -8(sp) // put t0 on the scratch stack before messing with it li t0, 0x7EC05 // write 0x7ec05 (for "VEC"tored and 05 for the interrupt code) j vectored_int_end_\MODE\() m_time_vector_\MODE\(): csrrw sp, \MODE\()scratch, sp // swap sp and scratch so we can use the scratch stack in the trap hanler without messing up sp's value or the stack itself. sd t0, -8(sp) // put t0 on the scratch stack before messing with it li t0, 0x7EC07 // write 0x7ec07 (for "VEC"tored and 07 for the interrupt code) j vectored_int_end_\MODE\() s_ext_vector_\MODE\(): csrrw sp, \MODE\()scratch, sp // swap sp and scratch so we can use the scratch stack in the trap hanler without messing up sp's value or the stack itself. sd t0, -8(sp) // put t0 on the scratch stack before messing with it li t0, 0x7EC09 // write 0x7ec09 (for "VEC"tored and 08 for the interrupt code) j vectored_int_end_\MODE\() m_ext_vector_\MODE\(): csrrw sp, \MODE\()scratch, sp // swap sp and scratch so we can use the scratch stack in the trap hanler without messing up sp's value or the stack itself. sd t0, -8(sp) // put t0 on the scratch stack before messing with it li t0, 0x7EC0B // write 0x7ec0B (for "VEC"tored and 0B for the interrupt code) j vectored_int_end_\MODE\() vectored_int_end_\MODE\(): sd t0, 0(a6) // store to signature to show vectored interrupts succeeded. addi t1, t1, 8 addi a6, a6, 8 ld t0, -8(sp) // restore t0 before continuing to handle trap in case its needed. j trap_stack_saved_\MODE\() // specific interrupt handlers soft_interrupt_\MODE\(): la t0, 0x02000000 // Reset by clearing MSIP interrupt from CLINT sw zero, 0(t0) csrci \MODE\()ip, 0x2 // clear supervisor software interrupt pending bit ld ra, -8(sp) // load return address from stack into ra (the address to return to after causing this interrupt) // Note: we do this because the mepc loads in the address of the instruction after the sw that causes the interrupt // This means that this trap handler will return to the next address after that one, which might be unpredictable behavior. j trapreturn_finished_\MODE\() // return to the code at ra value from before trap time_interrupt_\MODE\(): la t0, 0x02004000 // MTIMECMP register in CLINT li t2, 0xFFFFFFFF sd t2, 0(t0) // reset interrupt by setting mtimecmp to 0xFFFFFFFF li t0, 0x20 csrc \MODE\()ip, t0 ld ra, -8(sp) // load return address from stack into ra (the address to return to after the loop is complete) j trapreturn_finished_\MODE\() // return to the code at ra value from before trap ext_interrupt_\MODE\(): li t3, 0x10060000 // reset interrupt by clearing all the GPIO bits sw zero, 8(t3) // disable the first pin as an output sw zero, 40(t3) // write a 0 to the first output pin (reset interrupt) // reset PLIC to turn off external interrupts // m priority threshold = 7 li t3, 0xC200000 li t0, 0x7 sw t0, 0(t3) // s priority threshold = 7 li t3, 0xC201000 li t0, 0x7 sw t0, 0(t3) // source 3 (GPIO) priority = 0 li t3, 0xC000000 li t0, 0 sw t0, 0x0C(t3) // disable source 3 in M mode li t3, 0x0C002000 li t0, 0b0000 sw t0, 0(t3) // enable source 3 in S mode li t3, 0x0C002080 li t4, 0b0000 sw t4, 0(t3) li t0, 0x200 csrc \MODE\()ip, t0 ld ra, -8(sp) // load return address from stack into ra (the address to return to after the loop is complete) j trapreturn_finished_\MODE\() // return to the code at ra value from before trap // Table of trap behavior // lists what to do on each exception (not interrupts) // unexpected exceptions should cause segfaults for easy detection // Expected exceptions should increment the EPC to the next instruction and return .align 3 // aligns this data table to an 8 byte boundary exception_vector_table_\MODE\(): .8byte addr_misaligned_\MODE\() // 0: instruction address misaligned .8byte instrfault_\MODE\() // 1: instruction access fault .8byte illegalinstr_\MODE\() // 2: illegal instruction .8byte breakpt_\MODE\() // 3: breakpoint .8byte addr_misaligned_\MODE\() // 4: load address misaligned .8byte accessfault_\MODE\() // 5: load access fault .8byte addr_misaligned_\MODE\() // 6: store address misaligned .8byte accessfault_\MODE\() // 7: store access fault .8byte ecallhandler_\MODE\() // 8: ecall from U-mode .8byte ecallhandler_\MODE\() // 9: ecall from S-mode .8byte segfault_\MODE\() // 10: reserved .8byte ecallhandler_\MODE\() // 11: ecall from M-mode .8byte instrpagefault_\MODE\() // 12: instruction page fault .8byte trapreturn_\MODE\() // 13: load page fault .8byte segfault_\MODE\() // 14: reserved .8byte trapreturn_\MODE\() // 15: store page fault .align 3 // aligns this data table to an 8 byte boundary interrupt_vector_table_\MODE\(): .8byte segfault_\MODE\() // 0: reserved .8byte soft_interrupt_\MODE\() // 1: instruction access fault // the zero spot is taken up by the instruction to skip this table. .8byte segfault_\MODE\() // 2: reserved .8byte soft_interrupt_\MODE\() // 3: breakpoint .8byte segfault_\MODE\() // 4: reserved .8byte time_interrupt_\MODE\() // 5: load access fault .8byte segfault_\MODE\() // 6: reserved .8byte time_interrupt_\MODE\() // 7: store access fault .8byte segfault_\MODE\() // 8: reserved .8byte ext_interrupt_\MODE\() // 9: ecall from S-mode .8byte segfault_\MODE\() // 10: reserved .8byte ext_interrupt_\MODE\() // 11: ecall from M-mode .align 3 trap_return_pagetype_table_\MODE\(): .8byte 0xC // 0: kilopage has 12 offset bits .8byte 0x15 // 1: megapage has 21 offset bits .8byte 0x1E // 2: gigapage has 30 offset bits .8byte 0x27 // 3: terapage has 39 offset bits trap_handler_end_\MODE\(): // place to jump to so we can skip the trap handler and continue with the test .endm // Test Summary table! // Test Name : Description : Fault output value : Normal output values // ---------------------:-------------------------------------------:-------------------------------------------:------------------------------------------------------ // write64_test : Write 64 bits to address : 0x6, 0x7, or 0xf : None // write32_test : Write 32 bits to address : 0x6, 0x7, or 0xf : None // write16_test : Write 16 bits to address : 0x6, 0x7, or 0xf : None // write08_test : Write 8 bits to address : 0x6, 0x7, or 0xf : None // read64_test : Read 64 bits from address : 0x4, 0x5, or 0xd, then 0xbad : readvalue in hex // read32_test : Read 32 bits from address : 0x4, 0x5, or 0xd, then 0xbad : readvalue in hex // read16_test : Read 16 bits from address : 0x4, 0x5, or 0xd, then 0xbad : readvalue in hex // read08_test : Read 8 bits from address : 0x4, 0x5, or 0xd, then 0xbad : readvalue in hex // executable_test : test executable on virtual page : 0x0, 0x1, or 0xc, then 0xbad : value of t2 modified by exectuion code (usually 0x111) // terminate_test : terminate tests : mcause value for fault : from M 0xb, from S 0x9, from U 0x8 // goto_baremetal : satp.MODE = bare metal : None : None // goto_sv39 : satp.MODE = sv39 : None : None // goto_sv48 : satp.MODE = sv48 : None : None // goto_m_mode : go to mahcine mode : mcause value for fault : from M 0xb, from S 0x9, from U 0x8 // goto_s_mode : go to supervisor mode : mcause value for fault : from M 0xb, from S 0x9, from U 0x8 // goto_u_mode : go to user mode : mcause value for fault : from M 0xb, from S 0x9, from U 0x8 // write_read_csr : write to specified CSR : old CSR value, 0x2, depending on perms : value written to CSR // csr_r_access : test read-only permissions on CSR : 0xbad : 0x2, then 0x11 .macro WRITE64 ADDR VAL // attempt to write VAL to ADDR // Success outputs: // None // Fault outputs: // 0x6: misaligned address // 0x7: access fault // 0xf: page fault li t4, \VAL li t5, \ADDR sd t4, 0(t5) .endm .macro WRITE32 ADDR VAL // all write tests have the same description/outputs as write64 li t4, \VAL li t5, \ADDR sw t4, 0(t5) .endm .macro WRITE16 ADDR VAL // all write tests have the same description/outputs as write64 li t4, \VAL li t5, \ADDR sh t4, 0(t5) .endm .macro WRITE08 ADDR VAL // all write tests have the same description/outputs as write64 li t4, \VAL li t5, \ADDR sb t4, 0(t5) .endm .macro READ64 ADDR // Attempt read at ADDR. Write the value read out to the output *** Consider adding specific test for reading a non known value // Success outputs: // value read out from ADDR // Fault outputs: // One of the following followed by 0xBAD // 0x4: misaligned address // 0x5: access fault // 0xD: page fault li t2, 0xBAD // bad value that will be overwritten on good reads. li t4, \ADDR ld t2, 0(t4) sd t2, 0(t1) addi t1, t1, 8 addi a6, a6, 8 .endm .macro READ32 ADDR // All reads have the same description/outputs as read64. // They will store the sign extended value of what was read out at ADDR li t2, 0xBAD // bad value that will be overwritten on good reads. li t4, \ADDR lw t2, 0(t4) sd t2, 0(t1) addi t1, t1, 8 addi a6, a6, 8 .endm .macro READ16 ADDR // All reads have the same description/outputs as read64. // They will store the sign extended value of what was read out at ADDR li t2, 0xBAD // bad value that will be overwritten on good reads. li t4, \ADDR lh t2, 0(t4) sd t2, 0(t1) addi t1, t1, 8 addi a6, a6, 8 .endm .macro READ08 ADDR // All reads have the same description/outputs as read64. // They will store the sign extended value of what was read out at ADDR li t2, 0xBAD // bad value that will be overwritten on good reads. li t4, \ADDR lb t2, 0(t4) sd t2, 0(t1) addi t1, t1, 8 addi a6, a6, 8 .endm // These goto_x_mode tests all involve invoking the trap handler, // So their outputs are inevitably: // 0x8: test called from U mode // 0x9: test called from S mode // 0xB: test called from M mode // they generally do not fault or cause issues as long as these modes are enabled .macro GOTO_M_MODE RETURN_VPN=0x0 RETURN_PAGETYPE=0x0 li a0, 2 // determine trap handler behavior (go to machine mode) li a1, \RETURN_VPN // return VPN li a2, \RETURN_PAGETYPE // return page types ecall // writes mcause to the output. // now in S mode .endm .macro GOTO_S_MODE RETURN_VPN=0x0 RETURN_PAGETYPE=0x0 li a0, 3 // determine trap handler behavior (go to supervisor mode) li a1, \RETURN_VPN // return VPN li a2, \RETURN_PAGETYPE // return page types ecall // writes mcause to the output. // now in S mode .endm .macro GOTO_U_MODE RETURN_VPN=0x0 RETURN_PAGETYPE=0x0 li a0, 4 // determine trap handler behavior (go to user mode) li a1, \RETURN_VPN // return VPN li a2, \RETURN_PAGETYPE // return page types ecall // writes mcause to the output. // now in S mode .endm // These tests change virtual memory settings, turning it on/off and changing between types. // They don't have outputs as any error with turning on virtual memory should reveal itself in the tests .macro GOTO_BAREMETAL // Turn translation off li t2, 0 // satp.MODE value for bare metal (0) slli t2, t2, 60 csrw satp, t2 .endm .macro GOTO_SV39 ASID BASE_PPN // Turn on sv39 virtual memory li t2, 8 // satp.MODE value for Sv39 (8) slli t2, t2, 60 li t4, \ASID slli t4, t4, 44 or t2, t2, t4 // put ASID into the correct field of SATP li t3, \BASE_PPN // Base Pagetable physical page number, satp.PPN field. add t2, t2, t3 csrw satp, t2 .endm .macro GOTO_SV48 ASID BASE_PPN // Turn on sv48 virtual memory li t2, 9 // satp.MODE value for Sv39 (8) slli t2, t2, 60 li t4, \ASID slli t4, t4, 44 or t2, t2, t4 // put ASID into the correct field of SATP li t3, \BASE_PPN // Base Pagetable physical page number, satp.PPN field. add t2, t2, t3 csrw satp, t2 .endm .macro WRITE_READ_CSR CSR VAL // attempt to write CSR with VAL. Note: this also tests read access to CSR // Success outputs: // value read back out from CSR after writing // Fault outputs: // The previous CSR value before write attempt // Most likely 0x2, the mcause for illegal instruction if we don't have write or read access li t5, 0xbad // load bad value to be overwritten by csrr li t4, \VAL\() csrw \CSR\(), t4 csrr t5, \CSR sd t5, 0(t1) addi t1, t1, 8 addi a6, a6, 8 .endm .macro CSR_R_ACCESS CSR // verify that a csr is accessible to read but not to write // Success outputs: // 0x2, then // 0x11 // Fault outputs: // 0xBAD csrr t4, \CSR csrwi \CSR\(), 0xA // Attempt to write a 'random' value to the CSR csrr t5, \CSR bne t5, t4, 1f // 1f represents write_access li t5, 0x11 // Write failed, confirming read only permissions. j 2f // j r_access_end 1: // w_access (write succeeded, violating read-only) li t5, 0xBAD 2: // r_access end sd t5, 0(t1) addi t1, t1, 8 addi a6, a6, 8 .endm .macro EXECUTE_AT_ADDRESS ADDR // Execute the code already written to ADDR, returning the value in t2. // Note: this test itself doesn't write the code to ADDR because it might be callled at a point where we dont have write access to ADDR // Assumes the code modifies t2, usually to become 0x111. // Sample code: 0x11100393 (li t2, 0x111), 0x00008067 (ret) // Success outputs: // modified value of t2. (0x111 if you use the sample code) // Fault outputs: // One of the following followed by 0xBAD // 0x0: misaligned address // 0x1: access fault // 0xC: page fault fence.i // forces caches and main memory to sync so execution code written to ADDR can run. li t2, 0xBAD li t3, \ADDR jalr t3 // jump to executable test code sd t2, 0(t1) addi t1, t1, 8 addi a6, a6, 8 .endm // Place this macro in peripheral tests to setup all the PLIC registers to generate external interrupts .macro SETUP_PLIC # Setup PLIC with a series of register writes .equ PLIC_INTPRI_GPIO, 0x0C00000C # GPIO is interrupt 3 .equ PLIC_INTPRI_UART, 0x0C000028 # UART is interrupt 10 .equ PLIC_INTPENDING0, 0x0C001000 # intPending0 register .equ PLIC_INTEN00, 0x0C002000 # interrupt enables for context 0 (machine mode) sources 31:1 .equ PLIC_INTEN10, 0x0C002080 # interrupt enables for context 1 (supervisor mode) sources 31:1 .equ PLIC_THRESH0, 0x0C200000 # Priority threshold for context 0 (machine mode) .equ PLIC_CLAIM0, 0x0C200004 # Claim/Complete register for context 0 .equ PLIC_THRESH1, 0x0C201000 # Priority threshold for context 1 (supervisor mode) .equ PLIC_CLAIM1, 0x0C201004 # Claim/Complete register for context 1 .8byte PLIC_THRESH0, 0, write32_test # Set PLIC machine mode interrupt threshold to 0 to accept all interrupts .8byte PLIC_THRESH1, 7, write32_test # Set PLIC supervisor mode interrupt threshold to 7 to accept no interrupts .8byte PLIC_INTPRI_GPIO, 7, write32_test # Set GPIO to high priority .8byte PLIC_INTPRI_UART, 7, write32_test # Set UART to high priority .8byte PLIC_INTEN00, 0xFFFFFFFF, write32_test # Enable all interrupt sources for machine mode .8byte PLIC_INTEN10, 0x00000000, write32_test # Disable all interrupt sources for supervisor mode .endm .macro END_TESTS // invokes one final ecall to return to machine mode then terminates this program, so the output is // 0x8: termination called from U mode // 0x9: termination called from S mode // 0xB: termination called from M mode j terminate_test .endm // --------------------------------------------------------------------------------------------- // Test Handler // // This test handler works in a similar wy to the trap handler. It takes in a few things by reading from a table in memory // (see test_cases) and performing certain behavior based on them. // // Input parameters: // // t3: // Address input for the test taking place (think: address to read/write, new address to return to, etc...) // // t4: // Value input for the test taking place (think: value to write, any other extra info needed) // // t5: // Label for the location of the test that's about to take place // ------------------------------------------------------------------------------------------------------------------------------------ .macro INIT_TEST_TABLE run_test_loop: la t0, test_cases test_loop: ld t3, 0(t0) // fetch test case address ld t4, 8(t0) // fetch test case value ld t5, 16(t0) // fetch test case flag addi t0, t0, 24 // set t0 to next test case // t0 has the symbol for a test's location in the assembly li t2, 0x1FFFFF and t5, t5, t2 // This program is always on at least a megapage, so this masks out the megapage offset. auipc t2, 0x0 srli t2, t2, 21 slli t2, t2, 21 // zero out the bottom 21 bits so the megapage offset of the symbol can be placed there or t5, t2, t5 // t5 = virtual address of the symbol for this type of test. jr t5 // Test Name : Description : Fault output value : Normal output values // ----------------------:-------------------------------------------:------------------------:------------------------------------------------------ // write64_test : Write 64 bits to address : 0xf : None // write32_test : Write 32 bits to address : 0xf : None // write16_test : Write 16 bits to address : 0xf : None // write08_test : Write 8 bits to address : 0xf : None // read64_test : Read 64 bits from address : 0xd, 0xbad : readvalue in hex // read32_test : Read 32 bitsfrom address : 0xd, 0xbad : readvalue in hex // read16_test : Read 16 bitsfrom address : 0xd, 0xbad : readvalue in hex // read08_test : Read 8 bitsfrom address : 0xd, 0xbad : readvalue in hex // executable_test : test executable on virtual page : 0xc, 0xbad : value of t2 modified by exectuion code (usually 0x111) // terminate_test : terminate tests : mcause value for fault : from M 0xb, from S 0x9, from U 0x8 // goto_baremetal : satp.MODE = bare metal : None : None // goto_sv39 : satp.MODE = sv39 : None : None // goto_sv48 : satp.MODE = sv48 : None : None // write_mxr_sum : write sstatus.[19:18] = MXR, SUM bits : None : None // goto_m_mode : go to mahcine mode : mcause value for fault : from M 0xb, from S 0x9, from U 0x8 // goto_s_mode : go to supervisor mode : mcause value for fault : from M 0xb, from S 0x9, from U 0x8 // goto_u_mode : go to user mode : mcause value for fault : from M 0xb, from S 0x9, from U 0x8 // write_pmpcfg_x : Write one of the pmpcfg csr's : mstatuses?, 0xD : readback of pmpcfg value // write_pmpaddr_x : Write one of the pmpaddr csr's : None : readback of pmpaddr value write64_test: // address to write in t3, double value in t4 sd t4, 0(t3) j test_loop // go to next test case write32_test: // address to write in t3, word value in t4 sw t4, 0(t3) j test_loop // go to next test case write16_test: // address to write in t3, halfword value in t4 sh t4, 0(t3) j test_loop // go to next test case write08_test: // address to write in t3, value in t4 sb t4, 0(t3) j test_loop // go to next test case read64_test: // address to read in t3, expected 64 bit value in t4 (unused, but there for your perusal). li t2, 0xBAD // bad value that will be overwritten on good reads. ld t2, 0(t3) sd t2, 0(t1) addi t1, t1, 8 addi a6, a6, 8 j test_loop // go to next test case read32_test: // address to read in t3, expected 32 bit value in t4 (unused, but there for your perusal). li t2, 0xBAD // bad value that will be overwritten on good reads. lw t2, 0(t3) sd t2, 0(t1) addi t1, t1, 8 addi a6, a6, 8 j test_loop // go to next test case read16_test: // address to read in t3, expected 16 bit value in t4 (unused, but there for your perusal). li t2, 0xBAD // bad value that will be overwritten on good reads. lh t2, 0(t3) sd t2, 0(t1) addi t1, t1, 8 addi a6, a6, 8 j test_loop // go to next test case read08_test: // address to read in t3, expected 8 bit value in t4 (unused, but there for your perusal). li t2, 0xBAD // bad value that will be overwritten on good reads. lb t2, 0(t3) sd t2, 0(t1) addi t1, t1, 8 addi a6, a6, 8 j test_loop // go to next test case read04_test: // address to read in t3, expected 8 bit value in t4 (unused, but there for your perusal). li t2, 0xBAD // bad value that will be overwritten on good reads. lb t2, 0(t3) andi t2, t2, 0xF // mask lower 4 bits sd t2, 0(t1) addi t1, t1, 8 addi a6, a6, 8 j test_loop // go to next test case readmip_test: // read the MIP into the signature csrr t2, mip sd t2, 0(t1) addi t1, t1, 8 addi a6, a6, 8 j test_loop // go to next test case readsip_test: // read the MIP into the signature csrr t2, sip sd t2, 0(t1) addi t1, t1, 8 addi a6, a6, 8 j test_loop // go to next test case claim_m_plic_interrupts: // clears one non-pending PLIC interrupt li t2, 0x0C00000C // GPIO priority li t3, 7 lw t4, 0(t2) sw t3, 0(t2) sw t4, -4(sp) addi sp, sp, -4 li t2, 0x0C000028 // UART priority li t3, 7 lw t4, 0(t2) sw t3, 0(t2) sw t4, -4(sp) addi sp, sp, -4 li t2, 0x0C002000 li t3, 0x0C200004 li t4, 0xFFF lw t6, 0(t2) // save current enable status sw t4, 0(t2) // enable all relevant interrupts on PLIC lw t5, 0(t3) // make PLIC claim sw t5, 0(t3) // complete claim made sw t6, 0(t2) // restore saved enable status li t2, 0x0C00000C // GPIO priority li t3, 0x0C000028 // UART priority lw t4, 4(sp) // load stored GPIO and UART priority lw t5, 0(sp) addi sp, sp, 8 // restore stack pointer sw t4, 0(t2) sw t5, 0(t3) j test_loop claim_s_plic_interrupts: // clears one non-pending PLIC interrupt li t2, 0x0C00000C // GPIO priority li t3, 7 lw t4, 0(t2) sw t3, 0(t2) sw t4, -4(sp) addi sp, sp, -4 li t2, 0x0C000028 // UART priority li t3, 7 lw t4, 0(t2) sw t3, 0(t2) sw t4, -4(sp) addi sp, sp, -4 li t2, 0x0C002080 li t3, 0x0C201004 li t4, 0xFFF lw t6, 0(t2) // save current enable status sw t4, 0(t2) // enable all relevant interrupts on PLIC lw t5, 0(t3) // make PLIC claim sw t5, 0(t3) // complete claim made sw t6, 0(t2) // restore saved enable status li t2, 0x0C00000C // GPIO priority li t3, 0x0C000028 // UART priority lw t4, 4(sp) // load stored GPIO and UART priority lw t5, 0(sp) addi sp, sp, 8 // restore stack pointer sw t4, 0(t2) sw t5, 0(t3) j test_loop uart_lsr_intr_wait: // waits for interrupts to be ready li t2, 0x10000002 // IIR li t4, 0x6 uart_lsr_intr_loop: lb t3, 0(t2) andi t3, t3, 0x7 bne t3, t4, uart_lsr_intr_loop uart_save_iir_status: sd t3, 0(t1) addi t1, t1, 8 addi a6, a6, 8 j test_loop uart_data_wait: li t2, 0x10000005 // LSR li t3, 0x10000002 // IIR li a4, 0x61 uart_read_LSR_IIR: lbu t4, 0(t3) // save IIR before reading LSR might clear it // check if IIR is the rxfifotimeout interrupt. if it is, then read the fifo then go back and repeat this. li t5, 0xCC // Value in IIR for Fifo Enabled, with timeout interrupt pending beq t4, t5, uart_rxfifo_timout lb t5, 0(t2) // read LSR andi t6, t5, 0x61 // wait until all transmissions are done and data is ready bne a4, t6, uart_read_LSR_IIR j uart_data_ready uart_rxfifo_timout: li t4, 0x10000000 // read from the fifo to clear the rx timeout error lb t5, 0(t4) sb t5, 0(t4) // write back to the fifo to make sure we have the same data so expected future overrun errors still occur. //read the fifo until empty j uart_read_LSR_IIR uart_data_ready: li t2, 0 sd t2, 0(t1) // clear entry deadbeef from memory lbu t4, 0(t3) // re read IIR andi t5, t5, 0x9F // mask THRE and TEMT from signature sb t4, 1(t1) // IIR sb t5, 0(t1) // LSR addi t1, t1, 8 addi a6, a6, 8 j test_loop uart_clearmodemintr: li t2, 0x10000006 lb t2, 0(t2) j test_loop goto_s_mode: // return to address in t3, li a0, 3 // Trap handler behavior (go to supervisor mode) mv a1, t3 // return VPN mv a2, t4 // return page types ecall // writes mcause to the output. // now in S mode j test_loop goto_m_mode: li a0, 2 // Trap handler behavior (go to machine mode) mv a1, t3 // return VPN mv a2, t4 // return page types ecall // writes mcause to the output. j test_loop goto_u_mode: li a0, 4 // Trap handler behavior (go to user mode) mv a1, t3 // return VPN mv a2, t4 // return page types ecall // writes mcause to the output. j test_loop goto_baremetal: // Turn translation off GOTO_BAREMETAL j test_loop // go to next test case goto_sv39: // Turn sv39 translation on // Base PPN in t3, ASID in t4 li t2, 8 // satp.MODE value for sv39 (8) slli t2, t2, 60 slli t4, t4, 44 or t2, t2, t4 // put ASID into the correct field of SATP or t2, t2, t3 // Base Pagetable physical page number, satp.PPN field. csrw satp, t2 j test_loop // go to next test case goto_sv48: // Turn sv48 translation on // Base PPN in t3, ASID in t4 li t2, 9 // satp.MODE value for sv48 (9) slli t2, t2, 60 slli t4, t4, 44 or t2, t2, t4 // put ASID into the correct field of SATP or t2, t2, t3 // Base Pagetable physical page number, satp.PPN field. csrw satp, t2 j test_loop // go to next test case write_mxr_sum: // writes sstatus.[mxr, sum] with the (assumed to be) 2 bit value in t4. also assumes we're in S or M mode li t5, 0xC0000 // mask bits for MXR, SUM not t2, t4 slli t2, t2, 18 and t2, t2, t5 slli t4, t4, 18 csrc sstatus, t2 csrs sstatus, t4 j test_loop read_write_mprv: // reads old mstatus.mprv value to output, then // Writes mstatus.mprv with the 1 bit value in t4. assumes we're in m mode li t5, 0x20000 // mask bits for mprv csrr t2, mstatus and t2, t2, t5 srli t2, t2, 17 sd t2, 0(t1) // store old mprv to output addi t1, t1, 8 addi a6, a6, 8 not t2, t4 slli t2, t2, 17 slli t4, t4, 17 csrc mstatus, t2 csrs mstatus, t4 // clear or set mprv bit li t2, 0x1800 csrc mstatus, t2 li t2, 0x800 csrs mstatus, t2 // set mpp to supervisor mode to see if mprv=1 really executes in the mpp mode j test_loop write_pmpcfg_0: // writes the value in t4 to the pmpcfg register specified in t3. // then writes the final value of pmpcfgX to the output. csrw pmpcfg0, t4 csrr t5, pmpcfg0 j write_pmpcfg_end write_pmpcfg_2: csrw pmpcfg2, t4 csrr t5, pmpcfg2 // I would use csrrw but we need the value AFTER the csr has been written j write_pmpcfg_end write_pmpcfg_end: sd t5, 0(t1) addi t1, t1, 8 addi a6, a6, 8 j test_loop write_pmpaddr_0: // write_read_csr pmpaddr0, t4 // writes the value in t4 to the pmpaddr register specified in t3. // then writes the final value of pmpaddrX to the output. csrw pmpaddr0, t4 csrr t5, pmpaddr0 j write_pmpaddr_end write_pmpaddr_1: csrw pmpaddr1, t4 csrr t5, pmpaddr1 j write_pmpaddr_end write_pmpaddr_2: csrw pmpaddr2, t4 csrr t5, pmpaddr2 j write_pmpaddr_end write_pmpaddr_3: csrw pmpaddr3, t4 csrr t5, pmpaddr3 j write_pmpaddr_end write_pmpaddr_4: csrw pmpaddr4, t4 csrr t5, pmpaddr4 j write_pmpaddr_end write_pmpaddr_5: csrw pmpaddr5, t4 csrr t5, pmpaddr5 j write_pmpaddr_end write_pmpaddr_6: csrw pmpaddr6, t4 csrr t5, pmpaddr6 j write_pmpaddr_end write_pmpaddr_7: csrw pmpaddr7, t4 csrr t5, pmpaddr7 j write_pmpaddr_end write_pmpaddr_8: csrw pmpaddr8, t4 csrr t5, pmpaddr8 j write_pmpaddr_end write_pmpaddr_9: csrw pmpaddr9, t4 csrr t5, pmpaddr9 j write_pmpaddr_end write_pmpaddr_10: csrw pmpaddr10, t4 csrr t5, pmpaddr10 j write_pmpaddr_end write_pmpaddr_11: csrw pmpaddr11, t4 csrr t5, pmpaddr11 j write_pmpaddr_end write_pmpaddr_12: csrw pmpaddr12, t4 csrr t5, pmpaddr12 j write_pmpaddr_end write_pmpaddr_13: csrw pmpaddr13, t4 csrr t5, pmpaddr13 j write_pmpaddr_end write_pmpaddr_14: csrw pmpaddr14, t4 csrr t5, pmpaddr14 j write_pmpaddr_end write_pmpaddr_15: csrw pmpaddr15, t4 csrr t5, pmpaddr15 j write_pmpaddr_end write_pmpaddr_end: sd t5, 0(t1) addi t1, t1, 8 addi a6, a6, 8 j test_loop executable_test: // Execute the code at the address in t3, returning the value in t2. // Assumes the code modifies t2, to become the value stored in t4 for this test. fence.i // forces cache and main memory to sync so execution code written by the program can run. li t2, 0xBAD jalr t3 sd t2, 0(t1) addi t1, t1, 8 addi a6, a6, 8 j test_loop .endm // notably, terminate_test is not a part of the test table macro because it needs to be defined // in any type of test, macro or test table, for the trap handler to work terminate_test: li a0, 2 // Trap handler behavior (go to machine mode) ecall // writes mcause to the output. csrw mtvec, tp // restore original trap handler to halt program RVTEST_CODE_END RVMODEL_HALT .macro TEST_STACK_AND_DATA RVTEST_DATA_BEGIN .align 4 rvtest_data: .word 0xbabecafe RVTEST_DATA_END .align 3 // align stack to 8 byte boundary stack_bottom: .fill 1024, 4, 0xdeadbeef stack_top: .align 3 mscratch_bottom: .fill 512, 4, 0xdeadbeef mscratch_top: .align 3 sscratch_bottom: .fill 512, 4, 0xdeadbeef sscratch_top: RVMODEL_DATA_BEGIN test_1_res: .fill 1024, 4, 0xdeadbeef RVMODEL_DATA_END #ifdef rvtest_mtrap_routine mtrap_sigptr: .fill 64*(XLEN/32),4,0xdeadbeef #endif #ifdef rvtest_gpr_save gpr_save: .fill 32*(XLEN/32),4,0xdeadbeef #endif .endm