GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: lx_nor_flash_next_block_to_erase_find.c Lines: 83 120 69.2 %
Date: 2026-03-06 18:45:40 Branches: 42 66 63.6 %

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

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

9280
        if ((obsolete_sectors > max_obsolete_sectors) && (erase_count <= erase_count_threshold))
335
        {
336
337
            /* Update the new maximum obsolete sectors and related information.  */
338
1726
            max_obsolete_sectors =      obsolete_sectors;
339
1726
            max_obsolete_block =        i;
340
1726
            max_obsolete_erase_count =  erase_count;
341
1726
            max_obsolete_mapped_count = mapped_sectors;
342
1726
            max_obsolete_mapped_count_available = mapped_sectors_available;
343
344
        }
345

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