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