Skip to content

Commit f9eb48b

Browse files
Wren6991kilograham
andauthored
Add XIP cache maintenance API (fixes #2005) (#2013)
* Add XIP cache maintenance API (fixes #2005) Also add a cache clean to hardware_flash implementations, to avoid losing pending writes on the subsequent invalidate. * Fix comment typos, add to docs index, remove unnecessary defaulting of XIP_CACHE assertions on FLASH * Fix sense of unsigned wrapping test * update bazel build --------- Co-authored-by: graham sanderson <[email protected]>
1 parent 09a9379 commit f9eb48b

File tree

11 files changed

+348
-0
lines changed

11 files changed

+348
-0
lines changed

docs/index.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@
4646
* \cond hardware_uart \defgroup hardware_uart hardware_uart \endcond
4747
* \cond hardware_vreg \defgroup hardware_vreg hardware_vreg \endcond
4848
* \cond hardware_watchdog \defgroup hardware_watchdog hardware_watchdog \endcond
49+
* \cond hardware_xip_cache \defgroup hardware_xip_cache hardware_xip_cache \endcond
4950
* \cond hardware_xosc \defgroup hardware_xosc hardware_xosc \endcond
5051
* \cond hardware_powman hardware_powman
5152
* \cond hardware_hazard3 hardware_hazard3

src/cmake/rp2_common.cmake

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@ pico_add_subdirectory(rp2_common/hardware_timer)
6060
pico_add_subdirectory(rp2_common/hardware_uart)
6161
pico_add_subdirectory(rp2_common/hardware_vreg)
6262
pico_add_subdirectory(rp2_common/hardware_watchdog)
63+
pico_add_subdirectory(rp2_common/hardware_xip_cache)
6364
pico_add_subdirectory(rp2_common/hardware_xosc)
6465

6566
if (PICO_RP2350 OR PICO_COMBINED_DOCS)

src/rp2_common/hardware_flash/BUILD.bazel

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ cc_library(
1111
target_compatible_with = compatible_with_rp2(),
1212
deps = [
1313
"//src/rp2_common:hardware_structs",
14+
"//src/rp2_common/hardware_xip_cache",
1415
"//src/rp2_common:pico_platform",
1516
"//src/rp2_common/pico_bootrom",
1617
"//src/rp2_common/pico_multicore",
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
11
pico_simple_hardware_target(flash)
22
pico_mirrored_target_link_libraries(hardware_flash INTERFACE pico_bootrom)
3+
pico_mirrored_target_link_libraries(hardware_flash INTERFACE hardware_xip_cache)

src/rp2_common/hardware_flash/flash.c

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
#else
1414
#include "hardware/structs/qmi.h"
1515
#endif
16+
#include "hardware/xip_cache.h"
1617

1718
#define FLASH_BLOCK_ERASE_CMD 0xd8
1819

@@ -84,6 +85,8 @@ void __no_inline_not_in_flash_func(flash_range_erase)(uint32_t flash_offs, size_
8485
rom_flash_flush_cache_fn flash_flush_cache_func = (rom_flash_flush_cache_fn)rom_func_lookup_inline(ROM_FUNC_FLASH_FLUSH_CACHE);
8586
assert(connect_internal_flash_func && flash_exit_xip_func && flash_range_erase_func && flash_flush_cache_func);
8687
flash_init_boot2_copyout();
88+
// Commit any pending writes to external RAM, to avoid losing them in the subsequent flush:
89+
xip_cache_clean_all();
8790

8891
// No flash accesses after this point
8992
__compiler_memory_barrier();
@@ -112,6 +115,7 @@ void __no_inline_not_in_flash_func(flash_range_program)(uint32_t flash_offs, con
112115
rom_flash_flush_cache_fn flash_flush_cache_func = (rom_flash_flush_cache_fn)rom_func_lookup_inline(ROM_FUNC_FLASH_FLUSH_CACHE);
113116
assert(connect_internal_flash_func && flash_exit_xip_func && flash_range_program_func && flash_flush_cache_func);
114117
flash_init_boot2_copyout();
118+
xip_cache_clean_all();
115119

116120
__compiler_memory_barrier();
117121

@@ -152,6 +156,8 @@ void __no_inline_not_in_flash_func(flash_do_cmd)(const uint8_t *txbuf, uint8_t *
152156
rom_flash_flush_cache_fn flash_flush_cache_func = (rom_flash_flush_cache_fn)rom_func_lookup_inline(ROM_FUNC_FLASH_FLUSH_CACHE);
153157
assert(connect_internal_flash_func && flash_exit_xip_func && flash_flush_cache_func);
154158
flash_init_boot2_copyout();
159+
xip_cache_clean_all();
160+
155161
__compiler_memory_barrier();
156162
connect_internal_flash_func();
157163
flash_exit_xip_func();
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
load("//bazel:defs.bzl", "compatible_with_rp2")
2+
3+
package(default_visibility = ["//visibility:public"])
4+
5+
cc_library(
6+
name = "hardware_xip_cache",
7+
srcs = ["xip_cache.c"],
8+
hdrs = ["include/hardware/xip_cache.h"],
9+
includes = ["include"],
10+
target_compatible_with = compatible_with_rp2(),
11+
deps = [
12+
"//src/rp2_common/hardware_sync",
13+
],
14+
)
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
pico_simple_hardware_target(xip_cache)
2+
3+
pico_mirrored_target_link_libraries(hardware_xip_cache INTERFACE hardware_sync)
Lines changed: 210 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,210 @@
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
173+
static inline void xip_cache_clean_all(void) {}
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

0 commit comments

Comments
 (0)