| /* Testsuite architecture macros for OpenRISC. |
| |
| Copyright (C) 2017-2021 Free Software Foundation, Inc. |
| |
| This program is free software; you can redistribute it and/or modify |
| it under the terms of the GNU General Public License as published by |
| the Free Software Foundation; either version 3 of the License, or |
| (at your option) any later version. |
| |
| This program is distributed in the hope that it will be useful, |
| but WITHOUT ANY WARRANTY; without even the implied warranty of |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| GNU General Public License for more details. |
| |
| You should have received a copy of the GNU General Public License |
| along with this program. If not, see <http://www.gnu.org/licenses/>. */ |
| |
| #ifndef OR1K_ASM_TEST_H |
| #define OR1K_ASM_TEST_H |
| |
| #include "spr-defs.h" |
| |
| /* Register definitions */ |
| |
| /* The "jump and link" instructions store the return address in R9. */ |
| #define LINK_REGISTER_R9 r9 |
| |
| /* These register definitions match the ABI. */ |
| #define ZERO_R0 r0 |
| #define STACK_POINTER_R1 r1 |
| #define FRAME_POINTER_R2 r2 |
| #define RETURN_VALUE_R11 r11 |
| |
| /* Load/move/clear helpers */ |
| |
| .macro LOAD_IMMEDIATE reg, val |
| l.movhi \reg, hi ( \val ) |
| l.ori \reg, \reg, lo ( \val ) |
| .endm |
| |
| .macro MOVE_REG dest_reg, src_reg |
| .ifnes "\dest_reg","\src_reg" |
| l.ori \dest_reg, \src_reg, 0 |
| .endif |
| .endm |
| |
| .macro CLEAR_REG reg |
| l.movhi \reg, 0 |
| .endm |
| |
| .macro MOVE_FROM_SPR reg, spr_reg |
| l.mfspr \reg, ZERO_R0, \spr_reg |
| .endm |
| |
| .macro MOVE_TO_SPR spr_reg, reg |
| l.mtspr ZERO_R0, \reg, \spr_reg |
| .endm |
| |
| .macro SET_SPR_SR_FLAGS flag_mask, scratch_reg_1, scratch_reg_2 |
| /* We cannot use PUSH and POP here because some flags like Carry |
| would get overwritten. */ |
| |
| /* We could optimise this routine, as instruction l.mtspr already |
| does a logical OR. */ |
| MOVE_FROM_SPR \scratch_reg_2, SPR_SR |
| LOAD_IMMEDIATE \scratch_reg_1, \flag_mask |
| l.or \scratch_reg_2, \scratch_reg_2, \scratch_reg_1 |
| MOVE_TO_SPR SPR_SR, \scratch_reg_2 |
| .endm |
| |
| .macro CLEAR_SPR_SR_FLAGS flag_mask, scratch_reg_1, scratch_reg_2 |
| /* We cannot use PUSH and POP here because some flags like Carry |
| would get overwritten. */ |
| |
| MOVE_FROM_SPR \scratch_reg_2, SPR_SR |
| LOAD_IMMEDIATE \scratch_reg_1, ~\flag_mask |
| l.and \scratch_reg_2, \scratch_reg_2, \scratch_reg_1 |
| MOVE_TO_SPR SPR_SR, \scratch_reg_2 |
| |
| .endm |
| |
| /* Stack helpers */ |
| |
| /* This value is defined in the OpenRISC 1000 specification. */ |
| #define EXCEPTION_STACK_SKIP_SIZE 128 |
| |
| /* WARNING: Functions without prolog cannot use these PUSH or POP |
| macros. |
| |
| PERFORMANCE WARNING: These PUSH/POP macros are convenient, but |
| can lead to slow code. If you need to PUSH or POP several |
| registers, it's faster to use non-zero offsets when |
| loading/storing and then increment/decrement the stack pointer |
| just once. */ |
| |
| .macro PUSH reg |
| l.addi STACK_POINTER_R1, STACK_POINTER_R1, -4 |
| l.sw 0(STACK_POINTER_R1), \reg |
| .endm |
| |
| /* WARNING: see the warnings for PUSH. */ |
| .macro POP reg |
| l.lwz \reg, 0(STACK_POINTER_R1) |
| l.addi STACK_POINTER_R1, STACK_POINTER_R1, 4 |
| .endm |
| |
| /* l.nop definitions for simulation control and console output. */ |
| |
| /* Register definitions for the simulation l.nop codes. */ |
| #define NOP_REPORT_R3 r3 |
| #define NOP_EXIT_R3 r3 |
| |
| /* SEC = Simulation Exit Code */ |
| #define SEC_SUCCESS 0 |
| #define SEC_RETURNED_FROM_MAIN 1 |
| #define SEC_GENERIC_ERROR 2 |
| |
| /* When running under the simulator, this l.nop code terminates the |
| simulation. */ |
| .macro EXIT_SIMULATION_WITH_IMMEDIATE_EXIT_CODE immediate_value |
| LOAD_IMMEDIATE NOP_EXIT_R3, \immediate_value |
| l.nop 1 |
| .endm |
| |
| .macro EXIT_SIMULATION_WITH_REG_EXIT_CODE reg |
| MOVE_REG NOP_EXIT_R3, \reg |
| l.nop 1 |
| .endm |
| |
| /* When running under the simulator, this l.nop code prints the |
| value of R3 to the console. */ |
| .macro REPORT_TO_CONSOLE |
| l.nop 2 |
| .endm |
| |
| /* NOTE: The stack must be set up, as this macro uses PUSH and POP. */ |
| .macro REPORT_REG_TO_CONSOLE reg |
| .ifeqs "\reg","r3" |
| /* Nothing more to do here, R3 is the register that gets printed. */ |
| REPORT_TO_CONSOLE |
| .else |
| PUSH NOP_REPORT_R3 |
| MOVE_REG NOP_REPORT_R3, \reg |
| REPORT_TO_CONSOLE |
| POP NOP_REPORT_R3 |
| .endif |
| .endm |
| |
| /* NOTE: The stack must be set up, as this macro uses PUSH and POP. */ |
| .macro REPORT_IMMEDIATE_TO_CONSOLE val |
| PUSH NOP_REPORT_R3 |
| LOAD_IMMEDIATE NOP_REPORT_R3, \val |
| REPORT_TO_CONSOLE |
| POP NOP_REPORT_R3 |
| .endm |
| |
| .macro PRINT_NEWLINE_TO_CONSOLE |
| PUSH r3 |
| LOAD_IMMEDIATE r3, 0x0A |
| l.nop 4 |
| POP r3 |
| .endm |
| |
| /* If SR[F] is set, writes 0x00000001 to the console, otherwise it |
| writes 0x00000000. */ |
| .macro REPORT_SRF_TO_CONSOLE |
| OR1K_DELAYED_NOP (l.bnf \@1$) |
| REPORT_IMMEDIATE_TO_CONSOLE 0x00000001 |
| OR1K_DELAYED_NOP (l.j \@2$) |
| \@1$: |
| REPORT_IMMEDIATE_TO_CONSOLE 0x00000000 |
| \@2$: |
| .endm |
| |
| /* If the given register is 0, writes 0x00000000 to the console, |
| otherwise it writes 0x00000001. */ |
| .macro REPORT_BOOL_TO_CONSOLE reg |
| l.sfne \reg, ZERO_R0 |
| REPORT_SRF_TO_CONSOLE |
| .endm |
| |
| /* Writes to the console the value of the given register bit. */ |
| .macro REPORT_BIT_TO_CONSOLE reg, single_bit_mask |
| PUSH r2 |
| PUSH r3 |
| PUSH r4 |
| MOVE_REG r2, \reg |
| LOAD_IMMEDIATE r4, \single_bit_mask |
| l.and r3, r2, r4 |
| REPORT_BOOL_TO_CONSOLE r3 |
| POP r4 |
| POP r3 |
| POP r2 |
| .endm |
| |
| /* Jump helpers */ |
| |
| .macro CALL overwritten_reg, subroutine_name |
| LOAD_IMMEDIATE \overwritten_reg, \subroutine_name |
| OR1K_DELAYED_NOP (l.jalr \overwritten_reg) |
| .endm |
| |
| .macro RETURN_TO_LINK_REGISTER_R9 |
| OR1K_DELAYED_NOP (l.jr LINK_REGISTER_R9) |
| .endm |
| |
| /* Clear the BSS section on start-up */ |
| |
| .macro CLEAR_BSS overwritten_reg1, overwritten_reg2 |
| LOAD_IMMEDIATE \overwritten_reg1, _bss_begin |
| LOAD_IMMEDIATE \overwritten_reg2, _bss_end |
| l.sfgeu \overwritten_reg1, \overwritten_reg2 |
| OR1K_DELAYED_NOP (l.bf bss_is_empty) |
| bss_clear_loop: |
| /* Possible optimisation to investigate: |
| move "l.sw 0(\overwritten_reg1), r0" to the jump delay slot as |
| "l.sw -4(\overwritten_reg1), r0" or similar. But keep in mind that |
| there are plans to remove the jump delay slot. */ |
| l.sw 0(\overwritten_reg1), r0 |
| l.addi \overwritten_reg1, \overwritten_reg1, 4 |
| l.sfgtu \overwritten_reg2, \overwritten_reg1 |
| OR1K_DELAYED_NOP (l.bf bss_clear_loop) |
| bss_is_empty: |
| .endm |
| |
| #endif /* OR1K_ASM_TEST_H */ |