-
Notifications
You must be signed in to change notification settings - Fork 13.3k
/
Copy pathpgmspace.h
177 lines (153 loc) · 7.02 KB
/
pgmspace.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
/* PGMSPACE.H - Accessor utilities/types for accessing PROGMEM data */
#ifndef _PGMSPACE_H_
#define _PGMSPACE_H_
#include <stdint.h>
#ifdef __cplusplus
extern "C" {
#endif
#ifndef ICACHE_RODATA_ATTR
#define ICACHE_RODATA_ATTR __attribute__((section(".irom.text")))
#endif
#ifndef PROGMEM
// The following two macros cause a parameter to be enclosed in quotes
// by the preopressor (i.e. for concatenating ints to strings)
#define __STRINGIZE_NX(A) #A
#define __STRINGIZE(A) __STRINGIZE_NX(A)
// Since __section__ is supposed to be only use for global variables,
// there could be conflicts when a static/inlined function has them in the
// same file as a non-static PROGMEM object.
// Ref: https://gcc.gnu.org/onlinedocs/gcc-3.2/gcc/Variable-Attributes.html
// Place each progmem object into its own named section, avoiding conflicts
#define PROGMEM __attribute__((section( "\".irom.text." __FILE__ "." __STRINGIZE(__LINE__) "." __STRINGIZE(__COUNTER__) "\"")))
#endif
#ifndef PGM_P
#define PGM_P const char *
#endif
#ifndef PGM_VOID_P
#define PGM_VOID_P const void *
#endif
#ifndef PSTR_ALIGN
// PSTR() macro starts by default on a 32-bit boundary. This adds on average
// 1.5 bytes/string, but in return memcpy_P and strcpy_P will work 4~8x faster
// Allow users to override the alignment with PSTR_ALIGN
#define PSTR_ALIGN 4
#endif
#ifndef PSTRN
// Multi-alignment variant of PSTR, n controls the alignment and should typically be 1 or 4
// Adapted from AVR-specific code at https://forum.arduino.cc/index.php?topic=194603.0
// Uses C attribute section instead of ASM block to allow for C language string concatenation ("x" "y" === "xy")
#define PSTRN(s,n) (__extension__({static const char __pstr__[] __attribute__((__aligned__(n))) __attribute__((section( "\".irom0.pstr." __FILE__ "." __STRINGIZE(__LINE__) "." __STRINGIZE(__COUNTER__) "\", \"aSM\", @progbits, 1 #"))) = (s); &__pstr__[0];}))
#endif
#ifndef PSTR
// PSTR() uses the default alignment defined by PSTR_ALIGN
#define PSTR(s) PSTRN(s,PSTR_ALIGN)
#endif
#ifndef PSTR4
// PSTR4() enforces 4-bytes alignment whatever the value of PSTR_ALIGN
// as required by functions like ets_strlen() or ets_memcpy()
#define PSTR4(s) PSTRN(s,4)
#endif
// Flash memory must be read using 32 bit aligned addresses else a processor
// exception will be triggered.
// The order within the 32 bit values are:
// --------------
// b3, b2, b1, b0
// w1, w0
#define pgm_read_with_offset(addr, res) \
__asm__( \
"extui\t%0, %1, 0, 2\n\t" /* Extract offset within word (in bytes) */ \
"sub\t%1, %1, %0\n\t" /* Subtract offset from addr, yielding an aligned address */ \
"l32i.n\t%1, %1, 0\n\t" /* Load word from aligned address */ \
"ssa8l\t%0\n\t" /* Prepare to shift by offset (in bits) */ \
"src\t%0, %1, %1" /* Shift right; now the requested byte is the first one */ \
: "=r"(res), "+r"(addr))
#define pgm_read_dword_with_offset(addr, res) \
do { \
uint32_t temp; \
__asm__( \
"extui\t%0, %1, 0, 2\n\t" /* Extract offset within word (in bytes) */ \
"sub\t%1, %1, %0\n\t" /* Subtract offset from addr, yielding an aligned address */ \
"l32i.n\t%2, %1, 0\n\t" /* Load 2 words */ \
"l32i.n\t%1, %1, 4\n\t" /* from aligned address */ \
"ssa8l\t%0\n\t" /* Prepare to shift by offset (in bits) */ \
"src\t%0, %1, %2" /* Shift right in order to extract the requested dword */ \
: "=r"(res), "+r"(addr), "=r"(temp)); \
} while(0)
static inline uint8_t pgm_read_byte_inlined(const void* addr) {
uint32_t res;
pgm_read_with_offset(addr, res);
return res; /* Implicit cast to uint8_t masks the lower byte from the returned word */
}
/* Although this says "word", it's actually 16 bit, i.e. half word on Xtensa */
static inline uint16_t pgm_read_word_inlined(const void* addr) {
uint32_t res;
pgm_read_with_offset(addr, res);
return res; /* Implicit cast to uint16_t masks the lower half-word from the returned word */
}
/* Can't legally cast bits of uint32_t to a float w/o conversion or std::memcpy, which is inefficient. */
/* The ASM block doesn't care the type, so just pass in what C thinks is a float and return in custom fcn. */
static inline float pgm_read_float_unaligned(const void* addr) {
float res;
pgm_read_with_offset(addr, res);
return res;
}
#define pgm_read_byte(addr) pgm_read_byte_inlined(addr)
#define pgm_read_word_aligned(addr) pgm_read_word_inlined(addr)
#ifdef __cplusplus
#define pgm_read_dword_aligned(addr) (*reinterpret_cast<const uint32_t*>(addr))
#define pgm_read_float_aligned(addr) (*reinterpret_cast<const float*>(addr))
#define pgm_read_ptr_aligned(addr) (*reinterpret_cast<const void* const*>(addr))
#else
#define pgm_read_dword_aligned(addr) (*(const uint32_t*)(addr))
#define pgm_read_float_aligned(addr) (*(const float*)(addr))
#define pgm_read_ptr_aligned(addr) (*(const void* const*)(addr))
#endif
static inline uint32_t pgm_read_dword_unaligned(const void *addr) {
uint32_t res;
pgm_read_dword_with_offset(addr, res);
return res;
}
#define pgm_read_ptr_unaligned(addr) ((void*)pgm_read_dword_unaligned(addr))
#define pgm_read_word_unaligned(addr) ((uint16_t)pgm_read_dword_unaligned(addr))
// Allow selection of _aligned or _unaligned, but default to _unaligned for Arduino compatibility
// Add -DPGM_READ_UNALIGNED=0 or "#define PGM_READ_UNALIGNED 0" to code to use aligned-only (faster) macros by default
#ifndef PGM_READ_UNALIGNED
#define PGM_READ_UNALIGNED 1
#endif
#if PGM_READ_UNALIGNED
#define pgm_read_word(a) pgm_read_word_unaligned(a)
#define pgm_read_dword(a) pgm_read_dword_unaligned(a)
#define pgm_read_float(a) pgm_read_float_unaligned(a)
#define pgm_read_ptr(a) pgm_read_ptr_unaligned(a)
#else
#define pgm_read_word(a) pgm_read_word_aligned(a)
#define pgm_read_dword(a) pgm_read_dword_aligned(a)
#define pgm_read_float(a) pgm_read_float_aligned(a)
#define pgm_read_ptr(a) pgm_read_ptr_aligned(a)
#endif
#define pgm_read_byte_near(addr) pgm_read_byte(addr)
#define pgm_read_word_near(addr) pgm_read_word(addr)
#define pgm_read_dword_near(addr) pgm_read_dword(addr)
#define pgm_read_float_near(addr) pgm_read_float(addr)
#define pgm_read_ptr_near(addr) pgm_read_ptr(addr)
#define pgm_read_byte_far(addr) pgm_read_byte(addr)
#define pgm_read_word_far(addr) pgm_read_word(addr)
#define pgm_read_dword_far(addr) pgm_read_dword(addr)
#define pgm_read_float_far(addr) pgm_read_float(addr)
#define pgm_read_ptr_far(addr) pgm_read_ptr(addr)
#define _SFR_BYTE(n) (n)
#ifdef __PROG_TYPES_COMPAT__
typedef void prog_void;
typedef char prog_char;
typedef unsigned char prog_uchar;
typedef int8_t prog_int8_t;
typedef uint8_t prog_uint8_t;
typedef int16_t prog_int16_t;
typedef uint16_t prog_uint16_t;
typedef int32_t prog_int32_t;
typedef uint32_t prog_uint32_t;
#endif // defined(__PROG_TYPES_COMPAT__)
#ifdef __cplusplus
}
#endif
#endif