diff --git a/components/mqtt/esp-mqtt b/components/mqtt/esp-mqtt index 01594bf118..b9db8d9020 160000 --- a/components/mqtt/esp-mqtt +++ b/components/mqtt/esp-mqtt @@ -1 +1 @@ -Subproject commit 01594bf118ae502b5a0ead040446f2be75d26223 +Subproject commit b9db8d90204c7f9a23165630fd74ad621516c0c7 diff --git a/components/riscv/CMakeLists.txt b/components/riscv/CMakeLists.txt new file mode 100644 index 0000000000..6d3744d6a7 --- /dev/null +++ b/components/riscv/CMakeLists.txt @@ -0,0 +1,18 @@ +idf_build_get_property(target IDF_TARGET) +if(NOT "${target}" STREQUAL "esp32c3") + return() +endif() +if(BOOTLOADER_BUILD) + set(priv_requires soc) +else() + set(priv_requires soc freertos) + set(srcs + "interrupt.c" + "stdatomic.c" + "vectors.S") +endif() + +idf_component_register(SRCS "${srcs}" + LDFRAGMENTS linker.lf + INCLUDE_DIRS "include" + PRIV_REQUIRES ${priv_requires}) diff --git a/components/riscv/include/esp_attr.h b/components/riscv/include/esp_attr.h new file mode 100644 index 0000000000..38a8fbe4ee --- /dev/null +++ b/components/riscv/include/esp_attr.h @@ -0,0 +1,131 @@ +// Copyright 2015-2016 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. +#ifndef __ESP_ATTR_H__ +#define __ESP_ATTR_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +#include "sdkconfig.h" + +#define ROMFN_ATTR + +//Normally, the linker script will put all code and rodata in flash, +//and all variables in shared RAM. These macros can be used to redirect +//particular functions/variables to other memory regions. + +// Forces code into IRAM instead of flash +#define IRAM_ATTR _SECTION_ATTR_IMPL(".iram1", __COUNTER__) + +// Forces data into DRAM instead of flash +#define DRAM_ATTR _SECTION_ATTR_IMPL(".dram1", __COUNTER__) + +// Forces data to be 4 bytes aligned +#define WORD_ALIGNED_ATTR __attribute__((aligned(4))) + +// Forces data to be placed to DMA-capable places +#define DMA_ATTR WORD_ALIGNED_ATTR DRAM_ATTR + +// Forces a function to be inlined +#define FORCE_INLINE_ATTR static inline __attribute__((always_inline)) + +// Forces a string into DRAM instead of flash +// Use as ets_printf(DRAM_STR("Hello world!\n")); +#define DRAM_STR(str) (__extension__({static const DRAM_ATTR char __c[] = (str); (const char *)&__c;})) + +// Forces code into RTC fast memory. See "docs/deep-sleep-stub.rst" +#define RTC_IRAM_ATTR _SECTION_ATTR_IMPL(".rtc.text", __COUNTER__) + +#if CONFIG_SPIRAM_ALLOW_BSS_SEG_EXTERNAL_MEMORY +// Forces bss variable into external memory. " +#define EXT_RAM_ATTR _SECTION_ATTR_IMPL(".ext_ram.bss", __COUNTER__) +#else +#define EXT_RAM_ATTR +#endif + +// Forces data into RTC slow memory. See "docs/deep-sleep-stub.rst" +// Any variable marked with this attribute will keep its value +// during a deep sleep / wake cycle. +#define RTC_DATA_ATTR _SECTION_ATTR_IMPL(".rtc.data", __COUNTER__) + +// Forces read-only data into RTC memory. See "docs/deep-sleep-stub.rst" +#define RTC_RODATA_ATTR _SECTION_ATTR_IMPL(".rtc.rodata", __COUNTER__) + +// Allows to place data into RTC_SLOW memory. +#define RTC_SLOW_ATTR _SECTION_ATTR_IMPL(".rtc.force_slow", __COUNTER__) + +// Allows to place data into RTC_FAST memory. +#define RTC_FAST_ATTR _SECTION_ATTR_IMPL(".rtc.force_fast", __COUNTER__) + +// Forces data into noinit section to avoid initialization after restart. +#define __NOINIT_ATTR _SECTION_ATTR_IMPL(".noinit", __COUNTER__) + +// Forces data into RTC slow memory of .noinit section. +// Any variable marked with this attribute will keep its value +// after restart or during a deep sleep / wake cycle. +#define RTC_NOINIT_ATTR _SECTION_ATTR_IMPL(".rtc_noinit", __COUNTER__) + +// Forces to not inline function +#define NOINLINE_ATTR __attribute__((noinline)) + +// This allows using enum as flags in C++ +// Format: FLAG_ATTR(flag_enum_t) +#ifdef __cplusplus + +// Inline is required here to avoid multiple definition error in linker +#define FLAG_ATTR_IMPL(TYPE, INT_TYPE) \ +FORCE_INLINE_ATTR constexpr TYPE operator~ (TYPE a) { return (TYPE)~(INT_TYPE)a; } \ +FORCE_INLINE_ATTR constexpr TYPE operator| (TYPE a, TYPE b) { return (TYPE)((INT_TYPE)a | (INT_TYPE)b); } \ +FORCE_INLINE_ATTR constexpr TYPE operator& (TYPE a, TYPE b) { return (TYPE)((INT_TYPE)a & (INT_TYPE)b); } \ +FORCE_INLINE_ATTR constexpr TYPE operator^ (TYPE a, TYPE b) { return (TYPE)((INT_TYPE)a ^ (INT_TYPE)b); } \ +FORCE_INLINE_ATTR constexpr TYPE operator>> (TYPE a, int b) { return (TYPE)((INT_TYPE)a >> b); } \ +FORCE_INLINE_ATTR constexpr TYPE operator<< (TYPE a, int b) { return (TYPE)((INT_TYPE)a << b); } \ +FORCE_INLINE_ATTR TYPE& operator|=(TYPE& a, TYPE b) { a = a | b; return a; } \ +FORCE_INLINE_ATTR TYPE& operator&=(TYPE& a, TYPE b) { a = a & b; return a; } \ +FORCE_INLINE_ATTR TYPE& operator^=(TYPE& a, TYPE b) { a = a ^ b; return a; } \ +FORCE_INLINE_ATTR TYPE& operator>>=(TYPE& a, int b) { a >>= b; return a; } \ +FORCE_INLINE_ATTR TYPE& operator<<=(TYPE& a, int b) { a <<= b; return a; } + +#define FLAG_ATTR_U32(TYPE) FLAG_ATTR_IMPL(TYPE, uint32_t) +#define FLAG_ATTR FLAG_ATTR_U32 + +#else +#define FLAG_ATTR(TYPE) +#endif + +// Implementation for a unique custom section +// +// This prevents gcc producing "x causes a section type conflict with y" +// errors if two variables in the same source file have different linkage (maybe const & non-const) but are placed in the same custom section +// +// Using unique sections also means --gc-sections can remove unused +// data with a custom section type set +#define _SECTION_ATTR_IMPL(SECTION, COUNTER) __attribute__((section(SECTION "." _COUNTER_STRINGIFY(COUNTER)))) + +#define _COUNTER_STRINGIFY(COUNTER) #COUNTER + +/* Use IDF_DEPRECATED attribute to mark anything deprecated from use in + ESP-IDF's own source code, but not deprecated for external users. +*/ +#ifdef IDF_CI_BUILD +#define IDF_DEPRECATED(REASON) __attribute__((deprecated(REASON))) +#else +#define IDF_DEPRECATED(REASON) +#endif + +#ifdef __cplusplus +} +#endif +#endif /* __ESP_ATTR_H__ */ diff --git a/components/riscv/include/esp_debug_helpers.h b/components/riscv/include/esp_debug_helpers.h new file mode 100644 index 0000000000..d088ddeef1 --- /dev/null +++ b/components/riscv/include/esp_debug_helpers.h @@ -0,0 +1,133 @@ +// Copyright 2015-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. + +#pragma once + +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef __ASSEMBLER__ + +#include +#include "esp_err.h" +#include "soc/soc.h" + +#define ESP_WATCHPOINT_LOAD 0x40000000 +#define ESP_WATCHPOINT_STORE 0x80000000 +#define ESP_WATCHPOINT_ACCESS 0xC0000000 + +/* + * @brief Structure used for backtracing + * + * This structure stores the backtrace information of a particular stack frame + * (i.e. the PC and SP). This structure is used iteratively with the + * esp_cpu_get_next_backtrace_frame() function to traverse each frame within a + * single stack. The next_pc represents the PC of the current frame's caller, thus + * a next_pc of 0 indicates that the current frame is the last frame on the stack. + * + * @note Call esp_backtrace_get_start() to obtain initialization values for + * this structure + */ +typedef struct { + uint32_t pc; /* PC of the current frame */ + uint32_t sp; /* SP of the current frame */ + uint32_t next_pc; /* PC of the current frame's caller */ +} esp_backtrace_frame_t; + +/** + * @brief If an OCD is connected over JTAG. set breakpoint 0 to the given function + * address. Do nothing otherwise. + * @param fn Pointer to the target breakpoint position + */ +void esp_set_breakpoint_if_jtag(void *fn); + +/** + * @brief Set a watchpoint to break/panic when a certain memory range is accessed. + * + * @param no Watchpoint number. On the ESP32, this can be 0 or 1. + * @param adr Base address to watch + * @param size Size of the region, starting at the base address, to watch. Must + * be one of 2^n, with n in [0..6]. + * @param flags One of ESP_WATCHPOINT_* flags + * + * @return ESP_ERR_INVALID_ARG on invalid arg, ESP_OK otherwise + * + * @warning The ESP32 watchpoint hardware watches a region of bytes by effectively + * masking away the lower n bits for a region with size 2^n. If adr does + * not have zero for these lower n bits, you may not be watching the + * region you intended. + */ +esp_err_t esp_set_watchpoint(int no, void *adr, int size, int flags); + +/** + * @brief Clear a watchpoint + * + * @param no Watchpoint to clear + * + */ +void esp_clear_watchpoint(int no); + +/** + * Get the first frame of the current stack's backtrace + * + * Given the following function call flow (B -> A -> X -> esp_backtrace_get_start), + * this function will do the following. + * - Flush CPU registers and window frames onto the current stack + * - Return PC and SP of function A (i.e. start of the stack's backtrace) + * - Return PC of function B (i.e. next_pc) + * + * @note This function is implemented in assembly + * + * @param[out] pc PC of the first frame in the backtrace + * @param[out] sp SP of the first frame in the backtrace + * @param[out] next_pc PC of the first frame's caller + */ +extern void esp_backtrace_get_start(uint32_t *pc, uint32_t *sp, uint32_t *next_pc); + +/** + * Get the next frame on a stack for backtracing + * + * Given a stack frame(i), this function will obtain the next stack frame(i-1) + * on the same call stack (i.e. the caller of frame(i)). This function is meant to be + * called iteratively when doing a backtrace. + * + * Entry Conditions: Frame structure containing valid SP and next_pc + * Exit Conditions: + * - Frame structure updated with SP and PC of frame(i-1). next_pc now points to frame(i-2). + * - If a next_pc of 0 is returned, it indicates that frame(i-1) is last frame on the stack + * + * @param[inout] frame Pointer to frame structure + * + * @return + * - True if the SP and PC of the next frame(i-1) are sane + * - False otherwise + */ +bool esp_backtrace_get_next_frame(esp_backtrace_frame_t *frame); + +/** + * @brief Print the backtrace of the current stack + * + * @param depth The maximum number of stack frames to print (should be > 0) + * + * @return + * - ESP_OK Backtrace successfully printed to completion or to depth limit + * - ESP_FAIL Backtrace is corrupted + */ +esp_err_t esp_backtrace_print(int depth); + +#endif +#ifdef __cplusplus +} +#endif diff --git a/components/riscv/include/riscv/csr.h b/components/riscv/include/riscv/csr.h new file mode 100644 index 0000000000..030eb53a93 --- /dev/null +++ b/components/riscv/include/riscv/csr.h @@ -0,0 +1,135 @@ +// Copyright (c) 2013, The Regents of the University of California (Regents). +// Copyright (c) 2018-2019, The libfemto authors +// Copyright (c) 2020 Espressif Systems (Shanghai) PTE LTD +// All Rights Reserved. + +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// 1. Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// 2. Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// 3. Neither the name of the Regents nor the +// names of its contributors may be used to endorse or promote products +// derived from this software without specific prior written permission. + +// IN NO EVENT SHALL REGENTS BE LIABLE TO ANY PARTY FOR DIRECT, INDIRECT, +// SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING LOST PROFITS, ARISING +// OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF REGENTS HAS +// BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// REGENTS SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, BUT NOT LIMITED TO, +// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +// PURPOSE. THE SOFTWARE AND ACCOMPANYING DOCUMENTATION, IF ANY, PROVIDED +// HEREUNDER IS PROVIDED "AS IS". REGENTS HAS NO OBLIGATION TO PROVIDE +// MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + +#pragma once + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include +#include +#include +#include "encoding.h" + +/******************************************************** + Physical Memory Protection (PMP) register fields + (privileged spec) + ********************************************************/ + +/* Value of pmpcfg0 CSR. Note this macro is only needed for calculations like (CSR_PMPCFG0 + 1), which must + still be constant at compile time. Use the assembler name pmpcfg0, pmpcfg1, pmpcfg2, etc. in other cases */ +#define CSR_PMPCFG0 0x3A0 + +/* Value of pmpaddr0 CSR. Note, as above, this macro is only needed for calculations and the assembler names + pmpaddr0, pmpaddr1, pmpaddr2, etc should be used otherwise. */ +#define CSR_PMPADDR0 0x3B0 + +/* Generate the PMP address field value when PMPCFG.A == NAPOT + + START & END should be calculated at compile time. The size of the region + (END-START) must be a power of 2 size, and START must be aligned to this + size. + + Note: this value must be right-shifted PMP_SHIFT when written to the PMPADDR + register. The PMP_ENTRY_SET macro will do this. + */ +#define PMPADDR_NAPOT(START, END) ({ \ + _Static_assert(__builtin_popcount((END)-(START)) == 1, "Size must be a power of 2"); \ + _Static_assert((START) % ((END)-(START)) == 0, "Start must be aligned to size"); \ + (((START)) | (((END)-(START)-1)>>1)); \ + }) + +#define PMPADDR_ALL 0xFFFFFFFF + + +/* Set a PMP entry. + + - ENTRY is number of the PMP entry to set. This must be a compile-time constant because it's used to + generate specific assembly instructions. + - ADDR is the address to write to the PMPADDRx register. Note this is the unshifted address. + - CFG is the configuration value to write to the correct CFG entry register. Note that + the macro only sets bits in the CFG register, so it sould be zeroed already. +*/ +#define PMP_ENTRY_SET(ENTRY, ADDR, CFG) do { \ + RV_WRITE_CSR((CSR_PMPADDR0) + (ENTRY), (ADDR) >> (PMP_SHIFT)); \ + RV_SET_CSR((CSR_PMPCFG0) + (ENTRY)/4, ((CFG)&0xFF) << (ENTRY%4)*8); \ + } while(0) + +/******************************************************** + Trigger Module register fields (Debug specification) + ********************************************************/ + +/* tcontrol CSRs not recognized by toolchain currently */ +#define CSR_TCONTROL 0x7a5 +#define CSR_TDATA1 0x7a1 + +#define TCONTROL_MTE (1<<3) /*R/W, Current M mode trigger enable bit*/ + +#define TDATA1_LOAD (1<<0) /*R/W,Fire trigger on load address match*/ +#define TDATA1_STORE (1<<1) /*R/W,Fire trigger on store address mat*/ +#define TDATA1_EXECUTE (1<<2) /*R/W,Fire trigger on instruction fetch address match*/ +#define TDATA1_USER (1<<3) /*R/W,allow trigger to be fired in user mode*/ +#define TDATA1_MACHINE (1<<6) /*R/W,Allow trigger to be fired while hart is executing in machine mode*/ +#define TDATA1_MATCH +#define TDATA1_MATCH_V (0xF) /*R/W,Address match type :0 : Exact byte match 1 : NAPOT range match */ +#define TDATA1_MATCH_S (7) + + +/* RISC-V CSR macros + * Adapted from https://github.com/michaeljclark/riscv-probe/blob/master/libfemto/include/arch/riscv/machine.h + */ + +#define RV_READ_CONST_CSR(reg) ({ unsigned long __tmp; \ + asm ("csrr %0, " _CSR_STRINGIFY(reg) : "=r"(__tmp)); __tmp; }) + +#define RV_READ_CSR(reg) ({ unsigned long __tmp; \ + asm volatile ("csrr %0, " _CSR_STRINGIFY(reg) : "=r"(__tmp)); __tmp; }) + +#define RV_WRITE_CSR(reg, val) ({ \ + asm volatile ("csrw " _CSR_STRINGIFY(reg) ", %0" :: "rK"(val)); }) + +#define RV_SWAP_CSR(reg, val) ({ unsigned long __tmp; \ + asm volatile ("csrrw %0, " _CSR_STRINGIFY(reg) ", %1" : "=r"(__tmp) : "rK"(val)); __tmp; }) + +/* Note: this uses the atomic read-and-set instruction so possible to read the old CSR value as a result */ +#define RV_SET_CSR(reg, bit) ({ unsigned long __tmp; \ + asm volatile ("csrrs %0, " _CSR_STRINGIFY(reg) ", %1" : "=r"(__tmp) : "rK"(bit)); __tmp; }) + +/* Note: this uses the atomic read-and-clear instruction so possible to read the old CSR value as a result */ +#define RV_CLEAR_CSR(reg, bit) ({ unsigned long __tmp; \ + asm volatile ("csrrc %0, " _CSR_STRINGIFY(reg) ", %1" : "=r"(__tmp) : "rK"(bit)); __tmp; }) + +#define RV_SET_CSR_FIELD(_r, _f, _v) ({ (RV_WRITE_CSR((_r),((RV_READ_CSR(_r) & ~((_f##_V) << (_f##_S)))|(((_v) & (_f##_V))<<(_f##_S)))));}) +#define RV_CLEAR_CSR_FIELD(_r, _f) ({ (RV_WRITE_CSR((_r),(RV_READ_CSR(_r) & ~((_f##_V) << (_f##_S)))));}) + +#define _CSR_STRINGIFY(REG) #REG /* needed so the 'reg' argument can be a macro or a register name */ + +#ifdef __cplusplus +} +#endif diff --git a/components/riscv/include/riscv/encoding.h b/components/riscv/include/riscv/encoding.h new file mode 100644 index 0000000000..91c92a696f --- /dev/null +++ b/components/riscv/include/riscv/encoding.h @@ -0,0 +1,227 @@ +/* Copyright (c) 2010-2017, The Regents of the University of California + (Regents). All Rights Reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + 3. Neither the name of the Regents nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + IN NO EVENT SHALL REGENTS BE LIABLE TO ANY PARTY FOR DIRECT, INDIRECT, + SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING LOST PROFITS, ARISING + OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF REGENTS HAS + BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + REGENTS SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + PURPOSE. THE SOFTWARE AND ACCOMPANYING DOCUMENTATION, IF ANY, PROVIDED + HEREUNDER IS PROVIDED "AS IS". REGENTS HAS NO OBLIGATION TO PROVIDE + MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. +*/ + +/* Adapted from https://github.com/riscv/riscv-opcodes/blob/master/encoding.h */ + +#pragma once + + +#define MSTATUS_UIE 0x00000001 +#define MSTATUS_SIE 0x00000002 +#define MSTATUS_HIE 0x00000004 +#define MSTATUS_MIE 0x00000008 +#define MSTATUS_UPIE 0x00000010 +#define MSTATUS_SPIE 0x00000020 +#define MSTATUS_HPIE 0x00000040 +#define MSTATUS_MPIE 0x00000080 +#define MSTATUS_SPP 0x00000100 +#define MSTATUS_VS 0x00000600 +#define MSTATUS_MPP 0x00001800 +#define MSTATUS_FS 0x00006000 +#define MSTATUS_XS 0x00018000 +#define MSTATUS_MPRV 0x00020000 +#define MSTATUS_SUM 0x00040000 +#define MSTATUS_MXR 0x00080000 +#define MSTATUS_TVM 0x00100000 +#define MSTATUS_TW 0x00200000 +#define MSTATUS_TSR 0x00400000 +#define MSTATUS32_SD 0x80000000 +#define MSTATUS_UXL 0x0000000300000000 +#define MSTATUS_SXL 0x0000000C00000000 +#define MSTATUS64_SD 0x8000000000000000 + +#define SSTATUS_UIE 0x00000001 +#define SSTATUS_SIE 0x00000002 +#define SSTATUS_UPIE 0x00000010 +#define SSTATUS_SPIE 0x00000020 +#define SSTATUS_SPP 0x00000100 +#define SSTATUS_VS 0x00000600 +#define SSTATUS_FS 0x00006000 +#define SSTATUS_XS 0x00018000 +#define SSTATUS_SUM 0x00040000 +#define SSTATUS_MXR 0x00080000 +#define SSTATUS32_SD 0x80000000 +#define SSTATUS_UXL 0x0000000300000000 +#define SSTATUS64_SD 0x8000000000000000 + +#define USTATUS_UIE 0x00000001 +#define USTATUS_UPIE 0x00000010 + +#define DCSR_XDEBUGVER (3U<<30) +#define DCSR_NDRESET (1<<29) +#define DCSR_FULLRESET (1<<28) +#define DCSR_EBREAKM (1<<15) +#define DCSR_EBREAKH (1<<14) +#define DCSR_EBREAKS (1<<13) +#define DCSR_EBREAKU (1<<12) +#define DCSR_STOPCYCLE (1<<10) +#define DCSR_STOPTIME (1<<9) +#define DCSR_CAUSE (7<<6) +#define DCSR_DEBUGINT (1<<5) +#define DCSR_HALT (1<<3) +#define DCSR_STEP (1<<2) +#define DCSR_PRV (3<<0) + +#define DCSR_CAUSE_NONE 0 +#define DCSR_CAUSE_SWBP 1 +#define DCSR_CAUSE_HWBP 2 +#define DCSR_CAUSE_DEBUGINT 3 +#define DCSR_CAUSE_STEP 4 +#define DCSR_CAUSE_HALT 5 +#define DCSR_CAUSE_GROUP 6 + +#define MCONTROL_TYPE(xlen) (0xfULL<<((xlen)-4)) +#define MCONTROL_DMODE(xlen) (1ULL<<((xlen)-5)) +#define MCONTROL_MASKMAX(xlen) (0x3fULL<<((xlen)-11)) + +#define MCONTROL_SELECT (1<<19) +#define MCONTROL_TIMING (1<<18) +#define MCONTROL_ACTION (0x3f<<12) +#define MCONTROL_CHAIN (1<<11) +#define MCONTROL_MATCH (0xf<<7) +#define MCONTROL_M (1<<6) +#define MCONTROL_H (1<<5) +#define MCONTROL_S (1<<4) +#define MCONTROL_U (1<<3) +#define MCONTROL_EXECUTE (1<<2) +#define MCONTROL_STORE (1<<1) +#define MCONTROL_LOAD (1<<0) + +#define MCONTROL_TYPE_NONE 0 +#define MCONTROL_TYPE_MATCH 2 + +#define MCONTROL_ACTION_DEBUG_EXCEPTION 0 +#define MCONTROL_ACTION_DEBUG_MODE 1 +#define MCONTROL_ACTION_TRACE_START 2 +#define MCONTROL_ACTION_TRACE_STOP 3 +#define MCONTROL_ACTION_TRACE_EMIT 4 + +#define MCONTROL_MATCH_EQUAL 0 +#define MCONTROL_MATCH_NAPOT 1 +#define MCONTROL_MATCH_GE 2 +#define MCONTROL_MATCH_LT 3 +#define MCONTROL_MATCH_MASK_LOW 4 +#define MCONTROL_MATCH_MASK_HIGH 5 + +#define MIP_USIP (1 << IRQ_U_SOFT) +#define MIP_SSIP (1 << IRQ_S_SOFT) +#define MIP_HSIP (1 << IRQ_H_SOFT) +#define MIP_MSIP (1 << IRQ_M_SOFT) +#define MIP_UTIP (1 << IRQ_U_TIMER) +#define MIP_STIP (1 << IRQ_S_TIMER) +#define MIP_HTIP (1 << IRQ_H_TIMER) +#define MIP_MTIP (1 << IRQ_M_TIMER) +#define MIP_UEIP (1 << IRQ_U_EXT) +#define MIP_SEIP (1 << IRQ_S_EXT) +#define MIP_HEIP (1 << IRQ_H_EXT) +#define MIP_MEIP (1 << IRQ_M_EXT) + +#define SIP_SSIP MIP_SSIP +#define SIP_STIP MIP_STIP + +#define PRV_U 0 +#define PRV_S 1 +#define PRV_H 2 +#define PRV_M 3 + +#define SATP32_MODE 0x80000000 +#define SATP32_ASID 0x7FC00000 +#define SATP32_PPN 0x003FFFFF +#define SATP64_MODE 0xF000000000000000 +#define SATP64_ASID 0x0FFFF00000000000 +#define SATP64_PPN 0x00000FFFFFFFFFFF + +#define SATP_MODE_OFF 0 +#define SATP_MODE_SV32 1 +#define SATP_MODE_SV39 8 +#define SATP_MODE_SV48 9 +#define SATP_MODE_SV57 10 +#define SATP_MODE_SV64 11 + +#define PMP_R 0x01 +#define PMP_W 0x02 +#define PMP_X 0x04 +#define PMP_A 0x18 +#define PMP_L 0x80 +#define PMP_SHIFT 2 + +#define PMP_TOR 0x08 +#define PMP_NA4 0x10 +#define PMP_NAPOT 0x18 + +#define IRQ_U_SOFT 0 +#define IRQ_S_SOFT 1 +#define IRQ_H_SOFT 2 +#define IRQ_M_SOFT 3 +#define IRQ_U_TIMER 4 +#define IRQ_S_TIMER 5 +#define IRQ_H_TIMER 6 +#define IRQ_M_TIMER 7 +#define IRQ_U_EXT 8 +#define IRQ_S_EXT 9 +#define IRQ_H_EXT 10 +#define IRQ_M_EXT 11 +#define IRQ_COP 12 +#define IRQ_HOST 13 + +#define DEFAULT_RSTVEC 0x00001000 +#define CLINT_BASE 0x02000000 +#define CLINT_SIZE 0x000c0000 +#define EXT_IO_BASE 0x40000000 +#define DRAM_BASE 0x80000000 + +/* page table entry (PTE) fields */ +#define PTE_V 0x001 /* Valid */ +#define PTE_R 0x002 /* Read */ +#define PTE_W 0x004 /* Write */ +#define PTE_X 0x008 /* Execute */ +#define PTE_U 0x010 /* User */ +#define PTE_G 0x020 /* Global */ +#define PTE_A 0x040 /* Accessed */ +#define PTE_D 0x080 /* Dirty */ +#define PTE_SOFT 0x300 /* Reserved for Software */ + +#define PTE_PPN_SHIFT 10 + +#define PTE_TABLE(PTE) (((PTE) & (PTE_V | PTE_R | PTE_W | PTE_X)) == PTE_V) + +#ifdef __riscv + +#if __riscv_xlen == 64 +# define MSTATUS_SD MSTATUS64_SD +# define SSTATUS_SD SSTATUS64_SD +# define RISCV_PGLEVEL_BITS 9 +# define SATP_MODE SATP64_MODE +#else +# define MSTATUS_SD MSTATUS32_SD +# define SSTATUS_SD SSTATUS32_SD +# define RISCV_PGLEVEL_BITS 10 +# define SATP_MODE SATP32_MODE +#endif +#define RISCV_PGSHIFT 12 +#define RISCV_PGSIZE (1 << RISCV_PGSHIFT) + +#endif // __riscv diff --git a/components/riscv/include/riscv/interrupt.h b/components/riscv/include/riscv/interrupt.h new file mode 100644 index 0000000000..6498d57f2f --- /dev/null +++ b/components/riscv/include/riscv/interrupt.h @@ -0,0 +1,115 @@ +// Copyright 2015-2020 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. + +#pragma once + +#ifdef __cplusplus +extern "C" { +#endif + +enum intr_type { + INTR_TYPE_LEVEL = 0, + INTR_TYPE_EDGE +}; +/*************************** Software interrupt dispatcher ***************************/ + +/** Callback type of the interrupt handler */ +typedef void (*intr_handler_t)(void*); + +/** Set the interrupt handler function for the given CPU interrupt + * @param rv_int_num CPU interrupt number + * @param fn Handler function + * @param arg Handler argument + */ +void intr_handler_set(int rv_int_num, intr_handler_t fn, void* arg); + +/** Get the interrupt handler function for the given CPU interrupt + * + *@return interrupt handler registered for a particular interrupt number, or NULL otherwise + */ +intr_handler_t intr_handler_get(int rv_int_num); + +/** Get the interrupt handler argument associated with the given CPU interrupt + * + *@return interrupt handler argument for a particular interrupt number, or NULL otherwise + */ +void *intr_handler_get_arg(int rv_int_num); + +/*************************** Interrupt matrix ***************************/ + +/** + * this function will be removed in later, please use `intr_matrix_set` instead + * Route the peripheral interrupt signal to the CPU + * @param periph_intr_source Peripheral interrupt number, one of ETS_XXX_SOURCE + * @param rv_int_num CPU interrupt number + */ +void intr_matrix_route(int periph_intr_source, int rv_int_num); + +/*************************** ESP-RV Interrupt Controller ***************************/ + +/** + * @brief Enable interrupts from interrupt controller. + * + * @param uint32_t unmask, unmask bits for interrupts, each bit for an interrupt + * + * return none + */ +void esprv_intc_int_enable(uint32_t unmask); + +/** + * @brief Disable interrupts from interrupt controller. + * + * @param uint32_t mask, mask bits for interrupts, each bit for an interrupt + * + * return none + */ +void esprv_intc_int_disable(uint32_t mask); + +/** + * @brief Set interrupt type, level or edge + * + * @param int intr_num, interrupt number + * + * @param enum intr_type type, interrupt type, the level interrupt + can be cleared automatically once the interrupt source cleared, the edge interrupt should be clear by software after handled + * + * return none + */ +void esprv_intc_int_set_type(int intr_num, enum intr_type type); + +/** + * Set interrupt priority in the interrupt controller + * @param rv_int_num CPU interrupt number + * @param priority Interrupt priority level, 1 to 7 + */ +void esprv_intc_int_set_priority(int rv_int_num, int priority); + +/** + * Set interrupt priority threshold. + * Interrupts with priority levels lower than the threshold are masked. + * + * @param priority_threshold Interrupt priority threshold, 0 to 7 + */ +void esprv_intc_set_threshold(int priority_threshold); + +/** + * @brief Get interrupt unmask + * @param none + * @return uint32_t interrupt unmask + */ +uint32_t esprv_intc_get_interrupt_unmask(void); + +#ifdef __cplusplus +} +#endif diff --git a/components/riscv/include/riscv/riscv_interrupts.h b/components/riscv/include/riscv/riscv_interrupts.h new file mode 100644 index 0000000000..b3d225d58c --- /dev/null +++ b/components/riscv/include/riscv/riscv_interrupts.h @@ -0,0 +1,45 @@ +// Copyright 2015-2020 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. + +#pragma once + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * Enable CPU interrupt + * @param rv_int_num CPU interrupt number + */ +void riscv_interrupt_enable(int rv_int_num); + +/** + * Disable CPU interrupt + * @param rv_int_num CPU interrupt number + */ +void riscv_interrupt_disable(int rv_int_num); + +/** + * Globally enable CPU interrupts + */ +void riscv_global_interrupts_enable(void); + +/** + * Globally disable CPU interrupts + */ +void riscv_global_interrupts_disable(void); + +#ifdef __cplusplus +} +#endif diff --git a/components/riscv/include/riscv/rvruntime-frames.h b/components/riscv/include/riscv/rvruntime-frames.h new file mode 100644 index 0000000000..b4e4c141b5 --- /dev/null +++ b/components/riscv/include/riscv/rvruntime-frames.h @@ -0,0 +1,95 @@ +#ifndef __RVRUNTIME_FRAMES_H__ +#define __RVRUNTIME_FRAMES_H__ + +/* Align a value up to nearest n-byte boundary, where n is a power of 2. */ +#define ALIGNUP(n, val) (((val) + (n)-1) & -(n)) + +#ifdef STRUCT_BEGIN +#undef STRUCT_BEGIN +#undef STRUCT_FIELD +#undef STRUCT_AFIELD +#undef STRUCT_END +#endif + +#if defined(_ASMLANGUAGE) || defined(__ASSEMBLER__) + +#define STRUCT_BEGIN .pushsection .text; .struct 0 +#define STRUCT_FIELD(ctype,size,asname,name) asname: .space size +#define STRUCT_AFIELD(ctype,size,asname,name,n) asname: .space (size)*(n) +#define STRUCT_END(sname) sname##Size:; .popsection + +#else + +#define STRUCT_BEGIN typedef struct { +#define STRUCT_FIELD(ctype,size,asname,name) ctype name; +#define STRUCT_AFIELD(ctype,size,asname,name,n) ctype name[n]; +#define STRUCT_END(sname) } sname; + +#endif + +/* +------------------------------------------------------------------------------- + INTERRUPT/EXCEPTION STACK FRAME FOR A EXCEPTION OR NESTED INTERRUPT +------------------------------------------------------------------------------- +*/ +STRUCT_BEGIN +STRUCT_FIELD (long, 4, RV_STK_MEPC, mepc) /* Machine Exception Program Counter */ +STRUCT_FIELD (long, 4, RV_STK_RA, ra) /* Return address */ +STRUCT_FIELD (long, 4, RV_STK_SP, sp) /* Stack pointer */ +STRUCT_FIELD (long, 4, RV_STK_GP, gp) /* Global pointer */ +STRUCT_FIELD (long, 4, RV_STK_TP, tp) /* Thread pointer */ +STRUCT_FIELD (long, 4, RV_STK_T0, t0) /* Temporary/alternate link register */ +STRUCT_FIELD (long, 4, RV_STK_T1, t1) /* t1-2: Temporaries */ +STRUCT_FIELD (long, 4, RV_STK_T2, t2) +STRUCT_FIELD (long, 4, RV_STK_S0, s0) /* Saved register/frame pointer */ +STRUCT_FIELD (long, 4, RV_STK_S1, s1) /* Saved register */ +STRUCT_FIELD (long, 4, RV_STK_A0, a0) /* a0-1: Function arguments/return address */ +STRUCT_FIELD (long, 4, RV_STK_A1, a1) +STRUCT_FIELD (long, 4, RV_STK_A2, a2) /* a2-7: Function arguments */ +STRUCT_FIELD (long, 4, RV_STK_A3, a3) +STRUCT_FIELD (long, 4, RV_STK_A4, a4) +STRUCT_FIELD (long, 4, RV_STK_A5, a5) +STRUCT_FIELD (long, 4, RV_STK_A6, a6) +STRUCT_FIELD (long, 4, RV_STK_A7, a7) +STRUCT_FIELD (long, 4, RV_STK_S2, s2) /* s2-11: Saved registers */ +STRUCT_FIELD (long, 4, RV_STK_S3, s3) +STRUCT_FIELD (long, 4, RV_STK_S4, s4) +STRUCT_FIELD (long, 4, RV_STK_S5, s5) +STRUCT_FIELD (long, 4, RV_STK_S6, s6) +STRUCT_FIELD (long, 4, RV_STK_S7, s7) +STRUCT_FIELD (long, 4, RV_STK_S8, s8) +STRUCT_FIELD (long, 4, RV_STK_S9, s9) +STRUCT_FIELD (long, 4, RV_STK_S10, s10) +STRUCT_FIELD (long, 4, RV_STK_S11, s11) +STRUCT_FIELD (long, 4, RV_STK_T3, t3) /* t3-6: Temporaries */ +STRUCT_FIELD (long, 4, RV_STK_T4, t4) +STRUCT_FIELD (long, 4, RV_STK_T5, t5) +STRUCT_FIELD (long, 4, RV_STK_T6, t6) +STRUCT_FIELD (long, 4, RV_STK_MSTATUS, mstatus) /* Machine Status */ +STRUCT_FIELD (long, 4, RV_STK_MTVEC, mtvec) /* Machine Trap-Vector Base Address */ +STRUCT_FIELD (long, 4, RV_STK_MCAUSE, mcause) /* Machine Trap Cause */ +STRUCT_FIELD (long, 4, RV_STK_PCCRS, pccrs) /* Performance Counter Counter Registers */ +STRUCT_FIELD (long, 4, RV_STK_PCER, pcer) /* Performance Counter Enable */ +STRUCT_FIELD (long, 4, RV_STK_PCMR, pcmr) /* Performance Counter Mode */ +STRUCT_FIELD (long, 4, RV_STK_HWLP, hwlp) /* Hardware Loop Registers */ +STRUCT_FIELD (long, 4, RV_STK_PRIVLV, privlv) /* Privilege Level */ +STRUCT_FIELD (long, 4, RV_STK_UHARTID, uhartid) /* Hardware Thread ID */ +STRUCT_FIELD (long, 4, RV_STK_MHARTID, mhartid) /* Hardware Thread ID */ +STRUCT_FIELD (long, 4, RV_STK_DCSR, dcsr) /* Debug Control and Status */ +STRUCT_FIELD (long, 4, RV_STK_DPC, dpc) /* Debug PC */ +STRUCT_FIELD (long, 4, RV_STK_INTC_THRESH, intc_thresh) +STRUCT_FIELD (long, 4, RV_STK_RESERVED0, reserved0) +STRUCT_END(RvExcFrame) + +#if defined(_ASMLANGUAGE) || defined(__ASSEMBLER__) +# define RV_STK_SZ1 RvExcFrameSize +#else +# define RV_STK_SZ1 sizeof(RvExcFrame) +#endif + +/* + * This is the frame size. + */ +#define RV_STK_FRMSZ (ALIGNUP(0x10, RV_STK_SZ1)) + +#endif /* #ifndef __RVRUNTIME_FRAMES_H__ */ diff --git a/components/riscv/interrupt.c b/components/riscv/interrupt.c new file mode 100644 index 0000000000..1b48bfdf89 --- /dev/null +++ b/components/riscv/interrupt.c @@ -0,0 +1,111 @@ +// Copyright 2020 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. +#include +#include +#include +#include "riscv/interrupt.h" +#include "soc/interrupt_reg.h" +#include "riscv/csr.h" +#include "esp_attr.h" + +#define RV_INT_COUNT 32 + +static inline void assert_valid_rv_int_num(int rv_int_num) +{ + assert(rv_int_num != 0 && rv_int_num < RV_INT_COUNT && "Invalid CPU interrupt number"); +} + +/*************************** Software interrupt dispatcher ***************************/ + + +typedef struct { + intr_handler_t handler; + void *arg; +} intr_handler_item_t; + +static intr_handler_item_t s_intr_handlers[32]; + +void intr_handler_set(int int_no, intr_handler_t fn, void *arg) +{ + assert_valid_rv_int_num(int_no); + + s_intr_handlers[int_no] = (intr_handler_item_t) { + .handler = fn, + .arg = arg + }; +} + +intr_handler_t intr_handler_get(int rv_int_num) +{ + return s_intr_handlers[rv_int_num].handler; +} + +void *intr_handler_get_arg(int rv_int_num) +{ + return s_intr_handlers[rv_int_num].arg; +} + +/* called from vectors.S */ +void _global_interrupt_handler(intptr_t sp, int mcause) +{ + intr_handler_item_t it = s_intr_handlers[mcause]; + if (it.handler) { + (*it.handler)(it.arg); + } +} + +/*************************** RISC-V interrupt enable/disable ***************************/ + +void intr_matrix_route(int intr_src, int intr_num) +{ + assert(intr_num != 0); + + REG_WRITE(DR_REG_INTERRUPT_BASE + 4 * intr_src, intr_num); +} + +void riscv_global_interrupts_enable(void) +{ + RV_SET_CSR(mstatus, MSTATUS_MIE); +} + +void riscv_global_interrupts_disable(void) +{ + RV_CLEAR_CSR(mstatus, MSTATUS_MIE); +} + +uint32_t esprv_intc_get_interrupt_unmask(void) +{ + return REG_READ(INTERRUPT_CORE0_CPU_INT_ENABLE_REG); +} + +/*************************** Exception names. Used in .gdbinit file. ***************************/ + +const char *riscv_excp_names[16] __attribute__((used)) = { + "misaligned_fetch", + "fault_fetch", + "illegal_instruction", + "breakpoint", + "misaligned_load", + "fault_load", + "misaligned_store", + "fault_store", + "user_ecall", + "supervisor_ecall", + "hypervisor_ecall", + "machine_ecall", + "exec_page_fault", + "load_page_fault", + "reserved", + "store_page_fault" +}; diff --git a/components/riscv/linker.lf b/components/riscv/linker.lf new file mode 100644 index 0000000000..cfcb303cf0 --- /dev/null +++ b/components/riscv/linker.lf @@ -0,0 +1,5 @@ +[mapping:riscv] +archive: libriscv.a +entries: + interrupt (noflash_text) + vectors (noflash_text) diff --git a/components/riscv/stdatomic.c b/components/riscv/stdatomic.c new file mode 100644 index 0000000000..7c0c395db5 --- /dev/null +++ b/components/riscv/stdatomic.c @@ -0,0 +1,129 @@ +// Copyright 2015-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. + +//replacement for gcc built-in functions + +#include "sdkconfig.h" +#include +#include +#include "freertos/portmacro.h" + +//reserved to measure atomic operation time +#define atomic_benchmark_intr_disable() +#define atomic_benchmark_intr_restore(STATE) + +// This allows nested interrupts disabling and restoring via local registers or stack. +// They can be called from interrupts too. +// WARNING: Only applies to current CPU. +#define _ATOMIC_ENTER_CRITICAL(void) ({ \ + unsigned state = portENTER_CRITICAL_NESTED(); \ + atomic_benchmark_intr_disable(); \ + state; \ +}) + +#define _ATOMIC_EXIT_CRITICAL(state) do { \ + atomic_benchmark_intr_restore(state); \ + portEXIT_CRITICAL_NESTED(state); \ + } while (0) + +#define CMP_EXCHANGE(n, type) bool __atomic_compare_exchange_ ## n (type* mem, type* expect, type desired, int success, int failure) \ +{ \ + bool ret = false; \ + unsigned state = _ATOMIC_ENTER_CRITICAL(); \ + if (*mem == *expect) { \ + ret = true; \ + *mem = desired; \ + } else { \ + *expect = *mem; \ + } \ + _ATOMIC_EXIT_CRITICAL(state); \ + return ret; \ +} + +#define FETCH_ADD(n, type) type __atomic_fetch_add_ ## n (type* ptr, type value, int memorder) \ +{ \ + unsigned state = _ATOMIC_ENTER_CRITICAL(); \ + type ret = *ptr; \ + *ptr = *ptr + value; \ + _ATOMIC_EXIT_CRITICAL(state); \ + return ret; \ +} + +#define FETCH_SUB(n, type) type __atomic_fetch_sub_ ## n (type* ptr, type value, int memorder) \ +{ \ + unsigned state = _ATOMIC_ENTER_CRITICAL(); \ + type ret = *ptr; \ + *ptr = *ptr - value; \ + _ATOMIC_EXIT_CRITICAL(state); \ + return ret; \ +} + +#define FETCH_AND(n, type) type __atomic_fetch_and_ ## n (type* ptr, type value, int memorder) \ +{ \ + unsigned state = _ATOMIC_ENTER_CRITICAL(); \ + type ret = *ptr; \ + *ptr = *ptr & value; \ + _ATOMIC_EXIT_CRITICAL(state); \ + return ret; \ +} + +#define FETCH_OR(n, type) type __atomic_fetch_or_ ## n (type* ptr, type value, int memorder) \ +{ \ + unsigned state = _ATOMIC_ENTER_CRITICAL(); \ + type ret = *ptr; \ + *ptr = *ptr | value; \ + _ATOMIC_EXIT_CRITICAL(state); \ + return ret; \ +} + +#define FETCH_XOR(n, type) type __atomic_fetch_xor_ ## n (type* ptr, type value, int memorder) \ +{ \ + unsigned state = _ATOMIC_ENTER_CRITICAL(); \ + type ret = *ptr; \ + *ptr = *ptr ^ value; \ + _ATOMIC_EXIT_CRITICAL(state); \ + return ret; \ +} + +#pragma GCC diagnostic ignored "-Wbuiltin-declaration-mismatch" + +CMP_EXCHANGE(1, uint8_t) +CMP_EXCHANGE(2, uint16_t) +CMP_EXCHANGE(4, uint32_t) +CMP_EXCHANGE(8, uint64_t) + +FETCH_ADD(1, uint8_t) +FETCH_ADD(2, uint16_t) +FETCH_ADD(4, uint32_t) +FETCH_ADD(8, uint64_t) + +FETCH_SUB(1, uint8_t) +FETCH_SUB(2, uint16_t) +FETCH_SUB(4, uint32_t) +FETCH_SUB(8, uint64_t) + +FETCH_AND(1, uint8_t) +FETCH_AND(2, uint16_t) +FETCH_AND(4, uint32_t) +FETCH_AND(8, uint64_t) + +FETCH_OR(1, uint8_t) +FETCH_OR(2, uint16_t) +FETCH_OR(4, uint32_t) +FETCH_OR(8, uint64_t) + +FETCH_XOR(1, uint8_t) +FETCH_XOR(2, uint16_t) +FETCH_XOR(4, uint32_t) +FETCH_XOR(8, uint64_t) diff --git a/components/riscv/vectors.S b/components/riscv/vectors.S new file mode 100644 index 0000000000..4348cb1047 --- /dev/null +++ b/components/riscv/vectors.S @@ -0,0 +1,307 @@ +// Copyright 2020 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. +#include "soc/soc.h" +#include "soc/interrupt_reg.h" + + + .equ SAVE_REGS, 32 + .equ CONTEXT_SIZE, (SAVE_REGS * 4) + .equ PANIC_REGS, 38 + .equ PANIC_REGS_SIZE, (PANIC_REGS * 4) + +.altmacro + +.macro lwsp a, b + lw \a, ((\b)*4)(sp) +.endm + +.macro swsp a, b + sw \a, ((\b)*4)(sp) +.endm + + +.macro save_regs + addi sp, sp, -CONTEXT_SIZE + swsp ra, 1 + swsp a0, 2 + swsp a1, 3 + swsp a2, 4 + swsp a3, 5 + swsp a4, 6 + swsp a5, 7 + swsp a6, 8 + swsp a7, 9 + swsp t0, 10 + swsp t1, 11 + swsp t2, 12 + swsp t3, 13 + swsp t4, 14 + swsp t5, 15 + swsp t6, 16 + //swsp sp, 17 + //swsp gp, 18 + swsp tp, 19 + swsp s0, 20 + swsp s1, 21 + swsp s2, 22 + swsp s3, 23 + swsp s4, 24 + swsp s5, 25 + swsp s6, 26 + swsp s7, 27 + swsp s8, 28 + swsp s9, 29 + swsp s10, 30 + swsp s11, 31 +.endm + +.macro save_mepc + csrr t0, mepc + swsp t0, 0 +.endm + +.macro restore_regs + lwsp ra, 1 + lwsp a0, 2 + lwsp a1, 3 + lwsp a2, 4 + lwsp a3, 5 + lwsp a4, 6 + lwsp a5, 7 + lwsp a6, 8 + lwsp a7, 9 + lwsp t0, 10 + lwsp t1, 11 + lwsp t2, 12 + lwsp t3, 13 + lwsp t4, 14 + lwsp t5, 15 + lwsp t6, 16 + //lwsp sp, 17 + //lwsp gp, 18 + lwsp tp, 19 + lwsp s0, 20 + lwsp s1, 21 + lwsp s2, 22 + lwsp s3, 23 + lwsp s4, 24 + lwsp s5, 25 + lwsp s6, 26 + lwsp s7, 27 + lwsp s8, 28 + lwsp s9, 29 + lwsp s10, 30 + lwsp s11, 31 + addi sp, sp, CONTEXT_SIZE +.endm + +.macro restore_mepc + lwsp t0, 0 + csrw mepc, t0 +.endm + + .global vPortYieldFromISR + .global uxInterruptNesting + .global uxSchedulerRunning + .global xIsrStackTop + .global pxCurrentTCB + .global _global_interrupt_handler + + .section .exception_vectors.text + /* This is the vector table. MTVEC points here. + * + * Use 4-byte intructions here. 1 instruction = 1 entry of the table. + * The CPU jumps to MTVEC (i.e. the first entry) in case of an exception, + * and (MTVEC & 0xfffffffc) + (mcause & 0x7fffffff) * 4, in case of an interrupt. + * + * Note: for our CPU, we need to place this on a 256-byte boundary, as CPU + * only uses the 24 MSBs of the MTVEC, i.e. (MTVEC & 0xffffff00). + */ + + .balign 0x100 + .global _vector_table + .type _vector_table, @function +_vector_table: + .option push + .option norvc + j _panic_handler /* exception handler, entry 0 */ + .rept 31 + j _interrupt_handler /* 31 identical entries, all pointing to the interrupt handler */ + .endr + .option pop + .size _vector_table, .-_vector_table + + /* Exception handler.*/ + .global panicHandler + .type _panic_handler, @function +_panic_handler: + addi sp, sp, -PANIC_REGS_SIZE + swsp x0, 0 + swsp x1, 1 + /* x2 is sp - it will be placed on stack later (after we can use the other registers for computation) */ + swsp x3, 3 + swsp x4, 4 + swsp x5, 5 + swsp x6, 6 + swsp x7, 7 + swsp x8, 8 + swsp x9, 9 + swsp x10, 10 + swsp x11, 11 + swsp x12, 12 + swsp x13, 13 + swsp x14, 14 + swsp x15, 15 + swsp x16, 16 + swsp x17, 17 + swsp x18, 18 + swsp x19, 19 + swsp x20, 20 + swsp x21, 21 + swsp x22, 22 + swsp x23, 23 + swsp x24, 24 + swsp x25, 25 + swsp x26, 26 + swsp x27, 27 + swsp x28, 28 + swsp x29, 29 + swsp x30, 30 + swsp x31, 31 + /* "Undo" the modification already done to the sp (x2) by the panic handler */ + addi a1, x2, PANIC_REGS_SIZE + swsp a1, 2 + csrr a1, mcause + swsp a1, 32 + csrr a1, mepc + swsp a1, 33 + csrr a1, mhartid + swsp a1, 34 + csrr a1, mstatus + swsp a1, 35 + csrr a1, mtval + swsp a1, 36 + csrr a1, mtvec + swsp a1, 37 + mv a0, sp + csrr a1, mcause + jal zero, panicHandler + /* panicHandler never returns */ + .size _panic_handler, .-_panic_handler + + /* This is the interrupt handler. + * It saves the registers on the stack, + * prepares for interrupt nesting, + * re-enables the interrupts, + * then jumps to the C dispatcher in interrupt.c. + */ + .global _interrupt_handler + .type _interrupt_handler, @function +_interrupt_handler: + /* entry */ + save_regs + save_mepc + + /* scheduler not enabled, jump directly to ISR handler */ + lw t0, uxSchedulerRunning + beq t0,zero,already_on_handler + + /* increments the ISR nesting count */ + /* This will work properly when we have nested interrupts */ + la t0, uxInterruptNesting + lw t1, 0x0(t0) + addi t2,t1,1 + sw t2, 0x0(t0) + + /* If reached here from another low-prio ISR, skip stack pushing to TCB */ + bne t1,zero, already_on_handler + + /* Otherwise, save current sp, and use the isr stack from here */ + lw t0, pxCurrentTCB + sw sp, 0x0(t0) + lw sp, xIsrStackTop + +already_on_handler: + /* Before dispatch c handler, restore interrupt to enable nested intr */ + csrr s1, mcause + csrr s2, mstatus + + /* Save the interrupt threshold level */ + la t0, INTERRUPT_CORE0_CPU_INT_THRESH_REG + lw s3, 0(t0) + + /* Increase interrupt threshold level */ + la t2, 0x7fffffff + and t1, s1, t2 /* t1 = mcause & mask */ + slli t1, t1, 2 /* t1 = mcause * 4 */ + la t2, INTC_INT_PRIO_REG(0) + add t1, t2, t1 /* t1 = INTC_INT_PRIO_REG + 4 * mcause */ + lw t2, 0(t1) /* t2 = INTC_INT_PRIO_REG[mcause] */ + addi t2, t2, 1 /* t2 = t2 +1 */ + sw t2, 0(t0) /* INTERRUPT_CORE0_CPU_INT_THRESH_REG = t2 */ + fence + + la t0, 0x8 + csrrs t0, mstatus, t0 + + /* call the C dispatcher */ + mv a0, sp /* argument 1, stack pointer */ + csrr a1, mcause /* argument 2, interrupt number */ + /* mask off the interrupt flag of mcause */ + la t0, 0x7fffffff + and a1, a1, t0 + jal _global_interrupt_handler + + /* After dispatch c handler, disable interrupt to make freertos make context switch */ + + la t0, 0x8 + csrrc t0, mstatus, t0 + + /* restore the interrupt threshold level */ + la t0, INTERRUPT_CORE0_CPU_INT_THRESH_REG + sw s3, 0(t0) + fence + + /* may skip RTOS aware interrupt since scheduler was not started */ + lw t1, uxSchedulerRunning + beq t1,zero, isr_exit + + /* update nesting interrupts counter */ + la t0, uxInterruptNesting + lw t1, 0x0(t0) + + /* Already zero, protect againts underflow */ + beq t1, zero, isr_skip_decrement + addi t1,t1, -1 + sw t1, 0x0(t0) + +isr_skip_decrement: + /* may still have interrupts pending, skip section below and exit */ + bne t1,zero,isr_exit + + /* handlered all the ISRs and scheduled the next task, take its stack */ + /* load on sp, then exit. */ + lw sp, pxCurrentTCB + lw sp, 0x0(sp) + +isr_exit: + /* restore the rest of the registers */ + csrw mcause, s1 + csrw mstatus, s2 + restore_mepc + restore_regs + + /* exit, this will also re-enable the interrupts */ + mret + .size _interrupt_handler, .-_interrupt_handler