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