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