Skip to content

Q/OSPIFBlockDevice: fix misconception in minimum program size #13848

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 5 commits into from
Nov 17, 2020

Conversation

LDong-Arm
Copy link
Contributor

@LDong-Arm LDong-Arm commented Nov 3, 2020

Summary of changes

Fixes: #13795

Prior to this PR, the minimum program size (QSPI_MIN_PROG_SIZE) of QSPIFBlockDevice and OSPIFBlockDevice were 256 by default and 512 for some targets. Those values were in fact page sizes, not program/write sizes. Consequently the value returned by QSPIFBlockDevice::get_program_size() is the large page size, and taking out a whole page to just write a few bytes is highly inefficient.

Here's an explanation of the concepts:

  • Most flashes can be programmed to a granularity of 1-byte or 4-byte (1-word) - no need to be a whole page. (In fact the early version of QSPIFBlockDevice assumed 1-byte to be always supported.) This should be the value of QSPI_MIN_PROG_SIZE. Applications need to align buffer sizes to this granularity when programming QSPI flash.
  • Each sending of the underlying QSPI/OSPI program signal requires destination bytes to be located within the same page. If a ::program() call crosses page boundaries, the function breaks down the operation into multiple chunks, so it's not a concern for the application.

So this PR changes the default program size to 1 (byte), and overrides it to 4 for some targets.

Minimum program size/granularity for all supported QSPI/OSPI flash model (for targets with QSPIF in targets.json):

  • MX25R6435F: 1-byte, tested on DISCO_L475VG_IOT01A
  • MCU_NRF52840: 4-byte (one word), tested on NRF52840_DK
  • N25Q128A: 1-byte, tested on DISCO_F746NG
  • All MX25L*G series: 1-byte, see programming guide
  • MT25QL512: 1-byte, see datasheet

    If the number of bytes sent to the device is less than the maximum page size, they are correctly programmed at the specified addresses without any effect on the other bytes of the same page.

  • MCU_PSOC6: 1-byte - it can be inferred from the fact that Cy_SMIF_MemWrite() handles any number of bytes (incl. bytes not aligned to pages or anything) @ARMmbed/team-cypress Could you confirm this is the write size of the flash? Update: Tested by @kyle-cypress
  • EFM32GG11_STK3701: 4-byte, as its qspi_write() enforces word-size access (note: its read size is also 4-byte as per existing configuration) @ARMmbed/team-silabs Could you confirm this is the write size of the flash?
  • MCU_LPC546XX: 4-byte, as its qspi_write() enforces word-sized access @ARMmbed/team-nxp Could you confirm this is the write size of the flash?
  • MX25LM51245G (OSPI): 1-byte, see section 2 of its datasheet.

Notes:

  • No config is needed for the page size, as it comes from the SFDP table parsed during initialisation.
  • The same issue may exist for OSPIFlashBlockDevice too, but I didn't change it without having a target to test. Change made for OSPIFBlockDevice too.

Impact of changes

QSPIFBlockDevice::get_program_size() and OSPIFBlockDevice::get_program_size() return the correct value (1 or 4 normally) instead of the 256 or 512.

Migration actions required

None.

Documentation

None.


Pull request type

[x] Patch update (Bug fix / Target update / Docs update / Test update / Refactor)
[] Feature update (New feature / Functionality change / New API)
[] Major update (Breaking change E.g. Return code change / API behaviour change)

Test results

[] No Tests required for this change (E.g docs only update)
[x] Covered by existing mbed-os tests (Greentea or Unittest)
[] Tests / results supplied as part of this PR

Note: This change is tested by general_block_device.test_program_read_small_data_sizes - it programs 1-7 bytes using BufferedBlockDevice, which in turn uses the minimum program size of QSPIFBlockDevice to program (1 or 4 bytes).


Reviewers

@ARMmbed/mbed-os-core @VeijoPesonen @SeppoTakalo


@LDong-Arm
Copy link
Contributor Author

LDong-Arm commented Nov 3, 2020

Maybe the program granularity can be parsed from SFDP. Closing this PR until I've figured it out.
Update: It seems SFDP doesn't require write granularity to be smaller than one page...
From the SFDP specification (requires registration to view):

New system implementations should refer to 6.4.14 for the buffer (page) size. The legacy minimum write granularity is a single byte within any size programming buffer.

Not sure what to do about this. Maybe we do have to set the program size configuration in a chip-by-chip basis, but it's hard to check without having all the boards at hands. Or, keep the program size equal to the page size (as it is), which is the safest but far from optimal...

