1 /*---------------------------------------------------------------------------*
2 Project: TwlSDK - MI
3 File: mi_cache.c
4
5 Copyright 2007-2008 Nintendo. All rights reserved.
6
7 These coded instructions, statements, and computer programs contain
8 proprietary information of Nintendo of America Inc. and/or Nintendo
9 Company Ltd., and are protected by Federal copyright law. They may
10 not be disclosed to third parties or copied or duplicated in any form,
11 in whole or in part, without the prior written consent of Nintendo.
12
13 $Date:: 2008-11-07#$
14 $Rev: 9260 $
15 $Author: yosizaki $
16
17 *---------------------------------------------------------------------------*/
18
19
20 #include <nitro.h>
21
22
23 /*---------------------------------------------------------------------------*/
24 /* Functions */
25
26 /*---------------------------------------------------------------------------*
27 Name: MI_InitCache
28
29 Description: Initializes the memory cache.
30
31 Arguments: cache The MICache structure to be initialized.
32 page The buffer size per page.
33 Must be a power of two if four or more pages.
34 buffer The buffer used for page management information.
35 length The buffer size.
36 Broken into page lists that have the number equal to (length / (sizeof(MICachePage) + page))
37
38 It is guaranteed that the buffer leading address will be (buffer + N * page) for each page (N = 0, 1,...).
39
40
41 Returns: None.
42 *---------------------------------------------------------------------------*/
MI_InitCache(MICache * cache,u32 page,void * buffer,u32 length)43 void MI_InitCache(MICache *cache, u32 page, void *buffer, u32 length)
44 {
45 /* As a result of the current implementation, pages smaller than the word size are not supported */
46 SDK_ASSERT(page >= sizeof(u32));
47 /* Member initialization */
48 cache->pagewidth = MATH_CTZ(page);
49 cache->valid_total = 0;
50 cache->invalid_total = 0;
51 cache->loading_total = 0;
52 cache->valid = NULL;
53 cache->invalid = NULL;
54 cache->loading = NULL;
55 /* Page division */
56 {
57 u32 total = length / (sizeof(MICachePage) + page);
58 u8 *buf = (u8*)buffer;
59 MICachePage *inf = (MICachePage*)&buf[total * page];
60 cache->invalid_total += total;
61 for (; total > 0; --total)
62 {
63 inf->offset = 0;
64 inf->buffer = buf;
65 inf->next = cache->invalid;
66 cache->invalid = inf;
67 inf += 1;
68 buf += page;
69 }
70 }
71 }
72
73 /*---------------------------------------------------------------------------*
74 Name: WFSi_TouchCachePages
75
76 Description: Requests load preparations for the specified page range.
77 If the invalid list is empty, destroys the active list from its end.
78
79 Arguments: cache The MICache structure.
80 head The load target's leading page number.
81 bitset The load target's page's bit set.
82
83 Returns: None.
84 *---------------------------------------------------------------------------*/
WFSi_TouchCachePages(MICache * cache,u32 head,u32 bitset)85 static void WFSi_TouchCachePages(MICache *cache, u32 head, u32 bitset)
86 {
87 {
88 PLATFORM_ENTER_CRITICALSECTION();
89 MICachePage **load;
90 /* Search the request list, and exempt and duplicates for the current material */
91 for (load = &cache->loading; *load; load = &(*load)->next)
92 {
93 MICachePage *p = *load;
94 u32 pos = p->offset - head;
95 if ((pos < 32) && ((bitset & (1 << pos)) != 0))
96 {
97 bitset &= ~(1UL << pos);
98 }
99 }
100 /* If the invalid list is insufficient, destroys the active list from its end */
101 {
102 int rest = MATH_CountPopulation(bitset) - cache->invalid_total;
103 if (rest > 0)
104 {
105 int del = cache->valid_total;
106 MICachePage **valid;
107 for (valid = &cache->valid; *valid; valid = &(*valid)->next)
108 {
109 if (--del < rest)
110 {
111 MICachePage **pp;
112 for (pp = &cache->invalid; *pp; pp = &(*pp)->next)
113 {
114 }
115 *pp = *valid;
116 *valid = NULL;
117 cache->valid_total -= rest;
118 cache->invalid_total += rest;
119 break;
120 }
121 }
122 }
123 }
124 /* Move from the head of the invalid list to the end of the request list */
125 while (cache->invalid && bitset)
126 {
127 MICachePage *p = cache->invalid;
128 u32 pos = MATH_CTZ(bitset);
129 cache->invalid = p->next;
130 p->offset = head + pos;
131 p->next = NULL;
132 *load = p;
133 load = &p->next;
134 --cache->invalid_total;
135 --cache->loading_total;
136 bitset &= ~(1UL << pos);
137 }
138 PLATFORM_LEAVE_CRITICALSECTION();
139 }
140 /* If there are absolutely fewer pages than specified, the use method us invalid; issue only one warning */
141 if (bitset)
142 {
143 static BOOL output_once = FALSE;
144 if (!output_once)
145 {
146 output_once = TRUE;
147 OS_TWarning("not enough cache-page! "
148 "MI_TouchCache() failed. "
149 "(total pages = %d, but need more %d)",
150 cache->invalid_total + cache->valid_total,
151 MATH_CountPopulation(bitset));
152 }
153 }
154 }
155
156
157 /*---------------------------------------------------------------------------*
158 Name: MI_ReadCache
159
160 Description: Reads data from the cache.
161 Move the hit pages to the top of the active list
162
163 Arguments: cache The MICache structure.
164 buffer The transfer target memory.
165 When NULL is specified, only cache preparations are requested for the entire relevant range without loading any data.
166
167 offset The transfer source offset.
168 length The transfer size.
169
170 Returns: TRUE if the entire region is hit by the cache.
171 *---------------------------------------------------------------------------*/
MI_ReadCache(MICache * cache,void * buffer,u32 offset,u32 length)172 BOOL MI_ReadCache(MICache *cache, void *buffer, u32 offset, u32 length)
173 {
174 BOOL retval = TRUE;
175
176 /* Determine from the front in 32-page units */
177 const u32 unit = (1UL << cache->pagewidth);
178 u32 head = (offset >> cache->pagewidth);
179 u32 tail = ((offset + length + unit - 1UL) >> cache->pagewidth);
180 u32 pages;
181 for (; (pages = MATH_MIN(tail - head, 32UL)), (pages > 0); head += pages)
182 {
183 /* Search for the relevant pages from the active list */
184 u32 bitset = (1UL << pages) - 1UL;
185 {
186 PLATFORM_ENTER_CRITICALSECTION();
187 MICachePage **pp;
188 for (pp = &cache->valid; *pp && bitset; pp = &(*pp)->next)
189 {
190 MICachePage *p = *pp;
191 u32 pos = p->offset - head;
192 if ((pos < pages) && ((bitset & (1UL << pos)) != 0))
193 {
194 // Move the hit pages to the top of the active list
195 *pp = (*pp)->next;
196 p->next = cache->valid;
197 cache->valid = p;
198 pp = &cache->valid;
199 if (buffer)
200 {
201 u32 len = unit;
202 int src = 0;
203 int dst = (int)((p->offset << cache->pagewidth) - offset);
204 if (dst < 0)
205 {
206 len += dst;
207 src -= dst;
208 dst = 0;
209 }
210 if (dst + len > length)
211 {
212 len = length - dst;
213 }
214 MI_CpuCopy(&p->buffer[src], &((u8*)buffer)[dst], len);
215 }
216 bitset &= ~(1UL << pos);
217 }
218 }
219 PLATFORM_LEAVE_CRITICALSECTION();
220 }
221 /* When there is a page fault */
222 if (bitset)
223 {
224 retval = FALSE;
225 WFSi_TouchCachePages(cache, head, bitset);
226 }
227 }
228 return retval;
229 }
230
231 /*---------------------------------------------------------------------------*
232 Name: MI_LoadCache
233
234 Description: Runs the load process for all pages that exist in the load request list.
235 When the load request list is empty, nothing happens and a control is immediately returned; if an addition is made to the load request list during a call, that is processed too.
236
237
238 Note: This function must be called at a timing appropriate to a context in which device blocking is acceptable.
239
240 Therefore, it must not be called from an interrupt handler, etc.
241
242 Arguments: cache The MICache structure.
243 device The device to be loaded.
244
245 Returns: None.
246 *---------------------------------------------------------------------------*/
MI_LoadCache(MICache * cache,MIDevice * device)247 void MI_LoadCache(MICache *cache, MIDevice *device)
248 {
249 for (;;)
250 {
251 /* Get the header of the load request list */
252 MICachePage *p = cache->loading;
253 if (!p)
254 {
255 break;
256 }
257 /* Read pages from the device */
258 (void)MI_ReadDevice(device, p->buffer,
259 (p->offset << cache->pagewidth),
260 (1UL << cache->pagewidth));
261 /* Insert read pages onto the head of the active list */
262 {
263 PLATFORM_ENTER_CRITICALSECTION();
264 cache->loading = p->next;
265 p->next = cache->valid;
266 cache->valid = p;
267 ++cache->valid_total;
268 ++cache->loading_total;
269 PLATFORM_LEAVE_CRITICALSECTION();
270 }
271 }
272 }
273
274
275 /*---------------------------------------------------------------------------*
276 $Log: mi_cache.c,v $
277 Revision 1.1 2007/04/11 08:06:25 yosizaki
278 Initial upload.
279
280 $NoKeywords: $
281 *---------------------------------------------------------------------------*/
282