1 /***************************************************************************//**
2 * @file
3 * @brief RAM and peripheral bit-field set and clear API
4 *******************************************************************************
5 * # License
6 * <b>Copyright 2018 Silicon Laboratories Inc. www.silabs.com</b>
7 *******************************************************************************
8 *
9 * SPDX-License-Identifier: Zlib
10 *
11 * The licensor of this software is Silicon Laboratories Inc.
12 *
13 * This software is provided 'as-is', without any express or implied
14 * warranty. In no event will the authors be held liable for any damages
15 * arising from the use of this software.
16 *
17 * Permission is granted to anyone to use this software for any purpose,
18 * including commercial applications, and to alter it and redistribute it
19 * freely, subject to the following restrictions:
20 *
21 * 1. The origin of this software must not be misrepresented; you must not
22 * claim that you wrote the original software. If you use this software
23 * in a product, an acknowledgment in the product documentation would be
24 * appreciated but is not required.
25 * 2. Altered source versions must be plainly marked as such, and must not be
26 * misrepresented as being the original software.
27 * 3. This notice may not be removed or altered from any source distribution.
28 *
29 ******************************************************************************/
30
31 #ifndef EM_BUS_H
32 #define EM_BUS_H
33
34 #include "em_core.h"
35 #include "em_device.h"
36
37 #ifdef __cplusplus
38 extern "C" {
39 #endif
40
41 /***************************************************************************//**
42 * @addtogroup bus BUS - Bitfield Read/Write
43 * @brief BUS register and RAM bit/field read/write API
44 * @details
45 * API to perform bit-band and field set/clear access to RAM and peripherals.
46 * @{
47 ******************************************************************************/
48
49 /***************************************************************************//**
50 * @brief
51 * Perform a single-bit write operation on a 32-bit word in RAM.
52 *
53 * @details
54 * This function uses Cortex-M bit-banding hardware to perform an atomic
55 * read-modify-write operation on a single bit write on a 32-bit word in RAM.
56 * See the reference manual for more details about bit-banding.
57 *
58 * @note
59 * This function is atomic on Cortex-M cores with bit-banding support. Bit-
60 * banding is a multicycle read-modify-write bus operation. RAM bit-banding is
61 * performed using the memory alias region at BITBAND_RAM_BASE.
62 *
63 * @param[in] addr An ddress of a 32-bit word in RAM.
64 *
65 * @param[in] bit A bit position to write, 0-31.
66 *
67 * @param[in] val A value to set bit to, 0 or 1.
68 ******************************************************************************/
BUS_RamBitWrite(volatile uint32_t * addr,unsigned int bit,unsigned int val)69 __STATIC_INLINE void BUS_RamBitWrite(volatile uint32_t *addr,
70 unsigned int bit,
71 unsigned int val)
72 {
73 #if defined(BITBAND_RAM_BASE)
74 uint32_t aliasAddr =
75 BITBAND_RAM_BASE + (((uint32_t)addr - SRAM_BASE) * (uint32_t) 32) + (bit * (uint32_t) 4);
76
77 *(volatile uint32_t *)aliasAddr = (uint32_t)val;
78 #else
79 uint32_t tmp = *addr;
80
81 /* Make sure val is not more than 1 because only one bit needs to be set. */
82 *addr = (tmp & ~(1 << bit)) | ((val & 1) << bit);
83 #endif
84 }
85
86 /***************************************************************************//**
87 * @brief
88 * Perform a single-bit read operation on a 32-bit word in RAM.
89 *
90 * @details
91 * This function uses Cortex-M bit-banding hardware to perform an atomic
92 * read operation on a single register bit. See the
93 * reference manual for more details about bit-banding.
94 *
95 * @note
96 * This function is atomic on Cortex-M cores with bit-banding support.
97 * RAM bit-banding is performed using the memory alias region
98 * at BITBAND_RAM_BASE.
99 *
100 * @param[in] addr RAM address.
101 *
102 * @param[in] bit A bit position to read, 0-31.
103 *
104 * @return
105 * The requested bit shifted to bit position 0 in the return value.
106 ******************************************************************************/
BUS_RamBitRead(volatile const uint32_t * addr,unsigned int bit)107 __STATIC_INLINE unsigned int BUS_RamBitRead(volatile const uint32_t *addr,
108 unsigned int bit)
109 {
110 #if defined(BITBAND_RAM_BASE)
111 uint32_t aliasAddr =
112 BITBAND_RAM_BASE + (((uint32_t)addr - SRAM_BASE) * (uint32_t) 32) + (bit * (uint32_t) 4);
113
114 return *(volatile uint32_t *)aliasAddr;
115 #else
116 return ((*addr) >> bit) & 1;
117 #endif
118 }
119
120 /***************************************************************************//**
121 * @brief
122 * Perform a single-bit write operation on a peripheral register.
123 *
124 * @details
125 * This function uses Cortex-M bit-banding hardware to perform an atomic
126 * read-modify-write operation on a single register bit. See the
127 * reference manual for more details about bit-banding.
128 *
129 * @note
130 * This function is atomic on Cortex-M cores with bit-banding support. Bit-
131 * banding is a multicycle read-modify-write bus operation. Peripheral register
132 * bit-banding is performed using the memory alias region at BITBAND_PER_BASE.
133 *
134 * @param[in] addr A peripheral register address.
135 *
136 * @param[in] bit A bit position to write, 0-31.
137 *
138 * @param[in] val A value to set bit to, 0 or 1.
139 ******************************************************************************/
BUS_RegBitWrite(volatile uint32_t * addr,unsigned int bit,unsigned int val)140 __STATIC_INLINE void BUS_RegBitWrite(volatile uint32_t *addr,
141 unsigned int bit,
142 unsigned int val)
143 {
144 #if defined(PER_REG_BLOCK_SET_OFFSET) && defined(PER_REG_BLOCK_CLR_OFFSET)
145 uint32_t aliasAddr;
146 if (val) {
147 aliasAddr = (uint32_t)addr + PER_REG_BLOCK_SET_OFFSET;
148 } else {
149 aliasAddr = (uint32_t)addr + PER_REG_BLOCK_CLR_OFFSET;
150 }
151 *(volatile uint32_t *)aliasAddr = 1 << bit;
152 #elif defined(BITBAND_PER_BASE)
153 uint32_t aliasAddr =
154 BITBAND_PER_BASE + (((uint32_t)addr - PER_MEM_BASE) * (uint32_t) 32) + (bit * (uint32_t) 4);
155
156 *(volatile uint32_t *)aliasAddr = (uint32_t)val;
157 #else
158 uint32_t tmp = *addr;
159
160 /* Make sure val is not more than 1 because only one bit needs to be set. */
161 *addr = (tmp & ~(1 << bit)) | ((val & 1) << bit);
162 #endif
163 }
164
165 /***************************************************************************//**
166 * @brief
167 * Perform a single-bit read operation on a peripheral register.
168 *
169 * @details
170 * This function uses Cortex-M bit-banding hardware to perform an atomic
171 * read operation on a single register bit. See the
172 * reference manual for more details about bit-banding.
173 *
174 * @note
175 * This function is atomic on Cortex-M cores with bit-banding support.
176 * Peripheral register bit-banding is performed using the memory alias
177 * region at BITBAND_PER_BASE.
178 *
179 * @param[in] addr A peripheral register address.
180 *
181 * @param[in] bit A bit position to read, 0-31.
182 *
183 * @return
184 * The requested bit shifted to bit position 0 in the return value.
185 ******************************************************************************/
BUS_RegBitRead(volatile const uint32_t * addr,unsigned int bit)186 __STATIC_INLINE unsigned int BUS_RegBitRead(volatile const uint32_t *addr,
187 unsigned int bit)
188 {
189 #if defined(BITBAND_PER_BASE)
190 uint32_t aliasAddr =
191 BITBAND_PER_BASE + (((uint32_t)addr - PER_MEM_BASE) * (uint32_t)32) + (bit * (uint32_t) 4);
192
193 return *(volatile uint32_t *)aliasAddr;
194 #else
195 return ((*addr) >> bit) & 1;
196 #endif
197 }
198
199 /***************************************************************************//**
200 * @brief
201 * Perform a masked set operation on a peripheral register address.
202 *
203 * @details
204 * A peripheral register masked set provides a single-cycle and atomic set
205 * operation of a bit-mask in a peripheral register. All 1s in the mask are
206 * set to 1 in the register. All 0s in the mask are not changed in the
207 * register.
208 * RAMs and special peripherals are not supported. See the
209 * reference manual for more details about the peripheral register field set.
210 *
211 * @note
212 * This function is single-cycle and atomic on cores with peripheral bit set
213 * and clear support. It uses the memory alias region at PER_BITSET_MEM_BASE.
214 *
215 * @param[in] addr A peripheral register address.
216 *
217 * @param[in] mask A mask to set.
218 ******************************************************************************/
BUS_RegMaskedSet(volatile uint32_t * addr,uint32_t mask)219 __STATIC_INLINE void BUS_RegMaskedSet(volatile uint32_t *addr,
220 uint32_t mask)
221 {
222 #if defined(PER_REG_BLOCK_SET_OFFSET)
223 uint32_t aliasAddr = (uint32_t)addr + PER_REG_BLOCK_SET_OFFSET;
224 *(volatile uint32_t *)aliasAddr = mask;
225 #elif defined(PER_BITSET_MEM_BASE)
226 uint32_t aliasAddr = PER_BITSET_MEM_BASE + ((uint32_t)addr - PER_MEM_BASE);
227 *(volatile uint32_t *)aliasAddr = mask;
228 #else
229 CORE_DECLARE_IRQ_STATE;
230
231 CORE_ENTER_CRITICAL();
232 *addr |= mask;
233 CORE_EXIT_CRITICAL();
234 #endif
235 }
236
237 /***************************************************************************//**
238 * @brief
239 * Perform a masked clear operation on the peripheral register address.
240 *
241 * @details
242 * A peripheral register masked clear provides a single-cycle and atomic clear
243 * operation of a bit-mask in a peripheral register. All 1s in the mask are
244 * set to 0 in the register.
245 * All 0s in the mask are not changed in the register.
246 * RAMs and special peripherals are not supported. See the
247 * reference manual for more details about the peripheral register field clear.
248 *
249 * @note
250 * This function is single-cycle and atomic on cores with peripheral bit set
251 * and clear support. It uses the memory alias region at PER_BITCLR_MEM_BASE.
252 *
253 * @param[in] addr A peripheral register address.
254 *
255 * @param[in] mask A mask to clear.
256 ******************************************************************************/
BUS_RegMaskedClear(volatile uint32_t * addr,uint32_t mask)257 __STATIC_INLINE void BUS_RegMaskedClear(volatile uint32_t *addr,
258 uint32_t mask)
259 {
260 #if defined(PER_REG_BLOCK_CLR_OFFSET)
261 uint32_t aliasAddr = (uint32_t)addr + PER_REG_BLOCK_CLR_OFFSET;
262 *(volatile uint32_t *)aliasAddr = mask;
263 #elif defined(PER_BITCLR_MEM_BASE)
264 uint32_t aliasAddr = PER_BITCLR_MEM_BASE + ((uint32_t)addr - PER_MEM_BASE);
265 *(volatile uint32_t *)aliasAddr = mask;
266 #else
267 CORE_DECLARE_IRQ_STATE;
268
269 CORE_ENTER_CRITICAL();
270 *addr &= ~mask;
271 CORE_EXIT_CRITICAL();
272 #endif
273 }
274
275 /***************************************************************************//**
276 * @brief
277 * Perform peripheral register masked write.
278 *
279 * @details
280 * This function first reads the peripheral register and updates only bits
281 * that are set in the mask with content of val. Typically, the mask is a
282 * bit-field in the register and the value val is within the mask.
283 *
284 * @note
285 * The read-modify-write operation is executed in a critical section to
286 * guarantee atomicity. Note that atomicity can only be guaranteed if register
287 * is modified only by the core, and not by other peripherals (like DMA).
288 *
289 * @param[in] addr A peripheral register address.
290 *
291 * @param[in] mask A peripheral register mask.
292 *
293 * @param[in] val A peripheral register value. The value must be shifted to the
294 correct bit position in the register corresponding to the field
295 defined by the mask parameter. The register value must be
296 contained in the field defined by the mask parameter. The
297 register value is masked to prevent involuntary spillage.
298 ******************************************************************************/
BUS_RegMaskedWrite(volatile uint32_t * addr,uint32_t mask,uint32_t val)299 __STATIC_INLINE void BUS_RegMaskedWrite(volatile uint32_t *addr,
300 uint32_t mask,
301 uint32_t val)
302 {
303 CORE_DECLARE_IRQ_STATE;
304
305 CORE_ENTER_CRITICAL();
306 *addr = (*addr & ~mask) | (val & mask);
307 CORE_EXIT_CRITICAL();
308 }
309
310 /***************************************************************************//**
311 * @brief
312 * Perform a peripheral register masked read.
313 *
314 * @details
315 * Read an unshifted and masked value from a peripheral register.
316 *
317 * @note
318 * This operation is not hardware accelerated.
319 *
320 * @param[in] addr A peripheral register address.
321 *
322 * @param[in] mask A peripheral register mask.
323 *
324 * @return
325 * An unshifted and masked register value.
326 ******************************************************************************/
BUS_RegMaskedRead(volatile const uint32_t * addr,uint32_t mask)327 __STATIC_INLINE uint32_t BUS_RegMaskedRead(volatile const uint32_t *addr,
328 uint32_t mask)
329 {
330 return *addr & mask;
331 }
332
333 /** @} (end addtogroup bus) */
334
335 #ifdef __cplusplus
336 }
337 #endif
338
339 #endif /* EM_BUS_H */
340