@LDong-Arm LDong-Arm closed this Nov 3, 2020
@LDong-Arm
Copy link
Contributor Author

Reopening this PR for discussion.

@LDong-Arm LDong-Arm reopened this Nov 3, 2020
@ciarmcom ciarmcom added the release-type: patch Indentifies a PR as containing just a patch label Nov 3, 2020
@ciarmcom ciarmcom requested a review from a team November 3, 2020 17:27
@ciarmcom
Copy link
Member

ciarmcom commented Nov 3, 2020

@LDong-Arm, thank you for your changes.
@ARMmbed/mbed-os-maintainers please review.

@LDong-Arm LDong-Arm changed the title QSPIFBlockDevice: fix misconception in minimum program size WIP: QSPIFBlockDevice: fix misconception in minimum program size Nov 3, 2020
@mergify mergify bot added the do not merge label Nov 3, 2020
@0xc0170 0xc0170 requested a review from a team November 4, 2020 08:40
@LDong-Arm LDong-Arm force-pushed the qspif_program_size_fix branch from ff6e39d to bfae577 Compare November 4, 2020 10:27
@LDong-Arm LDong-Arm changed the title WIP: QSPIFBlockDevice: fix misconception in minimum program size QSPIFBlockDevice: fix misconception in minimum program size Nov 4, 2020
@LDong-Arm
Copy link
Contributor Author

This is ready for review. I've listed QSPI flash models and their program sizes based on existing materials, but haven't tried some of them on physical boards. Is it enough, or shall we ask silicon vendors to confirm them, or ask engineers with those boards to try them out?

@kyle-cypress
Copy link

I ran the QSPI HAL, BlockDevice, and KVStore tests on CY8CKIT-062-WIFI-BT and CY8CPROTO-062-4343W.
On CY8CKIT-062-WIFI-BT all passed.
CY8CPROTO-062-4343W reported a failure in storage-blockdevice-tests-tests-blockdevice-general_block_device. The immediate cause is that "QSPIF Testing contiguous erase, write and read" is timing out:

[1604631936.81][CONN][INF] found KV pair in stream: {{__testcase_start;QSPIF Testing contiguous erase, write and read}}, queued...
[1604631936.85][CONN][RXD] Test Contiguous Erase/Program/Read Starts..
[1604631936.88][CONN][RXD] program_size=1
[1604631936.90][CONN][RXD] block_device->size()=67108864
[1604631936.92][CONN][RXD] start_address=0x7c0000
[1604631936.96][CONN][RXD] stop_address=0x840000
[1604631936.98][CONN][RXD] contiguous_erase_size=10
[1604631937.02][CONN][RXD] erasing memory, from 0x7c0000 of size 0x80000
<Lengthy wait, at least a minute>
[1604632068.82][CONN][RXD] erasing memory, from 0x7c0000 of size 0x80000
<another lengthy wait>
[1604632195.98][HTST][INF] test suite run finished after 300.92 sec...
[1604632195.99][CONN][INF] received special event '__host_test_finished' value='True', finishing
[1604632196.03][HTST][INF] CONN exited with code: 0
[1604632196.03][HTST][INF] No events in queue
[1604632196.03][HTST][INF] stopped consuming events
[1604632196.03][HTST][INF] host test result(): None
[1604632196.03][HTST][WRN] missing __exit event from DUT
[1604632196.04][HTST][WRN] missing __exit_event_queue event from host test
[1604632196.04][HTST][ERR] missing __exit_event_queue event from host test and no result from host test, timeout...
[1604632196.04][HTST][INF] calling blocking teardown()
[1604632196.04][HTST][INF] teardown() finished
[1604632196.04][HTST][INF] {{result;timeout}}

I'm not sure if this is a true failure in the sense of the test hanging, or if the smaller program size is just slowing down the test so much that it hits the timeout.

@LDong-Arm
Copy link
Contributor Author

LDong-Arm commented Nov 6, 2020

@kyle-cypress Thanks a lot for trying it out. I think it's highly possible the test is way too slow, because it now programs one by at a time on that target.
To improve this, the test should program in large chunks, as long as chunk size % program size is 0. I'll update the test and see if it works.

From your log,

[1604631936.92][CONN][RXD] start_address=0x7c0000
[1604631936.96][CONN][RXD] stop_address=0x840000

