GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: lx_nor_flash_next_block_to_erase_find.c Lines: 83 120 69.2 %
Date: 2024-03-11 05:20:25 Branches: 42 66 63.6 %

Line Branch Exec Source
1
/***************************************************************************
2
 * Copyright (c) 2024 Microsoft Corporation
3
 *
4
 * This program and the accompanying materials are made available under the
5
 * terms of the MIT License which is available at
6
 * https://opensource.org/licenses/MIT.
7
 *
8
 * SPDX-License-Identifier: MIT
9
 **************************************************************************/
10
11
12
/**************************************************************************/
13
/**************************************************************************/
14
/**                                                                       */
15
/** LevelX Component                                                      */
16
/**                                                                       */
17
/**   NOR Flash                                                           */
18
/**                                                                       */
19
/**************************************************************************/
20
/**************************************************************************/
21
22
#define LX_SOURCE_CODE
23
24
25
/* Disable ThreadX error checking.  */
26
27
#ifndef LX_DISABLE_ERROR_CHECKING
28
#define LX_DISABLE_ERROR_CHECKING
29
#endif
30
31
32
/* Include necessary system files.  */
33
34
#include "lx_api.h"
35
36
37
/**************************************************************************/
38
/*                                                                        */
39
/*  FUNCTION                                               RELEASE        */
40
/*                                                                        */
41
/*    _lx_nor_flash_next_block_to_erase_find              PORTABLE C      */
42
/*                                                           6.3.0        */
43
/*  AUTHOR                                                                */
44
/*                                                                        */
45
/*    William E. Lamie, Microsoft Corporation                             */
46
/*                                                                        */
47
/*  DESCRIPTION                                                           */
48
/*                                                                        */
49
/*    This function finds the next block to erase in the NOR flash.       */
50
/*                                                                        */
51
/*  INPUT                                                                 */
52
/*                                                                        */
53
/*    nor_flash                             NOR flash instance            */
54
/*    return_erase_block                    Returned block to erase       */
55
/*    return_erase_count                    Returned erase count of block */
56
/*    return_mapped_sectors                 Returned number of mapped     */
57
/*                                            sectors                     */
58
/*    return_obsolete_sectors               Returned number of obsolete   */
59
/*                                            sectors                     */
60
/*                                                                        */
61
/*  OUTPUT                                                                */
62
/*                                                                        */
63
/*    return status                                                       */
64
/*                                                                        */
65
/*  CALLS                                                                 */
66
/*                                                                        */
67
/*    _lx_nor_flash_driver_read             Driver flash sector read      */
68
/*    _lx_nor_flash_system_error            Internal system error handler */
69
/*                                                                        */
70
/*  CALLED BY                                                             */
71
/*                                                                        */
72
/*    Internal LevelX                                                     */
73
/*                                                                        */
74
/*  RELEASE HISTORY                                                       */
75
/*                                                                        */
76
/*    DATE              NAME                      DESCRIPTION             */
77
/*                                                                        */
78
/*  05-19-2020     William E. Lamie         Initial Version 6.0           */
79
/*  09-30-2020     William E. Lamie         Modified comment(s),          */
80
/*                                            resulting in version 6.1    */
81
/*  06-02-2021     Bhupendra Naphade        Modified comment(s),          */
82
/*                                            resulting in version 6.1.7  */
83
/*  10-31-2023     Xiuwen Cai               Modified comment(s),          */
84
/*                                            added mapping bitmap cache, */
85
/*                                            added obsolete count cache, */
86
/*                                            optimized full obsoleted    */
87
/*                                            block searching logic,      */
88
/*                                            resulting in version 6.3.0  */
89
/*                                                                        */
90
/**************************************************************************/
91
1160
UINT  _lx_nor_flash_next_block_to_erase_find(LX_NOR_FLASH *nor_flash, ULONG *return_erase_block, ULONG *return_erase_count, ULONG *return_mapped_sectors, ULONG *return_obsolete_sectors)
92
{
93
94
ULONG   *block_word_ptr;
95
ULONG   *list_word_ptr;
96
ULONG   list_word;
97
ULONG   i, j;
98
ULONG   mapped_sectors;
99
ULONG   erase_count;
100
ULONG   obsolete_sectors;
101
1160
ULONG   min_block_erase = 0;
102
ULONG   min_block_erase_count;
103
1160
ULONG   min_block_obsolete_count = 0;
104
1160
ULONG   min_block_mapped_count = 0;
105
1160
ULONG   min_block_mapped_count_available = LX_FALSE;
106
ULONG   max_obsolete_sectors;
107
1160
ULONG   max_obsolete_block = 0;
108
1160
ULONG   max_obsolete_erase_count = 0;
109
1160
ULONG   max_obsolete_mapped_count = 0;
110
1160
ULONG   max_obsolete_mapped_count_available = LX_FALSE;
111
ULONG   min_system_block_erase_count;
112
ULONG   system_min_erased_blocks;
113
ULONG   max_system_block_erase_count;
114
ULONG   erase_count_threshold;
115
ULONG   min_logical_sector;
116
ULONG   max_logical_sector;
117
#ifndef LX_DIRECT_READ
118
UINT    status;
119
#endif
120
UINT    obsolete_sectors_available;
121
UINT    mapped_sectors_available;
122
123
124
    /* Setup the block word pointer to the first word of the search block.  */
125
1160
    block_word_ptr =  nor_flash -> lx_nor_flash_base_address;
126
127
    /* Initialize the minimum erase count.  */
128
1160
    min_block_erase_count =  LX_ALL_ONES;
129
130
    /* Initialize the system minimum and maximum erase counts.  */
131
1160
    min_system_block_erase_count =  LX_ALL_ONES;
132
1160
    system_min_erased_blocks = 0;
133
1160
    max_system_block_erase_count =  0;
134
135
    /* Initialize the maximum obsolete sector count.  */
136
1160
    max_obsolete_sectors =  0;
137
138
    /* Calculate the erase count threshold.  */
139
1160
    if (nor_flash -> lx_nor_flash_free_physical_sectors >= nor_flash -> lx_nor_flash_physical_sectors_per_block)
140
    {
141
142
        /* Calculate erase count threshold by adding constant to the current minimum.  */
143
1160
        erase_count_threshold =  nor_flash -> lx_nor_flash_minimum_erase_count + LX_NOR_FLASH_MAX_ERASE_COUNT_DELTA;
144
    }
145
    else
146
    {
147
148
        /* When the number of free sectors is low, simply pick the block that has the most number of obsolete sectors.  */
149
        erase_count_threshold =  LX_ALL_ONES;
150
    }
151
152
    /* Loop through the blocks to attempt to find the mapped logical sector.  */
153
10440
    for (i = 0; i < nor_flash -> lx_nor_flash_total_blocks; i++)
154
    {
155
156
        /* Read the erase count of this block.  */
157
#ifdef LX_DIRECT_READ
158
159
        /* Read the word directly.  */
160
        erase_count =  *(block_word_ptr);
161
#else
162
9280
        status =  _lx_nor_flash_driver_read(nor_flash, block_word_ptr, &erase_count, 1);
163
164
        /* Check for an error from flash driver. Drivers should never return an error..  */
165
9280
        if (status)
166
        {
167
168
            /* Call system error handler.  */
169
            _lx_nor_flash_system_error(nor_flash, status);
170
171
            /* Return the error.  */
172
            return(status);
173
        }
174
#endif
175
176
        /* Update the system minimum and maximum erase counts.  */
177
9280
        if (erase_count == min_system_block_erase_count)
178
        {
179
2634
            system_min_erased_blocks ++;
180
        }
181
9280
        if (erase_count < min_system_block_erase_count)
182
        {
183
2566
            min_system_block_erase_count =  erase_count;
184
2566
            system_min_erased_blocks = 1;
185
        }
186
9280
        if (erase_count > max_system_block_erase_count)
187
2034
            max_system_block_erase_count =  erase_count;
188
189
        /* Initialize the obsolete and mapped sector count available flags.  */
190
9280
        obsolete_sectors_available =  LX_FALSE;
191
9280
        mapped_sectors_available =  LX_FALSE;
192
193
#ifdef LX_NOR_ENABLE_OBSOLETE_COUNT_CACHE
194
195
        /* Determine if the obsolete sector count is available in the cache.  */
196
        if (i < nor_flash -> lx_nor_flash_extended_cache_obsolete_count_max_block)
197
        {
198
199
            /* Yes, the obsolete sector count is available.  */
200
            obsolete_sectors_available =  LX_TRUE;
201
202
            /* Pickup the obsolete sector count from the cache.  */
203
            obsolete_sectors = (ULONG)nor_flash -> lx_nor_flash_extended_cache_obsolete_count[i];
204
        }
205
        else
206
        {
207
#endif
208
        /* Read the minimum and maximum logical sector values in this block.  */
209
#ifdef LX_DIRECT_READ
210
211
        /* Read the word directly.  */
212
        min_logical_sector =  *(block_word_ptr + LX_NOR_FLASH_MIN_LOGICAL_SECTOR_OFFSET);
213
#else
214
9280
        status =  _lx_nor_flash_driver_read(nor_flash, block_word_ptr + LX_NOR_FLASH_MIN_LOGICAL_SECTOR_OFFSET, &min_logical_sector, 1);
215
216
        /* Check for an error from flash driver. Drivers should never return an error..  */
217
9280
        if (status)
218
        {
219
220
            /* Call system error handler.  */
221
            _lx_nor_flash_system_error(nor_flash, status);
222
223
            /* Return the error.  */
224
            return(status);
225
        }
226
#endif
227
228
        /* Determine if the minimum logical sector is valid.  */
229
9280
        if (min_logical_sector != LX_ALL_ONES)
230
        {
231
#ifdef LX_DIRECT_READ
232
233
            /* Read the word directly.  */
234
            max_logical_sector =  *(block_word_ptr + LX_NOR_FLASH_MAX_LOGICAL_SECTOR_OFFSET);
235
#else
236
8026
            status =  _lx_nor_flash_driver_read(nor_flash, block_word_ptr + LX_NOR_FLASH_MAX_LOGICAL_SECTOR_OFFSET, &max_logical_sector, 1);
237
238
            /* Check for an error from flash driver. Drivers should never return an error..  */
239
8026
            if (status)
240
            {
241
242
                /* Call system error handler.  */
243
                _lx_nor_flash_system_error(nor_flash, status);
244
245
                /* Return the error.  */
246
                return(status);
247
            }
248
#endif
249
250
            /* Are the values valid?  */
251
            /* Now let's check to see if all the sector are obsoleted.  */
252

8026
            if ((max_logical_sector != LX_ALL_ONES) && (max_logical_sector < min_logical_sector))
253
            {
254
255
                obsolete_sectors_available =  LX_TRUE;
256
                obsolete_sectors =  nor_flash -> lx_nor_flash_physical_sectors_per_block;
257
            }
258
        }
259
#ifdef LX_NOR_ENABLE_OBSOLETE_COUNT_CACHE
260
        }
261
#endif
262
263
        /* Determine if the mapped sector count is available.  */
264
9280
        if (obsolete_sectors_available == LX_FALSE)
265
        {
266
267
            /* Compute the number of obsolete and mapped sectors for this block.  */
268
269
            /* Initialize the obsolete and mapped sector counts.  */
270
9280
            obsolete_sectors =  0;
271
9280
            mapped_sectors =    0;
272
273
            /* Set the mapped sector count and obsolete sector count available flags.  */
274
9280
            mapped_sectors_available =  LX_TRUE;
275
9280
            obsolete_sectors_available =  LX_TRUE;
276
277
            /* Setup a pointer to the mapped list.  */
278
9280
            list_word_ptr =  block_word_ptr + nor_flash -> lx_nor_flash_block_physical_sector_mapping_offset;
279
280
            /* Loop through the mapped list for this block.  */
281
129858
            for (j = 0; j < nor_flash -> lx_nor_flash_physical_sectors_per_block; j++)
282
            {
283
284
                /* Read the current mapping entry.  */
285
#ifdef LX_DIRECT_READ
286
287
                /* Read the word directly.  */
288
                list_word =  *(list_word_ptr);
289
#else
290
121832
                status =  _lx_nor_flash_driver_read(nor_flash, list_word_ptr, &list_word, 1);
291
292
                /* Check for an error from flash driver. Drivers should never return an error..  */
293
121832
                if (status)
294
                {
295
296
                    /* Call system error handler.  */
297
                    _lx_nor_flash_system_error(nor_flash, status);
298
299
                    /* Return the error.  */
300
                    return(status);
301
                }
302
#endif
303
304
                /* Determine if the entry hasn't been used.  */
305
121832
                if (list_word == LX_NOR_PHYSICAL_SECTOR_FREE)
306
                {
307
308
                    /* Since allocations are done sequentially in the block, we know nothing
309
                       else exists after this point.  */
310
1254
                    break;
311
                }
312
313
                /* Is this entry obsolete?  */
314
120578
                if ((list_word & LX_NOR_PHYSICAL_SECTOR_VALID) == 0)
315
                {
316
317
                    /* Increment the number of obsolete sectors.  */
318
6768
                    obsolete_sectors++;
319
                }
320
                else
321
                {
322
323
                    /* Increment the number of mapped sectors.  */
324
113810
                    mapped_sectors++;
325
                }
326
327
                /* Move the list pointer ahead.  */
328
120578
                list_word_ptr++;
329
            }
330
        }
331
332
        /* Determine if this block contains full obsoleted sectors and the erase count is minimum.  */
333
9280
        if ((obsolete_sectors == nor_flash -> lx_nor_flash_physical_sectors_per_block) &&
334
            (erase_count == nor_flash -> lx_nor_flash_minimum_erase_count) &&
335
            (nor_flash -> lx_nor_flash_minimum_erased_blocks > 0))
336
        {
337
338
            /* Yes, we have a full obsoleted block with minimum erase count.  */
339
            *return_erase_block =       i;
340
            *return_erase_count =       erase_count;
341
            *return_obsolete_sectors =  obsolete_sectors;
342
            *return_mapped_sectors =    mapped_sectors;
343
344
            break;
345
        }
346
347
348
        /* Determine if we have a block with a new maximum number of obsolete sectors.  */
349

9280
        if ((obsolete_sectors > max_obsolete_sectors) && (erase_count <= erase_count_threshold))
350
        {
351
352
            /* Update the new maximum obsolete sectors and related information.  */
353
1726
            max_obsolete_sectors =      obsolete_sectors;
354
1726
            max_obsolete_block =        i;
355
1726
            max_obsolete_erase_count =  erase_count;
356
1726
            max_obsolete_mapped_count = mapped_sectors;
357
1726
            max_obsolete_mapped_count_available = mapped_sectors_available;
358
359
        }
360

7554
        else if ((max_obsolete_sectors) && (obsolete_sectors == max_obsolete_sectors) && (erase_count <= erase_count_threshold))
361
        {
362
363
            /* Another block has the same number of obsolete sectors.  Does this new block have a smaller erase
364
               count?  */
365
2024
            if (erase_count < max_obsolete_erase_count)
366
            {
367
368
                /* Yes, erase the block with the smaller erase count.  */
369
506
                max_obsolete_sectors =      obsolete_sectors;
370
506
                max_obsolete_block =        i;
371
506
                max_obsolete_erase_count =  erase_count;
372
506
                max_obsolete_mapped_count = mapped_sectors;
373
506
                max_obsolete_mapped_count_available = mapped_sectors_available;
374
            }
375
        }
376
377
        /* Determine if we have a new minimum erase count.  */
378
9280
        if (erase_count < min_block_erase_count)
379
        {
380
381
            /* Update the new minimum erase count and related information.  */
382
2566
            min_block_erase_count =     erase_count;
383
2566
            min_block_erase =           i;
384
2566
            min_block_obsolete_count =  obsolete_sectors;
385
2566
            min_block_mapped_count =    mapped_sectors;
386
2566
            min_block_mapped_count_available = mapped_sectors_available;
387
        }
388
389
        /* Move to the next block.  */
390
9280
        block_word_ptr =  block_word_ptr + nor_flash -> lx_nor_flash_words_per_block;
391
    }
392
393
    /* Determine if we found a block with full obsoleted sector and the erase count is minimum.  */
394
1160
    if (i == nor_flash -> lx_nor_flash_total_blocks)
395
    {
396
397
        /* Determine if we can erase the block with the most obsolete sectors.  */
398
1160
        if (max_obsolete_sectors)
399
        {
400
401
            /* Erase the block with the most obsolete sectors.  */
402
1160
            *return_erase_block =       max_obsolete_block;
403
1160
            *return_erase_count =       max_obsolete_erase_count;
404
1160
            *return_obsolete_sectors =  max_obsolete_sectors;
405
1160
            *return_mapped_sectors =    max_obsolete_mapped_count;
406
1160
            mapped_sectors_available =  max_obsolete_mapped_count_available;
407
        }
408
        else
409
        {
410
411
            /* Otherwise, choose the block with the smallest erase count.  */
412
            *return_erase_block =       min_block_erase;
413
            *return_erase_count =       min_block_erase_count;
414
            *return_obsolete_sectors =  min_block_obsolete_count;
415
            *return_mapped_sectors =    min_block_mapped_count;
416
            mapped_sectors_available =  min_block_mapped_count_available;
417
        }
418
419
        /* Update the overall minimum and maximum erase count.  */
420
1160
        nor_flash -> lx_nor_flash_minimum_erase_count =  min_system_block_erase_count;
421
1160
        nor_flash -> lx_nor_flash_minimum_erased_blocks =  system_min_erased_blocks;
422
1160
        nor_flash -> lx_nor_flash_maximum_erase_count =  max_system_block_erase_count;
423
    }
424
425
    /* Determine if the mapped sector count is available.  */
426
1160
    if (mapped_sectors_available == LX_FALSE)
427
    {
428
429
        /* Compute the number of obsolete and mapped sectors for this block.  */
430
        mapped_sectors =  0;
431
432
        /* Setup a pointer to the mapped list.  */
433
        block_word_ptr =  nor_flash -> lx_nor_flash_base_address + *return_erase_block * nor_flash -> lx_nor_flash_words_per_block;
434
        list_word_ptr =  block_word_ptr + nor_flash -> lx_nor_flash_block_physical_sector_mapping_offset;
435
436
        /* Loop through the mapped list for this block.  */
437
        for (j = 0; j < nor_flash -> lx_nor_flash_physical_sectors_per_block; j++)
438
        {
439
440
            /* Read the current mapping entry.  */
441
#ifdef LX_DIRECT_READ
442
443
            /* Read the word directly.  */
444
            list_word =  *(list_word_ptr);
445
#else
446
            status =  _lx_nor_flash_driver_read(nor_flash, list_word_ptr, &list_word, 1);
447
448
            /* Check for an error from flash driver. Drivers should never return an error..  */
449
            if (status)
450
            {
451
452
                /* Call system error handler.  */
453
                _lx_nor_flash_system_error(nor_flash, status);
454
455
                /* Return the error.  */
456
                return(status);
457
            }
458
#endif
459
460
            /* Determine if the entry hasn't been used.  */
461
            if (list_word == LX_NOR_PHYSICAL_SECTOR_FREE)
462
            {
463
464
                /* Since allocations are done sequentially in the block, we know nothing
465
                       else exists after this point.  */
466
                break;
467
            }
468
469
            /* Is this entry mapped?  */
470
            if ((list_word & LX_NOR_PHYSICAL_SECTOR_VALID) != 0)
471
            {
472
473
                /* Increment the number of mapped sectors.  */
474
                mapped_sectors++;
475
            }
476
477
            /* Move the list pointer ahead.  */
478
            list_word_ptr++;
479
        }
480
481
        /* Return the mapped sector count.  */
482
        *return_mapped_sectors = mapped_sectors;
483
484
    }
485
    /* Return success.  */
486
1160
    return(LX_SUCCESS);
487
}
488