Skip to content

Commit 61e4b55

Browse files
authored
Merge pull request #13947 from LDong-Arm/erase_algorithm_fix
Fix erase type determination for [Q/O/]BlockDevice::erase()
2 parents c9ff692 + c41f7cb commit 61e4b55

File tree

7 files changed

+195
-69
lines changed

7 files changed

+195
-69
lines changed

drivers/include/drivers/internal/SFDP.h

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -141,11 +141,11 @@ int sfdp_find_addr_region(bd_addr_t offset, const sfdp_hdr_info &sfdp_info);
141141
* @param region Region number
142142
* @param smptbl Information about different erase types
143143
*
144-
* @return Largest erase type
144+
* @return Largest erase type, or -1 if none matches the given address and size
145145
*/
146-
int sfdp_iterate_next_largest_erase_type(uint8_t &bitfield,
147-
int size,
148-
int offset,
146+
int sfdp_iterate_next_largest_erase_type(uint8_t bitfield,
147+
bd_size_t size,
148+
bd_addr_t offset,
149149
int region,
150150
const sfdp_smptbl_info &smptbl);
151151

drivers/source/SFDP.cpp

Lines changed: 15 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -393,34 +393,31 @@ int sfdp_find_addr_region(bd_addr_t offset, const sfdp_hdr_info &sfdp_info)
393393

394394
}
395395

