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