1 /*
2  * Copyright (c) 2020 Raspberry Pi (Trading) Ltd.
3  *
4  * SPDX-License-Identifier: BSD-3-Clause
5  */
6 
7 #ifndef _HARDWARE_ADDRESS_MAPPED_H
8 #define _HARDWARE_ADDRESS_MAPPED_H
9 
10 #include "pico.h"
11 #include "hardware/regs/addressmap.h"
12 
13 /** \file address_mapped.h
14  *  \defgroup hardware_base hardware_base
15  *
16  *  \brief Low-level types and (atomic) accessors for memory-mapped hardware registers
17  *
18  *  `hardware_base` defines the low level types and access functions for memory mapped hardware registers. It is included
19  *  by default by all other hardware libraries.
20  *
21  *  The following register access typedefs codify the access type (read/write) and the bus size (8/16/32) of the hardware register.
22  *  The register type names are formed by concatenating one from each of the 3 parts A, B, C
23 
24  *   A    | B | C | Meaning
25  *  ------|---|---|--------
26  *  io_   |   |   | A Memory mapped IO register
27  *   |ro_|   | read-only access
28  *   |rw_|   | read-write access
29  *   |wo_|   | write-only access (can't actually be enforced via C API)
30  *   |   |  8| 8-bit wide access
31  *   |   | 16| 16-bit wide access
32  *   |   | 32| 32-bit wide access
33  *
34  *  When dealing with these types, you will always use a pointer, i.e. `io_rw_32 *some_reg` is a pointer to a read/write
35  *  32 bit register that you can write with `*some_reg = value`, or read with `value = *some_reg`.
36  *
37  *  RP-series hardware is also aliased to provide atomic setting, clear or flipping of a subset of the bits within
38  *  a hardware register so that concurrent access by two cores is always consistent with one atomic operation
39  *  being performed first, followed by the second.
40  *
41  *  See hw_set_bits(), hw_clear_bits() and hw_xor_bits() provide for atomic access via a pointer to a 32 bit register
42  *
43  *  Additionally given a pointer to a structure representing a piece of hardware (e.g. `dma_hw_t *dma_hw` for the DMA controller), you can
44  *  get an alias to the entire structure such that writing any member (register) within the structure is equivalent
45  *  to an atomic operation via hw_set_alias(), hw_clear_alias() or hw_xor_alias()...
46  *
47  *  For example `hw_set_alias(dma_hw)->inte1 = 0x80;` will set bit 7 of the INTE1 register of the DMA controller,
48  *  leaving the other bits unchanged.
49  */
50 
51 #ifdef __cplusplus
52 extern "C" {
53 #endif
54 
55 #define check_hw_layout(type, member, offset) static_assert(offsetof(type, member) == (offset), "hw offset mismatch")
56 #define check_hw_size(type, size) static_assert(sizeof(type) == (size), "hw size mismatch")
57 
58 // PICO_CONFIG: PARAM_ASSERTIONS_ENABLED_ADDRESS_ALIAS, Enable/disable assertions in memory address aliasing macros, type=bool, default=0, group=hardware_base
59 #ifndef PARAM_ASSERTIONS_ENABLED_ADDRESS_ALIAS
60 #define PARAM_ASSERTIONS_ENABLED_ADDRESS_ALIAS 0
61 #endif
62 
63 typedef volatile uint64_t io_rw_64;
64 typedef const volatile uint64_t io_ro_64;
65 typedef volatile uint64_t io_wo_64;
66 typedef volatile uint32_t io_rw_32;
67 typedef const volatile uint32_t io_ro_32;
68 typedef volatile uint32_t io_wo_32;
69 typedef volatile uint16_t io_rw_16;
70 typedef const volatile uint16_t io_ro_16;
71 typedef volatile uint16_t io_wo_16;
72 typedef volatile uint8_t io_rw_8;
73 typedef const volatile uint8_t io_ro_8;
74 typedef volatile uint8_t io_wo_8;
75 
76 typedef volatile uint8_t *const ioptr;
77 typedef ioptr const const_ioptr;
78 
79 // A non-functional (empty) helper macro to help IDEs follow links from the autogenerated
80 // hardware struct headers in hardware/structs/xxx.h to the raw register definitions
81 // in hardware/regs/xxx.h. A preprocessor define such as TIMER_TIMEHW_OFFSET (a timer register offset)
82 // is not generally clickable (in an IDE) if placed in a C comment, so _REG_(TIMER_TIMEHW_OFFSET) is
83 // included outside of a comment instead
84 #define _REG_(x)
85 
86 // Helper method used by hw_alias macros to optionally check input validity
87 #define hw_alias_check_addr(addr) ((uintptr_t)(addr))
88 // can't use the following impl as it breaks existing static declarations using hw_alias, so would be a backwards incompatibility
89 //static __force_inline uint32_t hw_alias_check_addr(volatile void *addr) {
90 //    uint32_t rc = (uintptr_t)addr;
91 //    invalid_params_if(ADDRESS_ALIAS, rc < 0x40000000); // catch likely non HW pointer types
92 //    return rc;
93 //}
94 
95 #if PICO_RP2040
96 // Helper method used by xip_alias macros to optionally check input validity
xip_alias_check_addr(const void * addr)97 __force_inline static uint32_t xip_alias_check_addr(const void *addr) {
98     uint32_t rc = (uintptr_t)addr;
99     valid_params_if(ADDRESS_ALIAS, rc >= XIP_MAIN_BASE && rc < XIP_NOALLOC_BASE);
100     return rc;
101 }
102 #else
103 //static __force_inline uint32_t xip_alias_check_addr(const void *addr) {
104 //    uint32_t rc = (uintptr_t)addr;
105 //    valid_params_if(ADDRESS_ALIAS, rc >= XIP_BASE && rc < XIP_END);
106 //    return rc;
107 //}
108 #endif
109 
110 // Untyped conversion alias pointer generation macros
111 #define hw_set_alias_untyped(addr) ((void *)(REG_ALIAS_SET_BITS + hw_alias_check_addr(addr)))
112 #define hw_clear_alias_untyped(addr) ((void *)(REG_ALIAS_CLR_BITS + hw_alias_check_addr(addr)))
113 #define hw_xor_alias_untyped(addr) ((void *)(REG_ALIAS_XOR_BITS + hw_alias_check_addr(addr)))
114 
115 #if PICO_RP2040
116 #define xip_noalloc_alias_untyped(addr) ((void *)(XIP_NOALLOC_BASE | xip_alias_check_addr(addr)))
117 #define xip_nocache_alias_untyped(addr) ((void *)(XIP_NOCACHE_BASE | xip_alias_check_addr(addr)))
118 #define xip_nocache_noalloc_alias_untyped(addr) ((void *)(XIP_NOCACHE_NOALLOC_BASE | xip_alias_check_addr(addr)))
119 #endif
120 
121 // Typed conversion alias pointer generation macros
122 #define hw_set_alias(p) ((typeof(p))hw_set_alias_untyped(p))
123 #define hw_clear_alias(p) ((typeof(p))hw_clear_alias_untyped(p))
124 #define hw_xor_alias(p) ((typeof(p))hw_xor_alias_untyped(p))
125 #define xip_noalloc_alias(p) ((typeof(p))xip_noalloc_alias_untyped(p))
126 #define xip_nocache_alias(p) ((typeof(p))xip_nocache_alias_untyped(p))
127 #define xip_nocache_noalloc_alias(p) ((typeof(p))xip_nocache_noalloc_alias_untyped(p))
128 
129 /*! \brief Atomically set the specified bits to 1 in a HW register
130  *  \ingroup hardware_base
131  *
132  * \param addr Address of writable register
133  * \param mask Bit-mask specifying bits to set
134  */
hw_set_bits(io_rw_32 * addr,uint32_t mask)135 __force_inline static void hw_set_bits(io_rw_32 *addr, uint32_t mask) {
136     *(io_rw_32 *) hw_set_alias_untyped((volatile void *) addr) = mask;
137 }
138 
139 /*! \brief Atomically clear the specified bits to 0 in a HW register
140  *  \ingroup hardware_base
141  *
142  * \param addr Address of writable register
143  * \param mask Bit-mask specifying bits to clear
144  */
hw_clear_bits(io_rw_32 * addr,uint32_t mask)145 __force_inline static void hw_clear_bits(io_rw_32 *addr, uint32_t mask) {
146     *(io_rw_32 *) hw_clear_alias_untyped((volatile void *) addr) = mask;
147 }
148 
149 /*! \brief Atomically flip the specified bits in a HW register
150  *  \ingroup hardware_base
151  *
152  * \param addr Address of writable register
153  * \param mask Bit-mask specifying bits to invert
154  */
hw_xor_bits(io_rw_32 * addr,uint32_t mask)155 __force_inline static void hw_xor_bits(io_rw_32 *addr, uint32_t mask) {
156     *(io_rw_32 *) hw_xor_alias_untyped((volatile void *) addr) = mask;
157 }
158 
159 /*! \brief Set new values for a sub-set of the bits in a HW register
160  *  \ingroup hardware_base
161  *
162  * Sets destination bits to values specified in \p values, if and only if corresponding bit in \p write_mask is set
163  *
164  * Note: this method allows safe concurrent modification of *different* bits of
165  * a register, but multiple concurrent access to the same bits is still unsafe.
166  *
167  * \param addr Address of writable register
168  * \param values Bits values
169  * \param write_mask Mask of bits to change
170  */
hw_write_masked(io_rw_32 * addr,uint32_t values,uint32_t write_mask)171 __force_inline static void hw_write_masked(io_rw_32 *addr, uint32_t values, uint32_t write_mask) {
172     hw_xor_bits(addr, (*addr ^ values) & write_mask);
173 }
174 
175 #if !PICO_RP2040
176 // include this here to avoid the check in every other hardware/structs header that needs it
177 #include "hardware/structs/accessctrl.h"
178 #endif
179 
180 #ifdef __cplusplus
181 }
182 #endif
183 
184 #endif
185