start_address and stop_address are two sectors apart, so it looks like flash has a huge 256KB sector size.

Did the test pass on that target, without this PR?

@LDong-Arm
Copy link
Contributor Author

@pan- FYI, this is the PR I mentioned.

@evedon
Copy link
Contributor

evedon commented Nov 6, 2020

Notes:

  • The same issue may exist for OSPIFlashBlockDevice too, but I didn't change it without having a target to test.

Only DISCO_L4R9I in targets.json has support for OSPIF : https://github.com/ARMmbed/mbed-os/blob/master/targets/targets.json/#L3559

This target has 2 x OctoSPI memory interface. OSPI_MIN_PROG_SIZE is set to 256: https://github.com/ARMmbed/mbed-os/blob/master/storage/blockdevice/COMPONENT_OSPIF/mbed_lib.json/#L26
@ARMmbed/team-st-mcd Could you confirm this is the write size of the flash?

@LDong-Arm
Copy link
Contributor Author

LDong-Arm commented Nov 6, 2020

This target has 2 x OctoSPI memory interface. OSPI_MIN_PROG_SIZE is set to 256: https://github.com/ARMmbed/mbed-os/blob/master/storage/blockdevice/COMPONENT_OSPIF/mbed_lib.json/#L26
@ARMmbed/team-st-mcd Could you confirm this is the write size of the flash?

From the datasheet of the OctoSPI flash, section 2:

After program/erase command is issued, auto program/erase algorithms which program/erase and verify the
specified page or sector/block locations will be executed. Program command is executed on byte basis, or page (256
bytes) basis, or word basis. Erase command is executed on sector (4K-byte), or block (64K-byte), or whole chip
basis.

So I think the write size should be one byte (i.e. the minimum granularity that applications can program). I can do the change for OSPIFBlockDevice too, but then it'd be good if someone from @ARMmbed/team-st-mcd tries that on the target.

@jeromecoutant
Copy link
Collaborator

@rogeryou Maybe you could comment?

@LDong-Arm LDong-Arm changed the title QSPIFBlockDevice: fix misconception in minimum program size Q/OSPIFBlockDevice: fix misconception in minimum program size Nov 6, 2020
@LDong-Arm
Copy link
Contributor Author

@rogeryou Maybe you could comment?

I've added the change to OSPIFBlockDevice, so an easy way to check is to give it a try

@kyle-cypress
Copy link

@LDong-Arm

From your log,

[1604631936.92][CONN][RXD] start_address=0x7c0000
[1604631936.96][CONN][RXD] stop_address=0x840000

start_address and stop_address are two sectors apart, so it looks like flash has a huge 256KB sector

Indeed - printed at the beginning of the test:

[1604678326.13][CONN][RXD] read size: 1bytes (1bytes)
[1604678326.16][CONN][RXD] program size: 1bytes (1bytes)
[1604678326.20][CONN][RXD] erase size: 256kbytes (262144bytes)
[1604678326.24][CONN][RXD] total size: 64Mbytes (67108864bytes)

Did the test pass on that target, without this PR?

Yes, after I saw the failure on the PR branch I checked out master and confirmed that the test passes there (in significantly less time)

[1604678649.00][CONN][RXD] Test Contiguous Erase/Program/Read Starts..
[1604678649.02][CONN][RXD] program_size=512
[1604678649.05][CONN][RXD] block_device->size()=67108864
[1604678649.07][CONN][RXD] start_address=0x7c0000
[1604678649.09][CONN][RXD] stop_address=0x840000
[1604678649.12][CONN][RXD] contiguous_erase_size=10
[1604678649.17][CONN][RXD] erasing memory, from 0x7c0000 of size 0x80000
[1604678652.08][CONN][RXD] erasing memory, from 0x7c0000 of size 0x80000
[1604678655.84][CONN][INF] found KV pair in stream: {{__testcase_finish;QSPIF Testing contiguous erase, write and read;1;0}}, queued...
[1604678655.91][CONN][RXD] >>> 'QSPIF Testing contiguous erase, write and read': 1 passed, 0 failed

@LDong-Arm
Copy link
Contributor Author

LDong-Arm commented Nov 6, 2020

@kyle-cypress I've just optimized test_contiguous_erase_write_read() to use 256-byte chunks for read/write if possible. This is way larger than the 8 * 1-byte chunks (which timed out), but less than the 8 * 256-byte chunks (without this PR) - I was trying to find a balance that looks "generic".

