From 6f4ea0561f0ad9153b1ffd3b4f82ad9e3975969d Mon Sep 17 00:00:00 2001 From: PProvost Date: Mon, 11 May 2020 08:49:43 -0600 Subject: [PATCH] Initial commit --- .gitattributes | 40 + .gitignore | 14 + CMakeLists.txt | 28 + LICENSE.txt | 246 +++++ LICENSED-HARDWARE.txt | 16 + README.md | 1 + TODO | 3 + common/CMakeLists.txt | 63 ++ common/inc/lx_api.h | 530 ++++++++++ common/src/fx_nand_flash_simulated_driver.c | 441 ++++++++ common/src/fx_nor_flash_simulator_driver.c | 460 +++++++++ common/src/lx_nand_flash_256byte_ecc_check.c | 182 ++++ .../src/lx_nand_flash_256byte_ecc_compute.c | 229 +++++ common/src/lx_nand_flash_block_full_update.c | 180 ++++ .../src/lx_nand_flash_block_obsoleted_check.c | 369 +++++++ common/src/lx_nand_flash_block_reclaim.c | 631 ++++++++++++ common/src/lx_nand_flash_close.c | 129 +++ common/src/lx_nand_flash_defragment.c | 112 ++ common/src/lx_nand_flash_driver_block_erase.c | 137 +++ ...lx_nand_flash_driver_block_erased_verify.c | 93 ++ .../lx_nand_flash_driver_block_status_get.c | 132 +++ .../lx_nand_flash_driver_block_status_set.c | 103 ++ .../lx_nand_flash_driver_extra_bytes_get.c | 148 +++ .../lx_nand_flash_driver_extra_bytes_set.c | 113 ++ .../lx_nand_flash_driver_page_erased_verify.c | 92 ++ common/src/lx_nand_flash_driver_read.c | 180 ++++ common/src/lx_nand_flash_driver_write.c | 127 +++ .../src/lx_nand_flash_extended_cache_enable.c | 205 ++++ common/src/lx_nand_flash_initialize.c | 84 ++ .../src/lx_nand_flash_logical_sector_find.c | 837 +++++++++++++++ .../lx_nand_flash_next_block_to_erase_find.c | 301 ++++++ common/src/lx_nand_flash_open.c | 972 ++++++++++++++++++ common/src/lx_nand_flash_page_ecc_check.c | 130 +++ common/src/lx_nand_flash_page_ecc_compute.c | 104 ++ common/src/lx_nand_flash_partial_defragment.c | 123 +++ .../lx_nand_flash_physical_page_allocate.c | 392 +++++++ ...nd_flash_sector_mapping_cache_invalidate.c | 150 +++ common/src/lx_nand_flash_sector_read.c | 173 ++++ common/src/lx_nand_flash_sector_release.c | 268 +++++ common/src/lx_nand_flash_sector_write.c | 458 +++++++++ common/src/lx_nand_flash_simulator.c | 398 +++++++ common/src/lx_nand_flash_system_error.c | 107 ++ common/src/lx_nor_flash_block_reclaim.c | 562 ++++++++++ common/src/lx_nor_flash_close.c | 129 +++ common/src/lx_nor_flash_defragment.c | 112 ++ common/src/lx_nor_flash_driver_block_erase.c | 123 +++ common/src/lx_nor_flash_driver_read.c | 207 ++++ common/src/lx_nor_flash_driver_write.c | 141 +++ .../src/lx_nor_flash_extended_cache_enable.c | 147 +++ common/src/lx_nor_flash_initialize.c | 84 ++ common/src/lx_nor_flash_logical_sector_find.c | 475 +++++++++ .../lx_nor_flash_next_block_to_erase_find.c | 291 ++++++ common/src/lx_nor_flash_open.c | 872 ++++++++++++++++ common/src/lx_nor_flash_partial_defragment.c | 123 +++ .../lx_nor_flash_physical_sector_allocate.c | 337 ++++++ ...or_flash_sector_mapping_cache_invalidate.c | 134 +++ common/src/lx_nor_flash_sector_read.c | 202 ++++ common/src/lx_nor_flash_sector_release.c | 211 ++++ common/src/lx_nor_flash_sector_write.c | 352 +++++++ common/src/lx_nor_flash_simulator.c | 208 ++++ common/src/lx_nor_flash_system_error.c | 93 ++ docs/LevelX_User_Guide.docx | Bin 0 -> 110172 bytes docs/LevelX_User_Guide.pdf | Bin 0 -> 488308 bytes samples/demo_filex_nand_flash.c | 236 +++++ samples/demo_filex_nor_flash.c | 235 +++++ 65 files changed, 14775 insertions(+) create mode 100644 .gitattributes create mode 100644 .gitignore create mode 100644 CMakeLists.txt create mode 100644 LICENSE.txt create mode 100644 LICENSED-HARDWARE.txt create mode 100644 README.md create mode 100644 TODO create mode 100755 common/CMakeLists.txt create mode 100644 common/inc/lx_api.h create mode 100644 common/src/fx_nand_flash_simulated_driver.c create mode 100644 common/src/fx_nor_flash_simulator_driver.c create mode 100644 common/src/lx_nand_flash_256byte_ecc_check.c create mode 100644 common/src/lx_nand_flash_256byte_ecc_compute.c create mode 100644 common/src/lx_nand_flash_block_full_update.c create mode 100644 common/src/lx_nand_flash_block_obsoleted_check.c create mode 100644 common/src/lx_nand_flash_block_reclaim.c create mode 100644 common/src/lx_nand_flash_close.c create mode 100644 common/src/lx_nand_flash_defragment.c create mode 100644 common/src/lx_nand_flash_driver_block_erase.c create mode 100644 common/src/lx_nand_flash_driver_block_erased_verify.c create mode 100644 common/src/lx_nand_flash_driver_block_status_get.c create mode 100644 common/src/lx_nand_flash_driver_block_status_set.c create mode 100644 common/src/lx_nand_flash_driver_extra_bytes_get.c create mode 100644 common/src/lx_nand_flash_driver_extra_bytes_set.c create mode 100644 common/src/lx_nand_flash_driver_page_erased_verify.c create mode 100644 common/src/lx_nand_flash_driver_read.c create mode 100644 common/src/lx_nand_flash_driver_write.c create mode 100644 common/src/lx_nand_flash_extended_cache_enable.c create mode 100644 common/src/lx_nand_flash_initialize.c create mode 100644 common/src/lx_nand_flash_logical_sector_find.c create mode 100644 common/src/lx_nand_flash_next_block_to_erase_find.c create mode 100644 common/src/lx_nand_flash_open.c create mode 100644 common/src/lx_nand_flash_page_ecc_check.c create mode 100644 common/src/lx_nand_flash_page_ecc_compute.c create mode 100644 common/src/lx_nand_flash_partial_defragment.c create mode 100644 common/src/lx_nand_flash_physical_page_allocate.c create mode 100644 common/src/lx_nand_flash_sector_mapping_cache_invalidate.c create mode 100644 common/src/lx_nand_flash_sector_read.c create mode 100644 common/src/lx_nand_flash_sector_release.c create mode 100644 common/src/lx_nand_flash_sector_write.c create mode 100644 common/src/lx_nand_flash_simulator.c create mode 100644 common/src/lx_nand_flash_system_error.c create mode 100644 common/src/lx_nor_flash_block_reclaim.c create mode 100644 common/src/lx_nor_flash_close.c create mode 100644 common/src/lx_nor_flash_defragment.c create mode 100644 common/src/lx_nor_flash_driver_block_erase.c create mode 100644 common/src/lx_nor_flash_driver_read.c create mode 100644 common/src/lx_nor_flash_driver_write.c create mode 100644 common/src/lx_nor_flash_extended_cache_enable.c create mode 100644 common/src/lx_nor_flash_initialize.c create mode 100644 common/src/lx_nor_flash_logical_sector_find.c create mode 100644 common/src/lx_nor_flash_next_block_to_erase_find.c create mode 100644 common/src/lx_nor_flash_open.c create mode 100644 common/src/lx_nor_flash_partial_defragment.c create mode 100644 common/src/lx_nor_flash_physical_sector_allocate.c create mode 100644 common/src/lx_nor_flash_sector_mapping_cache_invalidate.c create mode 100644 common/src/lx_nor_flash_sector_read.c create mode 100644 common/src/lx_nor_flash_sector_release.c create mode 100644 common/src/lx_nor_flash_sector_write.c create mode 100644 common/src/lx_nor_flash_simulator.c create mode 100644 common/src/lx_nor_flash_system_error.c create mode 100644 docs/LevelX_User_Guide.docx create mode 100755 docs/LevelX_User_Guide.pdf create mode 100644 samples/demo_filex_nand_flash.c create mode 100644 samples/demo_filex_nor_flash.c diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..ee9315d --- /dev/null +++ b/.gitattributes @@ -0,0 +1,40 @@ +.git* export-ignore +.hooks* export-ignore + +# Custom attribute to mark sources as using our C code style. +[attr]our-c-style whitespace=tab-in-indent eol=lf format.clang-format-6.0 + +# Custom attribute to mark sources as generated. +# Do not perform whitespace checks. Do not format. +[attr]generated whitespace=-tab-in-indent,-indent-with-non-tab -format.clang-format-6.0 + +bootstrap eol=lf +configure eol=lf +*.[1-9] eol=lf +*.bash eol=lf +*.sh eol=lf +*.sh.in eol=lf + +*.bat eol=crlf +*.bat.in eol=crlf +*.sln eol=crlf +*.vcproj eol=crlf + +*.pfx -text +*.png -text +*.png.in -text + +*.c our-c-style +*.cc our-c-style +*.cpp our-c-style +*.cu our-c-style +*.cxx our-c-style +*.h our-c-style +*.hh our-c-style +*.hpp our-c-style +*.hxx our-c-style +*.notcu our-c-style + +*.cmake whitespace=tab-in-indent +*.rst whitespace=tab-in-indent conflict-marker-size=79 +*.txt whitespace=tab-in-indent \ No newline at end of file diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..9c33dc7 --- /dev/null +++ b/.gitignore @@ -0,0 +1,14 @@ +.vscode/ +_deps/ +build/ +CMakeFiles/ +CMakeScripts/ +CMakeLists.txt.user +CMakeCache.txt +Testing +Makefile +cmake_install.cmake +install_manifest.txt +compile_commands.json +CTestTestfile.cmake + diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..111bc45 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,28 @@ +cmake_minimum_required(VERSION 3.0.0 FATAL_ERROR) + +# Set up the project +project(levelx + VERSION 6.0.0 + LANGUAGES C ASM +) + +if(NOT DEFINED THREADX_ARCH) + message(FATAL_ERROR "Error: THREADX_ARCH not defined") +endif() +if(NOT DEFINED THREADX_TOOLCHAIN) + message(FATAL_ERROR "Error: THREADX_TOOLCHAIN not defined") +endif() + + +# Define our target library and an alias for consumers +add_library(${PROJECT_NAME}) +add_library("azrtos::${PROJECT_NAME}" ALIAS ${PROJECT_NAME}) + +# Define any required dependencies between this library and others +target_link_libraries(${PROJECT_NAME} PUBLIC + "azrtos::threadx" +) + +# Pick up the common stuff +add_subdirectory(${CMAKE_CURRENT_LIST_DIR}/common) + diff --git a/LICENSE.txt b/LICENSE.txt new file mode 100644 index 0000000..76974a3 --- /dev/null +++ b/LICENSE.txt @@ -0,0 +1,246 @@ +MICROSOFT SOFTWARE LICENSE TERMS + +MICROSOFT AZURE RTOS + +Shape + +These license terms are an agreement between you and Microsoft Corporation (or +one of its affiliates). They apply to the software named above and any Microsoft +services or software updates (except to the extent such services or updates are +accompanied by new or additional terms, in which case those different terms +apply prospectively and do not alter your or Microsoft’s rights relating to +pre-updated software or services). IF YOU COMPLY WITH THESE LICENSE TERMS, YOU +HAVE THE RIGHTS BELOW. BY USING THE SOFTWARE, YOU ACCEPT THESE TERMS. + +INSTALLATION AND USE RIGHTS. + +General. You may install and use the software and the included Microsoft +applications solely for internal development, testing and evaluation purposes. +Any distribution or production use requires a separate license as set forth in +Section 2. + +Contributions. Microsoft welcomes contributions to this software. In the event +that you make a contribution to this software you will be required to agree to a +Contributor License Agreement (CLA) declaring that you have the right to, and +actually do, grant Microsoft the rights to use your contribution. For details, +visit https://cla.microsoft.com. + +Included Microsoft Applications. The software includes other Microsoft +applications which are governed by the licenses embedded in or made available +with those applications. + +Third Party Components. The software may include third party components with +separate legal notices or governed by other agreements, as may be described +within the software or in the ThirdPartyNotices file(s) accompanying the +software. + +Competitive Benchmarking. If you are a direct competitor, and you access or use +the software for purposes of competitive benchmarking, analysis, or intelligence +gathering, you waive as against Microsoft, its subsidiaries, and its affiliated +companies (including prospectively) any competitive use, access, and +benchmarking test restrictions in the terms governing your software to the +extent your terms of use are, or purport to be, more restrictive than +Microsoft’s terms. If you do not waive any such purported restrictions in the +terms governing your software, you are not allowed to access or use this +software, and will not do so. + +DISTRIBUTION AND PRODUCTION USE. If you have obtained and/or are developing on +microprocessor(s) and/or microcontroller(s) (“hardware”) listed in the file +named “LICENSED-HARDWARE.txt” included in the repository and/or distributed with +the software you have the following rights in and to the software solely when +used in combination with the hardware. In the event hardware is not listed in +the LICENSED-HARDWARE.txt file, you do not have the rights in this Section 2. + +Distribution and Production Use Rights. + +You may use the software in production (e.g. program the modified or unmodified +software to devices you own or control) and distribute (i.e. make available to +third parties) the modified or unmodified binary image produced from this code. + + +You may permit your device distributors or developers to copy and distribute the +binary image as programmed or to be programmed to your devices. + +You may redistribute the unmodified or modified source to your device +distributors or developers. Modifications must be clearly marked. Any +redistribution in source code form must contain this license and any other +licenses that accompany the software. + +Requirements. For any code you distribute, you must: + +when distributed in binary form, except as embedded in a device, include with +such distribution the terms of this agreement; + +when distributed in source code form to distributors or developers of your +devices, include with such distribution the terms of this agreement; and + +indemnify, defend and hold harmless Microsoft from any claims, including +attorneys’ fees, related to the distribution or use of your devices, except to +the extent that any claim is based solely on the unmodified software. + +Restrictions. You may not: + +use or modify the software to create a competing real time operating system +software; + +remove any copyright notices or licenses contained in the software; + +use Microsoft’s trademarks or trade dress in your application in any way that +suggests your device or application comes from or is endorsed by Microsoft; + +transfer individual components, specific libraries, classes, functions or code +fragments of the software separately for purposes unrelated to the software; or + +use or distribute the software in any way that would subject the software or +Microsoft’s intellectual property or technology to any other license terms. + +SCOPE OF LICENSE. The software is licensed, not sold. Microsoft reserves all +other rights. Unless applicable law gives you more rights despite this +limitation, you will not (and have no right to): + +remove, minimize, block, or modify any notices of Microsoft or its suppliers in +the software; + +use the software in any way that is against the law or to create or propagate +malware; or + +share, publish, distribute, or lease the software (except as permitted in +Section 2 above), or provide the software as a stand-alone offering for others +to use. + +DATA. This software may interact with other Microsoft products that collect data +that is transmitted to Microsoft. To learn more about how Microsoft processes +personal data we collect, please see the Microsoft Privacy Statement at +https://go.microsoft.com/fwlink/?LinkId=248681. + +EXPORT RESTRICTIONS. You must comply with all domestic and international export +laws and regulations that apply to the software, which include restrictions on +destinations, end users, and end use. For further information on export +restrictions, visit https://aka.ms/exporting. + +SUPPORT SERVICES. Microsoft is not obligated under this agreement to provide any +support services for the software. Any support provided is “as is”, “with all +faults”, and without warranty of any kind. + +UPDATES. Microsoft may periodically update the software. You may obtain updates +only from Microsoft or Microsoft-authorized sources. Updates may not include or +support all existing software features, services, or peripheral devices. + +TERMINATION. Without prejudice to any other rights, Microsoft may terminate this +agreement if you fail to comply with any of its terms or conditions. In such +event, you must destroy all copies of the software and all of its component +parts. + +ENTIRE AGREEMENT. This agreement, and any other terms Microsoft may provide for +supplements, updates, or third-party applications, is the entire agreement for +the software. To the extent you have entered into a separate agreement with +Microsoft relating specifically to the software, the terms in such agreement +shall control. + +APPLICABLE LAW AND PLACE TO RESOLVE DISPUTES. If you acquired the software in +the United States or Canada, the laws of the state or province where you live +(or, if a business, where your principal place of business is located) govern +the interpretation of this agreement, claims for its breach, and all other +claims (including consumer protection, unfair competition, and tort claims), +regardless of conflict of laws principles. If you acquired the software in any +other country, its laws apply. If U.S. federal jurisdiction exists, you and +Microsoft consent to exclusive jurisdiction and venue in the federal court in +King County, Washington for all disputes heard in court. If not, you and +Microsoft consent to exclusive jurisdiction and venue in the Superior Court of +King County, Washington for all disputes heard in court. + +CONSUMER RIGHTS; REGIONAL VARIATIONS. This agreement describes certain legal +rights. You may have other rights, including consumer rights, under the laws of +your state or country. Separate and apart from your relationship with Microsoft, +you may also have rights with respect to the party from which you acquired the +software. This agreement does not change those other rights if the laws of your +state or country do not permit it to do so. For example, if you acquired the +software in one of the below regions, or mandatory country law applies, then the +following provisions apply to you: + +Australia. You have statutory guarantees under the Australian Consumer Law and +nothing in this agreement is intended to affect those rights. + +Germany and Austria. + +i.Warranty. The properly licensed software will perform substantially as +described in any Microsoft materials that accompany the software. However, +Microsoft gives no contractual guarantee in relation to the licensed software. + +ii.Limitation of Liability. In case of intentional conduct, gross negligence, +claims based on the Product Liability Act, as well as, in case of death or +personal or physical injury, Microsoft is liable according to the statutory law. + + +Subject to the foregoing clause ii., Microsoft will only be liable for slight +negligence if Microsoft is in breach of such material contractual obligations, +the fulfillment of which facilitate the due performance of this agreement, the +breach of which would endanger the purpose of this agreement and the compliance +with which a party may constantly trust in (so-called "cardinal obligations"). +In other cases of slight negligence, Microsoft will not be liable for slight +negligence. + +DISCLAIMER OF WARRANTY. THE SOFTWARE IS LICENSED “AS IS.” YOU BEAR THE RISK OF +USING IT. MICROSOFT GIVES NO EXPRESS WARRANTIES, GUARANTEES, OR CONDITIONS. TO +THE EXTENT PERMITTED UNDER APPLICABLE LAWS, MICROSOFT EXCLUDES ALL IMPLIED +WARRANTIES, INCLUDING MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, AND +NON-INFRINGEMENT. + +LIMITATION ON AND EXCLUSION OF DAMAGES. IF YOU HAVE ANY BASIS FOR RECOVERING +DAMAGES DESPITE THE PRECEDING DISCLAIMER OF WARRANTY, YOU CAN RECOVER FROM +MICROSOFT AND ITS SUPPLIERS ONLY DIRECT DAMAGES UP TO U.S. $5.00. YOU CANNOT +RECOVER ANY OTHER DAMAGES, INCLUDING CONSEQUENTIAL, LOST PROFITS, SPECIAL, +INDIRECT, OR INCIDENTAL DAMAGES. + +This limitation applies to (a) anything related to the software, services, +content (including code) on third party Internet sites, or third party +applications; and (b) claims for breach of contract, warranty, guarantee, or +condition; strict liability, negligence, or other tort; or any other claim; in +each case to the extent permitted by applicable law. + +It also applies even if Microsoft knew or should have known about the +possibility of the damages. The above limitation or exclusion may not apply to +you because your state, province, or country may not allow the exclusion or +limitation of incidental, consequential, or other damages. + + + +Please note: As this software is distributed in Canada, some of the clauses in +this agreement are provided below in French. + +Remarque: Ce logiciel étant distribué au Canada, certaines des clauses dans ce +contrat sont fournies ci-dessous en français. + +EXONÉRATION DE GARANTIE. Le logiciel visé par une licence est offert « tel quel +». Toute utilisation de ce logiciel est à votre seule risque et péril. Microsoft +n’accorde aucune autre garantie expresse. Vous pouvez bénéficier de droits +additionnels en vertu du droit local sur la protection des consommateurs, que ce +contrat ne peut modifier. La ou elles sont permises par le droit locale, les +garanties implicites de qualité marchande, d’adéquation à un usage particulier +et d’absence de contrefaçon sont exclues. + +LIMITATION DES DOMMAGES-INTÉRÊTS ET EXCLUSION DE RESPONSABILITÉ POUR LES +DOMMAGES. Vous pouvez obtenir de Microsoft et de ses fournisseurs une +indemnisation en cas de dommages directs uniquement à hauteur de 5,00 $ US. Vous +ne pouvez prétendre à aucune indemnisation pour les autres dommages, y compris +les dommages spéciaux, indirects ou accessoires et pertes de bénéfices. + +Cette limitation concerne: + +•tout ce qui est relié au logiciel, aux services ou au contenu (y compris le +code) figurant sur des sites Internet tiers ou dans des programmes tiers; et + +•les réclamations au titre de violation de contrat ou de garantie, ou au titre +de responsabilité stricte, de négligence ou d’une autre faute dans la limite +autorisée par la loi en vigueur. + +Elle s’applique également, même si Microsoft connaissait ou devrait connaître +l’éventualité d’un tel dommage. Si votre pays n’autorise pas l’exclusion ou la +limitation de responsabilité pour les dommages indirects, accessoires ou de +quelque nature que ce soit, il se peut que la limitation ou l’exclusion +ci-dessus ne s’appliquera pas à votre égard. + +EFFET JURIDIQUE. Le présent contrat décrit certains droits juridiques. Vous +pourriez avoir d’autres droits prévus par les lois de votre pays. Le présent +contrat ne modifie pas les droits que vous confèrent les lois de votre pays si +celles-ci ne le permettent pas. \ No newline at end of file diff --git a/LICENSED-HARDWARE.txt b/LICENSED-HARDWARE.txt new file mode 100644 index 0000000..77dd1ab --- /dev/null +++ b/LICENSED-HARDWARE.txt @@ -0,0 +1,16 @@ +LICENSED HARDWARE LIST + +Last Updated: 2020-05-08 + +Microsoft has entered into OEM Agreements with manufacturers of the following +microprocessors and microcontrollers (the “hardware”) to enable those +manufacturers to include and distribute Azure RTOS in certain hardware. If you +have obtained and/or are developing on microprocessor(s) and/or +microcontroller(s) (“hardware”) listed below you inherit the “Distribution and +Production Use” rights in Section 2 of the Microsoft Software License Terms for +Microsoft Azure RTOS. If hardware is not listed below, you do not have those +rights. + +-------------------------------------------------------------------------------- + +More coming soon. Please check back frequently for updates. \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..ef87e9c --- /dev/null +++ b/README.md @@ -0,0 +1 @@ +# LevelX \ No newline at end of file diff --git a/TODO b/TODO new file mode 100644 index 0000000..10ea7f7 --- /dev/null +++ b/TODO @@ -0,0 +1,3 @@ +TODO: + + diff --git a/common/CMakeLists.txt b/common/CMakeLists.txt new file mode 100755 index 0000000..a08604e --- /dev/null +++ b/common/CMakeLists.txt @@ -0,0 +1,63 @@ +target_sources(${PROJECT_NAME} PRIVATE + # {{BEGIN_TARGET_SOURCES}} + ${CMAKE_CURRENT_LIST_DIR}/src/fx_nand_flash_simulated_driver.c + ${CMAKE_CURRENT_LIST_DIR}/src/fx_nor_flash_simulator_driver.c + ${CMAKE_CURRENT_LIST_DIR}/src/lx_nand_flash_256byte_ecc_check.c + ${CMAKE_CURRENT_LIST_DIR}/src/lx_nand_flash_256byte_ecc_compute.c + ${CMAKE_CURRENT_LIST_DIR}/src/lx_nand_flash_block_full_update.c + ${CMAKE_CURRENT_LIST_DIR}/src/lx_nand_flash_block_obsoleted_check.c + ${CMAKE_CURRENT_LIST_DIR}/src/lx_nand_flash_block_reclaim.c + ${CMAKE_CURRENT_LIST_DIR}/src/lx_nand_flash_close.c + ${CMAKE_CURRENT_LIST_DIR}/src/lx_nand_flash_defragment.c + ${CMAKE_CURRENT_LIST_DIR}/src/lx_nand_flash_driver_block_erase.c + ${CMAKE_CURRENT_LIST_DIR}/src/lx_nand_flash_driver_block_erased_verify.c + ${CMAKE_CURRENT_LIST_DIR}/src/lx_nand_flash_driver_block_status_get.c + ${CMAKE_CURRENT_LIST_DIR}/src/lx_nand_flash_driver_block_status_set.c + ${CMAKE_CURRENT_LIST_DIR}/src/lx_nand_flash_driver_extra_bytes_get.c + ${CMAKE_CURRENT_LIST_DIR}/src/lx_nand_flash_driver_extra_bytes_set.c + ${CMAKE_CURRENT_LIST_DIR}/src/lx_nand_flash_driver_page_erased_verify.c + ${CMAKE_CURRENT_LIST_DIR}/src/lx_nand_flash_driver_read.c + ${CMAKE_CURRENT_LIST_DIR}/src/lx_nand_flash_driver_write.c + ${CMAKE_CURRENT_LIST_DIR}/src/lx_nand_flash_extended_cache_enable.c + ${CMAKE_CURRENT_LIST_DIR}/src/lx_nand_flash_initialize.c + ${CMAKE_CURRENT_LIST_DIR}/src/lx_nand_flash_logical_sector_find.c + ${CMAKE_CURRENT_LIST_DIR}/src/lx_nand_flash_next_block_to_erase_find.c + ${CMAKE_CURRENT_LIST_DIR}/src/lx_nand_flash_open.c + ${CMAKE_CURRENT_LIST_DIR}/src/lx_nand_flash_page_ecc_check.c + ${CMAKE_CURRENT_LIST_DIR}/src/lx_nand_flash_page_ecc_compute.c + ${CMAKE_CURRENT_LIST_DIR}/src/lx_nand_flash_partial_defragment.c + ${CMAKE_CURRENT_LIST_DIR}/src/lx_nand_flash_physical_page_allocate.c + ${CMAKE_CURRENT_LIST_DIR}/src/lx_nand_flash_sector_mapping_cache_invalidate.c + ${CMAKE_CURRENT_LIST_DIR}/src/lx_nand_flash_sector_read.c + ${CMAKE_CURRENT_LIST_DIR}/src/lx_nand_flash_sector_release.c + ${CMAKE_CURRENT_LIST_DIR}/src/lx_nand_flash_sector_write.c + ${CMAKE_CURRENT_LIST_DIR}/src/lx_nand_flash_simulator.c + ${CMAKE_CURRENT_LIST_DIR}/src/lx_nand_flash_system_error.c + ${CMAKE_CURRENT_LIST_DIR}/src/lx_nor_flash_block_reclaim.c + ${CMAKE_CURRENT_LIST_DIR}/src/lx_nor_flash_close.c + ${CMAKE_CURRENT_LIST_DIR}/src/lx_nor_flash_defragment.c + ${CMAKE_CURRENT_LIST_DIR}/src/lx_nor_flash_driver_block_erase.c + ${CMAKE_CURRENT_LIST_DIR}/src/lx_nor_flash_driver_read.c + ${CMAKE_CURRENT_LIST_DIR}/src/lx_nor_flash_driver_write.c + ${CMAKE_CURRENT_LIST_DIR}/src/lx_nor_flash_extended_cache_enable.c + ${CMAKE_CURRENT_LIST_DIR}/src/lx_nor_flash_initialize.c + ${CMAKE_CURRENT_LIST_DIR}/src/lx_nor_flash_logical_sector_find.c + ${CMAKE_CURRENT_LIST_DIR}/src/lx_nor_flash_next_block_to_erase_find.c + ${CMAKE_CURRENT_LIST_DIR}/src/lx_nor_flash_open.c + ${CMAKE_CURRENT_LIST_DIR}/src/lx_nor_flash_partial_defragment.c + ${CMAKE_CURRENT_LIST_DIR}/src/lx_nor_flash_physical_sector_allocate.c + ${CMAKE_CURRENT_LIST_DIR}/src/lx_nor_flash_sector_mapping_cache_invalidate.c + ${CMAKE_CURRENT_LIST_DIR}/src/lx_nor_flash_sector_read.c + ${CMAKE_CURRENT_LIST_DIR}/src/lx_nor_flash_sector_release.c + ${CMAKE_CURRENT_LIST_DIR}/src/lx_nor_flash_sector_write.c + ${CMAKE_CURRENT_LIST_DIR}/src/lx_nor_flash_simulator.c + ${CMAKE_CURRENT_LIST_DIR}/src/lx_nor_flash_system_error.c + + # {{END_TARGET_SOURCES}} +) + +# Add the Common/inc directory to the project include list +target_include_directories(${PROJECT_NAME} PUBLIC + ${CMAKE_CURRENT_LIST_DIR}/inc +) + diff --git a/common/inc/lx_api.h b/common/inc/lx_api.h new file mode 100644 index 0000000..622790b --- /dev/null +++ b/common/inc/lx_api.h @@ -0,0 +1,530 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** LevelX Component */ +/** */ +/** Application Interface (API) */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/**************************************************************************/ +/* */ +/* APPLICATION INTERFACE DEFINITION RELEASE */ +/* */ +/* lx_api.h PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This file defines the basic Application Interface (API) to the */ +/* high-performance LevelX. All service prototypes and data structure */ +/* definitions are defined in this file. */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 William E. Lamie Initial Version 6.0 */ +/* */ +/**************************************************************************/ + +#ifndef LX_API_H +#define LX_API_H + + +/* Include necessary system files. */ + +#include "tx_api.h" + +/* Disable warning of parameter not used. */ +#ifndef LX_PARAMETER_NOT_USED +#define LX_PARAMETER_NOT_USED(p) ((void)(p)) +#endif /* LX_PARAMETER_NOT_USED */ + + +/* Define general LevelX Constants. */ + +#define LX_FALSE 0 +#define LX_TRUE 1 +#define LX_NULL 0 +#define LX_BLOCK_ERASE_COUNT_MASK 0x7FFFFFFF +#define LX_BLOCK_ERASE_COUNT_MAX 0x7FFFFFFF +#define LX_BLOCK_ERASED 0x80000000 +#define LX_BLOCK_ERASE_STARTED 0 +#define LX_ALL_ONES 0xFFFFFFFF + + +/* Define error codes. */ + +#define LX_SUCCESS 0 +#define LX_ERROR 1 +#define LX_NO_SECTORS 2 +#define LX_SECTOR_NOT_FOUND 3 +#define LX_NO_PAGES 4 +#define LX_INVALID_WRITE 5 +#define LX_NAND_ERROR_CORRECTED 6 +#define LX_NAND_ERROR_NOT_CORRECTED 7 +#define LX_NO_MEMORY 8 +#define LX_DISABLED 9 +#define LX_SYSTEM_INVALID_FORMAT 90 +#define LX_SYSTEM_INVALID_BLOCK 91 +#define LX_SYSTEM_ALLOCATION_FAILED 92 +#define LX_SYSTEM_MUTEX_CREATE_FAILED 93 +#define LX_SYSTEM_INVALID_SECTOR_MAP 94 + + +/* Define NOR flash constants. */ + +#define LX_NOR_FLASH_OPENED ((ULONG) 0x4E4F524F) +#define LX_NOR_FLASH_CLOSED ((ULONG) 0x4E4F5244) +#define LX_NOR_SECTOR_SIZE (512/sizeof(ULONG)) +#define LX_NOR_FLASH_MIN_LOGICAL_SECTOR_OFFSET 1 +#define LX_NOR_FLASH_MAX_LOGICAL_SECTOR_OFFSET 2 +#ifndef LX_NOR_FLASH_MAX_ERASE_COUNT_DELTA +#define LX_NOR_FLASH_MAX_ERASE_COUNT_DELTA 4 +#endif +#define LX_NOR_SECTOR_MAPPING_CACHE_DEPTH 4 +#ifndef LX_NOR_SECTOR_MAPPING_CACHE_SIZE +#define LX_NOR_SECTOR_MAPPING_CACHE_SIZE 16 /* Minimum value of 8, all sizes must be a power of 2. */ +#endif +#ifndef LX_NOR_EXTENDED_CACHE_SIZE +#define LX_NOR_EXTENDED_CACHE_SIZE 8 /* Maximum number of extended cache sectors. */ +#endif + + +/* Define the mask for the hash index into the sector mapping cache table. The sector mapping cache is divided + into 4 entry pieces that are indexed by the formula: + + index = (sector & LX_NOR_SECTOR_MAPPING_CACHE_HASH_MASK) * LX_NOR_SECTOR_MAPPING_CACHE_DEPTH + + The LX_NOR_SECTOR_MAPPING_CACHE_DEPTH define must not be changed unless the related source code is also changed. */ + +#define LX_NOR_SECTOR_MAPPING_CACHE_HASH_MASK ((LX_NOR_SECTOR_MAPPING_CACHE_SIZE/LX_NOR_SECTOR_MAPPING_CACHE_DEPTH)-1) +#define LX_NOR_SECTOR_MAPPING_CACHE_ENTRY_MASK 0x7FFFFFFF +#define LX_NOR_SECTOR_MAPPING_CACHE_ENTRY_VALID 0x80000000 + +#define LX_NOR_PHYSICAL_SECTOR_VALID 0x80000000 +#define LX_NOR_PHYSICAL_SECTOR_SUPERCEDED 0x40000000 +#define LX_NOR_PHYSICAL_SECTOR_MAPPING_NOT_VALID 0x20000000 +#define LX_NOR_LOGICAL_SECTOR_MASK 0x1FFFFFFF +#define LX_NOR_PHYSICAL_SECTOR_FREE 0xFFFFFFFF + + +/* Define NAND flash constants. */ + +#define LX_NAND_GOOD_BLOCK 0xFF +#define LX_NAND_BAD_BLOCK 0x00 +#define LX_NAND_FLASH_OPENED ((ULONG) 0x4E4F524F) +#define LX_NAND_FLASH_CLOSED ((ULONG) 0x4E4F5244) +#ifndef LX_NAND_FLASH_MAX_ERASE_COUNT_DELTA +#define LX_NAND_FLASH_MAX_ERASE_COUNT_DELTA 64 +#endif + +/* Define the NAND sector mapping cache. */ + +#define LX_NAND_SECTOR_MAPPING_CACHE_DEPTH 4 /* Not required if LX_NAND_FLASH_DIRECT_MAPPING_CACHE is defined. */ +#ifndef LX_NAND_SECTOR_MAPPING_CACHE_SIZE +#define LX_NAND_SECTOR_MAPPING_CACHE_SIZE 128 /* Minimum value of 8, all sizes must be a power of 2, unless direct + mapping is defined, in which there is no power of 2 requirement. */ +#endif +#ifndef LX_NAND_ERASE_COUNT_WRITE_SIZE +#define LX_NAND_ERASE_COUNT_WRITE_SIZE (nand_flash -> lx_nand_flash_pages_per_block + 1) +#endif + + +/* Define the mask for the hash index into the NAND sector mapping cache table. The sector mapping cache is divided + into 4 entry pieces that are indexed by the formula: + + index = (sector & LX_NAND_SECTOR_MAPPING_CACHE_HASH_MASK) * LX_NAND_SECTOR_MAPPING_CACHE_DEPTH + + The LX_NAND_SECTOR_MAPPING_CACHE_DEPTH define must not be changed unless the related source code is also changed. */ + +#define LX_NAND_SECTOR_MAPPING_CACHE_HASH_MASK ((LX_NAND_SECTOR_MAPPING_CACHE_SIZE/LX_NAND_SECTOR_MAPPING_CACHE_DEPTH)-1) +#define LX_NAND_SECTOR_MAPPING_CACHE_ENTRY_MASK 0x7FFFFFFF +#define LX_NAND_SECTOR_MAPPING_CACHE_ENTRY_VALID 0x80000000 + +#define LX_NAND_BLOCK_VALID 0x80000000 +#define LX_NAND_BLOCK_EMPTY 0x40000000 +#define LX_NAND_BLOCK_FULL 0x20000000 +#define LX_NAND_PAGE_VALID 0x80000000 +#define LX_NAND_PAGE_SUPERCEDED 0x40000000 +#define LX_NAND_PAGE_MAPPING_NOT_VALID 0x20000000 +#define LX_NAND_LOGICAL_SECTOR_MASK 0x1FFFFFFF +#define LX_NAND_PAGE_FREE 0xFFFFFFFF +#define LX_NAND_PAGE_LIST_VALID 0xF0F0F0F0 + + +/* Define the NAND flash extra byte structure. This will be set in the spare area of each mapped physical page. */ + +typedef struct LX_NAND_PAGE_EXTRA_INFO_STRUCT +{ + ULONG lx_nand_page_extra_info_logical_sector; +} LX_NAND_PAGE_EXTRA_INFO; + + +/* Define the NAND flash logical sector cache entry structure. */ + +typedef struct LX_NAND_SECTOR_MAPPING_CACHE_ENTRY_STRUCT +{ +#ifndef LX_NAND_FLASH_DIRECT_MAPPING_CACHE + ULONG lx_nand_sector_mapping_cache_logical_sector; +#endif + USHORT lx_nand_sector_mapping_cache_block; + USHORT lx_nand_sector_mapping_cache_page; +} LX_NAND_SECTOR_MAPPING_CACHE_ENTRY; + + +/* Define the NAND flash control block structure. */ + +typedef struct LX_NAND_FLASH_STRUCT +{ + ULONG lx_nand_flash_state; + ULONG lx_nand_flash_total_blocks; + ULONG lx_nand_flash_pages_per_block; + ULONG lx_nand_flash_bytes_per_page; + ULONG lx_nand_flash_words_per_block; + ULONG lx_nand_flash_words_per_page; + ULONG lx_nand_flash_total_pages; + + ULONG lx_nand_flash_bad_blocks; + ULONG lx_nand_flash_free_pages; + ULONG lx_nand_flash_mapped_pages; + ULONG lx_nand_flash_obsolete_pages; + ULONG lx_nand_flash_minimum_erase_count; + ULONG lx_nand_flash_maximum_erase_count; + ULONG lx_nand_flash_free_block_search; + ULONG lx_nand_flash_found_block_search; + ULONG lx_nand_flash_found_page_search; + + ULONG lx_nand_flash_max_mapped_sector; + ULONG lx_nand_flash_page_corrections; + ULONG lx_nand_flash_last_block_correction; + ULONG lx_nand_flash_last_page_correction; + + ULONG lx_nand_flash_diagnostic_system_errors; + ULONG lx_nand_flash_diagnostic_system_error; + ULONG lx_nand_flash_diagnostic_sector_write_requests; + ULONG lx_nand_flash_diagnostic_sector_read_requests; + ULONG lx_nand_flash_diagnostic_sector_release_requests; + ULONG lx_nand_flash_diagnostic_page_allocates; + ULONG lx_nand_flash_diagnostic_page_allocate_errors; + + ULONG lx_nand_flash_diagnostic_sector_mapping_cache_hits; + ULONG lx_nand_flash_diagnostic_sector_mapping_cache_misses; + ULONG lx_nand_flash_diagnostic_page_extra_bytes_cache_hits; + ULONG lx_nand_flash_diagnostic_page_extra_bytes_cache_misses; + ULONG lx_nand_flash_diagnostic_page_0_cache_hits; + ULONG lx_nand_flash_diagnostic_page_0_cache_misses; + ULONG lx_nand_flash_diagnostic_block_status_cache_hits; + ULONG lx_nand_flash_diagnostic_block_status_cache_misses; + + ULONG lx_nand_flash_diagnostic_block_reclaim_attempts; + ULONG lx_nand_flash_diagnostic_block_erases; + ULONG lx_nand_flash_diagnostic_block_status_gets; + ULONG lx_nand_flash_diagnostic_block_status_sets; + ULONG lx_nand_flash_diagnostic_page_extra_bytes_sets; + ULONG lx_nand_flash_diagnostic_page_writes; + ULONG lx_nand_flash_diagnostic_page_extra_bytes_gets; + ULONG lx_nand_flash_diagnostic_page_reads; + ULONG lx_nand_flash_diagnostic_moved_pages; + ULONG lx_nand_flash_diagnostic_block_erased_verifies; + ULONG lx_nand_flash_diagnostic_page_erased_verifies; + + ULONG lx_nand_flash_diagnostic_initial_format; + ULONG lx_nand_flash_diagnostic_erased_block; + ULONG lx_nand_flash_diagnostic_re_erase_block; + ULONG lx_nand_flash_diagnostic_page_being_obsoleted; + ULONG lx_nand_flash_diagnostic_page_obsoleted; + ULONG lx_nand_flash_diagnostic_mapping_invalid; + ULONG lx_nand_flash_diagnostic_mapping_write_interrupted; + ULONG lx_nand_flash_diagnostic_page_not_free; + ULONG lx_nand_flash_diagnostic_page_data_not_free; + + UINT (*lx_nand_flash_driver_read)(ULONG block, ULONG page, ULONG *destination, ULONG words); + UINT (*lx_nand_flash_driver_write)(ULONG block, ULONG page, ULONG *source, ULONG words); + UINT (*lx_nand_flash_driver_block_erase)(ULONG block, ULONG erase_count); + UINT (*lx_nand_flash_driver_block_erased_verify)(ULONG block); + UINT (*lx_nand_flash_driver_page_erased_verify)(ULONG block, ULONG page); + UINT (*lx_nand_flash_driver_block_status_get)(ULONG block, UCHAR *bad_block_flag); + UINT (*lx_nand_flash_driver_block_status_set)(ULONG block, UCHAR bad_block_flag); + UINT (*lx_nand_flash_driver_extra_bytes_get)(ULONG block, ULONG page, UCHAR *destination, UINT size); + UINT (*lx_nand_flash_driver_extra_bytes_set)(ULONG block, ULONG page, UCHAR *source, UINT size); + UINT (*lx_nand_flash_driver_system_error)(UINT error_code, ULONG block, ULONG page); + + ULONG *lx_nand_flash_page_buffer; + UINT lx_nand_flash_sector_mapping_cache_enabled; + LX_NAND_SECTOR_MAPPING_CACHE_ENTRY + lx_nand_flash_sector_mapping_cache[LX_NAND_SECTOR_MAPPING_CACHE_SIZE]; + + + /* Define the extended cache structures for block status, page extra bytes, and block page 0 caches. The memory for these cache + extensions is optionally supplied by the application after the lx_nand_flash_open call has been made. */ + + UCHAR *lx_nand_flash_block_status_cache; + LX_NAND_PAGE_EXTRA_INFO *lx_nand_flash_page_extra_bytes_cache; + ULONG *lx_nand_flash_page_0_cache; + +#ifdef LX_THREAD_SAFE_ENABLE + + /* When this conditional is used, the LevelX code utilizes a ThreadX mutex for thread + safe operation. Generally, this is not required since FileX ensures thread safe operation at + a higher layer. */ + TX_MUTEX lx_nand_flash_mutex; +#endif + + /* Define the NAND flash control block open next/previous pointers. */ + struct LX_NAND_FLASH_STRUCT *lx_nand_flash_open_next, + *lx_nand_flash_open_previous; + +} LX_NAND_FLASH; + + + +/* Define the NOR flash sector cache entry structure. */ + +typedef struct LX_NOR_SECTOR_MAPPING_CACHE_ENTRY_STRUCT +{ + ULONG lx_nor_sector_mapping_cache_logical_sector; + ULONG *lx_nor_sector_mapping_cache_physical_sector_map_entry; + ULONG *lx_nor_sector_mapping_cache_physical_sector_address; +} LX_NOR_SECTOR_MAPPING_CACHE_ENTRY; + + +/* Define the NOR flash extended cache entry structure. */ + +typedef struct LX_NOR_FLASH_EXTENDED_CACHE_ENTRY_STRUCT +{ + ULONG *lx_nor_flash_extended_cache_entry_sector_address; + ULONG *lx_nor_flash_extended_cache_entry_sector_memory; + ULONG lx_nor_flash_extended_cache_entry_access_count; +} LX_NOR_FLASH_EXTENDED_CACHE_ENTRY; + + +/* Define the NOR flash control block structure. */ + +typedef struct LX_NOR_FLASH_STRUCT +{ + ULONG lx_nor_flash_state; + ULONG lx_nor_flash_total_blocks; + ULONG lx_nor_flash_words_per_block; + ULONG lx_nor_flash_total_physical_sectors; + ULONG lx_nor_flash_physical_sectors_per_block; + + ULONG *lx_nor_flash_base_address; + ULONG lx_nor_flash_block_free_bit_map_offset; + ULONG lx_nor_flash_block_bit_map_words; + ULONG lx_nor_flash_block_bit_map_mask; + ULONG lx_nor_flash_block_physical_sector_mapping_offset; + ULONG lx_nor_flash_block_physical_sector_offset; + + ULONG lx_nor_flash_free_physical_sectors; + ULONG lx_nor_flash_mapped_physical_sectors; + ULONG lx_nor_flash_obsolete_physical_sectors; + ULONG lx_nor_flash_minimum_erase_count; + ULONG lx_nor_flash_maximum_erase_count; + + ULONG lx_nor_flash_free_block_search; + ULONG lx_nor_flash_found_block_search; + ULONG lx_nor_flash_found_sector_search; + + ULONG lx_nor_flash_write_requests; + ULONG lx_nor_flash_read_requests; + ULONG lx_nor_flash_sector_mapping_cache_hits; + ULONG lx_nor_flash_sector_mapping_cache_misses; + ULONG lx_nor_flash_physical_block_allocates; + ULONG lx_nor_flash_physical_block_allocate_errors; + ULONG lx_nor_flash_diagnostic_system_errors; + ULONG lx_nor_flash_diagnostic_system_error; + ULONG lx_nor_flash_diagnostic_initial_format; + ULONG lx_nor_flash_diagnostic_erased_block; + ULONG lx_nor_flash_diagnostic_re_erase_block; + ULONG lx_nor_flash_diagnostic_sector_being_obsoleted; + ULONG lx_nor_flash_diagnostic_sector_obsoleted; + ULONG lx_nor_flash_diagnostic_mapping_invalidated; + ULONG lx_nor_flash_diagnostic_mapping_write_interrupted; + ULONG lx_nor_flash_diagnostic_sector_not_free; + ULONG lx_nor_flash_diagnostic_sector_data_not_free; + + UINT (*lx_nor_flash_driver_read)(ULONG *flash_address, ULONG *destination, ULONG words); + UINT (*lx_nor_flash_driver_write)(ULONG *flash_address, ULONG *source, ULONG words); + UINT (*lx_nor_flash_driver_block_erase)(ULONG block, ULONG erase_count); + UINT (*lx_nor_flash_driver_block_erased_verify)(ULONG block); + UINT (*lx_nor_flash_driver_system_error)(UINT error_code); + + ULONG *lx_nor_flash_sector_buffer; + UINT lx_nor_flash_sector_mapping_cache_enabled; + LX_NOR_SECTOR_MAPPING_CACHE_ENTRY + lx_nor_flash_sector_mapping_cache[LX_NOR_SECTOR_MAPPING_CACHE_SIZE]; + +#ifndef LX_NOR_DISABLE_EXTENDED_CACHE + + UINT lx_nor_flash_extended_cache_entries; + LX_NOR_FLASH_EXTENDED_CACHE_ENTRY + lx_nor_flash_extended_cache[LX_NOR_EXTENDED_CACHE_SIZE]; + ULONG lx_nor_flash_extended_cache_hits; + ULONG lx_nor_flash_extended_cache_misses; +#endif + +#ifdef LX_THREAD_SAFE_ENABLE + + /* When this conditional is used, the LevelX code utilizes a ThreadX mutex for thread + safe operation. Generally, this is not required since FileX ensures thread safe operation at + a higher layer. */ + TX_MUTEX lx_nor_flash_mutex; +#endif + + /* Define the NOR flash control block open next/previous pointers. */ + struct LX_NOR_FLASH_STRUCT *lx_nor_flash_open_next, + *lx_nor_flash_open_previous; + +} LX_NOR_FLASH; + + +/* Each physical NOR block has the following structure at the beginning of the block: + + Offset Meaning + + 0 Erase count + 4 Minimum logical sector number - only when the block is full + 8 Maximum logical sector number - only when the block is full + 12 Free physical sector bit map, where a value of 1 indicates a + free physical sector. The bit map is evenly divisible by 4 + . Array of physical sector mapping information (4 bytes per entry, + one entry for each physical sector in block). Each entry looks + like the following: + + Bit(s) Meaning + + 0-29 Logical sector mapped if not 0x3FFFFFFF + 30 If 0, entry is being superceded + 31 If 1, entry is valid + + Array of physical sectors, with each of size LX_NOR_SECTOR_SIZE +*/ + + +typedef struct LX_NOR_FLASH_BLOCK_HEADER_STRUCT +{ + ULONG lx_nor_flash_block_erase_count; /* Bit 31: 1 -> not used, 0 -> used */ + ULONG lx_nor_flash_block_min_logical_sector; /* On full block, minimum valid sector */ + ULONG lx_nor_flash_block_max_logical_sector; /* On full block, maximum valid sector */ +} LX_NOR_FLASH_BLOCK_HEADER; + + +/* Define external structure references. */ + +extern LX_NAND_FLASH *_lx_nand_flash_opened_ptr; +extern ULONG _lx_nand_flash_opened_count; +extern LX_NOR_FLASH *_lx_nor_flash_opened_ptr; +extern ULONG _lx_nor_flash_opened_count; + + +/* Map internal functions. */ + +#ifndef LX_SOURCE_CODE +#define lx_nand_flash_close _lx_nand_flash_close +#define lx_nand_flash_defragment _lx_nand_flash_defragment +#define lx_nand_flash_partial_defragment _lx_nand_flash_partial_defragment +#define lx_nand_flash_extended_cache_enable _lx_nand_flash_extended_cache_enable +#define lx_nand_flash_initialize _lx_nand_flash_initialize +#define lx_nand_flash_open _lx_nand_flash_open +#define lx_nand_flash_page_ecc_check _lx_nand_flash_page_ecc_check +#define lx_nand_flash_page_ecc_compute _lx_nand_flash_page_ecc_compute +#define lx_nand_flash_sector_read _lx_nand_flash_sector_read +#define lx_nand_flash_sector_release _lx_nand_flash_sector_release +#define lx_nand_flash_sector_write _lx_nand_flash_sector_write +#define lx_nand_flash_256byte_ecc_check _lx_nand_flash_256byte_ecc_check +#define lx_nand_flash_256byte_ecc_compute _lx_nand_flash_256byte_ecc_compute + +#define lx_nor_flash_close _lx_nor_flash_close +#define lx_nor_flash_defragment _lx_nor_flash_defragment +#define lx_nor_flash_partial_defragment _lx_nor_flash_partial_defragment +#define lx_nor_flash_extended_cache_enable _lx_nor_flash_extended_cache_enable +#define lx_nor_flash_initialize _lx_nor_flash_initialize +#define lx_nor_flash_open _lx_nor_flash_open +#define lx_nor_flash_sector_read _lx_nor_flash_sector_read +#define lx_nor_flash_sector_release _lx_nor_flash_sector_release +#define lx_nor_flash_sector_write _lx_nor_flash_sector_write +#endif + + +/* External LevelX API prototypes. */ + +UINT _lx_nand_flash_close(LX_NAND_FLASH *nand_flash); +UINT _lx_nand_flash_defragment(LX_NAND_FLASH *nand_flash); +UINT _lx_nand_flash_initialize(void); +UINT _lx_nand_flash_extended_cache_enable(LX_NAND_FLASH *nand_flash, VOID *memory, ULONG size); +UINT _lx_nand_flash_open(LX_NAND_FLASH *nand_flash, CHAR *name, UINT (*nand_driver_initialize)(LX_NAND_FLASH *)); +UINT _lx_nand_flash_page_ecc_check(LX_NAND_FLASH *nand_flash, UCHAR *page_buffer, UCHAR *ecc_buffer); +UINT _lx_nand_flash_page_ecc_compute(LX_NAND_FLASH *nand_flash, UCHAR *page_buffer, UCHAR *ecc_buffer); +UINT _lx_nand_flash_partial_defragment(LX_NAND_FLASH *nand_flash, UINT max_blocks); +UINT _lx_nand_flash_sector_read(LX_NAND_FLASH *nand_flash, ULONG logical_sector, VOID *buffer); +UINT _lx_nand_flash_sector_release(LX_NAND_FLASH *nand_flash, ULONG logical_sector); +UINT _lx_nand_flash_sector_write(LX_NAND_FLASH *nand_flash, ULONG logical_sector, VOID *buffer); + +UINT _lx_nor_flash_close(LX_NOR_FLASH *nor_flash); +UINT _lx_nor_flash_defragment(LX_NOR_FLASH *nor_flash); +UINT _lx_nor_flash_extended_cache_enable(LX_NOR_FLASH *nor_flash, VOID *memory, ULONG size); +UINT _lx_nor_flash_initialize(void); +UINT _lx_nor_flash_open(LX_NOR_FLASH *nor_flash, CHAR *name, UINT (*nor_driver_initialize)(LX_NOR_FLASH *)); +UINT _lx_nor_flash_partial_defragment(LX_NOR_FLASH *nor_flash, UINT max_blocks); +UINT _lx_nor_flash_sector_read(LX_NOR_FLASH *nor_flash, ULONG logical_sector, VOID *buffer); +UINT _lx_nor_flash_sector_release(LX_NOR_FLASH *nor_flash, ULONG logical_sector); +UINT _lx_nor_flash_sector_write(LX_NOR_FLASH *nor_flash, ULONG logical_sector, VOID *buffer); + + +/* Internal LevelX prototypes. */ + +UINT _lx_nand_flash_block_full_update(LX_NAND_FLASH *nand_flash, ULONG block, ULONG erase_count); +VOID _lx_nand_flash_block_obsoleted_check(LX_NAND_FLASH *nand_flash, ULONG block); +UINT _lx_nand_flash_block_reclaim(LX_NAND_FLASH *nand_flash); + +UINT _lx_nand_flash_driver_read(LX_NAND_FLASH *nand_flash, ULONG block, ULONG page, ULONG *destination, ULONG words); +UINT _lx_nand_flash_driver_write(LX_NAND_FLASH *nand_flash, ULONG block, ULONG page, ULONG *source, ULONG words); +UINT _lx_nand_flash_driver_block_erase(LX_NAND_FLASH *nand_flash, ULONG block, ULONG erase_count); +UINT _lx_nand_flash_driver_block_erased_verify(LX_NAND_FLASH *nand_flash, ULONG block); +UINT _lx_nand_flash_driver_page_erased_verify(LX_NAND_FLASH *nand_flash, ULONG block, ULONG page); +UINT _lx_nand_flash_driver_block_status_get(LX_NAND_FLASH *nand_flash, ULONG block, UCHAR *bad_block_flag); +UINT _lx_nand_flash_driver_block_status_set(LX_NAND_FLASH *nand_flash, ULONG block, UCHAR bad_block_flag); +UINT _lx_nand_flash_driver_extra_bytes_get(LX_NAND_FLASH *nand_flash, ULONG block, ULONG page, UCHAR *destination, UINT size); +UINT _lx_nand_flash_driver_extra_bytes_set(LX_NAND_FLASH *nand_flash, ULONG block, ULONG page, UCHAR *source, UINT size); + +VOID _lx_nand_flash_internal_error(LX_NAND_FLASH *nand_flash, ULONG error_code); +UINT _lx_nand_flash_logical_sector_find(LX_NAND_FLASH *nand_flash, ULONG logical_sector, ULONG superceded_check, ULONG *block, ULONG *page); +UINT _lx_nand_flash_next_block_to_erase_find(LX_NAND_FLASH *nand_flash, ULONG *return_erase_block, ULONG *return_erase_count, ULONG *return_mapped_pages, ULONG *return_obsolete_pages); +UINT _lx_nand_flash_physical_page_allocate(LX_NAND_FLASH *nand_flash, ULONG *block, ULONG *page, ULONG *erase_count); +VOID _lx_nand_flash_sector_mapping_cache_invalidate(LX_NAND_FLASH *nand_flash, ULONG logical_sector); +VOID _lx_nand_flash_system_error(LX_NAND_FLASH *nand_flash, UINT error_code, ULONG block, ULONG page); +UINT _lx_nand_flash_256byte_ecc_check(UCHAR *page_buffer, UCHAR *ecc_buffer); +UINT _lx_nand_flash_256byte_ecc_compute(UCHAR *page_buffer, UCHAR *ecc_buffer); + +UINT _lx_nor_flash_block_reclaim(LX_NOR_FLASH *nor_flash); +UINT _lx_nor_flash_driver_block_erase(LX_NOR_FLASH *nor_flash, ULONG block, ULONG erase_count); +UINT _lx_nor_flash_driver_read(LX_NOR_FLASH *nor_flash, ULONG *flash_address, ULONG *destination, ULONG words); +UINT _lx_nor_flash_driver_write(LX_NOR_FLASH *nor_flash, ULONG *flash_address, ULONG *source, ULONG words); +VOID _lx_nor_flash_internal_error(LX_NOR_FLASH *nor_flash, ULONG error_code); +UINT _lx_nor_flash_logical_sector_find(LX_NOR_FLASH *nor_flash, ULONG logical_sector, ULONG superceded_check, ULONG **physical_sector_map_entry, ULONG **physical_sector_address); +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); +UINT _lx_nor_flash_physical_sector_allocate(LX_NOR_FLASH *nor_flash, ULONG logical_sector, ULONG **physical_sector_map_entry, ULONG **physical_sector_address); +VOID _lx_nor_flash_sector_mapping_cache_invalidate(LX_NOR_FLASH *nor_flash, ULONG logical_sector); +VOID _lx_nor_flash_system_error(LX_NOR_FLASH *nor_flash, UINT error_code); + +#endif + diff --git a/common/src/fx_nand_flash_simulated_driver.c b/common/src/fx_nand_flash_simulated_driver.c new file mode 100644 index 0000000..21df7ce --- /dev/null +++ b/common/src/fx_nand_flash_simulated_driver.c @@ -0,0 +1,441 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** FileX Component */ +/** */ +/** FileX NAND FLASH Simulator Driver */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#include "tx_api.h" +#include "fx_api.h" +#include "lx_api.h" + + +/* Create a NAND flash control block. */ + +LX_NAND_FLASH nand_flash; + + +/* Define the NAND flash simulation initialization function. */ + +UINT _lx_nand_flash_simulator_initialize(LX_NAND_FLASH *nand_flash); +VOID _fx_nand_flash_read_sectors(ULONG logical_sector, ULONG sectors, UCHAR *destination_buffer); +VOID _fx_nand_flash_write_sectors(ULONG logical_sector, ULONG sectors, UCHAR *source_buffer); + + +/* The simulated NAND driver relies on the fx_media_format call to be made prior to + the fx_media_open call. + + fx_media_format(&ram_disk, + _fx_nand_sim_driver, // Driver entry + FX_NULL, // Unused + media_memory, // Media buffer pointer + sizeof(media_memory), // Media buffer size + "MY_NAND_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 120, // Total sectors + 2048, // Sector size + 1, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + +*/ + + +VOID _fx_nand_flash_simulator_driver(FX_MEDIA *media_ptr); + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _fx_nand_simulator_driver PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is the entry point to the generic NAND simulated */ +/* disk driver that is delivered with the flash wear leveling product */ +/* LevelX. */ +/* */ +/* This driver also serves as a template for developing other LevelX */ +/* NAND flash drivers for actual flash devices. Simply replace the */ +/* read/write sector logic with calls to read/write from the */ +/* appropriate physical device access functions. */ +/* */ +/* FileX NAND FLASH structures look like the following: */ +/* */ +/* Logical Sector Contents */ +/* */ +/* 0 Boot record */ +/* 1 FAT Area Start */ +/* +FAT Sectors Root Directory Start */ +/* +Directory Sectors Data Sector Start */ +/* */ +/* */ +/* INPUT */ +/* */ +/* media_ptr Media control block pointer */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* _lx_nand_flash_close Close NAND flash manager */ +/* _lx_nand_flash_open Open NAND flash manager */ +/* _lx_nand_flash_sector_read Read a NAND sector */ +/* _lx_nand_flash_sector_release Release a NAND sector */ +/* _lx_nand_flash_sector_write Write a NAND sector */ +/* */ +/* CALLED BY */ +/* */ +/* FileX System Functions */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 William E. Lamie Initial Version 6.0 */ +/* */ +/**************************************************************************/ +VOID _fx_nand_flash_simulator_driver(FX_MEDIA *media_ptr) +{ + +ULONG logical_sector; +ULONG count; +UCHAR *buffer; +UINT status; + + + /* There are several useful/important pieces of information contained in the media + structure, some of which are supplied by FileX and others are for the driver to + setup. The following is a summary of the necessary FX_MEDIA structure members: + + FX_MEDIA Member Meaning + + fx_media_driver_request FileX request type. Valid requests from FileX are + as follows: + + FX_DRIVER_READ + FX_DRIVER_WRITE + FX_DRIVER_FLUSH + FX_DRIVER_ABORT + FX_DRIVER_INIT + FX_DRIVER_BOOT_READ + FX_DRIVER_RELEASE_SECTORS + FX_DRIVER_BOOT_WRITE + FX_DRIVER_UNINIT + + fx_media_driver_status This value is RETURNED by the driver. If the + operation is successful, this field should be + set to FX_SUCCESS for before returning. Otherwise, + if an error occurred, this field should be set + to FX_IO_ERROR. + + fx_media_driver_buffer Pointer to buffer to read or write sector data. + This is supplied by FileX. + + fx_media_driver_logical_sector Logical sector FileX is requesting. + + fx_media_driver_sectors Number of sectors FileX is requesting. + + + The following is a summary of the optional FX_MEDIA structure members: + + FX_MEDIA Member Meaning + + fx_media_driver_info Pointer to any additional information or memory. + This is optional for the driver use and is setup + from the fx_media_open call. The RAM disk uses + this pointer for the RAM disk memory itself. + + fx_media_driver_write_protect The DRIVER sets this to FX_TRUE when media is write + protected. This is typically done in initialization, + but can be done anytime. + + fx_media_driver_free_sector_update The DRIVER sets this to FX_TRUE when it needs to + know when clusters are released. This is important + for FLASH wear-leveling drivers. + + fx_media_driver_system_write FileX sets this flag to FX_TRUE if the sector being + written is a system sector, e.g., a boot, FAT, or + directory sector. The driver may choose to use this + to initiate error recovery logic for greater fault + tolerance. + + fx_media_driver_data_sector_read FileX sets this flag to FX_TRUE if the sector(s) being + read are file data sectors, i.e., NOT system sectors. + + fx_media_driver_sector_type FileX sets this variable to the specific type of + sector being read or written. The following sector + types are identified: + + FX_UNKNOWN_SECTOR + FX_BOOT_SECTOR + FX_FAT_SECTOR + FX_DIRECTORY_SECTOR + FX_DATA_SECTOR + */ + + /* Process the driver request specified in the media control block. */ + switch(media_ptr -> fx_media_driver_request) + { + + case FX_DRIVER_READ: + { + + /* Read sector(s) from NAND flash. */ + logical_sector = media_ptr -> fx_media_driver_logical_sector; + count = media_ptr -> fx_media_driver_sectors; + buffer = (UCHAR *) media_ptr -> fx_media_driver_buffer; + while (count) + { + + /* Call LevelX to read one flash sector. */ + status = _lx_nand_flash_sector_read(&nand_flash, logical_sector, buffer); + + /* Determine if the read was successful. */ + if (status != LX_SUCCESS) + { + + /* Return an I/O error to FileX. */ + media_ptr -> fx_media_driver_status = FX_IO_ERROR; + + return; + } + + /* Successful sector read. */ + count--; + logical_sector++; + buffer += media_ptr -> fx_media_bytes_per_sector; + } + /* Successful driver request. */ + media_ptr -> fx_media_driver_status = FX_SUCCESS; + break; + } + + case FX_DRIVER_WRITE: + { + + /* Write sector(s) to NAND flash. */ + logical_sector = media_ptr -> fx_media_driver_logical_sector; + count = media_ptr -> fx_media_driver_sectors; + buffer = (UCHAR *) media_ptr -> fx_media_driver_buffer; + while (count) + { + + /* Call LevelX to write a sector. */ + status = _lx_nand_flash_sector_write(&nand_flash, logical_sector, buffer); + + /* Determine if the write was successful. */ + if (status != LX_SUCCESS) + { + + /* Return an I/O error to FileX. */ + media_ptr -> fx_media_driver_status = FX_IO_ERROR; + + return; + } + + /* Successful sector write. */ + count--; + logical_sector++; + buffer += media_ptr -> fx_media_bytes_per_sector; + } + + /* Successful driver request. */ + media_ptr -> fx_media_driver_status = FX_SUCCESS; + break; + } + + case FX_DRIVER_RELEASE_SECTORS: + { + + /* Release the mapping of this sector. */ + logical_sector = media_ptr -> fx_media_driver_logical_sector; + count = media_ptr -> fx_media_driver_sectors; + while (count) + { + + /* Call LevelX to release a sector mapping. */ + status = _lx_nand_flash_sector_release(&nand_flash, logical_sector); + + /* Determine if the sector release was successful. */ + if (status != LX_SUCCESS) + { + + /* Return an I/O error to FileX. */ + media_ptr -> fx_media_driver_status = FX_IO_ERROR; + + return; + } + + /* Successful sector release. */ + count--; + logical_sector++; + } + + /* Successful driver request. */ + media_ptr -> fx_media_driver_status = FX_SUCCESS; + break; + } + + case FX_DRIVER_FLUSH: + { + + /* Return driver success. */ + media_ptr -> fx_media_driver_status = FX_SUCCESS; + break; + } + + case FX_DRIVER_ABORT: + { + + /* Return driver success. */ + media_ptr -> fx_media_driver_status = FX_SUCCESS; + break; + } + + case FX_DRIVER_INIT: + { + + /* FLASH drivers are responsible for setting several fields in the + media structure, as follows: + + media_ptr -> fx_media_driver_free_sector_update + media_ptr -> fx_media_driver_write_protect + + The fx_media_driver_free_sector_update flag is used to instruct + FileX to inform the driver whenever sectors are not being used. + This is especially useful for FLASH managers so they don't have + maintain mapping for sectors no longer in use. + + The fx_media_driver_write_protect flag can be set anytime by the + driver to indicate the media is not writable. Write attempts made + when this flag is set are returned as errors. */ + + /* Perform basic initialization here... since the boot record is going + to be read subsequently and again for volume name requests. */ + + /* With flash wear leveling, FileX should tell wear leveling when sectors + are no longer in use. */ + media_ptr -> fx_media_driver_free_sector_update = FX_TRUE; + + /* Open the NAND flash simulation. */ + status = _lx_nand_flash_open(&nand_flash, "sim nand flash", _lx_nand_flash_simulator_initialize); + + /* Determine if the flash open was successful. */ + if (status != LX_SUCCESS) + { + + /* Return an I/O error to FileX. */ + media_ptr -> fx_media_driver_status = FX_IO_ERROR; + + return; + } + + /* Successful driver request. */ + media_ptr -> fx_media_driver_status = FX_SUCCESS; + break; + } + + case FX_DRIVER_UNINIT: + { + + /* There is nothing to do in this case for the RAM driver. For actual + devices some shutdown processing may be necessary. */ + + /* Close the NAND flash simulation. */ + status = _lx_nand_flash_close(&nand_flash); + + /* Determine if the flash close was successful. */ + if (status != LX_SUCCESS) + { + + /* Return an I/O error to FileX. */ + media_ptr -> fx_media_driver_status = FX_IO_ERROR; + + return; + } + + /* Successful driver request. */ + media_ptr -> fx_media_driver_status = FX_SUCCESS; + break; + } + + case FX_DRIVER_BOOT_READ: + { + + /* Read the boot record and return to the caller. */ + status = _lx_nand_flash_sector_read(&nand_flash, 0, (UCHAR *) media_ptr -> fx_media_driver_buffer); + + /* Determine if the read was successful. */ + if (status != LX_SUCCESS) + { + + /* Return an I/O error to FileX. */ + media_ptr -> fx_media_driver_status = FX_IO_ERROR; + + return; + } + + /* Successful driver request. */ + media_ptr -> fx_media_driver_status = FX_SUCCESS; + break; + } + + case FX_DRIVER_BOOT_WRITE: + { + + /* Write the boot record and return to the caller. */ + status = _lx_nand_flash_sector_write(&nand_flash, 0, (UCHAR *) media_ptr -> fx_media_driver_buffer); + + /* Determine if the write was successful. */ + if (status != LX_SUCCESS) + { + + /* Return an I/O error to FileX. */ + media_ptr -> fx_media_driver_status = FX_IO_ERROR; + + return; + } + + /* Successful driver request. */ + media_ptr -> fx_media_driver_status = FX_SUCCESS; + break ; + } + + default: + { + + /* Invalid driver request. */ + media_ptr -> fx_media_driver_status = FX_IO_ERROR; + break; + } + } +} + diff --git a/common/src/fx_nor_flash_simulator_driver.c b/common/src/fx_nor_flash_simulator_driver.c new file mode 100644 index 0000000..7658979 --- /dev/null +++ b/common/src/fx_nor_flash_simulator_driver.c @@ -0,0 +1,460 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** FileX Component */ +/** */ +/** FileX NOR FLASH Simulator Driver */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary system files. */ + +#include "tx_api.h" +#include "fx_api.h" +#include "lx_api.h" + + +/* Create a NOR flash control block. */ + +LX_NOR_FLASH nor_flash; + + +/* The simulated NOR driver relies on the fx_media_format call to be made prior to + the fx_media_open call. + + fx_media_format(&ram_disk, + _fx_nor_sim_driver, // Driver entry + FX_NULL, // Unused + media_memory, // Media buffer pointer + sizeof(media_memory), // Media buffer size + "MY_NOR_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 120, // Total sectors + 512, // Sector size + 1, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + +*/ + + +/* Define prototypes. */ + +UINT _lx_nor_flash_simulator_initialize(LX_NOR_FLASH *nor_flash); +VOID _fx_nor_flash_simulator_driver(FX_MEDIA *media_ptr); + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _fx_nor_flash_simulator_driver PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is the entry point to the generic NOR simulated */ +/* disk driver that is delivered with the flash wear leveling product */ +/* LevelX. */ +/* */ +/* This driver also serves as a template for developing other LevelX */ +/* NOR flash drivers for actual flash devices. Simply replace the */ +/* read/write sector logic with calls to read/write from the */ +/* appropriate physical device access functions. */ +/* */ +/* FileX NOR FLASH structures look like the following: */ +/* */ +/* Logical Sector Contents */ +/* */ +/* 0 Boot record */ +/* 1 FAT Area Start */ +/* +FAT Sectors Root Directory Start */ +/* +Directory Sectors Data Sector Start */ +/* */ +/* */ +/* INPUT */ +/* */ +/* media_ptr Media control block pointer */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* lx_nor_flash_close Close NOR flash manager */ +/* lx_nor_flash_open Open NOR flash manager */ +/* lx_nor_flash_sector_read Read a NOR sector */ +/* lx_nor_flash_sector_release Release a NOR sector */ +/* lx_nor_flash_sector_write Write a NOR sector */ +/* */ +/* CALLED BY */ +/* */ +/* FileX System Functions */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 William E. Lamie Initial Version 6.0 */ +/* */ +/**************************************************************************/ +VOID _fx_nor_flash_simulator_driver(FX_MEDIA *media_ptr) +{ + +UCHAR *source_buffer; +UCHAR *destination_buffer; +ULONG logical_sector; +ULONG i; +UINT status; + + + /* There are several useful/important pieces of information contained in the media + structure, some of which are supplied by FileX and others are for the driver to + setup. The following is a summary of the necessary FX_MEDIA structure members: + + FX_MEDIA Member Meaning + + fx_media_driver_request FileX request type. Valid requests from FileX are + as follows: + + FX_DRIVER_READ + FX_DRIVER_WRITE + FX_DRIVER_FLUSH + FX_DRIVER_ABORT + FX_DRIVER_INIT + FX_DRIVER_BOOT_READ + FX_DRIVER_RELEASE_SECTORS + FX_DRIVER_BOOT_WRITE + FX_DRIVER_UNINIT + + fx_media_driver_status This value is RETURNED by the driver. If the + operation is successful, this field should be + set to FX_SUCCESS for before returning. Otherwise, + if an error occurred, this field should be set + to FX_IO_ERROR. + + fx_media_driver_buffer Pointer to buffer to read or write sector data. + This is supplied by FileX. + + fx_media_driver_logical_sector Logical sector FileX is requesting. + + fx_media_driver_sectors Number of sectors FileX is requesting. + + + The following is a summary of the optional FX_MEDIA structure members: + + FX_MEDIA Member Meaning + + fx_media_driver_info Pointer to any additional information or memory. + This is optional for the driver use and is setup + from the fx_media_open call. The RAM disk uses + this pointer for the RAM disk memory itself. + + fx_media_driver_write_protect The DRIVER sets this to FX_TRUE when media is write + protected. This is typically done in initialization, + but can be done anytime. + + fx_media_driver_free_sector_update The DRIVER sets this to FX_TRUE when it needs to + know when clusters are released. This is important + for FLASH wear-leveling drivers. + + fx_media_driver_system_write FileX sets this flag to FX_TRUE if the sector being + written is a system sector, e.g., a boot, FAT, or + directory sector. The driver may choose to use this + to initiate error recovery logic for greater fault + tolerance. + + fx_media_driver_data_sector_read FileX sets this flag to FX_TRUE if the sector(s) being + read are file data sectors, i.e., NOT system sectors. + + fx_media_driver_sector_type FileX sets this variable to the specific type of + sector being read or written. The following sector + types are identified: + + FX_UNKNOWN_SECTOR + FX_BOOT_SECTOR + FX_FAT_SECTOR + FX_DIRECTORY_SECTOR + FX_DATA_SECTOR + */ + + /* Process the driver request specified in the media control block. */ + switch(media_ptr -> fx_media_driver_request) + { + + case FX_DRIVER_READ: + { + + /* Setup the destination buffer and logical sector. */ + logical_sector = media_ptr -> fx_media_driver_logical_sector; + destination_buffer = (UCHAR *) media_ptr -> fx_media_driver_buffer; + + /* Loop to read sectors from flash. */ + for (i = 0; i < media_ptr -> fx_media_driver_sectors; i++) + { + + /* Read a sector from NOR flash. */ + status = lx_nor_flash_sector_read(&nor_flash, logical_sector, destination_buffer); + + /* Determine if the read was successful. */ + if (status != LX_SUCCESS) + { + + /* Return an I/O error to FileX. */ + media_ptr -> fx_media_driver_status = FX_IO_ERROR; + + return; + } + + /* Move to the next entries. */ + logical_sector++; + destination_buffer = destination_buffer + 512; + } + + /* Successful driver request. */ + media_ptr -> fx_media_driver_status = FX_SUCCESS; + break; + } + + case FX_DRIVER_WRITE: + { + + /* Setup the source buffer and logical sector. */ + logical_sector = media_ptr -> fx_media_driver_logical_sector; + source_buffer = (UCHAR *) media_ptr -> fx_media_driver_buffer; + + /* Loop to write sectors to flash. */ + for (i = 0; i < media_ptr -> fx_media_driver_sectors; i++) + { + + /* Write a sector to NOR flash. */ + status = lx_nor_flash_sector_write(&nor_flash, logical_sector, source_buffer); + + /* Determine if the write was successful. */ + if (status != LX_SUCCESS) + { + + /* Return an I/O error to FileX. */ + media_ptr -> fx_media_driver_status = FX_IO_ERROR; + + return; + } + + /* Move to the next entries. */ + logical_sector++; + source_buffer = source_buffer + 512; + } + + /* Successful driver request. */ + media_ptr -> fx_media_driver_status = FX_SUCCESS; + break; + } + + case FX_DRIVER_RELEASE_SECTORS: + { + + /* Setup the logical sector. */ + logical_sector = media_ptr -> fx_media_driver_logical_sector; + + /* Release sectors. */ + for (i = 0; i < media_ptr -> fx_media_driver_sectors; i++) + { + + /* Release NOR flash sector. */ + status = lx_nor_flash_sector_release(&nor_flash, logical_sector); + + /* Determine if the sector release was successful. */ + if (status != LX_SUCCESS) + { + + /* Return an I/O error to FileX. */ + media_ptr -> fx_media_driver_status = FX_IO_ERROR; + + return; + } + + /* Move to the next entries. */ + logical_sector++; + } + + /* Successful driver request. */ + media_ptr -> fx_media_driver_status = FX_SUCCESS; + break; + } + + case FX_DRIVER_FLUSH: + { + + /* Return driver success. */ + media_ptr -> fx_media_driver_status = FX_SUCCESS; + break; + } + + case FX_DRIVER_ABORT: + { + + /* Return driver success. */ + media_ptr -> fx_media_driver_status = FX_SUCCESS; + break; + } + + case FX_DRIVER_INIT: + { + + /* FLASH drivers are responsible for setting several fields in the + media structure, as follows: + + media_ptr -> fx_media_driver_free_sector_update + media_ptr -> fx_media_driver_write_protect + + The fx_media_driver_free_sector_update flag is used to instruct + FileX to inform the driver whenever sectors are not being used. + This is especially useful for FLASH managers so they don't have + maintain mapping for sectors no longer in use. + + The fx_media_driver_write_protect flag can be set anytime by the + driver to indicate the media is not writable. Write attempts made + when this flag is set are returned as errors. */ + + /* Perform basic initialization here... since the boot record is going + to be read subsequently and again for volume name requests. */ + + /* With flash wear leveling, FileX should tell wear leveling when sectors + are no longer in use. */ + media_ptr -> fx_media_driver_free_sector_update = FX_TRUE; + + /* Open the NOR flash simulation. */ + status = lx_nor_flash_open(&nor_flash, "sim nor flash", _lx_nor_flash_simulator_initialize); + + /* Determine if the flash open was successful. */ + if (status != LX_SUCCESS) + { + + /* Return an I/O error to FileX. */ + media_ptr -> fx_media_driver_status = FX_IO_ERROR; + + return; + } + + /* Successful driver request. */ + media_ptr -> fx_media_driver_status = FX_SUCCESS; + break; + } + + case FX_DRIVER_UNINIT: + { + + /* There is nothing to do in this case for the RAM driver. For actual + devices some shutdown processing may be necessary. */ + + /* Close the NOR flash simulation. */ + status = lx_nor_flash_close(&nor_flash); + + /* Determine if the flash close was successful. */ + if (status != LX_SUCCESS) + { + + /* Return an I/O error to FileX. */ + media_ptr -> fx_media_driver_status = FX_IO_ERROR; + + return; + } + + /* Successful driver request. */ + media_ptr -> fx_media_driver_status = FX_SUCCESS; + break; + } + + case FX_DRIVER_BOOT_READ: + { + + /* Read the boot record and return to the caller. */ + + /* Setup the destination buffer. */ + destination_buffer = (UCHAR *) media_ptr -> fx_media_driver_buffer; + + /* Read boot sector from NOR flash. */ + status = lx_nor_flash_sector_read(&nor_flash, 0, destination_buffer); + + /* For NOR driver, determine if the boot record is valid. */ + if ((destination_buffer[0] != (UCHAR) 0xEB) || + (destination_buffer[1] != (UCHAR) 0x34) || + (destination_buffer[2] != (UCHAR) 0x90)) + { + + /* Invalid boot record, return an error! */ + media_ptr -> fx_media_driver_status = FX_MEDIA_INVALID; + return; + } + + /* Determine if the boot read was successful. */ + if (status != LX_SUCCESS) + { + + /* Return an I/O error to FileX. */ + media_ptr -> fx_media_driver_status = FX_IO_ERROR; + + return; + } + + /* Successful driver request. */ + media_ptr -> fx_media_driver_status = FX_SUCCESS; + break; + } + + case FX_DRIVER_BOOT_WRITE: + { + + /* Write the boot record and return to the caller. */ + + /* Setup the source buffer. */ + source_buffer = (UCHAR *) media_ptr -> fx_media_driver_buffer; + + /* Write boot sector to NOR flash. */ + status = lx_nor_flash_sector_write(&nor_flash, 0, source_buffer); + + /* Determine if the boot write was successful. */ + if (status != LX_SUCCESS) + { + + /* Return an I/O error to FileX. */ + media_ptr -> fx_media_driver_status = FX_IO_ERROR; + + return; + } + + /* Successful driver request. */ + media_ptr -> fx_media_driver_status = FX_SUCCESS; + break ; + } + + default: + { + + /* Invalid driver request. */ + media_ptr -> fx_media_driver_status = FX_IO_ERROR; + break; + } + } +} + diff --git a/common/src/lx_nand_flash_256byte_ecc_check.c b/common/src/lx_nand_flash_256byte_ecc_check.c new file mode 100644 index 0000000..4a9d0d7 --- /dev/null +++ b/common/src/lx_nand_flash_256byte_ecc_check.c @@ -0,0 +1,182 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** LevelX Component */ +/** */ +/** NAND Flash */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define LX_SOURCE_CODE + + +/* Disable ThreadX error checking. */ + +#ifndef TX_DISABLE_ERROR_CHECKING +#define TX_DISABLE_ERROR_CHECKING +#endif + + +/* Include necessary system files. */ + +#include "lx_api.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _lx_nand_flash_256byte_ecc_check PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function checks 256 bytes of a NAND flash and ECC and */ +/* attempts to correct any single bit errors. */ +/* */ +/* INPUT */ +/* */ +/* page_buffer Page buffer */ +/* ecc_buffer Returned ECC buffer */ +/* */ +/* OUTPUT */ +/* */ +/* return status */ +/* */ +/* CALLS */ +/* */ +/* _lx_nand_flash_256byte_ecc_compute Compute ECC for 256 bytes */ +/* */ +/* CALLED BY */ +/* */ +/* _lx_nand_flash_page_ecc_check NAND page check */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 William E. Lamie Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _lx_nand_flash_256byte_ecc_check(UCHAR *page_buffer, UCHAR *ecc_buffer) +{ + +INT i, j; +UCHAR mask; +UCHAR new_ecc_buffer[3]; +UCHAR ecc_errors[3]; +INT error_count; +USHORT *data; +USHORT byte; +USHORT bit; +ULONG correction_code; + + + /* Clear the error count. */ + ecc_errors[0] = 0; + ecc_errors[1] = 0; + ecc_errors[2] = 0; + + /* Calculate a new ECC for the 256 byte buffer. */ + _lx_nand_flash_256byte_ecc_compute(page_buffer, new_ecc_buffer); + + /* Clear error count. */ + error_count = 0; + + /* Loop through the ECC bytes to determine if there is an error in the page. */ + for (i = 0; i < 3; i++) + { + + /* Check for differences in the ECCs. */ + ecc_errors[i] = new_ecc_buffer[i] ^ ecc_buffer[i]; + + /* Are there any errors? */ + if (ecc_errors[i]) + { + + /* Accumulate the count of set bits. */ + mask = 1; + for (j = 0; j < 8; j++) + { + + /* Is this bit set? */ + if (ecc_errors[i] & mask) + { + + /* Yes, increment the count. */ + error_count++; + } + + /* Move mask to next bit. */ + mask = (UCHAR) ((mask << 1) & 0xFF); + } + } + } + + /* Determine if there are any errors. */ + if (error_count == 0) + { + + /* Everything is okay, return success. */ + return(LX_SUCCESS); + } + + /* Was a correctable error discovered? */ + else if (error_count == 11) + { + + /* Initialize bit and byte offset values. */ + bit = 0; + byte = 0; + + /* Setup the data pointer. */ + data = (USHORT *) page_buffer; + + /* Calculate the 32-bit correction code. */ + correction_code = (ULONG) (ecc_errors[2] << 16) | (ULONG)(ecc_errors[1] << 8) | (ULONG)ecc_errors[0]; + + /* Unpack the correction code. */ + byte = (USHORT) ((byte | ((correction_code >> (21+2)) & 1) << 6) & 0xFFFF); + byte = (USHORT) ((byte | ((correction_code >> (19+2)) & 1) << 5) & 0xFFFF); + byte = (USHORT) ((byte | ((correction_code >> (17+2)) & 1) << 4) & 0xFFFF); + byte = (USHORT) ((byte | ((correction_code >> (15+2)) & 1) << 3) & 0xFFFF); + byte = (USHORT) ((byte | ((correction_code >> (13+2)) & 1) << 2) & 0xFFFF); + byte = (USHORT) ((byte | ((correction_code >> (11+2)) & 1) << 1) & 0xFFFF); + byte = (USHORT) ((byte | ((correction_code >> (9+2)) & 1) << 0) & 0xFFFF); + bit = (USHORT) ((bit | ((correction_code >> (7+2)) & 1) << 3) & 0xFFFF); + bit = (USHORT) ((bit | ((correction_code >> (5+2)) & 1) << 2) & 0xFFFF); + bit = (USHORT) ((bit | ((correction_code >> (3+2)) & 1) << 1) & 0xFFFF); + bit = (USHORT) ((bit | ((correction_code >> (1+2)) & 1) << 0) & 0xFFFF); + + /* Fix the error. */ + data[byte] = (USHORT) ((data[byte] ^ (1 << bit)) & 0xFFFF); + + /* Return an error corrected status. */ + return(LX_NAND_ERROR_CORRECTED); + } + + /* Otherwise, an unrecoverable ECC or data error is present. */ + else + { + + /* Return an error. */ + return(LX_NAND_ERROR_NOT_CORRECTED); + } +} + diff --git a/common/src/lx_nand_flash_256byte_ecc_compute.c b/common/src/lx_nand_flash_256byte_ecc_compute.c new file mode 100644 index 0000000..0a8a78c --- /dev/null +++ b/common/src/lx_nand_flash_256byte_ecc_compute.c @@ -0,0 +1,229 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** LevelX Component */ +/** */ +/** NAND Flash */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define LX_SOURCE_CODE + + +/* Disable ThreadX error checking. */ + +#ifndef TX_DISABLE_ERROR_CHECKING +#define TX_DISABLE_ERROR_CHECKING +#endif + + +/* Include necessary system files. */ + +#include "lx_api.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _lx_nand_flash_256byte_ecc_compute PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function computes the ECC for 256 bytes of a NAND flash page. */ +/* The resulting ECC code is returned in 3 bytes. */ +/* */ +/* INPUT */ +/* */ +/* page_buffer Page buffer */ +/* ecc_buffer Returned ECC buffer */ +/* */ +/* OUTPUT */ +/* */ +/* return status */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* _lx_nand_flash_page_ecc_compute NAND page ECC compute */ +/* _lx_nand_flash_256byte_ecc_check Check 256 bytes and ECC */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 William E. Lamie Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _lx_nand_flash_256byte_ecc_compute(UCHAR *page_buffer, UCHAR *ecc_buffer) +{ + +USHORT i, j; +USHORT *data; +USHORT bits, mask; +USHORT bit_parity; +USHORT even_bit_parity; +USHORT odd_bit_parity; +USHORT even_byte_parity; +USHORT odd_byte_parity; + + + /* Initialize local variables. */ + bit_parity = 0; + even_bit_parity = 0; + odd_bit_parity = 0; + even_byte_parity = 0; + odd_byte_parity = 0; + + /* Initialize the return ECC code area. */ + ecc_buffer[0]= 0; + ecc_buffer[1]= 0; + ecc_buffer[2]= 0; + + /* Setup a 16-bit pointer to the buffer area. */ + data = (USHORT *) page_buffer; + + /* Loop through the 256 byte buffer, 16 bits at a time. */ + for (i = 0; i < 128; i++) + { + + /* Compute the ECC value. */ + bit_parity = bit_parity ^ data[i]; + + /* Now count the bits in the current data word. */ + bits = 0; + mask = 1; + for (j = 0; j < 16; j++) + { + + /* Is the bit set? */ + if (data[i] & mask) + { + + /* Yes, increment the bit count. */ + bits++; + } + + /* Move the mask to the next bit. */ + mask = (USHORT) ((mask << 1) & 0xFFFF); + } + + /* Determine if the number of bits is odd. */ + if ((bits & 1) == 1) + { + + /* Odd number of bits. Adjust the odd/even byte parity. */ + even_byte_parity = (USHORT) ((even_byte_parity ^ (0xffff - i)) & 0xFFFF); + odd_byte_parity = odd_byte_parity ^ i; + } + } + + /* Now look for bits set in the bit parity. */ + for (i = 0; i < 16; i++) + { + + /* Is the bit set? */ + if (bit_parity & 1) + { + + /* Yes, adjust the odd even byte parity. */ + even_bit_parity = (USHORT) ((even_bit_parity ^ (15 - i)) & 0xFFFF); + odd_bit_parity = odd_bit_parity ^ i; + } + + /* Look at next bit position. */ + bit_parity = bit_parity >> 1; + } + + /* At this point, we need to pack the 22 ECC bits into the 3 byte return area. */ + + /* Pack bit 21. */ + ecc_buffer[(21+2)/8] = ((UCHAR)(ecc_buffer[(21+2)/8] | ((odd_byte_parity >> 6) & 1) << (21+2)%8) & 0xFF); + + /* Pack bit 20. */ + ecc_buffer[(20+2)/8] = ((UCHAR)(ecc_buffer[(20+2)/8] | ((even_byte_parity >> 6) & 1) << (20+2)%8) & 0xFF); + + /* Pack bit 19. */ + ecc_buffer[(19+2)/8] = ((UCHAR)(ecc_buffer[(19+2)/8] | ((odd_byte_parity >> 5) & 1) << (19+2)%8) & 0xFF); + + /* Pack bit 18. */ + ecc_buffer[(18+2)/8] = ((UCHAR)(ecc_buffer[(18+2)/8] | ((even_byte_parity >> 5) & 1) << (18+2)%8) & 0xFF); + + /* Pack bit 17. */ + ecc_buffer[(17+2)/8] = ((UCHAR)(ecc_buffer[(17+2)/8] | ((odd_byte_parity >> 4) & 1) << (17+2)%8) & 0xFF); + + /* Pack bit 16. */ + ecc_buffer[(16+2)/8] = ((UCHAR)(ecc_buffer[(16+2)/8] | ((even_byte_parity >> 4) & 1) << (16+2)%8) & 0xFF); + + /* Pack bit 15. */ + ecc_buffer[(15+2)/8] = ((UCHAR)(ecc_buffer[(15+2)/8] | ((odd_byte_parity >> 3) & 1) << (15+2)%8) & 0xFF); + + /* Pack bit 14. */ + ecc_buffer[(14+2)/8] = ((UCHAR)(ecc_buffer[(14+2)/8] | ((even_byte_parity >> 3) & 1) << (14+2)%8) & 0xFF); + + /* Pack bit 13. */ + ecc_buffer[(13+2)/8] = ((UCHAR)(ecc_buffer[(13+2)/8] | ((odd_byte_parity >> 2) & 1) << (13+2)%8) & 0xFF); + + /* Pack bit 12. */ + ecc_buffer[(12+2)/8] = ((UCHAR)(ecc_buffer[(12+2)/8] | ((even_byte_parity >> 2) & 1) << (12+2)%8) & 0xFF); + + /* Pack bit 11. */ + ecc_buffer[(11+2)/8] = ((UCHAR)(ecc_buffer[(11+2)/8] | ((odd_byte_parity >> 1) & 1) << (11+2)%8) & 0xFF); + + /* Pack bit 10. */ + ecc_buffer[(10+2)/8] = ((UCHAR)(ecc_buffer[(10+2)/8] | ((even_byte_parity >> 1) & 1) << (10+2)%8) & 0xFF); + + /* Pack bit 9. */ + ecc_buffer[(9+2)/8] = ((UCHAR)(ecc_buffer[(9+2)/8] | ((odd_byte_parity >> 0) & 1) << (9+2)%8) & 0xFF); + + /* Pack bit 8. */ + ecc_buffer[(8+2)/8] = ((UCHAR)(ecc_buffer[(8+2)/8] | ((even_byte_parity >> 0) & 1) << (8+2)%8) & 0xFF); + + /* Pack bit 7. */ + ecc_buffer[(7+2)/8] = ((UCHAR)(ecc_buffer[(7+2)/8] | ((odd_bit_parity >> 3) & 1) << (7+2)%8) & 0xFF); + + /* Pack bit 6. */ + ecc_buffer[(6+2)/8] = ((UCHAR)(ecc_buffer[(6+2)/8] | ((even_bit_parity >> 3) & 1) << (6+2)%8) & 0xFF); + + /* Pack bit 5. */ + ecc_buffer[(5+2)/8] = ((UCHAR)(ecc_buffer[(5+2)/8] | ((odd_bit_parity >> 2) & 1) << (5+2)%8) & 0xFF); + + /* Pack bit 4. */ + ecc_buffer[(4+2)/8] = ((UCHAR)(ecc_buffer[(4+2)/8] | ((even_bit_parity >> 2) & 1) << (4+2)%8) & 0xFF); + + /* Pack bit 3. */ + ecc_buffer[(3+2)/8] = ((UCHAR)(ecc_buffer[(3+2)/8] | ((odd_bit_parity >> 1) & 1) << (3+2)%8) & 0xFF); + + /* Pack bit 2. */ + ecc_buffer[(2+2)/8] = ((UCHAR)(ecc_buffer[(2+2)/8] | ((even_bit_parity >> 1) & 1) << (2+2)%8) & 0xFF); + + /* Pack bit 1. */ + ecc_buffer[(1+2)/8] = ((UCHAR)(ecc_buffer[(1+2)/8] | ((odd_bit_parity >> 0) & 1) << (1+2)%8) & 0xFF); + + /* Pack bit 0. */ + ecc_buffer[(0+2)/8] = ((UCHAR)(ecc_buffer[(0+2)/8] | ((even_bit_parity >> 0) & 1) << (0+2)%8) & 0xFF); + + /* Return success! */ + return(LX_SUCCESS); +} + diff --git a/common/src/lx_nand_flash_block_full_update.c b/common/src/lx_nand_flash_block_full_update.c new file mode 100644 index 0000000..e63a4d5 --- /dev/null +++ b/common/src/lx_nand_flash_block_full_update.c @@ -0,0 +1,180 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** LevelX Component */ +/** */ +/** NAND Flash */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define LX_SOURCE_CODE + + +/* Disable ThreadX error checking. */ + +#ifndef TX_DISABLE_ERROR_CHECKING +#define TX_DISABLE_ERROR_CHECKING +#endif + + +/* Include necessary system files. */ + +#include "lx_api.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _lx_nand_flash_block_full_update PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function writes the list of page to logical sector mapping */ +/* when the block is full. */ +/* */ +/* INPUT */ +/* */ +/* nand_flash NAND flash instance */ +/* block Block that is full */ +/* erase_count Block erase count */ +/* */ +/* OUTPUT */ +/* */ +/* return status */ +/* */ +/* CALLS */ +/* */ +/* _lx_nand_flash_driver_write Driver flash page write */ +/* _lx_nand_flash_driver_extra_bytes_get Get extra bytes from spare */ +/* _lx_nand_flash_driver_extra_bytes_set Set extra bytes from spare */ +/* _lx_nand_flash_system_error Internal system error handler */ +/* */ +/* CALLED BY */ +/* */ +/* Internal LevelX */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 William E. Lamie Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _lx_nand_flash_block_full_update(LX_NAND_FLASH *nand_flash, ULONG block, ULONG erase_count) +{ + +LX_NAND_PAGE_EXTRA_INFO extra_info; +UINT status; +ULONG i; +ULONG *list_ptr; + + + /* Setup pointer to internal buffer. */ + list_ptr = nand_flash -> lx_nand_flash_page_buffer; + + /* Set the internal buffer of all ones. */ + for (i = 0; i < nand_flash -> lx_nand_flash_words_per_page; i++) + { + + /* Set the buffer to all ones. */ + list_ptr[i] = LX_ALL_ONES; + } + + /* Set the initial word to the erase count (this is what is already there!). */ + list_ptr[0] = erase_count; + + /* Now walk through the pages in the block to build the list of mapped logical + sectors to pages. */ + i = 1; + while (i < nand_flash -> lx_nand_flash_pages_per_block) + { + + /* Read the extra bytes of each page to pickup the associated mapping. */ + status = _lx_nand_flash_driver_extra_bytes_get(nand_flash, block, i, (UCHAR *) &extra_info, sizeof(extra_info)); + + /* Check for an error from flash driver. */ + if (status) + { + + /* Call system error handler. */ + _lx_nand_flash_system_error(nand_flash, status, block, i); + + /* Return the error... no point in continuing. */ + return(status); + } + + /* Determine if the extra info indicates the page has not yet been allocated. */ + if (extra_info.lx_nand_page_extra_info_logical_sector == LX_NAND_PAGE_FREE) + { + + /* Block not full, get out of the loop. */ + break; + } + + /* Save the mapping. */ + list_ptr[i] = extra_info.lx_nand_page_extra_info_logical_sector; + + /* Move to next entry. */ + i++; + } + + /* Check for a full block. */ + if (i == nand_flash -> lx_nand_flash_pages_per_block) + { + + /* Yes, the block is full. */ + + /* Place the list complete marker so we know the list is intact. */ + list_ptr[i] = LX_NAND_PAGE_LIST_VALID; + + /* At this point we can write the buffer to page 0 of the block. */ + + /* Write the mapping list to page 0. */ + status = _lx_nand_flash_driver_write(nand_flash, block, 0, list_ptr, (nand_flash -> lx_nand_flash_pages_per_block + 1)); + + /* Check for an error from flash driver. */ + if (status) + { + + /* Call system error handler. */ + _lx_nand_flash_system_error(nand_flash, status, block, 0); + + /* Return the error... no point in continuing. */ + return(status); + } + + /* Write the extra bytes for page 0. */ + extra_info.lx_nand_page_extra_info_logical_sector = ((ULONG) LX_NAND_PAGE_FREE) & ~(((ULONG) LX_NAND_BLOCK_EMPTY) | ((ULONG) LX_NAND_BLOCK_FULL)); + status = _lx_nand_flash_driver_extra_bytes_set(nand_flash, block, 0, (UCHAR *) &extra_info, sizeof(extra_info)); + + /* Check for an error from flash driver. */ + if (status) + { + + /* Call system error handler. */ + _lx_nand_flash_system_error(nand_flash, status, block, 0); + } + } + + /* Return status. */ + return(status); +} + diff --git a/common/src/lx_nand_flash_block_obsoleted_check.c b/common/src/lx_nand_flash_block_obsoleted_check.c new file mode 100644 index 0000000..77a53d7 --- /dev/null +++ b/common/src/lx_nand_flash_block_obsoleted_check.c @@ -0,0 +1,369 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** LevelX Component */ +/** */ +/** NAND Flash */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define LX_SOURCE_CODE + + +/* Disable ThreadX error checking. */ + +#ifndef TX_DISABLE_ERROR_CHECKING +#define TX_DISABLE_ERROR_CHECKING +#endif + + +/* Include necessary system files. */ + +#include "lx_api.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _lx_nand_flash_block_obsoleted_check PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function checks to see if the specified block is completely */ +/* obsoleted. If so, the block is reclaimed (erased) and made */ +/* available. */ +/* */ +/* INPUT */ +/* */ +/* nand_flash NAND flash instance */ +/* block Block number to check */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* _lx_nand_flash_driver_block_status_get */ +/* Driver block status */ +/* _lx_nand_flash_driver_block_erase Driver block erase */ +/* _lx_nand_flash_driver_extra_bytes_get Driver get extra bytes */ +/* _lx_nand_flash_driver_block_status_set */ +/* Driver set extra bytes */ +/* _lx_nand_flash_driver_read Driver page read */ +/* _lx_nand_flash_driver_write Driver page write */ +/* _lx_nand_flash_block_reclaim Reclaim one block */ +/* _lx_nand_flash_system_error Internal system error handler */ +/* */ +/* CALLED BY */ +/* */ +/* Internal LevelX */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 William E. Lamie Initial Version 6.0 */ +/* */ +/**************************************************************************/ +VOID _lx_nand_flash_block_obsoleted_check(LX_NAND_FLASH *nand_flash, ULONG block) +{ + +LX_NAND_PAGE_EXTRA_INFO extra_info; +UCHAR block_status; +ULONG *page_word_ptr; +ULONG erase_count; +ULONG j; +UINT status; + + + /* First, check to make sure this block is good. */ + status = _lx_nand_flash_driver_block_status_get(nand_flash, block, &block_status); + + /* Check for an error from flash driver. */ + if (status) + { + + /* Call system error handler. */ + _lx_nand_flash_system_error(nand_flash, status, block, 0); + + /* Simply return. */ + return; + } + + /* Is this block bad? */ + if (block_status != LX_NAND_GOOD_BLOCK) + { + + /* Yes, this block is bad. */ + + /* Call system error handler. */ + _lx_nand_flash_system_error(nand_flash, status, block, 0); + + /* Simply return. */ + return; + } + + /* Read the extra bytes of page 0. This will tell us if the page has any valid mappings. */ + status = _lx_nand_flash_driver_extra_bytes_get(nand_flash, block, 0, (UCHAR *) &extra_info, sizeof(extra_info)); + + /* Check for an error from flash driver. */ + if (status) + { + + /* Call system error handler. */ + _lx_nand_flash_system_error(nand_flash, status, block, 0); + + /* Simply return. */ + return; + } + + /* Determine if the block is full, since it must be full first before it can be + completely obsoleted. */ + if ((extra_info.lx_nand_page_extra_info_logical_sector & LX_NAND_BLOCK_FULL) != 0) + { + + /* This block is not full, therefore it cannot be obsoleted. */ + + /* Simply return. */ + return; + } + + /* Setup pointer to internal buffer. */ + page_word_ptr = nand_flash -> lx_nand_flash_page_buffer; + + /* Now read page 0 of the block, which has the erase count in the first 4 bytes. */ + status = _lx_nand_flash_driver_read(nand_flash, block, 0, page_word_ptr, (nand_flash -> lx_nand_flash_pages_per_block + 1)); + + /* Check for an error from flash driver. */ + if (status) + { + + /* Call system error handler. */ + _lx_nand_flash_system_error(nand_flash, status, block, 0); + + /* Determine if the error is fatal. */ + if (status != LX_NAND_ERROR_CORRECTED) + { + + /* Simply return. */ + return; + } + } + + /* Determine if we have a valid logical sector mapping list. */ + if ((page_word_ptr[1] != LX_NAND_PAGE_FREE) && + (page_word_ptr[nand_flash -> lx_nand_flash_pages_per_block] == LX_NAND_PAGE_LIST_VALID)) + { + + /* Yes, we have a valid logical sector mapping list in page 0. */ + + /* Traverse the list to look for the logical sector. */ + for (j = 1; j < nand_flash -> lx_nand_flash_pages_per_block; j++) + { + + /* Is this entry valid? */ + if (page_word_ptr[j] & LX_NAND_PAGE_VALID) + { + + /* Read in the actual page entry to make sure this logical sector mapping is still valid. */ + status = _lx_nand_flash_driver_extra_bytes_get(nand_flash, block, j, (UCHAR *) &extra_info, sizeof(extra_info)); + + /* Check for an error from flash driver. */ + if (status) + { + + /* Call system error handler. */ + _lx_nand_flash_system_error(nand_flash, status, block, j); + + /* Simply return. */ + return; + } + + /* Determine if the actual page is still valid. */ + if (extra_info.lx_nand_page_extra_info_logical_sector & LX_NAND_PAGE_VALID) + { + + /* If so, we found a valid page so the block isn't completely obsoleted. */ + + /* Simply return. */ + return; + } + } + } + + /* Pickup the erase count. */ + erase_count = page_word_ptr[0]; + + /* Determine if we need to reclaim blocks to balance out the erase counts. */ + if ((erase_count == nand_flash -> lx_nand_flash_maximum_erase_count) && + ((nand_flash -> lx_nand_flash_maximum_erase_count - nand_flash -> lx_nand_flash_minimum_erase_count) >= LX_NAND_FLASH_MAX_ERASE_COUNT_DELTA)) + { + + /* Attempt to reclaim one block. */ + _lx_nand_flash_block_reclaim(nand_flash); + } + else + { + + /* At this point we can reclaim the block. */ + + /* Write the erased started indication. */ + page_word_ptr[0] = LX_BLOCK_ERASE_STARTED; + status = _lx_nand_flash_driver_write(nand_flash, block, 0, page_word_ptr, LX_NAND_ERASE_COUNT_WRITE_SIZE); + + /* Check for an error from flash driver. */ + if (status) + { + + /* Call system error handler. */ + _lx_nand_flash_system_error(nand_flash, status, block, 0); + } + + /* Erase the entire block. */ + status = _lx_nand_flash_driver_block_erase(nand_flash, block, erase_count+1); + + /* Check for an error from flash driver. */ + if (status) + { + + /* Call system error handler. */ + _lx_nand_flash_system_error(nand_flash, status, block, 0); + + /* Attempt to mark this block as bad. */ + status = _lx_nand_flash_driver_block_status_set(nand_flash, block, LX_NAND_BAD_BLOCK); + + /* Check for error in setting the block status. */ + if (status) + { + + /* Call system error handler. */ + _lx_nand_flash_system_error(nand_flash, status, block, 0); + } + + /* Increment the bad block count. */ + nand_flash -> lx_nand_flash_bad_blocks++; + + /* Update number of obsolete pages but not free pages since this block is now bad. */ + nand_flash -> lx_nand_flash_obsolete_pages = nand_flash -> lx_nand_flash_obsolete_pages - (nand_flash -> lx_nand_flash_pages_per_block - 1); + } + else + { + + /* Increment the erase count. */ + erase_count++; + + /* Determine if the new erase count exceeds the maximum. */ + if (erase_count > ((ULONG) LX_BLOCK_ERASE_COUNT_MAX)) + { + + /* Yes, erase count is in overflow. Stay at the maximum count. */ + erase_count = ((ULONG) LX_BLOCK_ERASE_COUNT_MAX); + } + + /* Determine if we need to update the maximum erase count. */ + if (erase_count > nand_flash -> lx_nand_flash_maximum_erase_count) + { + + /* Yes, a new maximum is present. */ + nand_flash -> lx_nand_flash_maximum_erase_count = erase_count; + + /* Determine if the search pointer is on this block. */ + if (nand_flash -> lx_nand_flash_free_block_search == block) + { + + /* Move to next block. */ + nand_flash -> lx_nand_flash_free_block_search++; + + /* Check for wrap condition. */ + if (nand_flash -> lx_nand_flash_free_block_search >= nand_flash -> lx_nand_flash_total_blocks) + { + + /* Reset to the beginning block. */ + nand_flash -> lx_nand_flash_free_block_search = 0; + } + } + } + + /* Set the buffer to all ones. */ + for (j = 0; j < nand_flash -> lx_nand_flash_words_per_page; j++) + { + + /* Set word to all ones. */ + page_word_ptr[j] = LX_ALL_ONES; + } + + /* Now store the erase count. */ + page_word_ptr[0] = (erase_count | LX_BLOCK_ERASED); + + /* Write the erase count for the block. */ + status = _lx_nand_flash_driver_write(nand_flash, block, 0, page_word_ptr, LX_NAND_ERASE_COUNT_WRITE_SIZE); + + /* Check to see if the write was successful. */ + if (status == LX_SUCCESS) + { + + /* Now store the erase count. */ + page_word_ptr[0] = erase_count; + + /* Write the erase count for the block. */ + status = _lx_nand_flash_driver_write(nand_flash, block, 0, page_word_ptr, LX_NAND_ERASE_COUNT_WRITE_SIZE); + } + + /* Check for an error from flash driver. */ + if (status) + { + + /* Call system error handler. */ + _lx_nand_flash_system_error(nand_flash, status, block, 0); + + /* Attempt to mark this block as bad. */ + status = _lx_nand_flash_driver_block_status_set(nand_flash, block, LX_NAND_BAD_BLOCK); + + /* Check for error in setting the block status. */ + if (status) + { + + /* Call system error handler. */ + _lx_nand_flash_system_error(nand_flash, status, block, 0); + + /* Simply return. */ + return; + } + + /* Increment the bad block count. */ + nand_flash -> lx_nand_flash_bad_blocks++; + + /* Update number of obsolete pages but not free pages since this block is now bad. */ + nand_flash -> lx_nand_flash_obsolete_pages = nand_flash -> lx_nand_flash_obsolete_pages - (nand_flash -> lx_nand_flash_pages_per_block - 1); + } + else + { + + /* Update parameters of this flash. */ + nand_flash -> lx_nand_flash_free_pages = nand_flash -> lx_nand_flash_free_pages + (nand_flash -> lx_nand_flash_pages_per_block - 1); + nand_flash -> lx_nand_flash_obsolete_pages = nand_flash -> lx_nand_flash_obsolete_pages - (nand_flash -> lx_nand_flash_pages_per_block - 1); + } + } + } + } +} + diff --git a/common/src/lx_nand_flash_block_reclaim.c b/common/src/lx_nand_flash_block_reclaim.c new file mode 100644 index 0000000..5dd5340 --- /dev/null +++ b/common/src/lx_nand_flash_block_reclaim.c @@ -0,0 +1,631 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** LevelX Component */ +/** */ +/** NAND Flash */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define LX_SOURCE_CODE + + +/* Disable ThreadX error checking. */ + +#ifndef TX_DISABLE_ERROR_CHECKING +#define TX_DISABLE_ERROR_CHECKING +#endif + + +/* Include necessary system files. */ + +#include "lx_api.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _lx_nand_flash_block_reclaim PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function reclaims one block from the NAND flash. */ +/* */ +/* INPUT */ +/* */ +/* nand_flash NAND flash instance */ +/* */ +/* OUTPUT */ +/* */ +/* return status */ +/* */ +/* CALLS */ +/* */ +/* _lx_nand_flash_driver_block_erase Driver erase block */ +/* _lx_nand_flash_driver_block_status_set */ +/* Set block status to bad */ +/* _lx_nand_flash_driver_extra_bytes_get NAND flash get extra bytes */ +/* _lx_nand_flash_driver_extra_bytes_set NAND flash set extra bytes */ +/* _lx_nand_flash_driver_write Driver flash page write */ +/* _lx_nand_flash_driver_read Driver flash page read */ +/* _lx_nand_flash_block_full_update Update page 0 with list of */ +/* mapped pages */ +/* _lx_nand_flash_next_block_to_erase_find */ +/* Find next block to erase */ +/* _lx_nand_flash_physical_page_allocate Allocate new page */ +/* _lx_nand_flash_sector_mapping_cache_invalidate */ +/* Invalidate cache entry */ +/* _lx_nand_flash_system_error Internal system error handler */ +/* */ +/* CALLED BY */ +/* */ +/* Internal LevelX */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 William E. Lamie Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _lx_nand_flash_block_reclaim(LX_NAND_FLASH *nand_flash) +{ + +LX_NAND_PAGE_EXTRA_INFO old_extra_info; +LX_NAND_PAGE_EXTRA_INFO new_extra_info; +ULONG *page_word_ptr; +ULONG i; +ULONG erase_block; +ULONG erase_count; +ULONG mapped_pages; +ULONG obsolete_pages; +ULONG free_pages; +ULONG logical_sector; +ULONG new_block; +ULONG new_page; +ULONG new_erase_count; +UINT status; + + + /* Increment the reclaim attempts counter. */ + nand_flash -> lx_nand_flash_diagnostic_block_reclaim_attempts++; + + /* Determine the next block to erase. */ + _lx_nand_flash_next_block_to_erase_find(nand_flash, &erase_block, &erase_count, &mapped_pages, &obsolete_pages); + + /* Setup pointer to page memory. */ + page_word_ptr = nand_flash -> lx_nand_flash_page_buffer; + + /* Determine if the search pointer is set for this block. */ + if (nand_flash -> lx_nand_flash_free_block_search == erase_block) + { + + /* Ensure the search block is not the block we are trying to free. */ + nand_flash -> lx_nand_flash_free_block_search = erase_block + 1; + + /* Check for wrap condition. */ + if (nand_flash -> lx_nand_flash_free_block_search >= nand_flash -> lx_nand_flash_total_blocks) + nand_flash -> lx_nand_flash_free_block_search = 0; + } + + /* Determine if this block is completely obsolete. */ + if (obsolete_pages == nand_flash -> lx_nand_flash_pages_per_block) + { + + /* Read page 0 of the block, which has the erase count in the first 4 bytes. */ + status = _lx_nand_flash_driver_read(nand_flash, erase_block, 0, page_word_ptr, (nand_flash -> lx_nand_flash_pages_per_block + 1)); + + /* Check for an error from flash driver. */ + if (status) + { + + /* Call system error handler. */ + _lx_nand_flash_system_error(nand_flash, status, erase_block, 0); + } + + /* Write the erased started indication. */ + page_word_ptr[0] = LX_BLOCK_ERASE_STARTED; + status = _lx_nand_flash_driver_write(nand_flash, erase_block, 0, page_word_ptr, LX_NAND_ERASE_COUNT_WRITE_SIZE); + + /* Check for an error from flash driver. */ + if (status) + { + + /* Call system error handler. */ + _lx_nand_flash_system_error(nand_flash, status, erase_block, 0); + } + + /* Erase the entire block. */ + status = _lx_nand_flash_driver_block_erase(nand_flash, erase_block, erase_count+1); + + /* Check for an error from flash driver. */ + if (status) + { + + /* Call system error handler. */ + _lx_nand_flash_system_error(nand_flash, status, erase_block, 0); + + /* Attempt to mark this block as bad. */ + status = _lx_nand_flash_driver_block_status_set(nand_flash, erase_block, LX_NAND_BAD_BLOCK); + + /* Check for error in setting the block status. */ + if (status) + { + + /* Call system error handler. */ + _lx_nand_flash_system_error(nand_flash, status, erase_block, 0); + } + + /* Increment the bad block count. */ + nand_flash -> lx_nand_flash_bad_blocks++; + + /* Update number of obsolete pages but not free pages since this block is now bad. */ + nand_flash -> lx_nand_flash_obsolete_pages = nand_flash -> lx_nand_flash_obsolete_pages - obsolete_pages; + } + else + { + + /* Increment the erase count. */ + erase_count++; + + /* Determine if the new erase count exceeds the maximum. */ + if (erase_count > ((ULONG) LX_BLOCK_ERASE_COUNT_MAX)) + { + + /* Yes, erase count is in overflow. Stay at the maximum count. */ + erase_count = ((ULONG) LX_BLOCK_ERASE_COUNT_MAX); + } + + /* Determine if we need to update the maximum erase count. */ + if (erase_count > nand_flash -> lx_nand_flash_maximum_erase_count) + { + + /* Yes, a new maximum is present. */ + nand_flash -> lx_nand_flash_maximum_erase_count = erase_count; + } + + /* Set the buffer to all ones. */ + for (i = 0; i < nand_flash -> lx_nand_flash_words_per_page; i++) + { + + /* Set word to all ones. */ + page_word_ptr[i] = LX_ALL_ONES; + } + + /* Now store the erase count. */ + page_word_ptr[0] = (erase_count | LX_BLOCK_ERASED); + + /* Write the erase count for the block. */ + status = _lx_nand_flash_driver_write(nand_flash, erase_block, 0, page_word_ptr, LX_NAND_ERASE_COUNT_WRITE_SIZE); + + /* Check the status. */ + if (status == LX_SUCCESS) + { + + /* Now store the erase count. */ + page_word_ptr[0] = erase_count; + + /* Write the erase count for the block. */ + status = _lx_nand_flash_driver_write(nand_flash, erase_block, 0, page_word_ptr, LX_NAND_ERASE_COUNT_WRITE_SIZE); + } + + /* Check for an error from flash driver. */ + if (status) + { + + /* Call system error handler. */ + _lx_nand_flash_system_error(nand_flash, status, erase_block, 0); + + /* Attempt to mark this block as bad. */ + status = _lx_nand_flash_driver_block_status_set(nand_flash, erase_block, LX_NAND_BAD_BLOCK); + + /* Check for error in setting the block status. */ + if (status) + { + + /* Call system error handler. */ + _lx_nand_flash_system_error(nand_flash, status, erase_block, 0); + } + + /* Increment the bad block count. */ + nand_flash -> lx_nand_flash_bad_blocks++; + + /* Update number of obsolete pages but not free pages since this block is now bad. */ + nand_flash -> lx_nand_flash_obsolete_pages = nand_flash -> lx_nand_flash_obsolete_pages - obsolete_pages; + } + else + { + + /* Update parameters of this flash. */ + nand_flash -> lx_nand_flash_free_pages = nand_flash -> lx_nand_flash_free_pages + obsolete_pages; + nand_flash -> lx_nand_flash_obsolete_pages = nand_flash -> lx_nand_flash_obsolete_pages - obsolete_pages; + } + } + } + else + { + + /* Calculate the number of free pages in this block. */ + free_pages = nand_flash -> lx_nand_flash_pages_per_block - (obsolete_pages + mapped_pages); + + /* Determine if there are enough free pages outside of this block to reclaim this block. */ + if (mapped_pages <= (nand_flash -> lx_nand_flash_free_pages - free_pages)) + { + + /* Now search through the list to find mapped logical sectors to move. */ + for (i = 1; i < nand_flash -> lx_nand_flash_pages_per_block; i++) + { + + /* Read the logical sector mapping for this page. */ + status = _lx_nand_flash_driver_extra_bytes_get(nand_flash, erase_block, i, (UCHAR *) &old_extra_info, sizeof(old_extra_info)); + + /* Check for an error from flash driver. */ + if (status) + { + + /* Call system error handler. */ + _lx_nand_flash_system_error(nand_flash, status, erase_block, i); + + /* Return the error. */ + return(status); + } + + /* Determine if the entry hasn't been used. */ + if (old_extra_info.lx_nand_page_extra_info_logical_sector == LX_NAND_PAGE_FREE) + { + + /* Since allocations are done sequentially in the block, we know nothing + else exists after this point. */ + break; + } + + /* Is this entry mapped? */ + if (old_extra_info.lx_nand_page_extra_info_logical_sector & LX_NAND_PAGE_VALID) + { + + /* Pickup the logical sector associated with this mapped page. */ + logical_sector = old_extra_info.lx_nand_page_extra_info_logical_sector & LX_NAND_LOGICAL_SECTOR_MASK; + + /* Invalidate the old logical sector mapping cache entry. */ + _lx_nand_flash_sector_mapping_cache_invalidate(nand_flash, logical_sector); + + /* Allocate a new page for this write. */ + _lx_nand_flash_physical_page_allocate(nand_flash, &new_block, &new_page, &new_erase_count); + + /* Check to see if the new block is the same as the block we are trying to reclaim. */ + if (new_block == erase_block) + { + + /* Yes, the new page was found in the block to be erased. Simply move the search pointer + to the block after the erase block and search for another page from there. */ + nand_flash -> lx_nand_flash_free_block_search = erase_block + 1; + + /* Check for wrap condition. */ + if (nand_flash -> lx_nand_flash_free_block_search >= nand_flash -> lx_nand_flash_total_blocks) + nand_flash -> lx_nand_flash_free_block_search = 0; + + /* Allocate new page again. */ + _lx_nand_flash_physical_page_allocate(nand_flash, &new_block, &new_page, &new_erase_count); + + /* Check again for the new page inside of the block to erase. This should be impossible, since + we check previously if there are enough free pages outside of this block needed to reclaim + this block. */ + if (new_block == erase_block) + { + + /* System error, a new page is not available outside of the erase block. + Clear the new page so we fall through to the error handling. */ + new_page = 0; + } + } + + /* Determine if the new page allocation was successful. */ + if (new_page) + { + + /* Yes, we were able to allocate a new page for the logical sector. */ + + /* Read the existing page into the internal buffer. */ + status = _lx_nand_flash_driver_read(nand_flash, erase_block, i, page_word_ptr, nand_flash -> lx_nand_flash_words_per_page); + + /* Check for an error from flash driver. */ + if (status) + { + + /* Call system error handler. */ + _lx_nand_flash_system_error(nand_flash, status, erase_block, i); + + /* Determine if the error is fatal. */ + if (status != LX_NAND_ERROR_CORRECTED) + { + + /* Return the error. */ + return(status); + } + } + + /* Write the logical sector data to the new page. */ + status = _lx_nand_flash_driver_write(nand_flash, new_block, new_page, page_word_ptr, nand_flash -> lx_nand_flash_words_per_page); + + /* Check for an error from flash driver. */ + if (status) + { + + /* Call system error handler. */ + _lx_nand_flash_system_error(nand_flash, status, new_block, new_page); + + /* Return the error. */ + return(status); + } + + /* Now deprecate the old logical sector mapping. */ + + /* Clear bit 30, which indicates this page is being superceded. */ + old_extra_info.lx_nand_page_extra_info_logical_sector = old_extra_info.lx_nand_page_extra_info_logical_sector & ~((ULONG) LX_NAND_PAGE_SUPERCEDED); + + /* Write the value back to the flash to clear bit 30. */ + status = _lx_nand_flash_driver_extra_bytes_set(nand_flash, erase_block, i, (UCHAR *) &old_extra_info, sizeof(old_extra_info)); + + /* Check for an error from flash driver. */ + if (status) + { + + /* Call system error handler. */ + _lx_nand_flash_system_error(nand_flash, status, erase_block, i); + + /* Return the error. */ + return(status); + } + + /* Now build the new mapping entry - with the not valid bit set initially. */ + new_extra_info.lx_nand_page_extra_info_logical_sector = ((ULONG) LX_NAND_PAGE_VALID) | ((ULONG) LX_NAND_PAGE_SUPERCEDED) | ((ULONG) LX_NAND_PAGE_MAPPING_NOT_VALID) | logical_sector; + + /* Write out the new mapping entry. */ + status = _lx_nand_flash_driver_extra_bytes_set(nand_flash, new_block, new_page, (UCHAR *) &new_extra_info, sizeof(new_extra_info)); + + /* Check for an error from flash driver. */ + if (status) + { + + /* Call system error handler. */ + _lx_nand_flash_system_error(nand_flash, status, new_block, new_page); + + /* Return the error. */ + return(status); + } + + /* Now clear the not valid bit to make this sector mapping valid. This is done because the writing of the extra bytes itself can + be interrupted and we need to make sure this can be detected when the flash is opened again. */ + new_extra_info.lx_nand_page_extra_info_logical_sector = new_extra_info.lx_nand_page_extra_info_logical_sector & ~((ULONG) LX_NAND_PAGE_MAPPING_NOT_VALID); + + /* Clear the not valid bit. */ + status = _lx_nand_flash_driver_extra_bytes_set(nand_flash, new_block, new_page, (UCHAR *) &new_extra_info, sizeof(new_extra_info)); + + /* Check for an error from flash driver. */ + if (status) + { + + /* Call system error handler. */ + _lx_nand_flash_system_error(nand_flash, status, new_block, new_page); + + /* Return the error. */ + return(status); + } + +#ifdef LX_NAND_FLASH_DIRECT_MAPPING_CACHE + + /* Determine if this logical sector fits in the logical sector direct cache mapping. */ + if (logical_sector < LX_NAND_SECTOR_MAPPING_CACHE_SIZE) + { + + /* Remember the mapping for this logical sector. */ + nand_flash -> lx_nand_flash_sector_mapping_cache[logical_sector].lx_nand_sector_mapping_cache_block = (USHORT) new_block; + nand_flash -> lx_nand_flash_sector_mapping_cache[logical_sector].lx_nand_sector_mapping_cache_page = (USHORT) new_page; + } +#endif + + /* Now clear bit 31, which indicates this sector is now obsoleted. */ + old_extra_info.lx_nand_page_extra_info_logical_sector = old_extra_info.lx_nand_page_extra_info_logical_sector & ~((ULONG) LX_NAND_PAGE_VALID); + + /* Write the value back to the flash to clear bit 31. */ + status = _lx_nand_flash_driver_extra_bytes_set(nand_flash, erase_block, i, (UCHAR *) &old_extra_info, sizeof(old_extra_info)); + + /* Check for an error from flash driver. */ + if (status) + { + + /* Call system error handler. */ + _lx_nand_flash_system_error(nand_flash, status, erase_block, i); + + /* Return the error. */ + return(status); + } + + /* Increment the number of moved pages. */ + nand_flash -> lx_nand_flash_diagnostic_moved_pages++; + + /* Determine if the new page is the last page of the block. */ + if (new_page == (nand_flash -> lx_nand_flash_pages_per_block - 1)) + { + + /* Yes, we need to update page 0 of the block with the list of mapped + pages for this block. */ + _lx_nand_flash_block_full_update(nand_flash, new_block, new_erase_count); + } + } + else + { + + /* Call system error handler - the allocation should always succeed at this point. */ + _lx_nand_flash_system_error(nand_flash, LX_SYSTEM_ALLOCATION_FAILED, erase_block, i); + + /* System error... break! */ + break; + } + + /* Decrement the number of mapped pages. */ + mapped_pages--; + + /* Determine if we are done. */ + if (mapped_pages == 0) + break; + } + } + + /* Read page 0 of the block, which has the erase count in the first 4 bytes. */ + status = _lx_nand_flash_driver_read(nand_flash, erase_block, 0, page_word_ptr, (nand_flash -> lx_nand_flash_pages_per_block + 1)); + + /* Check for an error from flash driver. */ + if (status) + { + + /* Call system error handler. */ + _lx_nand_flash_system_error(nand_flash, status, erase_block, 0); + } + + /* Write the erased started indication. */ + page_word_ptr[0] = LX_BLOCK_ERASE_STARTED; + status = _lx_nand_flash_driver_write(nand_flash, erase_block, 0, page_word_ptr, LX_NAND_ERASE_COUNT_WRITE_SIZE); + + /* Check for an error from flash driver. */ + if (status) + { + + /* Call system error handler. */ + _lx_nand_flash_system_error(nand_flash, status, erase_block, 0); + } + + /* Erase the entire block. */ + status = _lx_nand_flash_driver_block_erase(nand_flash, erase_block, erase_count+1); + + /* Check for an error from flash driver. */ + if (status) + { + + /* Call system error handler. */ + _lx_nand_flash_system_error(nand_flash, status, erase_block, 0); + + /* Attempt to mark this block as bad. */ + status = _lx_nand_flash_driver_block_status_set(nand_flash, erase_block, LX_NAND_BAD_BLOCK); + + /* Check for error in setting the block status. */ + if (status) + { + + /* Call system error handler. */ + _lx_nand_flash_system_error(nand_flash, status, erase_block, 0); + } + + /* Increment the bad block count. */ + nand_flash -> lx_nand_flash_bad_blocks++; + + /* Update number of obsolete pages but not free pages since this block is now bad. */ + nand_flash -> lx_nand_flash_obsolete_pages = nand_flash -> lx_nand_flash_obsolete_pages - obsolete_pages; + } + else + { + + /* Increment the erase count. */ + erase_count++; + + /* Determine if the new erase count exceeds the maximum. */ + if (erase_count > ((ULONG) LX_BLOCK_ERASE_COUNT_MAX)) + { + + /* Yes, erase count is in overflow. Stay at the maximum count. */ + erase_count = ((ULONG) LX_BLOCK_ERASE_COUNT_MAX); + } + + /* Determine if we need to update the maximum erase count. */ + if (erase_count > nand_flash -> lx_nand_flash_maximum_erase_count) + { + + /* Yes, a new maximum is present. */ + nand_flash -> lx_nand_flash_maximum_erase_count = erase_count; + } + + /* Set the buffer to all ones. */ + for (i = 0; i < nand_flash -> lx_nand_flash_words_per_page; i++) + { + + /* Set word to all ones. */ + page_word_ptr[i] = LX_ALL_ONES; + } + + /* Now store the erase count. */ + page_word_ptr[0] = (erase_count | LX_BLOCK_ERASED); + + /* Write the erase count for the block. */ + status = _lx_nand_flash_driver_write(nand_flash, erase_block, 0, page_word_ptr, LX_NAND_ERASE_COUNT_WRITE_SIZE); + + /* Determine if the write was successful. */ + if (status == LX_SUCCESS) + { + + /* Now store the erase count. */ + page_word_ptr[0] = erase_count; + + /* Write the erase count for the block. */ + status = _lx_nand_flash_driver_write(nand_flash, erase_block, 0, page_word_ptr, LX_NAND_ERASE_COUNT_WRITE_SIZE); + } + + /* Check for an error from flash driver. */ + if (status) + { + + /* Call system error handler. */ + _lx_nand_flash_system_error(nand_flash, status, erase_block, 0); + + /* Attempt to mark this block as bad. */ + status = _lx_nand_flash_driver_block_status_set(nand_flash, erase_block, LX_NAND_BAD_BLOCK); + + /* Check for error in setting the block status. */ + if (status) + { + + /* Call system error handler. */ + _lx_nand_flash_system_error(nand_flash, status, erase_block, 0); + + /* Return the error. */ + return(status); + } + + /* Increment the bad block count. */ + nand_flash -> lx_nand_flash_bad_blocks++; + + /* Update number of obsolete pages but not free pages since this block is now bad. */ + nand_flash -> lx_nand_flash_obsolete_pages = nand_flash -> lx_nand_flash_obsolete_pages - obsolete_pages; + } + else + { + + /* Update parameters of this flash. */ + nand_flash -> lx_nand_flash_free_pages = nand_flash -> lx_nand_flash_free_pages + obsolete_pages; + nand_flash -> lx_nand_flash_obsolete_pages = nand_flash -> lx_nand_flash_obsolete_pages - obsolete_pages; + } + } + } + } + + /* Return status. */ + return(LX_SUCCESS); +} + diff --git a/common/src/lx_nand_flash_close.c b/common/src/lx_nand_flash_close.c new file mode 100644 index 0000000..db32145 --- /dev/null +++ b/common/src/lx_nand_flash_close.c @@ -0,0 +1,129 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** LevelX Component */ +/** */ +/** NAND Flash */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define LX_SOURCE_CODE + + +/* Disable ThreadX error checking. */ + +#ifndef TX_DISABLE_ERROR_CHECKING +#define TX_DISABLE_ERROR_CHECKING +#endif + + +/* Include necessary system files. */ + +#include "lx_api.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _lx_nand_flash_close PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function closes a NAND flash instance. */ +/* */ +/* INPUT */ +/* */ +/* nand_flash NAND flash instance */ +/* */ +/* OUTPUT */ +/* */ +/* return status */ +/* */ +/* CALLS */ +/* */ +/* tx_mutex_delete Delete thread-safe mutex */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 William E. Lamie Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _lx_nand_flash_close(LX_NAND_FLASH *nand_flash) +{ + +TX_INTERRUPT_SAVE_AREA + + + /* Lockout interrupts for NAND flash close. */ + TX_DISABLE + + /* See if the media is the only one on the media opened list. */ + if ((_lx_nand_flash_opened_ptr == nand_flash) && + (_lx_nand_flash_opened_ptr == nand_flash -> lx_nand_flash_open_next) && + (_lx_nand_flash_opened_ptr == nand_flash -> lx_nand_flash_open_previous)) + { + + /* Only opened NAND flash, just set the opened list to NULL. */ + _lx_nand_flash_opened_ptr = LX_NULL; + } + else + { + + /* Otherwise, not the only opened NAND flash, link-up the neighbors. */ + (nand_flash -> lx_nand_flash_open_next) -> lx_nand_flash_open_previous = + nand_flash -> lx_nand_flash_open_previous; + (nand_flash -> lx_nand_flash_open_previous) -> lx_nand_flash_open_next = + nand_flash -> lx_nand_flash_open_next; + + /* See if we have to update the opened list head pointer. */ + if (_lx_nand_flash_opened_ptr == nand_flash) + { + + /* Yes, move the head pointer to the next opened NAND flash. */ + _lx_nand_flash_opened_ptr = nand_flash -> lx_nand_flash_open_next; + } + } + + /* Decrement the opened NAND flash counter. */ + _lx_nand_flash_opened_count--; + + /* Finally, indicate that this NAND flash is closed. */ + nand_flash -> lx_nand_flash_state = LX_NAND_FLASH_CLOSED; + + /* Restore interrupt posture. */ + TX_RESTORE + +#ifdef LX_THREAD_SAFE_ENABLE + + /* Delete the thread safe mutex. */ + tx_mutex_delete(&nand_flash -> lx_nand_flash_mutex); +#endif + /* Return success. */ + return(LX_SUCCESS); +} + + diff --git a/common/src/lx_nand_flash_defragment.c b/common/src/lx_nand_flash_defragment.c new file mode 100644 index 0000000..18b7bfa --- /dev/null +++ b/common/src/lx_nand_flash_defragment.c @@ -0,0 +1,112 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** LevelX Component */ +/** */ +/** NAND Flash */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define LX_SOURCE_CODE + + +/* Disable ThreadX error checking. */ + +#ifndef TX_DISABLE_ERROR_CHECKING +#define TX_DISABLE_ERROR_CHECKING +#endif + + +/* Include necessary system files. */ + +#include "lx_api.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _lx_nand_flash_defragment PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function defragments the NAND flash. */ +/* */ +/* INPUT */ +/* */ +/* nand_flash NAND flash instance */ +/* */ +/* OUTPUT */ +/* */ +/* return status */ +/* */ +/* CALLS */ +/* */ +/* _lx_nand_flash_block_reclaim Reclaim a NAND flash block */ +/* tx_mutex_get Get thread protection */ +/* tx_mutex_put Release thread protection */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* Internal LevelX */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 William E. Lamie Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _lx_nand_flash_defragment(LX_NAND_FLASH *nand_flash) +{ + +ULONG i; + + +#ifdef LX_THREAD_SAFE_ENABLE + + /* Obtain the thread safe mutex. */ + tx_mutex_get(&nand_flash -> lx_nand_flash_mutex, TX_WAIT_FOREVER); +#endif + + /* Loop for max number of blocks, while there are obsolete count. */ + for (i = 0; i < nand_flash -> lx_nand_flash_total_blocks; i++) + { + + /* Determine if there is any more defragment work. */ + if (nand_flash -> lx_nand_flash_obsolete_pages == 0) + break; + + /* Call the block reclaim function to defragment. */ + _lx_nand_flash_block_reclaim(nand_flash); + } + +#ifdef LX_THREAD_SAFE_ENABLE + + /* Release the thread safe mutex. */ + tx_mutex_put(&nand_flash -> lx_nand_flash_mutex); +#endif + + /* Return successful completion. */ + return(LX_SUCCESS); +} + + diff --git a/common/src/lx_nand_flash_driver_block_erase.c b/common/src/lx_nand_flash_driver_block_erase.c new file mode 100644 index 0000000..e0e7dbb --- /dev/null +++ b/common/src/lx_nand_flash_driver_block_erase.c @@ -0,0 +1,137 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** LevelX Component */ +/** */ +/** NAND Flash */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define LX_SOURCE_CODE + + +/* Disable ThreadX error checking. */ + +#ifndef TX_DISABLE_ERROR_CHECKING +#define TX_DISABLE_ERROR_CHECKING +#endif + + +/* Include necessary system files. */ + +#include "lx_api.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _lx_nand_flash_driver_block_erase PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function calls the driver to erase a block. */ +/* */ +/* INPUT */ +/* */ +/* nand_flash NAND flash instance */ +/* block Block number */ +/* erase_count Erase count for this block */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* (lx_nand_flash_driver_block_erase) Driver erase block */ +/* */ +/* CALLED BY */ +/* */ +/* Internal LevelX */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 William E. Lamie Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _lx_nand_flash_driver_block_erase(LX_NAND_FLASH *nand_flash, ULONG block, ULONG erase_count) +{ + +ULONG cache_index; +ULONG i; +UINT status; + + + /* Determine if the block status cache is enabled. */ + if (nand_flash -> lx_nand_flash_block_status_cache != LX_NULL) + { + + /* Save the block status value in the cache. */ + nand_flash -> lx_nand_flash_block_status_cache[block] = 0xFF; + } + + /* Determine if the page extra byte cache is enabled. */ + if (nand_flash -> lx_nand_flash_page_extra_bytes_cache != LX_NULL) + { + + /* Calculate the cache index. */ + cache_index = (block * nand_flash -> lx_nand_flash_pages_per_block); + + /* Loop to clear the entries in the page extra bytes cache. */ + for (i = 0; i < nand_flash -> lx_nand_flash_pages_per_block; i++) + { + + /* Clear each cache entry. */ + nand_flash -> lx_nand_flash_page_extra_bytes_cache[cache_index+i].lx_nand_page_extra_info_logical_sector = (ULONG) 0xFFFFFFFF; + } + } + + /* Determine if the page 0 cache is enabled. */ + if (nand_flash -> lx_nand_flash_page_0_cache != LX_NULL) + { + + /* Yes, the page 0 cache is enabled. */ + + /* Build index to page 0 cache. */ + cache_index = block * (nand_flash -> lx_nand_flash_pages_per_block + 1); + + /* Clear the associated page 0 cache. */ + for (i = 0; i < (nand_flash -> lx_nand_flash_pages_per_block + 1); i++) + { + + /* Clear each cache entry. */ + nand_flash -> lx_nand_flash_page_0_cache[cache_index+i] = (ULONG) 0xFFFFFFFF; + } + } + + /* Increment the block erases count. */ + nand_flash -> lx_nand_flash_diagnostic_block_erases++; + + /* Call driver erase block function. */ + status = (nand_flash -> lx_nand_flash_driver_block_erase)(block, erase_count); + + /* Return status. */ + return(status); +} + + diff --git a/common/src/lx_nand_flash_driver_block_erased_verify.c b/common/src/lx_nand_flash_driver_block_erased_verify.c new file mode 100644 index 0000000..92958b4 --- /dev/null +++ b/common/src/lx_nand_flash_driver_block_erased_verify.c @@ -0,0 +1,93 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** LevelX Component */ +/** */ +/** NAND Flash */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define LX_SOURCE_CODE + + +/* Disable ThreadX error checking. */ + +#ifndef TX_DISABLE_ERROR_CHECKING +#define TX_DISABLE_ERROR_CHECKING +#endif + + +/* Include necessary system files. */ + +#include "lx_api.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _lx_nand_flash_driver_block_erased_verify PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function calls the driver to verify the block was erased. */ +/* */ +/* INPUT */ +/* */ +/* nand_flash NAND flash instance */ +/* block Block number */ +/* page Page number */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* (lx_nand_flash_driver_block_erased_verify) */ +/* Driver verify page erased */ +/* */ +/* CALLED BY */ +/* */ +/* Internal LevelX */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 William E. Lamie Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _lx_nand_flash_driver_block_erased_verify(LX_NAND_FLASH *nand_flash, ULONG block) +{ + +UINT status; + + /* Increment the block erased verify count. */ + nand_flash -> lx_nand_flash_diagnostic_block_erased_verifies++; + + /* Call driver block erased verify function. */ + status = (nand_flash -> lx_nand_flash_driver_block_erased_verify)(block); + + /* Return status. */ + return(status); +} + + diff --git a/common/src/lx_nand_flash_driver_block_status_get.c b/common/src/lx_nand_flash_driver_block_status_get.c new file mode 100644 index 0000000..3138759 --- /dev/null +++ b/common/src/lx_nand_flash_driver_block_status_get.c @@ -0,0 +1,132 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** LevelX Component */ +/** */ +/** NAND Flash */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define LX_SOURCE_CODE + + +/* Disable ThreadX error checking. */ + +#ifndef TX_DISABLE_ERROR_CHECKING +#define TX_DISABLE_ERROR_CHECKING +#endif + + +/* Include necessary system files. */ + +#include "lx_api.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _lx_nand_flash_driver_block_status_get PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function calls the driver to get the block status and */ +/* updates the internal cache. */ +/* */ +/* INPUT */ +/* */ +/* nand_flash NAND flash instance */ +/* block Block number */ +/* bad_block_flag Pointer to Bad block flag */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* (lx_nand_flash_driver_block_status_get) */ +/* NAND flash block status get */ +/* */ +/* CALLED BY */ +/* */ +/* Internal LevelX */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 William E. Lamie Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _lx_nand_flash_driver_block_status_get(LX_NAND_FLASH *nand_flash, ULONG block, UCHAR *bad_block_flag) +{ + +UINT status; + + + /* Determine if the block status cache is disabled. */ + if (nand_flash -> lx_nand_flash_block_status_cache == LX_NULL) + { + + /* Increment the block status get count. */ + nand_flash -> lx_nand_flash_diagnostic_block_status_gets++; + + /* Call driver block status get function. */ + status = (nand_flash -> lx_nand_flash_driver_block_status_get)(block, bad_block_flag); + } + else + { + + /* Determine if the block status cache entry is valid. */ + if (nand_flash -> lx_nand_flash_block_status_cache[block] != 0) + { + + /* Simply return this value. */ + *bad_block_flag = nand_flash -> lx_nand_flash_block_status_cache[block]; + + /* Increment the number of status byte cache hits. */ + nand_flash -> lx_nand_flash_diagnostic_block_status_cache_hits++; + + /* Return successful status. */ + status = LX_SUCCESS; + } + else + { + + /* Increment the block status get count. */ + nand_flash -> lx_nand_flash_diagnostic_block_status_gets++; + + /* Call driver block status get function. */ + status = (nand_flash -> lx_nand_flash_driver_block_status_get)(block, bad_block_flag); + + /* Increment the number of status byte cache misses. */ + nand_flash -> lx_nand_flash_diagnostic_block_status_cache_misses++; + + /* Save the block status value in the cache. */ + nand_flash -> lx_nand_flash_block_status_cache[block] = *bad_block_flag; + } + } + + /* Return status. */ + return(status); +} + + diff --git a/common/src/lx_nand_flash_driver_block_status_set.c b/common/src/lx_nand_flash_driver_block_status_set.c new file mode 100644 index 0000000..2bdd4d0 --- /dev/null +++ b/common/src/lx_nand_flash_driver_block_status_set.c @@ -0,0 +1,103 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** LevelX Component */ +/** */ +/** NAND Flash */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define LX_SOURCE_CODE + + +/* Disable ThreadX error checking. */ + +#ifndef TX_DISABLE_ERROR_CHECKING +#define TX_DISABLE_ERROR_CHECKING +#endif + + +/* Include necessary system files. */ + +#include "lx_api.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _lx_nand_flash_driver_block_status_set PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function calls the driver to set the block status and */ +/* updates the internal cache. */ +/* */ +/* INPUT */ +/* */ +/* nand_flash NAND flash instance */ +/* block Block number */ +/* bad_block_flag Bad block flag */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* (lx_nand_flash_driver_block_status_set) */ +/* NAND flash block status set */ +/* */ +/* CALLED BY */ +/* */ +/* Internal LevelX */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 William E. Lamie Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _lx_nand_flash_driver_block_status_set(LX_NAND_FLASH *nand_flash, ULONG block, UCHAR bad_block_flag) +{ + +UINT status; + + + /* Determine if the block status cache is enabled. */ + if (nand_flash -> lx_nand_flash_block_status_cache != LX_NULL) + { + + /* Save the block status value in the cache. */ + nand_flash -> lx_nand_flash_block_status_cache[block] = bad_block_flag; + } + + /* Increment the block status set count. */ + nand_flash -> lx_nand_flash_diagnostic_block_status_sets++; + + /* Call driver block status set function. */ + status = (nand_flash -> lx_nand_flash_driver_block_status_set)(block, bad_block_flag); + + /* Return status. */ + return(status); +} + + diff --git a/common/src/lx_nand_flash_driver_extra_bytes_get.c b/common/src/lx_nand_flash_driver_extra_bytes_get.c new file mode 100644 index 0000000..df481f6 --- /dev/null +++ b/common/src/lx_nand_flash_driver_extra_bytes_get.c @@ -0,0 +1,148 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** LevelX Component */ +/** */ +/** NAND Flash */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define LX_SOURCE_CODE + + +/* Disable ThreadX error checking. */ + +#ifndef TX_DISABLE_ERROR_CHECKING +#define TX_DISABLE_ERROR_CHECKING +#endif + + +/* Include necessary system files. */ + +#include "lx_api.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _lx_nand_flash_driver_extra_bytes_get PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function calls the driver to get the extra bytes of a NAND */ +/* page. */ +/* */ +/* INPUT */ +/* */ +/* nand_flash NAND flash instance */ +/* block Block number */ +/* page Page number */ +/* destination Pointer to destination buffer */ +/* words Number of words to read */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* (lx_nand_flash_driver_extra_bytes_get)Get extra bytes from spare */ +/* */ +/* CALLED BY */ +/* */ +/* Internal LevelX */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 William E. Lamie Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _lx_nand_flash_driver_extra_bytes_get(LX_NAND_FLASH *nand_flash, ULONG block, ULONG page, UCHAR *destination, UINT size) +{ + +ULONG *source_ptr; +ULONG *destination_ptr; +ULONG cache_index; +UINT status; + + + /* Determine if the page extra bytes cache is disabled. */ + if (nand_flash -> lx_nand_flash_page_extra_bytes_cache == LX_NULL) + { + + /* Increment the page extra bytes get count. */ + nand_flash -> lx_nand_flash_diagnostic_page_extra_bytes_gets++; + + /* Call driver extra bytes get function. */ + status = (nand_flash -> lx_nand_flash_driver_extra_bytes_get)(block, page, destination, size); + } + else + { + + /* Calculate the cache index. */ + cache_index = (block * nand_flash -> lx_nand_flash_pages_per_block) + page; + + /* Setup the destination pointer. */ + destination_ptr = (ULONG *) destination; + + /* Determine if this cache entry is valid. */ + if (nand_flash -> lx_nand_flash_page_extra_bytes_cache[cache_index].lx_nand_page_extra_info_logical_sector != 0) + { + + /* Simply return this value. */ + *destination_ptr = nand_flash -> lx_nand_flash_page_extra_bytes_cache[cache_index].lx_nand_page_extra_info_logical_sector; + + /* Increment the number of page extra bytes cache hits. */ + nand_flash -> lx_nand_flash_diagnostic_page_extra_bytes_cache_hits++; + + /* Return successful status. */ + status = LX_SUCCESS; + } + else + { + + /* Increment the page extra bytes get count. */ + nand_flash -> lx_nand_flash_diagnostic_page_extra_bytes_gets++; + + /* Call driver extra bytes get function. */ + status = (nand_flash -> lx_nand_flash_driver_extra_bytes_get)(block, page, destination, size); + + /* Increment the number of page extra bytes cache misses. */ + nand_flash -> lx_nand_flash_diagnostic_page_extra_bytes_cache_misses++; + + /* Setup destination pointer. */ + destination_ptr = &nand_flash -> lx_nand_flash_page_extra_bytes_cache[cache_index].lx_nand_page_extra_info_logical_sector; + + /* Setup source pointer. */ + source_ptr = (ULONG *) destination; + + /* Save the value in the page extra bytes cache. */ + *destination_ptr = *source_ptr; + } + } + + /* Return status. */ + return(status); +} + + diff --git a/common/src/lx_nand_flash_driver_extra_bytes_set.c b/common/src/lx_nand_flash_driver_extra_bytes_set.c new file mode 100644 index 0000000..778ced3 --- /dev/null +++ b/common/src/lx_nand_flash_driver_extra_bytes_set.c @@ -0,0 +1,113 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** LevelX Component */ +/** */ +/** NAND Flash */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define LX_SOURCE_CODE + + +/* Disable ThreadX error checking. */ + +#ifndef TX_DISABLE_ERROR_CHECKING +#define TX_DISABLE_ERROR_CHECKING +#endif + + +/* Include necessary system files. */ + +#include "lx_api.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _lx_nand_flash_driver_extra_bytes_set PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function calls the driver extra bytes set operation and */ +/* updates the internal cache. */ +/* */ +/* INPUT */ +/* */ +/* nand_flash NAND flash instance */ +/* block Block number */ +/* page Page number */ +/* source Pointer to source extra bytes */ +/* size Number of extra bytes */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* (lx_nand_flash_driver_extra_bytes_set) */ +/* NAND flash set extra bytes */ +/* */ +/* CALLED BY */ +/* */ +/* Internal LevelX */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 William E. Lamie Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _lx_nand_flash_driver_extra_bytes_set(LX_NAND_FLASH *nand_flash, ULONG block, ULONG page, UCHAR *source, UINT size) +{ + +ULONG cache_index; +ULONG *destination_ptr; +UINT status; + + + /* Increment the page extra bytes set count. */ + nand_flash -> lx_nand_flash_diagnostic_page_extra_bytes_sets++; + + /* Call driver extra bytes set function. */ + status = (nand_flash -> lx_nand_flash_driver_extra_bytes_set)(block, page, source, size); + + /* Determine if the page extra bytes set cache is enabled. */ + if (nand_flash -> lx_nand_flash_page_extra_bytes_cache != LX_NULL) + { + + /* Calculate the cache index. */ + cache_index = (block * nand_flash -> lx_nand_flash_pages_per_block) + page; + + /* Build destination address. */ + destination_ptr = &nand_flash -> lx_nand_flash_page_extra_bytes_cache[cache_index].lx_nand_page_extra_info_logical_sector; + + /* Now save this in the cache. */ + *destination_ptr = *((ULONG *) source); + } + + /* Return status. */ + return(status); +} + + diff --git a/common/src/lx_nand_flash_driver_page_erased_verify.c b/common/src/lx_nand_flash_driver_page_erased_verify.c new file mode 100644 index 0000000..7129e92 --- /dev/null +++ b/common/src/lx_nand_flash_driver_page_erased_verify.c @@ -0,0 +1,92 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** LevelX Component */ +/** */ +/** NAND Flash */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define LX_SOURCE_CODE + + +/* Disable ThreadX error checking. */ + +#ifndef TX_DISABLE_ERROR_CHECKING +#define TX_DISABLE_ERROR_CHECKING +#endif + + +/* Include necessary system files. */ + +#include "lx_api.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _lx_nand_flash_driver_page_erased_verify PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function calls the driver to verify the page was erased. */ +/* */ +/* INPUT */ +/* */ +/* nand_flash NAND flash instance */ +/* block Block number */ +/* page Page number */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* (lx_nand_flash_driver_page_erased_verify) */ +/* Driver verify page erased */ +/* */ +/* CALLED BY */ +/* */ +/* Internal LevelX */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 William E. Lamie Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _lx_nand_flash_driver_page_erased_verify(LX_NAND_FLASH *nand_flash, ULONG block, ULONG page) +{ + +UINT status; + + /* Increment the page erased verify count. */ + nand_flash -> lx_nand_flash_diagnostic_page_erased_verifies++; + + /* Call driver page erased verify function. */ + status = (nand_flash -> lx_nand_flash_driver_page_erased_verify)(block, page); + + /* Return status. */ + return(status); +} + diff --git a/common/src/lx_nand_flash_driver_read.c b/common/src/lx_nand_flash_driver_read.c new file mode 100644 index 0000000..28ef74b --- /dev/null +++ b/common/src/lx_nand_flash_driver_read.c @@ -0,0 +1,180 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** LevelX Component */ +/** */ +/** NAND Flash */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define LX_SOURCE_CODE + + +/* Disable ThreadX error checking. */ + +#ifndef TX_DISABLE_ERROR_CHECKING +#define TX_DISABLE_ERROR_CHECKING +#endif + + +/* Include necessary system files. */ + +#include "lx_api.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _lx_nand_flash_driver_read PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function calls the driver to read data from a page. */ +/* */ +/* INPUT */ +/* */ +/* nand_flash NAND flash instance */ +/* block Block number */ +/* page Page number */ +/* destination Pointer to destination buffer */ +/* words Number of words to read */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* (lx_nand_flash_driver_read) Driver page read */ +/* */ +/* CALLED BY */ +/* */ +/* Internal LevelX */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 William E. Lamie Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _lx_nand_flash_driver_read(LX_NAND_FLASH *nand_flash, ULONG block, ULONG page, ULONG *destination, ULONG words) +{ + +ULONG cache_index; +ULONG *source_ptr; +ULONG *destination_ptr; +ULONG i; +UINT status; + + + /* Determine if this is page 0. */ + if (page == 0) + { + + /* Determine if the page 0 cache is enabled. */ + if (nand_flash -> lx_nand_flash_page_0_cache != LX_NULL) + { + + /* Yes, the page 0 cache is enabled. */ + + /* Calculate the cache index. */ + cache_index = (block * (nand_flash -> lx_nand_flash_pages_per_block + 1)); + + /* Determine if this cache entry is valid. */ + if (nand_flash -> lx_nand_flash_page_0_cache[cache_index] != 0) + { + + /* Setup the destination pointer. */ + destination_ptr = (ULONG *) destination; + + /* Setup the source pointer. */ + source_ptr = &nand_flash -> lx_nand_flash_page_0_cache[cache_index]; + + /* Simply copy the page 0 information to the destination. */ + i = 0; + while (i < (nand_flash -> lx_nand_flash_pages_per_block + 1)) + { + + /* Copy one word. */ + *destination_ptr++ = *source_ptr++; + + /* Move to next word. */ + i++; + } + + /* Increment the number of page 0 cache hits. */ + nand_flash -> lx_nand_flash_diagnostic_page_0_cache_hits++; + + /* Return successful status. */ + status = LX_SUCCESS; + } + else + { + + /* Increment the page read count. */ + nand_flash -> lx_nand_flash_diagnostic_page_reads++; + + /* Increment the number of page 0 cache misses. */ + nand_flash -> lx_nand_flash_diagnostic_page_0_cache_misses++; + + /* Call driver read function. */ + status = (nand_flash -> lx_nand_flash_driver_read)(block, page, destination, words); + + /* Setup destination pointer. */ + destination_ptr = &nand_flash -> lx_nand_flash_page_0_cache[cache_index]; + + /* Setup source pointer. */ + source_ptr = (ULONG *) destination; + + /* Simply copy the page 0 information to the destination. */ + for (i = 0; i < (nand_flash -> lx_nand_flash_pages_per_block + 1); i++) + { + + /* Copy one word. */ + *destination_ptr++ = *source_ptr++; + } + } + } + else + { + + /* Increment the page read count. */ + nand_flash -> lx_nand_flash_diagnostic_page_reads++; + + /* Call driver read function. */ + status = (nand_flash -> lx_nand_flash_driver_read)(block, page, destination, words); + } + } + else + { + + /* Increment the page read count. */ + nand_flash -> lx_nand_flash_diagnostic_page_reads++; + + /* Call driver read function. */ + status = (nand_flash -> lx_nand_flash_driver_read)(block, page, destination, words); + } + + /* Return status. */ + return(status); +} + diff --git a/common/src/lx_nand_flash_driver_write.c b/common/src/lx_nand_flash_driver_write.c new file mode 100644 index 0000000..e2eb34a --- /dev/null +++ b/common/src/lx_nand_flash_driver_write.c @@ -0,0 +1,127 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** LevelX Component */ +/** */ +/** NAND Flash */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define LX_SOURCE_CODE + + +/* Disable ThreadX error checking. */ + +#ifndef TX_DISABLE_ERROR_CHECKING +#define TX_DISABLE_ERROR_CHECKING +#endif + + +/* Include necessary system files. */ + +#include "lx_api.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _lx_nand_flash_driver_write PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function calls the driver to write data to a NAND page. */ +/* */ +/* INPUT */ +/* */ +/* nand_flash NAND flash instance */ +/* block Block number */ +/* page Page number */ +/* source Pointer to source buffer */ +/* words Number of words to write */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* (lx_nand_flash_driver_write) Driver page write */ +/* */ +/* CALLED BY */ +/* */ +/* Internal LevelX */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 William E. Lamie Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _lx_nand_flash_driver_write(LX_NAND_FLASH *nand_flash, ULONG block, ULONG page, ULONG *source, ULONG words) +{ + +ULONG cache_index; +ULONG *source_ptr; +ULONG *destination_ptr; +ULONG i; +UINT status; + + + /* Determine if this is page 0. */ + if (page == 0) + { + + /* Determine if the page 0 cache is enabled. */ + if (nand_flash -> lx_nand_flash_page_0_cache != LX_NULL) + { + + /* Yes, the page 0 cache is enabled. */ + + /* Calculate the cache index. */ + cache_index = (block * (nand_flash -> lx_nand_flash_pages_per_block + 1)); + + /* Setup destination pointer. */ + destination_ptr = &nand_flash -> lx_nand_flash_page_0_cache[cache_index]; + + /* Setup source pointer. */ + source_ptr = (ULONG *) source; + + /* Simply copy the page 0 information to the destination. */ + for (i = 0; i < (nand_flash -> lx_nand_flash_pages_per_block + 1); i++) + { + + /* Copy one word. */ + *destination_ptr++ = *source_ptr++; + } + } + } + + /* Increment the page write count. */ + nand_flash -> lx_nand_flash_diagnostic_page_writes++; + + /* Call driver write function. */ + status = (nand_flash -> lx_nand_flash_driver_write)(block, page, source, words); + + /* Return status. */ + return(status); +} + diff --git a/common/src/lx_nand_flash_extended_cache_enable.c b/common/src/lx_nand_flash_extended_cache_enable.c new file mode 100644 index 0000000..f838167 --- /dev/null +++ b/common/src/lx_nand_flash_extended_cache_enable.c @@ -0,0 +1,205 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** LevelX Component */ +/** */ +/** NAND Flash */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define LX_SOURCE_CODE + + +/* Disable ThreadX error checking. */ + +#ifndef TX_DISABLE_ERROR_CHECKING +#define TX_DISABLE_ERROR_CHECKING +#endif + + +/* Include necessary system files. */ + +#include "lx_api.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _lx_nand_flash_extended_cache_enable PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function sets up the extended NAND cache for block status, */ +/* page extra bytes, and page 0 contents. The routine will enable as */ +/* many cache capabilities as possible until the supplied memory is */ +/* exhausted. */ +/* */ +/* INPUT */ +/* */ +/* nand_flash NAND flash instance */ +/* memory Pointer to memory for caches */ +/* size Size of memory in bytes */ +/* */ +/* OUTPUT */ +/* */ +/* Completion Status */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* Internal LevelX */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 William E. Lamie Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _lx_nand_flash_extended_cache_enable(LX_NAND_FLASH *nand_flash, VOID *memory, ULONG size) +{ + +UCHAR *working_ptr; +ULONG adjust_size; +ULONG i; + + + /* Setup the working pointer. */ + working_ptr = (UCHAR *) memory; + + /* Check for a NULL pointer. If NULL, disable all the caches. */ + if (working_ptr == TX_NULL) + { + + /* Disable all extended caches. */ + nand_flash -> lx_nand_flash_block_status_cache = TX_NULL; + nand_flash -> lx_nand_flash_page_extra_bytes_cache = TX_NULL; + nand_flash -> lx_nand_flash_page_0_cache = TX_NULL; + + /* Return success! */ + return(LX_SUCCESS); + } + + /* Determine if there is not enough memory for the block status cache. */ + if (size < nand_flash -> lx_nand_flash_total_blocks) + { + + /* Return an error since there is not enough memory for even the smallest + cache. */ + return(LX_ERROR); + } + + /* There is enough memory, setup the block status cache. */ + nand_flash -> lx_nand_flash_block_status_cache = working_ptr; + + /* Clear the block status cache. */ + for (i = 0; i < nand_flash -> lx_nand_flash_total_blocks; i++) + { + + /* Clear the block status cache. */ + nand_flash -> lx_nand_flash_block_status_cache[i] = ((UCHAR) 0); + } + + /* Calculate the size to adjust to achieve ULONG alignment. */ + adjust_size = nand_flash -> lx_nand_flash_total_blocks + ((ULONG) (sizeof(ULONG) - 1)); + adjust_size = (adjust_size/sizeof(ULONG)) * sizeof(ULONG); + + /* Adjust the size. */ + if (size >= adjust_size) + { + + /* Subtract the aligned size. */ + size = size - adjust_size; + } + else + { + + /* Just set size to 0. */ + size = 0; + } + + /* Determine if there is enough memory for the page extra bytes cache for each page. */ + if (size >= (nand_flash -> lx_nand_flash_total_pages * sizeof(LX_NAND_PAGE_EXTRA_INFO))) + { + + /* Yes, there is enough memory for the extra bytes cache. */ + + /* First adjust the working pointer to the start of this memory. */ + working_ptr = working_ptr + adjust_size; + + /* Setup the pointer to page extra bytes cache. */ + nand_flash -> lx_nand_flash_page_extra_bytes_cache = (LX_NAND_PAGE_EXTRA_INFO *) working_ptr; + + /* Clear the page extra bytes cache. */ + for (i = 0; i < nand_flash -> lx_nand_flash_total_pages; i++) + { + + /* Clear the block status cache. */ + nand_flash -> lx_nand_flash_page_extra_bytes_cache[i].lx_nand_page_extra_info_logical_sector = ((ULONG) 0); + } + + /* Calculate the size to adjust to achieve ULONG alignment. */ + adjust_size = (nand_flash -> lx_nand_flash_total_pages * sizeof(LX_NAND_PAGE_EXTRA_INFO) + ((ULONG) (sizeof(ULONG) - 1))); + adjust_size = (adjust_size/sizeof(ULONG)) * sizeof(ULONG); + + /* Adjust the size. */ + if (size >= adjust_size) + { + + /* Subtract the aligned size. */ + size = size - adjust_size; + } + else + { + + /* Just set size to 0. */ + size = 0; + } + } + + /* Determine if there is enough memory for the page 0 cache for each block. */ + if (size >= ((nand_flash -> lx_nand_flash_total_blocks * (nand_flash -> lx_nand_flash_pages_per_block + 1)) * sizeof(ULONG))) + { + + /* Yes, there is enough memory for the page 0 cache. */ + + /* First adjust the working pointer to the start of this memory. */ + working_ptr = working_ptr + adjust_size; + + /* Setup the pointer to page 0 cache. */ + nand_flash -> lx_nand_flash_page_0_cache = (ULONG *) working_ptr; + + /* Clear the page 0 cache. */ + for (i = 0; i < (nand_flash -> lx_nand_flash_total_blocks * (nand_flash -> lx_nand_flash_pages_per_block + 1)); i++) + { + + /* Clear the block status cache. */ + nand_flash -> lx_nand_flash_page_0_cache[i] = ((ULONG) 0); + } + } + + /* Return success. */ + return(LX_SUCCESS); +} + diff --git a/common/src/lx_nand_flash_initialize.c b/common/src/lx_nand_flash_initialize.c new file mode 100644 index 0000000..ef64285 --- /dev/null +++ b/common/src/lx_nand_flash_initialize.c @@ -0,0 +1,84 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** LevelX Component */ +/** */ +/** NAND Flash */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define LX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "lx_api.h" + + +/* Define the NAND flash opened list head and opened count. */ + +LX_NAND_FLASH *_lx_nand_flash_opened_ptr; +ULONG _lx_nand_flash_opened_count; + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _lx_nand_flash_initialize PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function initializes the NAND flash data structures. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* return status */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 William E. Lamie Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _lx_nand_flash_initialize(void) +{ + + /* Clear open list head pointer and opened count. */ + _lx_nand_flash_opened_ptr = LX_NULL; + _lx_nand_flash_opened_count = 0; + + /* Return success! */ + return(LX_SUCCESS); +} + diff --git a/common/src/lx_nand_flash_logical_sector_find.c b/common/src/lx_nand_flash_logical_sector_find.c new file mode 100644 index 0000000..28abf18 --- /dev/null +++ b/common/src/lx_nand_flash_logical_sector_find.c @@ -0,0 +1,837 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** LevelX Component */ +/** */ +/** NAND Flash */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define LX_SOURCE_CODE + + +/* Disable ThreadX error checking. */ + +#ifndef TX_DISABLE_ERROR_CHECKING +#define TX_DISABLE_ERROR_CHECKING +#endif + + +/* Include necessary system files. */ + +#include "lx_api.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _lx_nand_flash_logical_sector_find PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function attempts to find the specified logical sector in */ +/* the NAND flash. */ +/* */ +/* INPUT */ +/* */ +/* nand_flash NAND flash instance */ +/* logical_sector Logical sector number */ +/* superceded_check Check for page being */ +/* superceded (can happen if */ +/* on interruptions of page */ +/* write) */ +/* block Destination for block */ +/* page Destination for page */ +/* */ +/* OUTPUT */ +/* */ +/* return status */ +/* */ +/* CALLS */ +/* */ +/* _lx_nand_flash_driver_block_status_get */ +/* Driver block status */ +/* _lx_nand_flash_driver_extra_bytes_get Driver get extra bytes */ +/* _lx_nand_flash_driver_extra_bytes_set NAND flash set extra bytes */ +/* _lx_nand_flash_driver_read Driver page read */ +/* _lx_nand_flash_system_error Internal system error handler */ +/* */ +/* CALLED BY */ +/* */ +/* Internal LevelX */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 William E. Lamie Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _lx_nand_flash_logical_sector_find(LX_NAND_FLASH *nand_flash, ULONG logical_sector, ULONG superceded_check, ULONG *block, ULONG *page) +{ + +LX_NAND_PAGE_EXTRA_INFO extra_info; +UCHAR block_status; +ULONG *block_word_ptr; +ULONG valid_pages; +ULONG mapped_pages; +ULONG total_blocks; +ULONG total_pages; +ULONG i, j; +ULONG search_start; +ULONG max_mapped_sector; +#ifndef LX_NAND_FLASH_DIRECT_MAPPING_CACHE +LX_NAND_SECTOR_MAPPING_CACHE_ENTRY *sector_mapping_cache_entry_ptr = LX_NULL; +LX_NAND_SECTOR_MAPPING_CACHE_ENTRY temp_sector_mapping_cache_entry; +#endif +UINT status; + + + /* Initialize the return parameters. */ + *block = (ULONG) 0; + *page = (ULONG) 0; + + /* Determine if there are any mapped pages. */ + if (nand_flash -> lx_nand_flash_mapped_pages == 0) + { + + /* No mapped pages so nothing can be found!. */ + return(LX_SECTOR_NOT_FOUND); + } + + /* Determine if this logical sector is greater than what has previously been mapped. */ + if (logical_sector > nand_flash -> lx_nand_flash_max_mapped_sector) + { + + /* Since this logical sector request is greater than what has been mapped previously, + there is no point in searching any further. */ + + /* Return sector not found status. */ + return(LX_SECTOR_NOT_FOUND); + } + + /* Determine if the sector mapping cache is enabled. */ + if (nand_flash -> lx_nand_flash_sector_mapping_cache_enabled) + { + +#ifndef LX_NAND_FLASH_DIRECT_MAPPING_CACHE + + /* Calculate the starting index of the sector cache for this sector entry. */ + i = (logical_sector & LX_NAND_SECTOR_MAPPING_CACHE_HASH_MASK) * LX_NAND_SECTOR_MAPPING_CACHE_DEPTH; + + /* Build a pointer to the cache entry. */ + sector_mapping_cache_entry_ptr = &nand_flash -> lx_nand_flash_sector_mapping_cache[i]; + + /* Determine if the sector is in the sector mapping cache - assuming the depth of the sector + mapping cache is LX_NAND_SECTOR_MAPPING_CACHE_DEPTH entries. */ + if ((sector_mapping_cache_entry_ptr -> lx_nand_sector_mapping_cache_logical_sector) == (logical_sector | LX_NAND_SECTOR_MAPPING_CACHE_ENTRY_VALID)) + { + + /* Increment the sector mapping cache hit counter. */ + nand_flash -> lx_nand_flash_diagnostic_sector_mapping_cache_hits++; + + /* Yes, return the cached values associated with the sector. */ + *block = (ULONG) sector_mapping_cache_entry_ptr -> lx_nand_sector_mapping_cache_block; + *page = (ULONG) sector_mapping_cache_entry_ptr -> lx_nand_sector_mapping_cache_page; + + /* Don't move anything since we found the entry at the top. */ + + /* Return a successful status. */ + return(LX_SUCCESS); + } + else if (((sector_mapping_cache_entry_ptr + 1) -> lx_nand_sector_mapping_cache_logical_sector) == (logical_sector | LX_NAND_SECTOR_MAPPING_CACHE_ENTRY_VALID)) + { + + /* Increment the sector mapping cache hit counter. */ + nand_flash -> lx_nand_flash_diagnostic_sector_mapping_cache_hits++; + + /* Yes, return the cached values associated with the sector. */ + *block = (ULONG) (sector_mapping_cache_entry_ptr + 1) -> lx_nand_sector_mapping_cache_block; + *page = (ULONG) (sector_mapping_cache_entry_ptr + 1) -> lx_nand_sector_mapping_cache_page; + + /* Just swap the first and second entry. */ + temp_sector_mapping_cache_entry = *(sector_mapping_cache_entry_ptr); + *(sector_mapping_cache_entry_ptr) = *(sector_mapping_cache_entry_ptr + 1); + *(sector_mapping_cache_entry_ptr + 1) = temp_sector_mapping_cache_entry; + + /* Return a successful status. */ + return(LX_SUCCESS); + } + else if (((sector_mapping_cache_entry_ptr + 2) -> lx_nand_sector_mapping_cache_logical_sector) == (logical_sector | LX_NAND_SECTOR_MAPPING_CACHE_ENTRY_VALID)) + { + + /* Increment the sector mapping cache hit counter. */ + nand_flash -> lx_nand_flash_diagnostic_sector_mapping_cache_hits++; + + /* Yes, return the cached value. */ + *block = (ULONG) (sector_mapping_cache_entry_ptr + 2) -> lx_nand_sector_mapping_cache_block; + *page = (ULONG) (sector_mapping_cache_entry_ptr + 2) -> lx_nand_sector_mapping_cache_page; + + /* Move the third entry to the top and the first two entries down. */ + temp_sector_mapping_cache_entry = *(sector_mapping_cache_entry_ptr); + *(sector_mapping_cache_entry_ptr) = *(sector_mapping_cache_entry_ptr + 2); + *(sector_mapping_cache_entry_ptr + 2) = *(sector_mapping_cache_entry_ptr + 1); + *(sector_mapping_cache_entry_ptr + 1) = temp_sector_mapping_cache_entry; + + /* Return a successful status. */ + return(LX_SUCCESS); + } + else if (((sector_mapping_cache_entry_ptr + 3) -> lx_nand_sector_mapping_cache_logical_sector) == (logical_sector | LX_NAND_SECTOR_MAPPING_CACHE_ENTRY_VALID)) + { + + /* Increment the sector mapping cache hit counter. */ + nand_flash -> lx_nand_flash_diagnostic_sector_mapping_cache_hits++; + + /* Yes, return the cached value. */ + *block = (ULONG) (sector_mapping_cache_entry_ptr + 3) -> lx_nand_sector_mapping_cache_block; + *page = (ULONG) (sector_mapping_cache_entry_ptr + 3) -> lx_nand_sector_mapping_cache_page; + + /* Move the last entry to the top and the first three entries down. */ + temp_sector_mapping_cache_entry = *(sector_mapping_cache_entry_ptr); + *(sector_mapping_cache_entry_ptr) = *(sector_mapping_cache_entry_ptr + 3); + *(sector_mapping_cache_entry_ptr + 3) = *(sector_mapping_cache_entry_ptr + 2); + *(sector_mapping_cache_entry_ptr + 2) = *(sector_mapping_cache_entry_ptr + 1); + *(sector_mapping_cache_entry_ptr + 1) = temp_sector_mapping_cache_entry; + + /* Return a successful status. */ + return(LX_SUCCESS); + } + + /* If we get here, we have a cache miss so increment the counter before we fall through to the search loop. */ + nand_flash -> lx_nand_flash_diagnostic_sector_mapping_cache_misses++; +#else + + /* Direct mapping cache is defined. */ + + /* See if we are still opening the flash. */ + if (nand_flash -> lx_nand_flash_state == LX_NAND_FLASH_OPENED) + { + + /* Flash instance is opened. */ + + /* Determine if this logical sector fits in the logical sector direct cache mapping. */ + if (logical_sector < LX_NAND_SECTOR_MAPPING_CACHE_SIZE) + { + + /* Yes, invalidate the logical sector cache. */ + if ((nand_flash -> lx_nand_flash_sector_mapping_cache[logical_sector].lx_nand_sector_mapping_cache_block == 0) && + (nand_flash -> lx_nand_flash_sector_mapping_cache[logical_sector].lx_nand_sector_mapping_cache_page == 0)) + { + + /* Entry is not mapped. */ + + /* No, return sector not found status. */ + return(LX_SECTOR_NOT_FOUND); + } + else + { + + /* Sector is mapped, return the block and page of the mapping. */ + *block = nand_flash -> lx_nand_flash_sector_mapping_cache[logical_sector].lx_nand_sector_mapping_cache_block; + *page = nand_flash -> lx_nand_flash_sector_mapping_cache[logical_sector].lx_nand_sector_mapping_cache_page; + + /* Return a successful status. */ + return(LX_SUCCESS); + } + } + } + else + { + + /* If we get here, we have a cache miss so increment the counter before we fall through to the search loop. */ + nand_flash -> lx_nand_flash_diagnostic_sector_mapping_cache_misses++; + } +#endif + } + + /* Clear the maximum mapped sector. */ + max_mapped_sector = 0; + + /* Setup the total number of mapped pages. */ + mapped_pages = nand_flash -> lx_nand_flash_mapped_pages; + + /* Setup pointer to internal buffer. */ + block_word_ptr = nand_flash -> lx_nand_flash_page_buffer; + + /* Start searching from the last found block. */ + i = nand_flash -> lx_nand_flash_found_block_search; + + /* Setup the starting page to look at. */ + j = nand_flash -> lx_nand_flash_found_page_search; + + /* Pickup the total number of blocks. */ + total_blocks = nand_flash -> lx_nand_flash_total_blocks; + + /* Loop through the blocks to attempt to find the mapped logical sector. */ + while (total_blocks--) + { + + /* First, check to make sure this block is good. */ + status = _lx_nand_flash_driver_block_status_get(nand_flash, i, &block_status); + + /* Check for an error from flash driver. */ + if (status) + { + + /* Call system error handler. */ + _lx_nand_flash_system_error(nand_flash, status, i, 0); + + /* Return the error. */ + return(status); + } + + /* Is this block bad? */ + if (block_status != LX_NAND_GOOD_BLOCK) + { + + /* Yes, this block is bad. */ + + /* Move to the next block. */ + i++; + + /* Check for wrap condition. */ + if (i >= nand_flash -> lx_nand_flash_total_blocks) + { + + /* Wrap to the first block. */ + i = 0; + } + + /* Start looking at the first page of the next block. */ + j = 1; + + /* Continue to the next block. */ + continue; + } + + /* Read the extra bytes of page 0. This will tell us if the page has any valid mappings. */ + status = _lx_nand_flash_driver_extra_bytes_get(nand_flash, i, 0, (UCHAR *) &extra_info, sizeof(extra_info)); + + /* Check for an error from flash driver. */ + if (status) + { + + /* Call system error handler. */ + _lx_nand_flash_system_error(nand_flash, status, i, 0); + + /* Return the error... no point in continuing. */ + return(status); + } + + /* Determine if there are any valid mappings. */ + if (((extra_info.lx_nand_page_extra_info_logical_sector & LX_NAND_BLOCK_VALID) == 0) || + ((extra_info.lx_nand_page_extra_info_logical_sector & LX_NAND_BLOCK_EMPTY) != 0)) + { + + /* Yes, this block is either no longer valid or is empty, i.e., no mappings. */ + + /* Move to the next block. */ + i++; + + /* Check for wrap condition. */ + if (i >= nand_flash -> lx_nand_flash_total_blocks) + { + + /* Wrap to the first block. */ + i = 0; + } + + /* Reset to the first page of the next block. */ + j = 1; + + /* Continue to the next block. */ + continue; + } + + /* Now read page 0 of the block, which has the erase count in the first 4 bytes. */ + status = _lx_nand_flash_driver_read(nand_flash, i, 0, block_word_ptr, (nand_flash -> lx_nand_flash_pages_per_block + 1)); + + /* Check for an error from flash driver. */ + if (status) + { + + /* Call system error handler. */ + _lx_nand_flash_system_error(nand_flash, status, i, 0); + + /* Determine if the error is fatal. */ + if (status != LX_NAND_ERROR_CORRECTED) + { + + /* Return the error. */ + return(status); + } + } + + /* Determine if we have a valid logical sector mapping list. */ + if ((block_word_ptr[1] != LX_NAND_PAGE_FREE) && + (block_word_ptr[nand_flash -> lx_nand_flash_pages_per_block] == LX_NAND_PAGE_LIST_VALID)) + { + + /* Yes, we have a valid logical sector mapping list in page 0. */ + + /* Clear the valid entries counter. */ + valid_pages = 0; + + /* Setup the total number of pages, less the metadata page. */ + total_pages = nand_flash -> lx_nand_flash_pages_per_block - 1; + + /* Now search through the sector list to find a match. */ + while (total_pages--) + { + + /* Is this entry valid? */ + if ((block_word_ptr[j] & (LX_NAND_PAGE_VALID | LX_NAND_PAGE_MAPPING_NOT_VALID)) == LX_NAND_PAGE_VALID) + { + + /* Increment the valid entries counter. */ + valid_pages++; + + /* Determine if this is a new maximum logical sector. */ + if ((block_word_ptr[j] & LX_NAND_LOGICAL_SECTOR_MASK) > max_mapped_sector) + { + + /* Yes, a new maximum mapped sectors - remember it! */ + max_mapped_sector = (block_word_ptr[j] & LX_NAND_LOGICAL_SECTOR_MASK); + } + + /* Do we have a valid sector match? */ + if ((block_word_ptr[j] & LX_NAND_LOGICAL_SECTOR_MASK) == logical_sector) + { + + /* Read in the actual page entry to make sure this logical sector mapping is still valid. */ + status = _lx_nand_flash_driver_extra_bytes_get(nand_flash, i, j, (UCHAR *) &extra_info, sizeof(extra_info)); + + /* Check for an error from flash driver. */ + if (status) + { + + /* Call system error handler. */ + _lx_nand_flash_system_error(nand_flash, status, i, j); + + /* Return the error. */ + return(status); + } + + /* Verify that the actual page still has a valid sector mapping. */ + if (((extra_info.lx_nand_page_extra_info_logical_sector & (LX_NAND_PAGE_VALID | LX_NAND_PAGE_MAPPING_NOT_VALID)) == LX_NAND_PAGE_VALID) && + ((extra_info.lx_nand_page_extra_info_logical_sector & LX_NAND_LOGICAL_SECTOR_MASK) == logical_sector)) + { + + /* Decrement the number of mapped pages to examine. */ + mapped_pages--; + + /* Determine if we care about the superceded bit. */ + if (superceded_check == LX_FALSE) + { + + /* Prepare the return information. */ + *block = i; + *page = j; + + /* Determine if the sector mapping cache is enabled. */ + if (nand_flash -> lx_nand_flash_sector_mapping_cache_enabled) + { + +#ifndef LX_NAND_FLASH_DIRECT_MAPPING_CACHE + + /* Yes, update the cache with the logical sector mapping. */ + + /* Move all the cache entries down so the oldest is at the bottom. */ + *(sector_mapping_cache_entry_ptr + 3) = *(sector_mapping_cache_entry_ptr + 2); + *(sector_mapping_cache_entry_ptr + 2) = *(sector_mapping_cache_entry_ptr + 1); + *(sector_mapping_cache_entry_ptr + 1) = *(sector_mapping_cache_entry_ptr); + + /* Setup the new sector information in the cache. */ + sector_mapping_cache_entry_ptr -> lx_nand_sector_mapping_cache_logical_sector = (logical_sector | LX_NAND_SECTOR_MAPPING_CACHE_ENTRY_VALID); + sector_mapping_cache_entry_ptr -> lx_nand_sector_mapping_cache_block = (USHORT) i; + sector_mapping_cache_entry_ptr -> lx_nand_sector_mapping_cache_page = (USHORT) j; +#else + + /* Determine if this logical sector fits in the logical sector cache mapping. */ + if (logical_sector < LX_NAND_SECTOR_MAPPING_CACHE_SIZE) + { + + /* Yes, store the logical sector to block/page mapping in the cache. */ + nand_flash -> lx_nand_flash_sector_mapping_cache[logical_sector].lx_nand_sector_mapping_cache_block = (USHORT) i; + nand_flash -> lx_nand_flash_sector_mapping_cache[logical_sector].lx_nand_sector_mapping_cache_page = (USHORT) j; + } +#endif + } + + /* Move to the next page. */ + j++; + + /* Determine if the page has wrapped around. */ + if (j >= nand_flash -> lx_nand_flash_pages_per_block) + { + + /* Yes, page wrapped. Move to page 1 of the next block. */ + j = 1; + } + + /* Remember the last found block and page for next search. */ + nand_flash -> lx_nand_flash_found_block_search = i; + nand_flash -> lx_nand_flash_found_page_search = j; + + /* Return success! */ + return(LX_SUCCESS); + } + + /* Check for the superceded bit being clear, which means the sector was superceded. */ + else if (extra_info.lx_nand_page_extra_info_logical_sector & LX_NAND_PAGE_SUPERCEDED) + { + + /* Prepare the return information. */ + *block = i; + *page = j; + + /* No need to update the cache here, since this condition only happens during initialization. */ + + /* Move to the next page. */ + j++; + + /* Determine if the page has wrapped around. */ + if (j >= nand_flash -> lx_nand_flash_pages_per_block) + { + + /* Yes, page wrapped. Move to page 1 of the next block. */ + j = 1; + } + + /* Remember the last found block and page for next search. */ + nand_flash -> lx_nand_flash_found_block_search = i; + nand_flash -> lx_nand_flash_found_page_search = j; + + /* Return success! */ + return(LX_SUCCESS); + } + } + else + { + + /* The entry is not really valid anymore, decrement the valid entries counter. */ + valid_pages--; + } + } + } + + /* Move to the next page. */ + j++; + + /* Check for wrap condition. */ + if (j >= nand_flash -> lx_nand_flash_pages_per_block) + { + + /* Yes, page has wrapped, reset to page 1. */ + j = 1; + } + } + + /* At this point we know that nothing matched in the mapping list. Now we need to check to see if there are any valid entries. */ + if (valid_pages) + { + + /* Reset the valid pages counter. */ + valid_pages = 0; + + /* Setup the total number of pages, less the metadata page. */ + total_pages = nand_flash -> lx_nand_flash_pages_per_block - 1; + + /* Now search through the sector list to find a match. */ + while (total_pages--) + { + + /* Is this entry valid? */ + if ((block_word_ptr[j] & (LX_NAND_PAGE_VALID | LX_NAND_PAGE_MAPPING_NOT_VALID)) == LX_NAND_PAGE_VALID) + { + + /* Read in the actual page entry to make sure this logical sector mapping is still valid. */ + status = _lx_nand_flash_driver_extra_bytes_get(nand_flash, i, j, (UCHAR *) &extra_info, sizeof(extra_info)); + + /* Check for an error from flash driver. */ + if (status) + { + + /* Call system error handler. */ + _lx_nand_flash_system_error(nand_flash, status, i, j); + + /* Return the error. */ + return(status); + } + + /* Verify that the actual page still has a valid sector mapping. */ + if ((extra_info.lx_nand_page_extra_info_logical_sector & (LX_NAND_PAGE_VALID | LX_NAND_PAGE_MAPPING_NOT_VALID)) == LX_NAND_PAGE_VALID) + { + + /* Set the valid pages counter to 1 and break the loop. */ + valid_pages = 1; + + /* If one entry is valid, we can stop looking. */ + break; + } + } + + /* Move to the next page. */ + j++; + + /* Check for wrap condition. */ + if (j >= nand_flash -> lx_nand_flash_pages_per_block) + { + + /* Yes, page has wrapped, reset to page 1. */ + j = 1; + } + } + } + + /* Now, a final check for valid pages. */ + if (valid_pages == 0) + { + + /* There are no valid entries. We can now mark this block as not valid since there are no active + mappings. This will prevent us from examining it again during the search process. */ + extra_info.lx_nand_page_extra_info_logical_sector = 0; + status = _lx_nand_flash_driver_extra_bytes_set(nand_flash, i, 0, (UCHAR *) &extra_info, sizeof(extra_info)); + + /* Check for an error from flash driver. */ + if (status) + { + + /* Call system error handler. */ + _lx_nand_flash_system_error(nand_flash, status, i, 0); + } + + /* Reset the search page to 1. */ + j = 1; + } + } + else + { + + /* No, we don't have a valid logical sector mapping list. */ + + /* Setup the total number of pages, less the metadata page. */ + total_pages = nand_flash -> lx_nand_flash_pages_per_block - 1; + + /* Remember the start of the search. */ + search_start = j; + + /* Now search through the sector list to find a match. */ + while (total_pages--) + { + + /* Read in the actual page entry to look for the logical sector mapping. */ + status = _lx_nand_flash_driver_extra_bytes_get(nand_flash, i, j, (UCHAR *) &extra_info, sizeof(extra_info)); + + /* Check for an error from flash driver. */ + if (status) + { + + /* Call system error handler. */ + _lx_nand_flash_system_error(nand_flash, status, i, j); + + /* Return the error. */ + return(status); + } + + /* Is there a logical page mapping? */ + if (extra_info.lx_nand_page_extra_info_logical_sector == LX_NAND_PAGE_FREE) + { + + /* Since the mapping is done sequentially in the block, we know nothing + else exists after this point. */ + + /* Determine if the search started at the beginning of the block. */ + if (search_start == 1) + { + + /* Yes, we started at the beginning of the block. We are now done with this block. */ + break; + } + else + { + + /* Setup the new total to the search start. */ + total_pages = (search_start - 1); + + /* Clear search start. */ + search_start = 1; + + /* Start search over. */ + j = 1; + continue; + } + } + + /* Is this page mapping valid? */ + if ((extra_info.lx_nand_page_extra_info_logical_sector & (LX_NAND_PAGE_VALID | LX_NAND_PAGE_MAPPING_NOT_VALID)) == LX_NAND_PAGE_VALID) + { + + /* Yes, this page mapping is valid. */ + + /* Decrease the number of pages to examine. */ + mapped_pages--; + + + /* Determine if this is a new maximum logical sector. */ + if ((extra_info.lx_nand_page_extra_info_logical_sector & LX_NAND_LOGICAL_SECTOR_MASK) > max_mapped_sector) + { + + /* Yes, a new maximum mapped sectors - remember it! */ + max_mapped_sector = (extra_info.lx_nand_page_extra_info_logical_sector & LX_NAND_LOGICAL_SECTOR_MASK); + } + + /* Do we have a valid sector match? */ + if ((extra_info.lx_nand_page_extra_info_logical_sector & LX_NAND_LOGICAL_SECTOR_MASK) == logical_sector) + { + + /* Determine if we care about the superceded bit. */ + if (superceded_check == LX_FALSE) + { + + /* Prepare the return information. */ + *block = i; + *page = j; + + /* Determine if the sector mapping cache is enabled. */ + if (nand_flash -> lx_nand_flash_sector_mapping_cache_enabled) + { + + /* Yes, update the cache with the logical sector mapping. */ + +#ifndef LX_NAND_FLASH_DIRECT_MAPPING_CACHE + + /* Move all the cache entries down so the oldest is at the bottom. */ + *(sector_mapping_cache_entry_ptr + 3) = *(sector_mapping_cache_entry_ptr + 2); + *(sector_mapping_cache_entry_ptr + 2) = *(sector_mapping_cache_entry_ptr + 1); + *(sector_mapping_cache_entry_ptr + 1) = *(sector_mapping_cache_entry_ptr); + + /* Setup the new sector information in the cache. */ + sector_mapping_cache_entry_ptr -> lx_nand_sector_mapping_cache_logical_sector = (logical_sector | LX_NAND_SECTOR_MAPPING_CACHE_ENTRY_VALID); + sector_mapping_cache_entry_ptr -> lx_nand_sector_mapping_cache_block = (USHORT) i; + sector_mapping_cache_entry_ptr -> lx_nand_sector_mapping_cache_page = (USHORT) j; +#else + + /* Determine if this logical sector fits in the logical sector cache mapping. */ + if (logical_sector < LX_NAND_SECTOR_MAPPING_CACHE_SIZE) + { + + /* Yes, store the logical sector to block/page mapping in the cache. */ + nand_flash -> lx_nand_flash_sector_mapping_cache[logical_sector].lx_nand_sector_mapping_cache_block = (USHORT) i; + nand_flash -> lx_nand_flash_sector_mapping_cache[logical_sector].lx_nand_sector_mapping_cache_page = (USHORT) j; + } +#endif + } + + /* Move to the next page. */ + j++; + + /* Determine if the page has wrapped around. */ + if (j >= nand_flash -> lx_nand_flash_pages_per_block) + { + + /* Yes, page wrapped - move to page 1. */ + j = 1; + } + + /* Remember the last found block and page for next search. */ + nand_flash -> lx_nand_flash_found_block_search = i; + nand_flash -> lx_nand_flash_found_page_search = j; + + /* Return success! */ + return(LX_SUCCESS); + } + + /* Check for the superceded bit being clear, which means the sector was superceded. */ + else if (extra_info.lx_nand_page_extra_info_logical_sector & LX_NAND_PAGE_SUPERCEDED) + { + + /* Prepare the return information. */ + *block = i; + *page = j; + + /* No need to update the cache here, since this condition only happens during initialization. */ + + /* Move to the next page. */ + j++; + + /* Determine if the page has wrapped around. */ + if (j >= nand_flash -> lx_nand_flash_pages_per_block) + { + + /* Yes, page wrapped - move to page 1. */ + j = 1; + } + + /* Remember the last found block and page for next search. */ + nand_flash -> lx_nand_flash_found_block_search = i; + nand_flash -> lx_nand_flash_found_page_search = j; + + /* Return success! */ + return(LX_SUCCESS); + } + } + } + + /* Move to next page. */ + j++; + + /* Determine if we have a wrap-around condition. */ + if (j >= nand_flash -> lx_nand_flash_pages_per_block) + { + + /* Yes, page wrapped - move to page 1. */ + j = 1; + } + } + } + + /* Determine if there are any more mapped sectors. */ + if (mapped_pages == 0) + break; + + /* Move to the next block. */ + i++; + + /* Check for wrap condition. */ + if (i >= nand_flash -> lx_nand_flash_total_blocks) + { + + /* Wrap to the first block. */ + i = 0; + } + + /* Start at the first page in the next block. */ + j = 1; + } + + /* Determine if we should update the maximum mapped sector. */ + if (nand_flash -> lx_nand_flash_max_mapped_sector != 0xFFFFFFFF) + { + + /* Yes, update the maximum mapped sector. */ + nand_flash -> lx_nand_flash_max_mapped_sector = max_mapped_sector; + } + + /* Return sector not found status. */ + return(LX_SECTOR_NOT_FOUND); +} + diff --git a/common/src/lx_nand_flash_next_block_to_erase_find.c b/common/src/lx_nand_flash_next_block_to_erase_find.c new file mode 100644 index 0000000..78b43fc --- /dev/null +++ b/common/src/lx_nand_flash_next_block_to_erase_find.c @@ -0,0 +1,301 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** LevelX Component */ +/** */ +/** NAND Flash */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define LX_SOURCE_CODE + + +/* Disable ThreadX error checking. */ + +#ifndef TX_DISABLE_ERROR_CHECKING +#define TX_DISABLE_ERROR_CHECKING +#endif + + +/* Include necessary system files. */ + +#include "lx_api.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _lx_nand_flash_next_block_to_erase_find PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function finds the next block to erase in the NAND flash. */ +/* */ +/* INPUT */ +/* */ +/* nand_flash NAND flash instance */ +/* return_erase_block Returned block to erase */ +/* return_erase_count Returned erase count of block */ +/* return_mapped_pages Returned number of mapped */ +/* pages */ +/* return_obsolete_pages Returned number of obsolete */ +/* pages */ +/* */ +/* OUTPUT */ +/* */ +/* return status */ +/* */ +/* CALLS */ +/* */ +/* _lx_nand_flash_driver_extra_bytes_get Driver block extra bytes get */ +/* _lx_nand_flash_driver_block_status_get */ +/* Driver block status get */ +/* _lx_nand_flash_driver_read Driver page read */ +/* _lx_nand_flash_system_error Internal system error handler */ +/* */ +/* CALLED BY */ +/* */ +/* Internal LevelX */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 William E. Lamie Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _lx_nand_flash_next_block_to_erase_find(LX_NAND_FLASH *nand_flash, ULONG *return_erase_block, ULONG *return_erase_count, ULONG *return_mapped_pages, ULONG *return_obsolete_pages) +{ + +LX_NAND_PAGE_EXTRA_INFO extra_info; +UCHAR block_status; +ULONG *block_word_ptr; +ULONG block; +ULONG page; +ULONG mapped_pages; +ULONG erase_count; +ULONG obsolete_pages; +ULONG min_block_erase = 0; +ULONG min_block_erase_count = 0; +ULONG min_block_obsolete_count = 0; +ULONG min_block_mapped_count = 0; +ULONG max_obsolete_pages = 0; +ULONG max_obsolete_block = 0; +ULONG max_obsolete_erase_count = 0; +ULONG max_obsolete_mapped_count = 0; +ULONG min_system_block_erase_count = 0; +ULONG max_system_block_erase_count = 0; +ULONG erase_count_threshold = 0; +UINT status; + + + /* Initialize the minimum erase count. */ + min_block_erase_count = LX_ALL_ONES; + + /* Initialize the system minimum and maximum erase counts. */ + min_system_block_erase_count = LX_ALL_ONES; + max_system_block_erase_count = 0; + + /* Initialize the maximum obsolete page count. */ + max_obsolete_pages = 0; + + /* Calculate the erase count threshold. */ + if (nand_flash -> lx_nand_flash_free_pages >= nand_flash -> lx_nand_flash_pages_per_block) + { + + /* Calculate erase count threshold by adding constant to the current minimum. */ + erase_count_threshold = nand_flash -> lx_nand_flash_minimum_erase_count + LX_NAND_FLASH_MAX_ERASE_COUNT_DELTA; + } + else + { + + /* When the number of free pages is low, simply pick the block that has the most number of obsolete sectors. */ + erase_count_threshold = LX_ALL_ONES; + } + + /* Set the block word pointer to the internal buffer. */ + block_word_ptr = nand_flash -> lx_nand_flash_page_buffer; + + /* Loop through the blocks to attempt to find the block to erase. */ + for (block = 0; block < nand_flash -> lx_nand_flash_total_blocks; block++) + { + + /* First, check to see if this block is good. */ + status = _lx_nand_flash_driver_block_status_get(nand_flash, block, &block_status); + + /* Check for an error from flash driver. */ + if (status) + { + + /* Call system error handler. */ + _lx_nand_flash_system_error(nand_flash, status, block, 0); + + /* Return the error. */ + return(status); + } + + /* Determine if this block is bad. */ + if (block_status != LX_NAND_GOOD_BLOCK) + { + + /* Block is bad, move to the next block. */ + continue; + } + + /* Read page 0 of the block, the first word has the erase count. */ + status = _lx_nand_flash_driver_read(nand_flash, block, 0, block_word_ptr, (nand_flash -> lx_nand_flash_pages_per_block + 1)); + + /* Check for an error from flash driver. */ + if (status) + { + + /* Call system error handler. */ + _lx_nand_flash_system_error(nand_flash, status, block, 0); + + /* Determine if the error is fatal. */ + if (status != LX_NAND_ERROR_CORRECTED) + { + + /* Return the error. */ + return(status); + } + } + + /* Pickup the erase count from the first word of the block. */ + erase_count = block_word_ptr[0]; + + /* Update the system minimum and maximum erase counts. */ + if (erase_count < min_system_block_erase_count) + min_system_block_erase_count = erase_count; + if (erase_count > max_system_block_erase_count) + max_system_block_erase_count = erase_count; + + /* Compute the number of obsolete and mapped pages for this block. */ + obsolete_pages = 0; + mapped_pages = 0; + + /* Loop through the pages for this block. */ + for (page = 1; page < nand_flash -> lx_nand_flash_pages_per_block; page++) + { + + /* Read the logical sector mapping for this page. */ + status = _lx_nand_flash_driver_extra_bytes_get(nand_flash, block, page, (UCHAR *) &extra_info, sizeof(extra_info)); + + /* Check for an error from flash driver. */ + if (status) + { + + /* Call system error handler. */ + _lx_nand_flash_system_error(nand_flash, status, block, page); + + /* Return the error. */ + return(status); + } + + /* Is this entry free? */ + if (extra_info.lx_nand_page_extra_info_logical_sector == LX_NAND_PAGE_FREE) + { + + /* Get out of the loop, we are done! */ + break; + } + + /* Is this entry obsolete? */ + else if ((extra_info.lx_nand_page_extra_info_logical_sector & LX_NAND_PAGE_VALID) == 0) + { + + /* Increment the number of obsolete pages. */ + obsolete_pages++; + } + else + { + + /* Increment the number of mapped pages. */ + mapped_pages++; + } + } + + /* Determine if we have a block with a new maximum number of obsolete pages. */ + if ((obsolete_pages > max_obsolete_pages) && (erase_count <= erase_count_threshold)) + { + + /* Update the new maximum obsolete pages and related information. */ + max_obsolete_pages = obsolete_pages; + max_obsolete_block = block; + max_obsolete_erase_count = erase_count; + max_obsolete_mapped_count = mapped_pages; + } + else if ((max_obsolete_pages) && (obsolete_pages == max_obsolete_pages) && (erase_count <= erase_count_threshold)) + { + + /* Another block has the same number of obsolete pages. Does this new block have a smaller erase + count? */ + if (erase_count < max_obsolete_erase_count) + { + + /* Yes, choose the block with the smaller erase count. */ + max_obsolete_pages = obsolete_pages; + max_obsolete_block = block; + max_obsolete_erase_count = erase_count; + max_obsolete_mapped_count = mapped_pages; + } + } + + /* Determine if we have a new minimum erase count. */ + if (erase_count < min_block_erase_count) + { + + /* Update the new minimum erase count and related information. */ + min_block_erase_count = erase_count; + min_block_erase = block; + min_block_obsolete_count = obsolete_pages; + min_block_mapped_count = mapped_pages; + } + } + + /* Determine if we can erase the block with the most obsolete pages. */ + if (max_obsolete_pages) + { + + /* Erase the block with the most obsolete pages. */ + *return_erase_block = max_obsolete_block; + *return_erase_count = max_obsolete_erase_count; + *return_obsolete_pages = max_obsolete_pages; + *return_mapped_pages = max_obsolete_mapped_count; + } + else + { + + /* Otherwise, erase the block with the smallest erase count. */ + *return_erase_block = min_block_erase; + *return_erase_count = min_block_erase_count; + *return_obsolete_pages = min_block_obsolete_count; + *return_mapped_pages = min_block_mapped_count; + } + + /* Update the overall minimum and maximum erase count. */ + nand_flash -> lx_nand_flash_minimum_erase_count = min_system_block_erase_count; + nand_flash -> lx_nand_flash_maximum_erase_count = max_system_block_erase_count; + + /* Return success. */ + return(LX_SUCCESS); +} + diff --git a/common/src/lx_nand_flash_open.c b/common/src/lx_nand_flash_open.c new file mode 100644 index 0000000..b538e4f --- /dev/null +++ b/common/src/lx_nand_flash_open.c @@ -0,0 +1,972 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** LevelX Component */ +/** */ +/** NAND Flash */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define LX_SOURCE_CODE + + +/* Disable ThreadX error checking. */ + +#ifndef TX_DISABLE_ERROR_CHECKING +#define TX_DISABLE_ERROR_CHECKING +#endif + + +/* Include necessary system files. */ + +#include "lx_api.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _lx_nand_flash_open PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function opens a NAND flash instance and ensures the */ +/* NAND flash is in a coherent state. */ +/* */ +/* INPUT */ +/* */ +/* nand_flash NAND flash instance */ +/* name Name of NAND flash instance */ +/* nand_driver_initialize Driver initialize */ +/* */ +/* OUTPUT */ +/* */ +/* return status */ +/* */ +/* CALLS */ +/* */ +/* (nand_driver_initialize) Driver initialize */ +/* _lx_nand_flash_driver_block_status_get */ +/* Driver block status get */ +/* _lx_nand_flash_driver_block_status_set */ +/* Driver block status set */ +/* _lx_nand_flash_driver_extra_bytes_get Driver get extra bytes */ +/* _lx_nand_flash_driver_extra_bytes_set Driver set extra bytes */ +/* _lx_nand_flash_driver_read Driver page read */ +/* _lx_nand_flash_driver_write Driver page write */ +/* _lx_nand_flash_driver_block_erased_verify */ +/* Driver verify block erased */ +/* _lx_nand_flash_driver_block_erase Driver block erase */ +/* _lx_nand_flash_driver_page_erased_verify */ +/* Driver verify page erased */ +/* _lx_nand_flash_block_full_update Write mapping to page 0 */ +/* _lx_nand_flash_logical_sector_find Find logical sector */ +/* _lx_nand_flash_system_error System error handler */ +/* tx_mutex_create Create thread-safe mutex */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 William E. Lamie Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _lx_nand_flash_open(LX_NAND_FLASH *nand_flash, CHAR *name, UINT (*nand_driver_initialize)(LX_NAND_FLASH *)) +{ + +LX_NAND_PAGE_EXTRA_INFO extra_info; +ULONG block; +ULONG page; +ULONG found_block, found_page; +UCHAR block_status; +ULONG *page_word_ptr; +ULONG temp; +ULONG free_pages; +ULONG obsolete_pages; +ULONG mapped_pages; +ULONG max_mapped_sector; +ULONG erased_count, min_erased_count, max_erased_count; +UINT status; +LX_NAND_FLASH *tail_ptr; +#ifdef LX_NAND_FLASH_DIRECT_MAPPING_CACHE +ULONG logical_sector; +#endif + +TX_INTERRUPT_SAVE_AREA + + LX_PARAMETER_NOT_USED(name); + + /* Clear the NAND flash control block. */ + TX_MEMSET(nand_flash, 0, sizeof(LX_NAND_FLASH)); + + /* Call the flash driver's initialization function. */ + (nand_driver_initialize)(nand_flash); + + /* Determine if we can support this NAND flash size. */ + if (((nand_flash -> lx_nand_flash_pages_per_block * sizeof(ULONG)) + 8) > nand_flash -> lx_nand_flash_bytes_per_page) + { + + /* Not enough room in page 0 to contain the erase count and logical sector mapping list. */ + + /* Return error. */ + return(LX_ERROR); + } + + /* Determine if the driver supplied a RAM buffer for reading the page. */ + if (nand_flash -> lx_nand_flash_page_buffer == LX_NULL) + { + + /* Return an error. */ + return(LX_NO_MEMORY); + } + + + /* Calculate the number of words per block and per page. */ + nand_flash -> lx_nand_flash_words_per_page = (nand_flash -> lx_nand_flash_bytes_per_page / sizeof(ULONG)); + nand_flash -> lx_nand_flash_words_per_block = (nand_flash -> lx_nand_flash_words_per_page * nand_flash -> lx_nand_flash_pages_per_block); + + /* Calculate the total pages. */ + nand_flash -> lx_nand_flash_total_pages = nand_flash -> lx_nand_flash_total_blocks * nand_flash -> lx_nand_flash_pages_per_block; + + /* Setup default values for the max/min erased counts. */ + min_erased_count = LX_ALL_ONES; + max_erased_count = 0; + + /* Default the max mapped sector to 0. */ + max_mapped_sector = 0; + + /* Setup the block word pointer to the internal buffer area. */ + page_word_ptr = nand_flash -> lx_nand_flash_page_buffer; + + /* Initialize the last found block and page search markers. */ + nand_flash -> lx_nand_flash_found_block_search = 0; + nand_flash -> lx_nand_flash_found_page_search = 1; + + /* Loop through the blocks to check for bad blocks and determine the minimum and maximum erase count for each good block. */ + for (block = 0; block < nand_flash -> lx_nand_flash_total_blocks; block++) + { + + /* First, check to make sure this block is good. */ + status = _lx_nand_flash_driver_block_status_get(nand_flash, block, &block_status); + + /* Check for an error from flash driver. */ + if (status) + { + + /* Call system error handler. */ + _lx_nand_flash_system_error(nand_flash, status, block, 0); + + /* Return an error. */ + return(LX_ERROR); + } + + /* Is this block bad? */ + if (block_status != LX_NAND_GOOD_BLOCK) + { + + /* Yes, this block is bad. */ + + /* Increment the number of bad blocks. */ + nand_flash -> lx_nand_flash_bad_blocks++; + + /* Continue to the next block. */ + continue; + } + + /* Now read page 0 of the block, which has the erase count in the first 4 bytes. */ + status = _lx_nand_flash_driver_read(nand_flash, block, 0, page_word_ptr, (nand_flash -> lx_nand_flash_pages_per_block + 1)); + + /* Check for an error from flash driver. */ + if (status) + { + + /* Call system error handler. */ + _lx_nand_flash_system_error(nand_flash, status, block, 0); + + /* Determine if the error is fatal. */ + if (status != LX_NAND_ERROR_CORRECTED) + { + + /* Return an error. */ + return(LX_ERROR); + } + } + + /* Is the block erased? */ + if (((page_word_ptr[0] & LX_BLOCK_ERASED) != LX_BLOCK_ERASED) && (page_word_ptr[0] != LX_BLOCK_ERASE_STARTED)) + { + + /* No, valid block. Isolate the erased count. */ + erased_count = (page_word_ptr[0] & LX_BLOCK_ERASE_COUNT_MASK); + + /* Is this the new minimum? */ + if (erased_count < min_erased_count) + { + + /* Yes, remember the new minimum. */ + min_erased_count = erased_count; + } + + /* Is this the new maximum? */ + if (erased_count > max_erased_count) + { + + /* Yes, remember the new maximum. */ + max_erased_count = erased_count; + } + } + } + + /* If we haven't found any erased counts, we can assume the flash is completely erased and needs to + be setup for the first time. */ + if (min_erased_count == LX_ALL_ONES) + { + + /* Indicate that this is the initial format. */ + nand_flash -> lx_nand_flash_diagnostic_initial_format = LX_TRUE; + + /* Loop through the blocks to setup the flash the first time. */ + for (block = 0; block < nand_flash -> lx_nand_flash_total_blocks; block++) + { + + /* First, check to make sure this block is good. */ + status = _lx_nand_flash_driver_block_status_get(nand_flash, block, &block_status); + + /* Check for an error from flash driver. */ + if (status) + { + + /* Call system error handler. */ + _lx_nand_flash_system_error(nand_flash, status, block, 0); + + /* Return an error. */ + return(LX_ERROR); + } + + /* Is this block bad? */ + if (block_status != LX_NAND_GOOD_BLOCK) + { + + /* Yes, this block is bad. */ + + /* Increment the number of bad blocks. */ + nand_flash -> lx_nand_flash_bad_blocks++; + + /* Continue to the next block. */ + continue; + } + + /* Setup the initial erase count to 1. */ + page_word_ptr[0] = (((ULONG) 1) | LX_BLOCK_ERASED); + + /* Write the initial erase count for the block. */ + status = _lx_nand_flash_driver_write(nand_flash, block, 0, page_word_ptr, LX_NAND_ERASE_COUNT_WRITE_SIZE); + + /* Check for status after the block erase. */ + if (status == LX_SUCCESS) + { + + /* Setup the initial erase count to 1. */ + page_word_ptr[0] = ((ULONG) 1); + + /* Write the initial erase count for the block. */ + status = _lx_nand_flash_driver_write(nand_flash, block, 0, page_word_ptr, LX_NAND_ERASE_COUNT_WRITE_SIZE); + } + + /* Check for an error from flash driver. */ + if (status) + { + + /* Call system error handler. */ + _lx_nand_flash_system_error(nand_flash, status, block, 0); + + /* Attempt to mark this block as bad. */ + status = _lx_nand_flash_driver_block_status_set(nand_flash, block, LX_NAND_BAD_BLOCK); + + /* Check for error in setting the block status. */ + if (status) + { + + /* Call system error handler. */ + _lx_nand_flash_system_error(nand_flash, status, block, 0); + + /* Return an error. */ + return(LX_ERROR); + } + + /* Increment the bad block count. */ + nand_flash -> lx_nand_flash_bad_blocks++; + } + else + { + + /* Update the number of free pages. Subtract 1, since page 0 is used for the erase count and list of mapped pages. */ + nand_flash -> lx_nand_flash_free_pages = nand_flash -> lx_nand_flash_free_pages + + nand_flash -> lx_nand_flash_pages_per_block - 1; + } + } + + /* Update the overall minimum and maximum erase count. */ + nand_flash -> lx_nand_flash_minimum_erase_count = 1; + nand_flash -> lx_nand_flash_maximum_erase_count = 1; + } + else + { + + /* At this point, we have a previously managed flash structure. This needs to be traversed to prepare for the + current flash operation. */ + + /* Setup the maximum mapped sector value so all searches are valid. */ + nand_flash -> lx_nand_flash_max_mapped_sector = 0xFFFFFFFF; + + /* Default the flash block search to an invalid value. */ + nand_flash -> lx_nand_flash_free_block_search = nand_flash -> lx_nand_flash_total_blocks; + + /* Loop through the blocks. */ + for (block = 0; block < nand_flash -> lx_nand_flash_total_blocks; block++) + { + + /* First, check to make sure this block is good. */ + status = _lx_nand_flash_driver_block_status_get(nand_flash, block, &block_status); + + /* Check for an error from flash driver. */ + if (status) + { + + /* Call system error handler. */ + _lx_nand_flash_system_error(nand_flash, status, block, 0); + + /* Return an error. */ + return(LX_ERROR); + } + + /* Is this block bad? */ + if (block_status != LX_NAND_GOOD_BLOCK) + { + + /* Yes, this block is bad. */ + + /* Increment the number of bad blocks. */ + nand_flash -> lx_nand_flash_bad_blocks++; + + /* Continue to the next block. */ + continue; + } + + /* Now read page 0 of the block, which has the erase count in the first 4 bytes. */ + status = _lx_nand_flash_driver_read(nand_flash, block, 0, page_word_ptr, (nand_flash -> lx_nand_flash_pages_per_block + 1)); + + /* Check for an error from flash driver. */ + if (status) + { + + /* Call system error handler. */ + _lx_nand_flash_system_error(nand_flash, status, block, 0); + + /* Determine if the error is fatal. */ + if (status != LX_NAND_ERROR_CORRECTED) + { + + /* Return an error. */ + return(LX_ERROR); + } + } + + /* Is the block erased? */ + if (((page_word_ptr[0] & LX_BLOCK_ERASED) == LX_BLOCK_ERASED) || (page_word_ptr[0] == LX_BLOCK_ERASE_STARTED)) + { + + /* This can happen if we were previously in the process of erasing the flash block and a + power interruption occurs. It should only occur once though. */ + + /* Is this the first time? */ + if (nand_flash -> lx_nand_flash_diagnostic_erased_block) + { + + /* No, this is a potential format error, since this should only happen once in a given + NAND flash format. */ + _lx_nand_flash_system_error(nand_flash, LX_SYSTEM_INVALID_BLOCK, block, 0); + } + + /* Increment the erased block count. */ + nand_flash -> lx_nand_flash_diagnostic_erased_block++; + + /* Check to see if the block is erased. */ + status = _lx_nand_flash_driver_block_erased_verify(nand_flash, block); + + /* Is the block completely erased? */ + if (status != LX_SUCCESS) + { + + /* Is this the first time? */ + if (nand_flash -> lx_nand_flash_diagnostic_re_erase_block) + { + + /* No, this is a potential format error, since this should only happen once in a given + NAND flash format. */ + _lx_nand_flash_system_error(nand_flash, LX_SYSTEM_INVALID_BLOCK, block, 0); + } + + /* Increment the erased block diagnostic. */ + nand_flash -> lx_nand_flash_diagnostic_re_erase_block++; + + /* No, the block is not fully erased, erase it again. */ + status = _lx_nand_flash_driver_block_erase(nand_flash, block, max_erased_count); + + /* Check for an error from flash driver. */ + if (status) + { + + /* Call system error handler. */ + _lx_nand_flash_system_error(nand_flash, status, block, 0); + + /* Attempt to mark this block as bad. */ + status = _lx_nand_flash_driver_block_status_set(nand_flash, block, LX_NAND_BAD_BLOCK); + + /* Check for error in setting the block status. */ + if (status) + { + + /* Call system error handler. */ + _lx_nand_flash_system_error(nand_flash, status, block, 0); + + /* Return an error. */ + return(LX_ERROR); + } + + /* Increment the bad block count. */ + nand_flash -> lx_nand_flash_bad_blocks++; + + /* Continue with next block. */ + continue; + } + } + + /* Now read page 0 of the block, which is all ones at this point. */ + status = _lx_nand_flash_driver_read(nand_flash, block, 0, page_word_ptr, (nand_flash -> lx_nand_flash_pages_per_block + 1)); + + /* Check for an error from flash driver. */ + if (status) + { + + /* Call system error handler. */ + _lx_nand_flash_system_error(nand_flash, status, block, 0); + + /* Attempt to mark this block as bad. */ + status = _lx_nand_flash_driver_block_status_set(nand_flash, block, LX_NAND_BAD_BLOCK); + + /* Check for error in setting the block status. */ + if (status) + { + + /* Call system error handler. */ + _lx_nand_flash_system_error(nand_flash, status, block, 0); + + /* Return an error. */ + return(LX_ERROR); + } + + /* Increment the bad block count. */ + nand_flash -> lx_nand_flash_bad_blocks++; + + /* Continue with next block. */ + continue; + } + + /* Write the initial erase count for the block. */ + page_word_ptr[0] = (max_erased_count | LX_BLOCK_ERASED); + status = _lx_nand_flash_driver_write(nand_flash, block, 0, page_word_ptr, LX_NAND_ERASE_COUNT_WRITE_SIZE); + + /* Determine if the write was successful. */ + if (status == LX_SUCCESS) + { + + /* Write the initial erase count for the block. */ + page_word_ptr[0] = max_erased_count; + status = _lx_nand_flash_driver_write(nand_flash, block, 0, page_word_ptr, LX_NAND_ERASE_COUNT_WRITE_SIZE); + } + + /* Check for an error from flash driver. */ + if (status) + { + + /* Call system error handler. */ + _lx_nand_flash_system_error(nand_flash, status, block, 0); + + /* Attempt to mark this block as bad. */ + status = _lx_nand_flash_driver_block_status_set(nand_flash, block, LX_NAND_BAD_BLOCK); + + /* Check for error in setting the block status. */ + if (status) + { + + /* Call system error handler. */ + _lx_nand_flash_system_error(nand_flash, status, block, 0); + + /* Return an error. */ + return(LX_ERROR); + } + + /* Increment the bad block count. */ + nand_flash -> lx_nand_flash_bad_blocks++; + + /* Continue with next block. */ + continue; + } + + /* Update the number of free pages. */ + nand_flash -> lx_nand_flash_free_pages = nand_flash -> lx_nand_flash_free_pages + + nand_flash -> lx_nand_flash_pages_per_block - 1; + } + else + { + + /* Check for a format error, an interruption while writing the mapped sector list. */ + if ((page_word_ptr[1] != LX_NAND_PAGE_FREE) && + (page_word_ptr[nand_flash -> lx_nand_flash_pages_per_block] != LX_NAND_PAGE_LIST_VALID)) + { + + /* Increment the invalid mapping counter. */ + nand_flash -> lx_nand_flash_diagnostic_mapping_invalid++; + } + + /* Calculate the number of free pages by traversing the pages in the block. */ + free_pages = 0; + obsolete_pages = 0; + mapped_pages = 0; + for (page = 1; page < nand_flash -> lx_nand_flash_pages_per_block; page++) + { + + /* Read the logical sector mapping for this page. */ + status = _lx_nand_flash_driver_extra_bytes_get(nand_flash, block, page, (UCHAR *) &extra_info, sizeof(extra_info)); + + /* Check for an error from flash driver. */ + if (status) + { + + /* Call system error handler. */ + _lx_nand_flash_system_error(nand_flash, status, block, 0); + + /* Return an error. */ + return(LX_ERROR); + } + + /* Determine if the page is free. */ + if (extra_info.lx_nand_page_extra_info_logical_sector == LX_NAND_PAGE_FREE) + { + + /* The page is free and we know that the pages are allocated sequentially + so there is no need to go further. */ + + /* Increment the free pages counter. */ + free_pages = free_pages + (nand_flash -> lx_nand_flash_pages_per_block - page); + + /* Verify that the same position in the mapped list from page 0 is also free. */ + if (page_word_ptr[page] != LX_NAND_PAGE_FREE) + { + + /* Increment the invalid mapping counter. */ + nand_flash -> lx_nand_flash_diagnostic_mapping_invalid++; + + /* Call system error handler. */ + _lx_nand_flash_system_error(nand_flash, LX_SYSTEM_INVALID_FORMAT, block, page); + } + + /* Determine if the associated page is really free... it could be the case that the + page data area was in the process of being written or just completed when a power + interruption occurs before the extra bytes are setup with the logical sector. */ + + /* Read verify the page is erased. */ + status = _lx_nand_flash_driver_page_erased_verify(nand_flash, block, page); + + /* Check for an error from flash driver. */ + if (status == LX_ERROR) + { + + /* Increment the page data not free diagnostic counter. */ + nand_flash -> lx_nand_flash_diagnostic_page_data_not_free++; + + /* Decrement the free page counter and increment the obsolete page counter. */ + free_pages--; + obsolete_pages++; + + /* Now setup the extra info to show this page is obsolete. */ + extra_info.lx_nand_page_extra_info_logical_sector = 0; + status = _lx_nand_flash_driver_extra_bytes_set(nand_flash, block, page, (UCHAR *) &extra_info, sizeof(extra_info)); + + /* Check for an error from flash driver. */ + if (status) + { + + /* Call system error handler. */ + _lx_nand_flash_system_error(nand_flash, status, block, page); + + /* Return an error. */ + return(LX_ERROR); + } + } + + /* At this point we can break out of the page traversal loop, since nothing else can exist after this page. */ + break; + } + else + { + + /* Determine if the mapping list is not present and the value doesn't match the + logical sector value. */ + if ((page_word_ptr[nand_flash -> lx_nand_flash_pages_per_block] == LX_NAND_PAGE_LIST_VALID) && + ((extra_info.lx_nand_page_extra_info_logical_sector & LX_NAND_LOGICAL_SECTOR_MASK) != + (page_word_ptr[page] & LX_NAND_LOGICAL_SECTOR_MASK))) + { + + /* Increment the invalid mapping counter. */ + nand_flash -> lx_nand_flash_diagnostic_mapping_invalid++; + + /* Call system error handler. */ + _lx_nand_flash_system_error(nand_flash, LX_SYSTEM_INVALID_FORMAT, block, page); + } + + /* Determine if this mapped page is obsolete. */ + if ((extra_info.lx_nand_page_extra_info_logical_sector & LX_NAND_LOGICAL_SECTOR_MASK) == + extra_info.lx_nand_page_extra_info_logical_sector) + { + + /* Yes, page mapping is obsolete. Increment the obsolete count. */ + obsolete_pages++; + + /* Continue looking at next page. */ + continue; + } + + /* Determine if the mapping for this page isn't yet valid. */ + if (extra_info.lx_nand_page_extra_info_logical_sector & LX_NAND_PAGE_MAPPING_NOT_VALID) + { + + /* Yes, a power interruption or reset occurred while the sector mapping entry was being written. */ + + /* Increment the obsolete count. */ + obsolete_pages++; + + /* Increment the interrupted mapping counter. */ + nand_flash -> lx_nand_flash_diagnostic_mapping_write_interrupted++; + + /* Invalidate this entry - clearing valid bit, superceded bit and logical sector. */ + extra_info.lx_nand_page_extra_info_logical_sector = 0; + + /* Write it out the page extra information. */ + status = _lx_nand_flash_driver_extra_bytes_set(nand_flash, block, page, (UCHAR *) &extra_info, sizeof(extra_info)); + + /* Check for an error from flash driver. */ + if (status) + { + + /* Call system error handler. */ + _lx_nand_flash_system_error(nand_flash, status, block, page); + + /* Return an error. */ + return(LX_ERROR); + } + + /* Continue looking at next page. */ + continue; + } + + /* Determine if this page is still mapped. */ + if ((extra_info.lx_nand_page_extra_info_logical_sector & LX_NAND_PAGE_VALID) && + (extra_info.lx_nand_page_extra_info_logical_sector & LX_NAND_PAGE_SUPERCEDED)) + { + + /* Yes, sector is mapped. Increment the mapped count. */ + mapped_pages++; + + /* Determine if this is a new maximum mapped sector. */ + if ((extra_info.lx_nand_page_extra_info_logical_sector & LX_NAND_LOGICAL_SECTOR_MASK) > max_mapped_sector) + { + + /* Remember the maximum mapped logical sector. */ + max_mapped_sector = (extra_info.lx_nand_page_extra_info_logical_sector & LX_NAND_LOGICAL_SECTOR_MASK); + } + +#ifdef LX_NAND_FLASH_DIRECT_MAPPING_CACHE + + /* Logical sector is mapped, setup the direct cache. */ + logical_sector = extra_info.lx_nand_page_extra_info_logical_sector & LX_NAND_LOGICAL_SECTOR_MASK; + + /* Determine if this logical sector fits in the logical sector direct cache mapping. */ + if (logical_sector < LX_NAND_SECTOR_MAPPING_CACHE_SIZE) + { + + /* Remember the mapping for this logical sector. */ + nand_flash -> lx_nand_flash_sector_mapping_cache[logical_sector].lx_nand_sector_mapping_cache_block = (USHORT) block; + nand_flash -> lx_nand_flash_sector_mapping_cache[logical_sector].lx_nand_sector_mapping_cache_page = (USHORT) page; + } +#endif + /* Continue looking at next page. */ + continue; + } + + /* At this point, the page was in the process of being superceded. */ + + /* Determine if this is a new maximum mapped sector. */ + if ((extra_info.lx_nand_page_extra_info_logical_sector & LX_NAND_LOGICAL_SECTOR_MASK) > max_mapped_sector) + { + + /* Remember the maximum mapped logical sector. */ + max_mapped_sector = (extra_info.lx_nand_page_extra_info_logical_sector & LX_NAND_LOGICAL_SECTOR_MASK); + } + + /* Increment the being obsoleted count. */ + nand_flash -> lx_nand_flash_diagnostic_page_being_obsoleted++; + + /* Save the currently mapped pages. */ + temp = nand_flash -> lx_nand_flash_mapped_pages; + + /* Indicate all the pages are mapped for the purpose of this search. */ + nand_flash -> lx_nand_flash_mapped_pages = nand_flash -> lx_nand_flash_total_pages; + + /* Yes, this block was about to become obsolete. Perform a search for a logical sector entry that + has both of these bits set. */ + _lx_nand_flash_logical_sector_find(nand_flash, (extra_info.lx_nand_page_extra_info_logical_sector & LX_NAND_LOGICAL_SECTOR_MASK), + LX_TRUE, &found_block, &found_page); + + /* Read page 0 data again. */ + status = _lx_nand_flash_driver_read(nand_flash, block, 0, page_word_ptr, (nand_flash -> lx_nand_flash_pages_per_block + 1)); + + /* Check for an error from flash driver. */ + if (status) + { + + /* Call system error handler. */ + _lx_nand_flash_system_error(nand_flash, status, block, 0); + + /* Determine if the error is fatal. */ + if (status != LX_NAND_ERROR_CORRECTED) + { + + /* Return an error. */ + return(LX_ERROR); + } + } + + /* Restore the number of mapped pages. */ + nand_flash -> lx_nand_flash_mapped_pages = temp; + + /* Determine if the new logical sector entry is present. */ + if (found_page) + { + + /* Yes, make the current entry obsolete in favor of the new entry. */ + + /* Increment the obsolete count. */ + obsolete_pages++; + + /* Clear the valid bit. */ + extra_info.lx_nand_page_extra_info_logical_sector = extra_info.lx_nand_page_extra_info_logical_sector & ~((ULONG) LX_NAND_PAGE_VALID); + + /* Write it out the page extra information. */ + status = _lx_nand_flash_driver_extra_bytes_set(nand_flash, block, page, (UCHAR *) &extra_info, sizeof(extra_info)); + + /* Check for an error from flash driver. */ + if (status) + { + + /* Call system error handler. */ + _lx_nand_flash_system_error(nand_flash, status, block, page); + + /* Return an error. */ + return(LX_ERROR); + } + + /* Is this the first time? */ + if (nand_flash -> lx_nand_flash_diagnostic_page_obsoleted) + { + + /* No, this is a potential format error, since this should only happen once in a given + NAND flash format. */ + _lx_nand_flash_system_error(nand_flash, LX_SYSTEM_INVALID_SECTOR_MAP, block, page); + } + + /* Increment the obsoleted count. */ + nand_flash -> lx_nand_flash_diagnostic_page_obsoleted++; + +#ifdef LX_NAND_FLASH_DIRECT_MAPPING_CACHE + + /* Logical sector is mapped, setup the direct cache. */ + logical_sector = extra_info.lx_nand_page_extra_info_logical_sector & LX_NAND_LOGICAL_SECTOR_MASK; + + /* Determine if this logical sector fits in the logical sector direct cache mapping. */ + if (logical_sector < LX_NAND_SECTOR_MAPPING_CACHE_SIZE) + { + + /* Remember the mapping for this logical sector. */ + nand_flash -> lx_nand_flash_sector_mapping_cache[logical_sector].lx_nand_sector_mapping_cache_block = (USHORT) found_block; + nand_flash -> lx_nand_flash_sector_mapping_cache[logical_sector].lx_nand_sector_mapping_cache_page = (USHORT) found_page; + } +#endif + } + else + { + + /* A new page was not found. Increment the number of mapped pages. */ + mapped_pages++; + +#ifdef LX_NAND_FLASH_DIRECT_MAPPING_CACHE + + /* Logical sector is mapped, setup the direct cache. */ + logical_sector = extra_info.lx_nand_page_extra_info_logical_sector & LX_NAND_LOGICAL_SECTOR_MASK; + + /* Determine if this logical sector fits in the logical sector direct cache mapping. */ + if (logical_sector < LX_NAND_SECTOR_MAPPING_CACHE_SIZE) + { + + /* Remember the mapping for this logical sector. */ + nand_flash -> lx_nand_flash_sector_mapping_cache[logical_sector].lx_nand_sector_mapping_cache_block = (USHORT) block; + nand_flash -> lx_nand_flash_sector_mapping_cache[logical_sector].lx_nand_sector_mapping_cache_page = (USHORT) page; + } +#endif + } + } + } + + /* Update the number of free, obsolete, and mapped pages. */ + nand_flash -> lx_nand_flash_free_pages = nand_flash -> lx_nand_flash_free_pages + free_pages; + nand_flash -> lx_nand_flash_mapped_pages = nand_flash -> lx_nand_flash_mapped_pages + mapped_pages; + nand_flash -> lx_nand_flash_obsolete_pages = nand_flash -> lx_nand_flash_obsolete_pages + obsolete_pages; + + /* Determine if the mapping list needs to be written to the flash. */ + if ((free_pages == 0) && (page_word_ptr[1] == LX_ALL_ONES)) + { + + /* Yes, the mapping list should be written to page 0. */ + _lx_nand_flash_block_full_update(nand_flash, block, page_word_ptr[0]); + + /* Read page 0 data again. */ + status = _lx_nand_flash_driver_read(nand_flash, block, 0, page_word_ptr, (nand_flash -> lx_nand_flash_pages_per_block + 1)); + + /* Check for an error from flash driver. */ + if (status) + { + + /* Call system error handler. */ + _lx_nand_flash_system_error(nand_flash, status, block, 0); + + /* Determine if the error is fatal. */ + if (status != LX_NAND_ERROR_CORRECTED) + { + + /* Return an error. */ + return(LX_ERROR); + } + } + } + } + } + + /* Update the overall minimum and maximum erase count. */ + nand_flash -> lx_nand_flash_minimum_erase_count = min_erased_count; + nand_flash -> lx_nand_flash_maximum_erase_count = max_erased_count; + + /* Determine if we need to update the free sector search pointer. */ + if (nand_flash -> lx_nand_flash_free_block_search == nand_flash -> lx_nand_flash_total_blocks) + { + + /* Just start at the beginning. */ + nand_flash -> lx_nand_flash_free_block_search = 0; + } + } + +#ifdef LX_THREAD_SAFE_ENABLE + + /* If the thread safe option is enabled, create a ThreadX mutex that will be used in all external APIs + in order to provide thread-safe operation. */ + status = tx_mutex_create(&nand_flash -> lx_nand_flash_mutex, "NAND Flash Mutex", TX_NO_INHERIT); + + /* Determine if the mutex creation encountered an error. */ + if (status != TX_SUCCESS) + { + + /* Call system error handler, since this should not happen. */ + _lx_nand_flash_system_error(nand_flash, LX_SYSTEM_MUTEX_CREATE_FAILED, 0, 0); + + /* Return error to caller. */ + return(LX_ERROR); + } +#endif + + /* Enable the sector mapping cache. */ + nand_flash -> lx_nand_flash_sector_mapping_cache_enabled = LX_TRUE; + + /* Initialize the last found block and page search markers. */ + nand_flash -> lx_nand_flash_found_block_search = 0; + nand_flash -> lx_nand_flash_found_page_search = 1; + + /* Remember the maximum mapped sector. */ + nand_flash -> lx_nand_flash_max_mapped_sector = max_mapped_sector; + + /* Lockout interrupts. */ + TX_DISABLE + + /* At this point, the NAND flash has been opened successfully. Place the + NAND flash control block on the linked list of currently opened NAND flashes. */ + + /* Set the NAND flash state to open. */ + nand_flash -> lx_nand_flash_state = LX_NAND_FLASH_OPENED; + + /* Place the NAND flash control block on the list of opened NAND flashes. First, + check for an empty list. */ + if (_lx_nand_flash_opened_count) + { + + /* List is not empty - other NAND flashes are open. */ + + /* Pickup tail pointer. */ + tail_ptr = _lx_nand_flash_opened_ptr -> lx_nand_flash_open_previous; + + /* Place the new NAND flash control block in the list. */ + _lx_nand_flash_opened_ptr -> lx_nand_flash_open_previous = nand_flash; + tail_ptr -> lx_nand_flash_open_next = nand_flash; + + /* Setup this NAND flash's opened links. */ + nand_flash -> lx_nand_flash_open_previous = tail_ptr; + nand_flash -> lx_nand_flash_open_next = _lx_nand_flash_opened_ptr; + } + else + { + + /* The opened NAND flash list is empty. Add the NAND flash to empty list. */ + _lx_nand_flash_opened_ptr = nand_flash; + nand_flash -> lx_nand_flash_open_next = nand_flash; + nand_flash -> lx_nand_flash_open_previous = nand_flash; + } + + /* Increment the opened NAND flash counter. */ + _lx_nand_flash_opened_count++; + + /* Restore interrupts. */ + TX_RESTORE + + /* Return a successful completion. */ + return(LX_SUCCESS); +} + diff --git a/common/src/lx_nand_flash_page_ecc_check.c b/common/src/lx_nand_flash_page_ecc_check.c new file mode 100644 index 0000000..27fb9dc --- /dev/null +++ b/common/src/lx_nand_flash_page_ecc_check.c @@ -0,0 +1,130 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** LevelX Component */ +/** */ +/** NAND Flash */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define LX_SOURCE_CODE + + +/* Disable ThreadX error checking. */ + +#ifndef TX_DISABLE_ERROR_CHECKING +#define TX_DISABLE_ERROR_CHECKING +#endif + + +/* Include necessary system files. */ + +#include "lx_api.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _lx_nand_flash_page_ecc_check PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function checks the NAND page and ECC for errors and */ +/* attempts to correct 1 bit errors. */ +/* */ +/* INPUT */ +/* */ +/* nand_flash NAND flash instance */ +/* page_buffer Page buffer */ +/* ecc_buffer Returned ECC buffer */ +/* */ +/* OUTPUT */ +/* */ +/* return status */ +/* */ +/* CALLS */ +/* */ +/* _lx_nand_flash_256byte_ecc_check Check 256 bytes and ECC */ +/* */ +/* CALLED BY */ +/* */ +/* NAND flash driver */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 William E. Lamie Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _lx_nand_flash_page_ecc_check(LX_NAND_FLASH *nand_flash, UCHAR *page_buffer, UCHAR *ecc_buffer) +{ + +UINT bytes_checked; +UINT status; +UINT return_status = LX_SUCCESS; + + + /* Loop to check the entire NAND flash page. */ + bytes_checked = 0; + while (bytes_checked < nand_flash -> lx_nand_flash_bytes_per_page) + { + + /* Check this 256 byte piece of the NAND page. */ + status = _lx_nand_flash_256byte_ecc_check(page_buffer, ecc_buffer); + + /* Determine if there was an error. */ + if (status != LX_SUCCESS) + { + + /* Determine if a non-correctable error is present. */ + if (status == LX_NAND_ERROR_NOT_CORRECTED) + { + + /* Always return a non-correctable error, if present. */ + return_status = LX_NAND_ERROR_NOT_CORRECTED; + break; + } + + /* A single-bit error was corrected, return this status + if there is no LX_ERROR status already detected. */ + else if (return_status == LX_SUCCESS) + { + + /* Return a notice that the single bit error was corrected. */ + return_status = LX_NAND_ERROR_CORRECTED; + } + } + + /* Move to the next 256 byte portion of the page. */ + bytes_checked = bytes_checked + 256; + + /* Move the page buffer forward. */ + page_buffer = page_buffer + 256; + + /* Move the ECC buffer forward, note there are 3 bytes of ECC per page. */ + ecc_buffer = ecc_buffer + 3; + } + + /* Return status. */ + return(return_status); +} + diff --git a/common/src/lx_nand_flash_page_ecc_compute.c b/common/src/lx_nand_flash_page_ecc_compute.c new file mode 100644 index 0000000..0cbdfd7 --- /dev/null +++ b/common/src/lx_nand_flash_page_ecc_compute.c @@ -0,0 +1,104 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** LevelX Component */ +/** */ +/** NAND Flash */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define LX_SOURCE_CODE + + +/* Disable ThreadX error checking. */ + +#ifndef TX_DISABLE_ERROR_CHECKING +#define TX_DISABLE_ERROR_CHECKING +#endif + + +/* Include necessary system files. */ + +#include "lx_api.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _lx_nand_flash_page_ecc_compute PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function computes the ECC for a NAND flash page. */ +/* */ +/* INPUT */ +/* */ +/* nand_flash NAND flash instance */ +/* page_buffer Page buffer */ +/* ecc_buffer Returned ECC buffer */ +/* */ +/* OUTPUT */ +/* */ +/* return status */ +/* */ +/* CALLS */ +/* */ +/* _lx_nand_flash_256byte_ecc_compute Compute ECC for 256 bytes */ +/* */ +/* CALLED BY */ +/* */ +/* NAND flash driver */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 William E. Lamie Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _lx_nand_flash_page_ecc_compute(LX_NAND_FLASH *nand_flash, UCHAR *page_buffer, UCHAR *ecc_buffer) +{ + +UINT bytes_computed; + + + /* Loop to compute the ECC over the entire NAND flash page. */ + bytes_computed = 0; + while (bytes_computed < nand_flash -> lx_nand_flash_bytes_per_page) + { + + /* Compute the ECC for this 256 byte piece of the page. */ + _lx_nand_flash_256byte_ecc_compute(page_buffer, ecc_buffer); + + /* Move to the next 256 byte portion of the page. */ + bytes_computed = bytes_computed + 256; + + /* Move the page buffer forward. */ + page_buffer = page_buffer + 256; + + /* Move the ECC buffer forward, note there are 3 bytes of ECC per page. */ + ecc_buffer = ecc_buffer + 3; + } + + /* Return success. */ + return(LX_SUCCESS); +} + diff --git a/common/src/lx_nand_flash_partial_defragment.c b/common/src/lx_nand_flash_partial_defragment.c new file mode 100644 index 0000000..1f5c7ae --- /dev/null +++ b/common/src/lx_nand_flash_partial_defragment.c @@ -0,0 +1,123 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** LevelX Component */ +/** */ +/** NAND Flash */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define LX_SOURCE_CODE + + +/* Disable ThreadX error checking. */ + +#ifndef TX_DISABLE_ERROR_CHECKING +#define TX_DISABLE_ERROR_CHECKING +#endif + + +/* Include necessary system files. */ + +#include "lx_api.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _lx_nand_flash_partial_defragment PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function defragments the NAND flash up to the specified */ +/* number of blocks. */ +/* */ +/* INPUT */ +/* */ +/* nand_flash NAND flash instance */ +/* max_blocks Maximum number of blocks to */ +/* defragment */ +/* */ +/* OUTPUT */ +/* */ +/* return status */ +/* */ +/* CALLS */ +/* */ +/* _lx_nand_flash_block_reclaim Reclaim a NAND flash block */ +/* tx_mutex_get Get thread protection */ +/* tx_mutex_put Release thread protection */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* Internal LevelX */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 William E. Lamie Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _lx_nand_flash_partial_defragment(LX_NAND_FLASH *nand_flash, UINT max_blocks) +{ + +ULONG i; + + +#ifdef LX_THREAD_SAFE_ENABLE + + /* Obtain the thread safe mutex. */ + tx_mutex_get(&nand_flash -> lx_nand_flash_mutex, TX_WAIT_FOREVER); +#endif + + /* Determine if the maximum number of blocks exceeds the total blocks in this flash instance. */ + if (max_blocks >= nand_flash -> lx_nand_flash_total_blocks) + { + + /* Adjust the maximum to the total number of blocks. */ + max_blocks = nand_flash -> lx_nand_flash_total_blocks; + } + + /* Loop for max number of blocks, while there are obsolete count. */ + for (i = 0; i < max_blocks; i++) + { + + /* Determine if there is any more defragment work. */ + if (nand_flash -> lx_nand_flash_obsolete_pages == 0) + break; + + /* Call the block reclaim function to defragment. */ + _lx_nand_flash_block_reclaim(nand_flash); + } + +#ifdef LX_THREAD_SAFE_ENABLE + + /* Release the thread safe mutex. */ + tx_mutex_put(&nand_flash -> lx_nand_flash_mutex); +#endif + + /* Return successful completion. */ + return(LX_SUCCESS); +} + + diff --git a/common/src/lx_nand_flash_physical_page_allocate.c b/common/src/lx_nand_flash_physical_page_allocate.c new file mode 100644 index 0000000..6028a86 --- /dev/null +++ b/common/src/lx_nand_flash_physical_page_allocate.c @@ -0,0 +1,392 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** LevelX Component */ +/** */ +/** NAND Flash */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define LX_SOURCE_CODE + + +/* Disable ThreadX error checking. */ + +#ifndef TX_DISABLE_ERROR_CHECKING +#define TX_DISABLE_ERROR_CHECKING +#endif + + +/* Include necessary system files. */ + +#include "lx_api.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _lx_nand_flash_physical_page_allocate PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function allocates a free page for mapping to a */ +/* logical sector. */ +/* */ +/* INPUT */ +/* */ +/* nand_flash NAND flash instance */ +/* block Destination for block */ +/* page Destination for page */ +/* erase_count Destination for erase count */ +/* */ +/* OUTPUT */ +/* */ +/* return status */ +/* */ +/* CALLS */ +/* */ +/* _lx_nand_flash_driver_extra_bytes_get Driver get extra bytes */ +/* _lx_nand_flash_driver_extra_bytes_set Driver set extra bytes */ +/* _lx_nand_flash_driver_block_status_get */ +/* Driver block status get */ +/* _lx_nand_flash_driver_page_erased_verify */ +/* Driver verify page erased */ +/* _lx_nand_flash_driver_read Driver page read */ +/* _lx_nand_flash_block_full_update Update page 0 with list of */ +/* mapped pages */ +/* _lx_nand_flash_system_error Internal system error handler */ +/* */ +/* CALLED BY */ +/* */ +/* Internal LevelX */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 William E. Lamie Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _lx_nand_flash_physical_page_allocate(LX_NAND_FLASH *nand_flash, ULONG *block, ULONG *page, ULONG *erase_count) +{ + +LX_NAND_PAGE_EXTRA_INFO extra_info; +UCHAR block_status; +ULONG search_block; +ULONG search_page; +ULONG *word_ptr; +ULONG i, j; +UINT status; + + + /* Increment the number of page allocation requests. */ + nand_flash -> lx_nand_flash_diagnostic_page_allocates++; + + /* Initialize the return parameters. */ + *block = (ULONG) 0; + *page = (ULONG) 0; + *erase_count = nand_flash -> lx_nand_flash_minimum_erase_count; + + /* Determine if there are any free pages. */ + if (nand_flash -> lx_nand_flash_free_pages == 0) + { + + /* Increment the number of failed allocations. */ + nand_flash -> lx_nand_flash_diagnostic_page_allocate_errors++; + + /* No free pages, return . */ + return(LX_NO_PAGES); + } + + /* Pickup the search for a free page at the specified block. */ + search_block = nand_flash -> lx_nand_flash_free_block_search; + + /* Loop through the blocks to find a free page. */ + for (i = 0; i < nand_flash -> lx_nand_flash_total_blocks; i++) + { + + /* First, check to see if this block is good. */ + status = _lx_nand_flash_driver_block_status_get(nand_flash, search_block, &block_status); + + /* Check for an error from flash driver. */ + if (status) + { + + /* Call system error handler. */ + _lx_nand_flash_system_error(nand_flash, status, search_block, 0); + + /* Return the error. */ + return(status); + } + + /* Determine if this block is bad. */ + if (block_status != LX_NAND_GOOD_BLOCK) + { + + /* Move to next search block. */ + search_block++; + + /* Have we wrapped around yet? */ + if (search_block >= nand_flash -> lx_nand_flash_total_blocks) + { + + /* Reset the search block to the beginning. */ + search_block = 0; + } + + /* Block is bad, move to the next block. */ + continue; + } + + /* Read the extra bytes of page 0. This will tell us if the page has any free entries. */ + status = _lx_nand_flash_driver_extra_bytes_get(nand_flash, search_block, 0, (UCHAR *) &extra_info, sizeof(extra_info)); + + /* Check for an error from flash driver. */ + if (status) + { + + /* Call system error handler. */ + _lx_nand_flash_system_error(nand_flash, status, search_block, 0); + + /* Return the error... no point in continuing. */ + return(status); + } + + /* Determine if there are any free entries. */ + if ((extra_info.lx_nand_page_extra_info_logical_sector & LX_NAND_BLOCK_FULL) == 0) + { + + /* This block is full... go to the next block. */ + search_block++; + + /* Have we wrapped around yet? */ + if (search_block >= nand_flash -> lx_nand_flash_total_blocks) + { + + /* Reset the search block to the beginning. */ + search_block = 0; + } + + /* Block is bad, move to the next block. */ + continue; + } + + /* Now check to see if page 0 has been written to. When the block is full, page 0 is written with the mapping of pages + to logical sectors in this block - only when the block becomes full. If the block has additional pages free, page 0 is all 0xFFs. */ + status = _lx_nand_flash_driver_read(nand_flash, search_block, 0, nand_flash -> lx_nand_flash_page_buffer, nand_flash -> lx_nand_flash_words_per_page); + + /* Check for an error... an error here indicates the block does not have any free pages. */ + if (status) + { + + /* Call system error handler. */ + _lx_nand_flash_system_error(nand_flash, status, search_block, 0); + + /* Determine if the error is fatal. */ + if (status != LX_NAND_ERROR_CORRECTED) + { + + /* Return the error. */ + return(status); + } + } + + /* Setup pointer to the page 0 data. */ + word_ptr = nand_flash -> lx_nand_flash_page_buffer; + + /* Pickup the block erase count (first word in the page 0 data area). */ + if (*word_ptr != LX_ALL_ONES) + *erase_count = *word_ptr; + + /* Now determine if the remaining words are all ones, i.e. haven't been written to. */ + j = 1; + while (j < (nand_flash -> lx_nand_flash_pages_per_block + 1)) + { + + /* Has this word been written to? */ + if (word_ptr[j] != LX_ALL_ONES) + break; + + /* Move to next word. */ + j++; + } + + /* Does this block have any free pages? */ + if (j < (nand_flash -> lx_nand_flash_pages_per_block + 1)) + { + + /* Move to next search block. */ + search_block++; + + /* Have we wrapped around yet? */ + if (search_block >= nand_flash -> lx_nand_flash_total_blocks) + { + + /* Reset the search block to the beginning. */ + search_block = 0; + } + + /* No, the data area has been written to. Simply move to the next block. */ + continue; + } + + /* Now search for an available page within the block. */ + search_page = 1; + while (search_page < nand_flash -> lx_nand_flash_pages_per_block) + { + + /* Read the extra information for this page to see if there is a sector mapping. */ + status = _lx_nand_flash_driver_extra_bytes_get(nand_flash, search_block, search_page, (UCHAR *) &extra_info, sizeof(extra_info)); + + /* Check for an error from flash driver. */ + if (status) + { + + /* Call system error handler. */ + _lx_nand_flash_system_error(nand_flash, status, search_block, search_page); + + /* Return the error. */ + return(status); + } + + /* Now determine if this page is mapped or free. */ + if (extra_info.lx_nand_page_extra_info_logical_sector == LX_NAND_PAGE_FREE) + { + + /* Now make sure the data area of this page is free. */ + status = _lx_nand_flash_driver_page_erased_verify(nand_flash, search_block, search_page); + + /* Is the page free? */ + if (status != LX_SUCCESS) + { + + /* No, the page is not actually free. For now, simply mark the extra info area to indicate + the page is not available and continue our search. */ + extra_info.lx_nand_page_extra_info_logical_sector = 0; + + /* Decrease the number of free pages. */ + nand_flash -> lx_nand_flash_free_pages--; + + /* Increase the number of obsolete pages. */ + nand_flash -> lx_nand_flash_obsolete_pages++; + + /* Write out the extra info for this page. */ + status = _lx_nand_flash_driver_extra_bytes_set(nand_flash, search_block, search_page, (UCHAR *) &extra_info, sizeof(extra_info)); + + /* Check for an error from flash driver. */ + if (status) + { + + /* Call system error handler. */ + _lx_nand_flash_system_error(nand_flash, status, search_block, search_page); + + /* Return the error. */ + return(status); + } + + /* Determine if this is the last entry of the block. */ + if (search_page == (nand_flash -> lx_nand_flash_pages_per_block - 1)) + { + + /* Yes, we need to update page 0 of the block with the list of mapped + pages for this block. */ + _lx_nand_flash_block_full_update(nand_flash, search_block, *erase_count); + } + } + else + { + + /* Yes, we have found a free page in this block. Get out of the page search loop. */ + break; + } + } + + /* Move to next search page. */ + search_page++; + } + + /* Determine if we have found the free block. */ + if (search_page < nand_flash -> lx_nand_flash_pages_per_block) + { + + /* Remember the block to search. */ + nand_flash -> lx_nand_flash_free_block_search = search_block; + + /* Determine if the allocated page is the last one in this block. */ + if (search_page == (nand_flash -> lx_nand_flash_pages_per_block - 1)) + { + + /* Yes, move to the next block. */ + nand_flash -> lx_nand_flash_free_block_search++; + + /* Have we wrapped around yet? */ + if (nand_flash -> lx_nand_flash_free_block_search >= nand_flash -> lx_nand_flash_total_blocks) + { + + /* Wrap condition - reset the search block to the beginning. */ + nand_flash -> lx_nand_flash_free_block_search = 0; + } + } + + /* Prepare the return information. */ + *block = search_block; + *page = search_page; + + /* Determine if this is the first data page of the block. */ + if (search_page == 1) + { + + /* Yes, we need to update the extra bytes of page 0 to indicate the block is no longer empty. */ + extra_info.lx_nand_page_extra_info_logical_sector = ((ULONG) LX_NAND_PAGE_FREE) & ~((ULONG) LX_NAND_BLOCK_EMPTY); + status = _lx_nand_flash_driver_extra_bytes_set(nand_flash, search_block, 0, (UCHAR *) &extra_info, sizeof(extra_info)); + + /* Check for an error from flash driver. */ + if (status) + { + + /* Call system error handler. */ + _lx_nand_flash_system_error(nand_flash, status, search_block, 0); + } + } + + /* Return success! */ + return(LX_SUCCESS); + } + + /* Move to next search block. */ + search_block++; + + /* Have we wrapped around yet? */ + if (search_block >= nand_flash -> lx_nand_flash_total_blocks) + { + + /* Reset the search block to the beginning. */ + search_block = 0; + } + } + + /* Increment the number of failed allocations. */ + nand_flash -> lx_nand_flash_diagnostic_page_allocate_errors++; + + /* Return error. */ + return(LX_NO_PAGES); +} + diff --git a/common/src/lx_nand_flash_sector_mapping_cache_invalidate.c b/common/src/lx_nand_flash_sector_mapping_cache_invalidate.c new file mode 100644 index 0000000..d90236f --- /dev/null +++ b/common/src/lx_nand_flash_sector_mapping_cache_invalidate.c @@ -0,0 +1,150 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** LevelX Component */ +/** */ +/** NAND Flash */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define LX_SOURCE_CODE + + +/* Disable ThreadX error checking. */ + +#ifndef TX_DISABLE_ERROR_CHECKING +#define TX_DISABLE_ERROR_CHECKING +#endif + + +/* Include necessary system files. */ + +#include "lx_api.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _lx_nand_flash_sector_mapping_cache_invalidate PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function invalidates the sector's entry in the NAND flash */ +/* cache. */ +/* */ +/* INPUT */ +/* */ +/* nand_flash NAND flash instance */ +/* logical_sector Logical sector */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* Internal LevelX */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 William E. Lamie Initial Version 6.0 */ +/* */ +/**************************************************************************/ +VOID _lx_nand_flash_sector_mapping_cache_invalidate(LX_NAND_FLASH *nand_flash, ULONG logical_sector) +{ + +#ifndef LX_NAND_FLASH_DIRECT_MAPPING_CACHE +ULONG i; +LX_NAND_SECTOR_MAPPING_CACHE_ENTRY *sector_mapping_cache_entry_ptr; +#endif + + /* Determine if the sector mapping cache is enabled. */ + if (nand_flash -> lx_nand_flash_sector_mapping_cache_enabled) + { + +#ifndef LX_NAND_FLASH_DIRECT_MAPPING_CACHE + + /* Calculate the starting index of the sector mapping cache for this sector entry. */ + i = (logical_sector & LX_NAND_SECTOR_MAPPING_CACHE_HASH_MASK) * LX_NAND_SECTOR_MAPPING_CACHE_DEPTH; + + /* Build a pointer to the cache entry. */ + sector_mapping_cache_entry_ptr = &nand_flash -> lx_nand_flash_sector_mapping_cache[i]; + + /* Determine if the sector is in the sector mapping cache - assuming the depth of the sector + mapping cache is LX_NAND_SECTOR_MAPPING_CACHE_DEPTH entries. */ + if ((sector_mapping_cache_entry_ptr -> lx_nand_sector_mapping_cache_logical_sector) == (logical_sector | LX_NAND_SECTOR_MAPPING_CACHE_ENTRY_VALID)) + { + + /* Move all cache entries up and invalidate the last entry. */ + *(sector_mapping_cache_entry_ptr) = *(sector_mapping_cache_entry_ptr + 1); + *(sector_mapping_cache_entry_ptr + 1) = *(sector_mapping_cache_entry_ptr + 2); + *(sector_mapping_cache_entry_ptr + 2) = *(sector_mapping_cache_entry_ptr + 3); + + /* Invalidate the last entry. */ + (sector_mapping_cache_entry_ptr + 3) -> lx_nand_sector_mapping_cache_logical_sector = 0; + } + else if (((sector_mapping_cache_entry_ptr + 1) -> lx_nand_sector_mapping_cache_logical_sector) == (logical_sector | LX_NAND_SECTOR_MAPPING_CACHE_ENTRY_VALID)) + { + + /* Move all subsequent cache entries up and invalidate the last entry. */ + *(sector_mapping_cache_entry_ptr + 1) = *(sector_mapping_cache_entry_ptr + 2); + *(sector_mapping_cache_entry_ptr + 2) = *(sector_mapping_cache_entry_ptr + 3); + + /* Invalidate the last entry. */ + (sector_mapping_cache_entry_ptr + 3) -> lx_nand_sector_mapping_cache_logical_sector = 0; + } + else if (((sector_mapping_cache_entry_ptr + 2) -> lx_nand_sector_mapping_cache_logical_sector) == (logical_sector | LX_NAND_SECTOR_MAPPING_CACHE_ENTRY_VALID)) + { + + /* Move all subsequent cache entries up and invalidate the last entry. */ + *(sector_mapping_cache_entry_ptr + 2) = *(sector_mapping_cache_entry_ptr + 3); + + /* Invalidate the last entry. */ + (sector_mapping_cache_entry_ptr + 3) -> lx_nand_sector_mapping_cache_logical_sector = 0; + } + else if (((sector_mapping_cache_entry_ptr + 3) -> lx_nand_sector_mapping_cache_logical_sector) == (logical_sector | LX_NAND_SECTOR_MAPPING_CACHE_ENTRY_VALID)) + { + + /* Simply invalidate the last entry. */ + (sector_mapping_cache_entry_ptr + 3) -> lx_nand_sector_mapping_cache_logical_sector = 0; + } +#else + + /* Direct mapping cache is defined. */ + + /* Determine if this logical sector fits in the logical sector cache mapping. */ + if (logical_sector < LX_NAND_SECTOR_MAPPING_CACHE_SIZE) + { + + /* Yes, invalidate the logical sector cache. */ + nand_flash -> lx_nand_flash_sector_mapping_cache[logical_sector].lx_nand_sector_mapping_cache_block = 0; + nand_flash -> lx_nand_flash_sector_mapping_cache[logical_sector].lx_nand_sector_mapping_cache_page = 0; + } +#endif + } +} + diff --git a/common/src/lx_nand_flash_sector_read.c b/common/src/lx_nand_flash_sector_read.c new file mode 100644 index 0000000..0b6541d --- /dev/null +++ b/common/src/lx_nand_flash_sector_read.c @@ -0,0 +1,173 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** LevelX Component */ +/** */ +/** NAND Flash */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define LX_SOURCE_CODE + + +/* Disable ThreadX error checking. */ + +#ifndef TX_DISABLE_ERROR_CHECKING +#define TX_DISABLE_ERROR_CHECKING +#endif + + +/* Include necessary system files. */ + +#include "lx_api.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _lx_nand_flash_sector_read PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function reads a logical sector from NAND flash. */ +/* */ +/* INPUT */ +/* */ +/* nand_flash NAND flash instance */ +/* logical_sector Logical sector number */ +/* buffer Pointer to buffer to read into*/ +/* (the size is number of */ +/* bytes in a page) */ +/* */ +/* OUTPUT */ +/* */ +/* return status */ +/* */ +/* CALLS */ +/* */ +/* _lx_nand_flash_driver_read Driver page read */ +/* _lx_nand_flash_logical_sector_find Find logical sector */ +/* _lx_nand_flash_system_error Internal system error handler */ +/* tx_mutex_get Get thread protection */ +/* tx_mutex_put Release thread protection */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 William E. Lamie Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _lx_nand_flash_sector_read(LX_NAND_FLASH *nand_flash, ULONG logical_sector, VOID *buffer) +{ + +UINT status; +ULONG found_block; +ULONG found_page; +ULONG i; +ULONG *word_ptr; + + +#ifdef LX_THREAD_SAFE_ENABLE + + /* Obtain the thread safe mutex. */ + tx_mutex_get(&nand_flash -> lx_nand_flash_mutex, TX_WAIT_FOREVER); +#endif + + /* Increment the number of read requests. */ + nand_flash -> lx_nand_flash_diagnostic_sector_read_requests++; + + /* See if we can find the sector in the current mapping. */ + _lx_nand_flash_logical_sector_find(nand_flash, logical_sector, LX_FALSE, &found_block, &found_page); + + /* Determine if the logical sector mapping was found. */ + if (found_page) + { + + /* Yes, we were able to find the logical sector to page mapping. */ + + /* Read the data from the page. */ + status = _lx_nand_flash_driver_read(nand_flash, found_block, found_page, buffer, nand_flash -> lx_nand_flash_words_per_page); + + /* Check for an error from flash driver. */ + if (status) + { + + /* Call system error handler. */ + _lx_nand_flash_system_error(nand_flash, status, found_block, found_page); + + /* Determine if the error was corrected. */ + if (status == LX_NAND_ERROR_CORRECTED) + { + + /* Yes, we were able to read this page successfully with error correction. */ + + /* Change the status to LX_SUCCESS. */ + status = LX_SUCCESS; + } + else + { + + /* Adjust return status. */ + status = LX_ERROR; + } + } + else + { + + /* Set the status to success. */ + status = LX_SUCCESS; + } + } + else + { + + /* Sector hasn't been written. Simply fill the destination buffer with ones and return success. */ + + /* Setup pointer to users buffer. */ + word_ptr = (ULONG *) buffer; + + /* Put all ones in he buffer. */ + for (i = 0; i < nand_flash -> lx_nand_flash_words_per_page; i++) + { + + /* Copy a word. */ + *word_ptr++ = LX_ALL_ONES; + } + + /* Set the status to success. */ + status = LX_SUCCESS; + } + +#ifdef LX_THREAD_SAFE_ENABLE + + /* Release the thread safe mutex. */ + tx_mutex_put(&nand_flash -> lx_nand_flash_mutex); +#endif + + /* Return status. */ + return(status); +} + diff --git a/common/src/lx_nand_flash_sector_release.c b/common/src/lx_nand_flash_sector_release.c new file mode 100644 index 0000000..e299527 --- /dev/null +++ b/common/src/lx_nand_flash_sector_release.c @@ -0,0 +1,268 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** LevelX Component */ +/** */ +/** NAND Flash */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define LX_SOURCE_CODE + + +/* Disable ThreadX error checking. */ + +#ifndef TX_DISABLE_ERROR_CHECKING +#define TX_DISABLE_ERROR_CHECKING +#endif + + +/* Include necessary system files. */ + +#include "lx_api.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _lx_nand_flash_sector_release PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function releases a logical sector from being managed in the */ +/* NAND flash. */ +/* */ +/* INPUT */ +/* */ +/* nand_flash NAND flash instance */ +/* logical_sector Logical sector number */ +/* */ +/* OUTPUT */ +/* */ +/* return status */ +/* */ +/* CALLS */ +/* */ +/* _lx_nand_flash_driver_read Driver flash sector read */ +/* _lx_nand_flash_driver_write Driver flash sector write */ +/* _lx_nand_flash_driver_extra_bytes_get Get extra bytes from spare */ +/* _lx_nand_flash_driver_extra_bytes_set Set extra bytes in spare */ +/* _lx_nand_flash_block_obsoleted_check Check for block obsoleted */ +/* _lx_nand_flash_block_reclaim Reclaim one flash block */ +/* _lx_nand_flash_sector_mapping_cache_invalidate */ +/* Invalidate cache entry */ +/* _lx_nand_flash_logical_sector_find Find logical sector */ +/* _lx_nand_flash_system_error Internal system error handler */ +/* tx_mutex_get Get thread protection */ +/* tx_mutex_put Release thread protection */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 William E. Lamie Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _lx_nand_flash_sector_release(LX_NAND_FLASH *nand_flash, ULONG logical_sector) +{ + +LX_NAND_PAGE_EXTRA_INFO extra_info; +UINT status; +ULONG found_block; +ULONG found_page; +ULONG *block_word_ptr; +ULONG i; + + +#ifdef LX_THREAD_SAFE_ENABLE + + /* Obtain the thread safe mutex. */ + tx_mutex_get(&nand_flash -> lx_nand_flash_mutex, TX_WAIT_FOREVER); +#endif + + /* Increment the number of release requests. */ + nand_flash -> lx_nand_flash_diagnostic_sector_release_requests++; + + /* See if we can find the logical sector in the current mapping. */ + _lx_nand_flash_logical_sector_find(nand_flash, logical_sector, LX_FALSE, &found_block, &found_page); + + /* Determine if the logical sector to page mapping was found. */ + if (found_page) + { + + /* Yes, we were able to find the logical sector. */ + + /* Determine if this sector is the current maximum sector. */ + if (nand_flash -> lx_nand_flash_max_mapped_sector == logical_sector) + { + + /* Yes, this is the maximum sector. Reduce the maximum, if possible. */ + if (logical_sector) + { + + /* Decrement the logical sector. */ + nand_flash -> lx_nand_flash_max_mapped_sector--; + } + } + + /* Read the extra information from the page. */ + status = _lx_nand_flash_driver_extra_bytes_get(nand_flash, found_block, found_page, (UCHAR *) &extra_info, sizeof(extra_info)); + + /* Check for an error from flash driver. */ + if (status) + { + + /* Call system error handler. */ + _lx_nand_flash_system_error(nand_flash, status, found_block, found_page); + +#ifdef LX_THREAD_SAFE_ENABLE + + /* Release the thread safe mutex. */ + tx_mutex_put(&nand_flash -> lx_nand_flash_mutex); +#endif + + /* Return an error. */ + return(LX_ERROR); + } + + /* Mark this sector as invalid. */ + + /* Now clear bits 31 and 30, which indicates this sector is now obsoleted. */ + extra_info.lx_nand_page_extra_info_logical_sector = extra_info.lx_nand_page_extra_info_logical_sector & + ~(((ULONG) LX_NAND_PAGE_VALID) | ((ULONG) LX_NAND_PAGE_SUPERCEDED)); + + /* Write the value back to the flash to clear bits 31 & 30. */ + status = _lx_nand_flash_driver_extra_bytes_set(nand_flash, found_block, found_page, (UCHAR *) &extra_info, sizeof(extra_info)); + + /* Check for an error from flash driver. */ + if (status) + { + + /* Call system error handler. */ + _lx_nand_flash_system_error(nand_flash, status, found_block, found_page); + +#ifdef LX_THREAD_SAFE_ENABLE + + /* Release the thread safe mutex. */ + tx_mutex_put(&nand_flash -> lx_nand_flash_mutex); +#endif + + /* Return an error. */ + return(LX_ERROR); + } + + /* Increment the number of obsolete pages. */ + nand_flash -> lx_nand_flash_obsolete_pages++; + + /* Decrement the number of mapped pages. */ + nand_flash -> lx_nand_flash_mapped_pages--; + + /* Setup pointer to internal buffer. */ + block_word_ptr = nand_flash -> lx_nand_flash_page_buffer; + + /* Now read page 0 of the block, which has the erase count in the first 4 bytes. */ + status = _lx_nand_flash_driver_read(nand_flash, found_block, 0, block_word_ptr, (nand_flash -> lx_nand_flash_pages_per_block + 1)); + + /* Check for an error from flash driver. */ + if (status) + { + + /* Call system error handler. */ + _lx_nand_flash_system_error(nand_flash, status, found_block, 0); + + /* Determine if the error is fatal. */ + if (status != LX_NAND_ERROR_CORRECTED) + { + +#ifdef LX_THREAD_SAFE_ENABLE + + /* Release the thread safe mutex. */ + tx_mutex_put(&nand_flash -> lx_nand_flash_mutex); +#endif + + /* Return the error. */ + return(status); + } + } + +#ifndef LX_NAND_FLASH_MAPPING_LIST_UPDATE_DISABLE + + /* Determine if the mapping list in page 0 is valid. */ + if ((block_word_ptr[1] != LX_NAND_PAGE_FREE) && + (block_word_ptr[nand_flash -> lx_nand_flash_pages_per_block] == LX_NAND_PAGE_LIST_VALID)) + { + + /* Mark the entry as invalid. */ + block_word_ptr[found_page] &= ~LX_NAND_PAGE_VALID; + + /* Write the invalid entry. */ + status = _lx_nand_flash_driver_write(nand_flash, found_block, 0, block_word_ptr, (nand_flash -> lx_nand_flash_pages_per_block + 1)); + } +#endif + + /* Ensure the sector mapping cache no longer has this sector. */ + _lx_nand_flash_sector_mapping_cache_invalidate(nand_flash, logical_sector); + + /* Call routine to see if this block is completely obsoleted. If so, + we can reclaim it immediately. */ + _lx_nand_flash_block_obsoleted_check(nand_flash, found_block); + + /* Determine if there are less than two block's worth of free pages. */ + i = 0; + while (nand_flash -> lx_nand_flash_free_pages <= nand_flash -> lx_nand_flash_pages_per_block) + { + + /* Attempt to reclaim one block. */ + _lx_nand_flash_block_reclaim(nand_flash); + + /* Increment the block count. */ + i++; + + /* Have we exceeded the number of blocks in the system? */ + if (i >= nand_flash -> lx_nand_flash_total_blocks) + { + + /* Yes, break out of the loop. */ + break; + } + } + } + else + { + + /* Could not find the logical sector. */ + status = LX_SECTOR_NOT_FOUND; + } + +#ifdef LX_THREAD_SAFE_ENABLE + + /* Release the thread safe mutex. */ + tx_mutex_put(&nand_flash -> lx_nand_flash_mutex); +#endif + + /* Return status. */ + return(status); +} + diff --git a/common/src/lx_nand_flash_sector_write.c b/common/src/lx_nand_flash_sector_write.c new file mode 100644 index 0000000..c602087 --- /dev/null +++ b/common/src/lx_nand_flash_sector_write.c @@ -0,0 +1,458 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** LevelX Component */ +/** */ +/** NAND Flash */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define LX_SOURCE_CODE + + +/* Disable ThreadX error checking. */ + +#ifndef TX_DISABLE_ERROR_CHECKING +#define TX_DISABLE_ERROR_CHECKING +#endif + + +/* Include necessary system files. */ + +#include "lx_api.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _lx_nand_flash_sector_write PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function writes a logical sector to the NAND flash page. */ +/* */ +/* INPUT */ +/* */ +/* nand_flash NAND flash instance */ +/* logical_sector Logical sector number */ +/* buffer Pointer to buffer to write */ +/* (the size is number of */ +/* bytes in a page) */ +/* */ +/* OUTPUT */ +/* */ +/* return status */ +/* */ +/* CALLS */ +/* */ +/* _lx_nand_flash_driver_read Driver flash sector read */ +/* _lx_nand_flash_driver_write Driver flash sector write */ +/* _lx_nand_flash_driver_extra_bytes_get Get extra bytes from spare */ +/* _lx_nand_flash_driver_extra_bytes_set Set extra bytes in spare */ +/* _lx_nand_flash_block_full_update Update page 0 with list of */ +/* mapped pages */ +/* _lx_nand_flash_block_obsoleted_check Check for block obsoleted */ +/* _lx_nand_flash_block_reclaim Reclaim one flash block */ +/* _lx_nand_flash_logical_sector_find Find logical sector */ +/* _lx_nand_flash_physical_page_allocate Allocate new page */ +/* _lx_nand_flash_sector_mapping_cache_invalidate */ +/* Invalidate cache entry */ +/* _lx_nand_flash_system_error Internal system error handler */ +/* tx_mutex_get Get thread protection */ +/* tx_mutex_put Release thread protection */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 William E. Lamie Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _lx_nand_flash_sector_write(LX_NAND_FLASH *nand_flash, ULONG logical_sector, VOID *buffer) +{ + +LX_NAND_PAGE_EXTRA_INFO old_extra_info; +LX_NAND_PAGE_EXTRA_INFO new_extra_info; +ULONG old_block; +ULONG old_page; +ULONG new_block; +ULONG new_page; +ULONG new_erase_count; +ULONG i; +#ifndef LX_NAND_FLASH_DIRECT_MAPPING_CACHE +LX_NAND_SECTOR_MAPPING_CACHE_ENTRY *sector_mapping_cache_entry_ptr; +#endif +UINT status; +ULONG *block_word_ptr; + + +#ifdef LX_THREAD_SAFE_ENABLE + + /* Obtain the thread safe mutex. */ + tx_mutex_get(&nand_flash -> lx_nand_flash_mutex, TX_WAIT_FOREVER); +#endif + + /* Determine if there are less than two block's worth of free pages. */ + i = 0; + while (nand_flash -> lx_nand_flash_free_pages <= nand_flash -> lx_nand_flash_pages_per_block) + { + + /* Attempt to reclaim one block. */ + _lx_nand_flash_block_reclaim(nand_flash); + + /* Increment the block count. */ + i++; + + /* Have we exceeded the number of blocks in the system? */ + if (i >= nand_flash -> lx_nand_flash_total_blocks) + { + + /* Yes, break out of the loop. */ + break; + } + } + + /* Increment the number of write requests. */ + nand_flash -> lx_nand_flash_diagnostic_sector_write_requests++; + + /* See if we can find the logical sector in the current mapping. */ + _lx_nand_flash_logical_sector_find(nand_flash, logical_sector, LX_FALSE, &old_block, &old_page); + + /* Allocate a new page for this write. */ + _lx_nand_flash_physical_page_allocate(nand_flash, &new_block, &new_page, &new_erase_count); + + /* Determine if the new page allocation was successful. */ + if (new_page) + { + + /* Yes, we were able to allocate a new page. */ + + /* Determine if this is the new maximum mapped sector. */ + if (logical_sector > nand_flash -> lx_nand_flash_max_mapped_sector) + { + + /* Remember this maximum mapped sector. */ + nand_flash -> lx_nand_flash_max_mapped_sector = logical_sector; + } + + /* Update the number of free pages. */ + nand_flash -> lx_nand_flash_free_pages--; + + /* Write the logical sector data to the new page. */ + status = _lx_nand_flash_driver_write(nand_flash, new_block, new_page, buffer, nand_flash -> lx_nand_flash_words_per_page); + + /* Check for an error from flash driver. */ + if (status) + { + + /* Call system error handler. */ + _lx_nand_flash_system_error(nand_flash, status, new_block, new_page); + +#ifdef LX_THREAD_SAFE_ENABLE + + /* Release the thread safe mutex. */ + tx_mutex_put(&nand_flash -> lx_nand_flash_mutex); +#endif + + /* Return an error. */ + return(LX_ERROR); + } + + /* Determine if there was an old mapping. */ + if (old_page) + { + + /* Read the extra info of the old page. */ + status = _lx_nand_flash_driver_extra_bytes_get(nand_flash, old_block, old_page, (UCHAR *) &old_extra_info, sizeof(old_extra_info)); + + /* Check for an error from flash driver. */ + if (status) + { + + /* Call system error handler. */ + _lx_nand_flash_system_error(nand_flash, status, old_block, old_page); + +#ifdef LX_THREAD_SAFE_ENABLE + + /* Release the thread safe mutex. */ + tx_mutex_put(&nand_flash -> lx_nand_flash_mutex); +#endif + + /* Return an error. */ + return(LX_ERROR); + } + + /* Clear bit 30, which indicates this sector is superceded. */ + old_extra_info.lx_nand_page_extra_info_logical_sector = old_extra_info.lx_nand_page_extra_info_logical_sector & (~((ULONG) LX_NAND_PAGE_SUPERCEDED)); + + /* Write the extra info to old page. */ + status = _lx_nand_flash_driver_extra_bytes_set(nand_flash, old_block, old_page, (UCHAR *) &old_extra_info, sizeof(old_extra_info)); + + /* Check for an error from flash driver. */ + if (status) + { + + /* Call system error handler. */ + _lx_nand_flash_system_error(nand_flash, status, old_block, old_page); + +#ifdef LX_THREAD_SAFE_ENABLE + + /* Release the thread safe mutex. */ + tx_mutex_put(&nand_flash -> lx_nand_flash_mutex); +#endif + + /* Return an error. */ + return(LX_ERROR); + } + } + + /* Now build the new mapping entry - with the not valid bit set initially. */ + new_extra_info.lx_nand_page_extra_info_logical_sector = ((ULONG) LX_NAND_PAGE_VALID) | ((ULONG) LX_NAND_PAGE_SUPERCEDED) | ((ULONG) LX_NAND_PAGE_MAPPING_NOT_VALID) | logical_sector; + + /* Write out the new mapping entry. */ + status = _lx_nand_flash_driver_extra_bytes_set(nand_flash, new_block, new_page, (UCHAR *) &new_extra_info, sizeof(new_extra_info)); + + /* Check for an error from flash driver. */ + if (status) + { + + /* Call system error handler. */ + _lx_nand_flash_system_error(nand_flash, status, new_block, new_page); + +#ifdef LX_THREAD_SAFE_ENABLE + + /* Release the thread safe mutex. */ + tx_mutex_put(&nand_flash -> lx_nand_flash_mutex); +#endif + + /* Return an error. */ + return(LX_ERROR); + } + + /* Now clear the not valid bit to make this sector mapping valid. This is done because the writing of the extra bytes itself can + be interrupted and we need to make sure this can be detected when the flash is opened again. */ + new_extra_info.lx_nand_page_extra_info_logical_sector = new_extra_info.lx_nand_page_extra_info_logical_sector & ~((ULONG) LX_NAND_PAGE_MAPPING_NOT_VALID); + + /* Clear the not valid bit. */ + status = _lx_nand_flash_driver_extra_bytes_set(nand_flash, new_block, new_page, (UCHAR *) &new_extra_info, sizeof(new_extra_info)); + + /* Check for an error from flash driver. */ + if (status) + { + + /* Call system error handler. */ + _lx_nand_flash_system_error(nand_flash, status, new_block, new_page); + +#ifdef LX_THREAD_SAFE_ENABLE + + /* Release the thread safe mutex. */ + tx_mutex_put(&nand_flash -> lx_nand_flash_mutex); +#endif + + /* Return an error. */ + return(LX_ERROR); + } + + + /* Increment the number of mapped sectors. */ + nand_flash -> lx_nand_flash_mapped_pages++; + + /* Was there a previously mapped sector? */ + if (old_page) + { + + /* Now clear bit 31, which indicates this sector is now obsoleted. */ + old_extra_info.lx_nand_page_extra_info_logical_sector = old_extra_info.lx_nand_page_extra_info_logical_sector & ~((ULONG) LX_NAND_PAGE_VALID); + + /* Write the value back to the flash to clear bit 31. */ + status = _lx_nand_flash_driver_extra_bytes_set(nand_flash, old_block, old_page, (UCHAR *) &old_extra_info, sizeof(old_extra_info)); + + /* Check for an error from flash driver. */ + if (status) + { + + /* Call system error handler. */ + _lx_nand_flash_system_error(nand_flash, status, old_block, old_page); + +#ifdef LX_THREAD_SAFE_ENABLE + + /* Release the thread safe mutex. */ + tx_mutex_put(&nand_flash -> lx_nand_flash_mutex); +#endif + + /* Return an error. */ + return(LX_ERROR); + } + + /* Setup pointer to internal buffer. */ + block_word_ptr = nand_flash -> lx_nand_flash_page_buffer; + + /* Now read page 0 of the block, which has the erase count in the first 4 bytes. */ + status = _lx_nand_flash_driver_read(nand_flash, old_block, 0, block_word_ptr, (nand_flash -> lx_nand_flash_pages_per_block + 1)); + + /* Check for an error from flash driver. */ + if (status) + { + + /* Call system error handler. */ + _lx_nand_flash_system_error(nand_flash, status, old_block, 0); + + /* Determine if the error is fatal. */ + if (status != LX_NAND_ERROR_CORRECTED) + { + +#ifdef LX_THREAD_SAFE_ENABLE + + /* Release the thread safe mutex. */ + tx_mutex_put(&nand_flash -> lx_nand_flash_mutex); +#endif + + /* Return the error. */ + return(status); + } + } + +#ifndef LX_NAND_FLASH_MAPPING_LIST_UPDATE_DISABLE + + /* Determine if there is a valid mapping list. */ + if ((block_word_ptr[1] != LX_NAND_PAGE_FREE) && + (block_word_ptr[nand_flash -> lx_nand_flash_pages_per_block] == LX_NAND_PAGE_LIST_VALID)) + { + + /* Mark the entry as invalid. */ + block_word_ptr[old_page] &= ~LX_NAND_PAGE_VALID; + + /* Invalidate the page. */ + status = _lx_nand_flash_driver_write(nand_flash, old_block, 0, block_word_ptr, (nand_flash -> lx_nand_flash_pages_per_block + 1)); + } +#endif + + /* Increment the number of obsolete pages. */ + nand_flash -> lx_nand_flash_obsolete_pages++; + + /* Decrement the number of mapped pages. */ + nand_flash -> lx_nand_flash_mapped_pages--; + + /* Invalidate the old sector mapping cache entry. */ + _lx_nand_flash_sector_mapping_cache_invalidate(nand_flash, logical_sector); + + /* Call routine to see if this block is completely obsoleted. If so, + we can reclaim it immediately. */ + _lx_nand_flash_block_obsoleted_check(nand_flash, old_block); + } + + /* Read the extra info of the old page. */ + status = _lx_nand_flash_driver_extra_bytes_get(nand_flash, new_block, new_page, (UCHAR *) &new_extra_info, sizeof(new_extra_info)); + + /* Check for an error from flash driver. */ + if (status) + { + + /* Call system error handler. */ + _lx_nand_flash_system_error(nand_flash, status, old_block, old_page); + +#ifdef LX_THREAD_SAFE_ENABLE + + /* Release the thread safe mutex. */ + tx_mutex_put(&nand_flash -> lx_nand_flash_mutex); +#endif + + /* Return an error. */ + return(LX_ERROR); + } + + /* Check to see if the logical sector is still mapped to the same block/page. */ + if (((new_extra_info.lx_nand_page_extra_info_logical_sector & LX_NAND_LOGICAL_SECTOR_MASK) == logical_sector) && + (new_extra_info.lx_nand_page_extra_info_logical_sector & ((ULONG) LX_NAND_PAGE_VALID))) + { + + /* Determine if the new page is the last page of the block. */ + if (new_page == (nand_flash -> lx_nand_flash_pages_per_block - 1)) + { + + + /* Yes, we need to update page 0 of the block with the list of mapped + pages for this block. */ + _lx_nand_flash_block_full_update(nand_flash, new_block, new_erase_count); + } + + /* Determine if the logical sector mapping cache is enabled. */ + if (nand_flash -> lx_nand_flash_sector_mapping_cache_enabled) + { + + /* Yes, sector mapping cache is enabled, place this logical sector mapping information in the cache. */ + +#ifndef LX_NAND_FLASH_DIRECT_MAPPING_CACHE + + /* Calculate the starting index of the sector mapping cache for this sector entry. */ + i = (logical_sector & LX_NAND_SECTOR_MAPPING_CACHE_HASH_MASK) * LX_NAND_SECTOR_MAPPING_CACHE_DEPTH; + + /* Build a pointer to the cache entry. */ + sector_mapping_cache_entry_ptr = &nand_flash -> lx_nand_flash_sector_mapping_cache[i]; + + /* Move all the cache entries down so the oldest is at the bottom. */ + *(sector_mapping_cache_entry_ptr + 3) = *(sector_mapping_cache_entry_ptr + 2); + *(sector_mapping_cache_entry_ptr + 2) = *(sector_mapping_cache_entry_ptr + 1); + *(sector_mapping_cache_entry_ptr + 1) = *(sector_mapping_cache_entry_ptr); + + /* Setup the new logical sector information in the cache. */ + sector_mapping_cache_entry_ptr -> lx_nand_sector_mapping_cache_logical_sector = (logical_sector | LX_NAND_SECTOR_MAPPING_CACHE_ENTRY_VALID); + sector_mapping_cache_entry_ptr -> lx_nand_sector_mapping_cache_block = (USHORT) new_block; + sector_mapping_cache_entry_ptr -> lx_nand_sector_mapping_cache_page = (USHORT) new_page; +#else + + /* Determine if this logical sector fits in the logical sector cache mapping. */ + if (logical_sector < LX_NAND_SECTOR_MAPPING_CACHE_SIZE) + { + + /* Yes, store the logical sector to block/page mapping in the cache. */ + nand_flash -> lx_nand_flash_sector_mapping_cache[logical_sector].lx_nand_sector_mapping_cache_block = (USHORT) new_block; + nand_flash -> lx_nand_flash_sector_mapping_cache[logical_sector].lx_nand_sector_mapping_cache_page = (USHORT) new_page; + } +#endif + } + } + + /* Indicate the write was successful. */ + status = LX_SUCCESS; + } + else + { + + /* Indicate the write was unsuccessful. */ + status = LX_NO_SECTORS; + } + +#ifdef LX_THREAD_SAFE_ENABLE + + /* Release the thread safe mutex. */ + tx_mutex_put(&nand_flash -> lx_nand_flash_mutex); +#endif + + /* Return the completion status. */ + return(status); +} + + diff --git a/common/src/lx_nand_flash_simulator.c b/common/src/lx_nand_flash_simulator.c new file mode 100644 index 0000000..8368daa --- /dev/null +++ b/common/src/lx_nand_flash_simulator.c @@ -0,0 +1,398 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** LevelX Component */ +/** */ +/** NAND Flash Simulator */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary files. */ + +#include "tx_api.h" +#include "lx_api.h" + +/* Define constants for the NAND flash simulation. */ + +#define TOTAL_BLOCKS 8 +#define PHYSICAL_PAGES_PER_BLOCK 16 /* Min value of 2 */ +#define BYTES_PER_PHYSICAL_PAGE 2048 /* 2048 bytes per page */ +#define WORDS_PER_PHYSICAL_PAGE 2048/4 /* Words per page */ +#define SPARE_BYTES_PER_PAGE 64 /* 64 "spare" bytes per page */ + /* For 2048 byte block spare area: */ +#define BAD_BLOCK_POSITION 0 /* 0 is the bad block byte postion */ +#define EXTRA_BYTE_POSITION 2 /* 2 is the extra bytes starting byte postion */ +#define ECC_BYTE_POSITION 40 /* 40 is the ECC starting byte position */ + + +/* Definition of the spare area is relative to the block size of the NAND part and perhaps manufactures of the NAND part. + Here are some common definitions: + + 256 Byte Block + + Bytes Meaning + + 0,1,2 ECC bytes + 3,4,6,7 Extra + 5 Bad block flag + + 512 Byte Block + + Bytes Meaning + + 0,1,2,3,6,7 ECC bytes + 8-15 Extra + 5 Bad block flag + + 2048 Byte Block + + Bytes Meaning + + 0 Bad block flag + 2-39 Extra + 40-63 ECC bytes +*/ + + +typedef struct PHYSICAL_PAGE_STRUCT +{ + unsigned long memory[WORDS_PER_PHYSICAL_PAGE]; + unsigned char spare[SPARE_BYTES_PER_PAGE]; +} PHYSICAL_PAGE; + +typedef struct NAND_BLOCK_DIAG_STRUCT +{ + unsigned long erases; + unsigned long page_writes[PHYSICAL_PAGES_PER_BLOCK]; + unsigned long max_page_writes[PHYSICAL_PAGES_PER_BLOCK]; +} NAND_BLOCK_DIAG; + + + + +typedef struct NAND_FLASH_BLOCK_STRUCT +{ + PHYSICAL_PAGE physical_pages[PHYSICAL_PAGES_PER_BLOCK]; +} NAND_FLASH_BLOCK; + +NAND_FLASH_BLOCK nand_memory_area[TOTAL_BLOCKS]; + +NAND_BLOCK_DIAG nand_block_diag[TOTAL_BLOCKS]; + + +/* Define NAND flash buffer for LevelX. */ + +ULONG nand_flash_simulator_buffer[WORDS_PER_PHYSICAL_PAGE]; +ULONG *nand_flash_memory; + + +UINT _lx_nand_flash_simulator_initialize(LX_NAND_FLASH *nand_flash); +UINT _lx_nand_flash_simulator_read(ULONG block, ULONG page, ULONG *destination, ULONG words); +UINT _lx_nand_flash_simulator_write(ULONG block, ULONG page, ULONG *source, ULONG words); +UINT _lx_nand_flash_simulator_block_erase(ULONG block, ULONG erase_count); +UINT _lx_nand_flash_simulator_block_erased_verify(ULONG block); +UINT _lx_nand_flash_simulator_page_erased_verify(ULONG block, ULONG page); +UINT _lx_nand_flash_simulator_erase_all(VOID); +UINT _lx_nand_flash_simulator_block_status_get(ULONG block, UCHAR *bad_block_byte); +UINT _lx_nand_flash_simulator_block_status_set(ULONG block, UCHAR bad_block_byte); +UINT _lx_nand_flash_simulator_extra_bytes_get(ULONG block, ULONG page, UCHAR *destination, UINT size); +UINT _lx_nand_flash_simulator_extra_bytes_set(ULONG block, ULONG page, UCHAR *source, UINT size); +UINT _lx_nand_flash_simulator_system_error(UINT error_code, ULONG block, ULONG page); + + + +UINT _lx_nand_flash_simulator_initialize(LX_NAND_FLASH *nand_flash) +{ + + /* Setup the buffer pointer. */ + nand_flash_memory = (ULONG *) &nand_memory_area[0]; + + /* Setup geometry of the NAND flash. */ + nand_flash -> lx_nand_flash_total_blocks = TOTAL_BLOCKS; + nand_flash -> lx_nand_flash_pages_per_block = PHYSICAL_PAGES_PER_BLOCK; + nand_flash -> lx_nand_flash_bytes_per_page = BYTES_PER_PHYSICAL_PAGE; + + /* Setup function pointers for the NAND flash services. */ + nand_flash -> lx_nand_flash_driver_read = _lx_nand_flash_simulator_read; + nand_flash -> lx_nand_flash_driver_write = _lx_nand_flash_simulator_write; + nand_flash -> lx_nand_flash_driver_block_erase = _lx_nand_flash_simulator_block_erase; + nand_flash -> lx_nand_flash_driver_block_erased_verify = _lx_nand_flash_simulator_block_erased_verify; + nand_flash -> lx_nand_flash_driver_page_erased_verify = _lx_nand_flash_simulator_page_erased_verify; + nand_flash -> lx_nand_flash_driver_block_status_get = _lx_nand_flash_simulator_block_status_get; + nand_flash -> lx_nand_flash_driver_block_status_set = _lx_nand_flash_simulator_block_status_set; + nand_flash -> lx_nand_flash_driver_extra_bytes_get = _lx_nand_flash_simulator_extra_bytes_get; + nand_flash -> lx_nand_flash_driver_extra_bytes_set = _lx_nand_flash_simulator_extra_bytes_set; + nand_flash -> lx_nand_flash_driver_system_error = _lx_nand_flash_simulator_system_error; + + /* Setup local buffer for NAND flash operation. This buffer must be the page size of the NAND flash memory. */ + nand_flash -> lx_nand_flash_page_buffer = &nand_flash_simulator_buffer[0]; + + /* Return success. */ + return(LX_SUCCESS); +} + + +UINT _lx_nand_flash_simulator_read(ULONG block, ULONG page, ULONG *destination, ULONG words) +{ + +ULONG *flash_address; + + + /* Pickup the flash address. */ + flash_address = &(nand_memory_area[block].physical_pages[page].memory[0]); + + /* Loop to read flash. */ + while (words--) + { + /* Copy word. */ + *destination++ = *flash_address++; + } + + return(LX_SUCCESS); +} + + +UINT _lx_nand_flash_simulator_write(ULONG block, ULONG page, ULONG *source, ULONG words) +{ + +ULONG *flash_address; + + + /* Increment the diag info. */ + nand_block_diag[block].page_writes[page]++; + if (nand_block_diag[block].page_writes[page] > nand_block_diag[block].max_page_writes[page]) + nand_block_diag[block].max_page_writes[page] = nand_block_diag[block].page_writes[page]; + + /* Pickup the flash address. */ + flash_address = &(nand_memory_area[block].physical_pages[page].memory[0]); + + /* Loop to write flash. */ + while (words--) + { + + /* Can the word be written? We can clear new bits, but just can't unclear + in a NAND device. */ + if ((*source & *flash_address) != *source) + return(LX_INVALID_WRITE); + + /* Copy word. */ + *flash_address++ = *source++; + } + + return(LX_SUCCESS); +} + +UINT _lx_nand_flash_simulator_block_erase(ULONG block, ULONG erase_count) +{ + +ULONG *pointer; +ULONG words; +UINT i; + + LX_PARAMETER_NOT_USED(erase_count); + + /* Increment the diag info. */ + nand_block_diag[block].erases++; + for (i = 0; i < PHYSICAL_PAGES_PER_BLOCK;i++) + nand_block_diag[block].page_writes[i] = 0; + + /* Setup pointer. */ + pointer = (ULONG *) &nand_memory_area[block]; + + /* Loop to erase block. */ + words = sizeof(NAND_FLASH_BLOCK)/sizeof(ULONG); + while (words--) + { + + /* Erase word of block. */ + *pointer++ = (ULONG) 0xFFFFFFFF; + } + + return(LX_SUCCESS); +} + + +UINT _lx_nand_flash_simulator_erase_all(VOID) +{ + +ULONG *pointer; +ULONG words; +UINT i, j; + + + /* Increment the diag info. */ + for (i = 0; i < TOTAL_BLOCKS; i++) + { + nand_block_diag[i].erases = 0; + for (j = 0; j < PHYSICAL_PAGES_PER_BLOCK;j++) + nand_block_diag[i].page_writes[j] = 0; + } + + /* Setup pointer. */ + pointer = (ULONG *) &nand_memory_area[0]; + + /* Loop to erase block. */ + words = sizeof(nand_memory_area)/sizeof(ULONG); + while (words--) + { + + /* Erase word of block. */ + *pointer++ = (ULONG) 0xFFFFFFFF; + } + + return(LX_SUCCESS); +} + + +UINT _lx_nand_flash_simulator_block_erased_verify(ULONG block) +{ + +ULONG *word_ptr; +ULONG words; + + /* Determine if the block is completely erased. */ + + /* Pickup the pointer to the first word of the block. */ + word_ptr = (ULONG *) &nand_memory_area[block]; + + /* Calculate the number of words in a block. */ + words = sizeof(NAND_FLASH_BLOCK)/sizeof(ULONG); + + /* Loop to check if the block is erased. */ + while (words--) + { + + /* Is this word erased? */ + if (*word_ptr++ != 0xFFFFFFFF) + return(LX_ERROR); + } + + /* Return success. */ + return(LX_SUCCESS); +} + + +UINT _lx_nand_flash_simulator_page_erased_verify(ULONG block, ULONG page) +{ + +ULONG *word_ptr; +ULONG words; + + /* Determine if the block is completely erased. */ + + /* Pickup the pointer to the first word of the block's page. */ + word_ptr = (ULONG *) &nand_memory_area[block].physical_pages[page]; + + /* Calculate the number of words in a block. */ + words = WORDS_PER_PHYSICAL_PAGE; + + /* Loop to check if the page is erased. */ + while (words--) + { + + /* Is this word erased? */ + if (*word_ptr++ != 0xFFFFFFFF) + return(LX_ERROR); + } + + /* Return success. */ + return(LX_SUCCESS); +} + + +UINT _lx_nand_flash_simulator_block_status_get(ULONG block, UCHAR *bad_block_byte) +{ + + /* Pickup the bad block byte and return it. */ + *bad_block_byte = nand_memory_area[block].physical_pages[0].spare[BAD_BLOCK_POSITION]; + + /* Return success. */ + return(LX_SUCCESS); +} + +UINT _lx_nand_flash_simulator_block_status_set(ULONG block, UCHAR bad_block_byte) +{ + + /* Set the bad block byte. */ + nand_memory_area[block].physical_pages[0].spare[BAD_BLOCK_POSITION] = bad_block_byte; + + /* Return success. */ + return(LX_SUCCESS); +} + + + +UINT _lx_nand_flash_simulator_extra_bytes_get(ULONG block, ULONG page, UCHAR *destination, UINT size) +{ + +UCHAR *source; + + + /* Setup source pointer in the spare area. */ + source = (UCHAR *) &(nand_memory_area[block].physical_pages[page].spare[EXTRA_BYTE_POSITION]); + + /* Loop to return the extra bytes requested. */ + while (size--) + { + + /* Retrieve an extra byte from the spare area. */ + *destination++ = *source++; + } + + /* Return success. */ + return(LX_SUCCESS); +} + + +UINT _lx_nand_flash_simulator_extra_bytes_set(ULONG block, ULONG page, UCHAR *source, UINT size) +{ + +UCHAR *destination; + + + /* Increment the diag info. */ + nand_block_diag[block].page_writes[page]++; + if (nand_block_diag[block].page_writes[page] > nand_block_diag[block].max_page_writes[page]) + nand_block_diag[block].max_page_writes[page] = nand_block_diag[block].page_writes[page]; + + /* Setup destination pointer in the spare area. */ + destination = (UCHAR *) &(nand_memory_area[block].physical_pages[page].spare[EXTRA_BYTE_POSITION]); + + /* Loop to set the extra bytes. */ + while (size--) + { + + /* Set an extra byte in the spare area. */ + *destination++ = *source++; + } + + /* Return success. */ + return(LX_SUCCESS); +} + +UINT _lx_nand_flash_simulator_system_error(UINT error_code, ULONG block, ULONG page) +{ + LX_PARAMETER_NOT_USED(error_code); + LX_PARAMETER_NOT_USED(block); + LX_PARAMETER_NOT_USED(page); + + /* Custom processing goes here... all errors except for LX_NAND_ERROR_CORRECTED are fatal. */ + return(LX_ERROR); +} + + + diff --git a/common/src/lx_nand_flash_system_error.c b/common/src/lx_nand_flash_system_error.c new file mode 100644 index 0000000..f942323 --- /dev/null +++ b/common/src/lx_nand_flash_system_error.c @@ -0,0 +1,107 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** LevelX Component */ +/** */ +/** NAND Flash */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define LX_SOURCE_CODE + + +/* Disable ThreadX error checking. */ + +#ifndef TX_DISABLE_ERROR_CHECKING +#define TX_DISABLE_ERROR_CHECKING +#endif + + +/* Include necessary system files. */ + +#include "lx_api.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _lx_nand_flash_system_error PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function handles system errors in the NAND flash. */ +/* */ +/* INPUT */ +/* */ +/* nand_flash NAND flash instance */ +/* error_code System error code */ +/* block Block where error occurred */ +/* page Page where error occurred */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* (lx_nand_flash_driver_system_error) Driver system error handler */ +/* */ +/* CALLED BY */ +/* */ +/* Internal LevelX */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 William E. Lamie Initial Version 6.0 */ +/* */ +/**************************************************************************/ +VOID _lx_nand_flash_system_error(LX_NAND_FLASH *nand_flash, UINT error_code, ULONG block, ULONG page) +{ + + /* Increment the system error counter. */ + nand_flash -> lx_nand_flash_diagnostic_system_errors++; + + /* Save the most recent system error code. */ + nand_flash -> lx_nand_flash_diagnostic_system_error = error_code; + + /* Determine if the system error is a NAND page corrected error. */ + if (error_code == LX_NAND_ERROR_CORRECTED) + { + + /* Yes, increment error correction information. */ + nand_flash -> lx_nand_flash_page_corrections++; + + /* Remember the last block/page of corrected error. */ + nand_flash -> lx_nand_flash_last_block_correction = block; + nand_flash -> lx_nand_flash_last_page_correction = page; + } + + /* Determine if the driver has setup a system error handler. */ + if (nand_flash -> lx_nand_flash_driver_system_error) + { + + /* Yes, call the driver's system error handler. */ + (nand_flash -> lx_nand_flash_driver_system_error)(error_code, block, page); + } +} + diff --git a/common/src/lx_nor_flash_block_reclaim.c b/common/src/lx_nor_flash_block_reclaim.c new file mode 100644 index 0000000..c0553f9 --- /dev/null +++ b/common/src/lx_nor_flash_block_reclaim.c @@ -0,0 +1,562 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** LevelX Component */ +/** */ +/** NOR Flash */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define LX_SOURCE_CODE + + +/* Disable ThreadX error checking. */ + +#ifndef TX_DISABLE_ERROR_CHECKING +#define TX_DISABLE_ERROR_CHECKING +#endif + + +/* Include necessary system files. */ + +#include "lx_api.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _lx_nor_flash_block_reclaim PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function reclaims one block from the NOR flash. */ +/* */ +/* INPUT */ +/* */ +/* nor_flash NOR flash instance */ +/* */ +/* OUTPUT */ +/* */ +/* return status */ +/* */ +/* CALLS */ +/* */ +/* _lx_nor_flash_driver_block_erase Driver erase block */ +/* _lx_nor_flash_driver_write Driver flash sector write */ +/* _lx_nor_flash_driver_read Driver flash sector read */ +/* _lx_nor_flash_next_block_to_erase_find */ +/* Find next block to erase */ +/* _lx_nor_flash_physical_sector_allocate */ +/* Allocate new logical sector */ +/* _lx_nor_flash_sector_mapping_cache_invalidate */ +/* Invalidate cache entry */ +/* _lx_nor_flash_system_error Internal system error handler */ +/* */ +/* CALLED BY */ +/* */ +/* Internal LevelX */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 William E. Lamie Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _lx_nor_flash_block_reclaim(LX_NOR_FLASH *nor_flash) +{ + +ULONG *block_word_ptr; +ULONG *list_word_ptr; +ULONG list_word; +ULONG i; +ULONG erase_block; +ULONG erase_count; +ULONG temp_erase_count; +ULONG erase_started_value; +ULONG mapped_sectors; +ULONG obsolete_sectors; +ULONG free_sectors; +ULONG logical_sector; +ULONG *new_mapping_address; +ULONG *new_sector_address; +ULONG new_mapping_entry; +UINT status; + + + /* Determine the next block to erase. */ + _lx_nor_flash_next_block_to_erase_find(nor_flash, &erase_block, &erase_count, &mapped_sectors, &obsolete_sectors); + + /* Determine if the search pointer is set for this block. */ + if (nor_flash -> lx_nor_flash_free_block_search == erase_block) + { + + /* Ensure the search block is not the block we are trying to free. */ + nor_flash -> lx_nor_flash_free_block_search = erase_block + 1; + + /* Check for wrap condition. */ + if (nor_flash -> lx_nor_flash_free_block_search >= nor_flash -> lx_nor_flash_total_blocks) + nor_flash -> lx_nor_flash_free_block_search = 0; + } + + /* Setup the block word pointer to the first word of the search block. */ + block_word_ptr = nor_flash -> lx_nor_flash_base_address + (nor_flash -> lx_nor_flash_words_per_block * erase_block); + + /* Determine if this block is completely obsolete. */ + if (obsolete_sectors == nor_flash -> lx_nor_flash_physical_sectors_per_block) + { + + /* Write the erased started indication. */ + erase_started_value = LX_BLOCK_ERASE_STARTED; + status = _lx_nor_flash_driver_write(nor_flash, block_word_ptr, &erase_started_value, 1); + + /* Check for an error from flash driver. Drivers should never return an error.. */ + if (status) + { + + /* Call system error handler. */ + _lx_nor_flash_system_error(nor_flash, status); + + /* Return the error. */ + return(status); + } + + /* Erase the entire block. */ + status = _lx_nor_flash_driver_block_erase(nor_flash, erase_block, erase_count+1); + + /* Check for an error from flash driver. Drivers should never return an error.. */ + if (status) + { + + /* Call system error handler. */ + _lx_nor_flash_system_error(nor_flash, status); + + /* Return the error. */ + return(status); + } + + /* Increment the erase count. */ + erase_count++; + + /* Determine if the new erase count exceeds the maximum. */ + if (erase_count > ((ULONG) LX_BLOCK_ERASE_COUNT_MAX)) + { + + /* Yes, erase count is in overflow. Stay at the maximum count. */ + erase_count = ((ULONG) LX_BLOCK_ERASE_COUNT_MAX); + } + + /* Determine if we need to update the maximum erase count. */ + if (erase_count > nor_flash -> lx_nor_flash_maximum_erase_count) + { + + /* Yes, a new maximum is present. */ + nor_flash -> lx_nor_flash_maximum_erase_count = erase_count; + } + + /* Setup the free bit map that corresponds to the free physical sectors in this + block. Note that we only need to setup the portion of the free bit map that doesn't + have sectors associated with it. */ + status = _lx_nor_flash_driver_write(nor_flash, block_word_ptr+(nor_flash -> lx_nor_flash_block_free_bit_map_offset + (nor_flash -> lx_nor_flash_block_bit_map_words - 1)), + &(nor_flash -> lx_nor_flash_block_bit_map_mask), 1); + + /* Check for an error from flash driver. Drivers should never return an error.. */ + if (status) + { + + /* Call system error handler. */ + _lx_nor_flash_system_error(nor_flash, status); + + /* Return the error. */ + return(status); + } + + /* Write the initial erase count for the block with upper bit set. */ + temp_erase_count = (erase_count | LX_BLOCK_ERASED); + status = _lx_nor_flash_driver_write(nor_flash, block_word_ptr, &temp_erase_count, 1); + + /* Check for an error from flash driver. Drivers should never return an error.. */ + if (status) + { + + /* Call system error handler. */ + _lx_nor_flash_system_error(nor_flash, status); + + /* Return the error. */ + return(status); + } + + /* Write the final initial erase count for the block. */ + status = _lx_nor_flash_driver_write(nor_flash, block_word_ptr, &erase_count, 1); + + /* Check for an error from flash driver. Drivers should never return an error.. */ + if (status) + { + + /* Call system error handler. */ + _lx_nor_flash_system_error(nor_flash, status); + + /* Return the error. */ + return(status); + } + + /* Update parameters of this flash. */ + nor_flash -> lx_nor_flash_free_physical_sectors = nor_flash -> lx_nor_flash_free_physical_sectors + obsolete_sectors; + nor_flash -> lx_nor_flash_obsolete_physical_sectors = nor_flash -> lx_nor_flash_obsolete_physical_sectors - obsolete_sectors; + } + else + { + + /* Calculate the number of free sectors in this block. */ + free_sectors = nor_flash -> lx_nor_flash_physical_sectors_per_block - (obsolete_sectors + mapped_sectors); + + /* Determine if there are enough free sectors outside of this block to reclaim this block. */ + if (mapped_sectors <= (nor_flash -> lx_nor_flash_free_physical_sectors - free_sectors)) + { + + /* Setup a pointer to the mapped list. */ + list_word_ptr = block_word_ptr + nor_flash -> lx_nor_flash_block_physical_sector_mapping_offset; + + /* Now search through the list to find mapped sectors to move. */ + for (i = 0; i < nor_flash -> lx_nor_flash_physical_sectors_per_block; i++) + { + + /* Pickup the mapped sector list entry. */ +#ifdef LX_DIRECT_READ + + /* Read the word directly. */ + list_word = *(list_word_ptr); +#else + status = _lx_nor_flash_driver_read(nor_flash, list_word_ptr, &list_word, 1); + + /* Check for an error from flash driver. Drivers should never return an error.. */ + if (status) + { + + /* Call system error handler. */ + _lx_nor_flash_system_error(nor_flash, status); + + /* Return the error. */ + return(status); + } +#endif + + /* Determine if the entry hasn't been used. */ + if (list_word == LX_NOR_PHYSICAL_SECTOR_FREE) + { + + /* Since allocations are done sequentially in the block, we know nothing + else exists after this point. */ + break; + } + + /* Is this entry mapped? */ + if (list_word & LX_NOR_PHYSICAL_SECTOR_VALID) + { + + /* Pickup the logical sector associated with this mapped physical sector. */ + logical_sector = list_word & LX_NOR_LOGICAL_SECTOR_MASK; + + /* Invalidate the old sector mapping cache entry. */ + _lx_nor_flash_sector_mapping_cache_invalidate(nor_flash, logical_sector); + + /* Allocate a new physical sector for this write. */ + _lx_nor_flash_physical_sector_allocate(nor_flash, logical_sector, &new_mapping_address, &new_sector_address); + + /* Check to see if the new sector is also in the erase block. */ + if ((new_sector_address >= block_word_ptr) && (new_sector_address < (block_word_ptr + nor_flash -> lx_nor_flash_words_per_block))) + { + + /* Yes, the new sector was found in the block to be erased. Simply move the search pointer + to the block after the erase block and search for another sector from there. */ + nor_flash -> lx_nor_flash_free_block_search = erase_block + 1; + + /* Check for wrap condition. */ + if (nor_flash -> lx_nor_flash_free_block_search >= nor_flash -> lx_nor_flash_total_blocks) + nor_flash -> lx_nor_flash_free_block_search = 0; + + /* Allocate a new physical sector for this write. */ + _lx_nor_flash_physical_sector_allocate(nor_flash, logical_sector, &new_mapping_address, &new_sector_address); + + /* Check again for the new sector inside of the block to erase. This should be impossible, since + we check previously if there are enough free sectors outside of this block needed to reclaim + this block. */ + if ((new_sector_address >= block_word_ptr) && (new_sector_address < (block_word_ptr + LX_NOR_SECTOR_SIZE))) + { + + /* System error, a new sector is not available outside of the erase block. + Clear the new sector so we fall through to the error handling. */ + new_mapping_address = LX_NULL; + } + } + + /* Determine if the new sector allocation was successful. */ + if (new_mapping_address) + { + + /* Yes, we were able to allocate a new physical sector. */ + +#ifdef LX_DIRECT_READ + /* First, write the sector data to the new physical sector. */ + status = _lx_nor_flash_driver_write(nor_flash, new_sector_address, (block_word_ptr + nor_flash -> lx_nor_flash_block_physical_sector_offset) + + (i * LX_NOR_SECTOR_SIZE), LX_NOR_SECTOR_SIZE); + + /* Check for an error from flash driver. Drivers should never return an error.. */ + if (status) + { + + /* Call system error handler. */ + _lx_nor_flash_system_error(nor_flash, status); + + /* Return the error. */ + return(status); + } +#else + + /* First, read the sector data into the internal memory of the NOR flash instance. This internal memory + is supplied by the underlying driver during initialization. */ + status = _lx_nor_flash_driver_read(nor_flash, (block_word_ptr + nor_flash -> lx_nor_flash_block_physical_sector_offset) + + (i * LX_NOR_SECTOR_SIZE), nor_flash -> lx_nor_flash_sector_buffer, + LX_NOR_SECTOR_SIZE); + + /* Check for an error from flash driver. Drivers should never return an error.. */ + if (status) + { + + /* Call system error handler. */ + _lx_nor_flash_system_error(nor_flash, status); + + /* Return the error. */ + return(status); + } + + /* Next, write the sector data from the internal buffer to the new physical sector. */ + status = _lx_nor_flash_driver_write(nor_flash, new_sector_address, nor_flash -> lx_nor_flash_sector_buffer, LX_NOR_SECTOR_SIZE); + + /* Check for an error from flash driver. Drivers should never return an error.. */ + if (status) + { + + /* Call system error handler. */ + _lx_nor_flash_system_error(nor_flash, status); + + /* Return the error. */ + return(status); + } +#endif + + /* Now deprecate the old sector mapping. */ + + /* Clear bit 30, which indicates this sector is superceded. */ + list_word = list_word & ~((ULONG) LX_NOR_PHYSICAL_SECTOR_SUPERCEDED); + + /* Write the value back to the flash to clear bit 30. */ + status = _lx_nor_flash_driver_write(nor_flash, list_word_ptr, &list_word, 1); + + /* Check for an error from flash driver. Drivers should never return an error.. */ + if (status) + { + + /* Call system error handler. */ + _lx_nor_flash_system_error(nor_flash, status); + + /* Return the error. */ + return(status); + } + + /* Now build the new mapping entry - with the not valid bit set initially. */ + new_mapping_entry = ((ULONG) LX_NOR_PHYSICAL_SECTOR_VALID) | ((ULONG) LX_NOR_PHYSICAL_SECTOR_SUPERCEDED) | (ULONG) LX_NOR_PHYSICAL_SECTOR_MAPPING_NOT_VALID | logical_sector; + + /* Write out the new mapping entry. */ + status = _lx_nor_flash_driver_write(nor_flash, new_mapping_address, &new_mapping_entry, 1); + + /* Check for an error from flash driver. Drivers should never return an error.. */ + if (status) + { + + /* Call system error handler. */ + _lx_nor_flash_system_error(nor_flash, status); + + /* Return the error. */ + return(status); + } + + /* Now clear the not valid bit to make this sector mapping valid. This is done because the writing of the extra bytes itself can + be interrupted and we need to make sure this can be detected when the flash is opened again. */ + new_mapping_entry = new_mapping_entry & ~((ULONG) LX_NOR_PHYSICAL_SECTOR_MAPPING_NOT_VALID); + + /* Clear the not valid bit. */ + status = _lx_nor_flash_driver_write(nor_flash, new_mapping_address, &new_mapping_entry, 1); + + /* Check for an error from flash driver. Drivers should never return an error.. */ + if (status) + { + + /* Call system error handler. */ + _lx_nor_flash_system_error(nor_flash, status); + + /* Return the error. */ + return(status); + } + + /* Now clear bit 31, which indicates this sector is now obsoleted. */ + list_word = list_word & ~((ULONG) LX_NOR_PHYSICAL_SECTOR_VALID); + + /* Write the value back to the flash to clear bit 31. */ + status = _lx_nor_flash_driver_write(nor_flash, list_word_ptr, &list_word, 1); + + /* Check for an error from flash driver. Drivers should never return an error.. */ + if (status) + { + + /* Call system error handler. */ + _lx_nor_flash_system_error(nor_flash, status); + + /* Return the error. */ + return(status); + } + } + else + { + + /* Call system error handler - the allocation should always succeed at this point. */ + _lx_nor_flash_system_error(nor_flash, LX_SYSTEM_ALLOCATION_FAILED); + + /* Return the error. */ + return(status); + } + + /* Decrement the number of mapped sectors. */ + mapped_sectors--; + + /* Determine if we are done. */ + if (mapped_sectors == 0) + break; + } + + /* Move the list pointer ahead. */ + list_word_ptr++; + } + + /* Write the erased started indication. */ + erase_started_value = LX_BLOCK_ERASE_STARTED; + status = _lx_nor_flash_driver_write(nor_flash, block_word_ptr, &erase_started_value, 1); + + /* Check for an error from flash driver. Drivers should never return an error.. */ + if (status) + { + + /* Call system error handler. */ + _lx_nor_flash_system_error(nor_flash, status); + + /* Return the error. */ + return(status); + } + + /* Erase the entire block. */ + status = _lx_nor_flash_driver_block_erase(nor_flash, erase_block, erase_count+1); + + /* Check for an error from flash driver. Drivers should never return an error.. */ + if (status) + { + + /* Call system error handler. */ + _lx_nor_flash_system_error(nor_flash, status); + + /* Return the error. */ + return(status); + } + + /* Increment the erase count. */ + erase_count++; + + /* Determine if the new erase count exceeds the maximum. */ + if (erase_count > ((ULONG) LX_BLOCK_ERASE_COUNT_MAX)) + { + + /* Yes, erase count is in overflow. Stay at the maximum count. */ + erase_count = ((ULONG) LX_BLOCK_ERASE_COUNT_MAX); + } + + /* Determine if we need to update the maximum erase count. */ + if (erase_count > nor_flash -> lx_nor_flash_maximum_erase_count) + { + + /* Yes, a new maximum is present. */ + nor_flash -> lx_nor_flash_maximum_erase_count = erase_count; + } + + /* Setup the free bit map that corresponds to the free physical sectors in this + block. Note that we only need to setup the portion of the free bit map that doesn't + have sectors associated with it. */ + status = _lx_nor_flash_driver_write(nor_flash, block_word_ptr+(nor_flash -> lx_nor_flash_block_free_bit_map_offset + (nor_flash -> lx_nor_flash_block_bit_map_words - 1)) , + &(nor_flash -> lx_nor_flash_block_bit_map_mask), 1); + + /* Check for an error from flash driver. Drivers should never return an error.. */ + if (status) + { + + /* Call system error handler. */ + _lx_nor_flash_system_error(nor_flash, status); + + /* Return the error. */ + return(status); + } + + /* Write the initial erase count for the block with the upper bit set. */ + temp_erase_count = (erase_count | LX_BLOCK_ERASED); + status = _lx_nor_flash_driver_write(nor_flash, block_word_ptr, &temp_erase_count, 1); + + /* Check for an error from flash driver. Drivers should never return an error.. */ + if (status) + { + + /* Call system error handler. */ + _lx_nor_flash_system_error(nor_flash, status); + + /* Return the error. */ + return(status); + } + + /* Write the final initial erase count for the block. */ + status = _lx_nor_flash_driver_write(nor_flash, block_word_ptr, &erase_count, 1); + + /* Check for an error from flash driver. Drivers should never return an error.. */ + if (status) + { + + /* Call system error handler. */ + _lx_nor_flash_system_error(nor_flash, status); + + /* Return the error. */ + return(status); + } + + /* Update parameters of this flash. */ + nor_flash -> lx_nor_flash_free_physical_sectors = nor_flash -> lx_nor_flash_free_physical_sectors + obsolete_sectors; + nor_flash -> lx_nor_flash_obsolete_physical_sectors = nor_flash -> lx_nor_flash_obsolete_physical_sectors - obsolete_sectors; + } + } + + /* Return status. */ + return(LX_SUCCESS); +} + diff --git a/common/src/lx_nor_flash_close.c b/common/src/lx_nor_flash_close.c new file mode 100644 index 0000000..ea2981f --- /dev/null +++ b/common/src/lx_nor_flash_close.c @@ -0,0 +1,129 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** LevelX Component */ +/** */ +/** NOR Flash */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define LX_SOURCE_CODE + + +/* Disable ThreadX error checking. */ + +#ifndef TX_DISABLE_ERROR_CHECKING +#define TX_DISABLE_ERROR_CHECKING +#endif + + +/* Include necessary system files. */ + +#include "lx_api.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _lx_nor_flash_close PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function closes a NOR flash instance. */ +/* */ +/* INPUT */ +/* */ +/* nor_flash NOR flash instance */ +/* */ +/* OUTPUT */ +/* */ +/* return status */ +/* */ +/* CALLS */ +/* */ +/* tx_mutex_delete Delete thread-safe mutex */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 William E. Lamie Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _lx_nor_flash_close(LX_NOR_FLASH *nor_flash) +{ + +TX_INTERRUPT_SAVE_AREA + + + /* Lockout interrupts for NOR flash close. */ + TX_DISABLE + + /* See if the media is the only one on the media opened list. */ + if ((_lx_nor_flash_opened_ptr == nor_flash) && + (_lx_nor_flash_opened_ptr == nor_flash -> lx_nor_flash_open_next) && + (_lx_nor_flash_opened_ptr == nor_flash -> lx_nor_flash_open_previous)) + { + + /* Only opened NOR flash, just set the opened list to NULL. */ + _lx_nor_flash_opened_ptr = LX_NULL; + } + else + { + + /* Otherwise, not the only opened NOR flash, link-up the neighbors. */ + (nor_flash -> lx_nor_flash_open_next) -> lx_nor_flash_open_previous = + nor_flash -> lx_nor_flash_open_previous; + (nor_flash -> lx_nor_flash_open_previous) -> lx_nor_flash_open_next = + nor_flash -> lx_nor_flash_open_next; + + /* See if we have to update the opened list head pointer. */ + if (_lx_nor_flash_opened_ptr == nor_flash) + { + + /* Yes, move the head pointer to the next opened NOR flash. */ + _lx_nor_flash_opened_ptr = nor_flash -> lx_nor_flash_open_next; + } + } + + /* Decrement the opened NOR flash counter. */ + _lx_nor_flash_opened_count--; + + /* Finally, indicate that this NOR flash is closed. */ + nor_flash -> lx_nor_flash_state = LX_NOR_FLASH_CLOSED; + + /* Restore interrupt posture. */ + TX_RESTORE + +#ifdef LX_THREAD_SAFE_ENABLE + + /* Delete the thread safe mutex. */ + tx_mutex_delete(&nor_flash -> lx_nor_flash_mutex); +#endif + /* Return success. */ + return(LX_SUCCESS); +} + + diff --git a/common/src/lx_nor_flash_defragment.c b/common/src/lx_nor_flash_defragment.c new file mode 100644 index 0000000..e709efe --- /dev/null +++ b/common/src/lx_nor_flash_defragment.c @@ -0,0 +1,112 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** LevelX Component */ +/** */ +/** NOR Flash */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define LX_SOURCE_CODE + + +/* Disable ThreadX error checking. */ + +#ifndef TX_DISABLE_ERROR_CHECKING +#define TX_DISABLE_ERROR_CHECKING +#endif + + +/* Include necessary system files. */ + +#include "lx_api.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _lx_nor_flash_defragment PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function defragments the NOR flash. */ +/* */ +/* INPUT */ +/* */ +/* nor_flash NOR flash instance */ +/* */ +/* OUTPUT */ +/* */ +/* return status */ +/* */ +/* CALLS */ +/* */ +/* _lx_nor_flash_block_reclaim Reclaim a NOR flash block */ +/* tx_mutex_get Get thread protection */ +/* tx_mutex_put Release thread protection */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* Internal LevelX */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 William E. Lamie Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _lx_nor_flash_defragment(LX_NOR_FLASH *nor_flash) +{ + +ULONG i; + + +#ifdef LX_THREAD_SAFE_ENABLE + + /* Obtain the thread safe mutex. */ + tx_mutex_get(&nor_flash -> lx_nor_flash_mutex, TX_WAIT_FOREVER); +#endif + + /* Loop for max number of blocks, while there are obsolete count. */ + for (i = 0; i < nor_flash -> lx_nor_flash_total_blocks; i++) + { + + /* Determine if there is any more defragment work. */ + if (nor_flash -> lx_nor_flash_obsolete_physical_sectors == 0) + break; + + /* Call the block reclaim function to defragment. */ + _lx_nor_flash_block_reclaim(nor_flash); + } + +#ifdef LX_THREAD_SAFE_ENABLE + + /* Release the thread safe mutex. */ + tx_mutex_put(&nor_flash -> lx_nor_flash_mutex); +#endif + + /* Return successful completion. */ + return(LX_SUCCESS); +} + + diff --git a/common/src/lx_nor_flash_driver_block_erase.c b/common/src/lx_nor_flash_driver_block_erase.c new file mode 100644 index 0000000..01cc309 --- /dev/null +++ b/common/src/lx_nor_flash_driver_block_erase.c @@ -0,0 +1,123 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** LevelX Component */ +/** */ +/** NOR Flash */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define LX_SOURCE_CODE + + +/* Disable ThreadX error checking. */ + +#ifndef TX_DISABLE_ERROR_CHECKING +#define TX_DISABLE_ERROR_CHECKING +#endif + + +/* Include necessary system files. */ + +#include "lx_api.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _lx_nor_flash_driver_block_erase PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function performs a NOR flash block erase. */ +/* */ +/* INPUT */ +/* */ +/* nor_flash NOR flash instance */ +/* block Block number to erase */ +/* erase_count Erase count for this block */ +/* */ +/* OUTPUT */ +/* */ +/* return status */ +/* */ +/* CALLS */ +/* */ +/* (lx_nor_flash_driver_block_erase) Actual driver block erase */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 William E. Lamie Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _lx_nor_flash_driver_block_erase(LX_NOR_FLASH *nor_flash, ULONG block, ULONG erase_count) +{ + +UINT status; + +#ifndef LX_NOR_DISABLE_EXTENDED_CACHE + +UINT i; +ULONG *block_start_address; +ULONG *block_end_address; +ULONG *cache_entry_start; +ULONG *cache_entry_end; + + + /* Calculate the block starting address. */ + block_start_address = nor_flash -> lx_nor_flash_base_address + (block * nor_flash -> lx_nor_flash_words_per_block); + block_end_address = block_start_address + nor_flash -> lx_nor_flash_words_per_block; + + /* Loop through the cache entries to see if there is a sector in cache. */ + for (i = 0; i < nor_flash -> lx_nor_flash_extended_cache_entries; i++) + { + + /* Search through the cache to see if this cache entry needs to be invalidated. */ + + /* Determine the cache entry addresses. */ + cache_entry_start = nor_flash -> lx_nor_flash_extended_cache[i].lx_nor_flash_extended_cache_entry_sector_address; + cache_entry_end = cache_entry_start + LX_NOR_SECTOR_SIZE; + + /* Determine if the flash address in in the cache entry. */ + if ((cache_entry_start) && (block_start_address <= cache_entry_start) && (block_end_address > cache_entry_end)) + { + + /* Yes, this cache entry is in the block to be erased so invalidate it. */ + nor_flash -> lx_nor_flash_extended_cache[i].lx_nor_flash_extended_cache_entry_sector_address = LX_NULL; + nor_flash -> lx_nor_flash_extended_cache[i].lx_nor_flash_extended_cache_entry_access_count = 0; + } + } +#endif + + /* Call the actual driver block erase function. */ + status = (nor_flash -> lx_nor_flash_driver_block_erase)(block, erase_count); + + /* Return completion status. */ + return(status); +} + + diff --git a/common/src/lx_nor_flash_driver_read.c b/common/src/lx_nor_flash_driver_read.c new file mode 100644 index 0000000..1ee317e --- /dev/null +++ b/common/src/lx_nor_flash_driver_read.c @@ -0,0 +1,207 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** LevelX Component */ +/** */ +/** NOR Flash */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define LX_SOURCE_CODE + + +/* Disable ThreadX error checking. */ + +#ifndef TX_DISABLE_ERROR_CHECKING +#define TX_DISABLE_ERROR_CHECKING +#endif + + +/* Include necessary system files. */ + +#include "lx_api.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _lx_nor_flash_extended_cache_read PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function performs a read of the NOR flash memory. */ +/* */ +/* INPUT */ +/* */ +/* nor_flash NOR flash instance */ +/* flash_address Address of NOR flash to read */ +/* destination Destination for the read */ +/* words Number of words to read */ +/* */ +/* OUTPUT */ +/* */ +/* return status */ +/* */ +/* CALLS */ +/* */ +/* (lx_nor_flash_driver_read) Actual driver read */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 William E. Lamie Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _lx_nor_flash_driver_read(LX_NOR_FLASH *nor_flash, ULONG *flash_address, ULONG *destination, ULONG words) +{ +#ifndef LX_NOR_DISABLE_EXTENDED_CACHE + +UINT status; +UINT i; +ULONG *cache_entry_start; +ULONG *cache_entry_end; +ULONG cache_offset; +UINT least_used_cache_entry; + + + /* Is the request a whole sector or a partial sector. */ + if ((words == 1) && (nor_flash -> lx_nor_flash_extended_cache_entries)) + { + + /* One word request, which implies that it is a NOR flash metadata read. */ + + + /* Initialize the least used cache entry. */ + least_used_cache_entry = 0; + + do + { + + /* Loop through the cache entries to see if there is a sector in cache. */ + for (i = 0; i < nor_flash -> lx_nor_flash_extended_cache_entries; i++) + { + + /* Search through the cache to find the entry. */ + + /* Determine the cache entry addresses. */ + cache_entry_start = nor_flash -> lx_nor_flash_extended_cache[i].lx_nor_flash_extended_cache_entry_sector_address; + cache_entry_end = cache_entry_start + LX_NOR_SECTOR_SIZE; + + /* Determine if the flash address in in the cache entry. */ + if ((cache_entry_start) && (flash_address >= cache_entry_start) && (flash_address < cache_entry_end)) + { + + /* Yes, we found the entry. */ + + /* Increment the accessed count. */ + nor_flash -> lx_nor_flash_extended_cache[i].lx_nor_flash_extended_cache_entry_access_count++; + + /* Calculate the offset into the cache entry. */ + cache_offset = (ULONG)(flash_address - cache_entry_start); + + /* Copy the word from the cache. */ + *destination = *(nor_flash -> lx_nor_flash_extended_cache[i].lx_nor_flash_extended_cache_entry_sector_memory + cache_offset); + + /* Increment the number of cache hits. */ + nor_flash -> lx_nor_flash_extended_cache_hits++; + + /* Return success. */ + return(LX_SUCCESS); + } + else + { + + /* Determine if we have a new least used sector. */ + if (i != least_used_cache_entry) + { + + /* Determine if this entry has a smaller accessed count. */ + if (nor_flash -> lx_nor_flash_extended_cache[i].lx_nor_flash_extended_cache_entry_access_count < + nor_flash -> lx_nor_flash_extended_cache[least_used_cache_entry].lx_nor_flash_extended_cache_entry_access_count) + { + + /* New least used entry. */ + least_used_cache_entry = i; + } + } + } + } + + /* Now read in the sector into the cache. */ + cache_offset = (ULONG)(flash_address - nor_flash -> lx_nor_flash_base_address); + cache_offset = cache_offset & ~((ULONG) (LX_NOR_SECTOR_SIZE-1)); + cache_entry_start = nor_flash -> lx_nor_flash_base_address + cache_offset; + + /* Call the actual driver read function. */ + status = (nor_flash -> lx_nor_flash_driver_read)(cache_entry_start, + nor_flash -> lx_nor_flash_extended_cache[least_used_cache_entry].lx_nor_flash_extended_cache_entry_sector_memory, + LX_NOR_SECTOR_SIZE); + + /* Determine if there was an error. */ + if (status != LX_SUCCESS) + { + + /* Return the error to the caller. */ + return(status); + } + + /* Setup the cache entry. */ + nor_flash -> lx_nor_flash_extended_cache[least_used_cache_entry].lx_nor_flash_extended_cache_entry_sector_address = cache_entry_start; + nor_flash -> lx_nor_flash_extended_cache[least_used_cache_entry].lx_nor_flash_extended_cache_entry_access_count = 0; + + /* Increment the number of cache misses. */ + nor_flash -> lx_nor_flash_extended_cache_misses++; + + /* Decrement the number of cache hits, so that the increment that will happen next will be cancelled out. */ + nor_flash -> lx_nor_flash_extended_cache_hits--; + + } while (status == LX_SUCCESS); + + /* Return success. */ + return(LX_SUCCESS); + } + else + { + + /* Call the actual driver read function. */ + status = (nor_flash -> lx_nor_flash_driver_read)(flash_address, destination, words); + + /* Return completion status. */ + return(status); + } +#else +UINT status; + + + /* Call the actual driver read function. */ + status = (nor_flash -> lx_nor_flash_driver_read)(flash_address, destination, words); + + /* Return completion status. */ + return(status); +#endif +} + + diff --git a/common/src/lx_nor_flash_driver_write.c b/common/src/lx_nor_flash_driver_write.c new file mode 100644 index 0000000..358ebef --- /dev/null +++ b/common/src/lx_nor_flash_driver_write.c @@ -0,0 +1,141 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** LevelX Component */ +/** */ +/** NOR Flash */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define LX_SOURCE_CODE + + +/* Disable ThreadX error checking. */ + +#ifndef TX_DISABLE_ERROR_CHECKING +#define TX_DISABLE_ERROR_CHECKING +#endif + + +/* Include necessary system files. */ + +#include "lx_api.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _lx_nor_flash_driver_write PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function performs a write of the NOR flash memory. */ +/* */ +/* INPUT */ +/* */ +/* nor_flash NOR flash instance */ +/* flash_address Address of NOR flash to write */ +/* source Source for the write */ +/* words Number of words to write */ +/* */ +/* OUTPUT */ +/* */ +/* return status */ +/* */ +/* CALLS */ +/* */ +/* (lx_nor_flash_driver_write) Actual driver write */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 William E. Lamie Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _lx_nor_flash_driver_write(LX_NOR_FLASH *nor_flash, ULONG *flash_address, ULONG *source, ULONG words) +{ + +#ifndef LX_NOR_DISABLE_EXTENDED_CACHE + +UINT status; +UINT i; +ULONG *cache_entry_start; +ULONG *cache_entry_end; +ULONG cache_offset; + + + /* Is the request a whole sector or a partial sector. */ + if ((words == 1) && (nor_flash -> lx_nor_flash_extended_cache_entries)) + { + + /* One word request, which implies that it is a NOR flash metadata write. */ + + /* Loop through the cache entries to see if there is a sector in cache. */ + for (i = 0; i < nor_flash -> lx_nor_flash_extended_cache_entries; i++) + { + + /* Search through the cache to see if there is a cache entry. */ + + /* Determine the cache entry addresses. */ + cache_entry_start = nor_flash -> lx_nor_flash_extended_cache[i].lx_nor_flash_extended_cache_entry_sector_address; + cache_entry_end = cache_entry_start + LX_NOR_SECTOR_SIZE; + + /* Determine if the flash address in in the cache entry. */ + if ((cache_entry_start) && (flash_address >= cache_entry_start) && (flash_address < cache_entry_end)) + { + + /* Yes, we found the entry. */ + + /* Calculate the offset into the cache entry. */ + cache_offset = (ULONG)(flash_address - cache_entry_start); + + /* Copy the word into the cache. */ + *(nor_flash -> lx_nor_flash_extended_cache[i].lx_nor_flash_extended_cache_entry_sector_memory + cache_offset) = *source; + + /* Get out of the loop. */ + break; + } + } + } + + /* In any case, call the actual driver write function. */ + status = (nor_flash -> lx_nor_flash_driver_write)(flash_address, source, words); + + /* Return completion status. */ + return(status); + +#else +UINT status; + + + /* Call the actual driver write function. */ + status = (nor_flash -> lx_nor_flash_driver_write)(flash_address, source, words); + + /* Return completion status. */ + return(status); +#endif +} + diff --git a/common/src/lx_nor_flash_extended_cache_enable.c b/common/src/lx_nor_flash_extended_cache_enable.c new file mode 100644 index 0000000..f94a199 --- /dev/null +++ b/common/src/lx_nor_flash_extended_cache_enable.c @@ -0,0 +1,147 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** LevelX Component */ +/** */ +/** NOR Flash */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define LX_SOURCE_CODE + + +/* Disable ThreadX error checking. */ + +#ifndef TX_DISABLE_ERROR_CHECKING +#define TX_DISABLE_ERROR_CHECKING +#endif + + +/* Include necessary system files. */ + +#include "lx_api.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _lx_nor_flash_extended_cache_enable PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function enables or disables the extended cache. */ +/* */ +/* INPUT */ +/* */ +/* nor_flash NOR flash instance */ +/* memory Address of RAM for cache */ +/* size Size of the RAM for cache */ +/* */ +/* OUTPUT */ +/* */ +/* return status */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 William E. Lamie Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _lx_nor_flash_extended_cache_enable(LX_NOR_FLASH *nor_flash, VOID *memory, ULONG size) +{ +#ifndef LX_NOR_DISABLE_EXTENDED_CACHE + +UINT i; +ULONG cache_size; +ULONG *cache_memory; + + + /* Determine if memory was specified but with an invalid size (less than one NOR sector). */ + if ((memory) && (size < LX_NOR_SECTOR_SIZE)) + { + + /* Error in memory size supplied. */ + return(LX_ERROR); + } + +#ifdef LX_THREAD_SAFE_ENABLE + + /* Obtain the thread safe mutex. */ + tx_mutex_get(&nor_flash -> lx_nor_flash_mutex, TX_WAIT_FOREVER); +#endif + + /* Initialize the internal NOR cache. */ + nor_flash -> lx_nor_flash_extended_cache_entries = 0; + + /* Calculate cache size in words. */ + cache_size = size/sizeof(ULONG); + + /* Setup cache memory pointer. */ + cache_memory = (ULONG *) memory; + + /* Loop through the memory supplied and assign to cache entries. */ + i = 0; + while (cache_size >= LX_NOR_SECTOR_SIZE) + { + + /* Setup this cache entry. */ + nor_flash -> lx_nor_flash_extended_cache[i].lx_nor_flash_extended_cache_entry_sector_address = LX_NULL; + nor_flash -> lx_nor_flash_extended_cache[i].lx_nor_flash_extended_cache_entry_sector_memory = cache_memory; + nor_flash -> lx_nor_flash_extended_cache[i].lx_nor_flash_extended_cache_entry_access_count = 0; + + /* Move the cache memory forward. */ + cache_memory = cache_memory + LX_NOR_SECTOR_SIZE; + + /* Decrement the size. */ + cache_size = cache_size - LX_NOR_SECTOR_SIZE; + + /* Move to next cache entry. */ + i++; + } + + /* Save the number of cache entries. */ + nor_flash -> lx_nor_flash_extended_cache_entries = i; + +#ifdef LX_THREAD_SAFE_ENABLE + + /* Release the thread safe mutex. */ + tx_mutex_put(&nor_flash -> lx_nor_flash_mutex); +#endif + + /* Return successful completion. */ + return(LX_SUCCESS); +#else + + /* Return disabled error message. */ + return(LX_DISABLED); +#endif +} + + diff --git a/common/src/lx_nor_flash_initialize.c b/common/src/lx_nor_flash_initialize.c new file mode 100644 index 0000000..4e94e88 --- /dev/null +++ b/common/src/lx_nor_flash_initialize.c @@ -0,0 +1,84 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** LevelX Component */ +/** */ +/** NOR Flash */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define LX_SOURCE_CODE + + +/* Include necessary system files. */ + +#include "lx_api.h" + + +/* Define the NOR flash opened list head and opened count. */ + +LX_NOR_FLASH *_lx_nor_flash_opened_ptr; +ULONG _lx_nor_flash_opened_count; + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _lx_nor_flash_initialize PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function initializes the NOR flash data structures. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* return status */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 William E. Lamie Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _lx_nor_flash_initialize(void) +{ + + /* Clear open list head pointer and opened count. */ + _lx_nor_flash_opened_ptr = LX_NULL; + _lx_nor_flash_opened_count = 0; + + /* Return success! */ + return(LX_SUCCESS); +} + diff --git a/common/src/lx_nor_flash_logical_sector_find.c b/common/src/lx_nor_flash_logical_sector_find.c new file mode 100644 index 0000000..25d0269 --- /dev/null +++ b/common/src/lx_nor_flash_logical_sector_find.c @@ -0,0 +1,475 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** LevelX Component */ +/** */ +/** NOR Flash */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define LX_SOURCE_CODE + + +/* Disable ThreadX error checking. */ + +#ifndef TX_DISABLE_ERROR_CHECKING +#define TX_DISABLE_ERROR_CHECKING +#endif + + +/* Include necessary system files. */ + +#include "lx_api.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _lx_nor_flash_logical_sector_find PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function attempts to find the specified logical sector in */ +/* the NOR flash. */ +/* */ +/* INPUT */ +/* */ +/* nor_flash NOR flash instance */ +/* logical_sector Logical sector number */ +/* superceded_check Check for sector being */ +/* superceded (can happen if */ +/* on interruptions of sector */ +/* write) */ +/* physical_sector_map_entry Destination for physical */ +/* sector map entry address */ +/* physical_sector_address Destination for physical */ +/* sector data */ +/* */ +/* OUTPUT */ +/* */ +/* return status */ +/* */ +/* CALLS */ +/* */ +/* _lx_nor_flash_driver_read Driver flash sector read */ +/* _lx_nor_flash_system_error Internal system error handler */ +/* */ +/* CALLED BY */ +/* */ +/* Internal LevelX */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 William E. Lamie Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _lx_nor_flash_logical_sector_find(LX_NOR_FLASH *nor_flash, ULONG logical_sector, ULONG superceded_check, ULONG **physical_sector_map_entry, ULONG **physical_sector_address) +{ + +ULONG *block_word_ptr; +ULONG *list_word_ptr; +ULONG list_word; +ULONG min_logical_sector; +ULONG max_logical_sector; +ULONG mapped_sectors; +ULONG total_blocks; +ULONG total_sectors; +ULONG i, j; +ULONG search_start; +LX_NOR_SECTOR_MAPPING_CACHE_ENTRY *sector_mapping_cache_entry_ptr = LX_NULL; +LX_NOR_SECTOR_MAPPING_CACHE_ENTRY temp_sector_mapping_cache_entry; +#ifndef LX_DIRECT_READ +UINT status; +#endif + + + /* Initialize the return parameters. */ + *physical_sector_map_entry = (ULONG *) 0; + *physical_sector_address = (ULONG *) 0; + + /* Determine if there are any mapped physical sectors. */ + if (nor_flash -> lx_nor_flash_mapped_physical_sectors == 0) + { + + /* No mapped sector so nothing can be found!. */ + return(LX_SECTOR_NOT_FOUND); + } + + /* Determine if the sector mapping cache is enabled. */ + if (nor_flash -> lx_nor_flash_sector_mapping_cache_enabled) + { + + /* Calculate the starting index of the sector cache for this sector entry. */ + i = (logical_sector & LX_NOR_SECTOR_MAPPING_CACHE_HASH_MASK) * LX_NOR_SECTOR_MAPPING_CACHE_DEPTH; + + /* Build a pointer to the cache entry. */ + sector_mapping_cache_entry_ptr = &nor_flash -> lx_nor_flash_sector_mapping_cache[i]; + + /* Determine if the sector is in the sector mapping cache - assuming the depth of the sector + mapping cache is LX_NOR_SECTOR_MAPPING_CACHE_DEPTH entries. */ + if ((sector_mapping_cache_entry_ptr -> lx_nor_sector_mapping_cache_logical_sector) == (logical_sector | LX_NOR_SECTOR_MAPPING_CACHE_ENTRY_VALID)) + { + + /* Increment the sector mapping cache hit counter. */ + nor_flash -> lx_nor_flash_sector_mapping_cache_hits++; + + /* Yes, return the cached values associated with the sector. */ + *physical_sector_map_entry = sector_mapping_cache_entry_ptr -> lx_nor_sector_mapping_cache_physical_sector_map_entry; + *physical_sector_address = sector_mapping_cache_entry_ptr -> lx_nor_sector_mapping_cache_physical_sector_address; + + /* Don't move anything since we found the entry at the top. */ + + /* Return a successful status. */ + return(LX_SUCCESS); + } + else if (((sector_mapping_cache_entry_ptr + 1) -> lx_nor_sector_mapping_cache_logical_sector) == (logical_sector | LX_NOR_SECTOR_MAPPING_CACHE_ENTRY_VALID)) + { + + /* Increment the sector mapping cache hit counter. */ + nor_flash -> lx_nor_flash_sector_mapping_cache_hits++; + + /* Yes, return the cached values associated with the sector. */ + *physical_sector_map_entry = (sector_mapping_cache_entry_ptr + 1) -> lx_nor_sector_mapping_cache_physical_sector_map_entry; + *physical_sector_address = (sector_mapping_cache_entry_ptr + 1) -> lx_nor_sector_mapping_cache_physical_sector_address; + + /* Just swap the first and second entry. */ + temp_sector_mapping_cache_entry = *(sector_mapping_cache_entry_ptr); + *(sector_mapping_cache_entry_ptr) = *(sector_mapping_cache_entry_ptr + 1); + *(sector_mapping_cache_entry_ptr + 1) = temp_sector_mapping_cache_entry; + + /* Return a successful status. */ + return(LX_SUCCESS); + } + else if (((sector_mapping_cache_entry_ptr + 2) -> lx_nor_sector_mapping_cache_logical_sector) == (logical_sector | LX_NOR_SECTOR_MAPPING_CACHE_ENTRY_VALID)) + { + + /* Increment the sector mapping cache hit counter. */ + nor_flash -> lx_nor_flash_sector_mapping_cache_hits++; + + /* Yes, return the cached value. */ + *physical_sector_map_entry = (sector_mapping_cache_entry_ptr + 2) -> lx_nor_sector_mapping_cache_physical_sector_map_entry; + *physical_sector_address = (sector_mapping_cache_entry_ptr + 2) -> lx_nor_sector_mapping_cache_physical_sector_address; + + /* Move the third entry to the top and the first two entries down. */ + temp_sector_mapping_cache_entry = *(sector_mapping_cache_entry_ptr); + *(sector_mapping_cache_entry_ptr) = *(sector_mapping_cache_entry_ptr + 2); + *(sector_mapping_cache_entry_ptr + 2) = *(sector_mapping_cache_entry_ptr + 1); + *(sector_mapping_cache_entry_ptr + 1) = temp_sector_mapping_cache_entry; + + /* Return a successful status. */ + return(LX_SUCCESS); + } + else if (((sector_mapping_cache_entry_ptr + 3) -> lx_nor_sector_mapping_cache_logical_sector) == (logical_sector | LX_NOR_SECTOR_MAPPING_CACHE_ENTRY_VALID)) + { + + /* Increment the sector mapping cache hit counter. */ + nor_flash -> lx_nor_flash_sector_mapping_cache_hits++; + + /* Yes, return the cached value. */ + *physical_sector_map_entry = (sector_mapping_cache_entry_ptr + 3) -> lx_nor_sector_mapping_cache_physical_sector_map_entry; + *physical_sector_address = (sector_mapping_cache_entry_ptr + 3) -> lx_nor_sector_mapping_cache_physical_sector_address; + + /* Move the last entry to the top and the first three entries down. */ + temp_sector_mapping_cache_entry = *(sector_mapping_cache_entry_ptr); + *(sector_mapping_cache_entry_ptr) = *(sector_mapping_cache_entry_ptr + 3); + *(sector_mapping_cache_entry_ptr + 3) = *(sector_mapping_cache_entry_ptr + 2); + *(sector_mapping_cache_entry_ptr + 2) = *(sector_mapping_cache_entry_ptr + 1); + *(sector_mapping_cache_entry_ptr + 1) = temp_sector_mapping_cache_entry; + + /* Return a successful status. */ + return(LX_SUCCESS); + } + + /* If we get here, we have a cache miss so increment the counter before we fall through the loop. */ + nor_flash -> lx_nor_flash_sector_mapping_cache_misses++; + } + + /* Setup the total number of mapped sectors. */ + mapped_sectors = nor_flash -> lx_nor_flash_mapped_physical_sectors; + + /* Start searching from the last found block. */ + i = nor_flash -> lx_nor_flash_found_block_search; + + /* Setup the starting sector to look at. */ + j = nor_flash -> lx_nor_flash_found_sector_search; + + /* Pickup the total number of blocks. */ + total_blocks = nor_flash -> lx_nor_flash_total_blocks; + + /* Loop through the blocks to attempt to find the mapped logical sector. */ + while (total_blocks--) + { + + /* Setup the block word pointer to the first word of the search block. */ + block_word_ptr = (nor_flash -> lx_nor_flash_base_address + (i * nor_flash -> lx_nor_flash_words_per_block)); + + /* Determine if the minimum and maximum logical sector values are present in the block header. If these are + present, we can quickly skip blocks that don't have our sector. */ + + /* Read the minimum and maximum logical sector values in this block. */ +#ifdef LX_DIRECT_READ + + /* Read the word directly. */ + min_logical_sector = *(block_word_ptr + LX_NOR_FLASH_MIN_LOGICAL_SECTOR_OFFSET); +#else + status = _lx_nor_flash_driver_read(nor_flash, block_word_ptr + LX_NOR_FLASH_MIN_LOGICAL_SECTOR_OFFSET, &min_logical_sector, 1); + + /* Check for an error from flash driver. Drivers should never return an error.. */ + if (status) + { + + /* Call system error handler. */ + _lx_nor_flash_system_error(nor_flash, status); + + /* Return the error. */ + return(status); + } +#endif +#ifdef LX_DIRECT_READ + + /* Read the word directly. */ + max_logical_sector = *(block_word_ptr + LX_NOR_FLASH_MAX_LOGICAL_SECTOR_OFFSET); +#else + status = _lx_nor_flash_driver_read(nor_flash, block_word_ptr + LX_NOR_FLASH_MAX_LOGICAL_SECTOR_OFFSET, &max_logical_sector, 1); + + /* Check for an error from flash driver. Drivers should never return an error.. */ + if (status) + { + + /* Call system error handler. */ + _lx_nor_flash_system_error(nor_flash, status); + + /* Return the error. */ + return(status); + } +#endif + + /* Are the values valid? */ + if ((min_logical_sector != LX_ALL_ONES) && (max_logical_sector != LX_ALL_ONES)) + { + + /* Now let's check to see if the search sector is within this range. */ + if ((logical_sector < min_logical_sector) || (logical_sector > max_logical_sector)) + { + + /* Move to the next block. */ + i++; + + /* Determine if we have wrapped. */ + if (i >= nor_flash -> lx_nor_flash_total_blocks) + { + + /* Yes, we have wrapped, set to block 0. */ + i = 0; + } + + /* Start at the first sector in the next block. */ + j = 0; + + /* No point in looking further into this block, just continue the loop. */ + continue; + } + } + + /* Setup the total number of sectors. */ + total_sectors = nor_flash -> lx_nor_flash_physical_sectors_per_block; + + /* Remember the start of the search. */ + search_start = j; + + /* Now search through the sector list to find a match. */ + while (total_sectors--) + { + + /* Setup a pointer to the mapped list. */ + list_word_ptr = block_word_ptr + nor_flash -> lx_nor_flash_block_physical_sector_mapping_offset + j; + + + /* Read in the mapped list for this block. */ +#ifdef LX_DIRECT_READ + + /* Read the word directly. */ + list_word = *(list_word_ptr); +#else + status = _lx_nor_flash_driver_read(nor_flash, list_word_ptr, &list_word, 1); + + /* Check for an error from flash driver. Drivers should never return an error.. */ + if (status) + { + + /* Call system error handler. */ + _lx_nor_flash_system_error(nor_flash, status); + + /* Return the error. */ + return(status); + } +#endif + + /* Determine if the entry hasn't been used. */ + if (list_word == LX_NOR_PHYSICAL_SECTOR_FREE) + { + + /* Since the mapping is done sequentially in the block, we know nothing + else exists after this point. */ + + /* Determine if the search started at the beginning of the block. */ + if (search_start == 0) + { + + /* Yes, we started at the beginning of the block. We are now done with this block. */ + break; + } + else + { + + /* Setup the new total to the search start. */ + total_sectors = search_start; + + /* Clear search start. */ + search_start = 0; + + /* Start search over. */ + j = 0; + continue; + } + } + + /* Is this entry valid? */ + if ((list_word & (LX_NOR_PHYSICAL_SECTOR_VALID | LX_NOR_PHYSICAL_SECTOR_MAPPING_NOT_VALID)) == LX_NOR_PHYSICAL_SECTOR_VALID) + { + + /* Decrement the number of mapped sectors. */ + mapped_sectors--; + + /* Do we have a valid sector match? */ + if ((list_word & LX_NOR_LOGICAL_SECTOR_MASK) == logical_sector) + { + + /* Determine if we care about the superceded bit. */ + if (superceded_check == LX_FALSE) + { + + /* Prepare the return information. */ + *physical_sector_map_entry = list_word_ptr; + *physical_sector_address = block_word_ptr + nor_flash -> lx_nor_flash_block_physical_sector_offset + (j * LX_NOR_SECTOR_SIZE); + + /* Determine if the sector mapping cache is enabled. */ + if (nor_flash -> lx_nor_flash_sector_mapping_cache_enabled) + { + + /* Yes, update the cache with the sector mapping. */ + + /* Move all the cache entries down so the oldest is at the bottom. */ + *(sector_mapping_cache_entry_ptr + 3) = *(sector_mapping_cache_entry_ptr + 2); + *(sector_mapping_cache_entry_ptr + 2) = *(sector_mapping_cache_entry_ptr + 1); + *(sector_mapping_cache_entry_ptr + 1) = *(sector_mapping_cache_entry_ptr); + + /* Setup the new sector information in the cache. */ + sector_mapping_cache_entry_ptr -> lx_nor_sector_mapping_cache_logical_sector = (logical_sector | LX_NOR_SECTOR_MAPPING_CACHE_ENTRY_VALID); + sector_mapping_cache_entry_ptr -> lx_nor_sector_mapping_cache_physical_sector_map_entry = *physical_sector_map_entry; + sector_mapping_cache_entry_ptr -> lx_nor_sector_mapping_cache_physical_sector_address = *physical_sector_address; + } + + /* Remember the last found block for next search. */ + nor_flash -> lx_nor_flash_found_block_search = i; + + /* Remember the last found sector. */ + nor_flash -> lx_nor_flash_found_sector_search = j+1; + + /* Has this wrapped around? */ + if (nor_flash -> lx_nor_flash_found_sector_search >= nor_flash -> lx_nor_flash_physical_sectors_per_block) + { + + /* Reset to the beginning sector. */ + nor_flash -> lx_nor_flash_found_sector_search = 0; + } + + /* Return success! */ + return(LX_SUCCESS); + } + + /* Check for the superceded bit being clear, which means the sector was superceded. */ + else if (list_word & LX_NOR_PHYSICAL_SECTOR_SUPERCEDED) + { + + /* Prepare the return information. */ + *physical_sector_map_entry = list_word_ptr; + *physical_sector_address = block_word_ptr + nor_flash -> lx_nor_flash_block_physical_sector_offset + (j * LX_NOR_SECTOR_SIZE); + + /* No need to update the cache here, since this condition only happens during initialization. */ + + /* Remember the last found block for next search. */ + nor_flash -> lx_nor_flash_found_block_search = i; + + /* Remember the last found sector. */ + nor_flash -> lx_nor_flash_found_sector_search = j+1; + + /* Has this wrapped around? */ + if (nor_flash -> lx_nor_flash_found_sector_search >= nor_flash -> lx_nor_flash_physical_sectors_per_block) + { + + /* Reset to the beginning sector. */ + nor_flash -> lx_nor_flash_found_sector_search = 0; + } + + /* Return success! */ + return(LX_SUCCESS); + } + } + } + + /* Move to the next list entry. */ + j++; + + /* Check for wrap around. */ + if (j >= nor_flash -> lx_nor_flash_physical_sectors_per_block) + { + + /* Yes, wrap around, go back to the beginning. */ + j = 0; + } + } + + /* Determine if there are any more mapped sectors. */ + if (mapped_sectors == 0) + break; + + /* Move to the next block. */ + i++; + + /* Determine if we have wrapped. */ + if (i >= nor_flash -> lx_nor_flash_total_blocks) + { + + /* Yes, we have wrapped, set to block 0. */ + i = 0; + } + + /* Start at the first sector in the next block. */ + j = 0; + } + + /* Return sector not found status. */ + return(LX_SECTOR_NOT_FOUND); +} + diff --git a/common/src/lx_nor_flash_next_block_to_erase_find.c b/common/src/lx_nor_flash_next_block_to_erase_find.c new file mode 100644 index 0000000..6ca0b3d --- /dev/null +++ b/common/src/lx_nor_flash_next_block_to_erase_find.c @@ -0,0 +1,291 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** LevelX Component */ +/** */ +/** NOR Flash */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define LX_SOURCE_CODE + + +/* Disable ThreadX error checking. */ + +#ifndef TX_DISABLE_ERROR_CHECKING +#define TX_DISABLE_ERROR_CHECKING +#endif + + +/* Include necessary system files. */ + +#include "lx_api.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _lx_nor_flash_next_block_to_erase_find PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function finds the next block to erase in the NOR flash. */ +/* */ +/* INPUT */ +/* */ +/* nor_flash NOR flash instance */ +/* return_erase_block Returned block to erase */ +/* return_erase_count Returned erase count of block */ +/* return_mapped_sectors Returned number of mapped */ +/* sectors */ +/* return_obsolete_sectors Returned number of obsolete */ +/* sectors */ +/* */ +/* OUTPUT */ +/* */ +/* return status */ +/* */ +/* CALLS */ +/* */ +/* _lx_nor_flash_driver_read Driver flash sector read */ +/* _lx_nor_flash_system_error Internal system error handler */ +/* */ +/* CALLED BY */ +/* */ +/* Internal LevelX */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 William E. Lamie Initial Version 6.0 */ +/* */ +/**************************************************************************/ +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) +{ + +ULONG *block_word_ptr; +ULONG *list_word_ptr; +ULONG list_word; +ULONG i, j; +ULONG mapped_sectors; +ULONG erase_count; +ULONG obsolete_sectors; +ULONG min_block_erase = 0; +ULONG min_block_erase_count; +ULONG min_block_obsolete_count = 0; +ULONG min_block_mapped_count = 0; +ULONG max_obsolete_sectors; +ULONG max_obsolete_block = 0; +ULONG max_obsolete_erase_count = 0; +ULONG max_obsolete_mapped_count = 0; +ULONG min_system_block_erase_count; +ULONG max_system_block_erase_count; +ULONG erase_count_threshold; +#ifndef LX_DIRECT_READ +UINT status; +#endif + + + /* Setup the block word pointer to the first word of the search block. */ + block_word_ptr = nor_flash -> lx_nor_flash_base_address; + + /* Initialize the minimum erase count. */ + min_block_erase_count = LX_ALL_ONES; + + /* Initialize the system minimum and maximum erase counts. */ + min_system_block_erase_count = LX_ALL_ONES; + max_system_block_erase_count = 0; + + /* Initialize the maximum obsolete sector count. */ + max_obsolete_sectors = 0; + + /* Calculate the erase count threshold. */ + if (nor_flash -> lx_nor_flash_free_physical_sectors >= nor_flash -> lx_nor_flash_physical_sectors_per_block) + { + + /* Calculate erase count threshold by adding constant to the current minimum. */ + erase_count_threshold = nor_flash -> lx_nor_flash_minimum_erase_count + LX_NOR_FLASH_MAX_ERASE_COUNT_DELTA; + } + else + { + + /* When the number of free sectors is low, simply pick the block that has the most number of obsolete sectors. */ + erase_count_threshold = LX_ALL_ONES; + } + + /* Loop through the blocks to attempt to find the mapped logical sector. */ + for (i = 0; i < nor_flash -> lx_nor_flash_total_blocks; i++) + { + + /* Read the erase count of this block. */ +#ifdef LX_DIRECT_READ + + /* Read the word directly. */ + erase_count = *(block_word_ptr); +#else + status = _lx_nor_flash_driver_read(nor_flash, block_word_ptr, &erase_count, 1); + + /* Check for an error from flash driver. Drivers should never return an error.. */ + if (status) + { + + /* Call system error handler. */ + _lx_nor_flash_system_error(nor_flash, status); + + /* Return the error. */ + return(status); + } +#endif + + /* Update the system minimum and maximum erase counts. */ + if (erase_count < min_system_block_erase_count) + min_system_block_erase_count = erase_count; + if (erase_count > max_system_block_erase_count) + max_system_block_erase_count = erase_count; + + /* Compute the number of obsolete and mapped sectors for this block. */ + obsolete_sectors = 0; + mapped_sectors = 0; + + /* Setup a pointer to the mapped list. */ + list_word_ptr = block_word_ptr + nor_flash -> lx_nor_flash_block_physical_sector_mapping_offset; + + /* Loop through the mapped list for this block. */ + for (j = 0; j < nor_flash -> lx_nor_flash_physical_sectors_per_block; j++) + { + + /* Read the current mapping entry. */ +#ifdef LX_DIRECT_READ + + /* Read the word directly. */ + list_word = *(list_word_ptr); +#else + status = _lx_nor_flash_driver_read(nor_flash, list_word_ptr, &list_word, 1); + + /* Check for an error from flash driver. Drivers should never return an error.. */ + if (status) + { + + /* Call system error handler. */ + _lx_nor_flash_system_error(nor_flash, status); + + /* Return the error. */ + return(status); + } +#endif + + /* Determine if the entry hasn't been used. */ + if (list_word == LX_NOR_PHYSICAL_SECTOR_FREE) + { + + /* Since allocations are done sequentially in the block, we know nothing + else exists after this point. */ + break; + } + + /* Is this entry obsolete? */ + if ((list_word & LX_NOR_PHYSICAL_SECTOR_VALID) == 0) + { + + /* Increment the number of obsolete sectors. */ + obsolete_sectors++; + } + else + { + + /* Increment the number of mapped sectors. */ + mapped_sectors++; + } + + /* Move the list pointer ahead. */ + list_word_ptr++; + } + + /* Determine if we have a block with a new maximum number of obsolete sectors. */ + if ((obsolete_sectors > max_obsolete_sectors) && (erase_count <= erase_count_threshold)) + { + + /* Update the new maximum obsolete sectors and related information. */ + max_obsolete_sectors = obsolete_sectors; + max_obsolete_block = i; + max_obsolete_erase_count = erase_count; + max_obsolete_mapped_count = mapped_sectors; + } + else if ((max_obsolete_sectors) && (obsolete_sectors == max_obsolete_sectors) && (erase_count <= erase_count_threshold)) + { + + /* Another block has the same number of obsolete sectors. Does this new block have a smaller erase + count? */ + if (erase_count < max_obsolete_erase_count) + { + + /* Yes, erase the block with the smaller erase count. */ + max_obsolete_sectors = obsolete_sectors; + max_obsolete_block = i; + max_obsolete_erase_count = erase_count; + max_obsolete_mapped_count = mapped_sectors; + } + } + + /* Determine if we have a new minimum erase count. */ + if (erase_count < min_block_erase_count) + { + + /* Update the new minimum erase count and related information. */ + min_block_erase_count = erase_count; + min_block_erase = i; + min_block_obsolete_count = obsolete_sectors; + min_block_mapped_count = mapped_sectors; + } + + /* Move to the next block. */ + block_word_ptr = block_word_ptr + nor_flash -> lx_nor_flash_words_per_block; + } + + /* Determine if we can erase the block with the most obsolete sectors. */ + if (max_obsolete_sectors) + { + + /* Erase the block with the most obsolete sectors. */ + *return_erase_block = max_obsolete_block; + *return_erase_count = max_obsolete_erase_count; + *return_obsolete_sectors = max_obsolete_sectors; + *return_mapped_sectors = max_obsolete_mapped_count; + } + else + { + + /* Otherwise, choose the block with the smallest erase count. */ + *return_erase_block = min_block_erase; + *return_erase_count = min_block_erase_count; + *return_obsolete_sectors = min_block_obsolete_count; + *return_mapped_sectors = min_block_mapped_count; + } + + /* Update the overall minimum and maximum erase count. */ + nor_flash -> lx_nor_flash_minimum_erase_count = min_system_block_erase_count; + nor_flash -> lx_nor_flash_maximum_erase_count = max_system_block_erase_count; + + /* Return success. */ + return(LX_SUCCESS); +} + diff --git a/common/src/lx_nor_flash_open.c b/common/src/lx_nor_flash_open.c new file mode 100644 index 0000000..a7c3f6b --- /dev/null +++ b/common/src/lx_nor_flash_open.c @@ -0,0 +1,872 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** LevelX Component */ +/** */ +/** NOR Flash */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define LX_SOURCE_CODE + + +/* Disable ThreadX error checking. */ + +#ifndef TX_DISABLE_ERROR_CHECKING +#define TX_DISABLE_ERROR_CHECKING +#endif + + +/* Include necessary system files. */ + +#include "lx_api.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _lx_nor_flash_open PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function opens a NOR flash instance and ensures the NOR flash */ +/* is in a coherent state. */ +/* */ +/* INPUT */ +/* */ +/* nor_flash NOR flash instance */ +/* name Name of NOR flash instance */ +/* nor_driver_initialize Driver initialize */ +/* */ +/* OUTPUT */ +/* */ +/* return status */ +/* */ +/* CALLS */ +/* */ +/* (nor_driver_initialize) Driver initialize */ +/* _lx_nor_flash_driver_read Driver read */ +/* _lx_nor_flash_driver_write Driver write */ +/* (lx_nor_flash_driver_block_erased_verify) */ +/* NOR flash verify block erased */ +/* _lx_nor_flash_driver_block_erase Driver block erase */ +/* _lx_nor_flash_logical_sector_find Find logical sector */ +/* _lx_nor_flash_system_error System error handler */ +/* tx_mutex_create Create thread-safe mutex */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 William E. Lamie Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _lx_nor_flash_open(LX_NOR_FLASH *nor_flash, CHAR *name, UINT (*nor_driver_initialize)(LX_NOR_FLASH *)) +{ +ULONG sectors_per_block; +ULONG sector_map_words; +ULONG bit_map_words; +ULONG bit_map_mask; +ULONG total_header_words; +ULONG header_sectors; +ULONG *block_word_ptr; +ULONG block_word; +ULONG temp; +ULONG free_sectors; +ULONG used_sectors; +ULONG *new_map_entry; +ULONG *new_sector_address; +ULONG erased_count, min_erased_count, max_erased_count, temp_erased_count; +ULONG i, j, k; +UINT status; +#ifdef LX_FREE_SECTOR_DATA_VERIFY +ULONG *sector_word_ptr; +ULONG sector_word; +#endif +LX_NOR_FLASH *tail_ptr; +TX_INTERRUPT_SAVE_AREA + + LX_PARAMETER_NOT_USED(name); + + /* Clear the NOR flash control block. */ + TX_MEMSET(nor_flash, 0, sizeof(LX_NOR_FLASH)); + + /* Call the flash driver's initialization function. */ + (nor_driver_initialize)(nor_flash); + +#ifndef LX_DIRECT_READ + + /* Determine if the driver supplied a RAM buffer for reading the NOR sector if direct read is not + supported. */ + if (nor_flash -> lx_nor_flash_sector_buffer == LX_NULL) + { + + /* Return an error. */ + return(LX_NO_MEMORY); + } +#endif + + /* Setup the offset to the free bit map. */ + nor_flash -> lx_nor_flash_block_free_bit_map_offset = sizeof(LX_NOR_FLASH_BLOCK_HEADER)/sizeof(ULONG); + + /* Calculate the number of bits we need in the free physical sector bit map. Subtract 1 to account for the + flash block header itself. The case where multiple physical sectors are needed for certain sized flash + devices is handled below. */ + sectors_per_block = (nor_flash -> lx_nor_flash_words_per_block / LX_NOR_SECTOR_SIZE) - 1; + + /* Calculate the number of words required for the sector map array. */ + sector_map_words = sectors_per_block; + + /* Calculate the number of words we need for the free physical sector bit map. */ + bit_map_words = (sectors_per_block + 31)/ 32; + + /* Save the number of bit map words. */ + nor_flash -> lx_nor_flash_block_bit_map_words = bit_map_words; + + /* Setup the offset (in words) to the array of physical sector mapping. */ + nor_flash -> lx_nor_flash_block_physical_sector_mapping_offset = nor_flash -> lx_nor_flash_block_free_bit_map_offset + bit_map_words; + + /* Calculate the total number of words required for the flash block header. */ + total_header_words = sizeof(LX_NOR_FLASH_BLOCK_HEADER)/sizeof(ULONG) + bit_map_words + sector_map_words; + + /* Determine if more physical sectors are needed, which can happen on large devices. */ + if (total_header_words <= LX_NOR_SECTOR_SIZE) + { + + /* Round up to the size of 1 physical sector. */ + total_header_words = LX_NOR_SECTOR_SIZE; + } + else + { + + /* Otherwise calculate how many header sectors are necessary. */ + header_sectors = (total_header_words-1)/LX_NOR_SECTOR_SIZE; + + /* Round up to the next sector. */ + header_sectors++; + + /* Compute the total header words, rounding to the next sector. */ + total_header_words = header_sectors * LX_NOR_SECTOR_SIZE; + + /* Adjust the number of sectors per block. */ + sectors_per_block = sectors_per_block - (header_sectors - 1); + } + + /* Save the offset to the sector area. */ + nor_flash -> lx_nor_flash_block_physical_sector_offset = total_header_words; + + /* Save the physical sectors per block and total physical sectors. */ + nor_flash -> lx_nor_flash_physical_sectors_per_block = sectors_per_block; + nor_flash -> lx_nor_flash_total_physical_sectors = nor_flash -> lx_nor_flash_total_blocks * sectors_per_block; + + /* Build the free bit map mask, for the portion of the bit map that is less than 32 bits. */ + if ((sectors_per_block % 32) != 0) + { + bit_map_mask = (ULONG)(1 << (sectors_per_block % 32)); + bit_map_mask = bit_map_mask - 1; + } + else + { + + /* Exactly 32 sectors for the bit map mask. */ + bit_map_mask = LX_ALL_ONES; + } + + /* Save the free bit map mask in the control block. */ + nor_flash -> lx_nor_flash_block_bit_map_mask = bit_map_mask; + + /* Setup default values for the max/min erased counts. */ + min_erased_count = LX_ALL_ONES; + max_erased_count = 0; + + /* Setup the block word pointer to the first word of the first block, which is effectively the + flash base address. */ + block_word_ptr = nor_flash -> lx_nor_flash_base_address; + + /* Loop through the blocks to determine the minimum and maximum erase count. */ + for (i = 0; i < nor_flash -> lx_nor_flash_total_blocks; i++) + { + + /* Pickup the first word of the block. If the flash manager has executed before, this word contains the + erase count for the block. Otherwise, if the word is 0xFFFFFFFF, this flash block was either erased + or this is the first time it was used. */ +#ifdef LX_DIRECT_READ + + /* Read the word directly. */ + block_word = *block_word_ptr; +#else + + + + status = _lx_nor_flash_driver_read(nor_flash, block_word_ptr, &block_word, 1); + + /* Check for an error from flash driver. Drivers should never return an error.. */ + if (status) + { + + /* Call system error handler. */ + _lx_nor_flash_system_error(nor_flash, status); + + /* Return an error. */ + return(LX_ERROR); + } +#endif + + /* Is the block erased? */ + if (((block_word & LX_BLOCK_ERASED) != LX_BLOCK_ERASED) && (block_word != LX_BLOCK_ERASE_STARTED)) + { + + /* No, valid block. Isolate the erased count. */ + erased_count = (block_word & LX_BLOCK_ERASE_COUNT_MASK); + + /* Is this the new minimum? */ + if (erased_count < min_erased_count) + { + + /* Yes, remember the new minimum. */ + min_erased_count = erased_count; + } + + /* Is this the new maximum? */ + if (erased_count > max_erased_count) + { + + /* Yes, remember the new maximum. */ + max_erased_count = erased_count; + } + } + + /* Move to the next flash block. */ + block_word_ptr = block_word_ptr + (nor_flash -> lx_nor_flash_words_per_block); + } + + /* If we haven't found any erased counts, we can assume the flash is completely erased and needs to + be setup for the first time. */ + if (min_erased_count == LX_ALL_ONES) + { + + /* Indicate that this is the initial format. */ + nor_flash -> lx_nor_flash_diagnostic_initial_format = LX_TRUE; + + /* Setup the block word pointer to the first word of the first block, which is effectively the + flash base address. */ + block_word_ptr = nor_flash -> lx_nor_flash_base_address; + + /* Loop through the blocks to setup the flash the fist time. */ + for (i = 0; i < nor_flash -> lx_nor_flash_total_blocks; i++) + { + + /* Setup the free bit map that corresponds to the free physical sectors in this + block. Note that we only need to setup the portion of the free bit map that doesn't + have sectors associated with it. */ + status = _lx_nor_flash_driver_write(nor_flash, block_word_ptr+(nor_flash -> lx_nor_flash_block_free_bit_map_offset + (bit_map_words-1)) , &bit_map_mask, 1); + + /* Check for an error from flash driver. Drivers should never return an error.. */ + if (status) + { + + /* Call system error handler. */ + _lx_nor_flash_system_error(nor_flash, status); + + /* Return an error. */ + return(LX_ERROR); + } + + /* Setup the initial erase count to 1. */ + block_word = ((ULONG) 1); + + /* Write the initial erase count for the block. */ + status = _lx_nor_flash_driver_write(nor_flash, block_word_ptr, &block_word, 1); + + /* Check for an error from flash driver. Drivers should never return an error.. */ + if (status) + { + + /* Call system error handler. */ + _lx_nor_flash_system_error(nor_flash, status); + + /* Return an error. */ + return(LX_ERROR); + } + + /* Update the overall minimum and maximum erase count. */ + nor_flash -> lx_nor_flash_minimum_erase_count = 1; + nor_flash -> lx_nor_flash_maximum_erase_count = 1; + + /* Update the number of free physical sectors. */ + nor_flash -> lx_nor_flash_free_physical_sectors = nor_flash -> lx_nor_flash_free_physical_sectors + sectors_per_block; + + /* Move to the next flash block. */ + block_word_ptr = block_word_ptr + (nor_flash -> lx_nor_flash_words_per_block); + } + } + else + { + + /* At this point, we have a previously managed flash structure. This needs to be traversed to prepare for the + current flash operation. */ + + /* Default the flash free sector search to an invalid value. */ + nor_flash -> lx_nor_flash_free_block_search = nor_flash -> lx_nor_flash_total_blocks; + + /* Setup the block word pointer to the first word of the first block, which is effectively the + flash base address. */ + block_word_ptr = nor_flash -> lx_nor_flash_base_address; + + /* Loop through the blocks. */ + for (i = 0; i < nor_flash -> lx_nor_flash_total_blocks; i++) + { + + /* First, determine if this block has a valid erase count. */ +#ifdef LX_DIRECT_READ + + /* Read the word directly. */ + block_word = *block_word_ptr; +#else + status = _lx_nor_flash_driver_read(nor_flash, block_word_ptr, &block_word, 1); + + /* Check for an error from flash driver. Drivers should never return an error.. */ + if (status) + { + + /* Call system error handler. */ + _lx_nor_flash_system_error(nor_flash, status); + + /* Return an error. */ + return(LX_ERROR); + } +#endif + + /* Is the block erased? */ + if (((block_word & LX_BLOCK_ERASED) == LX_BLOCK_ERASED) || (block_word == LX_BLOCK_ERASE_STARTED)) + { + + /* This can happen if we were previously in the process of erasing the flash block and a + power interruption occurs. It should only occur once though. */ + + /* Is this the first time? */ + if (nor_flash -> lx_nor_flash_diagnostic_erased_block) + { + + /* No, this is a potential format error, since this should only happen once in a given + NOR flash format. */ + _lx_nor_flash_system_error(nor_flash, LX_SYSTEM_INVALID_BLOCK); + + /* Return an error. */ + return(LX_ERROR); + } + + /* Increment the erased block diagnostic. */ + nor_flash -> lx_nor_flash_diagnostic_erased_block++; + + /* Check to see if the block is erased. */ + status = (nor_flash -> lx_nor_flash_driver_block_erased_verify)(i); + + /* Is the block completely erased? */ + if (status != LX_SUCCESS) + { + + /* Is this the first time? */ + if (nor_flash -> lx_nor_flash_diagnostic_re_erase_block) + { + + /* No, this is a potential format error, since this should only happen once in a given + NOR flash format. */ + _lx_nor_flash_system_error(nor_flash, LX_SYSTEM_INVALID_BLOCK); + + /* Return an error. */ + return(LX_ERROR); + } + + /* Increment the erased block diagnostic. */ + nor_flash -> lx_nor_flash_diagnostic_re_erase_block++; + + /* No, the block is not fully erased, erase it again. */ + status = _lx_nor_flash_driver_block_erase(nor_flash, i, max_erased_count); + + /* Check for an error from flash driver. Drivers should never return an error.. */ + if (status) + { + + /* Call system error handler. */ + _lx_nor_flash_system_error(nor_flash, status); + + /* Return an error. */ + return(LX_ERROR); + } + } + + /* Setup the free bit map that corresponds to the free physical sectors in this + block. Note that we only need to setup the portion of the free bit map that doesn't + have sectors associated with it. */ + status = _lx_nor_flash_driver_write(nor_flash, block_word_ptr+(nor_flash -> lx_nor_flash_block_free_bit_map_offset + (bit_map_words-1)) , &bit_map_mask, 1); + + /* Check for an error from flash driver. Drivers should never return an error.. */ + if (status) + { + + /* Call system error handler. */ + _lx_nor_flash_system_error(nor_flash, status); + + /* Return an error. */ + return(LX_ERROR); + } + + /* Write the initial erase count for the block with upper bit set. */ + temp_erased_count = (max_erased_count | LX_BLOCK_ERASED); + status = _lx_nor_flash_driver_write(nor_flash, block_word_ptr, &temp_erased_count, 1); + + /* Check for an error from flash driver. Drivers should never return an error.. */ + if (status) + { + + /* Call system error handler. */ + _lx_nor_flash_system_error(nor_flash, status); + + /* Return an error. */ + return(LX_ERROR); + } + + /* Write the final initial erase count for the block. */ + status = _lx_nor_flash_driver_write(nor_flash, block_word_ptr, &max_erased_count, 1); + + /* Check for an error from flash driver. Drivers should never return an error.. */ + if (status) + { + + /* Call system error handler. */ + _lx_nor_flash_system_error(nor_flash, status); + + /* Return an error. */ + return(LX_ERROR); + } + + /* Update the number of free physical sectors. */ + nor_flash -> lx_nor_flash_free_physical_sectors = nor_flash -> lx_nor_flash_free_physical_sectors + sectors_per_block; + } + else + { + + /* Calculate the number of free sectors from the free sector bit map. */ + free_sectors = 0; + for (j = 0; j < bit_map_words; j++) + { + + /* Read this word of the free sector bit map. */ +#ifdef LX_DIRECT_READ + + /* Read the word directly. */ + block_word = *(block_word_ptr + nor_flash -> lx_nor_flash_block_free_bit_map_offset + j); +#else + status = _lx_nor_flash_driver_read(nor_flash, (block_word_ptr + nor_flash -> lx_nor_flash_block_free_bit_map_offset + j), &block_word, 1); + + /* Check for an error from flash driver. Drivers should never return an error.. */ + if (status) + { + + /* Call system error handler. */ + _lx_nor_flash_system_error(nor_flash, status); + + /* Return an error. */ + return(LX_ERROR); + } +#endif + + /* Count the number of set bits (free sectors). */ + for (k = 0; k < 32; k++) + { + + /* Is this sector free? */ + if (block_word & 1) + { + /* Yes, this sector is free, increment the free sectors count. */ + free_sectors++; + + /* Determine if we need to update the search pointer. */ + if (nor_flash -> lx_nor_flash_free_block_search == nor_flash -> lx_nor_flash_total_blocks) + { + + /* Remember the block with free sectors. */ + nor_flash -> lx_nor_flash_free_block_search = i; + } + } + + /* Shift down the free sector. */ + block_word = block_word >> 1; + } + } + + /* Update the number of free physical sectors. */ + nor_flash -> lx_nor_flash_free_physical_sectors = nor_flash -> lx_nor_flash_free_physical_sectors + free_sectors; + + /* We need to now examine the mapping list. */ + + /* Calculate how many non-free sectors there are - this includes valid and obsolete sectors. */ + used_sectors = sectors_per_block - free_sectors; + + /* Now walk the list of logical-physical sector mapping. */ + for (j = 0; j < sectors_per_block; j++) + { + + /* Read this word of the sector mapping list. */ +#ifdef LX_DIRECT_READ + + /* Read the word directly. */ + block_word = *(block_word_ptr + nor_flash -> lx_nor_flash_block_physical_sector_mapping_offset + j); +#else + status = _lx_nor_flash_driver_read(nor_flash, (block_word_ptr + nor_flash -> lx_nor_flash_block_physical_sector_mapping_offset + j), &block_word, 1); + + /* Check for an error from flash driver. Drivers should never return an error.. */ + if (status) + { + + /* Call system error handler. */ + _lx_nor_flash_system_error(nor_flash, status); + + /* Return an error. */ + return(LX_ERROR); + } +#endif + + /* Determine if we are expecting to find a used sector. */ + if (used_sectors) + { + + /* Yes, we expect this entry to be used. */ + + /* Is this sector in-use? */ + if ((block_word & LX_NOR_LOGICAL_SECTOR_MASK) != LX_NOR_LOGICAL_SECTOR_MASK) + { + + /* Determine if the valid bit is set and the superceded bit is clear. This indicates the block was + about to become obsolete. */ + if ((block_word & LX_NOR_PHYSICAL_SECTOR_VALID) && ((block_word & LX_NOR_PHYSICAL_SECTOR_SUPERCEDED) == 0)) + { + + + /* Increment the being obsoleted count. */ + nor_flash -> lx_nor_flash_diagnostic_sector_being_obsoleted++; + + /* Save the currently mapped physical sectors. */ + temp = nor_flash -> lx_nor_flash_mapped_physical_sectors; + + /* Indicate all the physical sectors are mapped for the purpose of this search. */ + nor_flash -> lx_nor_flash_mapped_physical_sectors = nor_flash -> lx_nor_flash_total_physical_sectors; + + /* Yes, this block was about to become obsolete. Perform a search for a logical sector entry that + has both of these bits set. */ + _lx_nor_flash_logical_sector_find(nor_flash, (block_word & LX_NOR_LOGICAL_SECTOR_MASK), LX_TRUE, &new_map_entry, &new_sector_address); + + /* Restore the number of mapped physical sectors. */ + nor_flash -> lx_nor_flash_mapped_physical_sectors = temp; + + /* Determine if the new logical sector entry is present. */ + if (new_map_entry) + { + + /* Yes, make the current entry obsolete in favor of the new entry. */ + block_word = block_word & ~((ULONG) LX_NOR_PHYSICAL_SECTOR_VALID); + status = _lx_nor_flash_driver_write(nor_flash, (block_word_ptr + nor_flash -> lx_nor_flash_block_physical_sector_mapping_offset + j), &block_word, 1); + + /* Check for an error from flash driver. Drivers should never return an error.. */ + if (status) + { + + /* Call system error handler. */ + _lx_nor_flash_system_error(nor_flash, status); + + /* Return an error. */ + return(LX_ERROR); + } + + /* Is this the first time? */ + if (nor_flash -> lx_nor_flash_diagnostic_sector_obsoleted) + { + + /* No, this is a potential format error, since this should only happen once in a given + NOR flash format. */ + _lx_nor_flash_system_error(nor_flash, LX_SYSTEM_INVALID_FORMAT); + + /* Return an error. */ + return(LX_ERROR); + } + + /* Increment the obsoleted count. */ + nor_flash -> lx_nor_flash_diagnostic_sector_obsoleted++; + } + } + } + + /* Determine if the sector is free. */ + else if (block_word == LX_NOR_PHYSICAL_SECTOR_FREE) + { + + /* A free entry when there are still used sectors implies that the sector was allocated and a power interruption + took place prior to writing the new logical sector number into the list. */ + + /* Is this the first time? */ + if (nor_flash -> lx_nor_flash_diagnostic_mapping_invalidated) + { + + /* No, this is a potential format error, since this should only happen once in a given + NOR flash format. */ + _lx_nor_flash_system_error(nor_flash, LX_SYSTEM_INVALID_FORMAT); + + /* Return an error. */ + return(LX_ERROR); + } + + /* Write 0s out to this entry to invalidate the sector entry. */ + block_word = 0; + status = _lx_nor_flash_driver_write(nor_flash, (block_word_ptr + nor_flash -> lx_nor_flash_block_physical_sector_mapping_offset + j), &block_word, 1); + + /* Check for an error from flash driver. Drivers should never return an error.. */ + if (status) + { + + /* Call system error handler. */ + _lx_nor_flash_system_error(nor_flash, status); + + /* Return an error. */ + return(LX_ERROR); + } + + /* Increment the number of mapping invalidates. */ + nor_flash -> lx_nor_flash_diagnostic_mapping_invalidated++; + } + + /* Yes, now determine if the sector is obsolete. */ + if ((block_word & LX_NOR_PHYSICAL_SECTOR_VALID) == 0) + { + + /* Increment the number of obsolete sectors. */ + nor_flash -> lx_nor_flash_obsolete_physical_sectors++; + } + + /* Determine if the mapping for this sector isn't yet valid. */ + else if (block_word & LX_NOR_PHYSICAL_SECTOR_MAPPING_NOT_VALID) + { + + /* Yes, a power interruption or reset occurred while the sector mapping entry was being written. */ + + /* Increment the number of obsolete sectors. */ + nor_flash -> lx_nor_flash_obsolete_physical_sectors++; + + /* Increment the interrupted mapping counter. */ + nor_flash -> lx_nor_flash_diagnostic_mapping_write_interrupted++; + + /* Invalidate this entry - clearing valid bit, superceded bit and logical sector. */ + block_word = 0; + status = _lx_nor_flash_driver_write(nor_flash, (block_word_ptr + nor_flash -> lx_nor_flash_block_physical_sector_mapping_offset + j), &block_word, 1); + + /* Check for an error from flash driver. Drivers should never return an error.. */ + if (status) + { + + /* Call system error handler. */ + _lx_nor_flash_system_error(nor_flash, status); + + /* Return an error. */ + return(LX_ERROR); + } + } + else + { + /* Increment the number of mapped physical sectors. */ + nor_flash -> lx_nor_flash_mapped_physical_sectors++; + } + + /* Decrease the number of used sectors. */ + used_sectors--; + } + else + { + + /* No more used sectors in this flash block. */ + + /* In this case the entry must be free or there is a serious NOR flash format error present. */ + if (block_word != LX_NOR_PHYSICAL_SECTOR_FREE) + { + + /* Increment the sector not free diagnostic. */ + nor_flash -> lx_nor_flash_diagnostic_sector_not_free++; + + /* NOR flash format. */ + _lx_nor_flash_system_error(nor_flash, LX_SYSTEM_INVALID_FORMAT); + + /* Write 0s out to this entry to invalidate the sector entry. */ + block_word = 0; + status = _lx_nor_flash_driver_write(nor_flash, (block_word_ptr + nor_flash -> lx_nor_flash_block_physical_sector_mapping_offset + j), &block_word, 1); + + /* Check for an error from flash driver. Drivers should never return an error.. */ + if (status) + { + + /* Call system error handler. */ + _lx_nor_flash_system_error(nor_flash, status); + + /* Return an error. */ + return(LX_ERROR); + } + } + +#ifdef LX_FREE_SECTOR_DATA_VERIFY + + /* Pickup address of the free sector data area. */ + sector_word_ptr = block_word_ptr + (nor_flash -> lx_nor_flash_block_physical_sector_offset) + (j * LX_NOR_SECTOR_SIZE); + + /* Determine if the data for this sector is free. */ + for (k = 0; k < LX_NOR_SECTOR_SIZE; k++) + { + +#ifdef LX_DIRECT_READ + + /* Read the word directly. */ + sector_word = *(sector_word_ptr); +#else + status = _lx_nor_flash_driver_read((sector_word_ptr), §or_word, 1); + + /* Check for an error from flash driver. Drivers should never return an error.. */ + if (status) + { + + /* Call system error handler. */ + _lx_nor_flash_system_error(nor_flash, status); + + /* Return an error. */ + return(LX_ERROR); + } +#endif + + /* Determine if this word is not available. */ + if (sector_word != LX_NOR_PHYSICAL_SECTOR_FREE) + { + + /* Increment the sector data not free diagnostic. */ + nor_flash -> lx_nor_flash_diagnostic_sector_data_not_free++; + + /* This is a format error. */ + _lx_nor_flash_system_error(nor_flash, LX_SYSTEM_INVALID_BLOCK); + + /* Return an error. */ + return(LX_ERROR); + } + + /* Move to the next word in the sector. */ + sector_word_ptr++; + } +#endif + } + } + } + + /* Move to the next flash block. */ + block_word_ptr = block_word_ptr + (nor_flash -> lx_nor_flash_words_per_block); + } + + /* Update the overall minimum and maximum erase count. */ + nor_flash -> lx_nor_flash_minimum_erase_count = min_erased_count; + nor_flash -> lx_nor_flash_maximum_erase_count = max_erased_count; + + /* Determine if we need to update the free sector search pointer. */ + if (nor_flash -> lx_nor_flash_free_block_search == nor_flash -> lx_nor_flash_total_blocks) + { + + /* Just start at the beginning. */ + nor_flash -> lx_nor_flash_free_block_search = 0; + } + } + +#ifdef LX_THREAD_SAFE_ENABLE + + /* If the thread safe option is enabled, create a ThreadX mutex that will be used in all external APIs + in order to provide thread-safe operation. */ + status = tx_mutex_create(&nor_flash -> lx_nor_flash_mutex, "NOR Flash Mutex", TX_NO_INHERIT); + + /* Determine if the mutex creation encountered an error. */ + if (status != TX_SUCCESS) + { + + /* Call system error handler, since this should not happen. */ + _lx_nor_flash_system_error(nor_flash, LX_SYSTEM_MUTEX_CREATE_FAILED); + + /* Return error to caller. */ + return(LX_ERROR); + } +#endif + + /* Enable the sector mapping cache. */ + nor_flash -> lx_nor_flash_sector_mapping_cache_enabled = LX_TRUE; + + /* Initialize the last found block and sector markers. */ + nor_flash -> lx_nor_flash_found_block_search = 0; + nor_flash -> lx_nor_flash_found_sector_search = 0; + + /* Lockout interrupts. */ + TX_DISABLE + + /* At this point, the NOR flash has been opened successfully. Place the + NOR flash control block on the linked list of currently opened NOR flashes. */ + + /* Set the NOR flash state to open. */ + nor_flash -> lx_nor_flash_state = LX_NOR_FLASH_OPENED; + + /* Place the NOR flash control block on the list of opened NOR flashes. First, + check for an empty list. */ + if (_lx_nor_flash_opened_count) + { + + /* List is not empty - other NOR flashes are open. */ + + /* Pickup tail pointer. */ + tail_ptr = _lx_nor_flash_opened_ptr -> lx_nor_flash_open_previous; + + /* Place the new NOR flash control block in the list. */ + _lx_nor_flash_opened_ptr -> lx_nor_flash_open_previous = nor_flash; + tail_ptr -> lx_nor_flash_open_next = nor_flash; + + /* Setup this NOR flash's opened links. */ + nor_flash -> lx_nor_flash_open_previous = tail_ptr; + nor_flash -> lx_nor_flash_open_next = _lx_nor_flash_opened_ptr; + } + else + { + + /* The opened NOR flash list is empty. Add the NOR flash to empty list. */ + _lx_nor_flash_opened_ptr = nor_flash; + nor_flash -> lx_nor_flash_open_next = nor_flash; + nor_flash -> lx_nor_flash_open_previous = nor_flash; + } + + /* Increment the opened NOR flash counter. */ + _lx_nor_flash_opened_count++; + + /* Restore interrupts. */ + TX_RESTORE + + /* Return a successful completion. */ + return(LX_SUCCESS); +} + diff --git a/common/src/lx_nor_flash_partial_defragment.c b/common/src/lx_nor_flash_partial_defragment.c new file mode 100644 index 0000000..15c1ca6 --- /dev/null +++ b/common/src/lx_nor_flash_partial_defragment.c @@ -0,0 +1,123 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** LevelX Component */ +/** */ +/** NOR Flash */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define LX_SOURCE_CODE + + +/* Disable ThreadX error checking. */ + +#ifndef TX_DISABLE_ERROR_CHECKING +#define TX_DISABLE_ERROR_CHECKING +#endif + + +/* Include necessary system files. */ + +#include "lx_api.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _lx_nor_flash_partial_defragment PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function defragments the NOR flash up to the specified */ +/* number of blocks. . */ +/* */ +/* INPUT */ +/* */ +/* nor_flash NOR flash instance */ +/* max_blocks Maximum number of blocks to */ +/* defragment */ +/* */ +/* OUTPUT */ +/* */ +/* return status */ +/* */ +/* CALLS */ +/* */ +/* _lx_nor_flash_block_reclaim Reclaim a NOR flash block */ +/* tx_mutex_get Get thread protection */ +/* tx_mutex_put Release thread protection */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* Internal LevelX */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 William E. Lamie Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _lx_nor_flash_partial_defragment(LX_NOR_FLASH *nor_flash, UINT max_blocks) +{ + +ULONG i; + + +#ifdef LX_THREAD_SAFE_ENABLE + + /* Obtain the thread safe mutex. */ + tx_mutex_get(&nor_flash -> lx_nor_flash_mutex, TX_WAIT_FOREVER); +#endif + + /* Determine if the maximum number of blocks exceeds the total blocks in this flash instance. */ + if (max_blocks >= nor_flash -> lx_nor_flash_total_blocks) + { + + /* Adjust the maximum to the total number of blocks. */ + max_blocks = nor_flash -> lx_nor_flash_total_blocks; + } + + /* Loop for max number of blocks, while there are obsolete count. */ + for (i = 0; i < max_blocks; i++) + { + + /* Determine if there is any more defragment work. */ + if (nor_flash -> lx_nor_flash_obsolete_physical_sectors == 0) + break; + + /* Call the block reclaim function to defragment. */ + _lx_nor_flash_block_reclaim(nor_flash); + } + +#ifdef LX_THREAD_SAFE_ENABLE + + /* Release the thread safe mutex. */ + tx_mutex_put(&nor_flash -> lx_nor_flash_mutex); +#endif + + /* Return successful completion. */ + return(LX_SUCCESS); +} + + diff --git a/common/src/lx_nor_flash_physical_sector_allocate.c b/common/src/lx_nor_flash_physical_sector_allocate.c new file mode 100644 index 0000000..5ed33b4 --- /dev/null +++ b/common/src/lx_nor_flash_physical_sector_allocate.c @@ -0,0 +1,337 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** LevelX Component */ +/** */ +/** NOR Flash */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define LX_SOURCE_CODE + + +/* Disable ThreadX error checking. */ + +#ifndef TX_DISABLE_ERROR_CHECKING +#define TX_DISABLE_ERROR_CHECKING +#endif + + +/* Include necessary system files. */ + +#include "lx_api.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _lx_nor_flash_physical_sector_allocate PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function allocates a free physical sector for mapping to a */ +/* logical sector. */ +/* */ +/* INPUT */ +/* */ +/* nor_flash NOR flash instance */ +/* logical_sector Logical sector number */ +/* physical_sector_map_entry Pointer to sector map entry */ +/* physical_sector_address Address of physical sector */ +/* */ +/* OUTPUT */ +/* */ +/* return status */ +/* */ +/* CALLS */ +/* */ +/* _lx_nor_flash_driver_write Driver flash sector write */ +/* _lx_nor_flash_driver_read Driver flash sector read */ +/* _lx_nor_flash_system_error Internal system error handler */ +/* */ +/* CALLED BY */ +/* */ +/* Internal LevelX */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 William E. Lamie Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _lx_nor_flash_physical_sector_allocate(LX_NOR_FLASH *nor_flash, ULONG logical_sector, ULONG **physical_sector_map_entry, ULONG **physical_sector_address) +{ + +ULONG search_block; +ULONG *block_word_ptr; +ULONG block_word; +ULONG min_logical_sector; +ULONG max_logical_sector; +ULONG *list_word_ptr; +ULONG list_word; +ULONG i, j, k, l; +UINT status; + + + /* Increment the number of physical sector allocation requests. */ + nor_flash -> lx_nor_flash_physical_block_allocates++; + + /* Initialize the return parameters. */ + *physical_sector_map_entry = (ULONG *) 0; + *physical_sector_address = (ULONG *) 0; + + /* Determine if there are any free physical sectors. */ + if (nor_flash -> lx_nor_flash_free_physical_sectors == 0) + { + + /* Increment the number of failed allocations. */ + nor_flash -> lx_nor_flash_physical_block_allocate_errors++; + + /* No free physical sectors, return . */ + return(LX_NO_SECTORS); + } + + /* Pickup the search for a free physical sector at the specified block. */ + search_block = nor_flash -> lx_nor_flash_free_block_search; + + /* Loop through the blocks to find a free physical sector. */ + for (i = 0; i < nor_flash -> lx_nor_flash_total_blocks; i++) + { + + /* Setup the block word pointer to the first word of the search block. */ + block_word_ptr = nor_flash -> lx_nor_flash_base_address + (search_block * nor_flash -> lx_nor_flash_words_per_block); + + /* Find the first free physical sector from the free sector bit map of this block. */ + for (j = 0; j < nor_flash -> lx_nor_flash_block_bit_map_words; j++) + { + + /* Read this word of the free sector bit map. */ +#ifdef LX_DIRECT_READ + + /* Read the word directly. */ + block_word = *(block_word_ptr + nor_flash -> lx_nor_flash_block_free_bit_map_offset + j); +#else + status = _lx_nor_flash_driver_read(nor_flash, (block_word_ptr + nor_flash -> lx_nor_flash_block_free_bit_map_offset + j), &block_word, 1); + + /* Check for an error from flash driver. Drivers should never return an error.. */ + if (status) + { + + /* Call system error handler. */ + _lx_nor_flash_system_error(nor_flash, status); + + /* Return the error. */ + return(status); + } +#endif + + /* Are there any free sectors in this word? */ + if (block_word) + { + + /* Yes, there are free sectors in this word. */ + for (k = 0; k < 32; k++) + { + + /* Is this sector free? */ + if (block_word & 1) + { + + /* Yes, this sector is free, clear the bit for this sector in the free sector map. */ + + /* Read this word of the free sector bit map again. */ +#ifdef LX_DIRECT_READ + + /* Read the word directly. */ + block_word = *(block_word_ptr + nor_flash -> lx_nor_flash_block_free_bit_map_offset + j); +#else + status = _lx_nor_flash_driver_read(nor_flash, (block_word_ptr + nor_flash -> lx_nor_flash_block_free_bit_map_offset + j), &block_word, 1); + + /* Check for an error from flash driver. Drivers should never return an error.. */ + if (status) + { + + /* Call system error handler. */ + _lx_nor_flash_system_error(nor_flash, status); + + /* Return the error. */ + return(status); + } +#endif + + /* Clear the bit associated with the free sector to indicate it is not free. */ + block_word = block_word & ~(((ULONG) 1) << k); + + /* Now write back free bit map word with the bit for this sector cleared. */ + status = _lx_nor_flash_driver_write(nor_flash, (block_word_ptr + nor_flash -> lx_nor_flash_block_free_bit_map_offset + j), &block_word, 1); + + /* Check for an error from flash driver. Drivers should never return an error.. */ + if (status) + { + + /* Call system error handler. */ + _lx_nor_flash_system_error(nor_flash, status); + + /* Return the error. */ + return(status); + } + + /* Determine if this is the last entry available in this block. */ + if (((block_word >> 1) == 0) && (j == (nor_flash -> lx_nor_flash_block_bit_map_words - 1))) + { + + /* This is the last physical sector in the block. Now we need to calculate the minimum valid logical + sector and the maximum valid logical sector. */ + + /* Setup the minimum and maximum logical sectors to the current logical sector. */ + min_logical_sector = logical_sector; + max_logical_sector = logical_sector; + + /* Setup a pointer to the mapped list. */ + list_word_ptr = block_word_ptr + nor_flash -> lx_nor_flash_block_physical_sector_mapping_offset; + + /* Loop to search the mapped list. */ + for (l = 0; l < nor_flash -> lx_nor_flash_physical_sectors_per_block; l++) + { + + /* Read the mapped sector entry. */ +#ifdef LX_DIRECT_READ + + /* Read the word directly. */ + list_word = *(list_word_ptr); +#else + status = _lx_nor_flash_driver_read(nor_flash, list_word_ptr, &list_word, 1); + + /* Check for an error from flash driver. Drivers should never return an error.. */ + if (status) + { + + /* Call system error handler. */ + _lx_nor_flash_system_error(nor_flash, status); + + /* Return the error. */ + return(status); + } +#endif + + /* Is this entry valid? */ + if (list_word & LX_NOR_PHYSICAL_SECTOR_VALID) + { + + /* Isolate the logical sector. */ + list_word = list_word & LX_NOR_LOGICAL_SECTOR_MASK; + + /* Determine if a new minimum has been found. */ + if (list_word < min_logical_sector) + min_logical_sector = list_word; + + /* Determine if a new maximum has been found. */ + if (list_word != LX_NOR_LOGICAL_SECTOR_MASK) + { + if (list_word > max_logical_sector) + max_logical_sector = list_word; + } + } + + /* Move the list pointer ahead. */ + list_word_ptr++; + } + + /* Move the search pointer forward, since we know this block is exhausted. */ + search_block++; + + /* Check for wrap condition on the search block. */ + if (search_block >= nor_flash -> lx_nor_flash_total_blocks) + { + + /* Reset search block to the beginning. */ + search_block = 0; + } + + /* Now write the minimum and maximum logical sector in this block. */ + status = _lx_nor_flash_driver_write(nor_flash, block_word_ptr + LX_NOR_FLASH_MIN_LOGICAL_SECTOR_OFFSET, &min_logical_sector, 1); + + /* Check for an error from flash driver. Drivers should never return an error.. */ + if (status) + { + + /* Call system error handler. */ + _lx_nor_flash_system_error(nor_flash, status); + + /* Return the error. */ + return(status); + } + + status = _lx_nor_flash_driver_write(nor_flash, block_word_ptr + LX_NOR_FLASH_MAX_LOGICAL_SECTOR_OFFSET, &max_logical_sector, 1); + + /* Check for an error from flash driver. Drivers should never return an error.. */ + if (status) + { + + /* Call system error handler. */ + _lx_nor_flash_system_error(nor_flash, status); + + /* Return the error. */ + return(status); + } + } + + /* Remember the block to search. */ + nor_flash -> lx_nor_flash_free_block_search = search_block; + + /* Prepare the return information. */ + *physical_sector_map_entry = block_word_ptr + (nor_flash -> lx_nor_flash_block_physical_sector_mapping_offset + (j * 32)) + k; + *physical_sector_address = block_word_ptr + (nor_flash -> lx_nor_flash_block_physical_sector_offset) + (((j * 32) + k) * LX_NOR_SECTOR_SIZE); + + /* Return success! */ + return(LX_SUCCESS); + } + + /* Shift down the bit map. */ + block_word = block_word >> 1; + + /* Determine if there are any more bits set? If not, we can break out of the search of this word. */ + if (block_word == 0) + break; + } + } + } + + /* Move to the next flash block. */ + search_block++; + + /* Determine if we have to wrap the search block. */ + if (search_block >= nor_flash -> lx_nor_flash_total_blocks) + { + + /* Set the search block to the beginning. */ + search_block = 0; + } + } + + /* Increment the number of failed allocations. */ + nor_flash -> lx_nor_flash_physical_block_allocate_errors++; + + /* Return no sector completion. */ + return(LX_NO_SECTORS); +} + diff --git a/common/src/lx_nor_flash_sector_mapping_cache_invalidate.c b/common/src/lx_nor_flash_sector_mapping_cache_invalidate.c new file mode 100644 index 0000000..47af9e1 --- /dev/null +++ b/common/src/lx_nor_flash_sector_mapping_cache_invalidate.c @@ -0,0 +1,134 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** LevelX Component */ +/** */ +/** NOR Flash */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define LX_SOURCE_CODE + + +/* Disable ThreadX error checking. */ + +#ifndef TX_DISABLE_ERROR_CHECKING +#define TX_DISABLE_ERROR_CHECKING +#endif + + +/* Include necessary system files. */ + +#include "lx_api.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _lx_nor_flash_sector_mapping_cache_invalidate PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function invalidates the sector's entry in the NOR flash */ +/* cache. */ +/* */ +/* INPUT */ +/* */ +/* nor_flash NOR flash instance */ +/* logical_sector Logical sector */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* Internal LevelX */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 William E. Lamie Initial Version 6.0 */ +/* */ +/**************************************************************************/ +VOID _lx_nor_flash_sector_mapping_cache_invalidate(LX_NOR_FLASH *nor_flash, ULONG logical_sector) +{ + +ULONG i; +LX_NOR_SECTOR_MAPPING_CACHE_ENTRY *sector_mapping_cache_entry_ptr; + + + /* Determine if the sector mapping cache is enabled. */ + if (nor_flash -> lx_nor_flash_sector_mapping_cache_enabled) + { + + /* Calculate the starting index of the sector mapping cache for this sector entry. */ + i = (logical_sector & LX_NOR_SECTOR_MAPPING_CACHE_HASH_MASK) * LX_NOR_SECTOR_MAPPING_CACHE_DEPTH; + + /* Build a pointer to the cache entry. */ + sector_mapping_cache_entry_ptr = &nor_flash -> lx_nor_flash_sector_mapping_cache[i]; + + /* Determine if the sector is in the sector mapping cache - assuming the depth of the sector + mapping cache is LX_NOR_SECTOR_MAPPING_CACHE_DEPTH entries. */ + if ((sector_mapping_cache_entry_ptr -> lx_nor_sector_mapping_cache_logical_sector) == (logical_sector | LX_NOR_SECTOR_MAPPING_CACHE_ENTRY_VALID)) + { + + /* Move all cache entries up and invalidate the last entry. */ + *(sector_mapping_cache_entry_ptr) = *(sector_mapping_cache_entry_ptr + 1); + *(sector_mapping_cache_entry_ptr + 1) = *(sector_mapping_cache_entry_ptr + 2); + *(sector_mapping_cache_entry_ptr + 2) = *(sector_mapping_cache_entry_ptr + 3); + + /* Invalidate the last entry. */ + (sector_mapping_cache_entry_ptr + 3) -> lx_nor_sector_mapping_cache_logical_sector = 0; + } + else if (((sector_mapping_cache_entry_ptr + 1) -> lx_nor_sector_mapping_cache_logical_sector) == (logical_sector | LX_NOR_SECTOR_MAPPING_CACHE_ENTRY_VALID)) + { + + /* Move all subsequent cache entries up and invalidate the last entry. */ + *(sector_mapping_cache_entry_ptr + 1) = *(sector_mapping_cache_entry_ptr + 2); + *(sector_mapping_cache_entry_ptr + 2) = *(sector_mapping_cache_entry_ptr + 3); + + /* Invalidate the last entry. */ + (sector_mapping_cache_entry_ptr + 3) -> lx_nor_sector_mapping_cache_logical_sector = 0; + } + else if (((sector_mapping_cache_entry_ptr + 2) -> lx_nor_sector_mapping_cache_logical_sector) == (logical_sector | LX_NOR_SECTOR_MAPPING_CACHE_ENTRY_VALID)) + { + + /* Move all subsequent cache entries up and invalidate the last entry. */ + *(sector_mapping_cache_entry_ptr + 2) = *(sector_mapping_cache_entry_ptr + 3); + + /* Invalidate the last entry. */ + (sector_mapping_cache_entry_ptr + 3) -> lx_nor_sector_mapping_cache_logical_sector = 0; + } + else if (((sector_mapping_cache_entry_ptr + 3) -> lx_nor_sector_mapping_cache_logical_sector) == (logical_sector | LX_NOR_SECTOR_MAPPING_CACHE_ENTRY_VALID)) + { + + /* Simply invalidate the last entry. */ + (sector_mapping_cache_entry_ptr + 3) -> lx_nor_sector_mapping_cache_logical_sector = 0; + } + } +} + diff --git a/common/src/lx_nor_flash_sector_read.c b/common/src/lx_nor_flash_sector_read.c new file mode 100644 index 0000000..5fbe478 --- /dev/null +++ b/common/src/lx_nor_flash_sector_read.c @@ -0,0 +1,202 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** LevelX Component */ +/** */ +/** NOR Flash */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define LX_SOURCE_CODE + + +/* Disable ThreadX error checking. */ + +#ifndef TX_DISABLE_ERROR_CHECKING +#define TX_DISABLE_ERROR_CHECKING +#endif + + +/* Include necessary system files. */ + +#include "lx_api.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _lx_nor_flash_sector_read PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function reads a logical sector from NOR flash. */ +/* */ +/* INPUT */ +/* */ +/* nor_flash NOR flash instance */ +/* logical_sector Logical sector number */ +/* buffer Pointer to buffer to read into*/ +/* (the size is 512 bytes) */ +/* */ +/* OUTPUT */ +/* */ +/* return status */ +/* */ +/* CALLS */ +/* */ +/* _lx_nor_flash_driver_write Driver flash sector write */ +/* _lx_nor_flash_driver_read Driver flash sector read */ +/* _lx_nor_flash_logical_sector_find Find logical sector */ +/* _lx_nor_flash_physical_sector_allocate */ +/* Allocate new logical sector */ +/* _lx_nor_flash_system_error Internal system error handler */ +/* tx_mutex_get Get thread protection */ +/* tx_mutex_put Release thread protection */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 William E. Lamie Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _lx_nor_flash_sector_read(LX_NOR_FLASH *nor_flash, ULONG logical_sector, VOID *buffer) +{ + +UINT status; +ULONG *mapping_address; +ULONG mapping_entry; +ULONG *sector_address; + + +#ifdef LX_THREAD_SAFE_ENABLE + + /* Obtain the thread safe mutex. */ + tx_mutex_get(&nor_flash -> lx_nor_flash_mutex, TX_WAIT_FOREVER); +#endif + + /* Increment the number of read requests. */ + nor_flash -> lx_nor_flash_read_requests++; + + /* See if we can find the sector in the current mapping. */ + _lx_nor_flash_logical_sector_find(nor_flash, logical_sector, LX_FALSE, &mapping_address, §or_address); + + /* Determine if the logical sector was found. */ + if (mapping_address) + { + + /* Yes, we were able to find the logical sector. */ + + /* Read the sector data from the physical sector. */ + status = _lx_nor_flash_driver_read(nor_flash, sector_address, buffer, LX_NOR_SECTOR_SIZE); + + /* Check for an error from flash driver. Drivers should never return an error.. */ + if (status) + { + + /* Call system error handler. */ + _lx_nor_flash_system_error(nor_flash, status); + + /* Adjust return status. */ + status = LX_ERROR; + } + else + { + + /* Set the status to success. */ + status = LX_SUCCESS; + } + } + else + { + + /* Allocate a new physical sector for this write. */ + _lx_nor_flash_physical_sector_allocate(nor_flash, logical_sector, &mapping_address, §or_address); + + /* Determine if the new sector allocation was successful. */ + if (mapping_address) + { + + /* Update the number of free physical sectors. */ + nor_flash -> lx_nor_flash_free_physical_sectors--; + + /* Read the sector data from the physical sector. */ + status = _lx_nor_flash_driver_read(nor_flash, sector_address, buffer, LX_NOR_SECTOR_SIZE); + + /* Check for an error from flash driver. Drivers should never return an error.. */ + if (status) + { + + /* Call system error handler. */ + _lx_nor_flash_system_error(nor_flash, status); + } + + /* Now build the new mapping entry. */ + mapping_entry = ((ULONG) LX_NOR_PHYSICAL_SECTOR_VALID) | ((ULONG) LX_NOR_PHYSICAL_SECTOR_SUPERCEDED) | logical_sector; + + /* Write out the new mapping entry. */ + status = _lx_nor_flash_driver_write(nor_flash, mapping_address, &mapping_entry, 1); + + /* Check for an error from flash driver. Drivers should never return an error.. */ + if (status) + { + + /* Call system error handler. */ + _lx_nor_flash_system_error(nor_flash, status); + +#ifdef LX_THREAD_SAFE_ENABLE + + /* Release the thread safe mutex. */ + tx_mutex_put(&nor_flash -> lx_nor_flash_mutex); +#endif + + /* Return status. */ + return(LX_ERROR); + } + + /* Increment the number of mapped physical sectors. */ + nor_flash -> lx_nor_flash_mapped_physical_sectors++; + + /* Set the status to success. */ + status = LX_SUCCESS; + } + else + { + + /* Could not find the logical sector. */ + status = LX_SECTOR_NOT_FOUND; + } + } + +#ifdef LX_THREAD_SAFE_ENABLE + + /* Release the thread safe mutex. */ + tx_mutex_put(&nor_flash -> lx_nor_flash_mutex); +#endif + + /* Return status. */ + return(status); +} + diff --git a/common/src/lx_nor_flash_sector_release.c b/common/src/lx_nor_flash_sector_release.c new file mode 100644 index 0000000..87268c4 --- /dev/null +++ b/common/src/lx_nor_flash_sector_release.c @@ -0,0 +1,211 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** LevelX Component */ +/** */ +/** NOR Flash */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define LX_SOURCE_CODE + + +/* Disable ThreadX error checking. */ + +#ifndef TX_DISABLE_ERROR_CHECKING +#define TX_DISABLE_ERROR_CHECKING +#endif + + +/* Include necessary system files. */ + +#include "lx_api.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _lx_nor_flash_sector_release PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function releases a logical sector from being managed in the */ +/* NOR flash. */ +/* */ +/* INPUT */ +/* */ +/* nor_flash NOR flash instance */ +/* logical_sector Logical sector number */ +/* */ +/* OUTPUT */ +/* */ +/* return status */ +/* */ +/* CALLS */ +/* */ +/* _lx_nor_flash_driver_write Driver flash sector write */ +/* _lx_nor_flash_driver_read Driver flash sector read */ +/* _lx_nor_flash_block_reclaim Reclaim one flash block */ +/* _lx_nor_flash_sector_mapping_cache_invalidate */ +/* Invalidate cache entry */ +/* _lx_nor_flash_logical_sector_find Find logical sector */ +/* _lx_nor_flash_system_error Internal system error handler */ +/* tx_mutex_get Get thread protection */ +/* tx_mutex_put Release thread protection */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 William E. Lamie Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _lx_nor_flash_sector_release(LX_NOR_FLASH *nor_flash, ULONG logical_sector) +{ + +UINT status; +ULONG *mapping_address; +ULONG mapping_entry; +ULONG *sector_address; +ULONG i; + + +#ifdef LX_THREAD_SAFE_ENABLE + + /* Obtain the thread safe mutex. */ + tx_mutex_get(&nor_flash -> lx_nor_flash_mutex, TX_WAIT_FOREVER); +#endif + + /* Increment the number of read requests. */ + nor_flash -> lx_nor_flash_read_requests++; + + /* See if we can find the sector in the current mapping. */ + _lx_nor_flash_logical_sector_find(nor_flash, logical_sector, LX_FALSE, &mapping_address, §or_address); + + /* Determine if the logical sector was found. */ + if (mapping_address) + { + + /* Yes, we were able to find the logical sector. */ + + /* Read in the old sector mapping. */ +#ifdef LX_DIRECT_READ + + /* Read the word directly. */ + mapping_entry = *(mapping_address); +#else + status = _lx_nor_flash_driver_read(nor_flash, mapping_address, &mapping_entry, 1); + + /* Check for an error from flash driver. Drivers should never return an error.. */ + if (status) + { + + /* Call system error handler. */ + _lx_nor_flash_system_error(nor_flash, status); + +#ifdef LX_THREAD_SAFE_ENABLE + + /* Release the thread safe mutex. */ + tx_mutex_put(&nor_flash -> lx_nor_flash_mutex); +#endif + + /* Return status. */ + return(LX_ERROR); + } +#endif + + /* Mark this sector as invalid. */ + /* Now clear bits 31 and 30, which indicates this sector is now obsoleted. */ + mapping_entry = mapping_entry & ~(((ULONG) LX_NOR_PHYSICAL_SECTOR_VALID) | ((ULONG) LX_NOR_PHYSICAL_SECTOR_SUPERCEDED)); + + /* Write the value back to the flash to clear bits 31 & 30. */ + status = _lx_nor_flash_driver_write(nor_flash, mapping_address, &mapping_entry, 1); + + /* Check for an error from flash driver. Drivers should never return an error.. */ + if (status) + { + + /* Call system error handler. */ + _lx_nor_flash_system_error(nor_flash, status); + +#ifdef LX_THREAD_SAFE_ENABLE + + /* Release the thread safe mutex. */ + tx_mutex_put(&nor_flash -> lx_nor_flash_mutex); +#endif + + /* Return status. */ + return(LX_ERROR); + } + + /* Increment the number of obsolete physical sectors. */ + nor_flash -> lx_nor_flash_obsolete_physical_sectors++; + + /* Decrement the number of mapped physical sectors. */ + nor_flash -> lx_nor_flash_mapped_physical_sectors--; + + /* Ensure the sector mapping cache no longer has this sector. */ + _lx_nor_flash_sector_mapping_cache_invalidate(nor_flash, logical_sector); + + /* Determine if there are less than two block's worth of free sectors. */ + i = 0; + while (nor_flash -> lx_nor_flash_free_physical_sectors <= nor_flash -> lx_nor_flash_physical_sectors_per_block) + { + + /* Attempt to reclaim one physical block. */ + _lx_nor_flash_block_reclaim(nor_flash); + + /* Increment the block count. */ + i++; + + /* Have we exceeded the number of blocks in the system? */ + if (i >= nor_flash -> lx_nor_flash_total_blocks) + { + + /* Yes, break out of the loop. */ + break; + } + } + + /* Set the status to success. */ + status = LX_SUCCESS; + } + else + { + + /* Could not find the logical sector. */ + status = LX_SECTOR_NOT_FOUND; + } + +#ifdef LX_THREAD_SAFE_ENABLE + + /* Release the thread safe mutex. */ + tx_mutex_put(&nor_flash -> lx_nor_flash_mutex); +#endif + + /* Return status. */ + return(status); +} + diff --git a/common/src/lx_nor_flash_sector_write.c b/common/src/lx_nor_flash_sector_write.c new file mode 100644 index 0000000..c88ae2c --- /dev/null +++ b/common/src/lx_nor_flash_sector_write.c @@ -0,0 +1,352 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** LevelX Component */ +/** */ +/** NOR Flash */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define LX_SOURCE_CODE + + +/* Disable ThreadX error checking. */ + +#ifndef TX_DISABLE_ERROR_CHECKING +#define TX_DISABLE_ERROR_CHECKING +#endif + + +/* Include necessary system files. */ + +#include "lx_api.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _lx_nor_flash_sector_write PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function writes a logical sector to the NOR flash. */ +/* */ +/* INPUT */ +/* */ +/* nor_flash NOR flash instance */ +/* logical_sector Logical sector number */ +/* buffer Pointer to buffer to write */ +/* (the size is 512 bytes) */ +/* */ +/* OUTPUT */ +/* */ +/* return status */ +/* */ +/* CALLS */ +/* */ +/* _lx_nor_flash_driver_write Driver flash sector write */ +/* _lx_nor_flash_driver_read Driver flash sector read */ +/* _lx_nor_flash_block_reclaim Reclaim one flash block */ +/* _lx_nor_flash_logical_sector_find Find logical sector */ +/* _lx_nor_flash_physical_sector_allocate */ +/* Allocate new physical sector */ +/* _lx_nor_flash_sector_mapping_cache_invalidate */ +/* Invalidate cache entry */ +/* _lx_nor_flash_system_error Internal system error handler */ +/* tx_mutex_get Get thread protection */ +/* tx_mutex_put Release thread protection */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 William E. Lamie Initial Version 6.0 */ +/* */ +/**************************************************************************/ +UINT _lx_nor_flash_sector_write(LX_NOR_FLASH *nor_flash, ULONG logical_sector, VOID *buffer) +{ + +ULONG *old_mapping_address; +ULONG *old_sector_address; +ULONG old_mapping_entry; +ULONG *new_mapping_address; +ULONG *new_sector_address; +ULONG new_mapping_entry; +ULONG i; +LX_NOR_SECTOR_MAPPING_CACHE_ENTRY *sector_mapping_cache_entry_ptr; +UINT status; + + +#ifdef LX_THREAD_SAFE_ENABLE + + /* Obtain the thread safe mutex. */ + tx_mutex_get(&nor_flash -> lx_nor_flash_mutex, TX_WAIT_FOREVER); +#endif + + /* Determine if there are less than two block's worth of free sectors. */ + i = 0; + while (nor_flash -> lx_nor_flash_free_physical_sectors <= nor_flash -> lx_nor_flash_physical_sectors_per_block) + { + + /* Attempt to reclaim one physical block. */ + _lx_nor_flash_block_reclaim(nor_flash); + + /* Increment the block count. */ + i++; + + /* Have we exceeded the number of blocks in the system? */ + if (i >= nor_flash -> lx_nor_flash_total_blocks) + { + + /* Yes, break out of the loop. */ + break; + } + } + + /* Increment the number of write requests. */ + nor_flash -> lx_nor_flash_write_requests++; + + /* See if we can find the sector in the current mapping. */ + _lx_nor_flash_logical_sector_find(nor_flash, logical_sector, LX_FALSE, &old_mapping_address, &old_sector_address); + + /* Allocate a new physical sector for this write. */ + _lx_nor_flash_physical_sector_allocate(nor_flash, logical_sector, &new_mapping_address, &new_sector_address); + + /* Determine if the new sector allocation was successful. */ + if (new_mapping_address) + { + + /* Yes, we were able to allocate a new physical sector. */ + + /* Update the number of free physical sectors. */ + nor_flash -> lx_nor_flash_free_physical_sectors--; + + /* Write the sector data to the new physical sector. */ + status = _lx_nor_flash_driver_write(nor_flash, new_sector_address, buffer, LX_NOR_SECTOR_SIZE); + + /* Check for an error from flash driver. Drivers should never return an error.. */ + if (status) + { + + /* Call system error handler. */ + _lx_nor_flash_system_error(nor_flash, status); + +#ifdef LX_THREAD_SAFE_ENABLE + + /* Release the thread safe mutex. */ + tx_mutex_put(&nor_flash -> lx_nor_flash_mutex); +#endif + + /* Return status. */ + return(LX_ERROR); + } + + /* Was there a previously mapped sector? */ + if (old_mapping_address) + { + + /* Now deprecate the old sector mapping. */ + + /* Read in the old sector mapping. */ +#ifdef LX_DIRECT_READ + + /* Read the word directly. */ + old_mapping_entry = *(old_mapping_address); +#else + status = _lx_nor_flash_driver_read(nor_flash, old_mapping_address, &old_mapping_entry, 1); + + /* Check for an error from flash driver. Drivers should never return an error.. */ + if (status) + { + + /* Call system error handler. */ + _lx_nor_flash_system_error(nor_flash, status); + +#ifdef LX_THREAD_SAFE_ENABLE + + /* Release the thread safe mutex. */ + tx_mutex_put(&nor_flash -> lx_nor_flash_mutex); +#endif + + /* Return status. */ + return(LX_ERROR); + } +#endif + + /* Clear bit 30, which indicates this sector is superceded. */ + old_mapping_entry = old_mapping_entry & ~((ULONG) LX_NOR_PHYSICAL_SECTOR_SUPERCEDED); + + /* Write the value back to the flash to clear bit 30. */ + status = _lx_nor_flash_driver_write(nor_flash, old_mapping_address, &old_mapping_entry, 1); + + /* Check for an error from flash driver. Drivers should never return an error.. */ + if (status) + { + + /* Call system error handler. */ + _lx_nor_flash_system_error(nor_flash, status); + +#ifdef LX_THREAD_SAFE_ENABLE + + /* Release the thread safe mutex. */ + tx_mutex_put(&nor_flash -> lx_nor_flash_mutex); +#endif + + /* Return status. */ + return(LX_ERROR); + } + } + + /* Now build the new mapping entry - with the not valid bit set initially. */ + new_mapping_entry = ((ULONG) LX_NOR_PHYSICAL_SECTOR_VALID) | ((ULONG) LX_NOR_PHYSICAL_SECTOR_SUPERCEDED) | ((ULONG) LX_NOR_PHYSICAL_SECTOR_MAPPING_NOT_VALID) | logical_sector; + + /* Write out the new mapping entry. */ + status = _lx_nor_flash_driver_write(nor_flash, new_mapping_address, &new_mapping_entry, 1); + + /* Check for an error from flash driver. Drivers should never return an error.. */ + if (status) + { + + /* Call system error handler. */ + _lx_nor_flash_system_error(nor_flash, status); + +#ifdef LX_THREAD_SAFE_ENABLE + + /* Release the thread safe mutex. */ + tx_mutex_put(&nor_flash -> lx_nor_flash_mutex); +#endif + + /* Return status. */ + return(LX_ERROR); + } + + /* Now clear the not valid bit to make this sector mapping valid. This is done because the writing of the extra bytes itself can + be interrupted and we need to make sure this can be detected when the flash is opened again. */ + new_mapping_entry = new_mapping_entry & ~((ULONG) LX_NOR_PHYSICAL_SECTOR_MAPPING_NOT_VALID); + + /* Clear the not valid bit. */ + status = _lx_nor_flash_driver_write(nor_flash, new_mapping_address, &new_mapping_entry, 1); + + /* Check for an error from flash driver. Drivers should never return an error.. */ + if (status) + { + + /* Call system error handler. */ + _lx_nor_flash_system_error(nor_flash, status); + +#ifdef LX_THREAD_SAFE_ENABLE + + /* Release the thread safe mutex. */ + tx_mutex_put(&nor_flash -> lx_nor_flash_mutex); +#endif + + /* Return status. */ + return(LX_ERROR); + } + + /* Increment the number of mapped physical sectors. */ + nor_flash -> lx_nor_flash_mapped_physical_sectors++; + + /* Was there a previously mapped sector? */ + if (old_mapping_address) + { + + /* Now clear bit 31, which indicates this sector is now obsoleted. */ + old_mapping_entry = old_mapping_entry & ~((ULONG) LX_NOR_PHYSICAL_SECTOR_VALID); + + /* Write the value back to the flash to clear bit 31. */ + status = _lx_nor_flash_driver_write(nor_flash, old_mapping_address, &old_mapping_entry, 1); + + /* Check for an error from flash driver. Drivers should never return an error.. */ + if (status) + { + + /* Call system error handler. */ + _lx_nor_flash_system_error(nor_flash, status); + +#ifdef LX_THREAD_SAFE_ENABLE + + /* Release the thread safe mutex. */ + tx_mutex_put(&nor_flash -> lx_nor_flash_mutex); +#endif + + /* Return status. */ + return(LX_ERROR); + } + + /* Increment the number of obsolete physical sectors. */ + nor_flash -> lx_nor_flash_obsolete_physical_sectors++; + + /* Decrement the number of mapped physical sectors. */ + nor_flash -> lx_nor_flash_mapped_physical_sectors--; + + /* Invalidate the old sector mapping cache entry. */ + _lx_nor_flash_sector_mapping_cache_invalidate(nor_flash, logical_sector); + } + + /* Determine if the sector mapping cache is enabled. */ + if (nor_flash -> lx_nor_flash_sector_mapping_cache_enabled) + { + + /* Yes, sector mapping cache is enabled, place this sector information in the cache. */ + + /* Calculate the starting index of the sector mapping cache for this sector entry. */ + i = (logical_sector & LX_NOR_SECTOR_MAPPING_CACHE_HASH_MASK) * LX_NOR_SECTOR_MAPPING_CACHE_DEPTH; + + /* Build a pointer to the cache entry. */ + sector_mapping_cache_entry_ptr = &nor_flash -> lx_nor_flash_sector_mapping_cache[i]; + + /* Move all the cache entries down so the oldest is at the bottom. */ + *(sector_mapping_cache_entry_ptr + 3) = *(sector_mapping_cache_entry_ptr + 2); + *(sector_mapping_cache_entry_ptr + 2) = *(sector_mapping_cache_entry_ptr + 1); + *(sector_mapping_cache_entry_ptr + 1) = *(sector_mapping_cache_entry_ptr); + + /* Setup the new sector information in the cache. */ + sector_mapping_cache_entry_ptr -> lx_nor_sector_mapping_cache_logical_sector = (logical_sector | LX_NOR_SECTOR_MAPPING_CACHE_ENTRY_VALID); + sector_mapping_cache_entry_ptr -> lx_nor_sector_mapping_cache_physical_sector_map_entry = new_mapping_address; + sector_mapping_cache_entry_ptr -> lx_nor_sector_mapping_cache_physical_sector_address = new_sector_address; + } + + /* Indicate the write was successful. */ + status = LX_SUCCESS; + } + else + { + + /* Indicate the write was unsuccessful. */ + status = LX_NO_SECTORS; + } + +#ifdef LX_THREAD_SAFE_ENABLE + + /* Release the thread safe mutex. */ + tx_mutex_put(&nor_flash -> lx_nor_flash_mutex); +#endif + + /* Return the completion status. */ + return(status); +} + + diff --git a/common/src/lx_nor_flash_simulator.c b/common/src/lx_nor_flash_simulator.c new file mode 100644 index 0000000..634b1f8 --- /dev/null +++ b/common/src/lx_nor_flash_simulator.c @@ -0,0 +1,208 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** LevelX Component */ +/** */ +/** NOR Flash Simulator */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/* Include necessary files. */ + +#include "tx_api.h" +#include "lx_api.h" + +/* Define constants for the NOR flash simulation. */ + +/* This configuration is for one physical sector of overhead. */ + + +#define TOTAL_BLOCKS 8 +#define PHYSICAL_SECTORS_PER_BLOCK 16 /* Min value of 2, max value of 120 for 1 sector of overhead. */ +#define WORDS_PER_PHYSICAL_SECTOR 128 +#define FREE_BIT_MAP_WORDS ((PHYSICAL_SECTORS_PER_BLOCK-1)/32)+1 +#define USABLE_SECTORS_PER_BLOCK (PHYSICAL_SECTORS_PER_BLOCK-1) +#define UNUSED_METADATA_WORDS_PER_BLOCK (WORDS_PER_PHYSICAL_SECTOR-(3+FREE_BIT_MAP_WORDS+USABLE_SECTORS_PER_BLOCK)) + + +typedef struct PHYSICAL_SECTOR_STRUCT +{ + unsigned long memory[WORDS_PER_PHYSICAL_SECTOR]; +} PHYSICAL_SECTOR; + + +typedef struct FLASH_BLOCK_STRUCT +{ + unsigned long erase_count; + unsigned long min_log_sector; + unsigned long max_log_sector; + unsigned long free_bit_map[FREE_BIT_MAP_WORDS]; + unsigned long sector_metadata[USABLE_SECTORS_PER_BLOCK]; + unsigned long unused_words[UNUSED_METADATA_WORDS_PER_BLOCK]; + PHYSICAL_SECTOR physical_sectors[USABLE_SECTORS_PER_BLOCK]; +} FLASH_BLOCK; + +FLASH_BLOCK nor_memory_area[TOTAL_BLOCKS]; + +ULONG nor_sector_memory[WORDS_PER_PHYSICAL_SECTOR]; + +UINT _lx_nor_flash_simulator_initialize(LX_NOR_FLASH *nor_flash); +UINT _lx_nor_flash_simulator_read(ULONG *flash_address, ULONG *destination, ULONG words); +UINT _lx_nor_flash_simulator_write(ULONG *flash_address, ULONG *source, ULONG words); +UINT _lx_nor_flash_simulator_block_erase(ULONG block, ULONG erase_count); +UINT _lx_nor_flash_simulator_block_erased_verify(ULONG block); +UINT _lx_nor_flash_simulator_erase_all(VOID); +UINT _lx_nor_flash_simulator_system_error(UINT error_code, ULONG block, ULONG sector); + + + +UINT _lx_nor_flash_simulator_initialize(LX_NOR_FLASH *nor_flash) +{ + + /* Setup the base address of the flash memory. */ + nor_flash -> lx_nor_flash_base_address = (ULONG *) &nor_memory_area[0]; + + /* Setup geometry of the flash. */ + nor_flash -> lx_nor_flash_total_blocks = TOTAL_BLOCKS; + nor_flash -> lx_nor_flash_words_per_block = sizeof(FLASH_BLOCK)/sizeof(ULONG); + + /* Setup function pointers for the NOR flash services. */ + nor_flash -> lx_nor_flash_driver_read = _lx_nor_flash_simulator_read; + nor_flash -> lx_nor_flash_driver_write = _lx_nor_flash_simulator_write; + nor_flash -> lx_nor_flash_driver_block_erase = _lx_nor_flash_simulator_block_erase; + nor_flash -> lx_nor_flash_driver_block_erased_verify = _lx_nor_flash_simulator_block_erased_verify; + + /* Setup local buffer for NOR flash operation. This buffer must be the sector size of the NOR flash memory. */ + nor_flash -> lx_nor_flash_sector_buffer = &nor_sector_memory[0]; + + /* Return success. */ + return(LX_SUCCESS); +} + + +UINT _lx_nor_flash_simulator_read(ULONG *flash_address, ULONG *destination, ULONG words) +{ + + /* Loop to read flash. */ + while (words--) + { + /* Copy word. */ + *destination++ = *flash_address++; + } + + return(LX_SUCCESS); +} + + +UINT _lx_nor_flash_simulator_write(ULONG *flash_address, ULONG *source, ULONG words) +{ + + /* Loop to write flash. */ + while (words--) + { + + /* Copy word. */ + *flash_address++ = *source++; + } + + return(LX_SUCCESS); +} + +UINT _lx_nor_flash_simulator_block_erase(ULONG block, ULONG erase_count) +{ + +ULONG *pointer; +ULONG words; + + LX_PARAMETER_NOT_USED(erase_count); + + /* Setup pointer. */ + pointer = (ULONG *) &nor_memory_area[block]; + + /* Loop to erase block. */ + words = sizeof(FLASH_BLOCK)/sizeof(ULONG); + while (words--) + { + + /* Erase word of block. */ + *pointer++ = (ULONG) 0xFFFFFFFF; + } + + return(LX_SUCCESS); +} + + +UINT _lx_nor_flash_simulator_erase_all(VOID) +{ + +ULONG *pointer; +ULONG words; + + + /* Setup pointer. */ + pointer = (ULONG *) &nor_memory_area[0]; + + /* Loop to erase block. */ + words = sizeof(nor_memory_area)/sizeof(ULONG); + while (words--) + { + + /* Erase word of block. */ + *pointer++ = (ULONG) 0xFFFFFFFF; + } + + return(LX_SUCCESS); +} + + +UINT _lx_nor_flash_simulator_block_erased_verify(ULONG block) +{ + +ULONG *word_ptr; +ULONG words; + + /* Determine if the block is completely erased. */ + + /* Pickup the pointer to the first word of the block. */ + word_ptr = (ULONG *) &nor_memory_area[block].erase_count; + + /* Calculate the number of words in a block. */ + words = sizeof(FLASH_BLOCK)/sizeof(ULONG); + + /* Loop to check if the block is erased. */ + while (words--) + { + + /* Is this word erased? */ + if (*word_ptr++ != 0xFFFFFFFF) + return(LX_ERROR); + } + + /* Return success. */ + return(LX_SUCCESS); +} + +UINT _lx_nor_flash_simulator_system_error(UINT error_code, ULONG block, ULONG sector) +{ + LX_PARAMETER_NOT_USED(error_code); + LX_PARAMETER_NOT_USED(block); + LX_PARAMETER_NOT_USED(sector); + + /* Custom processing goes here... all errors are fatal. */ + return(LX_ERROR); +} + diff --git a/common/src/lx_nor_flash_system_error.c b/common/src/lx_nor_flash_system_error.c new file mode 100644 index 0000000..e82bb51 --- /dev/null +++ b/common/src/lx_nor_flash_system_error.c @@ -0,0 +1,93 @@ +/**************************************************************************/ +/* */ +/* Copyright (c) Microsoft Corporation. All rights reserved. */ +/* */ +/* This software is licensed under the Microsoft Software License */ +/* Terms for Microsoft Azure RTOS. Full text of the license can be */ +/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */ +/* and in the root directory of this software. */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** LevelX Component */ +/** */ +/** NOR Flash */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#define LX_SOURCE_CODE + + +/* Disable ThreadX error checking. */ + +#ifndef TX_DISABLE_ERROR_CHECKING +#define TX_DISABLE_ERROR_CHECKING +#endif + + +/* Include necessary system files. */ + +#include "lx_api.h" + + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _lx_nor_flash_system_error PORTABLE C */ +/* 6.0 */ +/* AUTHOR */ +/* */ +/* William E. Lamie, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function handles system errors in the NOR flash. */ +/* */ +/* INPUT */ +/* */ +/* nor_flash NOR flash instance */ +/* error_code System error code */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* (lx_nor_flash_driver_system_error) Driver system error handler */ +/* */ +/* CALLED BY */ +/* */ +/* Internal LevelX */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 05-19-2020 William E. Lamie Initial Version 6.0 */ +/* */ +/**************************************************************************/ +VOID _lx_nor_flash_system_error(LX_NOR_FLASH *nor_flash, UINT error_code) +{ + + /* Increment the system error counter. */ + nor_flash -> lx_nor_flash_diagnostic_system_errors++; + + /* Save the most recent system error code. */ + nor_flash -> lx_nor_flash_diagnostic_system_error = error_code; + + /* Determine if the driver has setup a system error handler. */ + if (nor_flash -> lx_nor_flash_driver_system_error) + { + + /* Yes, call the driver's system error handler. */ + (nor_flash -> lx_nor_flash_driver_system_error)(error_code); + } +} + diff --git a/docs/LevelX_User_Guide.docx b/docs/LevelX_User_Guide.docx new file mode 100644 index 0000000000000000000000000000000000000000..0d10c75a323f9ac3f72410881b8738863e0546af GIT binary patch literal 110172 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 H0m}aXxYK%g literal 0 HcmV?d00001 diff --git a/docs/LevelX_User_Guide.pdf b/docs/LevelX_User_Guide.pdf new file mode 100755 index 0000000000000000000000000000000000000000..fc7eaea3876fda1aa4f164d6f702888134b54eed GIT binary patch literal 488308 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

{4}f z@?m1Asg%3c3!BK4{0SBgo?{d*Et}&Q9S=D{`z(6Agz&nL*;~S>s8S&P!?mMvMv+h^ z$6zMEocEc`D}&*npkyIplH5R@9e2ljrPsg7$ySJ#0kUQ!Nus6&vnS<6Pw|qXC9tWEo=v$ zE{)qwEXY4+X!L`UF6Hul4O&X;YGM&jDtlbaxXC;}3;12M3*2fmbk`4tMMiC~;2}5A z4BROMC%Y)V)S3X~*Jkc;xDA`cLHJ@c-)KPPt{GX>fe(8g?fgF|EIU)|6{z; zvoZX`lB@dNu+0MZY2EYP@m7eJ`|EeXuL{bT<|g!|yh?6MK6SJ*!fxH)2jWa|7t2?o zqfv-rwFh)5ykPDJ4%6H&!<-()t!XlUY6WbzT#LmA_tfkIs=n!>_eMx|RDX+b{nXLT&d2NP(hV-zeNu1WARqn!3xN;i zqZcCT@naX(u=VIvv?p~6Du-o7;H4Acix)Cw>vATme%Y}V6~_f#y!+m9cVzaxma^Vm zy`B8tqsl;KCS$JAjmQ?5%#AaO%$$$1WJ&F00FOXA$LrUtnQbS04B|Ku=o)CW!doUk}fUORbOTO2xR9$$#WBd019(3Q_c zcSE(Y{%iH+h8GgKujx9ei1!n2SC|Ea0u-nvZY^X7y*FLZ?7H@G}=x;=SZTsC(A zxH_8z?w!s<-{75qx&qUpF1wA-ktxomnIVb{N2gF8H%g+6n29x!O2R8xfyGUKu~)-z zPZvk2qWqlS8u|?;MXzFB16eOytB%alDz4tY_(Zw6dtPpLRsEPUX3BfDMkp3moxZ3Q zbMj#dPirkq5-f5jWw;)$JLL#;RkxRZH~BH>yvF3;C9t>;Zz;88n^l^I;9{KKzQ;rTC8ayCI z=}3P4_SBmVG-nusn2D+Q>(Ju)%1Q6vZ3WFkC_bf?47w_~_~{f6yRZA^m&(PZFeAf> zppV(Xa}H3=6~y4+HF)iAi~YIEEX!2mV6v&7bhrl`#kilF98HDYcqt8QRvJ!I=`15t zn2G@&nkW0<3*548?|s%Q@+mmvHiWvcLFstzLsFI(g~iN;y%LEbc-E2^BO)&Bp1dlc zQ*I9{epM2eX9@1>^AE6R7~YG{^up!@X0wRt9cp6 z!>#O^Sq_lbK$B2tw>7&nQ;k4K-BPFih>3ZBy4lJn-5AD7jxP6-X?(GxeRN8Lpx$t4 z4H~6Yf~cgYBlx%{56lyN9iZMGo+o&6a@m#ydu1X@7CN0;hS-?&=QbrGE+S0F62{CErC9^Cd&Vh86mR}Wle6t5V5-S2WI{fc*pTuY|fjl+TugZ z?dXzY*sC=ECoZTMt7pi2@vKDjXJB-*W!G&$qncp0E|`1v%pmei;CwwF*J@JFUj)Q$ zHe}{sfoVRxLBOFQ_Jlvv zJP4bXY+dINIQPT6tI6x;a`vXN$Skk?NTaCd!i^UXc*|IeC2#Zs;1;!UU1}~rTX=|2 zIrV^HDPDih2VQD2WE)Z;$sU`e@jXFm}jDs54D_R>|e(M-FHD^cxeb+E}hf8HFx-+y{ z70rFw)DvIfB%EqOHX64fI=m`7H5wk@Ehz@bOnq;UAr^508Dvnhs6rD}vZ&||#Kl|* z29oz)_1o37(jvUT2!nPWx7LyH7Pbx8tHCW)cc*ak?9$rtC4!?$+VwZ&C0+a&#Ud1<>T{(#|5gOVzyZ0NsIV0bB3=R!o!*5KTBW*-#i#IyMN zu);-9f8QIc$q7H8uo-LcP+q&E=O2y}=lr}1T+-a~L2Z7Fk+D(-Ot-oo`$^0Vk5W~w zLG;qb5KBuTnLZy@vd(-YIcF_>8_e~G#alsAp1g1YP6J`NZJM9|y2l&_R*_dp?rND| zM+_G=M?yCJP{1pUI(BfmppVWRGN59dYXzYP6aq04TWuPiOQUs84ER zQYhDHDFRj@HN~O$ULF3n_`_R+CX+O^izOl_pxsmn>M@YNDo$RyvpV{2L|PsSyligb z?P}fLV!(9UIXr%UQ5$PlsbAi80BQw~mZt)u&@MQ#NtaF~EAh|}FoSj?H+|L?i7S-jApC@cq=Z9)DpwHqF(BnLOEtUBjOY%)w#l#Hq zd65-2)0{}9vPGm{0sab{>t5)nsbw?3cXkntCY``8WZ{lvD_skeFM`;K>Hfh}4Eh|V z3X(LQpbAR~=!qwbE*-g2v_ndcI)%%XwULOlIaM=$snTT3V{~1O(fvgW;N^Jb=7rz= z#&7+@>8>~P2n(uHFZP|wU{*9lf*8MsuyUYAyMEGYiMGj=l>Ja2$`%Ouq1{rx(%XDX zPxGU$)Xdb5{OVbc!m}03Y{bzY9YzOw>_xb2`>|mXrr{j8u6R;-^Na5ZT`9Td9X4UG zvh*1O2%%i~Q5w?59CQOfQE!5lBm>obENWdfG;kr>k&N4zAo|r!p)dfe-Ha$cAQlp= z*NDKZAx*1KgTBbbd2$S{Cbd&gGVNNUM^WkWgrG{e%t=ry{3LG$h?l0s!zxvR4GExr zYJFCSBQg3}h7`ZqnVWtTk)iPnuvlG79xL>Gh1W6uXA2L6Q`!fma4o#@hyHI{toM6?{Ls=t4711+zKM!Oucu%~K^&D9 zrj_KFB4>L|oLN&Df!j~q8?(T`t#GHH4#_d8E`qPYjuP>7DL`~x%Mm!Xmc|kQpGU&F zEDtcnL;6!74x|&-Rs@+c=()Teb-~ZNz+@l6M%Kw6ZDf(m z8d)I*ND6I~gkOt$QzW;!wkmM0*e^D-{>jO3FWEFw^wI+*Gk?;e+?d5N?<{HQyG<5P zQ&jRrqYvB|e=#=Y9|wO0Qq(R<32Hy!m$%ls<%V}2WKB}I^0g}W79$c^Hk5l+3`%2~ zkZfFWmZgzsAse*=w}!~D<^5h=*co!}%R9zZWa`hZ-oi0^Rsbp+D?OavEvA3nAqh9(YI?2PP>ii~au+bpsyIMg`XYa2A5sJ;W8u+`aysYN#*JYLz<`BvQD^U4k zdWq9rZBIw;lRKjL=N`nu!|i^4>(p{?mb5Zs*tUy~{PK%EN@q^n*`KG)mf1g?TRh2v zFp>DiAK|hENnfCK7pAEHV)gnrBP^I$|G~sRsP1EvE{yiM9oOCe!JJCQzD)}=Qzsr4 z-;lXfrNw0>udcR$50hq~i6_w})BSYWV+S}8oUtwn>}mVdadr2(e|$|gxmRwRmfzpM zE0+~Ae|C(Z1vNZ+duRJ@h5j8nQ11$VXjg}pwe3B+IFnA+5izGyJ##Y(=5_G|MsoV? z!+|H-JqCs0>JDO zaG!EDwsn%jFlch~{;?*f zJNGR~r&b#8+QR5J>dAxd+r@6)7}Qmf2z99kYR+KzML@Q2J9+t4?&#W^$e`l| z9&9cO!%)?5Xo|2Ev@^*v>QM3=o53ps9yx1mMMvmK2Y_W=eue7V7TKZ~p>)Bg*ga@$u8$pI9G-dP|5=^85;pGoPHb}4&*DWv@rq?U2(K(f*A09O zUMlN~QKHw@ez4Gv82@pd8{z?BSU9xY$W%07(-%09xoyni#JqS9Uxh(?4xvIa=UrQD z2ET{7+F1PU1s#6;{`-6w!qd=@w#mv+^?~YRJ{LHoEB4#Vp;*w- znh6k89kz)#LV9H45#zfE)C#Hb`$zlteYXd|4v(>Ry0;B!JI=)*+GTFDN{MOWAE=MV zr~UWo+2i|ntb=8EU7ya#I;_ng^fBw=d9v{;>!TzOrBKjU)qAZrpp5rNOmFRR=U)tlhN@MmmhsJACTyO<3|6+2L3x<_xH2@mO#I`(f^=b{~I^@_e1|Fk^Wb1 z^q+jK|BJxrf8e110~lrf4{-S}V3g&5qJn?F=s&;f{~0j)_XqvwTmA2V(SPz&{Qm$B z|6R73mFd3$hyO+t$Tv;>4IKUjH?uG>{u8+Q8-4yq+LHC(vwbiy{bStLpr)0>nkd5O zCB;|B3t4Ma?V*yq7KIXwGt252LbcRo6U9~@7z#@xOAVe)+}p!4w*a5+VU4XrI&2+X zXmNY7PSz#*(Y>W|RPYD?sCFj@H|6)SV^@n&woH5c$yM>@)%>6PIv-Yyr}0@sPxp`e z!U3I5%Gc(>^9PH^!;Z;ME*^1L|1W!dwLU7%dCfXCJJ0(o#BWcx`#d)fhnI!gl*4#i z{~jR#u0)D1I~N(VMB{;S{UxF2dGT4o=HxErYNkgbwf2Xj=RLdr%~i$Q-OVhp=Wg|| zNPFzA;Q60q7EWl>7i0OP_~Dv78`Ai?+C2YWOdMvVp~i{@$2Dy6Qeg>`xQJqOD!=_W z@WI4(V+-5qS6RNhgMhSRdqD>f^V0|?&}_m~t5sbeQS#Pn!dJ_Wv$3r6dj#qiiMJ1S z35=>%eT2mucNc<|28}!}&$Gz4y>)HN{9QpGoU`LD){AXq+j&Zm+TS3xVeT~r0laA( zqN+rRl-81>1w&cSM}JZ$rrk4 z|0IQh_|%Z6G~zZZI))?z(o#+3?E-CQ*M>W%BiA6*qid2pUs_Ie^$|pjc|5K1Kk$35 zb94FT^kwgTERpOq0x+{I8IAw0tF3=UOlV|Zp4>%7?w~*BNxx3ElMt>KWA`H-Kx(pK z`u0MTafCtpUCyZ~oKyf$r}d`?U$c*Q9*bMQVOs2_mr6`zZ@IKZ*8Ahihaz=3Vtw4tQJX3|{AUcq}qRAdoNJJaa_!dahh%Kg)R9@($Gy5H?x9S{ZG*y38HPncb=4 z?Jka$2oHp;)u{4;V~{EX z!5w@5G+5a?0FpLwFHd&Ig+Q`6`Tn3)EC2N8>|egA2mC5q?N4g7!A*Mfk^zw=K`4Y~ z+K~zz0KO?#y}}FxEc$f`<(8129Vwg`7p^y^zdwkO!5LA+_(zrStAyAXyhbKlX!VIqEo_byYhAQ@Bj+ zHfvC0a(w{fYAXh{MXoc_TBiyW>;A-M08Z>>Rt}^JyhWarQwz5YLZvB!{$mZeL>bVO zEy(*G=rVs`RLkXD%YkYqA#%YIYbd`z0t^^uM}cHoQO^}9U}OZYoJ106k6zqJy6ne@ z`w_wvL;RcwRpy4ci9!=P>_oO%6?8>Vl-p)vZh z5%gtHF_I+D7Y#t|_~SVVYVZ#nv9Ev%C4VxUD0^v~Gm6)XUe7VqI8HX+Pp*Xg6Tyez zw;IYw1Rcx{RRVgYxZM#vJdAsuPaQAzPtQqQ*4mO=9$Pse z(`FlKS?WF<6wTN@P~QzwkpR`WP>`j^|Jhjx7S>(A>~{ zC(}5Zi?4iV_5QSXq7VlD2f-mXJVg!H1{bm;skVfDB%|zMiXp=ZXn)lVwhS81YLW5x zBa8Puc8t|Wxns4{WjH8SCN0U4HTt3H8+XVN&ioPEB^clQfr;!)psa2E5Bd4}5pMKa zRN3N%+=nHGUVNJ$2RQ@Um|!IE{zc;ZYb0n4ipKZBD)Xu4bR7vdg>R*h)$M@Q+?sy0 zGJ=+}PuTY!Bd>qR{86(w2Hwf6_uSW)RjGb$(mHZkIZuKw9rwWI2L!^*E35x(SniSOn^BDYu5Jo4c{Nm-Q%0?K;S;2xmkbS$+)t56GrijSk;cXR>F zAytRuwIE=Jj~F63OUdK5pGqn(X-aKtX2dfGjTu&nKNG83bejle%)cPkj8T1iJ3E^2 z%J0RdBEkGh2Q7Q4Hs%i|-O2b_emS1A;{j|I@%JUd~Fj!pX9Q4Eb*4L?m(txXuKXPREUgA+sTpdFrv@ zh=iEsEM&;C+4Fg>48eDg9Qq0z%=fa3w6K+8t{L1&4VejR)%tNjBHl$Tx`nWCPErK8 zc&WIWtHkI}Y6e!AWmR#s%JCEz$-q#60|21xXdQ)_ZyDh)3D>1+4JWD1FnK2%m@%Hi zJI`#506Qj2UB%jH?>sraApqe5P@7VB-d2{Dm?PEW*1{48o5e?AwonY=^LaQC%8OE|-OWcT&0 z(VJ|=&Y%Tlyka?6?d6i!OtAP0wXBJ)`CkA?Y4WdstagRS6NiFVFxkKZv#N~8qx>rn zai7AchOMV>Vt+ayX4+>48y3=&E%G!k+G3$+<%cOb|t*PT} z|DFK@)nILe*Ol8rAiZLJ)CphJ3nAU=HONCT?-~&Lfth)IxZc+YC*BKM@KSsi- z|1VoaHb}XsX;)ZW4VCW&kyc2PDJm>w3`(#$Wfa}|tzn_m)0soU|%kE~Z77u2zpt>rKa)J78!Zi)I?VhUg1r?!Sx?+t9Mj0)yUh zMx!0--XCo#yDaf#yI1Lph|iQxOxPXb+rpeH&CLW=&I)~!We+Z zUBG;_V|YpE-?~*2w~$ZyMyc1URJKX>j1UHA-$qf$YB!nSj>@>K34rzZ=eKl4{~yv7 z$_%ZNBsZBt$SIS~6c+Bg9&MW|vVG=O6?G?n=pj8RW}e3&yXBb zt*^sgF;1-6s>o6RfZD!{YyR*2qgA$m>w6tqp15OIEqf|yT!`3(3zBK$nq z5ZDlN9e?kvdd#~^eCZrHJXp60`2&&u}VpfkcX3y$bSI~j%pCl#rJyjDrtfJaO|-n@^u ztv5(VPDm9OGVO{wTY4V4&SbGc&!VLh5mr$#Ack$5ADxJPX)i?4K$EU%B7R-&ZrS?H zEeszd`pf6VOx>q@fS0R!6||#={t%KKT9w*alM>FVB{3pzCoblnVi|^cs)cYetiY2k3!#Sfa@9 zv9L`tRidp~_i)8*cfLSB$@|?ftjCG84du*Z&(F-dL9_;20h&e7Vny7c?@j)erIdi( zpe{rImZerlTQ`O$-nL!rCkK5qH_|l>lfZ8vIzO1&J`LzvI=X~cwG8D1rtZ!cR0WV- z#OrK!sj^n$O)>{D#slEp7aNl$h2$a(K!7yg4~en5cY|s9V$^<>{#fX@zH8IdE%h&C z>9el&b?zh&IYT@xo!b;=M$~xVpI%0`RWl%qzZGe*>>WW#YwXT;6u z`C|qa)DECVVXt)9g%o0em@e6>aT=n(%?dY~I;TumBi3DqDCwgS9p}Ih$Xz+U`e>}w zeqeewivxI&Rgugx8Y5UxUc{9whz|+$U}CSG9D6`)53lA1y#l)EVxyj0B>dqRTk`n< z_9wA2-~1;1^HaNr^-L5ev@UfCgS@56_cA1}@f~$|DT?Z!&wsXmuU3$Of#Dz1CDgTS z(nm15{>t1=mSr{wMh`~MTof;jw{AZ@bVPj0NL))4!ot$9w}fc1dR)GGgVE(=Q2=$R zIP`}?bKSbC&UML3p0{J57^UsLpBiUCmzv|etz}g;^mzh_R$KGP-jF#GW7nGSU>#pa zpzRckZ)f^?P`N$Lli|tE0%GdnofJTT10PlmA(D}i`TX*qkde`bmX>j^CZ1K4U_i^x zqHCeXi!gPiLn1mXKi<54e&a6OLOR@PfW8#eWxmjE9W_tm;U zqqRlV^L_A^friMADdm?|Z*6i6a{dAoMR9_b91emA2)#?}?wa2jA0_E_*z=Uv!49_dhL_o5)lp|Hfh#olO`HT$4vt+%a!L4dVK*Q6@_=SS?u7M$9R!^r zP{}bWFfcId`#}))5Zl!iGF$E4!74{8sC-b6oy%8KHd`^M##+QentJLPExC{mnZ_t> zJCSLab=n*Zc)jSxoysl!5M8(rS=a2dueOyA6sgerg1F~rlk8Yk;8@x@a>n2lIZLry za56T@$J7Q5{Tt9?n)FoGR79P>1bnONNdxiO35iSsr!-~*iw)j;;k5+7#}qlWWUvOl zq6zu1<;u-rthrf!cATT_7+Bzv<@Uk0nWU^>1IR++#^@#HLt&Pt*f;+4iA7PJimZtl zFGg>2sDcO=)z7Qnxirvd*XKN(VQ>sK(eh}>6i*65ev2JacEI$Z>ovDlk0D@=`(g+R zwlrKSFdN@@=9PofaXSD?Ud;S|7<-2(O}A}bH^a7V+qP}nwr$&yVKc+F?T8H9wtZus za|^qzedlef_LsieKl?K&wez==Obf?UY2ikI!QiTu zQ_QoP_C^H*FdezqB0JkqA$acP+!0KlaS1cW1pNGU}a+kn*+ zpRmG>;l5G3px~b41QofqxCQ1@w(f(7L^p?dUaSiF(v!t{l+3O*M5wSEl%RBQc~;{O zXIs;l^!KfXe6D{s>F_V8Ni8^Erz8TfsIC+rDRUxzayRuxBL02S&`J%CDO2;= z!G}_#$##M-_&z~SLQ-h_fnip|>8yNmYG)d9(doF#@EveP{@*n=@wwc&i@pZP+^kFK z)!8PTb{BOJa(5Qe45V!L`8TYgs?w~uRI_JOCQh7=EK=~?y$LZPWM%>(LOzx%s;VB~ z*d?PM-Z+z+A=AIYgjlCur}cpkCqaeg)+@47afv(`L%4m-?pPRQdz$5;HDu zYP)-DRKN!(xqSc3ABoy#8@^aQkW|c?U(r;0Wm;3Q$#lzRSLUY+DcDz)N81#1*A6Zo>iv*&L-(#jqXTQk|Edy*vqeNf{4?kvGwf zXjJLtl1-tpNo8!a%gc1-lDp>+j@hacTQ|t={MMRzake?*>b^|76l=&@=xP)5675l3 zKBPJRU1Ywd^+&i~1);t=#rAg0Q}=wsU*!X*j)O@*{B!NMt>$IRx%j+gkHCg1EqZ=j zMG$u)2$h790a0%gNJ}C2K%mYX00YeuE9VlAltM=HTB|UJjxvX2!TnzY9H;a!1mL@1p8DG~+73>N2gA4Dd{LmZsbx9NXy30N)>_b=85* zXSP<{_r}y6bG4|4l9gjTss1}!%m=LVI#V^l%Id`zNV5P;f@qdW ziJ~7|K^dJ{Z;y^Fi*KG+!v7qenlalsIb^&~xswE!imZFVT>!ZC z5E~N6ZqPt~A!|5^JknB3Ju9<|Rt)enHMynuAvcqvx~ql2#R+Zn*X6aAduv~Y+|l;~ zygP(DH#v=A6ndwPo;PYAIo`s)z1&+podP%;?CY-p&Nf1B+UQ#HydHfmzkuQ+zNcB? zTm6q$<}{+vMG2tBO+TBMb9t1tu0$AEOB*mg9>hguL~~+N8;qMk?2jp#K%y-T3g=X~ zV*|nYKpjc0RqcL!m@fM^iY+8DYwOWV)inY@-(9XKhs=fjk>_hOYYM8q!?Am2Vy0D3 z4X((Ab=-Q-RYXikS{+I(QC)6&=iq;?sx~?@6)mx^TN52B?OZmK>uknC61+vL8ltvc zkOm{^`hzGVsx$FbKje7|6WO_qgt~%xzo?N14twxqCw2ej^7G#rWf@rL|Cd|CMs8me z{_Ce&%YaYi7J~KtsHti`3BUKTqGLfP|4d?og0U~SOd^Z3gx}A{Ob;8d*#VnXjd=b1 zacyRjyMe0#=`Boyw=GtTgOmLP@5qC@U7~tcxk zHzB=906e=t7!&&a6*d1pi8ox#qmKFYmJ1ge&w-EFagPfZ*-aDOrYsXRv>+KCSeaho zo}GO%0jPGL~Nkq2PW_ByJ%Qra8>`qFkxV`kXHAN%(VSJP{AV0I*n8B6{GIx$FK(gYNdp;~FyC^z=^^+TR^T3rN0SFjEL-(G1Hm_ zX=SC*M;JnkMLPg9bA04VR$kwQGYd5*?3wQUTc+!)J=8+TOB1uk09WH*jJ-dW4f~KQ zr;D^%x#XU0Dy5M#W-aM1_!s+wM{O@37RRKmZmOTn_sL1NSr~5ow)n;im34*_jGnpI zF{#+40YdmJ0N=VEFkC;k?Amz$v18i?8{oW>J#c3xADwqqHu?*TS==mYom+q`2%ue- zDS4atWMQ||s4;v5LbE8z1GbJ+ZtgHhC<4SR9XL~R<04aF9p$&Gx&ot#h+YWrTz@23 zFt&rBH+!)EK71@Ztm&I~Yb1a+1$KbP(fbW-G|ABYX!GJd5tHbqlY1D17mRc+CY3Bq zD&3U(rk=j|JC4Cs2qsduix>{Db)Kyd6GygfVB|7?S|_RV2@3)B$+|m?{~ORbjdM&> z?}JKR6?d58Ks#^jZjQ}}{m|K%U- z1oyOE8m&u=I)vJz7Fs~6!>Znfh2>p`>3O?SqCx>rDS)FVU)IQdu2l#u2>UI60pT=vi6Wr^$N7-)^^vF?**cu9%fsc&fr_ZCd)I>!yKh z$F>UV8&%`!tZs$8Plb4yljn759K zXGpz0eW;ISlK*@!Q{Wzht-b6X`Q)}2~Dfy)3JMK1C)g)ItgKEl=fTI3MSlXLfCh*6=?+Wc+ z$jhEQO8+S}J`L-=zNA;;+!HXR$MyLhNwnih{^4G|8R-hlsQw)%uH)o`XG__}T*N5D z8+VbdaUkZA!d=d>iq~aa^_3ZXxE`j+QBQV29LX9}<5H8<2?EPEe3GgV?&5AE0JjGOKPn@L~3R4g-a(=!|Q;Vpuv?Wl~tZ~m4jUN8~ zoz28n;+BOnX3S1GI+513Uw|H4a+|LG{1$b%to!}Wc%=hUL|7 zZC0b3u>8}{tJSiv0QXO%BJTf0D(YL;Uq3-4IM_CAlz>us*#?@gGA-V0@n?Jo`9Pjpmo}e^( zbdiP(Y*cTib>x~<O)le57V!P3_Tvyp$|pvxAkRf%&>429 zl9)GQ8A$R3d%kLo@+3v#PF2fy=8j(E7z3S3N7=prxG#TU#C17A=Kdw|Cm~l=O{YdFq zoP&4{@;C%5^=K}wa3#^4gd?}aZ;H#^N|trR;3tD&4M%6)M-|d&?mTU+Kur(GU$m8W z827U~tIn|fqnfYHYcOIqhpEx*5+Sk_K)8(IOI>e@f32bask`jq)0kHiYzlcT*U*!~N1mBreU`ATL3)^P&10F_Q3_BXIqT|<>`&BmgEWVRp3|!ssA6X0Phk2~d66-9JOob;-|9wSbw^WP@iF!nj}jM%hpM!rR9kCoj*^#Ozj|pI$(u9kVlY}as_C*4bcoG)wx2wEH&(jLu|nB&nl>D z6FjkmgW8N_xFFj)=@dy&pg*lG)!u(j5=twzDPqE0Zm!IRsl2qZ^Miz}(&Pp7iEWNW zeIpQfeSEB7#F7x;vWewzmTFn-ho%Ng1p_v&#aP3jZ_$tCSOx&0C7)uyxV9t63`h`hH04dOYGTW znbPMg!P@%MWzhxFlDW;4)+ckBUo0h4zXHShkbL-lJ$_8nhKP(Rn4UbJOoBC@>fJLm zLBlsS1R-?U|bOoP{6VaP9|CQ_5ovLuZ_4WNY=&yb#s?WwMgcZN4W| zG2p?8D{bnDb;nq@%IFj9k_NDbxjO+m?tkY8O-^!Ox6fO=TgA{uFBZm2;(&9%@r+StuTe zU=n(B`BL;<{u}09{G@`7(PM{$SeXi-(WBLrS{tcR!=%@n<6 zR>pj-*kjOStP#y5@bh$t;ROmIyvN<)CJ-t=V5_On$X)YI{{kH0(ryKg+VOh`(edqIP#9Y?wb!Z>5T1imZbZUbi%pPLDS{{P9k4^kdNZ;M+UwKaJZNo~Qe|F3S zi%hJmlPiSHisjc6Xwu-YXvfOgf_Nk3w_#0xAsi4O1Y)oQDWLsP+sj+yMJtV%1xNZe zHL_P%0$5jG@ie_sui4T(}yz#N0RsJSJops6JV%X@ps#A8DV z;nMpAo+3z9@%XM_xmcei#IxsV4lK(~pSkjOX48lBmtrA1g(eGbBmG6fiJv6|>~mLV zqFD+X8*c2?sq~F^887!Gcp7ZzpyO)YDvzbgwg-7a7>@-KOW14phH+!BUe$kOjk z%sv}znln!U5ZWZ(x=MS!GQIf>jCswsz?Y^u51EI=Q;ZSzeSDr!+#AF$K9%4R1O4oX zCGp!FY%vdvr6GrUK-b*)W{UA*sf)Ffe&lu|q|#vDLUcc6Ww5h)^(h)Sovrx7^|d)S zqU(iqhGI5I1-?mR8Rr&lMM$;dtuKD2Ujy2NaAPH5An;Y7{gPu)_>$NaM-gP}c78|2 zK&xuSKsRRc&|$b_qQ@xOVf*aq)cq!wvbeENLn#9Rcj!&vEg~={G?eHYGljco;lnpy zcb_cL#{!HYtBevshc}}q+a!7VI0CVhT?MwlIC#R=VZ`%X zl8H`|HP(_iz%K2{gkygV+|gb)r`CewzSlc-_C6s;c9{lv|4x4|zf99f7YkgS(tDT8 zom$wB$mY`*go#{|#3vNBP>q;z*x`|e+O^9{pPk(!R$I>K0O!{4yiR46bgPw@D=;}$ zI34p!9>)cl)MCRphhqN$+?WQr?>iaftQT@vPovbkOgdINPaA5XNm%RU)#$-Y>Nh|{ z8#P_6(QRO-y}~~Ht~Kq$7WTd_t0s-^En0xD>!}_f z4hh2#Fy_Gym0;AJf#|J_Wzd3%7Jt%yz&l0kC!5yIQgnH;R5in+p9bZobljI=)K&%2O!KExLN30!NiLm}axCHodoNouc(TY_T1X0c{OIS+BowPSY(ZM> zw+}cJ^yjT^5;|cyNA75`n!HDzis=N#k{yp{DB@>j-dah-K!K7iz~wOi8t_10GtA%pG*(9I4BV0R6#cXLhP4$RMREr6QOu(Zb4Zo4lW8j) zN;*Tw^{|`;a<*2vt3r{{G6^CQ8}9HFA<#?*8zEzeQ}|q!1d}yl!He;>CZ{b}PwF0& zF!VjpwUvX`6ieBKB4FUZmBq)MskEWlUcV4@>Q)+Szf%E ziM*XBfXeuRs47lqgpTQ0SfKgHFEE|hu7H0zEB`0x6d|)!e^nDCE{GMw%P+0hKOeoO*&*P)P_aov!1lH7rrP|DrQ6>< zI3)H3By_vZuF_1n(aD7Fiq|J>dtAJFY?H1$xHBgA922M7)9%j^?YgA)3=(^MWwx+! zmQ|vx8-;Go5SZKP0!e_7|tN1$^)zsA( zjoB6r)n?RW1ov5d(zvo4DuyQKb;OF4Q$V^O)(7cLxg2__HeR8Zy`1M5{_9y#VCYxI z0q`I)!YNEQyY^q_T8^S?rKRYVqVqVnH{wmFzX$I@NkV%~jT~gWkqalAxP{$+XiVN3 z_@c}Px~a0AG102+hd&81k~Lu6-aG)=_L_4HbfJy`V$v;WgrfKf3v|r^Eiccv(Q@Mm zF@*!gNy?Ii?FCf$(NP3@GOsXU#&WPawHd}y5)kYVPY5G6yM7_Jj^H>pUthgWuM(uk z7P*66VsaRL){5T@1x#?424`YyO|mU?^c}-UX)k!l2>Dv6*~!`|Qc|uBIBDGo_*d7# z`o)rtH827d>H}{4U0CMFNT!y{;KxhSSV`2N$7~9N)9j}zz?^H-duFQ-gA>YLs*#=c ztRm{hNUVeJe&Qu2Ck{j010z5>7}2zKc}~7Zg8RfUUT{CUk}ylKyhZL;6l`y5WFBI8 z$!V?!L`#AGh0Beonz&zpzq((*I@y1IehVba^VI6n%~_iJ{sYSk`)nzFPrJgs&vttg zcdhIXK32~dc3m10kUN7oe;Y`g0GFE$XWO?KZqLD@`%=2QKQaYFoRY>@E8Q`}o8GaP z-pK@jfVZ#W7O)jzHHBwttqE|1uw*T{-+>NbqwrBb@q*+@>L7Rc$qEA*#J z3VA?xzadDbnAn_c=|fn}0jz2!t~Em#CAMnC;k4wYvo&P1Yzo2GnI*B2@bR*67gukr2XzJsx3E)*c#34j=FV%Cl%b`$4 z%f5`DH$kp45*;>9^Yz=fIRTH^L9ps$ikn8}xX8jVEZ9Ap3xq3?#G%1O(yW5xQiIqM4 zjWW+%mbvn6MpQu3A!sUml+}I$rluq7gg7D7ph2@{dE|xax#&b5`^b)IBqvjiwn4)K z0+@SAx z3yY+!d;8oz{8_1!RI0hb80Oz#m$7I;5hA55nvdtK$A}3A8J zngD#)IdUS{ocQwo#QMFp#i<-2rue!1vN+l~LA0^AsEfHzhS+3`W6kq+>CDB24dG6{ zsI;QtbALHcwTVKd|3E1O5LvgeVS}g$?GD0{+*Y`FbxhSsUg8|a?;VS$S;gY{UPU@6 zK4ubao4b#b4?;uRdBvl(Ho?y8?GLwFKh9K8<(+BXyVpXs*}b14Uo-UZUH*IIspiz? z#{LKM2m$su(-%|=M%Ir?sABL|WktCV&z4KG@Hh7U!Y_*$0+HQsa8ojjEK}L9{eQ@_rF{m{#!^G+ke`1G;Hkl z*x|o^s#|q~?I1$r8={2m zvDarNnf8B2Smc(jy073FpFA}B-LH|^~Nw_MG+WNnQaUu{;(vMFPiK7B*y@ zkSr*HzDE-jQ@HnXdOr8T=L~=D2sd8ATsM4@(+z~u%h~vEmM}kyv_Ue|#L#;GL+^$7 z9H;*>EW65_*k2%?-MP!b&ejsE5#1_jc_?NBSMbX$&p0vQC`PWHet3#G0tgLNzeJRA zTjw`sf;W~_{e?gsp+Sa2v+d(h4z$8n4%9bNh@N;sCi)KU6EGP`I%dv&A~#EoYb

Bpp$e9HXk#`pe-?x+2Ut`~6uti4rqTAsM(u0!@ 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 z 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�u(!U+Zg<)ZgP^M!kAv< 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

+W==rFZM8W@`Rr$;dZbC-VmG z0`#MOtTNV;In11vUv?0`Me!-jvWe_>o%~gYX3UDRgs)<()2YvsHjM8M@otd>}@xpdhop$n0QUAHoUid+_@pTq6-r z2)s6Y9+9B%-1U$BOj(6CD&q&N-}}M`5!&nr_lY!emYjMnTfXLFto?kNDO&1Pheba!?^e!b~_WKT)gP%yGzK@U;mxcy^>I9#LIV{CfA+1Qdkiphogiy~;$i3ie zak%H7(0t*Kz^S&vuZP}c@FV*(@{#@rBc0g5s1{Gh4_Xk94M!1aS!52*dH zdjmelLGJlepu02Q4uzw{WWZM$gw!Q`nx^oS5e0R}jSk?e2kO?;wCx2R3gG28=10UI6`2`jOWTNGb3ibRXb;*xeZaIQPBX z_xFd-0@z%5R8m-qOv*HA-Jd}we+f;n6K6m5)`@p7haK#MEpgF0gN$_U0SWWjZEYA`m?Bs76)mC0Dc^gj7 z6iC^?t*Zg17|`|D_F}=EP*i)%IO8+8DgNgj=qg(>*y>pzJkLm=q#Td zeeeGT!1OX2*3nlm_(Dn`c){n}wVh>5$nr8u_Fo7xzM0drg%UP#mGo-{pFv-m9I}*U zLh`ct6P}EyUn$E3^XB}Gr2#pa2R>%k6F8OVrGPYzpLk5+yLn1~-%r9hvAepLI{3clT z%%hoE$sK1D>p6HyMpeMHP!cFsUGnbk&Upf!X&M2+5tH8f5D{%+fl`YsNDBp=P&MNs z&|D|)8E~>bGLeYZ@wc}aXL#1Oum~H>0ix{;z~zl=05$@dWu}(CzDVW_ECwbs|2SeU zmzkJ*=YtxVCmaWRATC@S97T-&I)T)y>;y4Aai&(QwAD6uahb0Kt5J;2?6TNb3IV@P zLYi$`GXmFOuqebKvD#=<<0Bdi6HM0$>r4_UHE~p$Af(U=rjDiNl0iu#d9$jjzIh~! zWG0JBO%|1yzQlQJtFET$6bFhhLPiDmu7FD&P9w{bW*2#P&n(<$Xhf7JVa6;tXCGX9 z-!_cN`fl0Z26(`r-CSKWtuvEmTg#F*FVYQSvqCd5gIB9686Q+FRdqE$%KA}22yjz; zw2WqgQ~^_E=!n#rjI~sQcXtKhj-}W_ow6u1Yhm5;)xK&*6WP1FGyn5gpF~5MI98CJ zB-#m%_x;!~_OWgYVYiZD0wt^9tHWX}GfAY*Y#eA?eSf~a19#TB zhcSQ935s-kFZR?9<0qj+-(ffpW2U1?o=7`11aJ+`JmG#sk(`MuCQ%jfY)#_T1}g6_ z#h@W?V!0dY$stTI?8iXFG6VIECLE0D)iAIeb5VO_3L`*9^4og0sikf|ef5spARzi{ zI`tVe{OzF~qW09jkTWX}8EO7AA0FH!_J2*dZp}l@ISBTw1GEzCqOd0>Po_gI z@o2KK^z+FJceQzVjkexi1It=(X=gdF_H=}y7@wB@evze}efc1M3}cMo-D7{Rs_iLF z?QJ!x?|5W>T-xM_mB+*Br&u__?{DdCqpVO(ou9x?JekT*QBP9O(br+=v^XMN$mJ=? zcE8#CL!8p_gk!snKu4pi(@Nqeqo4j4XF&%KvKieNRhp`Gx+JPBMK)iBq0dLz@8ok@ z68Z6=>6u9;KHQgFTzYmr+C%Gcq5wJkpIc!AHsFq2n?Tr;!N<`08Q%N_ZHGrTm+P0j zKG%QSvro!<{SW(Uvs|{+AG5^b=X?)oF5wS@o1K&KadZC4z~(CiOJ5e#||S z+uPkfe;)pK{LJ}JIp>_`na=ueuDfrXZ{~GGHN&z6KqsrDxWedODlZUX_4fkAYq|Wd zbe#!Iepnog|KSclimN0NMIX8#Uz|)+dME9M%8w8n$1^n9_ZJ<_00@z}*qsKrPOy z&a1>KI|vZ?n2L&C`|ybJ_HRgx?rtcrj&GxSsXn*E8uBj!F&ou(n2Eqri88bSn04rS`ACka)yX-N`>?MZj97Q^oYXFkWZ%NZ z^FHv7y?|aElVq_*M6rfrY>4f(PffdHv2JDQxpM1!G9mMdv?V;P0E$3Zp%tTqjEkeu zAttddUhGE-`Co2UVjfuY==C&h3~r0>EBI=`d(q#r*;b6yJymn#KtxNh<}A6u?0U9wZ$Bu!NBwH1w8b1laz* zyLh^bI1|ege|fJEOEQS)h8PxkI$0@(@ulm1++%;{+CiC~kIuJFB?fpUjs~HRRkC17 z&P++BTA~HqL&SM=ADq22hM-`ZB)qI<?I>t9%*7IcV@0noo?W^ z6K{>L*zaoUm%>cy*FABZ{XqS*W*x4o_{H>NMeJ*$&@~2LveY@8w~LGnP=5~NOz3s? zqo{qw72o=(1+0P{@)DN+Pj@4doy9?}iW~P3*?~$j+b5q6K;o*nv?$JmWllBxn@^4GIDzxSt{bDgT1Kunf?!{s7?c z@@obq4DDzI66p!51-yfK7%mpKhX=zTs#6FtSJ_ZTT#BhL&dik@-tN zfb7lz-K* zd}-PPxrD=VOT#j4l4lSoipm;Br+?{B53PmggCG#2o>|V=X*J<2*3NS2Stno#X9Nu)nu3!sVC9SM!6Dq^ABH(T~z@H;bOtl1YAD* zxBH&L9;~oa$ms%r++9k%7PH%#gVS^M4D>9!-@*kVp2+ZUiw7uchpilaMh!}=sfqiv z2YdJv;e9;Kmvf6rq1H#tty=fw=*h?S-osQel~reLpV!LQwP}a=Y`f#noJJj8`I$}H zMKPQKr$wo!&@jumjIL_?Pp$xQk02>xZ_`QD(7@8x&Wdx)u;il3Ifv*WX^37YS|bl_ zCw1a3j&O8Mmu}{CmOZ;*+#VxIVQyV+vmW%QWiLaZ?xQ$bnuTo{&vTqbTu=mKtvCAW zY&`}gM^IwWjWD9E&+{sfvRL-*&);4Ye+(8<3|Y>fIz=Y_!LGYZ+wc!u+`q88IlT>J zJs8U7VL%KU{)W&&gCqwI0@&~y@V5J4G6cMbKg5k+H}2c~o!|D)MSR{08W%qAcgF<- zzb_Qor2s=WDBbkIM%>~wrKzcpx=uX3;a9ePw0XiD%$)TnySbwQa;+}zf}xZI>Nltm z^ZE%Qe#b;2WHy*FRKXeBc*kb+*{|=jMf1{THE++ELSS9A5$f~sgA=ug;)FL&gkd80 zhNrZ)kJNy6Asl;kJhTeDn_<{7Uz%OMiI1%fCIQbkui2Q!=4s!G{%ufl;g|HQ5nd%@ z$3aV&!V15=+M)N%9#w1bsmmMDzVT~&$gjz@T>PSSQU39m1sipRiQ^JP9`eLDOc;t6 zSQRO9i-POP`Zmw~p2BmVC)LNR0n}rV;i44lW>z_0-6Xo@SNYGrYjF%SQK462MLZi4 zuY?v$#2~l~=(mKt6gN29)diTHIQ?tQ$Am2v-cA2bM1}!3{6K@D6(AV;KO1UCfH>PD zMaaQX001!bCSVePFXOl*1xcAJKveW_PQjahqqc=dMn_5v;*J?1zyvSd7aGIUhGOt{ zpjT|UB_!s#=8|um4paH!D|t8@cuBy< z0Lc^o9jpDh2i-*9qbLvo&jPiB0CV!Uz2s2jMgyjGVc59t*x<(V7;~=PDHdH^yc9Dk zQwf6R@EJibyKY)dO5)KT!jUq2y?RHS=qeR<3PWB|YYxVlM9sKKL<><-Gz@{hlf6Ey zrFEw+ceDuE!CHGZZ(GzN4$?@4eXbE08(R8o)?lZ*|U^!s8$NffX#&^s_}?ltssrxPfEXQ_G~0+ zQ%t5eLtm~KLM%d65=bRp1+w&|xb~$jM{;DYyVST0qUCpnL&94L#KnM(`j9g7?L(x%#rnZ>2@3c!PSa48o#WYd%hJ0xq(>dw?GS&$y1adF z640BNm7h`?qetJyp+rV@Ktw9#=^EOcq$Ce?bu5Dq5<1(9lq*>+sOGU%SaU8JD!3!7 z`Y9rcDJQT8-L))hK1!3#kSAXR@QcU*L=iv=?*H|jgP%lx{gp&~HKT>-f2wz&!2x`o zP~FQHwcs}a44)t=IS2@O@h5Y%t%m?^ocYsfQ{3Z&0>+?%Fo+>A@Tqro9ZOHrD8;>Y z#TKog_4oU&#~g<-B^P{sTlQ|lzAMEb%zmM7Nowv(bn){=k!hw{O%K#xrT9S>g;DlL z5DIgNr?o-7?2`Jjwmu9f0+Si=InC zzIW}>?AbK6#o>s{ne%jXQl^>7%0~9f2pi(Lu%$WvS7}wRWW&V@GoA zl~?iMQbf}tH1kif_nZAaT1%(%Dr<_Q@(Fm;a0uU3rp`&?zD3%bQ-`6)Kes3+koxg5 z{HsIJF`KRW2aL9Ha#0qZ6M={A`P<$5{k_yuZPqqOpLzRi zi6nj%I!^f!4A->`YNw-mKA1*C-%0jEL-C>-bj9vah9xynk*yfs(keR>ELMJ2@?3Go z*^+IMJqJQD*Ut70*6W&koOiD%?ZKincGHE0=(e|wOhE^W|99_5>@=NSRW7;K@^DaE zSi1a-1vIMiJvvG_ci4=68+2paqe*Au;PH?hi~&!xXexC%%_maK%;1i*NP`MGW3YN;arFD zXyDQ}bJ(u(>dfjqFKX#KpDmEVFZw|%rWubS5thIVIQ%X$_l2;L8Q>WpSJL<62%VYb?&@Z#Pyq11xZ(M!^ z^bnE)4+a2iMDy?;4TqLDspUozN_H<&?oSgnN!e+;x>b{Y&y%(x4EJ>X%DN|S6~j+0 zVv^zsDAwUjrf8Cv$q*loP5$ZiwuRlA*CN8DDs zgA_`cCZ`x#n4aP?vsf}&C=5fM(H=*g&uBsaRrOa2Phf<=S`Ci!p#iS6K-2R{kMKHV z=Kw(+Y}G=Mh=|BeczKTj>tNe-N-Svmy+m`Gj+>5%k`pK@$nGr5&1Qs7uUvV#oVbri zMnfSP%d2QxzoyY>bh%Mkl6~dhESRS$n7>vRTPMyWm*EV8Hg}Gn(+gtDI?;Jt8Oww5 zQQP<_Dl_YFF-!Wh>TJn@`3DBQivPhj7IK=QyKFsUyyL?oU7dIf_0b`EAjrq5B9zLO z90|O2E596xj=^ggq$=%ez9Ndte=|B$1N7B7BYzrND<2bM>s&VaWcwpcL#@B2)ZV2O z(7_iA&u@V5^Y_*BjSBpYt+m=dzZnouhPvmF9DmOl#b@8Ga-L}wmN|tyt>zCgEy6`g z*r^zXHb0y|@m0-Gs4Bo3qK0*F`U&jX*}ObET`)ft6G5+e0}2V$|0Z&Q zf&>ISr~PPk`sX41aMDi0+zlsB*b$Kqh9%+hMhx~3#iBsO;BaqC06P45Ap;C6Z4*^% zBL#;KhTAfne&WzGhZmQ3@EkZ^At+`*7^hHS4}VQyix9^a8s*M=)iJ|rIYdt$!ySJs z&9Mo5dvkqld~_0v`7M}1K!+*fSR5HD(B3_r`IL*My!=cvx=N>n7i6kMy1Cog;dO6Fzb9;=o3nxtTc?wzVm*dTnVoSGHS zdVUgM+k;EH6tq>E!pbjZ6NV1Ly(bfB6TPCa@3jikx;e^K9rNWA!Pi7y!nqy|JfJi zVK|nU{+=5ZIEmeUA&b|%3H@}AL$!A75b18 ziygX%`v?$T8=DTA+5f}!OZKFvs*^jOZo-8Emb zusCxN`%JYl=iQH-{()OS>$>YDG5igaehtRiw5ja^=JEW^QZ>Z#HEo*fap~m6e1xGr z4*=22W(I9x_Nttk&(a(7V;KN3mYbQB7qF*-XkQY0NFK;dCFOEg zf@|%b3M=7SeW+@hgva565J@1~LexUuK%8EO=^Mg^ke#cRxaP_vWw-_xK0N_M`{1( zd0TwgHC)D=Y~|$#z$LdL^}nTz_5YMICXWB0QF<9uJ98HcMgmq=MmCoJnZgxsPl1%v z*FVnrFSzD`0T`GmK4!T8u7;v3pcpKxDmG+Vzm8sjc_TIG1WUC;x zI!&@=@69mN($vz*nxvh!QS|W7f5Rnc@$PT`I=9c`y!)a#=bZPPd(AOdFd~8)6DFHq zK)l4{&-py^Bg%`~dz`{VRgD+n&6GvyOO>Fh%;6qy{5Qp$8_5+<48_6LX*Ct2PXqrW zfs!a{;$by4`$WF)PC*w@A?;oixm+$+mGSf|UPN4x)(8IM(Q;fqm%(g5 zeIo8v{6nX>@sVv`S(Cxzz?B~GoE}&2*}r6D zAtyg33|J~WUfrfw(U%{^PjER?yX9>_Wf+;D7_7d}LGFXlvT%YD;HjOnNkHWZNiVCv zL=~Ll_?8Ts6+mauy-OA}U|d-F!cme*@QySWlLg+I=@O2()jd)rP+7D>uH1!bF+vwH zO+LxNsPHq?_jOV#T%7}f*~A8YzU3Z;QxTa5ggdSW=8AoadPR3Bunz!P#l{CU^53a_ zM{zbMPV>m#qPQ#%WbjAYoDuR%-&X;A>8B=zGg%Gv??I*(;;x) zOHhd615N344@_EvZiwLc7_zR<#QQ*d=gu5rZwk!4bFvk=Ks|7u-T?Ym-Rj1INT7x0 zs5F0UvB8iSrU3!_CxtaaAua*H{OIQdk?91ROjuBm847NcP-iv84p=~xIBRF)z{FvC zC59BNChKSUMOGrw`xoFAF6c)htiIK=`mqm?&mf%_Tr*We|Mm~S?F0KSSFly81PmD) zRPVIq@hr^AOdeVzj)Hv@L?R>*Nx35Bk|1BU4<=WQ0w>bVpOk&F;Fg?mMp0J?KBExm z4(W)n8xL7UrU>K^cRBNhD+9|L4oG=0JDLfrU;Z5$(V_8rfO(4Q<3(eVD`GL6(Sx@x z31#jdiVLi72(gQnRw~*Qhily9jDg6bgZj|Zu;;BV7p(qj(cbV6^H6>C`nIA{xm%lN zJGHOP*;wkUj!-b_(4ebkrT)?{>?Qx=erMwT*R{uL`H1Iw!&3MuMP2V#TN;h zSc($QV7*3YNK2Kr@nC z+Hfm0k`s^krz)?Izk7+B)2NQ%k00{mEASUGj;Fd;h@2yWnFX(E-0OSa9Xq8&i&S5x zewpEf{WRLVGr3zUv z5IJ=|ptz%yvj&+>2*cV&Gv_Un(3V#^TtSr74(+N!#^2AYjB^R#S+jJjXVqj|;vE9m zYFl#fF0iX4ZQyW^XlA(gg-#>L_|?5>t+%>_xDQu(FN8&|f$!tR6}H1M8dCWRuNC=& zXi9UI4L?<_&;m7HaL4wM3?ta!3q~lk8}}3xeBhq&LuK&Bn}gIB#{Y%M-39ASA^eps z^zP)U((0NaA1U*74nBG2sHV2Wggbx{ec*9{T=E0g*NXLh(9LZaw_tZF;MelZxn+el zlZbIbM?swpsQu|L{8(>NUaFh0OgdHJfCcHngP4GRwGrc-ys2|Vp zgWY9Knulhh_DRPaqKkm}&z5%zk`RZ1Jj$Jb!F`HO!zPVroq+k4?j0GQKx-DIXL(v| zf?F7l?u+!182m z^oyLa#}|+A9&zx}J`R)voFYd=F`eEE7kG+dywkYcQ_%+Cf+@l@RgvWsqK61d^ng2M zfKvL*$NDHhjo%V=#Owv|TmzC9eAzbq!W%A{CpPxP^Qatk4heCl%zWfX89tX}H81WH z-ql}_bImg|8sqAQsp5%^a072o2IImOPfldYC8Q%AXuIXmRZci|EkooG05-H`4{RdV zxJ21*@B?_shSRYR0r!6Av2|mvfJf^f(z>E;7wn2QbR%KKukwvyGNa)&0eS8lJ%3rC zU6d<`ORYIgKOJ;at9fLh5x(k=YRVC@Jw8x}h^eu}p27O7{WSMHU2 zC1Q!MU{V{Pn-3b|y_O19tJeUJN#94IDICMkNpjEagDGH0(=Xf<^ul|SQ3b5`$?6m0 z^G;zNDJ~LVjyB`a|B@5(cbEWdlq>}+nfd-kvA&#Nbg4$ za{xjsdPU158)esAxiv=sA?*E-;ZDQYCT)h-Z(3vs!+BG zS;uYhsoTO=&8b+->TjPmt_x}FWy3ABp0qQScz5dRl@SzGFJy%i{#u&{V3le;8K71n zh|U$96>uG8HKjFi=B**e+eA2v?5K{C%iiX? z4FS)wQcoioJ0*E@yaHm zz4QL*zj^4tIi0Whv9CanvW*bjMtIYSZ6FQkP@X}{j1U3$L@in|@2GJm|@0ico08UMr|@OShsHri1l zTT;F+WmkwhO5=@nP5_JvuZ0ipakuFDjRO+CAQihY92f^`c(>fVE!(MKzI9vee4S0_#wGX z^hOx=ACA4C{?&syK1UpGH~0tsioyIYch!3P@70kcW0~=7MYB7%sH(I*-5Y(zY=QOio9Axkj z&qj#ylJL7RNe#h$0{V}X^#QBBLvjOn*q+!wN}h-H5ub$o9^fD2Jnskb8`)j`#7Dk|D9RrV z9`WoCQYWOr4v)K0&MwI3ibYP5d7>`klg?6)Xc|H!0B+o5+iND%5Nq^U%*cJ-=W# zZ({r-E#G3D`+v|R_(Yi9q&xGt6_^S0?qYb!QQYEqMmq#1Q^Xb|HKssLo7)MpHO?bs z|HNaI{Dq&5-2lx18S2Xq2XJ6#pd8rnGZ?ALoUZy)rjp@(97}G}m-!zK0X~1i)d(Rl zmslY-s%n(2$%vR^MzFBVp!>1N^N_g74|Anb_FA>U6yx(cvR`hG@kcdPSNsPC1lLjZ z<%Pr2V6gd$Tuv$$P4^gC?7s8OpXGf^(%#Z)|CU?+?pn?;nZ~r)yjCWb5QoE4VQ`ro z1nm0zIDNg$&0nq-d6|o~*PrPuD{oUyO6DRVAL``f!g=Y~$hfGOij2b@v^6SLk&tZQ zpja?rKLK)SAj?*W)c>5q=^OtuuI~balTqHTgK|-6xu$sITv+|H!pSez5q8#zy#zcv zv1Z`)laa?J)5hUUNlv?>aN_gKc>UW%eBHz{$yZ0=vTISi&PP8&TeuL&7E@m!P`;k_ zIHSxiT#f5W$$foC+rAKcdRGux7nyo2^`iu;zAJ1s))m?p*Pb8u!5eu*7S6KzV{TSaG4EzdGK$J+B_*vhr*Fq1y}gK`Y)*`I_}o77nS-bv zPg1(JS>tD^1{%(?5+rk5ovbz1k`-3B78TwenYpSe2Lnsu|+cj z4Ex%z9lQUe_^r@Cfhdr8UYECcmz2)eqQ`Yq(ZK&hH&Rm!v)bLcOm1DOv~)G^3b7aO zY4Oy~?71(-E@n?8-MxK_bZ|F!xsdpja5Zp9`iQ6L z#JFkoR&RaK-ooepBBciKD=b)6-cO)cC_t);`wl?#BVJj^-($3J_v0Y@Bo^9FFzVhZ z^S||+ZScQJ)s)ombPrW^#eS-+-+@><-!H)}OM+TSX|~qo+fd-nv9HF^Z%=!t@08^J zTHNpz5|B2TD&)1Qpjnr!;zZEoD+Sye?(YYB_>HXE6?*8lYW zRr^rAiE*H$=${{*4qx%~_>5#U-Jq4gO-oO+L8F15e(ZI5#H8A~ALy)mg>F^5Rl$aD z6&QsXu^$^%9Esb%A<(XgnbrzhtFJLz`i8CXl}&ja-Zj6YORbUtr2i1-so7xq zg8AWR@4UZ0J?8l%chG3Q6^?&LXt_9E>!_V$WWKk z1;i78EO0epEI@03J4lw|6)e+9q=rQmCgo(|rH7w62OL-kj*?~Fd1GG1;#qU??66oi zcr4p_9GiJ8+Z%@M9EPnNw(VSVQ~I>S_(nBv{c_%Ji#hp?<$2Sr8PnQR)hzIBd;i$6 zMZiYD9!+a!u(wjpO#PNWPQxR zfFK(LFs9ARlw`biFV!Xx%@wtPT7HvSJH$)nUX{waVQ|6q1UeaN^bz8R&cAJ4$x(JL zpLDT~tId#Qx$`M=eG5j^^fU2yug>V}mnjGRwC)Xa8cc1qai4s2+I$T-b9&lpbJ3eF zZmn6ec4770J3Z9ezPm2()OBgxvoo=iu-9g@H~Me(_{X&JuQ&OS?w|4wlV7?G6%rHU zbJ(pXQF-v*-SYPSl6LsTZSbob-0e;GQv(?Y%R{Bfp@C$43 zE9+Wmo?fIyPms(mp#WQclwd-jGYN?v`4R@~)W1Lr3ffx#^9k}Y@Y3(-M=W;;N z;nv^ZP(6D_mCD1&M8BRTG@fMf7?p<+iR5x#ywSpb2X4;Q^e}Nptlkdmdn3`xDD?Wi zZ;wVSJsape!QpCZM>Nzm&Ri@wx-n8LIY|ynjwJl?O*nD?q#Ud`W&cNbjz)hV^56ZV zmgSuipSQmU%B(UiJxR^nz{lQ+ofda+X;uE&s2SleUYy?GIEix#gvn~i_Iv?#zSA?Y z4qDC}JcJWyfpokAqc=w}nwUL8Z6Q8&mMs+0^Z`C zdYwXmD{F~TTbF=X2-@(|w+SJ=uHe}WMCoQr;-BDO$=DspKvztPtxHI*Kt*(VQ)~Qo2r9EMND%WDFu3)rf{R=*?kiDTC&?wyx)LWd;UD$HemQ=U} z@tjG`oY^PQybMw~$#zNb66Ps|s1Yehm6Az2I$BWl^9v|eM}lxCrtxZ@^Z`i*YqH=+cD!0gegZynJj{7C;ksx(*P#X50LVV+?@nVu$O|pt$J2v zORilx_yw;m!STzG*YabpiQ^(QhCORh!z7)1DJu10A+BKw4W%U{t*g{GAM znt0k@-meZ8W?7%~7zCN(me4mP|82geH$*{5^rw&?Fnz-CPtQM%FF{nQeM**6)v~ODi)|QxCBIAzq^##DtD>83?fc2%~pA`Ef`J>q} z0-9!4AZm|ZAK^aKvZ#9rc*%IFas}sZXU9_^r*=+>H4XkE51zlc5ZA2!ajyYQ*rNWV z;)R9nudlu5%q_XQC@!bY0NQ}Of$9KVW5%nJ-=pMBxLp~y#@k5V78KFB`=azs0cCY1o7^qM3Lm4Z;}UXhtf zWGdyc2z1F&31`ym($S`(JJ;1Rm@U^_FUq)~Yny>oz5-w3l3SE=~sth1XShPPGSnPvF z6CaBFf$f1(e`kR?q1YyX5%*gs@M!#@_61js!al1&WcQUq`edX_>1mwAw}hNWdc=j! zybZUTOdCHdR^ zAiJ&Np{!3_i%gwez9fAN`qMOm|Ed14H!9`u;#6joI&De3Nw8*0kJ7hnd_>nO*#XA> z#zZt=oht{Yls6YX>7^&o_mWxC~1_v2@i!&QUu@ z$*kij*;VJ^>XL!;WS0xkPo*!-<-&C{F3-!Jr zd^h8cFMxPMm>wOB-MoxN$EIX2r#!#jvD4Lrf^cw^`nTAtt}&mNiHSR8Tf+;iqh&8Y z`Bue9#=@bpV{(;>lD+UPrX=*4Ff->dVVyO`K|CzYp`^eCgOYiGbMwEOfKgg=&kRw!9JAY>R!wldT7PiGztZ`j}aU zzZ-Xac=GFI) zahr{_Z=E$%nW!7@UB=k^`yg7joO0clF`Pyg-MI$0c@t}kal4@=n%g7L6n_u#uQmv_ z-AbCC^qm^eOIKXhK9|dVt$m^h{S_G3%*+L)|7CSN3^ZKwhhq>817(e|?&IB7ES#3d zWRH9iS7?ZcUv+5ql_6PGYu3$MMI&Kx>pPU4U_2W7jPkTd4fPpuQvqB`vW6`cYyRq zt-4n=-;~p>Yr<9LDwfB0?Ke3;zFXhP7jn`)6Z@unD%G3W`^spYNjC$Y(#I2yHC#rM z&1f_k*+jBzmT9)BUKDQT*|>b>ZV5eoe;d>3P|dR7E^fcnxz)^-}0jzLXz?pLWEl_mXGE`vGrM2D!f_yMNK)E)dQm$nLR zh~?6FRdkKHjoH@FY*ifycg4QY5yigvVPncR$HXPpD_>@1d3n}&nl+yFLbIjLnl**? zQa7zynZ9R_?r@b(Laz@9w{_@{nGxDc{!3c+Q;Q#%xGbWREiL=$MGs86C!*6$maK7w z_Z?44YI%!K4>o`Q*q!BrXWn>y`9Zd#I&@}5@y&ztR@~dS`SUln5AiHt)sJ0oKxgL> z0Dq8wp(qaZr1hauQIn%?i+YG&je3vtqp8Fyj#;@6^|5-b)2#EYHP#oc+pYCcMx!xg zC!%){1@SYUU`tR?NQlX@*J!Z{qoH0rv6XbJ)74`v0&gKqyw{)x7%Wp_vDz#atMzs6 zNl9eGGfJ|+1a$^ukR>|FYO!?OC2pc1@iy#;F>DbtUX4MoCEBz@==m7Lj7t>Y;#ZOT zA};c2wPo22Q_a2D*6-RkhGtj-8p?jv(qEkav^>EMfbjIy3o_DD-;vcqL}SC zHGZkH+FCR-eilnkzuh}EyU%on>HMbIvzPSv6?VkfoX)13)h9bnH6IT*1%-C7%fDP6 zXSIaKg<5QJa@SD-VZqiI_5a~)z@$@vdGdmj%-O+7LD{=(JjOQP`kZ^ecq{46 zls#_!d)D0vqQ>^9?G>kVTgtMOweBaAo=tu_g;^tQn1xfYrr-0T#+T!g20eaGN zh`8Gv2&WH|BCmWH&4nCab5rt)X*1vMVsiwCbszlktfh6`s+TvfyinRV&>9+KvfO&- zrN-H_$EV-0^v(&%q2^9SlV)zbz4z@4tC|nIto}v55c0o>yKD+|;U_$aEKz7kRER4i zIV6?R@~aD+07=|OR{0fj$D#}V5k)ZG$wef*Fr#1 zr~O>0LZQo!#c{VtTc?HP&^ZFa$Cv!_!j(eD%dexKb& z(V1+@@1vX2X>SH$QEpUNF5d{HwbH0?wn`(LvD*dN=cNJ-4K!`D@iG^QMuAL?aOo`y zm%iOEH>LVhRBD*YJxjTUGBVuj%3YLf8=lxdy9{9OAccdzs|5dg0H&m&;^pZkl`D_hA7 zPaIV0ppi~O;q%xy?GOu%bW+=+C`7{Jzb0r@s5vK9R6e%*98!<}ijv-wXaw1@$uG@7&4WX_!q`5?YJ$!ru75#e)LG} z$c)d>XMwT&i5ZqSN;|Wz0;)JGuKYQjsKT~wFE{|QCcr5xvf=|SXvDyC4=6EBfl~HSJ(H?`N%u5!%=&(AN7CjM3%-sGv z`}*f(FDfM*68HAjYxaH3CGa-a(y>)nESZ@o_n2b-7qM=`)jwvWX+fXeVzm8@XeNW# zGH=>>sZiqum!5OcJ=w->2QFFav{{_)uiexTY^XeSezEZ6typ(w7s31k5WT7SE0n@( zmAXXIQ3{6~-byGWl=K8B^>SVZ^{zyLsqT76EymTuLt?#JD?M&+zCAQIJSTB4zbCLa ztY4G3n|nZdLi%ZVIPtplo-~qV>m#_7oRhpbd2MoSawE4wSE2v5kq<+q2 zsUQU&h>E|(BS(4(@yn#Y!kZOapkh{dKNQf#OrDcfsHi>Xk4$>qu}Kagu9tXTS=LOa zcR&L)TpY=9M-9O>GcrtpI7}?32$n)TLKsI5bgJ-u=|}Hcy#5`JUbA+5IytZvH_uwx zJ7a}C#6G=t{H+T#{2qCsW%`ZZJaNf10^xL5%g%*AOoSiVkJMjtA?6f}|8#1B(w^W0 zwo1Ynv{#%)p_DXQOKN^3gBl@TY2rhCSgPk`KFw$NMn20o@y&b-pW7LB@Z_623VhWv zyu>1ALIa#f%o>kTF;mQ($u*mr&3Sp2smJ^kQ=fUU^Qwk*=3AWKG(9H@zQim%(|c#) z-GnYRyEs4I5?5VED+_W$mVltXNzX zb+6f4k}DkvSroIuO0Yjj2W7s#zS2R-X0yUBVEftS?0Qzm$~;Y1I0DMm=D}UBo=Fe`zon%LVtH6g%N$kc}5geQ3y8y;zu=VIy}jV zbj^+@ZG19PZVjK_Z}KR9^D1g%UW*pHPAw(IJ#b)vRP@RYjV9%>$9r)`AbkX;UF#}P zg>mU5uptQ@cTc&n@k5fA9D8&8+&+yf$3G!8%l82aMgRrx0SYAab4BdYDdssg#n}_+ z)17ZV-`N*frn}5(0`+a>H3`$@@dl(SM|Dkd6!4YT=a=&j^N0E4JPTp_a|#;*RUhIuTs%osvV3Gj zuBrLc_=GSDsDdxa@$hMsqL0$I95_>x9~eJO8q_@?4MI%EDc6~fpX?Eq*aXjKH$0yx zHa>@F#2`IQ$ZqN@0M-}N*U%InpJVQJ(l|gRr*K+14W|K7iq@Xo6lMe3)6(z>7!d=M z8Ne4K;4=^7OBEL_n-sxZ;Z6OfLDON=ag)yEi#vhccVSt@PJrBt&?G1-ejXMM3&#bc z;Ee%@Vh|R+gPW^ik&2`FBS%yohk7bF0&Gk@&@ZDvu7;|3Ks9kX?D)^ttvzn)g#%P$ zmp*8>I_Slx`pJ`i^}ws@lmCE}me;WQY@@aG;H6c%=UDlLUwx*@96p^A*N|L2? zm*-!?KLchnYbRswvsyC_tJQAj9Fo}~pa_O{P>RIs6WYi(W?KqH;NF{eOI0TPqn5Gn$bP{p~<5Yz>QAWCdQA|ZBn zT}32uhd7LPDv~Lblp70jRwtY6vRP@&W=$ruvFK*pMW)074-6?BRuToQG&ScjQJ-SS zDu%=+@wmu{Vtc8rZA~kiNXSLDwUsRv$+wJycql5Z?fIe-Em(_EF+A+t;mgAucU>1UQR>zVH}dMoobbDW_W=)0%HwiaoI z--`?QD$Vx6(%10Edk0ovPgY*@f7OzI8+)O8UoFW=!rbJ))<_yY(pg#PewoFwpiz za4hmgTe^4gR76I^pefJ|;)znAP(j7|`3l8D0qit71T_s29;rc99v*^TP=7@B1J&c& zr$Ba6JQV7upjJPnY0-~J9jNYWy}>jo9tyQL)M{_2)!tB(-iikrGBMzU2Z|yof;_8U z^58B|9H&Q?XT27aOhzw`($TY{%^K6?9FvLqFL$B-w)8Rnz*aTDe4Uh?2lAx)?T_() zzeO7P1D$G2`bNMR2|sYahWn^PXZxVBH1&IOeD#sSkrrpFv)LUQ z)5T-qpctqnMGV-)85o*UnXenUHIDP*sYUZO60=GdY2SdJy21rgcN(Dp23ivZ2!<${ z!nA022zI-}5Br4WNGS*9m>~P&v6$cQqZpfPGI1Qn$QZC^l48JN=lz#avID<_#BU

Ji1q}uYp4g%8*9q5w}yVXx2k1GE@c3Kj0V~eiI3C( zje;jnP)0_8*nZXA{lnUEf7R^O@vd`gmW@<14oC5SG+}&}+m15y_2W0bd4}CCC)J38kDfy6oWzvIF=$(l}%qa5|szV~Cm5b--yDVL{Ii8KIu2sY(XI|{o z&9Tg}b$V~btErpVHRi{3J1jeF+r59&ePsT?@{#=$=MhnGc^YhS&vpD&o*O-zg^i+7 zG6h{RBW;Z#))6x)o>0X^45=PsPt*EJPu{ftlKb*m6YewNLDN3dTc$&%PfZghy~!u} zV>EeKW`S_UekB1Pp~**R@{v9o_t5L;L3$tk7JZ2Rl%AlCv^Us$jy7YY2CG{3eqe>x zR&)5+mdmMWVhgZwoaFBM5t- zqLx1SKO0^>xa{?pe?9)jrrWS-`->YdeQw8!ec^xp_ja8A?N#F+{N%Oq&A+)|2zNcc z>z&2B#=p4p`QIb;e=|VLYrq8=)JmCl%SlryOtNccYbhPFmg*H(Jntl5cP*e5k)LJM zf4A_<;G6ZYrw@exC&bo;O(|Ap(@mlFR8zVqG&kIn{%UA(cu{Ie^VO;4(wg)J>09A@ zq%Gk`!_S7E4)2#a4l&XhA9%`-!DTV4wPHe>jG@DCD2h3p{;NxVeW z`-*;QGI&sFK!^W*&>%_OawsBYvQnunBk`!Q6}N&cwnGS!L1Lpsuo}u{8yk(SNxKYD z1;r<=G8>gsDV8-FAy#PPQhTW_C_*wdnS2Dk^B!j?59%udDtai8ISkjha#t=EP`S$+ z%JTsnSX0W7tOy4j+A7;@SXMQ?4diwRViIsiZ8n}ix?DxPrk+25h?6g4Ks#vYu@h>F zOjZ*f8stZavqmK8A)gj?R>ifUTqe;fypftpEtNbZZ%U-~02;tk(?Gk!`jA-~o_PPc zv{@DkfqdY!d^z;NzrBM8Z&{hM@le^BU3(E|89((-=Mhhxn~l=wy$6Pi}LjQ zPmrV7wXbl&CF|Q3+)BLEFM$&d0w*lMGnMajdFo0cY;!zj*es=U12d#Kb1n{C7+ULD z>0cgL(Y3sL_5AMywgz_0`fQ%{%D~DXHN!Jkxxn*PX|b|o&J~_z%8k;^b8eouLApnJ zLwSAH`_a$*wzmKZ)iqXTmHFm2z}DT~9jP4QXg=@_c8F_SiS#NurB(RI`CG^OpY-q^Xr zc(vk~5eskP7006nk_I#Q&OdoEA&V38{ISu`M#e@?jP|M|QHJ-}iLp_ji&2s|x0gyb zVgggC^ya1k7=HA$edf7On>8X?vS=TgJ~0Y1d=v#>LlehNBNKIq@zjjTRN9p=ic@gA z7`IEEE+JEghRMuAZ5Bn#$CB;|^{iJi{bo1<4Oi=!enOT^dc@XsiGF@^716V4Cq~YGC*^c{>*}~zG%8{wklFcM67d-4IIqtqR;R&pNsedP9M=+$ z$sSY8mX>9^jgn#vi=y%&jC-&W9>i~fE5++EglG#eg*l&mh{ZkZAWMbVCYEBey}bh? zZ)ST3NTr%ybuMSDtS&88vnOOOorn-}#B=5$@MfN@Ee989flY0YWG!3|wiUet1LVr& zJ+NZM03J|Zz10_C96eKA$a0pbplb$q_8I2LmiqBSPkwI?x23ZtE+V0=Ta0&KxA6YE zocC=d;usf12T2%=%^3P*&ppW|>P5mVWE-0iix1p0_mRus&{Xc=B&g#pU_lM^DkXyu zOCcIL>ReVIk9-tQ^A5=+`OJ2Y5`eQTXZ)&Z*i^ju4~vR40;C}eu#hQ+UDIB+t%R8 zvEQJ+;rfPq6WUB|c5QZV^8Atdlj~1zbJ^7;P$?Hi9@aKvNrPH4VC1_=L#L@}Xqhd% zLvU7Irxe4XaIB$0bDdolGppAz+PQ8?#Nm(#N}BB7CUv`9`1cVwG zovxZ@0+`W^k&@lW#k(k%3!591gd7fsygoS;a>y3FY%x;}vR5gz=DkX?;8mh=Xt)Zl z^-=^H-eBJ9mAqMR#oOc6@m`F)yqEG0q07`~XXDf$ZI6|$ctiycQ}-%KN`ZKb5<~8n zT;!@Q1uo=LqV-VtgL&6_Z$Z16VQTYk`ZA{skK^DT@WT~`tZKP*mmtG)Sx?e#q*iAS zbxAm{PR01EQ!#e9ijLQXbWOT`-E!S}ok8~k)r(SSgPI3WlZfyd0iQia3i3MP^Nz`* z+LX0bowCLg``Z@#JY#Cd+B^lB8ihE&IyE|WM9m14HHg&`$_$wd)!n8wYEeDBpV5=w z@F<)ILeadF6hxU<)&99p^YAa%6Q_^Oj2Vn=ln{L{CiCbmaH_1{lzZaiXOYok#4gR% zpZjzJoSR;qV}~2Y(A^7%A|xkZrtB^|>FzYr#wq2X`VAsrN!Z>gB-Muq1x03MU8L}M z$Se>kTmr#|Kmu@5cmxu_lfp%UbQg)eVZbbB$IxoPLF4F`H~>Sue5M4^N9lLJ)iHk6 zhU+C2=WT=nUb!6Ky}pbquc&;`8W4m!u0U5#{etPJVf<8t&Yt=m{a0<@>en%V`bB{H zivabrsd2?(wNeh`CBHD&+3n=FBUWe0?s1f&9=;SwI!X#j1=>nEKpIprsP=$M4%Lk zN31Sfj}!Jpq}w(py2!RDx*fBie{441Zl9gc7Z92a^dF5zY&HL4aE;>4fK@fsqMt!@a17Ze zj_sLkE|{?q4Nsh`fkU0Xk;zh&nn9~!x2UE1)q!WBQSby>D|R*LZmrm=HLBLrsYMM%8*+wN}pyN$ot;s9KZr&p{mtne6{QcSV**;IkJ&oOu95 zBd!6l76CZUJV21?!4XMKQ2m1dapS4+xf|86U{pgSJ~{rWL=LFg{Jvv&YaeMG4G4}p zQ-Qi@Y;1Df#EauIw02!UM^iNZu!iIDUi_oeF+8zq>@DWj@hGyQHbpRE#4;8gMp2fv z8!bMY&dU$u4bR+7MD`K>D9X-UwCgP+9#~wS50-+f>yO||RyjspjLDR~xWQ#Lfqu9r zz?vgtZy9)#5HxV=><)!qfQY1@UJp!B!jCBnfkZbN?lVvu4JVA0hkC8~kIl41NhQ!W zR?@vBAqng3b1}ZDd6R+SY)oOb@h;t6tq4y4vjG)RY zbz2Y$#$!S}9y9l3hUw5yHq3FvtRxswj+RRQ!0{gZNE&g)Fl}--ft|P&d$i&n#&;@a zM_4k%-SKwLJOB}Eiz1em;-%;xaZ69^k+=fp&=c>AFN^PtGjZQEpK34Widuu)^$laE z;>?BhYDayaewm)ud)s4ie#Z0ok6MBpYvJwKQIgjp(?fvEvC+}d546yB?1(HAK?z75 zA@i+S2z`m$tInH}aE5FIbamyj?o8b6wg+v|!tlgjCL1%GzVizxa>Vm<$^{doK_xgdmUt4`~-{m*_cFpPsZdr2YW#5oC zS*`8Srn13qbp*YcwLPF0)T}IiG~C;{0884(d`NPzxL3KsNt~pUhc9Mc z4`0(vq>4W2NH)0ccRd*2&g?KeY1$D!>EafqFHhf?-jKW_y~DZJW#DlZKGJPw*2eH} zFuo&wFMb$5jrW;!wiU`$*X+to~nasm2_uSDj4>i-VEQq!y{MIVPsk!aQ9gm-dIhGO|ANn)oJ$;VlMI+I&EU{+eW@zciYP< zE7w*D##IGloH#ZF*a%jojcf7*2BTSwAow7e4WyDYrnNz)AlgV6qGzQya7%6JAQ^V5 zuxkIohu(N#{L|b2dUaIQ6az=qgeK1QZ2RWI9k*S1bj=Rt!?AaM_4?L3$3J=GSKHO4 znH^9{U(B1c`k5`OPNTS_gP8Xjog9{kw%_hvR2*&N;%-WOpkWGSCy!Io#xF#e7r zFnTGRHj&K}&Kvk14*kOjKp(P_m6`x}Q$hhdN+|HJCRE8A$|r~rcKHe@fh&ogL|j<+rwV zeMPX<3C_7+f9b)S58l^X0u;VAaY$!YQ7Gf%&m&>tAo1Q#h$To+0(G0}#kuuR2qcA0 zGJR^wyhJKxrMZNRCTk_`v6N;xui+>wmv>GJ%@AfemH6etw8*M{y$7;j2#wr#81S@%?UN9>8zbLa)j3w1vW?~T76dKdixf6V<8e;z-P zayXqrlw&y)>pDFZI)&bt)a?`yXL6XsQ7#d8(nt zS(O1s&=HjfYRhg^M-6YJB|=MD67)1`KvQMW-o%?jT*w@% z3-KlPU-f3jdu=^sPkCf-y8eFW6QVB4hD;#~tz+~?hNH`#a{YYIx#Il#^XUc50%3vk zeAo5jYH>^aN$*kSGlpMSkD)7_2$2;oIHnIfOtG-v5b8ncI-)khm0(YBdGPzeLqT28 z*N{l#G#&17^|=nYXqPwHeqr@zG-IRcQj+;cMpfzL1-QROBBv+>kV=)K!|-#gD;XR-5*PwJ)AJE_Ad4m3i+Zu9X4yVu7zdNOI;=sA6n z)l~3im%7s!2YDZaV?IE5yhj?QgNoD;7O}k$^fc=EV$#R+5nn5QCEeqDg?IBsPMyUd zs*30Favt;E;$F&xe}E<@vjC;!M^NYSM<>?+%^xdQMn*@pl~tOoQzI*Nasq3M+Wx~V zqMBFM63%30FL}U*s{W2r2kylx)W(*P@{N1YgG}&e)Q6}YIRR#_Shz_1`Tiod7JG`zitCEU zi@M@4{-?s0g6?36YyWd~^&1(qF*4CSFmj~225v-Mk4@H|RLUbqYAIZg+&f97TG~ln z+q=sa&m(#9nW{V!XbzwkC*DWg#3!I%kD)g5!=qYy$vla~%*5!fX2JjB#0eCDUMbkn z#EDu@bG?%rlz{4pkkU`rvwdHTPC7L)UCOyHAHE>p^mElR4nHvFm0`pH9_bZ z9L;#buaKB6ynM*k6xrN=-Y3Lz{$JO|QmC%D^Wfe4ulU}xS05PPu<*WH&V6vz zZUF?h<8zg-CkzP3nPu9NNbbw|2$SFu<5PUc(< zEjXe7qc|?ojLxBp>u4N_Lh$MY{&RY~J`Kt#xv{w=r?tU4OdMNN=m|bS$*Dvv8mTIP z_SSr^1=@S0?g);e$S}TJ@j|Sg>uEN-Gmo=W?<2eBnD zS!qQ4cS+V#EQUID#p`K&zE4f0P!mT$pC3W`>fBUy`VsW`Sy9*|VN@rmzI>u;5Y(k# zzv9uw-~6tAnVL-)S4X_>2Hn@Lyx`U?1E(L;L_7%hFBWxfxXK?K`{yKS&@T7fODlJb z9h>~2m8#}}GhTu5mUK2mqX2e3k8~3s5&b8SoSy&LkT-Y&mw~SjKkJCJ++YmJ>COrO0wj5@-sgw?JD8 zyp~T3D=pm6m-a%Nq@OfxX&;wBd;9t*(8Z;_JQ^Tvq0mdADR8^6^Y)%|B+JgHy{vWL zx~%1#XlBoxIeYf(*|X=jXI#3{4s|3i+HD#kpip=vyG?pbMo!9{F`Zkk)ao=A51Mas zkJHaV|JqDsP?6>=UQgro$P*Qc1ll)yG!`~J_jWMf=oWo(VERsXM=eLIo(#Qc(QGoO%qhze^F7rHQ*|5OmP)G3XdHxk z8#+x%ZxewRIG0-pR8>nchNj9UuCBhRHQKhch4PiX20Jy_EA5=yWv{S^Moqo-b)gkC z?DI{Py{0OQ+3Y1%7GgG$sE%qY$M7mR+6`3 zW-}-WVoA6Jms`nES|6;h)0ik<1#SKj3rWH|3Su_?ASC2#Ao5{Dly*dE6u{#(vFJ(e z@tD4&?Qk2{mJm9s4_9+Qll|c<)Crz4(<&5fW@1%t)pc~x1U*b;^a(moRuZ0ixU+20 zN0gser~~wXb8egctdpMu#em~^2mh*gFvKD*X)@XLu90NZ*9o#|w1J~Df@2Dxs0P_f zG^;DFM%vHvhz+C=T|5fnbntK3JJx`ZI^SS30m9aH7cBB`gszKOMdC(^9rr2h$mJMh zX^sH_<`__8jyak;%qL}aj}(~CKEZ$Nvn^;hDV<1z-cYrMsW7oWd?+e-s#>ejUJ87? zSqgnK_{YI#a|!4tyRJTFans_EREL`?dRNRDbHwa1Tg)1(h!sYyI4CVyGk&NV$NFZ( zUv5H{oWvzKv$Pav#z`kkFwKPpWjn3|)Zkn21$KY}04K(}&~c!{5;@V3(S?o!9lPl0 ztQ~rW3o>i78!U+H*Rdvuwg|kd!(s=>4r9TN8ap^qfk6g@z_H_kpgpEHi#c}yAk`+2 zT7iyQ1&$-8)iE@ z7Dt>nl}U>x%;a;8p*^S%Icr-(@pgT?z1`OyZm(-^ZIAcq`|Q1~z40+JLq1PT>-6!; z9^dVi-xUJ0|3&2F*T>^{G*((et0Eik%@cXxZE1e7@GC(2e( zw-3>`WWKkQIMNTP8+|gFC!sDgB&=4O&7Y{P4Mh{7HQk9dS8AdggLYduM54UmEoLC= zf{BMM)_P^DE!G&dq5Ujx6KsvPLpHhXBzN$*|E*9VSqPJbAX$hd3kjjd(4mk#1j%C7 zXrdd}{<7V)+p*DR3)!N!=tj*cx`zH+;k>&fE_31rx8#Y|sD*LB8$ABU9c*wq-oTjy zF7dxnl&eKnsW<^Ftm@k@FhE~ylD_RdMO`A1xMNV5s!-6v`YLIv!WMk72dLDmaBCVJ zA39-lTG5H4Lr$K59>-4xTyO!|v`M~S@?K?cxupMpL+7R zF?ywY#m>1Ov*70}=9ZYNc0vr64AdyegC0nN9#}=+_bBm+15rOVcIbJ~KW;?p@iVbu ztOUVf==c}U&pdzi$jUu#kDu0gTIrRZ6#WGKisydMDQ`JUr0m28Be77WsnX8-_^MWA zkMh(0Pqy6Q(feT}9DvDL1^4bVCEUGUK`%8=@~t9DHcv9s+e>ChWj=$Ox^af2aU_VQ z0j5SW7zMs^oK1{Y$F$_F#+K_^KHKs)8{u5?0#4TLV536;vKqadrI zRUTU=DZ4-x-t+Xw;F`K~>ql-;>}B=&OLW%lQA}*V>QkT2md<`|lj{4>2iATfZE?>1 zeqr)stLWR_^YOVqEFFn+crr|siq8jHfcMRB!qt8KSs!hOIp>%P-+ zo9lm@zvtFfDXQ%U6bEcKC~mUcVE=^s9n0JHKe@~GK2l!cD=RbkIy4DQ7N)5$YZMx% z*Xc{hUY2n(ho`utSb9Oqk}U}B`EzImL+s~YKW?_VPKlP0#X;C!)E8)*ttc%0T17)^ z(XiF8XLi;fJo3U_)OPDXzHsOKYuE03?a;SwOy2Xck#G5ac=z?x@YUz<{N(((uZ`UB zv&^;kZrp!#>-GNxliXj;Un~C(@U5CO(eK5w8n4sKnO^eJ3L4Um(+B9cwGXd&wC>4z zRe%fB&91mFEI-`%awBJ|)7E(-E9&a|JpJDOhH(A(}^wIc2Pr1yqK6>VtL>U5;S>Tr9!VuP?&nDjcW)Bvjem0k}vj#OoD zV~rg5`ouH8)kGPAR#4;9lvh~w9-F6GQ|l|cZby`q9V+9pW%rkHWzH5INqD^;h`rzQ zq~`}7S%+u4C*wKfxz%&nqfD>>g7%ei!jo6QT_q%I~CzeDrjHdIilDhM+o(Knm&cf<$*VPYiCfiNh9kOj(NDitq zC!KqE6NXFeoJ!a3!86)kT-NmPB`1BDk7w0Ul=bk|6oa9y10A#iKMqrr1?^1=N?UBB zB;a3xt?{DG5oYei#VGnqVaJpCd%ib+;-xdG$!Beba=&jrD-JXCV+(x`!kXsGbY@d$e5oQf*%nK?ZF&fWX(hBo=r`D+zhnQS$4 z^ifiM{>?|A0M$Vqc0WIjeb>+qB$Dc;+k%I~|464 zSIot=ky!Omc$ypvX2Z7xzZ$$R_%JyY{6X*~*ULfulinx8-&3DezY%#`{U84G5!G!? z_tI}@zTrITy)UBN5E>#Q)&J0R2mP{N8B#ZdY8qCD)~h$vDwSnrDvi24 z?$A(8!pUy3fZ`J;@8r&*ZEMz0{;Vk0gW9s{p%p`4VI$CQBJa+CEIf0z078RW$A&k#WE=wI8@;xsie`^B5Aj;FcB(|$!8~z z|LSYc+)mY>`o(-c`_k-@j~xfOYtop_CQ2wrEG~DYKTuUL zXS37dS_^jkh9D&cBQ?C-fHTRA3WHv$RcXp7u~@u;DoY?xRh@u#tt4!t{N~t79yDM@ z!sSo6+?qfnSQX%P3BHW(D5GW0I+CcWvLyI`EfCT0fx!Sr0%t&PJ0dWJd-d3kMfMHs ztjAL01~dZgm^-_z7Q(ZIHa!GL&&m)QGXVjG1!Rrmc9-Q9Zxt43>Z zHQ2BC=Gjr2$3foQ9=ApCJ$w6|Vz2+g7luFRtPsk~0q52C&9^dZXoJPWJ&p_BIH&t5 z^zi`kI=%Kw_OFwBEce%beegl@MD2rvPmzDA zJw5nNQ1`pwF9tb7uzIjRc-r$^T(N##yvtwdcB*Oyv^UF?F<(u2+ez9LQ`c#%zJanX zA8ObWIxEw(c(jW7O!Ly_Sc94*RHlSFNmMkedP;Rh^`=U$at=9t>v7#;J*wz8*VFZP zx6~$@*Cbj_fyQlywrb{G9i*9Nn$I-9*(_^zynoft>?i4p8w!RCXsaNeyAdl#42PMK zu4uvVhG-2|VoLIG&}y+(Sl@T@{Ob?)SbHs?Uw?PS!XCyp$}U2Qm3wh(*4+qB&q>jG zz$5vUg^P^X5mGlECdGpZIHOo6`GVxH#hoYz0F?ri(~C~8U}&#|pI5-EMJ_UrSaOHX zf@Rav;~d31u}Y3}*l^1;+FCCMLBdFREjxNI_p*~5ls2xr?v4+1uMSh|nvtQ&pWih0 zrKn14wCK&UYIdeWxq9FWCmvs!JwLN{^D`g4>IPZZ!>%CwEM6yJRF$>)Th}(NZ1dYI zEv|{!`rCH2G}mekc$VXvUZ+{7@|MBW|1&)`ePLJaNL$DLEg{@l?glxWl7F5=DIW_( zctd-$jz)wIVS{k0?k{zU#-hkhLKwVd-4g^r;>v2#J z-~(JDumjroN$$YIZE9%wZO|og$1>8?`2hb%v?9Qx5VYjfw`9yf8_tR@k6Yb)H!j?n zfQH~_K@;H48}y3gXTvt5Ugal!sQJP|p3(ZfQ(3 z`*dc$!W5};=7*e>Tm$w5e-ZCrfSOW6O?_TAN<#E-Y@A*P@_H+U{uq2mBRHR?)ZjAs z>}tEQMqOj933SL;1~$q+B;TVrsL1I)t@w)K^SUnvO!bZq$AIGlj%yr>FW4!KwXSTX z?C5~Q&eXlr;H7?_*X49bBiWK^osrjR@P0tHvhE0gQZ;#1YKuyx_6Gu9;*f6V0heexs@CyPZ2pe$-Ne#Dvg!5iJgZyqIi%Bg}Eh~>~E9q-q;&1nm;bAmx}UJ z5>YUI{0HZA&yLTirr7}X^bPWXt{?npn#suTzrkjZ2fs4CMX2JuY-D;%g>`M>aWNq7 zB@y(h#Z`d{(Ie=YMNbUH+WB_izlT&=NhMclstB-0s#6Q^Its>99#|%JXL0U~V*X=2I|Guc7f{FlEK=2+xvi z+j|z21N1<6uPn1(2koF(VzJJScyANeZVT3rymYhVuf^>*_RklPEnNUNw_LC$&OPv% z6Pww@V>PpzxaqEH`kk9u#2OZ{Nv12VaL=t1mjOiM38>ZA&YxFwV|$lf7fTu(P)#~Y z92%!4q%d{Z6LxM(XcyV#+!Z=V4$5BQe#5EML?tV;s_bf~;wM)64UQh=Xf5|54AZ)b|hjn=rwfEm?uhMesO|ZEx!r!a&+{?B3$}qc_fphmT%$I_5xT zE_U=tI-DdOI>MDG(eAoo%^MwO&z;4K1a4H7Y?PIpID&tL^G1%CTHk6Fz+ux~ao?ap zJaE(l`JJFzo1b+^xxH-4y&#m2;f#jJfE;EFQiy(t^E>=jf6)Y#-38}BYf1+mr?aBN zu}kp`UWmfeLg+ZI!y`lJIB>_vijD(!jI8K5@OmRFIu6`&vZ7-{g>1)Jg$*4C4y?TF zP7oN2dC@;3-3?OoV;;b26Y%_!fO(=LL&wU3C(oaiZV&k>`Ya3|urQK?VIYQuktht~ z8nLu!%)&qz3*(*`+sHGCs}JCZQ1M;rSVrl-*WzIx+2$*g(G|sAuwsiZIWDzeJoBk^ z&-_nzscNyAzs6emZWRrTucMx8a2Nhn78RtNd+sbPZT=ISW$t&}9SbUe67T%A3L9vE zHo80JCmLeZSa1(sRjo7$ie^Pj@g<9#m@SsJ1&xgASD-GpP+eV9*g$WtZ06XNMpDDp zs7sC=8A|4J(9j#|!_wY)U1cxZpO>sz+h8hJU1I^XW@0g!iMrmLKz;3UD;w(dmXq{} z<86r=@M1zWn!E7TwAy)v>9lz@xrwLDbagw;P&sw1e2m7)QJ6;-CJUmK+n@G^#XG;* zxgutq+Rm&LZ(OwV!A=!1Jx!%c-nOwdD6?4QxThOa<1jx~PypiY!>Br8Voz z8|ReQvlsJj)&Z!n?sZNro^`35OK``eir2f8@{-xVbo<+9Knh&+XE6=WQB#ysP759U zx!8wzEuk72;gv?7x@A0XF*edheh1ybUq`RwzsG&w!27A6Tgk;}45CN*Bis?g3)~Ba zGxYbeGyE&YcVurHjrdm`PIEFI)whAS7z{iffP}tR=6i@YdR;D1yvE~1S%zlJY8mI% z%e*?B0U{sNYh(!>%p;G>6Iz%P94AT2miqC^IPO;dDW2m`dyEN}DdB=tkKiiM8K=yN z3;afXk{EaccbDM-!&8Pc2D#xB{T$)w{0a9e)^B5%eQw)XaU!{Q?(8{s`R%LBo{q;W zjiCcS8^)p9jUN>+g0>g0G&aC6ehbZsceF4SXsJN4G}yK66tBGH(P6tKxWmum<~>u2 zY5;h&>}9vONIs--bnuNZcjlNaFaUfI6r{Zca3o8TEh=VaW@ct)W@ct)sm0u4X2upX zGqYOUVwPIWj88pxXYTyjefz)Jw~RUw>3-ZZ(zDE}tSZ7Cl6yVI_2;1;-^Z{}<98{C6C5Y?zaC68p{YL`omnw}Y#WCcDA zeJMzqFLYU64{vo>_Z#GDz$1!aaOlSaq8G2DMs7#LlRC&?$7V6~LBJ-Ov*FZWC%~M2 zPg2|>$35x^yFS*w70wG(^jp*OnVhox`sPwRTj zwnTcMea1Bz0W|^(R!rVIUq}L;4_3Qp%x8cPN=_qTZO1tQwFJ08KX>5If@`@1!iIeW zPcF7*YbOVj{UCL}Ct~%=vBRKv+&$IzE-h7em`{)Th}MvD*7)-BQLL54izPAWpKBYy zr;i6tg~+_tHM}!RJg(7WT#f0<)m~d>9&h-oC$4t94pU22zi{GYpIBcMe0-w)0$JYP z+8#oG=lH>?OJQMQJ^jwz%Vll`9_0@+IF2nYge~~_iO=|+Fo<6CwO|LxBfQBoWIvO1 zi|je%(Nwx$`n~uvHMAmin|_~sYi@aU)dQQ$&(6}yvRuf**Od9~)l6Dh8Ja!#A`6+g z^)%A*clYA*>iQ(oeChSZ8~E%yGg&t*;R<@MW$?^fPje$n7YyKwC;%IAebDv{c?NcY za~HtI=JN75q*B2%6M%1YZrAeavKGAm(})Gl9J5q4E#Kp=u=qA7$WO%=>kF?H)cPKs z3&o?I=fa!Va?EQ*>gC%cw)_t59!UHFpEu+;eeK^jL%sKYf5zl6%jO@L zKWqA?35O9WRk)64v^ING9o-w%cMn#+Kg6ITcpZ)YE)qhA zwbe3a&%_2LPMG_}?x!Rga#lL_%o=^W+9Vje8+2gQ@%$IGNH9E(?o6#wLw+iOjC?}| zsFD0seHfpdU={gsdg;1>q92j9qkf2J&-DF7pQ0N3JrI#^=!Ws1L^L=0!GDx;_U`3kzL3Lrl3?1JZ-$V=PuQ4e0z5nONH8A#TPC%U#h=k2 z#xM9x%gPBgr6l@1RW;EVBpBCk>3yo<2fv@Q$id#5CbqI8x7vU;RE#QMmh-Yp3x6DVKL`dkxD8T4Q$ zfWpk+Cl+Z%czdof5*{GTz}D<5pB$M61*kWaqD}A%4(YO>Qgoq0giN9aghYeoQi3fZ zzIT5g{_Pw<9-!?hfN2&?U=NsWn$8K-Tf~S3_5?J4tBCw$l70&bpir73RDvy%{UDCv zQ9H_WFNpI%4R3B>7CH2ZU~h>ACmFqj9LQnlfXa|7iq5`elD)xQrU(xvB}vef37Ae? z5%bOKEY`4%X>idj6CMmVK&7n~HSgNZlt$Zw|GmrDs5*~w&t5HHFYKqETDraWwD1hG_;ZitdMmPw%> zZqO_%vLri5l*xg%wWqwi&XfcLCGykd)X}3^ zL{wvz1dEk+a>$R-Tty0$`DMhZ9uMZBBqMHw*O5*pS*Il{gyUpEUPRGp9p_-DU(jMz zyws6LTM~&wt#F%rjJa7zhPE#4aEI;ykyF5;5A0~fi&{iB% z)|Y`z)0}mj;Hy(eGl9eP1J~qLM`o!Fd!w zO>tfxm6TKx&1<>%<*X-YMnpc4ku2@?4&srU_h#i&h!FiiV))rW`w0E^ci-l1sNejwTD5v!*so}261dC5B z)})D|SWxF)bu?Y>)hQ2GMwHUuGKQGCdcSK9kMQ~#^P+hH zXt_Y_*@S;!8+8zxffiw;Y}OdCx}u(jc@b`qEscEV2<^r7zd|e7^9t|n$5;h81_wa* z#U@v8;Mw)JO)JJRYGoI);eT?Z#Pyv8Wy~aqy5cGU9eM-XrgnVWB)|VoR7{LOk0RZg zR?e4V98e#PfCco{HMb_$yD@S__@3qm9|RuZ@*t7q#)L;`Hn;+ca^HY3YzO3rFbCW4 z#;utyY6Y%=>9yUZk-||3;9Q+LPE{iOVUx0HfJ^A}enpd1o_L*?s@TxmLw5xp;566+ zw;5B5M+B(v){p?ld@)3tV`%xRX_2pDBy z;wM=@CqFYXdKojfl9Av#88<(;9z+Bm<4A>|&GB%Cq^!vcB(Fqegbuz2YpJ7b z%-rEDF}LEs(OLz^zq>#k(44oC?BaS*y z8uO+iiYhH_=p6}<%fdphZ)54i4I-huN}LyBOI}Ny{tdWsifSe`_ z;xoKR#JQql=50oUkEnpPy$0c{JW9YzI(Z=#drp;agT^E)A1yl_Rd|H*CmsM$;wCr!^W5&wB5SXi5qUyyc;69f91o`v6m+@l4U)Pgo3npZo z-D%$><5d-W6tZAUKrdwYp!+#;`i;KUjg^g^8N~-&wKM<CShyD1=hG7CiJFXhcm}3y2NJ`}e!zx@IqZ}FZ`n%FP9v2ivz~a@TU`G#HiVdDsPv%S{A!hc;*g=I$GJ@=3 zDpW?LSSuMn2l~^FGD3WQr7Y`IEeIzUXMR;8^}vC^@ za)GTuUKw%;hBVW4Cnyb4L<|YYoTP9+6o$Am{G^~x55oQH;r=n&<~szh4>53f19+Sp zR8Y1sv5#)%3wKy>pR91u_Z<$CEm#Zi7cj3-g4Czsr(Ox(eJDpEZ)T9-oz3YF-xSr~ zWO=^fv%iut)|nPss@+A7WbfQZ2%T(kC{A_O@X;FrgMgS4#6n~X`wWc6<>Z6piM#cq zLxzHMV~0m-#4Yw0UK~otxEx zk78dECWoIYS+XS+jn#hNnk=3hzewOEdnI3iTq&$Ko{&EKYM+psbo6zfzi8_su~R1| z&iryG&Wz4!>6_^P)fNvYCe9;M*Pz!m^_=wo(LU{4_Tg-AH)O#c`(GDG=UwL?h0EVe z4@c4yo=-Y{7AWTlZ`xjTJ_aVqVU#!GY`uZd&tk@mf0vM!All{@by zDG^M_mt-T$ZihiH(cq6Kt6@G;D%*|Q0xW4toWl&d{SA>0FR+*pc>Y=77t%_g^%*NK zcb;T$+6d7D?|U)5o>LY+)5On?iMVv2)Ilud$HebyNK@?*aS8G47g`rN@_(xf$o9{= zfZ7BM+5~J247vma%>NuP{yk#-myG=%qd(MtfBi9I`zME)TiXlj+}Q$jJWh6V@g8 zM@a3BtYD#-7}?qXj^m%m{P*K8>EHMt@%tYv{`T?Tt^U&eCI9O={QWfk=>F;bf71Qa z?|=9DPpkjz|MvHvn*Z(mUsnGSDvtlRQ2ld)|9k)E(*OUDzr6f!xA9Nizi#JW!TrB$ z{^tH;^dAA>{AU1oc<99}ZCp&9=*4UdT}(wxjqSgd)c;j}lYohtgMs7kTAa+x4F6G1 zwbL8gJ85X~k@NQKP)vwTP>3LIV=4heqF+oP7-95KYz6{S(mz<6O`w&cc2qU)Yb{QR zh7oF&inKzx21*Xd7Mq4j2R5SDoS^_){B+gRK#nWr1sLE6`Bqv7FPB|rvyZVCG@&BYi?^V z-UWzu1+~-6=#v?#J~!;~A`gTfvDvKd&yzh5-_9^*M?pmf#3rk&(yy;K@8ktfp$)EV zP(;r5rt;u37QQ`n4!OT6eom2eUXq`%x>uWf$v&OnjeNhDyXO0*H-!t`{$6of@dH2P zq1m%3(z+4M8FqG!{NU;ESDwjQtE-6i?sukKoo&s=cPELp_L<-rOlw2?b+Z##`Hc^D)Q5b zmTb`S33r6jLdIMoUa((};3P1KdzBfvw!|RDz$Y2Ip*WEkCq!*9z`X1k%F>n8m7%8+ zs^Z51FAo^B1m$P#kBL7d?h0z+==Z7`Q@2XYEw~wSFN(#F?YQK6hSwPFcEY0k6!pvV zgzq{X!;wZsyC~w#6zELzM!b_yFfK98XU#zG1vjv$3OV+0_n0FJS-gUM?!L7P$zykT zV5?&0&I@4)dNx8H0k$2fNVsF-&k11JXXPuhq~hg=3FJmy&TvO#wJ9;g7OM^uI=Cwq z?4p2U>cK(_PT((r0sUwS9Tf10S7uB=WXZPAQ(f3-4XzDp5&ZpvB`)ZQlx$dny1f!v zqQo~eFN-qHh$Acc<5GUjv&J#-qR{k6S3;B})2y8Fo^_LTQ*=xHD`EU7-$l^#r&#nm zqr`eYH9_{?9TLe(7@QLRokWtrG9HAFYPhl?tu|*u;&R{TKKYYk^Q~O765wGYSb}FT z7AQ73qne~2D2bXFcxzUTd1@W%s;HIjjev5qn;;Vsag^>Z>s4h!@0pmt5RsN7%8ag{ zKs_)iL&~)UtGeJEu)LPNXA*9%^t@KBc$iANyT%dsyma;4ecK?#ZqPJ)z@`^|!2F6=>Eg45P(hJ1INfUAx^3OO?c zj8Zv*=`ROu^{Od=7`dQ#K8aeTg;K#gs^HawD%&mSW8#9TBB1e8FI?}cv0#0{+GI< z=@`y@YYY##S73#Ni9}8fq3a&K(T><@WzY?))!qUAq~iW=~aQQLbE z-e3FF=4ZA@UyWty^W}4{;jxO|qyblX3U~tTbb^;20YZF}B5r`BhS$qQ4jzTnLtrLv z`2<(z@0j@n?pZ)TYG9sem>8V7wlLPio?*96&|Uk5S}{&Hr6xscTzAGzA$($XQv^)UZgJk6sDPPITMAZbC1;4YfiF*PSkp9X zny&;bf6@(-T#5pd1$9`(&CF^sZm#Z~ZwCv-n&*D16vi_=p=(mXlkEQY6CYX^{q7cn z%9}p#1s%I!vk7w!w`$MY5znh}0Kb^UP`%!a6hqcnp$o_ve^-t%+K}QnEyz!+%3wLq zup5K1lg1tn5X@g(M2?Ip8=puuj^# zH(nPN`ZxR=w?CehXYDSZ2tgjBwlA)fXAOT_gf&(OMiWYWg0J>-drOD|977N67*6B~ zB4I)ZQr&>0g7DKmcHl%|Ha_tzb+SM1@Iv?i32MbJ4Cb-9H5TM%=nbs;PC{ujQpIh0 zG*vDN)r45{{;J=OL3=eWYdj*DtSbH>w>ZF^Cm6w(2HxDsu7-!ge!XWN`$g`jE6UwslW`C zW9+{^`{dJIswyOS3||0G+`(nwH6dI`!@6Q-D^6`r5;(^0iyVnr~ z?+8b&->N-E;rV@@fyyI@+ZfQi#xjMZkF&# z5l;REmASyP+*5JpXc)6{0T=ya@crmw61l0Tu%_r^kQ!Y-MnrG68N&TRSJiElMNYJYk!=Aky z0%VOY3N%`4juANW!U#wV)gun{N$>NFV4NkKaXRR(_&UMLsh!AXffw%{^uyycqUTCk z^)CgNNDh;E{M+m5{>cffxVaF=@GDW+8@g9epZT9xu6L*-;c`4*iOu0}zat>_4|uk= zgK~`$g6+Ar!a63%7{bz)57nqj>`Ja?7FD5guc+nZmwkP*8o!Eq^H!mv4*q3)S@&$XJ$wY3`jO$b<7BOn! zrJ3Wz)^bHbAy#co{=`W0p|ZF?;JFyd<_uzGo0ohV;k#oMqc=~Nv)>+8F*pl*8%_+7 zTGWyc!7(S`sKxI%!d;#QT)sAu@B+Demm3e=_*o1p;a)JJ)3`Ki4?gYvo#XrOox#oT zp}T_{@^FA`BCR1=%-y0MKc4{J?M7e+YjkaMt357*uR2d?FAbFmqB**I(Tg{NoTA#b zM_=th?ioISry3NacdUhtK`spFT!kr=*@fC7L37Woday+X_TAB&w#NL?S~D%qEH;LU z?QB z4sqYL^9}e_)S3Y+Y72`u%3}+3>sOpbhXD=~99EfFeeh-DCCh~@ecx^b$q@w*Cf}ax z9&1SQ@HpF6(J*#L^jDz*pBl>c!ZmQ?(tON}OZuy*^T4_>hgU404iw)@?GiD1@$Yp8 z>Ki2#v1*S_kFnF&;)geh9{R<;6c|rL-ZP@d-&SWn!mi0K9=J>ma<_srCR`-#w(b~# z9#qyRyRLfS3&<#fa3PqIe=j@7ZDqP(Y*wjfUY{}YF*aka{U{Cp4Sh$kvGnzICew(; z4eOa!1`j1i;0a>~ayb^45Gm(vUSL$L^*etkzw1?GxoRgwU;BArZn75DZE5~3v?Y)b z-`S`69sJq;Ge`2Ez^Z4j)`@*qKd-LT7Gw<*L^~JOqgTS#XP$F(W1ue}zXCxQgO*6& z@2ouY?I2b;gFfJ$_`=%ED8qnnH7Gw43k2to=rkuq}%#Dbi>i!>)DEK zw_j5S4Jtp?XHNKLj<2DBhE~Pw8uWWS4)Ea(M2@GAT)8<359OPP-{^kajUZ@TXk!oK zDt#T0;(Fd&hd#5zZSV9vYImiU%AE9icC0a~-aatKi1dvA=c{(`i`?u58G<+0W>1Z4 zdCxF%XRLc?svkXU&LH(p^ntI(A_F*-C#MHuxyv#nX?zbA3$151=H~F4q8XjZF74@u8(ea zOfF;idFE6ucQ$vB{q7Kb?w&@e`oG)vSdO=>56~C%W|)`{Ek|%}7gnC>T%}=O9-cYQ z4Syd(ehAdeQ0tJ}q8EA<-)BBRJ^RGksdc2SHx;3&m1X-9^~7MG+K#_8P~yF+@WkHw ziHc`HYvX`_mkG>05iVS)-pwmD==eys&v$-9?57IuqSBy5eujSr)`Ot(+xz|GXz>oE z6@viS5q>Cb@yYHBZp?!PaoK==zBMxKi}PCm_IZA*;{Xi5jJ>b^ac8A-Va{vsBn`~o zRBpY4gli6puP@Wge%YncF+e(Bxyx`?^ol9-nTCJDT-)9WH`n^=klVoyFEBlI*Bc)! z{Zf2KVI8%a50OH5@g)T?eSC2xn4pO-J|{$+&{c}(Yum4|p{V?o?p+fX{L{hKVC}L7Y6)5bY4ia0)>!A7=!i(!@Hw6Jd!t; zZ}#bvYV~>cudQmSs{XJ$!k)<*LLUEYd*+97-M1wE!?5i|e4*oaDSSGxelfZo-qz3Y zZ!{PE!#&(!9>YD?9=_r(_kQdpYdie-0lMoFXvXlJ<7OH%IBvZ;GtW?GRr-b_l=;QO zmlUXM&7&TXcp^Ss*Ix_phcLCbJzw^G?@^L5e&++~Iw*UlsXM{d41VzZe4;Kk0Pm5Q zv;P?i?gg2oyq%fTpOYEB_zlL5AB%INHS;4FdFyqVajVoM$;6mkq7SQDWEGj(GsqEU ziCBJXXZY8$_CNqa<*iZAS?vV)L67Y7DV)BZM-c{rZ2(-2kLSwN~2r7m|$*OrN@{1 zpLE2t%vYJ)upe6hw>1nCpdPU!w&DXG2?mUXWmu1EpjW2uz3MC^_zIirXg|sVT+sV7 z!L4e6a0l*U{l0!jU~frQgV22!ZTd=8&b?e6@OE(fSv$4D)oJcq1IlB&Gg_d?zMF*J}>_p;=6e6=cSVSh7zaYTWiEkeO)oBv0XQ%`QX)= zp{8)yy2n6X0C4qBmxrGc@~6LAwhvj4{& zUQD1+S4W`$oZznnXlt{hVq`_%V+SaL#s;CxuH4=Eo)!2|r*hMIN*%;wC-B}TSN=-3Bn!DYLC1Kn* zI1Q(EuEv613&$$W>zfOOVb<_+!MsqmuBgdy9XW~HKgubORy=SQlWDg$HcU@9yUB>V zW|8(%&5VB^36L?B+mB0_t;nCA{MfbioX(`u2S|_!CMU!jf9e%Ot z<7L-L{CfF&a&teIyFl|0>iOo|y|C6P5NDUPZ*d9%5NCW|nU=-w{?hPGF_RKI=^nX= zUE^w& z^VBbW4JISGt~pan(uNmar6xxeQBx6WjS+E&CqLCVr{BF0ITT3z{o5UpX~n z)f_eI?If3sRru)I%DzS62uAJVu3=4tce4X=0!Tl^#u941yGSo}kQ=X!!M(-+87qDW zn)h5uQ*S`Bs?Oc)$8vh=6yp^PTBq8v5Y$XZgowUBjj8q8-6ycT^-5J-+-R3A zncjUHXiOGJhUGr}c<9sSw_!>Bg4 zLYiqkHdQ^YxdNvQ3mPU<(7_w&D#f4i6`5a`SsGkCQ87hem8(63V`tqADaCu}}1Rl^J5L_q!YWBE%1DKRqhFbBN!mZT%2_ne0#E9~v3F{=U)l0GZ!w31G@6_zq1 zfqT^CETZT;H5ZP=CxS#SMA0M2oNIuKFF`i0#0dD@jQ|%>92|VnN`Q+1K^I>f8Tee0 zfE9F+T5^sx6~0F9nmRXlUt5!WT|%x=d#+Hl6k-c)88{XI?lL$INPpR;2@%@^;c#nX zq!CawG4)Z7sta;sL&ODn-VNjr?}8)Wx#}W33jh}bdvX^1x&jg2%!He9?5Wy=?6E~<8 z74OK|E!Klx>`8=KT2f8T&8c#+#KFzZW!M4alF%ZnFy&{a&yib{c}NdwmwTN?w`_Wx zMjM(DT}0=UNq`fbS8?uhqs=ZY6nD%SYbWw@#vSt;^hRdrMim;CfA?`WoCR>VBd;PK zUO_IDUqm{>AW%r3ad`(=q|LW9Q6vEVjK?l?MRrE5son)bnJw21Yowo}3s_i9N;BUW zXe&Uf5SS^Hkl;othzRy3Cd`nibS4=@hHQ?X374=CA-SVK@I+5Rxm4K{-y9>F3VbmOY z7_1iNCBz<@;VcrPMkK|ONIRQiBHdKH7BQJ361kCJBH2`=9U)Zi{5yQDHhgiHAx zgglC&s%h6JX=M;{Fg;w?pMZTAl&S^Iv1^=_U`f?3*NXPFQuQ)r$+!MMa2qAB%xO_Q z3&9zcJ(B~DZe;d_!*aKExmt>L7sILuca-DL`@vrl2RTzU^L$s=>Z>AThKKq(*g$+$ER=n@W>-L6k06*oFYJMx0iXi>8SrG>bY_}% z5C=gE`5;{{?C692oY=N0KnIN$cl?B6V>u#o6(Y4|hy_R~>n@xDXDv`GGFYP+4Oj|* zbbx8O37`XaErLdo)FIHV|Fge%@+8;Qxp5Z!||{4~AWC|AStrZ8_C$3=JR}(3G+S(BAW2A%l3Tbm&RoLts2r zChRoebts7!%?Mw#BlCbiK>4DXmkas_ivLRz;G0tLm*h*_KW{NU&e}@=4O8DsAkFAN z1t!Pf5Ey_AlNIp?47NdlOO3vO9D}2;x9kr<{$Z3uatLlgzR1nEQ@!YkZb853%{&Hn ztIvJ``dwGvrRpC5^`SrdhWZBt|1kcIrcZ1p81Dwaov&j(=wbk-06nR4zvz$s1F)nl zL9*Ax0E$61Sq6YAfXbkNiV~d4-?7)D>8{~O?W#=Gn`e6u5Fh@@1LY6&jsWxF4gGn{NR!%+O2mTPrYhW2wt>Pm*yX8m4g40_$z<^2Qf_% z@+paek8?W_6c4EFxECf5Ev@XN9FScBSVoyN(SNKL#h{!j9Y6(0wO2${2~GuI&;MHL z(oB(mXaEvV?#dxBO!@i?ka@DUcU1lW2t2t52kIZ_`@{G*+Ew7lw}>!wp3dzZQ23hN zFQE7E=o_f`1JHPSS65VjAn*_4Z`wYg+KuwA+yx+Y8;o@uNW}`&nuUL~O+eM8|CEfs zDN3KvjUnYqZJNxoDA*Y>u3G}sH_1>{l0L4pS|sdBQpHK2sq{Qe=~QQ2jC;~3U+Upy z|2n(AG;@P9WQKmCC>DI;>ETgk#%`D>X52m5q1Hskc9N*MnAug4wu*1N16z}|o8UAc zcHq_EFThjl1FpbwVyOXOc@U#uOaUqcm=ZuX0z?9ijv^@7$cN+SWTDVmz!VYsD%WDD zpGrd$D&CP1kt$gSCu&&tMwTkNmPw^7lMHo5-Z9zC}mKmwA*&G^pAr zI}(|*iz<3B>s!>rk97WIUy+XUUNOlH-A^ZbB{s^ls$`R_BGMB;5F%4DPIfIY8kI5J4I7n9BZG+F{^5N zY?!`6m%k!^W_k{CZS@Ib9nep>pvU}0+9~bf!+hxo3sd!JZPCTu$j98sN88w~Z{Z_l z;d43TWo_Y8GjrN+G(cmRJwP--)ZgDP)GuR56xCmC%_i42cW_Bj4P7@%GDu>aIIbNW zbx^~+kDE0BvLSPUKInjh9LgSneNU%VM?v><&&X%Xg(}Bv{!{tF;xN1p*|mW<_I}xj zs(Msf8%4j;ot}Pa!cpHIZQ#BMbcl_|>yhccy(UjeUU9{YY#$uthHk$B=(%YW9rVUR zzj!N6Z5_ud=~#F!;{Iw*GC6v(r?NkjZYfAg)RY;!-a}WI)KYTraWUsn8Q+3v#SLll z*Zv%uTe*H&wPahv!y4Y~rH58ppD+2DVkv9O{0+AW^o2n@psLR1`0@09E<@T|Z9Oc)AjwfK3`{BED1V@I&BUM^l%`b1J#> zP)d)q8fmD)A*Qi=ZhV+wykb1}Ox`_G4spiIJ+O<4e!AWum?^;U;b{?)e*2^+o2yv$ zI6j7l7b;E_vkkMY9x9Z+f@?zm5u|)q9ln9Qvv&~73cK@DNwThSOZfb|ik?limpeugnvJmBK zx`BbaZ-c76T`u%n0~@NxL3c2_s4nchB;@ke`=)W!$C@XRH^@n1dy75A?`ZaQcU&jh zQO-%?VP!&1mqGR7S05Q~>}CBCRV717GGQwWKGSupmll*?SxuX)X8b@vBB6@6N@>+d~kY^SMKDH|lu1HFf4U2D&dIOcG8!ru{ryF+2G-bme2f66lL{ z5IBeB;N$-x1AdHyer2c}k?w7y5#18?NRcF#cS&~d9)>2j8{aAHGQYV*wPg>;o1NLr^E)1yx7^=`B>K_RggX6pc^8P2(E>Yb|e#D%)e#7uPA`dv1 z`(1BzuOdmy`LIiP3m*nXi4%=uckT4Uk3}^r?!y!;YwOoMLTr0^Eb+D=jhTBWT|Dur=)$9z2RRTFw%#K zqCC2;(_h>tHjc$`EU7H0rUJ}p@`_!+hdYs&J~73*;S9-#OLqH|;w zwlY8N$^;Yg05z2F4|N9MAK1RR^G%_H8k1NW)nX8v8S=J7{2Da8j#ta)tvFwVvksGW zESW8Foarx><(@K$fyBgf+rIgJ>8AU9pFG94b9$O-6pRHaCSN$ZY}#p+#qa?;(WJ+2X)#S_Cl==? z*%JHN4^H@Q>>cqrmWnw}trUz*oI_(4!9C=5UErbm)F`JFc<9g5(4;l$lI&LVfO6;{ z!Ck7$;>zaASezD`ZYA5pbk5La#AVoS^Of;|jUM_t{dN1Ozi8tdKg8p;=;}|;JNa%P+8+2Fx)%^P7Qy}7o;*qUtLQiCO)8f4tcyLL z1p)0Q^Jm>_=T(5yOZi-4GseuZ>;f{|^a@2_M`9WpjKAb#A^FM9K2*XhfH<__#@1;d>I z);|JXg|@Ujkj|0x<_+c}XXfH-=kP3a$%H&OU2Yi-3rdGhKc|*c{aX~X-hZB*q|j8U zRTz_=YUuKrNK$LwWLso!gK%@O1q%SNN)qlsLOjlUY*M-E}c{L}Ux({>E>(r zUJC=kfRd%gHP;pPS62(ptDvb%>Cg62U5}D;Yvw|!Re8A-){E_jSpN$>3LfhLyB&rTcuBuP zKc8Q^M}S{_tNiCN_FaPB0{0!-#z;H=>BEt4^*4z}*xHS#>8ggh*FHbKAv=R#b=J$i z&4XqFyFuZ&uIB;ehID(-m2YoL~=RD&5NIs=nAY zVL#IZlnwNIKzm+r<0ga|TBLgiC}EyA-7#@R6B&f<`<~$LkY6fLCuTWA8)gAUxgk?L z6HB70`+nJl;z8H%5&|U|4F3d3_XcF5M*AgLni9}uClK2#N_PUq~=0SQmIEX*AN}Atp(z_1>tt=W2m_`Z^?_t zrs3d%F4ab?+EZ|1_Cu@^?87ot`Mo+V$vuI$x&f^WsRvS7F?*qHyz3zDY4gs;9`|f3 zfwVuF(IooOcVTXdTaASig^9sifq$^L)+Qy+t|Cri!PHsQ$H<~V(@uHDY8!w$37s=0 zyXk5h8)sGvyZ3eGn2vGc;ySl;b9cD%DnE`76--u+bD_~`Z=OEg-Ir)KWGFSXFW|T{ zzh+&?ngo*P7K)s|bd4IZ8IK0i#%eiFi%-?;h^J>A&(e&h3KD%D;BrJBk-*;Lr*k=- zjiT@vpo@Quq8Lhk8$J$&%xu3;;76FqQdjgwnYz%2!{&$b$x`5%PF(R#yb`i zA;!CsH%9eVs+Msk@7(&+=`}Z;Mmf!LsxD-bh!z_T6>2DcAO3c*t7NLH;?p0EZ=-ej z(b)*Waa*l)iVT`7e^>B!?3oqYYw8JFJx1*yn4=T-=(z(_la_d^M?)PMkqJ;l&`-GQ zpPQo5|cF z&^oueq&M>nvIVu}?z83t?k(;u)`$L>ecq9t<1zLa`V-|5bsMBD&?TBL&NjuHrcaGX zhw*kviA{T)X@&6+M>IT2>Kj0`bQVgyi6l8Ctf(anzEUet1i%l~ZMMo@+B6+nCpc3% z`M!J>wy#b64$JYb3~+VN$>J&vIFiM^e#J_{1~_H;@zrI79E3OLD^0}0T6iJes)@Wy zs$5R{{tdj`gDow$=hn(s_$5;hYfSrd@oUw7$IVeSs|zPW@|dGJNw}ln_{GCm0K8y< z6allvWhiMoqXbj90FJ#@@-+I72G3Ai;h4+_(gELiuIwv$3^uuNDG4u2zLyb}v_YFE zFwqa|d2Jr?ShlW=4Lj7ww#MG@ze+W9XlqIP`1R>cepb~isGa4&{{EcRrn;wla-gP` z_vty&S^m*o#W){RdSR@#(=210`SJ7-)5tZf!{UkoV05I^u9q_O;6j-T|Do)V;i8{(XBsdcVvC{V~N%-c~_Zvprj+uQ>U={dHw3PPcJa zQ>soe_5fz7#Y4-8ws=^ne1V19C*Fb|Xr;MuJdinNBSVDgLcd0lsjNpHBs(iBgcO%Pg4#_M3KN@# z7<4PaaV4uo6sLPfBTYo1YGL@xT35@M@uX1*JC)uyN}DJQQ(|kBHL0IJUb0VeGjt?W zGDR#^GH@u9$t2VJ`zViZrLuM*5QRj$U6??<`0L%AOsdL*&=d$;M}<@=i=6249N7=F zD^}~1FajE~Um>_vg8+)bqQQ#eBfwbChBI$BjR-L#v6>jx=T?P)omDe6xDL@ea}K?w zsj)A%JckE*Muvrjf5BC9epaU_2zMaZ^Y4L=SN}2=u?nl{xO=%>1$uUV%{fiR#>&xg z-OCNpDIwOIProMb%F_1s@OkDTlJ6X5uc+o5mB#fPRN8=_ws>M{$xp6=fngx&E>IP?P7IKpfVuPIGyuzSv-#w5k2{gM8{JJ zd^p3fW(0`BEK%69_l8Ai^>&@tVqZ(r)lum*&6`MV?-_NoSrLVsu_j#u27bg0@4{f4 z7clxng(!mMVvu671c%K)xFIDA*a0H4?8m6$hA;Js_y`Dx2X3Q6<6(Fp|4N1Lc3*nG zg?~&=+Qe9dcDr7}7H{G6c--J$;GPK4seYTz(NB(LW7{v2Z_Ak3!?s_pKd>ft-vCv# zr`34js=Y&b(Kz1v?XBy<-Cc9x^O5Jk@Zq~r+F!~TDakOBhUQy*i{xAUy89ykf7pBL zu&TPPZ&;L0MM^@tq;|rlMY=>#N$KwHlx{>+Is_!7Tj@pxB&EBNZX~~j_kGT}Z}xe< z=RNNq?_XzM>(VvH8gs;Njyc9$i@n#hM9;(9tMl-lRJ!PCu0IZHKDEuz&M%>@Yhpwf zs3ia1g=V9JD?6UCGKPM8(7V!J$*)f!@RSIuzb&Phr>h$r9CA0T<$cI)DB0Luy+;@2 zVdQ%@gc$}5tjs|%)tMvvr$wesAE_-q*OG*v+=!kwFvY&j#ltHmhmQ{uwB`r%>&+xd z=kB~=_m9KEy8YfaEa$`y2Jz25xYHRn#2DkqoTUtvZ8CYKlcxHxSD#vX5dPBH0=L>T z9@^kF93$*%yD#@ENp(K|z4hBDPmfL~%1fJ_$#upOrLAtyGS9OFpR*C3Lbc`P6Jwqf z?zOWpE7vVe%E;S2d=%?jp4}ILZ}Z#CO4laW@9;0>>pCNv;S%|3&fJ8i9z4IS=qrSl zpDnmd+@U=jl6p=-en&5?eA!Rpq3xZuypZ@_Sl7v@98uPYz;PMV3eJJL72Nej>&Xj= zyQu159j&K(JrntT-reNot-^1Z8=6TKV7wCLs4M|nR1 z_~G!cp%XKP3x2eU7qLtD+qdzSg{``ofsD;*f1JKC7WBXO%mdfjs6?61t4atafYY`(Qwt%o;7bTw-0j9pO?s8vk{a{M zG01IRrswR@i&bsZ%n=k+A5{;-IwG;NPzR`bw3XSM2P8AuuYR#!F}2CqCLfd-Pt1fP z^HuUd|M*s$L@A-fF6q;ekP!NiW8NcpB}9@i20p>GDQ!iXEjmF{JdB`XCTRzGef&8m zLV$0h8ABtSPI``k|Aec*P5zz4%mk6C++$@C-nrn|Rk{QG8;kv{IW^>hKSF~f?e|+7 zC&xZ?$xt#g2|5zIAJ&pu2`E}u7tk^@w_&vYv_0oFA^sg)^X>4#gN{oYhuH#!fboRV zWC>ekH(IoAowda>N3Pd35>JiZ=VauTg$HY{_mVzbV5__B28u7~Jx6 zAD!Mr^tqQ(j>aNyLk{XPb*hk%lA^SL~hmkXRu7iRK;3J9~Ap&yvpvn`mG4 z2mjpqoXd&Xsne{9A@j=O;lnK2duhc>zs_6tGJ0Gt+T~cr$Skol2iJ2ySDZd8@ien(n<-v0}pD2ZdpJe=9o)(xxrEoC{z} zNGLvXZs?eD`0Tt8Eh#Z1bOsuwnZwBQzL{T-=b0HiZ)5>-!F5D`N2EYT%<1kaWXrsF z=z!<1{Is+*=~RwJrQy9PI8h#Yr!v7gS3G7lR>ZP^<7bsng?Uww+xQa$T;Y$1?LHcx z&A=14?3l7(p>*OT)5h>15inCQodZosQhDdD34V}0g1M65Mf$x516RIDYSW<|)-;*5|zYpY?zrl}sLqSWAIgR$CSQic+d1J2g=U-VGE_=;Dov{4x}TymRtTLi`dDr zu<56oY!2|Q)i{(uLnp}cqZg9}gCpaz?15E0K12sSO=>7$o%myNDl{!CXR%iSi#OSV zoA&N2Ehowy-2t)1ttgqI0^bDFe_~Zk=$!9jCOzkWq+dzdGy(p(uyogLUDwxa|GVeu zw_$n5)b+uK3hp{q+>H|p`&%r+VukiENUIASBC6{b>)_{|`)s4bnpy zIfpw|9BUXH+b4co)Xb_${6^=Dn*Y{Rb()kyD)+eaVIMkwFX`(SbAd#f zJ4V#|7AV}aeF1`Nf(nn+^Y>nXloHw9h@g}a@@P^0TKnOly(MEy>^m<0o8%j-9~HDb z&0NL`Dni2~sIpy8+~MkleeuCqQ=Swi)UnF(8zsRT*zTys|fLZ z_;ppU?_Eo}&0CwdUDZ!GA4rviCzZkSEKL$hShkuCc$VvKD}1%4!|W0qcb)nW$W_xO zw~Y$n;vw+M>;LBYMkDDB8wrI9BT;M?)9XBf5H4Mcs!Ski2*E-aOgE@Z#e`j+I| zU}q{nSZ}4KA>9j}TZq2<3oreB-|JEx9DP?$jElRwA81N*FC!_XYzaCYul}y4dUuCFtvi3RF!OZgw%#*c{H}B@X#?QU?WfTWx zZxkurje4mhrVp~kl&-SP;(SU<$LVBRk@Vnp1M!pRss$e&J!~Udmq8VI*q=xrUzFJ$ zA(ofvWG($9jhUtQBcss`s&DV9`%h>Ks0U9(3bcz3)+)S|)|e~2l81^0PQV)ltU*E* z@}vuQ9L1c#kJ8_m9Nj4t+sLfMA>ho(RIhz2S>Pc}<6qmwJ>!^Au1_AccS_6e^lrhV z!We^+QSxQuvSS|(F*FCQ_CdB#w^?4s8%i|19Olf(SGQ?J9+_c<5T1WwEnm!iN%D#% zmwO2l>kc79rt(H8{>sf%-^6tTa|{z^2gEbB z-_!NFliWv*{yOkr_)SE3KGh>p^Wn(wha?_my1ApHA4TZkW2A@>VpA!VtjSJc1y3W`%H?=hNJ?Ha0 zAQfqT-}8Csy_V#rqD5`}i9TKY{^J9s=MwhS#JleFn#p6M1@E;w@J+0qnTf8p({X>_ zk$op2?Of4@=y<|TM2J;|wm`7!KDMT~E3RIT_v#7pPnKtE)zynBx;;6|kE0@w!e?7T zp+s1nx=eBmR7?DxsgxbrhzZ5-{BpDz{sxTN&cs%jhk2T|ST9~Y9J_7xnKxXw> zc1&y8tDxy6tdEX|M|s_HIhujS0h7_wo_8l)DOsBjn1u9m$W6n|GMk`u5tZ&SMmBe} zd!Ic`EM~m9^yD_0C{u>X0lIDP0zqj#x%ce>xyV}U&;@&Skt{JI3Xf+FZ%`jRN)Po* zO{UKji&Z`z9h;}ORD30X@j^1i&!)KGeBA%WhBaED7XMAPphP1otw(hXN4<8!jyr8G zO5W9Z1&gM?B=s-4^xUNNcR~*<_PCxH^=!I>$-~g^ijRf2&F$G?9|uwy@J6xElvFt{ z5X#HT=c@Tlvygs$KxMDN{XI-@F>LTLQ*=sR{5@)wRPYn3a|KEQ+N=NA^gVtlvyj7wT&W*}bf5PDCaSS~?6^`24_Wyx_&zeM+^ zvU}YFzQjRN{VT1Kw_d?W@EpG~bnvok5rALBs9}+?uFSQizZ2r?8d+|lX!~`#R41^MrjHOiX&Mq&%V-|DQ9=E*J zlh2nJ9Py1SrB3H!(fIjY*-@S}>~hI>lHY4;fEW2wu4a9nJsJA2m`M2^AD-=CqUWEq zoAm9|Cf_UiUv*{LETjUR;rFIW*UK7O8dkkJkFt&*>sW8NA9{mR$;`A!wDCb_nQK0$ zX~((`k0Q4U#*X5Ta4|Tj$EKGf*gW#gsE4e%h4no1mzeSA4ZGR`-@fbI$U7#!??*El z?j>!!D5ci!dpn5E{zukixGyzK>2)Aj3lhrf+7oPvWyJR{k>7u)Nd&U zeQv!w$3%kq0=1s_lbHyEHK`O9?6C@K;vQdA$rGLoh}We2VlKZ`QJThOcIl>5*VI|f zFYU9Y>f|2pJ&m6E?4pxBd7jP7pN=t?Wt%N)Te86M17IOMi*^-ZIBYJ_3n_nHd$!U24sne@yUSuw)D?qSnkWOs3fnSx4$Bk zLwn;<`xSPJJ1HqSF+^G8qSGp-XXyF)d#%18tBCB(w96b9`Q8=i!UqJy;UOQS6U>_~ zhEC5OY7;GGK$kvF*mTERc1FEHn@Dk&`t~`?_U+_gdY^f?rCRH!5cV*liXW1eJ0)v0 zxyp7JgRj+u&gh7y*3@U<`9#ReF$cQ4Qf>S46(FsGoZL>v!O3qAKE5;c zLBUK`PK%{wkpKDS-lLBwfzL(*2`}*!;58M7^40qhIMS9M5)1XU+^8n++4kW2xSq5_ zFN4=oYfndCMugjpQFU@B@vMENXxJHu-X$>$UzOuOz15!(8`J4caoXLj{cXlN+e+;O zcv)-N{SizpN4U%LbW6PB1Y>%#Y3oJ-G#xDOzUBklu9%6K$Bp=W{FAi?&n;vky!h@4 zfB0zi!%nwiGb8aLxjg38s?* zF!fM)Mhf&1IZ{qaZ=lEGI1us1Gz|I9rW`!ygyOHM&6xc%>9*fl(#O5gKP>ha8@y_5 zzxUk1@|Pv8^KFnYE7^gl&|FCxkRN$UoKm_02$vj=q2L27cxJGTd_$ul)jH+R!e`GITY8#j;r%+~^w z@RlhdK3ENxwn>6x`_CVX(@9F@H(jSOrmAoj1XEo_SQA`D6&D2C!WK0a-4}x(<4`W6 zDM*8^E1Prgnbv0ThU$=MlSEygg+*?xM(G~KlA*k9I#tUnY~NL08Ipy@ELFCINf5!k zQWlaTE~90`<>_}dTt`?RXS_SuYp1@Ab*Uw;S72ABaj~Bm*3Xly@%@S#axOdc+wfjJ zX6TftRgH8XT?+5^bzs$hu{$Cb|5fbaA%vE@RDnH)OBH5CPcp4oo)aEfS)P~C_;sbS zNgOS#ebr^!hYvq#N-%Yz?y2G3P)94pK%U_r?^EzM1Tj^*E~+0%lA4#Pw#WUDiAuU- zz}q5I&~DR%CrEu%9GUX+KGTGs`y18t-i&f0_o)PUUgym7o2$M@}V& z)IJX-PG}pz7&6E4J(v7aFIT;Pi_C|UMj;ZHN(u!Omx-u`xh4w6RHwLODL!8sceOrc zRohk1t|q>5LGixECb;w@S}&Kio6+Dtt)PFB1ZDx#jVN=0bX#dn^CH%-s73>LvPL(C zM{`8kfAlu8e$N+p!f7h-$dd`SMCi<$`pVUR|Mu>$TtOy7x>B-uffBsnGgla}Ps1ji z(r{_*Pd&ps>5yL;D-ta=2BNp>UJ7ztekjaa4xpen_u_XM9pfq-Z`iMYP5-S^hd-oN z(0z~BnO%-3Eb7d>Fa3SdrPGDHCg<0wk#=$QyOv!?&9i*W0yma(3gjm|mts#i34+Yl zKh&qxD~Zj1h}i-e z#H@%CBpQ>vOY-3L03W-aB2hmcp34ZguD*qXnaKQkyx0r*g_x#S6j+(qLM-g0fdL%N zR=ST6?5YTa^y6@BHxaq5Rx(mWc)L@sq7&`W(cr5xL7bClw5?%b$F$5s%KK@$Z{{__ zBhgG=`UTsr`blXTHb1W3GF!s7DRucqIJaM|OWXVD2|KUmPqTrq&P(pzvImI&6!q=m>RiN~A2a=$T2xoZ>`i+9x6{+Xq^0*@F*6 z+H@1|l};6MK9_or-ltP8h5A#YOH%Z8w^^tJV`=!-!NykDPJ+5JjBOSWq7La2&9GHA|cwoP60(xono^&UT0p1ZYl`!eNJ$9Ft<%W%`t z@-{carabxaC4&-Rn=Q{@Lq^f$3$~Xj$4E~ zL1VI(1PASRu?QR4t)gZEZ!yN`cL}wvlccc~sGePnJ7NwGkSELZ0mQG)06I`qt!xyy2DN0c#yKi5L*z5n2| zx({Z#P)cA4%WZXp-RoFX{JF_X_ANHB5Dm1mhhEfVuL>Co z5>uP(kfiOX%hlKOX#NUpT(yJOyl@!Tt{}HE(C_*%?KK-xd8jm9Sg#}iD&7t>mcZ!Q z{`#Sw^hBnBL--eOeWeFmS(v559i4=b<8M8)7rWdO83jT-nf4BPz9dnDu~m6eEU zQld8qpXNWBvhO+KJ*t!Je?|$F%zCR)QI%B0UGFxdyjI|@wdQWQ?VX@$->s4$JbqTN z6#HguGXN(e`7SAoZi9!e!`W!6fFKxu`DDa|DsPLtY;_Z3vSMwrVOZ0?*GXYnaI$Db z%f4aim)jBB{K2%-XnG;Wi>R6uySkN+j-My?+x$-P@00JGdgixr_m%eUN_CQIq*&!z zn(pi?G>Ul(TO`llZ!_O>J? zocZMri+hit@|ksJ%`TYmR0H+mdsz+ zY45PR79A^34h!T>nb-GxPTfZkd?s*LuaopB+cfPF(mtQavEJ^_$-ZE(Ws(&1up(mE zM~o@B2Q5j|)Nxm}^bL579mWh)4rEj9zc9BN1ozqNG5jocrBvXr|5?nxVn2f+K{@wr z`%7?(ZDDqH@|2+lnP=@e&W1uo!^p+P;h59q^Q~fU;rijAVV9PRywp~Qqs=N#%^mrB zwF>T+7{X~>QOUxWt79lz&)I%qnFI=tZ|gQV3l~to^I^yAu5E&*&y3IF4c)yJyru9B zmw4-eFR6tkW6#%ix3zE8mGd9sZA|jn(c&!EyY9IOEv=nTP#>ReC7xG};uwD)LASmf zUKt4(Eqb*4lmKO$C+>FT)})sPgWu&y++Ilsd63)r@#v^+O~H z;!ZkN0=GWT`c0UWePD_#;G1?FqI9@#n)i9pV?fQmkJ!RJ-g98lq+x39{yXHq-WR2M6Jup_etEDbMo?)l?`lj`9(*fq7sDz_J(kj(s(kUs>T8v(y%`5kmp#7tnD42{XqQ>*b@c zYEz6?;B0WNfl<35{5)g zU(RHIPwH7ZeWQ>W8DLDlmd!-DpMgnrHsZQC0$!{=zle+$$-xfdrz;zlw>NY=nJW@- zaAUFGc&K5KYcxH2`Y!F@tp9D{Asc~ajospy=;4q1*{2JQK-PY~2k^0N>Az6 zxmS;~m77&_Aa<`js;suZ@m^Tj@)p_!=YrtzOze=m`*yBNkwDd#JzCewr8TDm=TzsO ztxf7_@##5ZZ$}#ggKaE&7n;0v^C_oh5=;jf8}!hKLa@vfZe9;ak01xcHHS zHX#l*_&(kphDDLMk+?T#>RW{v#*5~y4Ly&I)@t3$xV(K1bIuhdD-WAI{T%BK{P2<= z7wrja64cyx5+0bS^j>d_zNKyEnz0x$G(1-CVzS>m9yDb|jo3YlUgl59sWi*`ACJqe zf<2rc%9j5;gfH94!p4CYXb|)dmk;n4Ii0X3aQ_@#y@V7N{VMg}u&7*u5;}pj_D%^4 z4TH?<*=9Z0#UjMZA`P(}evYQq$u{HVXE;4tv39ZygvVX)9I9Lggs)QudX zAgnB#FHn#gX%Z+%jkV!0xA1)2e5xwDCQDeTuaCxeux`^?>Q50@bKp*B^|7p~9_x)I zKL56z^9aMb4s&F!xD7`*KffE}x{e@KH|mz(jwcl@7Qd=W2dnQ|uN9R!A-aa67*=7> zPJ5(+>_}6jBN-}>&udMmDF1+SH1>5a9bIn}oKqat5$(q2i<=~eq{YPzKES1k1)+(Z zaa4ZAqZsWN(2CctENaa*5ANf7MqLbk*IjB%E3wg94nlO-$MurM6yE85r(i&B>Jg&p zekT2npqS)LRT^Ghf`3!sl&i0X;{B}*fVY+cmD+ztc?%DVKW^?^-gg}H@}2*kOEljt z+Os2u@Z-xz+jsVJ``L82bLl+Bq=htNKqt?R3lq0R2_-yc*YsDO8j1q1MPq!tEnOb9 z`^mpa=o@#FQ2O2*|LE^An8UFn4fU-T2aIk%Vpo2s`c7XRbFmVgTs&mPq86``_4IEr zj_?i42=w5-d#RGLQOq78*Ux2Su$t5S=`qpTot&m#3biTaIgM{@wF@9Mk>zm3cOEy2Szv`;FR8{QS z1#BmOHa4h8^HW+Ot*aR3Q~Q~%BTsk|x=(2oJd;Osygu}hnaeUJ`883ebLnX1;InKN zg{J}MwtZ&WDq93wk@t|U;pke z^sypmS#2pOOEE(1KE*R>*zQMceUta_#^R;3*Gso*ekwO~P3cv9A!av{-qUpoBv1`; z?cgNW4MX2z7u$oA-SsL>m&nj$=U{UIS8r9;s4A+t28r}=5J8w#N`r8`!&rJ5ir}G( z;rlg@_!3KZ!^ZsHiZGty2y57~&plu(x{Hc&D<@PYl~pYCTPXkfrrRNOTfFJfgVji< z0mA}?uP@|p9>~Yjx8>n4qz!Ps$ryP9Q9k5o+}RZ*sh!l;%OKi-td{bXsdUuzX8g<> z;KMon{zHEB`6T=JmR5xF-GlOH*0P0j2MS>GQ2yve(SV8BZ;@pFngOTM4W_oFmF~MblZau1;PVoDc`t!(j$~0c^|dS_|ex_8&x@E?da9S z-GI#FHOtyG-OtZx=S-xz3F^ptQ=`jJ(x~X%ScG>|nGi~^?A2w+X((t7GkLKH19F2h z!fdo|)FO5WUVVQwhPevD+j|sCBkhxfky&_?V55w|xL1-zn@n9X@5ZQ3U?%IBHQ{$0 z8s7c})H19w+Vo<(P*_CFfp5Q2j%A#ic!7U5lQc#7gE1ZK8CMt02P2#klC-k<{_3AM z-kH`1Gw$PjnP*Vrlo6bbkp9W|MFo*3rQI$SHTfczmC(FR(+2s$%!nYkJ>Mcms=I-7u%g!HrSW#ZXd&N;>XmS3d&06!Q5H$A}(^d-M%4 zKaMkXjqsj!uSppBVDy{b9~d6ml>7$FyfonO`nS@iOg$O*-=h+&Npr*ueBVjRvbD4qm;F#q zq!6Z<(r+jIv?txSmHxxxbHQYaIMf8bCn1KWX7L$<5}e^S4P8KDjm+n>67O? z_uTqnPuedSySr!`25!xZ5)$;1%n6AlDRhygiHG)>{_;?V;&i%Lu_el{(VTva{z|aj z*-kS3%=wtaBwEsa7Sux_U}|Qj_0&bC$FM07TLsFR0(Xp)zZr^d4DTpQ_?)}E_t45P z$ih&Kf#9x=>cCCqXg^=$QWKG+eACRz*RBEj42|k&!>YLZxYJt0F>8#kEpI&)jFGec z0J30TjgWH-I$Aw%hBB@^oVDu5mi!qHw=(-&o6FpHCXW88_Td_(TABg|r>o^%Ps5*$ za6=2@{=kF>RVfeT&W^16dX~$K&%HRH#Rb+YY8)T(ZUuf`>PVpm zQNPliB-s2={N=rl)Y5lXm+Z4&g{_$M?QAAX=IAT_S$&1(re8Q9&aPSMtetihxuz(| zF&VGW>zYu>^VOlxi{)fy^OzQ>#1!l_;lmG&%fTbq0@$6OOK$#pCz6r#j4dB=I-{QU z(2UcV!I5~L-jJb+yNUjzl&K)zyH(Lp!?E2@>?B3|F=DKEm_M5doLlF|-MpxRWSixq zX9N2+2282uo^4C0XR*XKzI=@de|DS|-LJBfA1zXxa!mdOf6 zn?3oUcA8K9JLT$(9J<+;AEd}lO$qhfCr4_-BID3CjL`l2f**eKNtY>oG4R@ztufRV zw<)!sb?1BNZ55U9HFfFkAiZE#(OsR_hGEfk*>-CWOe)gf8WyUj=Y*`=yJ9j?y+HlS zMtE+8-sq8twd>-*S3((#=F0M&RIl$t?=zdwBlNl6t>?#fEa1DEayE^@XmlZ+0({-K zqB}h}iQ<)9wl4xyLHC#Y$OyW*{6$5`aKxEf!k)hW!svBpNIbo`!k^~uOB1?p(eD|E zvD>3ZJV4yMw?Q%HLjnLgx$1ere*vg{l|QyP$`-J&zMs5?~^Lxt{G~!9Wxr zi4r^G0eVe#qY%{YC)+!_6aNq)mwz*i#xr~&F`V;7WB_M3!?7GpfY`q&Oym{24VaQB z|6wlhsvTI-c5b7`jM6J*@s~2;y_pW3P!aN*UMR;r@tETDIqe3p*TEtxC>;7B&Hal{ z1ah_?vJ*-A$#tYZyx)z*mM(@mB<|~rosbagiu$CufsBB}FdiGd`5{|mm~1pg!h`x` z-(`4pMPuSS8CBOxLvvq^*s2G5`Tg=rA4W}o<$NUIdZHOZn2srvkFqrp`!&znTr|7|M(9d^{9|?x?ejk@Tf~r9iXQs}@ zCz@M2JJ@&f`W&k-d{0M1<6>9%SR~!$Q+`@5){Ir95n2{I`~1`ZJ;kyPVR@dA{NBwe zc{7bopX13nB$p&Kv}*4qyON@tWYxS-2X9B_eEEF1e)A5vBT2uOuB}y}?lM)B!y@}e zHUxcWjC~|8U9R$c9q+WRIPckEI_(yEtMgs58c(ho>1phdQ{#w(SspZ{)vg5pal4!K z1X_Y9_Rx&&uT;>2ZP~2K_uIt@J*aYy;ht|cC}fUt{=uD-dU z1C5@cv8k0H-BwKl9gV4hAe{| zf^;Sh4mNyTT+YtUoX${AYdd2uFfT7J7YM=yfp7pE9QLkO4!SNJR`&G&kRWDguWx5+ z<6vrSMRO%lSI^qfL6DA)=1;jcf01it&-vRhPJL@jF5m(em=nr%B?vg@dtzs8;HYnC zCnRsGZ)a_9ZRF5gNTXtHXFy|QZAYVEWMryu2<-6y7r5&AFKz*@E3JRA;!n^1Lix9L z8R-AhT^mO`i{H)~=yMrb7+M-yIoJcHfq$E3pwH)E>R@3g^cyb`3x_{WEKINb{X(si_OpcArk(Y^xl534Lph5k7C-Hq#CqJT4fV8k7)?f#JV)X?@HVuh4- zEgTIEXe5Cb@;mFg(ui7^0`^>!_nZGp-an=MU!nj6&Oc%J+vvFH+5ixMenS8-@rnLn z?lWs^i~oIqu6GdlC&5q*a7zdR0`qc!fCK&v4C8}B`5;gZ5Dy;+B*1lz^LI~qYXegw z*Z-5Af8qSUjSIj4dHMf;Oe`(`0gJpmpP042<5k2+iwQY8ni}wlbMuNKc)+3@JP0_5 z1IF_R!hsN%;N}nkL*d-eN4z{>gv4(*|K9h?QGgeIP|BbHy46Z-QKCq?-mYx5+DG>VC zO@*Nqa3|pm+%o)6H!tA7+`NdH+S^#@x&rH)XIJZA8V5T^!>fB0K**n)jv*j_VgG%v zqpDy}E|-Ms^*h5`5e+>BZu`_Sw2v2G!o1 zAD&En_;h!o&ct?j25)n7^Y(-16ZydR;A{7iGly^8OlOYE0}TCnaPc z*OH0PZM!jgBDGKQXH=Av(o|im^v;>X@RXZ++5%IFDK=CsJiJQ2+P7ty6DLwVvzXGb z0DG4#R<|h!5osK;R;s0q3=vfAwQNMPqem)QG!Uk6I;m0(xiOp9#bB5(n}%stHg~iY zj?Z`My*j0EFfJKSC(~NLuqp`>p5ANKE7}RGQ}MwtnC1;VuWZV0L&+n-NO?kOKuTTf zZu_Nou6H!7#bSE)h)9xMkMEw^pQ>1W8{X~@N<0sq;Xw>xsS zV-szww`Ny(2k2df6x&AiNtV}N;0k3UhFjdGD%*akkdS()yntET?qszwc96uXH+4?z z8*&kgE>goJE%6EW}6(gc|l1|6;vpzrECX>+H!|ZEH0hAG*#_FC@9o} zoUQWDV-q9AEZqYOLaH$rmY|{T7^DQVtJSKD0T^M!LM&6cW%Q}}svZ?uJ1st?1BUHHi$ zoK~!ZEQ>kN^+?zKsfy{H+BeDko%~0<1#5Jv*8>ELnGf4vj!>WK2~+yC*kIx}Hz9~5 z=cOnk>1~J}H`*f5JLX)HLSt;{9yhZhaExbQNs&<7`{?r)Nq+XWV9wTGpKg%qVsf@H zej=J1jWGE3g5pjT;uD)frnp+U($s}H+|7D09D^R54#X2qW8OM zIzUfI&%;+M*CbRq>ACVSIxfe743bl^f*U>6v&;XD_TZ`Vhg5`0SE$UB9{Qo z-=xkB6O$miGJM6zfUcSJL%3L_airq6!$B4qzzYuvR!J*Gev|R1Nlqi>8PAW?-$Z}; zV*Cd3n^Qt412xGN)m4A-hJ)CrVdcTdrje=#PR5(qlZ?EP@!gIA>ri` zJoIr_P-nucfvYSMtkCp`x~=l%z34AFx(twh^2x6Ai^jVPnA!{w-+LdzLjg4fciO%f zx5BCcH*^3r;-bGunL9vz@67@pt7jzM>AF&O0(h(hSPCc$)?0w;kxzz;Nr346?sWZD z77u(6rQii&y!APV3@mg}?Go{FvY%z8jcg@Mv5z1_9~K@429ulsQ#2w3bpOJ42TaS< zI1R#La->_gT-}PpjnSu-3o`{VW;G7RVdLmA$wjS#=#3gJ;?SV;)N#c2`uJUupFzwd zjkR$Yd-_7`;V7!8VR~TpNXBCBq1Jv?Y*{^5_8`MzcID%qyzP^YHCV;*!} zp7qG9X;~_Ms&3X{JYIj;a$YQq-}n-Ni7|hhGPK185!o1xz?PgRqYRt35qR7fg1}WyO$D(e~f+JLGZ{HZqgV~AkJzHoZB z%J0K4TPbU#M6v7ecddE4rPJOn^~IR zjFlZZ8a;AwM4W^ieb$|UU{h4+?mcjX4xl%^ZxGYyDg>JV9dh(>a0;iqwQu*q5#+q@5Hni}Kq zJWU*l6snCJb4e-ph4Bmr|HPreXeskdct7A@xr91>LG+N6d3OZWZ|z9R%$3%as|wQo z$@rIkrlyt@9KU=2g?J_Gdb0vA{EAG0Z7#OnJ4nt{V`<_p_ow2&@g@+}Bw9(nY?jc? zC{`R{u^{5crU-~=9ts|z{kxtkICD&3@CN{5p@7Zla5|^N{>TNcKll=f%&pe<`hulx z-&{B!?5kqCfw520xK4DR9XZ#2=mYV!H^!|*m4LY0n?8{d%+GVh-MFhqcSd}7-Z~C7 zX72aQ#+D1o%|F9>m(>o7BN@+-n=zH8LZ9!Go$OmHfDoyX4Z=^s9Wc$S)K;|Q+G zZg4;`2wmsSRoO+nYkUpNZvCsWTP&d57&>&qAj_s}nD6_%g#04pM+y2+tGX)1+Kl$* ztby#Bo?w0yUNJ3qbNdKtq8`*DlB1ZhvUz9(eO!+pxAkxYg>8Y+arO&O&zT9_LB^Whm4s=z586uneu1YYX+`c-QYQw~`rePza zvcgjHamy;=mhl{WlI7yKb&6jTf^>P- zPC=AM^C$u)PsdtLZr^XGcOiUx~0Yi$9j&cc7tH7;!NMe=V<4SP%G&%QRp8L_-g20I-DX{k&W2+`bgF_Ot0c&4Kdsz_SC6~%JpM~XojHC#>E)* zUtFP2y~j7_!fUj!lm6!<+f~aj47y5Mf7nr$^~EY|EFstA8SDW$F);mpqY7luYt8b& zuYV@gzuJH0m8yIG^M5hJ#hw15eqfgs$iL-c*uSy9D(-XuxXM*jcN563QZDQ70XB4i z{vI|!+T?zKI9^N)!Pbr%kXrcvoas%VE7(`aZt15Bw>OWFVa)3Z9Gl3?M^kA%EnqFx zr#t(hu5Ivj+0ra-Jq~p$K3}&~A0ijYO5jT0K{dr>~Fg!0w+ezU2$ky z>IDEFcUL5@&TlTOu3OZPuEcq{$X8^2iM&bxs%x_B9KPZuWF#Ggs)Vw-rtE%(C6w+^>j%s?J)lcU}6z2U(Cu~ zRvoK0Mya7AYY|XTf>+t*L>9a;`*cEZed?vnJ8j%ghd`tMNDH$YS4wKj z)mscYyhf-o8h=CaXJ)`J$^|g9C5o(M6=kRL)29uE%Q+?8pf1nVEo~Wpd({XX=ruEm z%!@CbmrFu}8sf!J`Pp)Q_ls2q>@`uzbsDRBT28gM(P+V6yOC6>YspyXKl}_BBeGC) zn}Q0f23M-WAacMS+q;3YMiagowiH$!R94lKr++LD{>T|EHSa95)_3~N0P#^XiUNTh zJE$qGPhH5(R9g&!0C{oiK;lsSbr@HN`P&ArdE#2^EpbYu(X* zwNf~#I)oGar2>wvmH$~02k_N_qVs1BTK_^&^kDsqGY2s@cB1mgp`tR-C(Cn1@{jtp z_64h~?lD_|sp*fa679HPteR~5XAR`r)s+2Hfoaiz%vD)TI6TJ(NRb{jNRlbOAIxy9kB{BIw0x!4&R;bDWWo>4)8|Nr{4?z2U zMq~9)8kh%oigoq*3yqr>*i-)RG%yGPzfOaIV8BDKzw<#LAYeQ8Ycv>O1riN`qyqwn zA=v`q0RvCvuF2ztA@zlV!N@cy9QgLa-}^%0;OjcTfN$4r2E#$T*KG#Fp}g1S0fvccC2wrX^K3+J_b-fTM1W6tQ3gv-b(*c3PVb^_w zKzR_?V*>)^MT!>)3<5#Y3xh+i;{t(kb0gDuk!b+VNPGa!*Rg`Yxsl=p0_R0sk3R@E z`07FI-^Lum4TD_A55mm@N2VbVNHpNPB}iEDfRW-J!UN+*!jA_q@VYHLJV>zy;Xxqd zf&e4qf`CG<$1VhjHKg%C@F0&lf)|A38!rfHUP5@m+(`KG!g!Hs+z=#xd3oSS{_-M_ z;vNbD^IYeHf}luw0}A2>UDp8x@j#LKfV|YxeA5yBH;{$L69+kLAjAIfbl?)e1n6K z#t2A>$YTUY&Jlola3uVI5J%F%4TT~3!ToQr;{G>S0dwnmj6-?;4OT#?A?e`Zg(6{q zfFRWXC<2N=(t-FlSOHT8Db|3Lh@^w}s>=R7=RkQ8$nnC>+s#7akA~&-EO}0|SWH`C!On1f(XUwGR&uFOqKvF!DMF0W5Q_#|w~# zkk)#v{d(h7f4d1iETTHignf783}h4NQkp`ssU=l)$TVL6==V;+E|5NcPXS zEFIVV`8khgzHj7CSNDVG?E6N_>VM9X!fhVU&$X@dIL+fmjr-@Ry35N;-NTlbT)tEP z<|*QuwJo_7&(D)fuC{CXU(&m!ZCSEHa=HdnR^vHkN1aQKI$wi{yRKzctkzOCP3dCXMA`YK}YB`F;>W-SczZvfjsh{VuuVtP{7E zEo-&s|9Lzk&Hw3kWdu&=l1HRx{tx!Eo~hFtSM~3)RF1RcX=vttNSi0X>9|Lp@8|P7 z?q_}$Sk9MY9^>hvt90aYo0+$q$2GHJ$!+bJ^`4zC9S0p5ryU0!d4B0Q=t!Q@anO-xnu^Q4 zxXYj;_mYl-j?8&<9CYM4yW^lEIcLS?*|E!@BiBdAK}VjwI}SR+_>O~)+@CuRIx-(o zaVK<~&~ZXXo@u&0=tzFlanO+*r{ka_&$bnp`OYqbjyzj*9CYM9*>TX3duPW%M>tb) znak)h=m;A-4m$FD+Hug4dvV7>N9N`_4mvWYRdIQC>@w(hpd-)sT^Bm?Y};|rk@>og zYaLsjpXa#DYN4!kWTp#otz&y>9hnhB-PWqR_7dccNuhK4!+}{Bl%m$K}Vjs zIu1I(^E?%HUGRL&Wz;e9ywPRQk@@0|gN{7^bsThn=Ubkst1ft+*+axZN1i9UJ?O|> zRmVX`<_ruD+W6vB)AER}Q+`i*lMwH0-K$j^81d`Etsw=vxwcs}NV>$u?gj{H1ta!?mK!1EpX zdEWS-J?H?>^Jb{)g6Em&M;vs3=R0_w2}QIA9pL#6o@aKm+XK&c@H}s>P#66Ho{x18 zH4c!U@8J0kp6BgYw+Ei*A=1Nr$5p6|%d#~fp6Lw>%4=R0`5gXcSVz9T>1!SlT6 z?DGT9cjV_|JyK}{&+~>5ak%e-=R0^l-hcFQp#wbMk)P)+WVLsI=LdLxfaeGD^8-B3 zLJAzWbz~DL;(AR1~((N`B-DvanJ#tpWyiko@WJC9|Jr;!SfS5Kf&{{4!LxI=O=i6g6CO;*6o4k`Hm#w zFs{J!6Z!cGo}b|PiTwNo&rk3?3*q~m!Sk^%q5f0wJPRHXhyDQ1v#_u0g6AiAeuC#G zcs|w~mk#j!1kba|26b_LfahcHLmd}9&muX*VO)XdW1VFm1N{M>pWyiko}b`(RvPtj z!SfS5&%#aAg%0riM1GzHkE`~uG}vHG+VC-S*vL092{D8Q=KE|5GZVx)}{(P)|uDam)1)h(!ja?Ty!1J+&x$8m)cz%KB zV;@zu2cBQxc@`6+E_7hN{Q}RkXdCUJKfv<~JkRROZVx=a!1J*-zWx{RJS*1`2OY@I zFYx>V&oA)&0?#k-`~uI%p1slro?qa37K@`Uu5m`7dc0M9S*{07f&@O@b%W`vg!SfqDAA3xyJ@EVn&$H11b^H1ldoueN&;g#`$j`Hp6zxF=cz%QD z*^txif#)}Po{iwBi|YeCk2wMALI-$$gXd%aNBz&>c{Tze4m!Z|8$7=;-+qJVV}EF! zA9#L)=QntMgXcGReuL*@Z*HFp`U5<_!SfhMbbH`=b{ZlM{Q;ie;Q83cS2~cN#~Qce z!1EhCzrphxJRd#|buQrf4W8fN`HlJZ;cHOG0MBpm{07gnNet~l2Y4QX5Y$C~faf=O z{($EXc>aLrF_oxu8Qv4~fAxUp4|ty4uc+JC#{-@};Q0fdKj3*blJ;@I^9S?oG0{L> z=m5_j@caSKAMkwar>p-NJb%FRu?N5FLI-$0_HK6FzCIrC`~lA&@chAi``A}m$3=er zfaec*{($GhQ=*Omo@e(p;?N)9`2(Im;Q533_TdZB=ZF44e*S>x4|x87=P|wLbK`2(I0zol*u*9Un1falre-tEoo&Ti-SigLw|IBo;dVJ z^E`3rkIv8I6oYZqJWm{SG|v+U9nJH^K}YjEanR8`UtXH$DMNoW&l87n)%khiFs_>C ziNm;Ro+l3Ds(Bvg9_VPECk{HA=ZS-k=6T|vqj{dV))6??wGjBAtjASuiiqoRl?xMb zJ+3l{5ZB`>LOKpSkIfO{paVRQ^%3eq2Y4QLCDeru@I1CjT^Br$u@K_WAK>`{p2uDZ z?Lh~49?PY!3!cY@331Q?o-g2eOtH`&bb#jzc)oz=3wSWAC&lm7KE?a01 zI>7SiqTPz#{ zI>7SkI@+FLI-%hfak+Yw9f_o0iI`?r|QOa4`n^BV)lo)))8ZN z#C3nH$j?{sd_{gfeBS5(U#N{s7P8+}P)g{5*!&h{L!7&sXHKZTL`3jz|$j|e^1hfYo;Q5ODeE4_v`Jq3+ z^EmH!UGRJb&*RXKy3m3AJWl?o3mxG33ZBPUx7!2HSMWSv0YF{o0MA$OJZAD}5B&k2 zui*I#p0CKy^HqU9E_l8oKVQM~6+BN{%{NY;ZoN3EBM1Du64j4E^)0RUrfOH^|*pRTvETjU**dR-5z+pf#<{DyZ$Ti zd;`xnfamasTQ=}~1JCoJl|C+bKKy}8FL=HoKZieDtgX=>;Q8>Tuj7K} zc z4*tbumkV&59H_hC_^6?JcmDAK4kzM;Q5C9JRb|daWSrtpO5dI z)PD+|!yj&eKU~V713Vwz?|oe80MGN0CDeru@ErbdnGVCa0?+dShCVKMzJcc(c)o$> z8+bmxmsS52c%Bb{AP)Tjp2Ht5Q-bIZ%(usE9LMecfInQO7@LFehf7@RfInQ~dR)OD zE^&Q*z#lGg&;g#qA1+g+-5>CWOI(jD_`@Zx`y(H#sQ*4+szDiafag2%bNIt$$`}0s zp6}p!J^<3^2cG8(If#P}@ErbdOTMJj?Sbd;hf7_IEASltaH$I&;CVh$(C3HzdHi8mhd*47f&Ku`^M$59 z26zsCxV#pE4)7fQaH)&_0MB>u9R6@=4?4he_`~H@7IYv#hdiu@e@aH)&_0MFqMm#-q>`aphufal};l6B7DIsD;rT#PI59R6^5 z-HmYtp2Ht5b#a}8=kSM1U0fgFIsD=BIv(Q+`8oXI##gHPe@1=|f4J1e^?~_z_`{_x z#uf5&_`~H>Rv1^{IsD;L7vl=^?fFty|1aP<{Na`ZJcmDA;-CXOKaiipA1>{oKfrVN z!=*0z13ZU6T$Xe|2Y3#DxYR{|famasOI=*&m~V$aT$Y^lb$){9@P|v?zRvT-wi+++ zhfCcaS0{K5f4F=BuCH_W!;O#m^|{QscoqSSxv#AK&(qFt-+ND8=sVr5 z`dpBo!yhioeV_w8hd*5ELI-#bf4J0z4!l2yKU|h0VO$|Uhd*5EVq76Vhd*5EVq76V zhd*4FOJQ6gKZieD>NY>%50^Of2Y7yBzI}XKc77IsKU|i0K?isaf4J0z4)8qZ(dUBs zcKE~P7|?^jC3^79isKf!bO!{r#wk9-lg^&&rqKU}^#iT=R*^AkLWKU~_w z^#Pur;5q!^(jNK)`FTE4+W!IGpP%44{Nb{k5IT^b!yhhn(I1#^Kf!bO!(|yG#ua#8 zUU1>ZnC~opfamas%Wm2@YiEADCiYm?- zI>2-I!(|C+b1)x4?qkf1&nCWn_!V(|onOe$FYp}xaLWar!yhhj&;g!b$j{*qm-e6o z`8oXIQWyP!{2cypS*i;i$j>kE9R6@=4?4i}3p|HE-1s_g_c?eDf4J1exI%sof4J0z z4)7fQaH)&_0MFqMx4<7RWzYehU*I|X;nE(ibMX8E&*2Z3Z#myklp_acn*KK1^#d;Lw_JYzwrJX{%~mz;|e^#z;pP+WxGM&uiy`txYhxG zxWsjTz#lGgt>XsI;SZO(eSO^EIsD-k_`{{F?^p1LOI-6D{&0!w>jVC9iGvRC9R6_G z{(=4g&*2Z3y3hfh!yhhn`}%-C+;W5G@P|trbb#mZhf7`P0MFqMm%7k_`Su$;hd|o{QL&b;SaaKA1-C+5Agg3&u{P?{%|=4?pNSB{Nb`44EzAk z;SZO(=nwGx2G8LSm-e6oJcmDAwy;45@^kpZr7m=U=Qr|m_`{_==m5{*54XS{E@l0E z1%J52wGQ~hC9bb?_`@Zxbv($=;SZOsg54kRhf7>vAMl4uTtAn6DgZv!+ zaM|7n9e95Zf4I~|e;_}HKV0fU2l8|H!=-NX1O9Le{NYlD{s7P650|SA1h=MVDp2Rw&AT(*4UItS0;50|>=59H_Y zhZ~N9{h#)Hd$yk!KT=lxk+RZ}vf@X|sy|XzI#O2rNLlqq%1TGdYFw=^&GVE&NAo;! z(9t|k9CS3#69*m5^Ta_%^E_Msp`-Kj#6d^%JaN#`JWm{Sbbg*V=;-`>ed+h-DZ{vG zo+l3Ds(GF`jH{k+PaMWo=jVA7hU=r}+Y<*J&GYeQZN4s3hW==tCl3A5JWm|@qx16> z{%|RSj?T{$2OZ7x#GyZ$=ZQmqG|yvOf&OToCk{F~KTjNVG|v+U9i5*i4mz6WvD$!+ zet(`g=xClN4mz6WiGz;jdE%g>c^-QZ=xClN4mvtNPaJeK&l3k7&GW=TNAod7e1vXr3nyIyyg39CU!^@P~^vOYHdH}T;iYuJcmDA>h^ti0ngzNm%7jap2Ht57C7h+@O%N!7w{bZa5)D013X{AbNIu> zq6hr}o@XO{k1OyT{%|=4`U5=AhFu&3{Q;h5b7a>A&*2XjOQL?hf$o$odgr=%BNv9gj>{XgSz9*sj{Ul?;!<{Hj8A2GPMrR$ zZ2n!tp4V|(migXuS*D;Sy?b_JhXKFU{q*tsAAkDr@$H+>KY#z-kN^JgE)HDV`)_~# z`O~|c$-8gg|KaaH{G7s689#pim(Q24@&e$iud~wgy1!ia>;C$i^LM!?oZFX~>A1i7 z;(tf{_1za=ynXYJPv3w1^skQ}et7#P+ezcd^7hSVpMCwuZ{EK7wJ@sRzWG0$-}^s( j{P53rzn<3nKY#n-hwnc9?cJ~c-F)i#49>d!_tgIbP~;+w literal 0 HcmV?d00001 diff --git a/samples/demo_filex_nand_flash.c b/samples/demo_filex_nand_flash.c new file mode 100644 index 0000000..da9f801 --- /dev/null +++ b/samples/demo_filex_nand_flash.c @@ -0,0 +1,236 @@ +/* This is a small demo of the high-performance FileX FAT file system with LevelX + and the NAND simulated driver. */ + +#include +#include "fx_api.h" +#include "lx_api.h" + + +#define DEMO_STACK_SIZE 4096 + + +/* Buffer for FileX FX_MEDIA sector cache. This must be large enough for at least one + sector, which are typically 512 bytes in size. */ + +unsigned char media_memory[4096]; + + +/* Define NAND simulated device driver entry. */ + +VOID _fx_nand_flash_simulator_driver(FX_MEDIA *media_ptr); + + +/* Define LevelX NAND simulated flash erase. */ + +UINT _lx_nand_flash_simulator_erase_all(VOID); + + +/* Define thread prototypes. */ + +void thread_0_entry(ULONG thread_input); +UCHAR thread_0_stack[DEMO_STACK_SIZE]; + + +/* Define FileX global data structures. */ + +FX_MEDIA nand_disk; +FX_FILE my_file; +FX_FILE my_file1; + + +/* Define ThreadX global data structures. */ + +TX_THREAD thread_0; +ULONG thread_0_counter; + + +int main(void) +{ + + /* Enter the ThreadX kernel. */ + tx_kernel_enter(); +} + + +/* Define what the initial system looks like. */ + +void tx_application_define(void *first_unused_memory) +{ + + + /* Put system definition stuff in here, e.g. thread creates and other assorted + create information. */ + + /* Create the main thread. */ + tx_thread_create(&thread_0, "thread 0", thread_0_entry, 0, + thread_0_stack, DEMO_STACK_SIZE, + 1, 1, TX_NO_TIME_SLICE, TX_AUTO_START); + + /* Initialize NAND flash. */ + lx_nand_flash_initialize(); + + /* Initialize FileX. */ + fx_system_initialize(); +} + + + +void thread_0_entry(ULONG thread_input) +{ + +UINT status; +ULONG actual; +CHAR local_buffer[30]; + + LX_PARAMETER_NOT_USED(thread_input); + + /* Erase the simulated NAND flash. */ + _lx_nand_flash_simulator_erase_all(); + + /* Format the NAND disk - the memory for the NAND flash disk is setup in + the NAND simulator. Note that for best performance, the format of the + NAND flash should be less than one full NAND flash block of sectors. */ + fx_media_format(&nand_disk, + _fx_nand_flash_simulator_driver, // Driver entry + FX_NULL, // Unused + media_memory, // Media buffer pointer + sizeof(media_memory), // Media buffer size + "MY_NAND_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 120, // Total sectors + 2048, // Sector size + 1, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + + /* Loop to repeat the demo over and over! */ + do + { + + /* Open the NAND disk. */ + status = fx_media_open(&nand_disk, "NAND DISK", _fx_nand_flash_simulator_driver, FX_NULL, media_memory, sizeof(media_memory)); + + /* Check the media open status. */ + if (status != FX_SUCCESS) + { + + /* Error, break the loop! */ + break; + } + + /* Create a file called TEST.TXT in the root directory. */ + status = fx_file_create(&nand_disk, "TEST.TXT"); + + /* Check the create status. */ + if (status != FX_SUCCESS) + { + + /* Check for an already created status. This is expected on the + second pass of this loop! */ + if (status != FX_ALREADY_CREATED) + { + + /* Create error, break the loop. */ + break; + } + } + + /* Open the test file. */ + status = fx_file_open(&nand_disk, &my_file, "TEST.TXT", FX_OPEN_FOR_WRITE); + + /* Check the file open status. */ + if (status != FX_SUCCESS) + { + + /* Error opening file, break the loop. */ + break; + } + + /* Seek to the beginning of the test file. */ + status = fx_file_seek(&my_file, 0); + + /* Check the file seek status. */ + if (status != FX_SUCCESS) + { + + /* Error performing file seek, break the loop. */ + break; + } + + /* Write a string to the test file. */ + status = fx_file_write(&my_file, " ABCDEFGHIJKLMNOPQRSTUVWXYZ\n", 28); + + /* Check the file write status. */ + if (status != FX_SUCCESS) + { + + /* Error writing to a file, break the loop. */ + break; + } + + /* Seek to the beginning of the test file. */ + status = fx_file_seek(&my_file, 0); + + /* Check the file seek status. */ + if (status != FX_SUCCESS) + { + + /* Error performing file seek, break the loop. */ + break; + } + + /* Read the first 28 bytes of the test file. */ + status = fx_file_read(&my_file, local_buffer, 28, &actual); + + /* Check the file read status. */ + if ((status != FX_SUCCESS) || (actual != 28)) + { + + /* Error reading file, break the loop. */ + break; + } + + /* Close the test file. */ + status = fx_file_close(&my_file); + + /* Check the file close status. */ + if (status != FX_SUCCESS) + { + + /* Error closing the file, break the loop. */ + break; + } + + /* Delete the file. */ + status = fx_file_delete(&nand_disk, "TEST.TXT"); + + /* Check the file delete status. */ + if (status != FX_SUCCESS) + { + + /* Error deleting the file, break the loop. */ + break; + } + + /* Close the media. */ + status = fx_media_close(&nand_disk); + + /* Check the media close status. */ + if (status != FX_SUCCESS) + { + + /* Error closing the media, break the loop. */ + break; + } + + /* Increment the thread counter, which represents the number + of successful passes through this loop. */ + thread_0_counter++; + } while (1); + + /* If we get here the FileX test failed! */ + return; +} + diff --git a/samples/demo_filex_nor_flash.c b/samples/demo_filex_nor_flash.c new file mode 100644 index 0000000..2125bbd --- /dev/null +++ b/samples/demo_filex_nor_flash.c @@ -0,0 +1,235 @@ +/* This is a small demo of the high-performance FileX FAT file system with LevelX + and the NOR simulated driver. */ + +#include "fx_api.h" +#include "lx_api.h" + + +#define DEMO_STACK_SIZE 2048 + + +/* Buffer for FileX FX_MEDIA sector cache. This must be large enough for at least one + sector, which are typically 512 bytes in size. */ + +unsigned char media_memory[512]; + + +/* Define NOR simulated device driver entry. */ + +VOID _fx_nor_flash_simulator_driver(FX_MEDIA *media_ptr); + + +/* Define LevelX NOR simulated flash erase. */ + +UINT _lx_nor_flash_simulator_erase_all(VOID); + + +/* Define thread prototypes. */ + +void thread_0_entry(ULONG thread_input); +UCHAR thread_0_stack[DEMO_STACK_SIZE]; + + +/* Define FileX global data structures. */ + +FX_MEDIA nor_disk; +FX_FILE my_file; + + + +/* Define ThreadX global data structures. */ + +TX_THREAD thread_0; +ULONG thread_0_counter; + + +int main(void) +{ + + /* Enter the ThreadX kernel. */ + tx_kernel_enter(); +} + + +/* Define what the initial system looks like. */ + +void tx_application_define(void *first_unused_memory) +{ + + /* Put system definition stuff in here, e.g. thread creates and other assorted + create information. */ + + /* Create the main thread. */ + tx_thread_create(&thread_0, "thread 0", thread_0_entry, 0, + thread_0_stack, DEMO_STACK_SIZE, + 1, 1, TX_NO_TIME_SLICE, TX_AUTO_START); + + /* Initialize NOR flash. */ + lx_nor_flash_initialize(); + + /* Initialize FileX. */ + fx_system_initialize(); +} + + + +void thread_0_entry(ULONG thread_input) +{ + +UINT status; +ULONG actual; +CHAR local_buffer[30]; + + LX_PARAMETER_NOT_USED(thread_input); + + /* Erase the simulated NOR flash. */ + _lx_nor_flash_simulator_erase_all(); + + /* Format the NOR disk - the memory for the NOR flash disk is setup in + the NOR simulator. Note that for best performance, the format of the + NOR flash should be less than one full NOR flash block of sectors. */ + fx_media_format(&nor_disk, + _fx_nor_flash_simulator_driver, // Driver entry + FX_NULL, // Unused + media_memory, // Media buffer pointer + sizeof(media_memory), // Media buffer size + "MY_NOR_DISK", // Volume Name + 1, // Number of FATs + 32, // Directory Entries + 0, // Hidden sectors + 120, // Total sectors + 512, // Sector size + 1, // Sectors per cluster + 1, // Heads + 1); // Sectors per track + + /* Loop to repeat the demo over and over! */ + do + { + + /* Open the NOR disk. */ + status = fx_media_open(&nor_disk, "NOR DISK", _fx_nor_flash_simulator_driver, FX_NULL, media_memory, sizeof(media_memory)); + + /* Check the media open status. */ + if (status != FX_SUCCESS) + { + + /* Error, break the loop! */ + break; + } + + /* Create a file called TEST.TXT in the root directory. */ + status = fx_file_create(&nor_disk, "TEST.TXT"); + + /* Check the create status. */ + if (status != FX_SUCCESS) + { + + /* Check for an already created status. This is expected on the + second pass of this loop! */ + if (status != FX_ALREADY_CREATED) + { + + /* Create error, break the loop. */ + break; + } + } + + /* Open the test file. */ + status = fx_file_open(&nor_disk, &my_file, "TEST.TXT", FX_OPEN_FOR_WRITE); + + /* Check the file open status. */ + if (status != FX_SUCCESS) + { + + /* Error opening file, break the loop. */ + break; + } + + /* Seek to the beginning of the test file. */ + status = fx_file_seek(&my_file, 0); + + /* Check the file seek status. */ + if (status != FX_SUCCESS) + { + + /* Error performing file seek, break the loop. */ + break; + } + + /* Write a string to the test file. */ + status = fx_file_write(&my_file, " ABCDEFGHIJKLMNOPQRSTUVWXYZ\n", 28); + + /* Check the file write status. */ + if (status != FX_SUCCESS) + { + + /* Error writing to a file, break the loop. */ + break; + } + + /* Seek to the beginning of the test file. */ + status = fx_file_seek(&my_file, 0); + + /* Check the file seek status. */ + if (status != FX_SUCCESS) + { + + /* Error performing file seek, break the loop. */ + break; + } + + /* Read the first 28 bytes of the test file. */ + status = fx_file_read(&my_file, local_buffer, 28, &actual); + + /* Check the file read status. */ + if ((status != FX_SUCCESS) || (actual != 28)) + { + + /* Error reading file, break the loop. */ + break; + } + + /* Close the test file. */ + status = fx_file_close(&my_file); + + /* Check the file close status. */ + if (status != FX_SUCCESS) + { + + /* Error closing the file, break the loop. */ + break; + } + + /* Delete the file. */ + status = fx_file_delete(&nor_disk, "TEST.TXT"); + + /* Check the file delete status. */ + if (status != FX_SUCCESS) + { + + /* Error deleting the file, break the loop. */ + break; + } + + /* Close the media. */ + status = fx_media_close(&nor_disk); + + /* Check the media close status. */ + if (status != FX_SUCCESS) + { + + /* Error closing the media, break the loop. */ + break; + } + + /* Increment the thread counter, which represents the number + of successful passes through this loop. */ + thread_0_counter++; + + } while (1); + + /* If we get here the FileX test failed! */ + return; +} +