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