AVR-LibC  2.3.0
Standard C library for AVR-GCC
 

AVR-LibC Manual

AVR-LibC Sources

Main Page

User Manual

Lib­rary Refe­rence

FAQ

Exam­ple Pro­jects

Index

Loading...
Searching...
No Matches
lpm-elpm.h
1/* Copyright (c) 2025 Georg-Johann Lay
2 All rights reserved.
3
4 Redistribution and use in source and binary forms, with or without
5 modification, are permitted provided that the following conditions are met:
6
7 * Redistributions of source code must retain the above copyright
8 notice, this list of conditions and the following disclaimer.
9 * Redistributions in binary form must reproduce the above copyright
10 notice, this list of conditions and the following disclaimer in
11 the documentation and/or other materials provided with the
12 distribution.
13 * Neither the name of the copyright holders nor the names of
14 contributors may be used to endorse or promote products derived
15 from this software without specific prior written permission.
16
17 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
18 AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
21 LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
22 CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
23 SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
24 INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
25 CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
26 ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27 POSSIBILITY OF SUCH DAMAGE. */
28
29#ifndef __BITS_LPM_ELPM_H_
30#define __BITS_LPM_ELPM_H_
31
32/* This header provides low-level macros for other parts of AVR-LibC
33 that read from program memory by means of inline assembly that
34 uses LPM and ELPM instructions. */
35
36/* Historically, avr/pgmspace.h used asm volatile in definitions of
37 macros like pgm_read_byte(), though that's not required since there
38 are no hidden side effects in them. Though volatile may avoid
39 undesired code reordering against (volatile) I/O accesses. */
40
41#ifndef __LPM_VOLATILE
42#define __LPM_VOLATILE __volatile__
43#endif
44
45#ifndef __ELPM_VOLATILE
46#define __ELPM_VOLATILE __volatile__
47#endif
48
49#if defined(__AVR_TINY__)
50/* For Reduced Tiny devices, avr-gcc adds 0x4000 when it takes the address
51 of a __progmem__ object. This means we can use open coded C/C++ to read
52 from progmem. This assumes we have
53 - GCC PR71948 - Make progmem work on Reduced Tiny (GCC v7 / 2016-08) */
54#define __LPM__1(res, addr) res = *addr
55#define __LPM__2(res, addr) res = *addr
56#define __LPM__3(res, addr) res = *addr
57#define __LPM__4(res, addr) res = *addr
58#define __LPM__8(res, addr) res = *addr
59
60#elif defined(__AVR_HAVE_LPMX__)
61#define __LPM__1(res, addr) \
62 __asm __LPM_VOLATILE ("lpm %0,%a1" \
63 : "=r" (res) : "z" (addr))
64
65#define __LPM__2(res, addr) \
66 __asm __LPM_VOLATILE ("lpm %A0,%a1+" "\n\t" \
67 "lpm %B0,%a1+" \
68 : "=r" (res), "+z" (addr))
69
70#define __LPM__3(res, addr) \
71 __asm __LPM_VOLATILE ("lpm %A0,%a1+" "\n\t" \
72 "lpm %B0,%a1+" "\n\t" \
73 "lpm %C0,%a1+" \
74 : "=r" (res), "+z" (addr))
75
76#define __LPM__4(res, addr) \
77 __asm __LPM_VOLATILE ("lpm %A0,%a1+" "\n\t" \
78 "lpm %B0,%a1+" "\n\t" \
79 "lpm %C0,%a1+" "\n\t" \
80 "lpm %D0,%a1+" \
81 : "=r" (res), "+z" (addr))
82
83#define __LPM__8(res, addr) \
84 __asm __LPM_VOLATILE ("lpm %r0+0,%a1+" "\n\t" \
85 "lpm %r0+1,%a1+" "\n\t" \
86 "lpm %r0+2,%a1+" "\n\t" \
87 "lpm %r0+3,%a1+" "\n\t" \
88 "lpm %r0+4,%a1+" "\n\t" \
89 "lpm %r0+5,%a1+" "\n\t" \
90 "lpm %r0+6,%a1+" "\n\t" \
91 "lpm %r0+7,%a1+" \
92 : "=r" (res), "+z" (addr))
93#else /* Has no LPMx and no Reduced Tiny => Has LPM. */
94#define __LPM__1(res, addr) \
95 __asm __LPM_VOLATILE ("lpm $ mov %A0,r0" \
96 : "=r" (res) : "z" (addr) : "r0")
97
98#define __LPM__2(res, addr) \
99 __asm __LPM_VOLATILE ("lpm $ mov %A0,r0 $ adiw %1,1" "\n\t" \
100 "lpm $ mov %B0,r0" \
101 : "=r" (res), "+z" (addr) :: "r0")
102
103#define __LPM__3(res, addr) \
104 __asm __LPM_VOLATILE ("lpm $ mov %A0,r0 $ adiw %1,1" "\n\t" \
105 "lpm $ mov %B0,r0 $ adiw %1,1" "\n\t" \
106 "lpm $ mov %C0,r0" \
107 : "=r" (res), "+z" (addr) :: "r0")
108
109#define __LPM__4(res, addr) \
110 __asm __LPM_VOLATILE ("lpm $ mov %A0,r0 $ adiw %1,1" "\n\t" \
111 "lpm $ mov %B0,r0 $ adiw %1,1" "\n\t" \
112 "lpm $ mov %C0,r0 $ adiw %1,1" "\n\t" \
113 "lpm $ mov %D0,r0" \
114 : "=r" (res), "+z" (addr) :: "r0")
115
116#define __LPM__8(res, addr) \
117 __asm __LPM_VOLATILE ("lpm $ mov %r0+0,r0 $ adiw %1,1" "\n\t" \
118 "lpm $ mov %r0+1,r0 $ adiw %1,1" "\n\t" \
119 "lpm $ mov %r0+2,r0 $ adiw %1,1" "\n\t" \
120 "lpm $ mov %r0+3,r0 $ adiw %1,1" "\n\t" \
121 "lpm $ mov %r0+4,r0 $ adiw %1,1" "\n\t" \
122 "lpm $ mov %r0+5,r0 $ adiw %1,1" "\n\t" \
123 "lpm $ mov %r0+6,r0 $ adiw %1,1" "\n\t" \
124 "lpm $ mov %r0+7,r0" \
125 : "=r" (res), "+z" (addr) :: "r0")
126#endif /* LPM cases */
127
128
129#if defined(__AVR_HAVE_ELPMX__)
130
131#ifdef __AVR_HAVE_RAMPD__
132/* For devices with EBI, reset RAMPZ to zero after. */
133#define __pgm_clr_RAMPZ_ "\n\t" "out __RAMPZ__,__zero_reg__"
134#else
135/* Devices without EBI: no need to reset RAMPZ. */
136#define __pgm_clr_RAMPZ_ /* empty */
137#endif
138
139#define __ELPM__1(res, addr, T) \
140 __asm __ELPM_VOLATILE ("movw r30,%1" "\n\t" \
141 "out __RAMPZ__,%C1" "\n\t" \
142 "elpm %A0,Z" \
143 __pgm_clr_RAMPZ_ \
144 : "=r" (res) \
145 : "r" (addr) \
146 : "r30", "r31")
147
148#define __ELPM__2(res, addr, T) \
149 __asm __ELPM_VOLATILE ("movw r30,%1" "\n\t" \
150 "out __RAMPZ__,%C1" "\n\t" \
151 "elpm %A0,Z+" "\n\t" \
152 "elpm %B0,Z+" \
153 __pgm_clr_RAMPZ_ \
154 : "=r" (res) \
155 : "r" (addr) \
156 : "r30", "r31")
157
158#define __ELPM__3(res, addr, T) \
159 __asm __ELPM_VOLATILE ("movw r30,%1" "\n\t" \
160 "out __RAMPZ__,%C1" "\n\t" \
161 "elpm %A0,Z+" "\n\t" \
162 "elpm %B0,Z+" "\n\t" \
163 "elpm %C0,Z+" \
164 __pgm_clr_RAMPZ_ \
165 : "=r" (res) \
166 : "r" (addr) \
167 : "r30", "r31")
168
169#define __ELPM__4(res, addr, T) \
170 __asm __ELPM_VOLATILE ("movw r30,%1" "\n\t" \
171 "out __RAMPZ__,%C1" "\n\t" \
172 "elpm %A0,Z+" "\n\t" \
173 "elpm %B0,Z+" "\n\t" \
174 "elpm %C0,Z+" "\n\t" \
175 "elpm %D0,Z+" \
176 __pgm_clr_RAMPZ_ \
177 : "=r" (res) \
178 : "r" (addr) \
179 : "r30", "r31")
180
181#define __ELPM__8(res, addr, T) \
182 __asm __ELPM_VOLATILE ("movw r30,%1" "\n\t" \
183 "out __RAMPZ__,%C1" "\n\t" \
184 "elpm %r0+0,Z+" "\n\t" \
185 "elpm %r0+1,Z+" "\n\t" \
186 "elpm %r0+2,Z+" "\n\t" \
187 "elpm %r0+3,Z+" "\n\t" \
188 "elpm %r0+4,Z+" "\n\t" \
189 "elpm %r0+5,Z+" "\n\t" \
190 "elpm %r0+6,Z+" "\n\t" \
191 "elpm %r0+7,Z+" \
192 __pgm_clr_RAMPZ_ \
193 : "=r" (res) \
194 : "r" (addr) \
195 : "r30", "r31")
196
197/* FIXME: AT43USB320 does not have RAMPZ but supports (external) program
198 memory of 64 KiW, at least that's what the comments in io43usb32x.h are
199 indicating. A solution would be to put the device in a different
200 multilib-set (see GCC PR78275), as io.h has "#define FLASHEND 0x0FFFF".
201 For now, just exclude AT43USB320 from code that uses RAMPZ. Also note
202 that the manual asserts that the entire program memory can be accessed
203 by LPM, implying only 64 KiB of program memory. */
204#elif defined(__AVR_HAVE_ELPM__) \
205 && !defined(__AVR_AT43USB320__)
206/* The poor devices without ELPMx: Do 24-bit addresses by hand... */
207#define __ELPM__1(res, addr, T) \
208 __asm __ELPM_VOLATILE ("mov r30,%A1" "\n\t" \
209 "mov r31,%B1" "\n\t" \
210 "out __RAMPZ__,%C1 $ elpm $ mov %A0,r0" \
211 : "=r" (res) \
212 : "r" (addr) \
213 : "r30", "r31", "r0")
214
215#define __ELPM__2(res, addr, T) \
216 __asm __ELPM_VOLATILE \
217 ("mov r30,%A1" "\n\t" \
218 "mov r31,%B1" "\n\t" \
219 "mov %B0,%C1" "\n\t" \
220 "out __RAMPZ__,%B0 $ elpm $ mov %A0,r0 $ adiw r30,1 $ adc %B0,r1\n\t"\
221 "out __RAMPZ__,%B0 $ elpm $ mov %B0,r0" \
222 : "=r" (res) \
223 : "r" (addr) \
224 : "r30", "r31", "r0")
225
226#define __ELPM__3(res, addr, T) \
227 __asm __ELPM_VOLATILE \
228 ("mov r30,%A1" "\n\t" \
229 "mov r31,%B1" "\n\t" \
230 "mov %C0,%C1" "\n\t" \
231 "out __RAMPZ__,%C0 $ elpm $ mov %A0,r0 $ adiw r30,1 $ adc %C0,r1\n\t"\
232 "out __RAMPZ__,%C0 $ elpm $ mov %B0,r0 $ adiw r30,1 $ adc %C0,r1\n\t"\
233 "out __RAMPZ__,%C0 $ elpm $ mov %C0,r0" \
234 : "=r" (res) \
235 : "r" (addr) \
236 : "r30", "r31", "r0")
237
238#define __ELPM__4(res, addr, T) \
239 __asm __ELPM_VOLATILE \
240 ("mov r30,%A1" "\n\t" \
241 "mov r31,%B1" "\n\t" \
242 "mov %D0,%C1" "\n\t" \
243 "out __RAMPZ__,%D0 $ elpm $ mov %A0,r0 $ adiw r30,1 $ adc %D0,r1\n\t"\
244 "out __RAMPZ__,%D0 $ elpm $ mov %B0,r0 $ adiw r30,1 $ adc %D0,r1\n\t"\
245 "out __RAMPZ__,%D0 $ elpm $ mov %C0,r0 $ adiw r30,1 $ adc %D0,r1\n\t"\
246 "out __RAMPZ__,%D0 $ elpm $ mov %D0,r0" \
247 : "=r" (res) \
248 : "r" (addr) \
249 : "r30", "r31", "r0")
250
251#define __ELPM__8(res, addr, T) \
252 __asm __ELPM_VOLATILE \
253 ("mov r30,%A1" "\n\t" \
254 "mov r31,%B1" "\n\t" \
255 "mov %r0+7,%C1" "\n\t" \
256 "out __RAMPZ__,%r0+7 $ elpm $ mov %r0+0,0 $ adiw 30,1 $ adc %r0+7,1\n\t"\
257 "out __RAMPZ__,%r0+7 $ elpm $ mov %r0+1,0 $ adiw 30,1 $ adc %r0+7,1\n\t"\
258 "out __RAMPZ__,%r0+7 $ elpm $ mov %r0+2,0 $ adiw 30,1 $ adc %r0+7,1\n\t"\
259 "out __RAMPZ__,%r0+7 $ elpm $ mov %r0+3,0 $ adiw 30,1 $ adc %r0+7,1\n\t"\
260 "out __RAMPZ__,%r0+7 $ elpm $ mov %r0+4,0 $ adiw 30,1 $ adc %r0+7,1\n\t"\
261 "out __RAMPZ__,%r0+7 $ elpm $ mov %r0+5,0 $ adiw 30,1 $ adc %r0+7,1\n\t"\
262 "out __RAMPZ__,%r0+7 $ elpm $ mov %r0+6,0 $ adiw 30,1 $ adc %r0+7,1\n\t"\
263 "out __RAMPZ__,%r0+7 $ elpm $ mov %r0+7,0" \
264 : "=r" (res) \
265 : "r" (addr) \
266 : "r30", "r31", "r0")
267#else
268/* No ELPM: Fall back to __LPM__<N>. */
269#define __ELPM__1(r,a,T) const T *__a = (const T*)(uint16_t) a; __LPM__1(r,__a)
270#define __ELPM__2(r,a,T) const T *__a = (const T*)(uint16_t) a; __LPM__2(r,__a)
271#define __ELPM__3(r,a,T) const T *__a = (const T*)(uint16_t) a; __LPM__3(r,__a)
272#define __ELPM__4(r,a,T) const T *__a = (const T*)(uint16_t) a; __LPM__4(r,__a)
273#define __ELPM__8(r,a,T) const T *__a = (const T*)(uint16_t) a; __LPM__8(r,__a)
274#endif /* ELPM cases */
275
276#endif /* __BITS_LPM_ELPM_H_ */