Could you fetch my last update and try the test again? Thanks.

@LDong-Arm LDong-Arm force-pushed the qspif_program_size_fix branch from d8861aa to d7222fa Compare November 6, 2020 17:08
// (which should be a power of 2) is greater than that. If it's less than
// that, the test finishes quickly anyway...
if ((program_size < 256) && (256 % program_size == 0)
&& (contiguous_erase_size >= 256) && (contiguous_erase_size % 256 == 0)) {
Copy link
Contributor Author

@LDong-Arm LDong-Arm Nov 6, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Checks of (256 % program_size == 0) and (contiguous_erase_size % 256 == 0) shouldn't be necessary (if the other two conditions are satisfied), because sector and program sizes are always powers of 2. We're just trying to be rigorous...

@LDong-Arm LDong-Arm force-pushed the qspif_program_size_fix branch from d7222fa to d26398b Compare November 6, 2020 17:22
@kyle-cypress
Copy link

@kyle-cypress I've just optimized test_contiguous_erase_write_read() to use 256-byte chunks for read/write if possible. This is way larger than the 8 * 1-byte chunks (which timed out), but less than the 8 * 256-byte chunks (without this PR) - I was trying to find a balance that looks "generic".

Could you fetch my last update and try the test again? Thanks.

Thanks, that fixed the failure. Reran on CY8CKIT-062-WIFI-BT and confirmed things are still passing there as well.

evedon
evedon previously approved these changes Nov 9, 2020
Copy link
Contributor

@evedon evedon left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good.

@0xc0170
Copy link
Contributor

0xc0170 commented Nov 12, 2020

I've checked one log for disco, it contains a failure in DISCO_L475VG_IOT01A-ARMC6 | DISCO_L475VG_IOT01A | storage-kvstore-tests-tests-kvstore-general_tests_phase . Please review

Prior to this PR, the minimum program size (QSPI_MIN_PROG_SIZE) of
QSPIFBlockDevice was 256 by default and 512 for some targets.
Those values were in fact page sizes, not program sizes.

Here's an explanation:
* Most QSPI flashes can be programmed to a granularity of a
  single byte or a few bytes - no need to be a whole page.
  This should be the value of QSPI_MIN_PROG_SIZE. Applications
  need to align buffer sizes to this granularity when
  programming QSPI flashes.
* Each sending of the underlying QSPI program signal requires
  destination bytes to be located within the same page.
  If a QSPIFBlockDevice::program() call crosses page boundaries,
  this function breaks down the operation into multiple chunks,
  so it's not a concern for the application.

So this PR changes the default program size to 1 (byte), and
for targets with a 4-byte (1-word) read size it overrides the
program size.

Note: No config is needed for the page size, as it comes from
the SFDP table parsed during initialisation.
From the datasheet of the only OctaSPI flash we currently support
(http://www.mt-system.ru/sites/default/files/docs/Macronix/mx25lm51245g_3v_512mb_v0.01.pdf):

After program/erase command is issued, auto program/erase
algorithms which program/erase and verify the specified page
or sector/block locations will be executed. Program command is
executed on byte basis, or page (256 bytes) basis, or word basis.
Erase command is executed on sector (4K-byte), or block (64K-byte),
or whole chip basis.

So the minimum write size is one byte.
@LDong-Arm LDong-Arm force-pushed the qspif_program_size_fix branch from d26398b to 68a816c Compare November 12, 2020 16:40
@mergify mergify bot dismissed stale reviews from evedon and 0xc0170 November 12, 2020 16:40

Pull request has been modified.

@LDong-Arm
Copy link
Contributor Author

I've checked one log for disco, it contains a failure in DISCO_L475VG_IOT01A-ARMC6 | DISCO_L475VG_IOT01A | storage-kvstore-tests-tests-kvstore-general_tests_phase . Please review

Thanks, I just fixed it in the last commit. What happened was, after we fixed the QSPIFBlockDevice's write size, the test doesn't allocate enough space for SecureStore to run set_add_data_set_key_value_five_Kbytes (which requires > 5KB). I added a minimum value to the calculation of required size.

@LDong-Arm LDong-Arm requested review from 0xc0170 and evedon November 12, 2020 16:44
…e enough

The test case set_add_data_set_key_value_five_Kbytes stores
5KB of value, and to allow for overheads, we allocate at least
8KB per area of TDBStore (so 16KB for two areas of a whole
TDBStore).
@LDong-Arm LDong-Arm force-pushed the qspif_program_size_fix branch from 68a816c to 13c5b64 Compare November 12, 2020 16:46
@mergify mergify bot added needs: CI and removed needs: work labels Nov 16, 2020
@0xc0170
Copy link
Contributor

0xc0170 commented Nov 17, 2020

CI started

@mbed-ci
Copy link

mbed-ci commented Nov 17, 2020

Jenkins CI Test : ❌ FAILED

Build Number: 2 | 🔒 Jenkins CI Job | 🌐 Logs & Artifacts

CLICK for Detailed Summary

jobs Status
jenkins-ci/mbed-os-ci_unittests ✔️
jenkins-ci/mbed-os-ci_cmake-example-GCC_ARM ✔️
jenkins-ci/mbed-os-ci_cmake-example-ARM ✔️
jenkins-ci/mbed-os-ci_build-greentea-ARM ✔️
jenkins-ci/mbed-os-ci_build-greentea-GCC_ARM ✔️
jenkins-ci/mbed-os-ci_build-example-ARM ✔️
jenkins-ci/mbed-os-ci_build-cloud-example-ARM ✔️
jenkins-ci/mbed-os-ci_build-cloud-example-GCC_ARM ✔️
jenkins-ci/mbed-os-ci_build-example-GCC_ARM ✔️
jenkins-ci/mbed-os-ci_dynamic-memory-usage ✔️
jenkins-ci/mbed-os-ci_cmake-example-test ✔️
jenkins-ci/mbed-os-ci_greentea-test
jenkins-ci/mbed-os-ci_cloud-client-pytest ✔️

@0xc0170
Copy link
Contributor

0xc0170 commented Nov 17, 2020

Ci restarted (K64 failed to sync)

@mbed-ci
Copy link

mbed-ci commented Nov 17, 2020

Jenkins CI Test : ❌ FAILED

Build Number: 3 | 🔒 Jenkins CI Job | 🌐 Logs & Artifacts

CLICK for Detailed Summary

jobs Status
jenkins-ci/mbed-os-ci_unittests ✔️
jenkins-ci/mbed-os-ci_cmake-example-GCC_ARM ✔️
jenkins-ci/mbed-os-ci_cmake-example-ARM ✔️
jenkins-ci/mbed-os-ci_build-greentea-GCC_ARM ✔️
jenkins-ci/mbed-os-ci_build-example-ARM ✔️
jenkins-ci/mbed-os-ci_build-cloud-example-ARM ✔️
jenkins-ci/mbed-os-ci_build-cloud-example-GCC_ARM ✔️
jenkins-ci/mbed-os-ci_build-example-GCC_ARM ✔️
jenkins-ci/mbed-os-ci_build-greentea-ARM ✔️
jenkins-ci/mbed-os-ci_dynamic-memory-usage ✔️
jenkins-ci/mbed-os-ci_cmake-example-test ✔️
jenkins-ci/mbed-os-ci_cloud-client-pytest ✔️
jenkins-ci/mbed-os-ci_greentea-test

@mbed-ci
Copy link

mbed-ci commented Nov 17, 2020

Jenkins CI Test : ✔️ SUCCESS

Build Number: 4 | 🔒 Jenkins CI Job | 🌐 Logs & Artifacts

CLICK for Detailed Summary

jobs Status
jenkins-ci/mbed-os-ci_unittests ✔️
jenkins-ci/mbed-os-ci_cmake-example-GCC_ARM ✔️
jenkins-ci/mbed-os-ci_cmake-example-ARM ✔️
jenkins-ci/mbed-os-ci_build-greentea-ARM ✔️
jenkins-ci/mbed-os-ci_build-greentea-GCC_ARM ✔️
jenkins-ci/mbed-os-ci_build-example-ARM ✔️
jenkins-ci/mbed-os-ci_build-cloud-example-ARM ✔️
jenkins-ci/mbed-os-ci_build-cloud-example-GCC_ARM ✔️
jenkins-ci/mbed-os-ci_build-example-GCC_ARM ✔️
jenkins-ci/mbed-os-ci_greentea-test ✔️
jenkins-ci/mbed-os-ci_cmake-example-test ✔️
jenkins-ci/mbed-os-ci_dynamic-memory-usage ✔️
jenkins-ci/mbed-os-ci_cloud-client-pytest ✔️

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Incorrect return value of QSPIFBlockDevice::get_program_size()
8 participants