| /* |
| Copyright 2019 Espressif Systems (Shanghai) PTE LTD |
| |
| Licensed under the Apache License, Version 2.0 (the "License"); |
| you may not use this file except in compliance with the License. |
| You may obtain a copy of the License at |
| |
| http://www.apache.org/licenses/LICENSE-2.0 |
| |
| Unless required by applicable law or agreed to in writing, software |
| distributed under the License is distributed on an "AS IS" BASIS, |
| WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| See the License for the specific language governing permissions and |
| limitations under the License. |
| */ |
| |
| /* |
| * LoadStoreErrorCause: Occurs when trying to access 32 bit addressable memory region as 8 bit or 16 bit |
| * LoadStoreAlignmentCause: Occurs when trying to access in an unaligned manner |
| * |
| * xxxx xxxx = imm8 field |
| * yyyy = imm4 field |
| * ssss = s field |
| * tttt = t field |
| * |
| * 16 0 |
| * ------------------- |
| * L32I.N yyyy ssss tttt 1000 |
| * S32I.N yyyy ssss tttt 1001 |
| * |
| * 23 0 |
| * ----------------------------- |
| * L8UI xxxx xxxx 0000 ssss tttt 0010 <- LoadStoreError |
| * L16UI xxxx xxxx 0001 ssss tttt 0010 <- LoadStoreError, LoadStoreAlignment |
| * L16SI xxxx xxxx 1001 ssss tttt 0010 <- LoadStoreError, LoadStoreAlignment |
| * L32I xxxx xxxx 0010 ssss tttt 0010 <- LoadStoreAlignment |
| * |
| * S8I xxxx xxxx 0100 ssss tttt 0010 <- LoadStoreError |
| * S16I xxxx xxxx 0101 ssss tttt 0010 <- LoadStoreError, LoadStoreAlignment |
| * S32I xxxx xxxx 0110 ssss tttt 0010 <- LoadStoreAlignment |
| * |
| * ******* UNSUPPORTED ******* |
| * |
| * L32E 0000 1001 rrrr ssss tttt 0000 |
| * S32E 0100 1001 rrrr ssss tttt 0000 |
| * ----------------------------- |
| */ |
| |
| #include "xtensa_rtos.h" |
| #include "sdkconfig.h" |
| #include "soc/soc.h" |
| |
| #define LOADSTORE_HANDLER_STACK_SZ 8 |
| .section .bss, "aw" |
| .balign 16 |
| LoadStoreHandlerStack: |
| .rept LOADSTORE_HANDLER_STACK_SZ |
| .word 0 |
| .endr |
| |
| |
| /* LoadStoreErrorCause handler: |
| * |
| * Completes 8-bit or 16-bit load/store instructions from 32-bit aligned memory region |
| * Called from UserExceptionVector if EXCCAUSE is LoadStoreErrorCause |
| */ |
| |
| .global LoadStoreErrorHandler |
| .section .iram1, "ax" |
| |
| .literal_position |
| |
| .balign 4 |
| LoadStoreErrorHandler: |
| .type LoadStoreErrorHandler, @function |
| |
| wsr a0, depc // Save return address in depc |
| mov a0, sp |
| movi sp, LoadStoreHandlerStack |
| s32i a0, sp, 0x04 // Since a0 contains value of a1 |
| s32i a2, sp, 0x08 |
| s32i a3, sp, 0x0c |
| s32i a4, sp, 0x10 |
| |
| rsr a0, sar // Save SAR in a0 to restore later |
| |
| /* Check whether the address lies in the valid range */ |
| rsr a3, excvaddr |
| movi a4, _iram_text_end // End of code section of IRAM |
| bge a3, a4, 1f |
| movi a4, SOC_CACHE_APP_LOW // Check if in APP cache region |
| blt a3, a4, .LS_wrong_opcode |
| movi a4, SOC_CACHE_APP_HIGH |
| bge a3, a4, .LS_wrong_opcode |
| j 2f |
| |
| 1: |
| movi a4, SOC_IRAM_HIGH // End of IRAM address range |
| bge a3, a4, .LS_wrong_opcode |
| |
| 2: |
| /* Examine the opcode which generated the exception */ |
| /* Note: Instructions are in this order to avoid pipeline stalls. */ |
| rsr a2, epc1 |
| movi a4, ~3 |
| ssa8l a2 // sar is now correct shift for aligned read |
| and a2, a2, a4 // a2 now 4-byte aligned address of instruction |
| l32i a4, a2, 0 |
| l32i a2, a2, 4 |
| |
| src a2, a2, a4 // a2 now instruction that failed |
| bbci a2, 1, .LS_wrong_opcode |
| bbsi a2, 14, .LSE_store_op // Store instruction |
| |
| /* l8/l16ui/l16si */ |
| movi a4, ~3 |
| and a4, a3, a4 // a4 now word aligned read address |
| |
| ssa8l a3 // sar is now shift to extract a3's byte |
| l32i a4, a4, 0 // perform the actual read |
| srl a4, a4 // shift right correct distance |
| extui a3, a2, 12, 4 |
| bnez a3, 1f // l16ui/l16si |
| extui a4, a4, 0, 8 // mask off bits needed for an l8 |
| j 2f |
| |
| 1: |
| extui a4, a4, 0, 16 |
| bbci a2, 15, 2f // l16ui |
| |
| /* Sign adjustment */ |
| slli a4, a4, 16 |
| srai a4, a4, 16 // a4 contains the value |
| |
| 2: |
| /* a4 contains the value */ |
| rsr a3, epc1 |
| addi a3, a3, 3 |
| wsr a3, epc1 |
| wsr a0, sar |
| rsr a0, excsave1 |
| |
| extui a2, a2, 3, 5 |
| blti a2, 10, .LSE_stack_reg |
| |
| movi a3, .LS_jumptable_base |
| addx8 a2, a2, a3 // a2 is now the address to jump to |
| l32i a3, sp, 0x0c |
| jx a2 |
| |
| .LSE_stack_reg: |
| addx2 a2, a2, sp |
| s32i a4, a2, 0 |
| |
| /* Restore all values */ |
| l32i a4, sp, 0x10 |
| l32i a3, sp, 0x0c |
| l32i a2, sp, 0x08 |
| l32i a1, sp, 0x04 |
| rfe |
| |
| .LSE_store_op: |
| s32i a5, a1, 0x14 |
| s32i a6, a1, 0x18 |
| |
| /* a2 -> instruction that caused the error */ |
| /* a3 -> unaligned address */ |
| extui a4, a2, 4, 4 |
| blti a4, 7, 1f |
| movi a5, .LSE_store_reg |
| addx8 a5, a4, a5 |
| jx a5 |
| |
| 1: |
| addx4 a4, a4, sp |
| l32i a4, a4, 0 |
| |
| .LSE_store_data: |
| /* a4 contains the value */ |
| rsr a6, epc1 |
| addi a6, a6, 3 |
| wsr a6, epc1 |
| |
| ssa8b a3 |
| movi a5, -1 |
| bbsi a2, 12, 1f // s16 |
| extui a4, a4, 0, 8 |
| movi a6, 0xff |
| j 2f |
| 1: |
| extui a4, a4, 0, 16 |
| movi a6, 0xffff |
| 2: |
| sll a4, a4 // shift the value to proper offset |
| sll a6, a6 |
| xor a5, a5, a6 // a5 contains the mask |
| |
| movi a6, ~3 |
| and a3, a3, a6 // a3 has the aligned address |
| l32i a6, a3, 0 // a6 contains the data at the aligned address |
| and a6, a6, a5 |
| or a4, a6, a4 |
| s32i a4, a3, 0 |
| |
| /* Restore registers */ |
| wsr a0, sar |
| |
| l32i a6, sp, 0x18 |
| l32i a5, sp, 0x14 |
| l32i a4, sp, 0x10 |
| l32i a3, sp, 0x0c |
| l32i a2, sp, 0x08 |
| l32i a1, sp, 0x04 |
| rsr a0, excsave1 |
| |
| rfe |
| |
| .LSE_store_reg: |
| .org .LSE_store_reg + (7 * 8) |
| mov a4, a7 |
| j .LSE_store_data |
| |
| .org .LSE_store_reg + (8 * 8) |
| mov a4, a8 |
| j .LSE_store_data |
| |
| .org .LSE_store_reg + (9 * 8) |
| mov a4, a9 |
| j .LSE_store_data |
| |
| .org .LSE_store_reg + (10 * 8) |
| mov a4, a10 |
| j .LSE_store_data |
| |
| .org .LSE_store_reg + (11 * 8) |
| mov a4, a11 |
| j .LSE_store_data |
| |
| .org .LSE_store_reg + (12 * 8) |
| mov a4, a12 |
| j .LSE_store_data |
| |
| .org .LSE_store_reg + (13 * 8) |
| mov a4, a13 |
| j .LSE_store_data |
| |
| .org .LSE_store_reg + (14 * 8) |
| mov a4, a14 |
| j .LSE_store_data |
| |
| .org .LSE_store_reg + (15 * 8) |
| mov a4, a15 |
| j .LSE_store_data |
| |
| |
| /* LoadStoreAlignmentCause handler: |
| * |
| * Completes unaligned 16-bit and 32-bit load/store instructions from 32-bit aligned memory region |
| * Called from UserExceptionVector if EXCCAUSE is LoadStoreAlignmentCause |
| */ |
| |
| .global AlignmentErrorHandler |
| .section .iram1, "ax" |
| |
| .literal_position |
| |
| .balign 4 |
| AlignmentErrorHandler: |
| .type AlignmentErrorHandler, @function |
| |
| wsr a0, depc // Save return address in depc |
| mov a0, sp |
| movi sp, LoadStoreHandlerStack |
| s32i a0, sp, 0x04 // Since a0 contains value of a1 |
| s32i a2, sp, 0x08 |
| s32i a3, sp, 0x0c |
| s32i a4, sp, 0x10 |
| |
| rsr a0, sar // Save SAR in a0 to restore later |
| |
| /* Check whether the address lies in the valid range */ |
| rsr a3, excvaddr |
| movi a4, _iram_text_end // End of code section of IRAM |
| bge a3, a4, 1f |
| movi a4, SOC_CACHE_APP_LOW // Check if in APP cache region |
| blt a3, a4, .LS_wrong_opcode |
| movi a4, SOC_CACHE_APP_HIGH |
| bge a3, a4, .LS_wrong_opcode |
| j 2f |
| |
| 1: |
| movi a4, SOC_IRAM_HIGH // End of IRAM address range |
| bge a3, a4, .LS_wrong_opcode |
| |
| 2: |
| /* Examine the opcode which generated the exception */ |
| /* Note: Instructions are in this order to avoid pipeline stalls. */ |
| rsr a2, epc1 |
| movi a4, ~3 |
| ssa8l a2 // sar is now correct shift for aligned read |
| and a2, a2, a4 // a2 now 4-byte aligned address of instruction |
| l32i a4, a2, 0 |
| l32i a2, a2, 4 |
| |
| /* a2 has the instruction that caused the error */ |
| src a2, a2, a4 |
| extui a4, a2, 0, 4 |
| addi a4, a4, -9 |
| beqz a4, .LSA_store_op |
| bbsi a2, 14, .LSA_store_op |
| |
| ssa8l a3 // a3 contains the unaligned address |
| movi a4, ~3 |
| and a4, a3, a4 // a4 has the aligned address |
| l32i a3, a4, 0 |
| l32i a4, a4, 4 |
| src a4, a4, a3 |
| |
| rsr a3, epc1 |
| addi a3, a3, 2 |
| bbsi a2, 3, 1f // l32i.n |
| bbci a2, 1, .LS_wrong_opcode |
| addi a3, a3, 1 |
| |
| bbsi a2, 13, 1f // l32 |
| extui a4, a4, 0, 16 |
| bbci a2, 15, 1f // l16ui |
| |
| /* Sign adjustment */ |
| slli a4, a4, 16 |
| srai a4, a4, 16 // a4 contains the value |
| |
| 1: |
| wsr a3, epc1 |
| wsr a0, sar |
| rsr a0, excsave1 |
| |
| extui a2, a2, 4, 4 |
| blti a2, 5, .LSA_stack_reg // a3 contains the target register |
| |
| movi a3, .LS_jumptable_base |
| slli a2, a2, 4 |
| add a2, a2, a3 // a2 is now the address to jump to |
| l32i a3, sp, 0x0c |
| jx a2 |
| |
| .LSA_stack_reg: |
| addx4 a2, a2, sp |
| s32i a4, a2, 0 |
| |
| /* Restore all values */ |
| l32i a4, sp, 0x10 |
| l32i a3, sp, 0x0c |
| l32i a2, sp, 0x08 |
| l32i a1, sp, 0x04 |
| rfe |
| |
| /* Store instruction */ |
| .LSA_store_op: |
| s32i a5, sp, 0x14 |
| s32i a6, sp, 0x18 |
| s32i a7, sp, 0x1c |
| |
| /* a2 -> instruction that caused the error */ |
| /* a3 -> unaligned address */ |
| extui a4, a2, 4, 4 |
| blti a4, 8, 1f |
| movi a5, .LSA_store_reg |
| addx8 a5, a4, a5 |
| jx a5 |
| |
| 1: |
| addx4 a4, a4, sp |
| l32i a4, a4, 0 // a4 contains the value |
| |
| .LSA_store_data: |
| movi a6, 0 |
| |
| rsr a7, epc1 |
| addi a7, a7 ,2 |
| bbsi a2, 3, 1f // s32i.n |
| bbci a2, 1, .LS_wrong_opcode |
| |
| addi a7, a7, 1 |
| bbsi a2, 13, 1f // s32i |
| |
| movi a5, -1 |
| extui a4, a4, 0, 16 |
| slli a6, a5, 16 // 0xffff0000 |
| |
| 1: |
| wsr a7, epc1 |
| movi a5, ~3 |
| and a5, a3, a5 // a5 has the aligned address |
| |
| ssa8b a3 |
| movi a3, -1 |
| src a7, a6, a3 |
| src a3, a3, a6 |
| |
| /* Store data on lower address */ |
| l32i a6, a5, 0 |
| and a6, a6, a7 |
| sll a7, a4 |
| or a6, a6, a7 |
| s32i a6, a5, 0 |
| |
| /* Store data on higher address */ |
| l32i a7, a5, 4 |
| srl a6, a4 |
| and a3, a7, a3 |
| or a3, a3, a6 |
| s32i a3, a5, 4 |
| |
| /* Restore registers */ |
| wsr a0, sar |
| rsr a0, excsave1 |
| |
| l32i a7, sp, 0x1c |
| l32i a6, sp, 0x18 |
| l32i a5, sp, 0x14 |
| l32i a4, sp, 0x10 |
| l32i a3, sp, 0x0c |
| l32i a2, sp, 0x08 |
| l32i a1, sp, 0x04 |
| rfe |
| |
| .LSA_store_reg: |
| .org .LSA_store_reg + (8 * 8) |
| mov a4, a8 |
| j .LSA_store_data |
| |
| .org .LSA_store_reg + (9 * 8) |
| mov a4, a9 |
| j .LSA_store_data |
| |
| .org .LSA_store_reg + (10 * 8) |
| mov a4, a10 |
| j .LSA_store_data |
| |
| .org .LSA_store_reg + (11 * 8) |
| mov a4, a11 |
| j .LSA_store_data |
| |
| .org .LSA_store_reg + (12 * 8) |
| mov a4, a12 |
| j .LSA_store_data |
| |
| .org .LSA_store_reg + (13 * 8) |
| mov a4, a13 |
| j .LSA_store_data |
| |
| .org .LSA_store_reg + (14 * 8) |
| mov a4, a14 |
| j .LSA_store_data |
| |
| .org .LSA_store_reg + (15 * 8) |
| mov a4, a15 |
| j .LSA_store_data |
| |
| /* |
| * Common routines for both the exception handlers |
| */ |
| .balign 4 |
| .LS_jumptable: |
| /* The first 5 entries (80 bytes) of this table are unused (registers |
| a0..a4 are handled separately above). Rather than have a whole bunch |
| of wasted space, just pretend that the table starts 80 bytes |
| earlier in memory. */ |
| .set .LS_jumptable_base, .LS_jumptable - (16 * 5) |
| |
| .org .LS_jumptable_base + (16 * 5) |
| mov a5, a4 |
| l32i a4, sp, 0x10 |
| l32i a2, sp, 0x08 |
| l32i a1, sp, 0x04 |
| rfe |
| |
| .org .LS_jumptable_base + (16 * 6) |
| mov a6, a4 |
| l32i a4, sp, 0x10 |
| l32i a2, sp, 0x08 |
| l32i a1, sp, 0x04 |
| rfe |
| |
| .org .LS_jumptable_base + (16 * 7) |
| mov a7, a4 |
| l32i a4, sp, 0x10 |
| l32i a2, sp, 0x08 |
| l32i a1, sp, 0x04 |
| rfe |
| |
| .org .LS_jumptable_base + (16 * 8) |
| mov a8, a4 |
| l32i a4, sp, 0x10 |
| l32i a2, sp, 0x08 |
| l32i a1, sp, 0x04 |
| rfe |
| |
| .org .LS_jumptable_base + (16 * 9) |
| mov a9, a4 |
| l32i a4, sp, 0x10 |
| l32i a2, sp, 0x08 |
| l32i a1, sp, 0x04 |
| rfe |
| |
| .org .LS_jumptable_base + (16 * 10) |
| mov a10, a4 |
| l32i a4, sp, 0x10 |
| l32i a2, sp, 0x08 |
| l32i a1, sp, 0x04 |
| rfe |
| |
| .org .LS_jumptable_base + (16 * 11) |
| mov a11, a4 |
| l32i a4, sp, 0x10 |
| l32i a2, sp, 0x08 |
| l32i a1, sp, 0x04 |
| rfe |
| |
| .org .LS_jumptable_base + (16 * 12) |
| mov a12, a4 |
| l32i a4, sp, 0x10 |
| l32i a2, sp, 0x08 |
| l32i a1, sp, 0x04 |
| rfe |
| |
| .org .LS_jumptable_base + (16 * 13) |
| mov a13, a4 |
| l32i a4, sp, 0x10 |
| l32i a2, sp, 0x08 |
| l32i a1, sp, 0x04 |
| rfe |
| |
| .org .LS_jumptable_base + (16 * 14) |
| mov a14, a4 |
| l32i a4, sp, 0x10 |
| l32i a2, sp, 0x08 |
| l32i a1, sp, 0x04 |
| rfe |
| |
| .org .LS_jumptable_base + (16 * 15) |
| mov a15, a4 |
| l32i a4, sp, 0x10 |
| l32i a2, sp, 0x08 |
| l32i a1, sp, 0x04 |
| rfe |
| |
| .LS_wrong_opcode: |
| /* Reaches here if the address is in invalid range or the opcode isn't supported. |
| * Restore registers and jump back to _xt_user_exc |
| */ |
| wsr a0, sar |
| l32i a4, sp, 0x10 |
| l32i a3, sp, 0x0c |
| l32i a2, sp, 0x08 |
| l32i a1, sp, 0x04 |
| rsr a0, depc |
| ret // Equivalent to jx a0 |