1 /**
2  *
3  * Copyright (c) 2019 Microchip Technology Inc. and its subsidiaries.
4  *
5  * \asf_license_start
6  *
7  * \page License
8  *
9  * SPDX-License-Identifier: Apache-2.0
10  *
11  * Licensed under the Apache License, Version 2.0 (the "License"); you may
12  * not use this file except in compliance with the License.
13  * You may obtain a copy of the Licence at
14  *
15  * http://www.apache.org/licenses/LICENSE-2.0
16  *
17  * Unless required by applicable law or agreed to in writing, software
18  * distributed under the License is distributed on an AS IS BASIS, WITHOUT
19  * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
20  * See the License for the specific language governing permissions and
21  * limitations under the License.
22  *
23  * \asf_license_stop
24  *
25  */
26 
27 /** @file cpu.h
28  *MEC1701 CPU abstractions
29  */
30 /** @defgroup cpu
31  */
32 
33 
34 #ifndef _CPU_H
35 #define _CPU_H
36 
37 #include <stdint.h>
38 
39 #ifdef __cplusplus
40 extern "C" {
41 #endif
42 
43 
44 #ifdef __CC_ARM /* Keil ARM MDK */
45 
46 #ifndef UINT8_C
47 #define UINT8_C(x) (unsigned char)(x)
48 #endif
49 
50 #ifndef UINT16_C
51 #define UINT16_C(x) (unsigned int)(x)
52 #endif
53 
54 #ifndef UINT32_C
55 #define UINT32_C(x) (unsigned long)(x)
56 #endif
57 
58 #define USED __attribute__((used))
59 #define WEAK __attribute__((weak))
60 #define INLINE __inline
61 #define NORETURN __declspec(noreturn)
62 #define PACKED __packed
63 #define CPU_GET_INTERRUPT_STATE() __get_PRIMASK()
64 #define CPU_SET_INTERRUPT_STATE(x) __set_PRIMASK((x))
65 
66 /*
67  * Keil MDK intrisic __disable_irq() returns the value of MSR PRIMASK
68  * before disabling interrupts.
69 */
70 #define CPU_DISABLE_INTERRUPTS() __disable_irq()
71 #define CPU_GET_DISABLE_INTERRUPTS(x) {(x) = __disable_irq();}
72 #define CPU_RESTORE_INTERRUPTS(x) { if (!(x)) { __enable_irq(); } }
73 #define CPU_ENABLE_INTERRUPTS() __enable_irq()
74 
75 #define CPU_NOP() __nop()
76 #define CPU_WAIT_FOR_INTR() __wfi()
77 
78 #define CPU_REV(x) __rev(x)
79 
80 #define CPU_CLZ(x) __clz(x)
81 
82 #elif defined(__XC32_PART_SUPPORT_VERSION) /* Microchip XC32 compiler customized GCC */
83 
84 #error "!!! FORCED BUILD ERROR: compiler.h XC32 support has not been implemented !!!"
85 
86 #elif defined(__GNUC__) && defined(__ARM_EABI__)    /* GCC for ARM (arm-none-eabi-gcc) */
87 
88 #include <stdint.h>
89 #include <stddef.h>
90 
91 #ifndef __always_inline
92 #define __always_inline inline __attribute__((always_inline))
93 #endif
94 
95 static __always_inline void __NOP_THUMB2(void)
96 {
97 	__asm volatile ("nop");
98 }
99 
100 #define CPU_NOP() __NOP_THUMB2()
101 
102 static __always_inline void __WFI_THUMB2(void)
103 {
104 	__asm volatile ("dsb");
105 	__asm volatile ("isb");
106 	__asm volatile ("wfi");
107 	__asm volatile ("nop");
108 	__asm volatile ("nop");
109 	__asm volatile ("nop");
110 }
111 
112 #define CPU_WAIT_FOR_INTR() __WFI_THUMB2()
113 
114 /* We require user have ARM CMSIS header files available and configured
115  * core_cmFunc.h includes inlines for global interrupt control.
116  * For some reason, CMSIS __disable_irq for GCC does not return current PRIMASK
117  * value. */
118 
119 static __always_inline uint32_t __get_disable_irq(void)
120 {
121 	uint32_t pri_mask;
122 
123 	__asm volatile (
124 		"\tmrs %0, primask\n"
125 		"\tcpsid i\n"
126 		"\tdsb\n"
127 		"\tisb\n"
128 		: "=r" (pri_mask) :: "memory"
129 	);
130 	return pri_mask;
131 }
132 
133 static __always_inline uint32_t __get_primask(void)
134 {
135 	uint32_t pri_mask;
136 
137 	__asm volatile (
138 		"\tmrs %0, primask\n"
139 		"\tisb\n"
140 		: "=r" (pri_mask) :: "memory"
141 	);
142 	return pri_mask;
143 }
144 
145 static __always_inline void __enable_irqs(void)
146 {
147 	__asm volatile ("cpsie i" : : : "memory");
148 }
149 
150 static __always_inline void __disable_irqs(void)
151 {
152     __asm volatile (
153 	"\tcpsid i\n"
154 	"\tdsb\n"
155 	"\tisb\n" : : : "memory");
156 }
157 
158 
159 #define CPU_GET_INTERRUPT_STATE() __get_primask()
160 #define CPU_GET_DISABLE_INTERRUPTS(x) {(x) = __get_disable_irq();}
161 #define CPU_RESTORE_INTERRUPTS(x) { if (!(x)) { __enable_irqs(); } }
162 #define CPU_ENABLE_INTERRUPTS() __enable_irqs()
163 #define CPU_DISABLE_INTERRUPTS() __disable_irqs()
164 
165 
166 static __always_inline uint32_t __REV_THUMB2(uint32_t u32)
167 {
168 	return __builtin_bswap32(u32);
169 }
170 
171 #define CPU_REV(x) __REV_THUMB2(x)
172 
173 /*
174  * __builtin_clz() will not be available if user compiles with built-ins disabled flag.
175  */
176 #define CPU_CLZ(x) __builtin_clz(x)
177 
178 static inline __attribute__((always_inline, noreturn)) void CPU_JMP(uint32_t addr)
179 {
180 	addr |= (1ul << 0);
181 
182 	__asm volatile (
183 		"\n\t"
184 		"\tBX  %0 \n"
185 		"\tNOP \n"
186 		:   /* no outputs */
187 		:"r"(addr)
188 		:);
189 	while(1);
190 }
191 
192 static __always_inline void write_read_back8(volatile uint8_t* addr, uint8_t val)
193 {
194 	__asm__ __volatile__ (
195 		"\n\t"
196 		"strb %1, [%0]      \n\t"
197 		"ldrb %1, [%0]      \n\t"
198 		: /* No outputs */
199 		: "r" (addr), "r" (val)
200 		: "memory"
201 	);
202 }
203 
204 static __always_inline void write_read_back16(volatile uint16_t* addr, uint16_t val)
205 {
206 	__asm__ __volatile__ (
207 		"\n\t"
208 		"strh %1, [%0]      \n\t"
209 		"ldrh %1, [%0]      \n\t"
210 		: /* No outputs */
211 		: "r" (addr), "r" (val)
212 		: "memory"
213 	);
214 }
215 
216 static __always_inline void write_read_back32(volatile uint32_t* addr, uint32_t val)
217 {
218 	__asm__ __volatile__ (
219 		"\n\t"
220 		"str %1, [%0]       \n\t"
221 		"ldr %1, [%0]       \n\t"
222 		: /* No outputs */
223 		: "r" (addr), "r" (val)
224 		: "memory"
225 	);
226 }
227 
228 #else  /* Unknown compiler */
229 
230 #error "!!! FORCED BUILD ERROR: cpu.h Unknown compiler !!!"
231 
232 #endif
233 
234 #ifdef __cplusplus
235 }
236 #endif
237 
238 #endif /* #ifndef _CPU_H */
239 /**   @}
240  */
241