TNeo  v1.08
tn_arch_pic24_bfa.h
Go to the documentation of this file.
1 /*******************************************************************************
2  *
3  * TNeo: real-time kernel initially based on TNKernel
4  *
5  * TNKernel: copyright 2004, 2013 Yuri Tiomkin.
6  * PIC32-specific routines: copyright 2013, 2014 Anders Montonen.
7  * TNeo: copyright 2014 Dmitry Frank.
8  *
9  * TNeo was born as a thorough review and re-implementation of
10  * TNKernel. The new kernel has well-formed code, inherited bugs are fixed
11  * as well as new features being added, and it is tested carefully with
12  * unit-tests.
13  *
14  * API is changed somewhat, so it's not 100% compatible with TNKernel,
15  * hence the new name: TNeo.
16  *
17  * Permission to use, copy, modify, and distribute this software in source
18  * and binary forms and its documentation for any purpose and without fee
19  * is hereby granted, provided that the above copyright notice appear
20  * in all copies and that both that copyright notice and this permission
21  * notice appear in supporting documentation.
22  *
23  * THIS SOFTWARE IS PROVIDED BY THE DMITRY FRANK AND CONTRIBUTORS "AS IS"
24  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
26  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL DMITRY FRANK OR CONTRIBUTORS BE
27  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
28  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
29  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
30  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
31  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
32  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
33  * THE POSSIBILITY OF SUCH DAMAGE.
34  *
35  ******************************************************************************/
36 
37 /**
38  * \file
39  *
40  * Atomic bit-field access macros for PIC24/dsPIC.
41  *
42  * Initially, the code was taken from the [article by Alex Borisov
43  * (russian)](http://www.pic24.ru/doku.php/articles/mchp/c30_atomic_access),
44  * and modified a bit.
45  *
46  * The kernel would not probably provide that kind of functionality, but
47  * the kernel itself needs it, so, it is made public so that application can
48  * use it too.
49  */
50 
51 #ifndef _TN_ARCH_PIC24_BFA_H
52 #define _TN_ARCH_PIC24_BFA_H
53 
54 
55 
56 /*******************************************************************************
57  * INCLUDED FILES
58  ******************************************************************************/
59 
60 
61 
62 #ifdef __cplusplus
63 extern "C" { /*}*/
64 #endif
65 
66 
67 /*******************************************************************************
68  * DEFINITIONS
69  ******************************************************************************/
70 
71 /* Access params */
72 
73 ///
74 /// Command for `TN_BFA()` macro: Set bits in the bit field by mask;
75 /// `...` macro param should be set to the bit mask to set.
76 #define TN_BFA_SET 0x1111
77 ///
78 /// Command for `TN_BFA()` macro: Clear bits in the bit field by mask;
79 /// `...` macro param should be set to the bit mask to clear.
80 #define TN_BFA_CLR 0x2222
81 ///
82 /// Command for `TN_BFA()` macro: Invert bits in the bit field by mask;
83 /// `...` macro param should be set to the bit mask to invert.
84 #define TN_BFA_INV 0x3333
85 ///
86 /// Command for `TN_BFA()` macro: Write bit field;
87 /// `...` macro param should be set to the value to write.
88 #define TN_BFA_WR 0xAAAA
89 ///
90 /// Command for `TN_BFA()` macro: Read bit field;
91 /// `...` macro param ignored.
92 #define TN_BFA_RD 0xBBBB
93 
94 
95 
96 /**
97  * Macro for atomic access to the structure bit field. The `BFA` acronym
98  * means Bit Field Access.
99  *
100  * @param comm
101  * command to execute:
102  * - `#TN_BFA_WR` - write bit field
103  * - `#TN_BFA_RD` - read bit field
104  * - `#TN_BFA_SET` - set bits by mask
105  * - `#TN_BFA_CLR` - clear bits by mask
106  * - `#TN_BFA_INV` - invert bits by mask
107  *
108  * @param reg_name
109  * register name (`PORTA`, `CMCON`, ...).
110  *
111  * @param field_name
112  * structure field name
113  *
114  * @param ...
115  * used if only `comm != #TN_BFA_RD`. Meaning depends on the `comm`,
116  * see comments for specific command: `#TN_BFA_WR`, etc.
117  *
118  *
119  * Usage examples:
120  *
121  * \code{.c}
122  *
123  * int a = 0x02;
124  *
125  * //-- Set third bit of the INT0IP field in the IPC0 register:
126  * // IPC0bits.INT0IP |= (1 << 2);
127  * TN_BFA(TN_BFA_SET, IPC0, INT0IP, (1 << 2));
128  *
129  * //-- Clear second bit of the INT0IP field in the IPC0 register:
130  * // IPC0bits.INT0IP &= ~(1 << 1);
131  * TN_BFA(TN_BFA_CLR, IPC0, INT0IP, (1 << 1));
132  *
133  * //-- Invert two less-significant bits of the INT0IP field
134  * // in the IPC0 register:
135  * // IPC0bits.INT0IP ^= 0x03;
136  * TN_BFA(TN_BFA_INV, IPC0, INT0IP, 0x03);
137  *
138  * //-- Write value 0x05 to the INT0IP field of the IPC0 register:
139  * // IPC0bits.INT0IP = 0x05;
140  * TN_BFA(TN_BFA_WR, IPC0, INT0IP, 0x05);
141  *
142  * //-- Write value of the variable a to the INT0IP field of the IPC0
143  * // register:
144  * // IPC0bits.INT0IP = a;
145  * TN_BFA(TN_BFA_WR, IPC0, INT0IP, a);
146  *
147  * //-- Read the value that is stored in the INT0IP field of the IPC0
148  * // register, to the int variable a:
149  * // int a = IPC0bits.INT0IP;
150  * a = TN_BFA(TN_BFA_RD, IPC0, INT0IP);
151  *
152  * \endcode
153  */
154 #define TN_BFA(comm, reg_name, field_name, ...) \
155  ({ \
156  union \
157  { \
158  _TN_BFA_STRUCT_TYPE(reg_name) o; \
159  unsigned int i; \
160  } lcur, lmask={}, lval={}; \
161  \
162  lmask.o.field_name = -1; \
163  \
164  ((comm) == TN_BFA_WR) \
165  ?({ \
166  _TN_BFA_COMM_GET(comm) v = __VA_ARGS__+0; \
167  if (lmask.i & (lmask.i-1)){ \
168  lval.o.field_name = v; \
169  lcur.o = _TN_BFA_STRUCT_VAL(reg_name); \
170  __asm__ __volatile__ \
171  ("xor %0":"=U"(reg_name):"a"(lmask.i & (lval.i^lcur.i))); \
172  } else { \
173  if (v & 1) \
174  { \
175  _TN_BFA_STRUCT_VAL(reg_name).field_name = 1; \
176  } else { \
177  _TN_BFA_STRUCT_VAL(reg_name).field_name = 0; \
178  } \
179  } \
180  }), 0 \
181  \
182  :(((comm) == TN_BFA_INV) \
183  ?({ \
184  _TN_BFA_COMM_GET(comm) v = __VA_ARGS__+0; \
185  lval.o.field_name = v; \
186  __asm__ __volatile__(" xor %0" \
187  :"=U"(reg_name):"a"(lmask.i & lval.i)); \
188  }), 0 \
189  \
190  :((((comm) == TN_BFA_SET) \
191  ?({ \
192  _TN_BFA_COMM_GET(comm) v = __VA_ARGS__+0; \
193  if (lmask.i & (lmask.i-1)){ \
194  lval.o.field_name = v; \
195  __asm__ __volatile__ \
196  ("ior %0":"=U"(reg_name):"a"(lmask.i & lval.i)); \
197  } else { \
198  if (v & 1) \
199  _TN_BFA_STRUCT_VAL(reg_name).field_name = 1; \
200  } \
201  }), 0 \
202  \
203  :(((((comm) == TN_BFA_CLR) \
204  ?({ \
205  _TN_BFA_COMM_GET(comm) v = __VA_ARGS__+0; \
206  if (lmask.i & (lmask.i-1)){ \
207  lval.o.field_name = v; \
208  __asm__ __volatile__ \
209  ("and %0":"=U"(reg_name):"a"(~(lmask.i & lval.i))); \
210  } else { \
211  if (v & 1) \
212  _TN_BFA_STRUCT_VAL(reg_name).field_name = 0; \
213  } \
214  }), 0 \
215  \
216  :({ /* TN_BFA_RD */ \
217  _TN_BFA_STRUCT_VAL(reg_name).field_name; \
218  }))))))); \
219  })
220 
221 
222 
223 /**
224  * Macro for atomic access to the structure bit field specified as a range.
225  *
226  * @param comm
227  * command to execute:
228  * - `#TN_BFA_WR` - write bit field
229  * - `#TN_BFA_RD` - read bit field
230  * - `#TN_BFA_SET` - set bits by mask
231  * - `#TN_BFA_CLR` - clear bits by mask
232  * - `#TN_BFA_INV` - invert bits by mask
233  *
234  * @param reg_name
235  * variable name (`PORTA`, `CMCON`, ...). Variable should be in the near
236  * memory (first 8 KB)
237  *
238  * @param lower
239  * number of lowest affected bit of the field
240  *
241  * @param upper
242  * number of highest affected bit of the field
243  *
244  * @param ...
245  * used if only `comm != #TN_BFA_RD`. Meaning depends on the `comm`,
246  * see comments for specific command: `#TN_BFA_WR`, etc.
247  *
248  *
249  * Usage examples:
250  *
251  * \code{.c}
252  *
253  * int a = 0x02;
254  *
255  * //-- Write constant 0xaa to the least significant byte of the TRISB
256  * // register:
257  * TN_BFAR(TN_BFA_WR, TRISB, 0, 7, 0xaa);
258  *
259  * //-- Invert least significant nibble of the most significant byte
260  * // in the register TRISB:
261  * TN_BFAR(TN_BFA_INV, TRISB, 8, 15, 0x0f);
262  *
263  * //-- Get 5 least significant bits from the register TRISB and store
264  * // result to the variable a
265  * a = TN_BFAR(TN_BFA_RD, TRISB, 0, 4);
266  *
267  * \endcode
268  *
269  */
270 #define TN_BFAR(comm, reg_name, lower, upper, ...) \
271  ({ \
272  unsigned int lmask, lmaskl, lmasku; \
273  lmaskl = (1 << (lower)); \
274  lmasku = (1 << (upper)); \
275  lmask = ((lmaskl-1) ^ (lmasku-1)) | lmaskl | lmasku; \
276  ((comm) == TN_BFA_WR) \
277  ?({ \
278  _TN_BFA_COMM_GET(comm) v = __VA_ARGS__+0; \
279  v = (v << (((lower) < (upper)) ? (lower) : (upper))) & lmask; \
280  __asm__ __volatile__ \
281  (" xor %0": "=T"(reg_name):"a"(lmask & (v ^ reg_name))); \
282  }), 0 \
283  :(((comm) == TN_BFA_INV) \
284  ?({ \
285  _TN_BFA_COMM_GET(comm) v = __VA_ARGS__+0; \
286  if (lmask & (lmask-1)) \
287  { \
288  v = (v << (((lower) < (upper)) ? (lower) : (upper))) & lmask; \
289  __asm__ __volatile__( \
290  " xor %0":"=T"(reg_name):"a"(lmask & v)); \
291  } else { \
292  if (v & 1) \
293  __asm__ __volatile__( \
294  " btg %0, #%1" :"=T"(reg_name) :"i"(lower)); \
295  } \
296  }), 0 \
297  \
298  :(((comm) == TN_BFA_SET) \
299  ?({ \
300  _TN_BFA_COMM_GET(comm) v = __VA_ARGS__+0; \
301  if (lmask & (lmask-1)) \
302  { \
303  v = (v << (((lower) < (upper)) ? (lower) : (upper))) & lmask; \
304  __asm__ __volatile__(" ior %0 " \
305  :"=T"(reg_name) \
306  :"a"(lmask & v)); \
307  } else { \
308  if (v & 1) \
309  __asm__ __volatile__( \
310  " bset %0, #%1" :"=T"(reg_name) :"i"(lower)); \
311  } \
312  }), 0 \
313  \
314  :((((comm) == TN_BFA_CLR) \
315  ?({ \
316  _TN_BFA_COMM_GET(comm) v = __VA_ARGS__+0; \
317  if (lmask & (lmask-1)) \
318  { \
319  v = (v << (((lower) < (upper)) ? (lower) : (upper))) & lmask; \
320  __asm__ __volatile__(" and %0 " \
321  :"=T"(reg_name) \
322  :"a"(~(lmask & v))); \
323  } else { \
324  if (v & 1) \
325  __asm__ __volatile__( \
326  " bclr %0, #%1" :"=T"(reg_name) :"i"(lower)); \
327  } \
328  }), 0 \
329  \
330  :({ /* TN_BFA_RD */ \
331  (reg_name & lmask) >> (((lower) < (upper)) ? (lower) : (upper)); \
332  }))))); \
333  })
334 
335 
336 
337 //-- internal kernel macros {{{
338 #ifndef DOXYGEN_SHOULD_SKIP_THIS
339 
340 #define _TN_BFA_COMM_ERR(a) _TN_BFA_COMMAND_ERROR_##a
341 #define _TN_BFA_COMM_GET(a) _TN_BFA_COMM_ERR(a)
342 
343 typedef unsigned int _TN_BFA_COMM_GET(TN_BFA_SET);
344 typedef unsigned int _TN_BFA_COMM_GET(TN_BFA_CLR);
345 typedef unsigned int _TN_BFA_COMM_GET(TN_BFA_INV);
346 
347 typedef unsigned int _TN_BFA_COMM_GET(TN_BFA_WR);
348 typedef unsigned int _TN_BFA_COMM_GET(TN_BFA_RD);
349 
350 #define _TN_BFA_STRUCT_TYPE(a) a##BITS
351 #define _TN_BFA_STRUCT_VAL(a) a##bits
352 
353 #endif
354 // }}}
355 
356 
357 #ifdef __cplusplus
358 } /* extern "C" */
359 #endif
360 
361 #endif // _TN_ARCH_PIC24_BFA_H
362 
#define TN_BFA_SET
Command for TN_BFA() macro: Set bits in the bit field by mask; ... macro param should be set to the b...
#define TN_BFA_INV
Command for TN_BFA() macro: Invert bits in the bit field by mask; ... macro param should be set to th...
#define TN_BFA_RD
Command for TN_BFA() macro: Read bit field; ... macro param ignored.
#define TN_BFA_CLR
Command for TN_BFA() macro: Clear bits in the bit field by mask; ... macro param should be set to the...
#define TN_BFA_WR
Command for TN_BFA() macro: Write bit field; ... macro param should be set to the value to write...