396-
int sfdp_iterate_next_largest_erase_type(uint8_t &bitfield,
397-
int size,
398-
int offset,
396+
int sfdp_iterate_next_largest_erase_type(uint8_t bitfield,
397+
bd_size_t size,
398+
bd_addr_t offset,
399399
int region,
400400
const sfdp_smptbl_info &smptbl)
401401
{
402402
uint8_t type_mask = SFDP_ERASE_BITMASK_TYPE4;
403-
int largest_erase_type = 0;
404-
405-
int idx;
406-
for (idx = 3; idx >= 0; idx--) {
403+
unsigned int erase_size;
404+
for (int idx = 3; idx >= 0; idx--) {
407405
if (bitfield & type_mask) {
408-
largest_erase_type = idx;
409-
if ((size > (int)(smptbl.erase_type_size_arr[largest_erase_type])) &&
410-
((smptbl.region_high_boundary[region] - offset)
411-
> (uint64_t)(smptbl.erase_type_size_arr[largest_erase_type]))) {
412-
break;
413-
} else {
414-
bitfield &= ~type_mask;
406+
erase_size = smptbl.erase_type_size_arr[idx];
407+
// Criteria:
408+
// * offset is aligned to the type's erase size
409+
// * erase size is no larger than the requested size,
410+
// * erase range does not exceed the region boundary
411+
if ((offset % erase_size == 0) && (size >= erase_size) &&
412+
(offset + erase_size - 1 <= smptbl.region_high_boundary[region])) {
413+
return idx;
415414
}
416415
}
417416
type_mask = type_mask >> 1;
418417
}
419418

420-
if (idx == -1) {
421-
tr_error("No erase type was found for current region addr");
422-
}
423-
return largest_erase_type;
419+
tr_error("No erase type was found for current region addr");
420+
return -1;
424421
}
425422

426423
int sfdp_detect_device_density(uint8_t *bptbl_ptr, sfdp_bptbl_info &bptbl_info)
Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
/* Copyright (c) 2020 ARM Limited
2+
* SPDX-License-Identifier: Apache-2.0
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
#include "gtest/gtest.h"
18+
#include "gmock/gmock.h"
19+
#include "drivers/internal/SFDP.h"
20+
21+
class TestSFDP : public testing::Test {
22+
protected:
23+
struct mbed::sfdp_smptbl_info smptbl;
24+
25+
/**
26+
* Construct Mbed OS SFDP info.
27+
* Normally this is parsed from the flash-chips's
28+
* raw SFDP table bytes, but for unit test we construct
29+
* SFDP info manually
30+
*/
31+
virtual void SetUp()
32+
{
33+
// The mock flash supports 4KB, 32KB and 64KB erase types
34+
smptbl.erase_type_size_arr[0] = 4 * 1024;
35+
smptbl.erase_type_size_arr[1] = 32 * 1024;
36+
smptbl.erase_type_size_arr[2] = 64 * 1024;
37+
38+
// The mock flash has three regions, with address ranges:
39+
// * 0 to 64KB - 1B
40+
// * 64KB to 256KB - 1B
41+
// * 256KB to 1024KB - 1B
42+
smptbl.region_high_boundary[0] = 64 * 1024 - 1;
43+
smptbl.region_high_boundary[1] = 256 * 1024 - 1;
44+
smptbl.region_high_boundary[2] = 1024 * 1024 - 1;
45+
46+
// Bitfields indicating which regions support which erase types
47+
smptbl.region_erase_types_bitfld[0] = 0b0001; // 4KB only
48+
smptbl.region_erase_types_bitfld[1] = 0b0111; // 64KB, 32KB, 4KB
49+
smptbl.region_erase_types_bitfld[2] = 0b0110; // 64KB, 32KB
50+
}
51+
};
52+
53+
/**
54+
* Test if sfdp_iterate_next_largest_erase_type() returns the most
55+
* optimal erase type, whose erase size is as large as possible
56+
* while being compatible with both alignment and size requirements.
57+
*/
58+
TEST_F(TestSFDP, TestEraseTypeAlgorithm)
59+
{
60+
// Erase 104KB starting at 92KB
61+
int address = 92 * 1024;
62+
int size = 104 * 1024;
63+
int region = 1;
64+
int type;
65+
66+
// Expected outcome:
67+
// * The starting position 92KB is 4KB-aligned
68+
// * The next position 96KB (92KB + 4KB) is 32KB-aligned
69+
// * The next position 128KB (96KB + 32KB) is 64KB-aligned
70+
// * At the final position 192KB (128KB + 64KB), we only
71+
// have 4KB (104KB - 4KB - 32KB - 64KB) remaining to erase
72+
int expected_erase_KBs[] = {4, 32, 64, 4};
73+
74+
for (int i = 0; i < sizeof(expected_erase_KBs) / sizeof(int); i++) {
75+
type = sfdp_iterate_next_largest_erase_type(
76+
smptbl.region_erase_types_bitfld[region],
77+
size,
78+
address,
79+
region,
80+
smptbl);
81+
int erase_size = smptbl.erase_type_size_arr[type];
82+
EXPECT_EQ(erase_size, expected_erase_KBs[i] * 1024);
83+
address += erase_size;
84+
size -= erase_size;
85+
}
86+
87+
EXPECT_EQ(size, 0); // All erased
88+
89+
// Test unaligned erase
90+
// Region 2 only allows at least 32KB-aligned erases
91+
address = (512 + 16) * 1024;
92+
size = 64 * 1024;
93+
region = 2;
94+
type = sfdp_iterate_next_largest_erase_type(
95+
smptbl.region_erase_types_bitfld[region],
96+
size,
97+
address,
98+
region,
99+
smptbl);
100+
EXPECT_EQ(type, -1); // Invalid erase
101+
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
2+
####################
3+
# UNIT TESTS
4+
####################
5+
set(TEST_SUITE_NAME "SFDP")
6+
7+
# Source files
8+
set(unittest-sources
9+
../drivers/source/SFDP.cpp
10+
)
11+
12+
# Test files
13+
set(unittest-test-sources
14+
../drivers/tests/UNITTESTS/SFDP/test_sfdp.cpp
15+
stubs/mbed_assert_stub.cpp
16+
)
17+
18+
set(unittest-test-flags
19+
-DDEVICE_SPI
20+
)

storage/blockdevice/COMPONENT_OSPIF/source/OSPIFBlockDevice.cpp

Lines changed: 19 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
#include "mbed_trace.h"
3232
#define TRACE_GROUP "OSPIF"
3333

34+
using namespace std::chrono;
3435
using namespace mbed;
3536

3637
/* Default OSPIF Parameters */
@@ -409,7 +410,7 @@ int OSPIFBlockDevice::program(const void *buffer, bd_addr_t addr, bd_size_t size
409410
uint32_t chunk = 0;
410411
bd_size_t written_bytes = 0;
411412

412-
tr_debug("Program - Buff: 0x%lxh, addr: %llu, size: %llu", (uint32_t)buffer, addr, size);
413+
tr_debug("Program - Buff: %p, addr: %llu, size: %llu", buffer, addr, size);
413414

414415
while (size > 0) {
415416
// Write on _page_size_bytes boundaries (Default 256 bytes a page)
@@ -457,28 +458,25 @@ int OSPIFBlockDevice::program(const void *buffer, bd_addr_t addr, bd_size_t size
457458
return status;
458459
}
459460

460-
int OSPIFBlockDevice::erase(bd_addr_t addr, bd_size_t in_size)
461+
int OSPIFBlockDevice::erase(bd_addr_t addr, bd_size_t size)
461462
{
462463
int type = 0;
463-
uint32_t offset = 0;
464-
uint32_t chunk = 4096;
465464
ospi_inst_t cur_erase_inst = OSPI_NO_INST;
466-
int size = (int)in_size;
467465
bool erase_failed = false;
468466
int status = OSPIF_BD_ERROR_OK;
469467
// Find region of erased address
470468
int region = sfdp_find_addr_region(addr, _sfdp_info);
471469
// Erase Types of selected region
472470
uint8_t bitfield = _sfdp_info.smptbl.region_erase_types_bitfld[region];
473471

474-
tr_debug("Erase - addr: %llu, in_size: %llu", addr, in_size);
472+
tr_debug("Erase - addr: %llu, size: %llu", addr, size);
475473

476-
if ((addr + in_size) > _sfdp_info.bptbl.device_size_bytes) {
474+
if ((addr + size) > _sfdp_info.bptbl.device_size_bytes) {
477475
tr_error("Erase exceeds flash device size");
478476
return OSPIF_BD_ERROR_INVALID_ERASE_PARAMS;
479477
}
480478

481-
if (((addr % get_erase_size(addr)) != 0) || (((addr + in_size) % get_erase_size(addr + in_size - 1)) != 0)) {
479+
if (((addr % get_erase_size(addr)) != 0) || (((addr + size) % get_erase_size(addr + size - 1)) != 0)) {
482480
tr_error("Invalid erase - unaligned address and size");
483481
return OSPIF_BD_ERROR_INVALID_ERASE_PARAMS;
484482
}
@@ -500,11 +498,16 @@ int OSPIFBlockDevice::erase(bd_addr_t addr, bd_size_t in_size)
500498
cur_erase_inst = _sfdp_info.bptbl.legacy_erase_instruction;
501499
eu_size = OSPIF_DEFAULT_SE_SIZE;
502500
}
503-
offset = addr % eu_size;
504-
chunk = ((offset + size) < eu_size) ? size : (eu_size - offset);
505501

506-
tr_debug("Erase - addr: %llu, size:%d, Inst: 0x%xh, chunk: %lu ",
507-
addr, size, cur_erase_inst, chunk);
502+
if (addr % eu_size != 0 || addr + size < eu_size) {
503+
// Should not happen if the erase table parsing
504+
// and alignment checks were performed correctly
505+
tr_error("internal error: address %llu not aligned to erase size %u (type %d)",
506+
addr, eu_size, type);
507+
}
508+
509+
tr_debug("Erase - addr: %llu, size:%llu, Inst: 0x%xh, erase size: %u ",
510+
addr, size, cur_erase_inst, eu_size);
508511
tr_debug("Erase - Region: %d, Type:%d ",
509512
region, type);
510513

@@ -524,8 +527,8 @@ int OSPIFBlockDevice::erase(bd_addr_t addr, bd_size_t in_size)
524527
goto exit_point;
525528
}
526529

527-
addr += chunk;
528-
size -= chunk;
530+
addr += eu_size;
531+
size -= eu_size;
529532

530533
if ((size > 0) && (addr > _sfdp_info.smptbl.region_high_boundary[region])) {
531534
// erase crossed to next region
@@ -1517,7 +1520,7 @@ bool OSPIFBlockDevice::_is_mem_ready()
15171520
bool mem_ready = true;
15181521

15191522
do {
1520-
rtos::ThisThread::sleep_for(1);
1523+
rtos::ThisThread::sleep_for(1ms);
15211524
retries++;
15221525
//Read Status Register 1 from device, the number of read byte need to be even in octa flash DOPI mode
15231526
if (OSPI_STATUS_OK != _ospi_send_general_command(OSPIF_INST_RSR1, OSPI_NO_ADDRESS_COMMAND,
@@ -1664,7 +1667,7 @@ ospi_status_t OSPIFBlockDevice::_ospi_send_general_command(ospi_inst_t instructi
16641667
(instruction == OSPIF_INST_RDCR2) || (instruction == OSPIF_INST_RDCR)) {
16651668
_ospi.configure_format(_inst_width, _inst_size, _address_width, _address_size, OSPI_CFG_BUS_SINGLE, 0, _data_width, _dummy_cycles);
16661669
addr = 0;
1667-
} else if ((instruction == OSPIF_INST_WSR1)) {
1670+
} else if (instruction == OSPIF_INST_WSR1) {
16681671
addr = 0;
16691672
}
16701673
}

storage/blockdevice/COMPONENT_QSPIF/source/QSPIFBlockDevice.cpp

Lines changed: 17 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -338,7 +338,7 @@ int QSPIFBlockDevice::program(const void *buffer, bd_addr_t addr, bd_size_t size
338338
uint32_t chunk = 0;
339339
bd_size_t written_bytes = 0;
340340

341-
tr_debug("Program - Buff: 0x%lxh, addr: %llu, size: %llu", (uint32_t)buffer, addr, size);
341+
tr_debug("Program - Buff: %p, addr: %llu, size: %llu", buffer, addr, size);
342342

343343
while (size > 0) {
344344
// Write on _page_size_bytes boundaries (Default 256 bytes a page)
@@ -385,28 +385,25 @@ int QSPIFBlockDevice::program(const void *buffer, bd_addr_t addr, bd_size_t size
385385
return status;
386386
}
387387

388-
int QSPIFBlockDevice::erase(bd_addr_t addr, bd_size_t in_size)
388+
int QSPIFBlockDevice::erase(bd_addr_t addr, bd_size_t size)
389389
{
390390
int type = 0;
391-
uint32_t offset = 0;
392-
uint32_t chunk = 4096;
393391
qspi_inst_t cur_erase_inst = QSPI_NO_INST;
394-
int size = (int)in_size;
395392
bool erase_failed = false;
396393
int status = QSPIF_BD_ERROR_OK;
397394
// Find region of erased address
398395
int region = sfdp_find_addr_region(addr, _sfdp_info);
399396
// Erase Types of selected region
400397
uint8_t bitfield = _sfdp_info.smptbl.region_erase_types_bitfld[region];
401398

402-
tr_debug("Erase - addr: %llu, in_size: %llu", addr, in_size);
399+
tr_debug("Erase - addr: %llu, size: %llu", addr, size);
403400

404-
if ((addr + in_size) > _sfdp_info.bptbl.device_size_bytes) {
401+
if ((addr + size) > _sfdp_info.bptbl.device_size_bytes) {
405402
tr_error("Erase exceeds flash device size");
406403
return QSPIF_BD_ERROR_INVALID_ERASE_PARAMS;
407404
}
408405

409-
if (((addr % get_erase_size(addr)) != 0) || (((addr + in_size) % get_erase_size(addr + in_size - 1)) != 0)) {
406+
if (((addr % get_erase_size(addr)) != 0) || (((addr + size) % get_erase_size(addr + size - 1)) != 0)) {
410407
tr_error("Invalid erase - unaligned address and size");
411408
return QSPIF_BD_ERROR_INVALID_ERASE_PARAMS;
412409
}
@@ -417,7 +414,7 @@ int QSPIFBlockDevice::erase(bd_addr_t addr, bd_size_t in_size)
417414
if (_sfdp_info.bptbl.legacy_erase_instruction == QSPI_NO_INST) {
418415
// Iterate to find next largest erase type that is a) supported by region, and b) smaller than size.
419416
// Find the matching instruction and erase size chunk for that type.
420-
type = sfdp_iterate_next_largest_erase_type(bitfield, size, (int)addr,
417+
type = sfdp_iterate_next_largest_erase_type(bitfield, size, addr,
421418
region,
422419
_sfdp_info.smptbl);
423420
cur_erase_inst = _sfdp_info.smptbl.erase_type_inst_arr[type];
@@ -427,11 +424,16 @@ int QSPIFBlockDevice::erase(bd_addr_t addr, bd_size_t in_size)
427424
cur_erase_inst = _sfdp_info.bptbl.legacy_erase_instruction;
428425
eu_size = QSPIF_DEFAULT_SE_SIZE;
429426
}
430-
offset = addr % eu_size;
431-
chunk = ((offset + size) < eu_size) ? size : (eu_size - offset);
432427

433-
tr_debug("Erase - addr: %llu, size:%d, Inst: 0x%xh, chunk: %lu ",
434-
addr, size, cur_erase_inst, chunk);
428+
if (addr % eu_size != 0 || addr + size < eu_size) {
429+
// Should not happen if the erase table parsing
430+
// and alignment checks were performed correctly
431+
tr_error("internal error: address %llu not aligned to erase size %u (type %d)",
432+
addr, eu_size, type);
433+
}
434+
435+
tr_debug("Erase - addr: %llu, size:%llu, Inst: 0x%xh, erase size: %u",
436+
addr, size, cur_erase_inst, eu_size);
435437
tr_debug("Erase - Region: %d, Type:%d ",
436438
region, type);
437439

@@ -451,8 +453,8 @@ int QSPIFBlockDevice::erase(bd_addr_t addr, bd_size_t in_size)
451453
goto exit_point;
452454
}
453455

454-
addr += chunk;
455-
size -= chunk;
456+
addr += eu_size;
457+
size -= eu_size;
456458

457459
if ((size > 0) && (addr > _sfdp_info.smptbl.region_high_boundary[region])) {
458460
// erase crossed to next region

0 commit comments

Comments
 (0)