1 /*
2 * Copyright (c) 2024 Raspberry Pi Ltd.
3 *
4 * SPDX-License-Identifier: BSD-3-Clause
5 */
6
7 #ifndef _HARDWARE_XIP_CACHE_H
8 #define _HARDWARE_XIP_CACHE_H
9
10 #include "pico.h"
11 #include "hardware/regs/addressmap.h"
12
13 /** \file xip_cache.h
14 * \defgroup hardware_xip_cache hardware_xip_cache
15 *
16 * \brief Low-level cache maintenance operations for the XIP cache
17 *
18 * These functions apply some maintenance operation to either the entire cache contents, or a range
19 * of offsets within the downstream address space. Offsets start from 0 (indicating the first byte
20 * of flash), so pointers should have XIP_BASE subtracted before passing into one of these
21 * functions.
22 *
23 * \if rp2040-specific
24 * The only valid cache maintenance operation on RP2040 is "invalidate", which tells the cache to
25 * forget everything it knows about some address. This is necessary after a programming operation,
26 * because the cache does not automatically know about any serial programming operations performed
27 * on the external flash device, and could return stale data.
28 * \endif
29 *
30 * \if rp2350-specific
31 * On RP2350, the three types of operation are:
32 *
33 * * Invalidate: tell the cache to forget everything it knows about some address. The next access to
34 * that address will fetch from downstream memory.
35 *
36 * * Clean: if the addressed cache line contains data not yet written to external memory, then write
37 * that data out now, and mark the line as "clean" (i.e. not containing uncommitted write data)
38 *
39 * * Pin: mark an address as always being resident in the cache. This persists until the line is
40 * invalidated, and can be used to allocate part of the cache for cache-as-SRAM use.
41 *
42 * When using both external flash and external RAM (e.g. PSRAM), a simple way to maintain coherence
43 * over flash programming operations is to:
44 *
45 * 1. Clean the entire cache (e.g. using xip_cache_clean_all())
46 *
47 * 2. Erase + program the flash using serial SPI commands
48 *
49 * 3. Invalidate ("flush") the entire cache (e.g. using xip_cache_invalidate_all())
50 *
51 * The invalidate ensures the programming is visible to subsequent reads. The clean ensures that the
52 * invalidate does not discard any cached PSRAM write data.
53 *
54 * \endif
55 *
56 */
57
58 // PICO_CONFIG: PARAM_ASSERTIONS_ENABLED_HARDWARE_XIP_CACHE, Enable/disable assertions in the hardware_xip_cache module, type=bool, default=0, group=hardware_xip_cache
59 #ifndef PARAM_ASSERTIONS_ENABLED_HARDWARE_XIP_CACHE
60 #define PARAM_ASSERTIONS_ENABLED_HARDWARE_XIP_CACHE 0
61 #endif
62
63 #define XIP_CACHE_LINE_SIZE _u(8)
64
65 #define XIP_CACHE_SIZE (_u(16) * _u(1024))
66
67 #if PICO_RP2040
68 #define XIP_CACHE_ADDRESS_SPACE_SIZE (_u(16) * _u(1024) * _u(1024))
69 #else
70 #define XIP_CACHE_ADDRESS_SPACE_SIZE (XIP_END - XIP_BASE)
71 #endif
72
73 // A read-only cache never requires cleaning (you can still call the functions, they are just no-ops)
74 #if PICO_RP2040
75 #define XIP_CACHE_IS_READ_ONLY 1
76 #else
77 #define XIP_CACHE_IS_READ_ONLY 0
78 #endif
79
80 #ifndef __ASSEMBLER__
81
82 #ifdef __cplusplus
83 extern "C" {
84 #endif
85
86 /*! \brief Invalidate the cache for the entire XIP address space
87 * \ingroup hardware_xip_cache
88 *
89 * Invalidation ensures that subsequent reads will fetch data from the downstream memory, rather
90 * than using (potentially stale) cached data.
91 *
92 * This function is faster than calling xip_cache_invalidate_range() for the entire address space,
93 * because it iterates over cachelines instead of addresses.
94 *
95 * @note Any pending write data held in the cache is lost: you can force the cache to commit these
96 * writes first, by calling xip_cache_clean_all()
97 *
98 * @note Unlike flash_flush_cache(), this function affects *only* the cache line state.
99 * flash_flush_cache() calls a ROM API which can have other effects on some platforms, like
100 * cleaning up the bootrom's QSPI GPIO setup on RP2040. Prefer this function for general cache
101 * maintenance use, and prefer flash_flush_cache in sequences of ROM flash API calls.
102 */
103 void xip_cache_invalidate_all(void);
104
105 /*! \brief Invalidate a range of offsets within the XIP address space
106 * \ingroup hardware_xip_cache
107 *
108 * \param start_offset The first offset to be invalidated. Offset 0 means the first byte of XIP
109 * memory (e.g. flash). Pointers must have XIP_BASE subtracted before passing into this function.
110 * Must be 4-byte-aligned on RP2040. Must be a aligned to the start of a cache line
111 * (XIP_CACHE_LINE_SIZE) on other platforms.
112 *
113 * \param size_bytes The number of bytes to invalidate. Must be a multiple of 4 bytes on RP2040.
114 * Must be a multiple of XIP_CACHE_LINE_SIZE on other platforms.
115 *
116 * Invalidation ensures that subsequent reads will fetch data from the downstream memory, rather
117 * than using (potentially stale) cached data.
118
119 * @note Any pending write data held in the cache is lost: you can force the cache to commit these
120 * writes first, by calling xip_cache_clean_range() with the same parameters. Generally this is
121 * not necessary because invalidation is used with flash (write-behind via programming), and
122 * cleaning is used with PSRAM (writing through the cache).
123 *
124 */
125 void xip_cache_invalidate_range(uintptr_t start_offset, uintptr_t size_bytes);
126
127 #if !XIP_CACHE_IS_READ_ONLY
128
129 /*! \brief Clean the cache for the entire XIP address space
130 * \ingroup hardware_xip_cache
131 *
132 * This causes the cache to write out all pending write data to the downstream memory. For example,
133 * when suspending the system with state retained in external PSRAM, this ensures all data has made
134 * it out to external PSRAM before powering down.
135 *
136 * This function is faster than calling xip_cache_clean_range() for the entire address space,
137 * because it iterates over cachelines instead of addresses.
138 *
139 * \if rp2040-specific
140 * On RP2040 this is a no-op, as the XIP cache is read-only. This is indicated by the
141 * XIP_CACHE_IS_READ_ONLY macro.
142 * \endif
143 *
144 * \if rp2350-specific
145 * On RP2350, due to the workaround applied for RP2350-E11, this function also effectively
146 * invalidates all cache lines after cleaning them. The next access to each line will miss. Avoid
147 * this by calling xip_cache_clean_range() which does not suffer this issue.
148 * \endif
149 *
150 */
151 void xip_cache_clean_all(void);
152
153 /*! \brief Clean a range of offsets within the XIP address space
154 * \ingroup hardware_xip_cache
155 *
156 * This causes the cache to write out pending write data at these offsets to the downstream memory.
157 *
158 * \if rp2040-specific
159 * On RP2040 this is a no-op, as the XIP cache is read-only. This is indicated by the
160 * XIP_CACHE_IS_READ_ONLY macro.
161 * \endif
162 *
163 * \param start_offset The first offset to be invalidated. Offset 0 means the first byte of XIP
164 * memory (e.g. flash). Pointers must have XIP_BASE subtracted before passing into this function.
165 * Must be aligned to the start of a cache line (XIP_CACHE_LINE_SIZE).
166 *
167 * \param size_bytes The number of bytes to clean. Must be a multiple of XIP_CACHE_LINE_SIZE.
168 */
169 void xip_cache_clean_range(uintptr_t start_offset, uintptr_t size_bytes);
170
171 #else
172 // Stub these out inline to avoid generating a call to an empty function when they are no-ops
xip_cache_clean_all(void)173 static inline void xip_cache_clean_all(void) {}
xip_cache_clean_range(uintptr_t start_offset,uintptr_t size_bytes)174 static inline void xip_cache_clean_range(uintptr_t start_offset, uintptr_t size_bytes) {
175 (void)start_offset;
176 (void)size_bytes;
177 }
178 #endif
179
180 #if !PICO_RP2040
181
182 /*! \brief Pin a range of offsets within the XIP address space
183 * \ingroup hardware_xip_cache
184 *
185 * Pinning a line at an address allocates the line exclusively for use at that address. This means
186 * that all subsequent accesses to that address will hit the cache, and will not go to downstream
187 * memory. This persists until one of two things happens:
188 *
189 * * The line is invalidated, e.g. via xip_cache_invalidate_all()
190 *
191 * * The same line is pinned at a different address (note lines are selected by address modulo
192 * XIP_CACHE_SIZE)
193 *
194 * \param start_offset The first offset to be pinnned. Offset 0 means the first byte of XIP
195 * memory (e.g. flash). Pointers must have XIP_BASE subtracted before passing into this function.
196 * Must be aligned to the start of a cache line (XIP_CACHE_LINE_SIZE).
197 *
198 * \param size_bytes The number of bytes to pin. Must be a multiple of XIP_CACHE_LINE_SIZE.
199 *
200 */
201 void xip_cache_pin_range(uintptr_t start_offset, uintptr_t size_bytes);
202 #endif
203
204 #ifdef __cplusplus
205 }
206 #endif
207
208 #endif // !__ASSEMBLER__
209
210 #endif // !_HARDWARE_XIP_CACHE_H
211