1 2 /** \addtogroup platform */ 3 /** @{*/ 4 /* General C++ Object Thunking class 5 * 6 * - allows direct callbacks to non-static C++ class functions 7 * - keeps track for the corresponding class instance 8 * - supports an optional context parameter for the called function 9 * - ideally suited for class object receiving interrupts (NVIC_SetVector) 10 * 11 * Copyright (c) 2014-2015 ARM Limited 12 * 13 * Licensed under the Apache License, Version 2.0 (the "License"); 14 * you may not use this file except in compliance with the License. 15 * You may obtain a copy of the License at 16 * 17 * http://www.apache.org/licenses/LICENSE-2.0 18 * 19 * Unless required by applicable law or agreed to in writing, software 20 * distributed under the License is distributed on an "AS IS" BASIS, 21 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 22 * See the License for the specific language governing permissions and 23 * limitations under the License. 24 */ 25 26 /* General C++ Object Thunking class 27 * 28 * - allows direct callbacks to non-static C++ class functions 29 * - keeps track for the corresponding class instance 30 * - supports an optional context parameter for the called function 31 * - ideally suited for class object receiving interrupts (NVIC_SetVector) 32 */ 33 34 #ifndef __CTHUNK_H__ 35 #define __CTHUNK_H__ 36 37 #define CTHUNK_ADDRESS 1 38 #define CTHUNK_VARIABLES volatile uint32_t code[2] 39 40 #if (defined(__CORTEX_M3) || defined(__CORTEX_M4) || defined(__CORTEX_M7) || defined(__CORTEX_A9)) 41 /** 42 * CTHUNK disassembly for Cortex-M3/M4/M7/A9 (thumb2): 43 * * adr r0, #4 44 * * ldm r0, {r0, r1, r2, pc} 45 * 46 * This instruction loads the arguments for the static thunking function to r0-r2, and 47 * branches to that function by loading its address into PC. 48 * 49 * This is safe for both regular calling and interrupt calling, since it only touches scratch registers 50 * which should be saved by the caller, and are automatically saved as part of the IRQ context switch. 51 */ 52 #define CTHUNK_ASSIGMENT do { \ 53 m_thunk.code[0] = 0xE890A001; \ 54 m_thunk.code[1] = 0x00008007; \ 55 } while (0) 56 57 #elif (defined(__CORTEX_M0PLUS) || defined(__CORTEX_M0)) 58 /* 59 * CTHUNK disassembly for Cortex M0/M0+ (thumb): 60 * * adr r0, #4 61 * * ldm r0, {r0, r1, r2, r3} 62 * * bx r3 63 */ 64 #define CTHUNK_ASSIGMENT do { \ 65 m_thunk.code[0] = 0xC80FA001; \ 66 m_thunk.code[1] = 0x00004718; \ 67 } while (0) 68 69 #else 70 #error "Target is not currently suported." 71 #endif 72 73 /* IRQ/Exception compatible thunk entry function */ 74 typedef void (*CThunkEntry)(void); 75 76 /** 77 * Class for created a pointer with data bound to it 78 * 79 * @Note Synchronization level: Not protected 80 */ 81 template<class T> 82 class CThunk 83 { 84 public: 85 typedef void (T::*CCallbackSimple)(void); 86 typedef void (T::*CCallback)(void* context); 87 CThunk(T * instance)88 inline CThunk(T *instance) 89 { 90 init(instance, NULL, NULL); 91 } 92 CThunk(T * instance,CCallback callback)93 inline CThunk(T *instance, CCallback callback) 94 { 95 init(instance, callback, NULL); 96 } 97 ~CThunk()98 ~CThunk() { 99 100 } 101 CThunk(T * instance,CCallbackSimple callback)102 inline CThunk(T *instance, CCallbackSimple callback) 103 { 104 init(instance, (CCallback)callback, NULL); 105 } 106 CThunk(T & instance,CCallback callback)107 inline CThunk(T &instance, CCallback callback) 108 { 109 init(instance, callback, NULL); 110 } 111 CThunk(T & instance,CCallbackSimple callback)112 inline CThunk(T &instance, CCallbackSimple callback) 113 { 114 init(instance, (CCallback)callback, NULL); 115 } 116 CThunk(T & instance,CCallback callback,void * context)117 inline CThunk(T &instance, CCallback callback, void* context) 118 { 119 init(instance, callback, context); 120 } 121 callback(CCallback callback)122 inline void callback(CCallback callback) 123 { 124 m_callback = callback; 125 } 126 callback(CCallbackSimple callback)127 inline void callback(CCallbackSimple callback) 128 { 129 m_callback = (CCallback)callback; 130 } 131 context(void * context)132 inline void context(void* context) 133 { 134 m_thunk.context = (uint32_t)context; 135 } 136 context(uint32_t context)137 inline void context(uint32_t context) 138 { 139 m_thunk.context = context; 140 } 141 entry(void)142 inline uint32_t entry(void) 143 { 144 return (((uint32_t)&m_thunk)|CTHUNK_ADDRESS); 145 } 146 147 /* get thunk entry point for connecting rhunk to an IRQ table */ CThunkEntry(void)148 inline operator CThunkEntry(void) 149 { 150 return (CThunkEntry)entry(); 151 } 152 153 /* get thunk entry point for connecting rhunk to an IRQ table */ uint32_t(void)154 inline operator uint32_t(void) 155 { 156 return entry(); 157 } 158 159 /* simple test function */ call(void)160 inline void call(void) 161 { 162 (((CThunkEntry)(entry()))()); 163 } 164 165 private: 166 T* m_instance; 167 volatile CCallback m_callback; 168 169 // TODO: this needs proper fix, to refactor toolchain header file and all its use 170 // PACKED there is not defined properly for IAR 171 #if defined (__ICCARM__) 172 typedef __packed struct 173 { 174 CTHUNK_VARIABLES; 175 volatile uint32_t instance; 176 volatile uint32_t context; 177 volatile uint32_t callback; 178 volatile uint32_t trampoline; 179 } CThunkTrampoline; 180 #else 181 typedef struct 182 { 183 CTHUNK_VARIABLES; 184 volatile uint32_t instance; 185 volatile uint32_t context; 186 volatile uint32_t callback; 187 volatile uint32_t trampoline; 188 } __attribute__((__packed__)) CThunkTrampoline; 189 #endif 190 trampoline(T * instance,void * context,CCallback * callback)191 static void trampoline(T* instance, void* context, CCallback* callback) 192 { 193 if(instance && *callback) { 194 (static_cast<T*>(instance)->**callback)(context); 195 } 196 } 197 198 volatile CThunkTrampoline m_thunk; 199 init(T * instance,CCallback callback,void * context)200 inline void init(T *instance, CCallback callback, void* context) 201 { 202 /* remember callback - need to add this level of redirection 203 as pointer size for member functions differs between platforms */ 204 m_callback = callback; 205 206 /* populate thunking trampoline */ 207 CTHUNK_ASSIGMENT; 208 m_thunk.context = (uint32_t)context; 209 m_thunk.instance = (uint32_t)instance; 210 m_thunk.callback = (uint32_t)&m_callback; 211 m_thunk.trampoline = (uint32_t)&trampoline; 212 213 #if defined(__CORTEX_A9) 214 /* Data cache clean */ 215 /* Cache control */ 216 { 217 uint32_t start_addr = (uint32_t)&m_thunk & 0xFFFFFFE0; 218 uint32_t end_addr = (uint32_t)&m_thunk + sizeof(m_thunk); 219 uint32_t addr; 220 221 /* Data cache clean and invalid */ 222 for (addr = start_addr; addr < end_addr; addr += 0x20) { 223 __v7_clean_inv_dcache_mva((void *)addr); 224 } 225 /* Instruction cache invalid */ 226 __v7_inv_icache_all(); 227 __ca9u_inv_tlb_all(); 228 __v7_inv_btac(); 229 } 230 #endif 231 #if defined(__CORTEX_M7) 232 /* Data cache clean and invalid */ 233 SCB_CleanInvalidateDCache(); 234 235 /* Instruction cache invalid */ 236 SCB_InvalidateICache(); 237 #endif 238 __ISB(); 239 __DSB(); 240 } 241 }; 242 243 #endif/*__CTHUNK_H__*/ 244 245 /** @}*/ 246