├── .gitignore ├── .npmignore ├── LICENSE ├── README.md ├── include ├── qspi-flash-c-api.h └── qspi-flash.h ├── package.json ├── scripts └── xpacks-helper.sh ├── src ├── qspi-descr.cpp ├── qspi-descr.h ├── qspi-flash-c-wrapper.cpp ├── qspi-flash.cpp ├── qspi-micron.cpp ├── qspi-micron.h ├── qspi-winbond.cpp └── qspi-winbond.h └── test ├── test-chan-fatfs.cpp ├── test-chan-fatfs.h ├── test-qspi-c-api.c ├── test-qspi-c-api.h ├── test-qspi-config.h ├── test-qspi.cpp └── test-qspi.h /.gitignore: -------------------------------------------------------------------------------- 1 | *.tgz 2 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | /test/ 2 | /scripts/ 3 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2016-2020 Lix N. Paulian (lix@paulian.net) 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ![GitHub package.json version](https://img.shields.io/github/package-json/v/lixpaulian/stm32f7-qspi) 2 | ![GitHub Tag](https://img.shields.io/github/v/tag/lixpaulian/stm32f7-qspi) 3 | ![GitHub License](https://img.shields.io/github/license/lixpaulian/stm32f7-qspi) 4 | 5 | # stm32f7-qspi 6 | This is a QSPI serial flash driver for the STM32F7xx family of controllers. 7 | 8 | ## Package 9 | The class is provided as an **xPack** (for more details on xPacks see https://xpack.github.io). It can be installed in a project using either `xpm` or the attached script. Of course, it can be installed without using the xPacks tools, either by linking the class as a Git submodule or by copying it in your project, but then updating it later might be more difficult. 10 | 11 | Note that the xPacks project evolved with the time. Initially it was based on shell scripts, but the current version is based on a set of utilities, `xpm` and a JSON description file. You will still find the `xpacks-helper.sh` script in the `scripts` subdirectory, but it is not recommened as it is deprecated and will not be supported in the future. Instead use the procedure described below. 12 | 13 | To install the package using `xpm` you must make sure that you have already `nodejs` and `xpm` installed on your computer (see also [xPack install](https://xpack.github.io/install/)). Then, in your project directory issue the commands: 14 | 15 | ```sh 16 | cd my-project 17 | xpm init # Add a package.json if not already present 18 | xpm install github:lixpaulian/stm32f7-qspi#v2.2.4 --copy 19 | ``` 20 | 21 | Note: Without `--copy`, the default is to create a link to a read-only instance of the package in the `xpm` central store. 22 | 23 | ## Dependencies 24 | The driver depends on the following software packages: 25 | * STM32F7 CMSIS (https://github.com/xpacks/stm32f7-cmsis) 26 | * STM32F7xx HAL Library (https://github.com/xpacks/stm32f7-hal) 27 | * µOS++ (https://github.com/micro-os-plus/micro-os-plus-iii), version 6.3.14 and up. 28 | 29 | Alternatively, the CMSIS and HAL Library xPacks can be provided by ST's CubeMX code generator (recommended, as the STM32xxx xPacks will probably be discontinued). 30 | 31 | The hardware initialisations (µController clock, peripherals clocks, etc.) must be separately performed, normaly in, or called from the `initialize_hardware.c` file of a GNU MCU Eclipse project. You can do this using the CubeMX code generator from ST. You may find helpful to check the following projects as references: 32 | * https://github.com/micro-os-plus/eclipse-demo-projects/tree/master/f746gdiscovery-blinky-micro-os-plus 33 | * https://github.com/micro-os-plus/eclipse-demo-projects/tree/master/f746gdiscovery-blinky-micro-os-plus/cube-mx which details how to integrate the CubeMX generated code into a µOS++ based project. 34 | 35 | Since version 2.0 of the driver, the API has been changed for a better integration with the POSIX layer of µOS++. It is an implementation of an µOS++ block device. Although the driver is now more tightly coupled to the µOS++ ecosystem, it can be however ported to other RTOSes. It has been tested on the Winbond W25Q128FV and Micrel/ST MT25QL128ABA flash chips, but support for other devices will be added in the future. 36 | 37 | An optional plain C API is also provided. Note that in the case of the C interface the qspi object is generated dynamically. However, this API may be discontinued in the future, as a better approach is to use the native C Posix interface offered through µOS++. 38 | 39 | ## Short theory of operation 40 | Most QSPI flash devices operate in two basic modes: 41 | * Extended SPI mode: instruction, address and data can be sent/received to/from the chip both in single and quad (or dual) mode (e.g. instruction and address in single line mode and data in quad mode). 42 | * Quad (or QPI) mode: the communication to/from the chip is done exclusively in quad mode. 43 | 44 | A device cannot operate in both modes at the same time. There are provisions to switch the chip from one mode to the other, however there are differences on how the switch is done from chip to chip. There is also the danger that the flash chip and the controller get out of sync, e.g. if the controller is reset but the flash chip is not. 45 | 46 | The philosophy behind the driver is that there is only one command executed in standard mode: read ID. This is done right after the system comes up and is initialized. If the chip is identified and known for the driver, it is immediately switched to quad mode. From now on, all commands are implemented in quad mode. If for any unforeseen reasons there is a need to switch back to standard mode, you can use the reset function call. For an example on how to use the driver, check out the "test" directory. 47 | 48 | ## Tests 49 | There is a test that must be run on a real target. Note that the test is distructive, the whole content of the flash will be lost! Test files are provided for both C++ and C APIs. To select what API to use, you have to set the proper value for the TEST_CPLUSPLUS_API symbol in the test-qspi-config.h file. 50 | 51 | The test performs the following flash operations: 52 | * Switches the power-on 53 | * Reads-out the chip ID and initializes the internal driver structures (manufacturer, flash type, sector count and size) 54 | * Switches the flash to memory-mapped mode; the flash is mapped at the address 0x90000000 55 | * Checks if the flash is erased (all FFs); if it is not, the flash will be erased 56 | * Generates a stream of random bytes and writes them to the flash, one sector (4 KBytes at a time). 57 | * Compares the values written to the original values in RAM. 58 | * Switches the flash in deep sleep mode, then switches the power off. 59 | 60 | The C++ version includes timings for most of the operations, whereas the C version does not. 61 | 62 | In addition, a test is provided to assess compatibility with the ChaN FAT file system, offered through µOS++; for running this test, you need to install the ChaN FAT file system xPack at https://github.com/xpacks/chan-fatfs.git. This xPack contains among other things, a C++ diskio wrapper. 63 | 64 | 65 | 66 | -------------------------------------------------------------------------------- /include/qspi-flash-c-api.h: -------------------------------------------------------------------------------- 1 | /* 2 | * qspi-flash-c-api.h 3 | * 4 | * Copyright (c) 2017, 2018, 2020 Lix N. Paulian (lix@paulian.net) 5 | * 6 | * Permission is hereby granted, free of charge, to any person 7 | * obtaining a copy of this software and associated documentation 8 | * files (the "Software"), to deal in the Software without 9 | * restriction, including without limitation the rights to use, 10 | * copy, modify, merge, publish, distribute, sublicense, and/or 11 | * sell copies of the Software, and to permit persons to whom 12 | * the Software is furnished to do so, subject to the following 13 | * conditions: 14 | * 15 | * The above copyright notice and this permission notice shall be 16 | * included in all copies or substantial portions of the Software. 17 | * 18 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 19 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 20 | * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 21 | * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 22 | * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 23 | * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 24 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 25 | * OTHER DEALINGS IN THE SOFTWARE. 26 | * 27 | * Created on: 5 Feb 2017 (LNP) 28 | */ 29 | 30 | #ifndef INCLUDE_QSPI_FLASH_C_API_H_ 31 | #define INCLUDE_QSPI_FLASH_C_API_H_ 32 | 33 | #include 34 | #include 35 | #include 36 | 37 | #include "qspi-flash.h" 38 | 39 | #ifdef __cplusplus 40 | extern "C" 41 | { 42 | #endif 43 | 44 | typedef enum 45 | { 46 | qspi_ok = 0, 47 | qspi_error = HAL_ERROR, 48 | qspi_busy = HAL_BUSY, 49 | qspi_timeout = HAL_TIMEOUT, 50 | qspi_type_not_found, 51 | } qspi_result_t; 52 | 53 | typedef struct 54 | { 55 | ; 56 | } qspi_t; 57 | 58 | qspi_t* 59 | qspi_new (QSPI_HandleTypeDef* hqspi); 60 | 61 | void 62 | qspi_delete (qspi_t* qspi_instance); 63 | 64 | void 65 | qspi_get_version (qspi_t* qspi_instance, uint8_t* version_major, 66 | uint8_t* version_minor, uint8_t* version_patch); 67 | 68 | void 69 | qspi_power (qspi_t* qspi_instance, bool state); 70 | 71 | qspi_result_t 72 | qspi_sleep (qspi_t* qspi_instance, bool state); 73 | 74 | qspi_result_t 75 | qspi_initialize (qspi_t* qspi_instance); 76 | 77 | qspi_result_t 78 | qspi_uninitialize (qspi_t* qspi_instance); 79 | 80 | qspi_result_t 81 | qspi_enter_mem_mapped (qspi_t* qspi_instance); 82 | 83 | qspi_result_t 84 | qspi_exit_mem_mapped (qspi_t* qspi_instance); 85 | 86 | qspi_result_t 87 | qspi_read (qspi_t* qspi_instance, uint32_t address, uint8_t* buff, 88 | size_t count); 89 | 90 | qspi_result_t 91 | qspi_write (qspi_t* qspi_instance, uint32_t address, uint8_t* buff, 92 | size_t count); 93 | 94 | qspi_result_t 95 | qspi_read_sector (qspi_t* qspi_instance, uint32_t sector, uint8_t* buff, 96 | size_t count); 97 | 98 | qspi_result_t 99 | qspi_write_sector (qspi_t* qspi_instance, uint32_t sector, uint8_t* buff, 100 | size_t count); 101 | 102 | qspi_result_t 103 | qspi_erase_sector (qspi_t* qspi_instance, uint32_t sector); 104 | 105 | qspi_result_t 106 | qspi_erase_block32K (qspi_t* qspi_instance, uint32_t address); 107 | 108 | qspi_result_t 109 | qspi_erase_block64K (qspi_t* qspi_instance, uint32_t address); 110 | 111 | qspi_result_t 112 | qspi_erase_chip (qspi_t* qspi_instance); 113 | 114 | qspi_result_t 115 | qspi_reset_chip (qspi_t* qspi_instance); 116 | 117 | const char* 118 | qspi_get_manufacturer (qspi_t* qspi_instance); 119 | 120 | const char* 121 | qspi_get_memory_type (qspi_t* qspi_instance); 122 | 123 | size_t 124 | qspi_get_sector_size (qspi_t* qspi_instance); 125 | 126 | size_t 127 | qspi_get_sector_count (qspi_t* qspi_instance); 128 | 129 | void 130 | qspi_event_cb (qspi_t* qspi_instance); 131 | 132 | #ifdef __cplusplus 133 | } 134 | #endif 135 | 136 | #endif /* INCLUDE_QSPI_FLASH_C_API_H_ */ 137 | -------------------------------------------------------------------------------- /include/qspi-flash.h: -------------------------------------------------------------------------------- 1 | /* 2 | * qspi-flash.h 3 | * 4 | * Copyright (c) 2016-2021 Lix N. Paulian (lix@paulian.net) 5 | * 6 | * Permission is hereby granted, free of charge, to any person 7 | * obtaining a copy of this software and associated documentation 8 | * files (the "Software"), to deal in the Software without 9 | * restriction, including without limitation the rights to use, 10 | * copy, modify, merge, publish, distribute, sublicense, and/or 11 | * sell copies of the Software, and to permit persons to whom 12 | * the Software is furnished to do so, subject to the following 13 | * conditions: 14 | * 15 | * The above copyright notice and this permission notice shall be 16 | * included in all copies or substantial portions of the Software. 17 | * 18 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 19 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 20 | * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 21 | * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 22 | * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 23 | * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 24 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 25 | * OTHER DEALINGS IN THE SOFTWARE. 26 | * 27 | * Created on: 9 Oct 2016 (LNP) 28 | */ 29 | 30 | #ifndef QSPI_FLASH_H_ 31 | #define QSPI_FLASH_H_ 32 | 33 | #include 34 | #include 35 | #include "cmsis_device.h" 36 | #include "quadspi.h" 37 | 38 | #if defined (__cplusplus) 39 | 40 | namespace os 41 | { 42 | namespace driver 43 | { 44 | namespace stm32f7 45 | { 46 | typedef struct qspi_device_s qspi_device_t; 47 | class qspi_intern; 48 | 49 | class qspi_impl : public os::posix::block_device_impl 50 | { 51 | public: 52 | qspi_impl (QSPI_HandleTypeDef* hqspi); 53 | 54 | ~qspi_impl (); 55 | 56 | typedef enum 57 | { 58 | ok = HAL_OK, // HAL errors 59 | error = HAL_ERROR, 60 | busy = HAL_BUSY, 61 | timeout = HAL_TIMEOUT, 62 | type_not_found = 10, // qspi specific errors 63 | } qspi_result_t; 64 | 65 | virtual bool 66 | do_is_opened (void) override; 67 | 68 | virtual int 69 | do_vopen (const char* path, int oflag, std::va_list args) override; 70 | 71 | virtual ssize_t 72 | do_read_block (void* buf, blknum_t blknum, std::size_t nblocks) 73 | override; 74 | 75 | virtual ssize_t 76 | do_write_block (const void* buf, blknum_t blknum, std::size_t nblocks) 77 | override; 78 | 79 | virtual int 80 | do_vioctl (int request, std::va_list args) override; 81 | 82 | virtual void 83 | do_sync (void) override; 84 | 85 | virtual int 86 | do_close (void) override; 87 | 88 | void 89 | get_version (uint8_t& version_major, uint8_t& version_minor, 90 | uint8_t& version_patch); 91 | 92 | qspi_result_t 93 | sleep (bool state); 94 | 95 | qspi_result_t 96 | initialize (void); 97 | 98 | qspi_result_t 99 | uninitialize (void); 100 | 101 | qspi_result_t 102 | enter_mem_mapped (void); 103 | 104 | qspi_result_t 105 | exit_mem_mapped (void); 106 | 107 | qspi_result_t 108 | read (uint32_t address, uint8_t* buff, size_t count); 109 | 110 | qspi_result_t 111 | write (uint32_t address, uint8_t* buff, size_t count); 112 | 113 | qspi_result_t 114 | read_sector (uint32_t sector, uint8_t* buff, size_t count); 115 | 116 | qspi_result_t 117 | write_sector (uint32_t sector, uint8_t* buff, size_t count); 118 | 119 | qspi_result_t 120 | erase_sector (uint32_t sector); 121 | 122 | qspi_result_t 123 | erase_block32K (uint32_t address); 124 | 125 | qspi_result_t 126 | erase_block64K (uint32_t address); 127 | 128 | qspi_result_t 129 | erase_chip (void); 130 | 131 | qspi_result_t 132 | reset_chip (void); 133 | 134 | const char* 135 | get_manufacturer (void); 136 | 137 | const char* 138 | get_memory_type (void); 139 | 140 | size_t 141 | get_sector_size (void); 142 | 143 | size_t 144 | get_sector_count (void); 145 | 146 | void 147 | cb_event (void); 148 | 149 | friend class qspi_winbond; 150 | friend class qspi_micron; 151 | 152 | protected: 153 | qspi_result_t 154 | enter_quad_mode (void); 155 | 156 | qspi_result_t 157 | qspi_command (QSPI_HandleTypeDef* hq, QSPI_CommandTypeDef* cmd, 158 | uint32_t timeout); 159 | 160 | // Standard command sub-set (common for all flash chips) 161 | static constexpr uint8_t JEDEC_ID = 0x9F; 162 | 163 | static constexpr uint8_t WRITE_ENABLE = 0x06; 164 | static constexpr uint8_t WRITE_DISABLE = 0x04; 165 | 166 | static constexpr uint8_t READ_STATUS_REGISTER = 0x05; 167 | static constexpr uint8_t WRITE_STATUS_REGISTER = 0x01; 168 | 169 | static constexpr uint8_t SECTOR_ERASE = 0x20; 170 | static constexpr uint8_t BLOCK_32K_ERASE = 0x52; 171 | static constexpr uint8_t BLOCK_64K_ERASE = 0xD8; 172 | static constexpr uint8_t CHIP_ERASE = 0xC7; 173 | 174 | static constexpr uint8_t RESET_ENABLE = 0x66; 175 | static constexpr uint8_t RESET_DEVICE = 0x99; 176 | 177 | static constexpr uint8_t POWER_DOWN = 0xB9; 178 | static constexpr uint8_t RELEASE_POWER_DOWN = 0xAB; 179 | 180 | static constexpr uint8_t PAGE_PROGRAM = 0x02; 181 | static constexpr uint8_t QUAD_PAGE_PROGRAM = 0x32; 182 | 183 | static constexpr uint8_t READ_DATA = 0x03; 184 | static constexpr uint8_t FAST_READ_DATA = 0x0B; 185 | static constexpr uint8_t FAST_READ_QUAD_OUT = 0x6B; 186 | static constexpr uint8_t FAST_READ_QUAD_IN_OUT = 0xEB; 187 | 188 | // Some timeouts 189 | static constexpr uint32_t one_ms = 1000 190 | / os::rtos::sysclock.frequency_hz; 191 | static constexpr uint32_t one_sec = 1000 * one_ms; 192 | static constexpr uint32_t TIMEOUT = 10 * one_ms; 193 | static constexpr uint32_t WRITE_TIMEOUT = 50 * one_ms; 194 | static constexpr uint32_t ERASE_TIMEOUT = 2 * one_sec; 195 | static constexpr uint32_t CHIP_ERASE_TIMEOUT = 200 * one_sec; 196 | 197 | QSPI_HandleTypeDef* hqspi_; 198 | os::rtos::semaphore_binary semaphore_ 199 | { "qspi", 0 }; 200 | 201 | private: 202 | qspi_result_t 203 | page_write (uint32_t address, uint8_t* buff, size_t count); 204 | 205 | qspi_result_t 206 | read_JEDEC_ID (void); 207 | 208 | qspi_result_t 209 | erase (uint32_t address, uint8_t which); 210 | 211 | void 212 | invalidate_dcache (uint8_t* ptr, size_t len); 213 | 214 | void 215 | clean_dcache (uint8_t* ptr, size_t len); 216 | 217 | static constexpr uint8_t VERSION_MAJOR = 2; 218 | static constexpr uint8_t VERSION_MINOR = 2; 219 | static constexpr uint8_t VERSION_PATCH = 4; 220 | 221 | class qspi_intern* pimpl = nullptr; 222 | uint8_t manufacturer_ID_ = 0; 223 | uint16_t memory_type_ = 0; 224 | const char* pmanufacturer_ = nullptr; 225 | const qspi_device_t* pdevice_ = nullptr; 226 | bool volatile is_opened_ = false; 227 | uint8_t lbuff_[256]; 228 | 229 | }; 230 | 231 | class qspi_intern 232 | { 233 | public: 234 | qspi_intern (void) 235 | { 236 | } 237 | 238 | virtual 239 | ~qspi_intern () = default; 240 | 241 | virtual qspi_impl::qspi_result_t 242 | enter_quad_mode (qspi_impl* pq) = 0; 243 | 244 | }; 245 | 246 | inline void 247 | qspi_impl::get_version (uint8_t& version_major, uint8_t& version_minor, 248 | uint8_t& version_patch) 249 | { 250 | version_major = VERSION_MAJOR; 251 | version_minor = VERSION_MINOR; 252 | version_patch = VERSION_PATCH; 253 | } 254 | 255 | inline qspi_impl::qspi_result_t 256 | qspi_impl::enter_quad_mode (void) 257 | { 258 | return (pimpl == nullptr) ? error : pimpl->enter_quad_mode (this); 259 | } 260 | 261 | inline qspi_impl::qspi_result_t 262 | qspi_impl::exit_mem_mapped (void) 263 | { 264 | return ((qspi_impl::qspi_result_t) (HAL_QSPI_Abort (hqspi_))); 265 | } 266 | 267 | inline qspi_impl::qspi_result_t 268 | qspi_impl::erase_block32K (uint32_t address) 269 | { 270 | return erase (address, BLOCK_32K_ERASE); 271 | } 272 | 273 | inline qspi_impl::qspi_result_t 274 | qspi_impl::erase_block64K (uint32_t address) 275 | { 276 | return erase (address, BLOCK_64K_ERASE); 277 | } 278 | 279 | inline qspi_impl::qspi_result_t 280 | qspi_impl::erase_chip (void) 281 | { 282 | return erase (0, CHIP_ERASE); 283 | } 284 | 285 | inline const char* 286 | qspi_impl::get_manufacturer (void) 287 | { 288 | return pmanufacturer_; 289 | } 290 | 291 | inline void 292 | qspi_impl::invalidate_dcache (uint8_t* ptr, size_t len) 293 | { 294 | if (SCB->CCR & (uint32_t) SCB_CCR_DC_Msk) 295 | { 296 | // D-cache is enabled 297 | uint32_t* aligned_buff = (uint32_t*) (((uint32_t) ptr) & 0xFFFFFFE0); 298 | uint32_t aligned_count = (uint32_t) (len & 0xFFFFFFE0) + 32; 299 | SCB_CleanInvalidateDCache_by_Addr (aligned_buff, aligned_count); 300 | } 301 | } 302 | 303 | inline void 304 | qspi_impl::clean_dcache (uint8_t* ptr, size_t len) 305 | { 306 | if (SCB->CCR & (uint32_t) SCB_CCR_DC_Msk) 307 | { 308 | // D-cache is enabled 309 | uint32_t* aligned_buff = (uint32_t*) (((uint32_t) (ptr)) 310 | & 0xFFFFFFE0); 311 | uint32_t aligned_count = (uint32_t) (len & 0xFFFFFFE0) + 32; 312 | SCB_CleanDCache_by_Addr (aligned_buff, aligned_count); 313 | } 314 | } 315 | 316 | } /* namespace stm32f7 */ 317 | } /* namespace driver */ 318 | } /* namespace os */ 319 | 320 | #endif // (__cplusplus) 321 | 322 | #endif /* QSPI_FLASH_H_ */ 323 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@lix/stm32f7-qspi", 3 | "version": "2.2.4", 4 | "description": "This is a QSPI serial flash driver for the STM32F7xx family of controllers", 5 | "author": { 6 | "name": "Lix N. Paulian", 7 | "email": "lix@paulian.net" 8 | }, 9 | "license": "MIT", 10 | "xpack": { 11 | "minimumXpmRequired": "0.16.2" 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /scripts/xpacks-helper.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | #set -euo pipefail 3 | #IFS=$'\n\t' 4 | 5 | # ----------------------------------------------------------------------------- 6 | # Bash helper script used in project generate.sh scripts. 7 | # ----------------------------------------------------------------------------- 8 | 9 | do_add_stm32f7_qspi_xpack() { 10 | local pack_name='stm32f7-qspi' 11 | do_tell_xpack "${pack_name}-xpack" 12 | 13 | do_select_pack_folder "lix/${pack_name}.git" 14 | 15 | do_prepare_dest "${pack_name}/include" 16 | do_add_content "${pack_folder}/include"/* 17 | 18 | do_prepare_dest "${pack_name}/src" 19 | do_add_content "${pack_folder}/src"/* 20 | } 21 | 22 | # ----------------------------------------------------------------------------- 23 | -------------------------------------------------------------------------------- /src/qspi-descr.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * qspi-descr.cpp 3 | * 4 | * Copyright (c) 2017, 2018, 2020 Lix N. Paulian (lix@paulian.net) 5 | * 6 | * Permission is hereby granted, free of charge, to any person 7 | * obtaining a copy of this software and associated documentation 8 | * files (the "Software"), to deal in the Software without 9 | * restriction, including without limitation the rights to use, 10 | * copy, modify, merge, publish, distribute, sublicense, and/or 11 | * sell copies of the Software, and to permit persons to whom 12 | * the Software is furnished to do so, subject to the following 13 | * conditions: 14 | * 15 | * The above copyright notice and this permission notice shall be 16 | * included in all copies or substantial portions of the Software. 17 | * 18 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 19 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 20 | * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 21 | * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 22 | * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 23 | * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 24 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 25 | * OTHER DEALINGS IN THE SOFTWARE. 26 | * 27 | * Created on: 1 Jan 2017 (LNP) 28 | */ 29 | 30 | #include "qspi-descr.h" 31 | #include "qspi-micron.h" 32 | #include "qspi-winbond.h" 33 | 34 | namespace os 35 | { 36 | namespace driver 37 | { 38 | namespace stm32f7 39 | { 40 | 41 | // Micron devices; accepted dummy cycles can be between 1 and 14 42 | const qspi_device_t micron_devices[] = 43 | { 44 | { 0xBA18, 4096, "MT25QL128ABA", 0, QSPI_ALTERNATE_BYTES_NONE, 45 | QSPI_ALTERNATE_BYTES_8_BITS, 8, 0, true }, 46 | 47 | { 0xBB18, 4096, "MT25QL128ABA", 0, QSPI_ALTERNATE_BYTES_NONE, 48 | QSPI_ALTERNATE_BYTES_8_BITS, 8, 0, true }, 49 | 50 | { } // 51 | }; 52 | 53 | // Winbond devices; accepted dummy cycles can be either 2, 4, 6 or 8 54 | const qspi_device_t winbond_devices[] = 55 | { 56 | { 0x6016, 4096, "W25Q32FV", 0xF, QSPI_ALTERNATE_BYTES_4_LINES, 57 | QSPI_ALTERNATE_BYTES_8_BITS, 6, 2, false }, 58 | 59 | { 0x6017, 4096, "W25Q64FV", 0xF, QSPI_ALTERNATE_BYTES_4_LINES, 60 | QSPI_ALTERNATE_BYTES_8_BITS, 6, 2, false }, 61 | 62 | { 0x6018, 4096, "W25Q128FV", 0xF, QSPI_ALTERNATE_BYTES_4_LINES, 63 | QSPI_ALTERNATE_BYTES_8_BITS, 6, 2, false }, 64 | 65 | { 0x7018, 4096, "W25Q128JV", 0xF, QSPI_ALTERNATE_BYTES_4_LINES, 66 | QSPI_ALTERNATE_BYTES_8_BITS, 6, 2, true }, 67 | 68 | { } // 69 | }; 70 | 71 | // Factories for the manufacturer's table 72 | static qspi_intern* 73 | new_micron (void) 74 | { 75 | return new qspi_micron 76 | { }; 77 | } 78 | 79 | static qspi_intern* 80 | new_winbond (void) 81 | { 82 | return new qspi_winbond 83 | { }; 84 | } 85 | 86 | // Supported manufactures 87 | const qspi_manuf_t qspi_manufacturers[] = 88 | { 89 | { MANUF_ID_MICRON, "Micron/ST", micron_devices, new_micron }, 90 | { MANUF_ID_WINBOND, "Winbond", winbond_devices, new_winbond }, 91 | { } // 92 | }; 93 | 94 | } /* namespace stm32f7 */ 95 | } /* namespace driver */ 96 | } /* namespace os */ 97 | -------------------------------------------------------------------------------- /src/qspi-descr.h: -------------------------------------------------------------------------------- 1 | /* 2 | * qspi-descr.h 3 | * 4 | * Copyright (c) 2017, 2018, 2020 Lix N. Paulian (lix@paulian.net) 5 | * 6 | * Permission is hereby granted, free of charge, to any person 7 | * obtaining a copy of this software and associated documentation 8 | * files (the "Software"), to deal in the Software without 9 | * restriction, including without limitation the rights to use, 10 | * copy, modify, merge, publish, distribute, sublicense, and/or 11 | * sell copies of the Software, and to permit persons to whom 12 | * the Software is furnished to do so, subject to the following 13 | * conditions: 14 | * 15 | * The above copyright notice and this permission notice shall be 16 | * included in all copies or substantial portions of the Software. 17 | * 18 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 19 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 20 | * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 21 | * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 22 | * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 23 | * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 24 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 25 | * OTHER DEALINGS IN THE SOFTWARE. 26 | * 27 | * Created on: 1 Jan 2017 (LNP) 28 | */ 29 | 30 | #ifndef QSPI_DESCR_H_ 31 | #define QSPI_DESCR_H_ 32 | 33 | #include "stdint.h" 34 | 35 | namespace os 36 | { 37 | namespace driver 38 | { 39 | namespace stm32f7 40 | { 41 | 42 | #define MANUF_ID_MICRON 0x20 43 | #define MANUF_ID_WINBOND 0xEF 44 | 45 | typedef struct qspi_device_s 46 | { 47 | uint16_t device_ID; 48 | uint16_t sector_size; 49 | const char* device_name; 50 | 51 | // following parameters used only in fast read mode 52 | uint32_t alt_bytes; // optional alt bytes 53 | uint32_t alt_bytes_mode; // none, one, two or four lines 54 | uint32_t alt_bytes_size; // 8, 16 or 32 bits 55 | uint8_t dummy_cycles; // dummy cycles 56 | uint8_t alt_bytes_cycles; // alt bytes cycles to subtract from dummy cycles 57 | bool DDR_support; // dual data rate (not used for now) 58 | } qspi_device_t; 59 | 60 | typedef struct qspi_manuf_s 61 | { 62 | uint8_t manufacturer_ID; 63 | const char* manufacturer_name; 64 | const qspi_device_t* devices; 65 | class qspi_intern* 66 | (*qspi_factory) (); 67 | } qspi_manuf_t; 68 | 69 | extern const qspi_manuf_t qspi_manufacturers[]; 70 | 71 | } /* namespace stm32f7 */ 72 | } /* namespace driver */ 73 | } /* namespace os */ 74 | 75 | #endif /* QSPI_DESCR_H_ */ 76 | -------------------------------------------------------------------------------- /src/qspi-flash-c-wrapper.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * qspi-flash-c-wrapper.cpp 3 | * 4 | * Copyright (c) 2017, 2018, 2020 Lix N. Paulian (lix@paulian.net) 5 | * 6 | * Permission is hereby granted, free of charge, to any person 7 | * obtaining a copy of this software and associated documentation 8 | * files (the "Software"), to deal in the Software without 9 | * restriction, including without limitation the rights to use, 10 | * copy, modify, merge, publish, distribute, sublicense, and/or 11 | * sell copies of the Software, and to permit persons to whom 12 | * the Software is furnished to do so, subject to the following 13 | * conditions: 14 | * 15 | * The above copyright notice and this permission notice shall be 16 | * included in all copies or substantial portions of the Software. 17 | * 18 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 19 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 20 | * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 21 | * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 22 | * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 23 | * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 24 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 25 | * OTHER DEALINGS IN THE SOFTWARE. 26 | * 27 | * Created on: 5 Feb 2017 (LNP) 28 | */ 29 | 30 | #include "qspi-flash.h" 31 | #include "qspi-flash-c-api.h" 32 | 33 | using namespace os::driver::stm32f7; 34 | 35 | // Explicit template instantiation 36 | template class os::posix::block_device_implementable< 37 | os::driver::stm32f7::qspi_impl>; 38 | using qspi_c = os::posix::block_device_implementable; 39 | 40 | /** 41 | * @brief Allocate a qspi_flash object instance and construct it. 42 | * @param hqspi: qspi handle. 43 | * @return qspi_ok if the class could be created, qspi_error otherwise. 44 | */ 45 | qspi_t * 46 | qspi_new (QSPI_HandleTypeDef* hqspi) 47 | { 48 | qspi_t* pqspi = reinterpret_cast (new qspi_c 49 | { "flash", hqspi }); 50 | return pqspi; 51 | } 52 | 53 | /** 54 | * @brief Destruct the qspi_flash object instance and deallocate it. 55 | * @param qspi_instance: pointer to the qspi object. 56 | */ 57 | void 58 | qspi_delete (qspi_t* qspi_instance) 59 | { 60 | delete reinterpret_cast (qspi_instance); 61 | } 62 | 63 | /** 64 | * @brief Return the driver's version. 65 | * @param qspi_instance: pointer to the qspi object. 66 | * @param version_major: pointer where the major version number will be returned to. 67 | * @param version_minor: pointer where the minor version number will be returned to. 68 | * @param version_patch: pointer where the patch number will be returned to. 69 | */ 70 | void 71 | qspi_get_version (qspi_t* qspi_instance, uint8_t* version_major, 72 | uint8_t* version_minor, uint8_t* version_patch) 73 | { 74 | ((reinterpret_cast (qspi_instance))->impl ()).get_version ( 75 | *version_major, *version_minor, *version_patch); 76 | } 77 | 78 | /** 79 | * @brief Switch the flash chip into or out of deep sleep. 80 | * @param qspi_instance: pointer to the qspi object. 81 | * @param state: if true, enter deep sleep; if false, exit deep sleep. 82 | * @return qspi_ok if successful, an error otherwise. 83 | */ 84 | qspi_result_t 85 | qspi_sleep (qspi_t* qspi_instance, bool state) 86 | { 87 | return (qspi_result_t) (((reinterpret_cast (qspi_instance))->impl ()).sleep ( 88 | state)); 89 | } 90 | 91 | /** 92 | * @brief Read the flash chip ID and initialize the internal structures accordingly. 93 | * @param qspi_instance: pointer to the qspi object. 94 | * @return qspi_ok if successful, or a qspi error if the flash could not be identified 95 | * or it is not supported by the driver. 96 | */ 97 | qspi_result_t 98 | qspi_initialize (qspi_t* qspi_instance) 99 | { 100 | return (qspi_result_t) (((reinterpret_cast (qspi_instance))->impl ()).initialize ()); 101 | } 102 | 103 | /** 104 | * @brief Set flash device to default state. 105 | * @param qspi_instance: pointer to the qspi object. 106 | * @return if successful, or a qspi error otherwise. 107 | */ 108 | qspi_result_t 109 | qspi_uninitialize (qspi_t* qspi_instance) 110 | { 111 | return (qspi_result_t) (((reinterpret_cast (qspi_instance))->impl ()).uninitialize ()); 112 | } 113 | 114 | /** 115 | * @brief Map the flash to the addressing space of the controller, starting at 116 | * address 0x90000000. 117 | * @param qspi_instance: pointer to the qspi object. 118 | * @return qspi_ok if successful, false otherwise. 119 | */ 120 | qspi_result_t 121 | qspi_enter_mem_mapped (qspi_t* qspi_instance) 122 | { 123 | return (qspi_result_t) (((reinterpret_cast (qspi_instance))->impl ()).enter_mem_mapped ()); 124 | } 125 | 126 | /** 127 | * @brief Exit memory mapped mode. 128 | * @param qspi_instance: pointer to the qspi object. 129 | * @return qspi_ok if successful, false otherwise. 130 | */ 131 | qspi_result_t 132 | qspi_exit_mem_mapped (qspi_t* qspi_instance) 133 | { 134 | return (qspi_result_t) (((reinterpret_cast (qspi_instance))->impl ()).exit_mem_mapped ()); 135 | } 136 | 137 | /** 138 | * @brief Read a block of data from the flash. 139 | * @param qspi_instance: pointer to the qspi object. 140 | * @param address: start address in flash where to read from. 141 | * @param buff: buffer where to copy data to. 142 | * @param count: amount of data to be retrieved from flash. 143 | * @return qspi_ok if successful, a qspi error otherwise. 144 | */ 145 | qspi_result_t 146 | qspi_read (qspi_t* qspi_instance, uint32_t address, uint8_t* buff, size_t count) 147 | { 148 | return (qspi_result_t) (((reinterpret_cast (qspi_instance))->impl ()).read ( 149 | address, buff, count)); 150 | } 151 | 152 | /** 153 | * @brief Write data to flash. 154 | * @param qspi_instance: pointer to the qspi object. 155 | * @param address: start address in flash where to write data to. 156 | * @param buff: source data to be written. 157 | * @param count: amount of data to be written. 158 | * @return qspi_ok if successful, a qspi error otherwise. 159 | */ 160 | qspi_result_t 161 | qspi_write (qspi_t* qspi_instance, uint32_t address, uint8_t* buff, 162 | size_t count) 163 | { 164 | return (qspi_result_t) (((reinterpret_cast (qspi_instance))->impl ()).write ( 165 | address, buff, count)); 166 | } 167 | 168 | /** 169 | * @brief Read sector. 170 | * @param qspi_instance: pointer to the qspi object. 171 | * @param sector: sector number to read from. 172 | * @param buff: buffer to receive the data from flash. 173 | * @param count: number of bytes to read (buffer should be large enough). 174 | * @return qspi_ok if successful, or a qspi error otherwise. 175 | */ 176 | qspi_result_t 177 | qspi_read_sector (qspi_t* qspi_instance, uint32_t sector, uint8_t* buff, 178 | size_t count) 179 | { 180 | return (qspi_result_t) (((reinterpret_cast (qspi_instance))->impl ()).read_sector ( 181 | sector, buff, count)); 182 | } 183 | 184 | /** 185 | * @brief Write sector. 186 | * @param qspi_instance: pointer to the qspi object. 187 | * @param sector: sector number to write to. 188 | * @param buff: buffer containing the data to be written to flash. 189 | * @param count: number of bytes to be written. 190 | * @return qspi_ok if successful, or a qspi error otherwise. 191 | */ 192 | qspi_result_t 193 | qspi_write_sector (qspi_t* qspi_instance, uint32_t sector, uint8_t* buff, 194 | size_t count) 195 | { 196 | return (qspi_result_t) (((reinterpret_cast (qspi_instance))->impl ()).write_sector ( 197 | sector, buff, count)); 198 | } 199 | 200 | /** 201 | * @brief Erase sector. 202 | * @param qspi_instance: pointer to the qspi object. 203 | * @param sector: sector to be erased. 204 | * @return qspi_ok if successful, or a qspi error otherwise. 205 | */ 206 | qspi_result_t 207 | qspi_erase_sector (qspi_t* qspi_instance, uint32_t sector) 208 | { 209 | return (qspi_result_t) (((reinterpret_cast (qspi_instance))->impl ()).erase_sector ( 210 | sector)); 211 | } 212 | 213 | /** 214 | * @brief Erase 32K block. 215 | * @param qspi_instance: pointer to the qspi object. 216 | * @param address: address in the block to be erased. 217 | * @return qspi_ok if successful, or a qspi error otherwise. 218 | */ 219 | qspi_result_t 220 | qspi_erase_block32K (qspi_t* qspi_instance, uint32_t address) 221 | { 222 | return (qspi_result_t) (((reinterpret_cast (qspi_instance))->impl ()).erase_block32K ( 223 | address)); 224 | } 225 | 226 | /** 227 | * @brief Erase 64K block. 228 | * @param qspi_instance: pointer to the qspi object. 229 | * @param address: address in the block to be erased. 230 | * @return qspi_ok if successful, or a qspi error otherwise. 231 | */ 232 | qspi_result_t 233 | qspi_erase_block64K (qspi_t* qspi_instance, uint32_t address) 234 | { 235 | return (qspi_result_t) (((reinterpret_cast (qspi_instance))->impl ()).erase_block64K ( 236 | address)); 237 | } 238 | 239 | /** 240 | * @brief Erase chip. 241 | * @param qspi_instance: pointer to the qspi object. 242 | * @return qspi_ok if successful, or a qspi error otherwise. 243 | */ 244 | qspi_result_t 245 | qspi_erase_chip (qspi_t* qspi_instance) 246 | { 247 | return (qspi_result_t) (((reinterpret_cast (qspi_instance))->impl ()).erase_chip ()); 248 | } 249 | 250 | /** 251 | * @brief Software reset the flash chip. 252 | * @param qspi_instance: pointer to the qspi object. 253 | * @return qspi_ok if successful, or a qspi error otherwise. 254 | */ 255 | qspi_result_t 256 | qspi_reset_chip (qspi_t* qspi_instance) 257 | { 258 | return (qspi_result_t) (((reinterpret_cast (qspi_instance))->impl ()).reset_chip ()); 259 | } 260 | 261 | /** 262 | * @brief Return the manufacturer. 263 | * @param qspi_instance: pointer to the qspi object. 264 | * @return Pointer to a string representing the human readable manufacturer, or 265 | * null pointer if the system is not initialized. 266 | */ 267 | const char* 268 | qspi_get_manufacturer (qspi_t* qspi_instance) 269 | { 270 | return (((reinterpret_cast (qspi_instance))->impl ()).get_manufacturer ()); 271 | } 272 | 273 | /** 274 | * @brief Return the memory type. 275 | * @param qspi_instance: pointer to the qspi object. 276 | * @return Pointer to a string representing the human readable memory type, or 277 | * null pointer if the system is not initialized. 278 | */ 279 | const char* 280 | qspi_get_memory_type (qspi_t* qspi_instance) 281 | { 282 | return (((reinterpret_cast (qspi_instance))->impl ()).get_memory_type ()); 283 | } 284 | 285 | /** 286 | * @brief Return the sector size. 287 | * @param qspi_instance: pointer to the qspi object. 288 | * @return The sector size in bytes, zero if system is not initialized. 289 | */ 290 | size_t 291 | qspi_get_sector_size (qspi_t* qspi_instance) 292 | { 293 | return (((reinterpret_cast (qspi_instance))->impl ()).get_sector_size ()); 294 | } 295 | 296 | /** 297 | * @brief Return the sectors count. 298 | * @param qspi_instance: pointer to the qspi object. 299 | * @return The sectors count, zero if system is not initialized. 300 | */ 301 | size_t 302 | qspi_get_sector_count (qspi_t* qspi_instance) 303 | { 304 | return (((reinterpret_cast (qspi_instance))->impl ()).get_sector_count ()); 305 | } 306 | 307 | /** 308 | * @brief Events call-back handler 309 | * @param qspi_instance: pointer to the qspi object. 310 | */ 311 | void 312 | qspi_event_cb (qspi_t* qspi_instance) 313 | { 314 | ((reinterpret_cast (qspi_instance))->impl ()).cb_event (); 315 | } 316 | 317 | -------------------------------------------------------------------------------- /src/qspi-flash.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * qspi-flash.cpp 3 | * 4 | * Copyright (c) 2016-2021 Lix N. Paulian (lix@paulian.net) 5 | * 6 | * Permission is hereby granted, free of charge, to any person 7 | * obtaining a copy of this software and associated documentation 8 | * files (the "Software"), to deal in the Software without 9 | * restriction, including without limitation the rights to use, 10 | * copy, modify, merge, publish, distribute, sublicense, and/or 11 | * sell copies of the Software, and to permit persons to whom 12 | * the Software is furnished to do so, subject to the following 13 | * conditions: 14 | * 15 | * The above copyright notice and this permission notice shall be 16 | * included in all copies or substantial portions of the Software. 17 | * 18 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 19 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 20 | * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 21 | * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 22 | * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 23 | * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 24 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 25 | * OTHER DEALINGS IN THE SOFTWARE. 26 | * 27 | * Created on: 9 Oct 2016 (LNP) 28 | */ 29 | 30 | /* 31 | * This file implements the common basic low level functions to control 32 | * a QSPI flash device. 33 | */ 34 | 35 | #include 36 | #include 37 | #include "qspi-flash.h" 38 | #include "qspi-descr.h" 39 | #include "qspi-winbond.h" 40 | #include "qspi-micron.h" 41 | 42 | namespace os 43 | { 44 | namespace driver 45 | { 46 | namespace stm32f7 47 | { 48 | /** 49 | * @brief Constructor. 50 | * @param hqspi: HAL qspi handle. 51 | */ 52 | qspi_impl::qspi_impl (QSPI_HandleTypeDef* hqspi) 53 | { 54 | trace::printf ("%s(%p) @%p\n", __func__, hqspi, this); 55 | hqspi_ = hqspi; 56 | } 57 | 58 | qspi_impl::~qspi_impl () 59 | { 60 | trace::printf ("%s(%p) @%p\n", __func__, this); 61 | } 62 | 63 | #pragma GCC diagnostic push 64 | #pragma GCC diagnostic ignored "-Wunused-parameter" 65 | 66 | //----------------- POSIX interface ------------------------------ 67 | 68 | /** 69 | * @brief Check if the device is opened 70 | * @return Returns true if the device is already opened, false otherwise 71 | */ 72 | bool 73 | qspi_impl::do_is_opened (void) 74 | { 75 | return is_opened_; 76 | } 77 | 78 | /** 79 | * @brief Open the block device 80 | * @param path: path to the device. 81 | * @param oflag: flags. 82 | * @param args: arguments list. 83 | * @return 0 if the device was successfully opened, -1 otherwise. 84 | */ 85 | int 86 | qspi_impl::do_vopen (const char* path, int oflag, std::va_list args) 87 | { 88 | int result = -1; 89 | 90 | do 91 | { 92 | if (is_opened_) 93 | { 94 | errno = EEXIST; // already opened 95 | break; 96 | } 97 | 98 | if (hqspi_->Instance == nullptr) 99 | { 100 | errno = EIO; // no QSPI IP defined 101 | break; 102 | } 103 | 104 | if (qspi_impl::initialize () != qspi_impl::ok) 105 | { 106 | errno = EIO; 107 | break; 108 | } 109 | 110 | num_blocks_ = qspi_impl::get_sector_count (); 111 | block_logical_size_bytes_ = qspi_impl::get_sector_size (); 112 | block_physical_size_bytes_ = qspi_impl::get_sector_size (); 113 | 114 | if (num_blocks_ == 0 || block_physical_size_bytes_ == 0) 115 | { 116 | errno = EIO; 117 | break; 118 | } 119 | 120 | is_opened_ = true; 121 | result = 0; 122 | } 123 | while (false); 124 | 125 | return result; 126 | } 127 | 128 | /** 129 | * @brief Read a block of data. 130 | * @param buf: buffer where the data will be returned. 131 | * @param blknum: the block number. 132 | * @param nblocks: number of blocks to read. 133 | * @return Number of blocks read or -1 if error. 134 | */ 135 | ssize_t 136 | qspi_impl::do_read_block (void* buf, posix::block_device::blknum_t blknum, 137 | std::size_t nblocks) 138 | { 139 | // compute the block's address and the total bytes to be read 140 | uint32_t address = block_logical_size_bytes_ * blknum; 141 | size_t count = block_logical_size_bytes_ * nblocks; 142 | 143 | if (qspi_impl::read (address, (uint8_t*) buf, count) != ok) 144 | { 145 | errno = EIO; 146 | nblocks = -1; 147 | } 148 | 149 | return nblocks; 150 | } 151 | 152 | /** 153 | * @brief Write data to the block device. 154 | * @param buf: buffer with the data to be written. 155 | * @param blknum: the block number. 156 | * @param nblocks: number of blocks to be written. 157 | * @return Number of blocks written or -1 if error. 158 | */ 159 | ssize_t 160 | qspi_impl::do_write_block (const void* buf, 161 | posix::block_device::blknum_t blknum, 162 | std::size_t nblocks) 163 | { 164 | // compute the block's address and the total bytes to be written 165 | uint32_t address = block_logical_size_bytes_ * blknum; 166 | size_t count = block_logical_size_bytes_ * nblocks; 167 | bool to_write = false; 168 | 169 | // check if we really need to write 170 | uint8_t* p = (uint8_t*) buf; 171 | for (posix::block_device::blknum_t i = 0; i < count; i++) 172 | { 173 | if (*p++ != 0xFF) 174 | { 175 | to_write = true; 176 | break; // yes, we have data to write 177 | } 178 | } 179 | 180 | int i = nblocks; 181 | if (to_write == false) 182 | { 183 | // nothing to write, only erase then quit 184 | while (i--) 185 | { 186 | qspi_impl::erase_sector (blknum++); 187 | } 188 | } 189 | else 190 | { 191 | uint32_t sector_256 = 0; 192 | p = (uint8_t*) buf; 193 | bool to_erase = false; 194 | 195 | do 196 | { 197 | bool valid_data = false; 198 | 199 | if (qspi_impl::read (address + (sector_256 * sizeof(lbuff_)), 200 | lbuff_, sizeof(lbuff_)) != ok) 201 | { 202 | errno = EIO; 203 | nblocks = -1; 204 | break; // read error, exit 205 | } 206 | 207 | // check if we need to erase before write 208 | for (int j = 0; j < (int) sizeof(lbuff_); j++, p++) 209 | { 210 | if (*p != lbuff_[j] && lbuff_[j] != 0xFF) 211 | { 212 | // yes, we must erase before write 213 | to_erase = true; 214 | break; 215 | } 216 | if (*p != 0xFF && *p != lbuff_[j]) 217 | { 218 | valid_data = true; 219 | } 220 | } 221 | if (to_erase) 222 | { 223 | break; 224 | } 225 | 226 | // sector already erased, just write but only if whole data != 0xFF 227 | if (valid_data) 228 | { 229 | if (qspi_impl::write ( 230 | address + (sector_256 * sizeof(lbuff_)), 231 | (uint8_t*) buf + (sector_256 * sizeof(lbuff_)), 232 | sizeof(lbuff_)) != ok) 233 | { 234 | errno = EIO; 235 | nblocks = -1; 236 | break; 237 | } 238 | } 239 | sector_256++; 240 | } 241 | while (p < ((uint8_t*) buf + count)); 242 | 243 | if (to_erase == true && nblocks > 0) 244 | { 245 | // write without erase did not work 246 | // so erase first the blocks to be written 247 | while (i--) 248 | { 249 | qspi_impl::erase_sector (blknum++); 250 | } 251 | 252 | if (qspi_impl::write (address, (uint8_t*) buf, count) != ok) 253 | { 254 | errno = EIO; 255 | nblocks = -1; 256 | } 257 | } 258 | } 259 | 260 | return nblocks; 261 | } 262 | 263 | /** 264 | * @brief Control the device parameters. 265 | * @param request: command to the device. 266 | * @param args: command's parameter(s). 267 | * @return 0 if successful, -1 otherwise. 268 | */ 269 | int 270 | qspi_impl::do_vioctl (int request, std::va_list args) 271 | { 272 | return -1; 273 | } 274 | 275 | /** 276 | * @brief Synch (flush) the data to the device. 277 | */ 278 | void 279 | qspi_impl::do_sync (void) 280 | { 281 | ; 282 | } 283 | 284 | /** 285 | * @brief Close the block device. 286 | * @return 0 if successful, -1 otherwise. 287 | */ 288 | int 289 | qspi_impl::do_close (void) 290 | { 291 | if (qspi_impl::uninitialize () != ok) 292 | { 293 | errno = EIO; 294 | return -1; 295 | } 296 | 297 | is_opened_ = false; 298 | 299 | return 0; 300 | } 301 | 302 | //------------- End of POSIX interface --------------------------- 303 | 304 | /** 305 | * @brief Read the flash chip ID and initialize the internal structures accordingly. 306 | * @return qspi_impl::ok if successful, or a qspi_impl error if the flash could not be identified 307 | * or it is not supported by the driver. 308 | */ 309 | qspi_impl::qspi_result_t 310 | qspi_impl::initialize (void) 311 | { 312 | qspi_impl::qspi_result_t result; 313 | 314 | // Read flash device ID 315 | if ((result = qspi_impl::read_JEDEC_ID ()) != ok) 316 | { 317 | // Flash device might be in deep sleep 318 | qspi_impl::sleep (false); 319 | 320 | // Reset and try reading ID again 321 | if ((result = qspi_impl::reset_chip ()) == ok) 322 | { 323 | result = qspi_impl::read_JEDEC_ID (); 324 | } 325 | } 326 | 327 | // If all OK, switch flash device in quad mode 328 | if (result == ok) 329 | result = enter_quad_mode (); 330 | 331 | return result; 332 | } 333 | 334 | /** 335 | * @brief Set flash device to default state. 336 | * @return qspi::ok if successful, or a qspi error otherwise. 337 | */ 338 | qspi_impl::qspi_result_t 339 | qspi_impl::uninitialize (void) 340 | { 341 | pimpl = nullptr; 342 | qspi_impl::sleep (false); 343 | return qspi_impl::reset_chip (); 344 | } 345 | 346 | /** 347 | * @brief Read the memory parameters (manufacturer and type). 348 | * @return qspi::ok if successful, or a qspi error otherwise. 349 | */ 350 | qspi_impl::qspi_result_t 351 | qspi_impl::read_JEDEC_ID (void) 352 | { 353 | qspi_impl::qspi_result_t result = error; 354 | uint8_t buff[3]; 355 | QSPI_CommandTypeDef sCommand; 356 | 357 | // Read command settings 358 | sCommand.AddressSize = QSPI_ADDRESS_24_BITS; 359 | sCommand.AlternateByteMode = QSPI_ALTERNATE_BYTES_NONE; 360 | sCommand.DdrMode = QSPI_DDR_MODE_DISABLE; 361 | sCommand.DdrHoldHalfCycle = QSPI_DDR_HHC_ANALOG_DELAY; 362 | sCommand.SIOOMode = QSPI_SIOO_INST_EVERY_CMD; 363 | sCommand.InstructionMode = QSPI_INSTRUCTION_1_LINE; 364 | sCommand.AddressMode = QSPI_ADDRESS_NONE; 365 | sCommand.DataMode = QSPI_DATA_1_LINE; 366 | sCommand.DummyCycles = 0; 367 | sCommand.NbData = 3; 368 | sCommand.Instruction = JEDEC_ID; 369 | 370 | // Initiate read and wait for the event 371 | result = qspi_command (hqspi_, &sCommand, TIMEOUT); 372 | if (result == ok) 373 | { 374 | result = (qspi_impl::qspi_result_t) HAL_QSPI_Receive_IT (hqspi_, 375 | buff); 376 | if (result == ok) 377 | { 378 | if (semaphore_.timed_wait (TIMEOUT) == rtos::result::ok) 379 | { 380 | manufacturer_ID_ = buff[0]; 381 | memory_type_ = buff[1] << 8; 382 | memory_type_ += buff[2]; 383 | 384 | // Do we know this device? 385 | result = type_not_found; 386 | for (const qspi_manuf_t* pqm = qspi_manufacturers; 387 | pqm->manufacturer_ID != 0; pqm++) 388 | { 389 | if (pqm->manufacturer_ID == manufacturer_ID_) 390 | { 391 | // Manufacturer found 392 | for (const qspi_device_t* pqd = pqm->devices; 393 | pqd->device_ID != 0; pqd++) 394 | { 395 | if (pqd->device_ID == memory_type_) 396 | { 397 | // Device found, initialize class 398 | pmanufacturer_ = pqm->manufacturer_name; 399 | pdevice_ = pqd; 400 | pimpl = pqm->qspi_factory (); 401 | result = ok; 402 | break; 403 | } 404 | } 405 | } 406 | } 407 | } 408 | else 409 | { 410 | result = timeout; 411 | } 412 | } 413 | } 414 | 415 | return result; 416 | } 417 | 418 | /** 419 | * @brief Switch the flash chip into or out of deep sleep. 420 | * @param state: if true, enter deep sleep; if false, exit deep sleep. 421 | * @return qspi::ok if successful, an error otherwise. 422 | */ 423 | qspi_impl::qspi_result_t 424 | qspi_impl::sleep (bool state) 425 | { 426 | qspi_impl::qspi_result_t result = error; 427 | QSPI_CommandTypeDef sCommand; 428 | 429 | // Initial command settings 430 | sCommand.AddressSize = QSPI_ADDRESS_24_BITS; 431 | sCommand.AlternateByteMode = QSPI_ALTERNATE_BYTES_NONE; 432 | sCommand.DdrMode = QSPI_DDR_MODE_DISABLE; 433 | sCommand.DdrHoldHalfCycle = QSPI_DDR_HHC_ANALOG_DELAY; 434 | sCommand.SIOOMode = QSPI_SIOO_INST_EVERY_CMD; 435 | sCommand.InstructionMode = QSPI_INSTRUCTION_4_LINES; 436 | sCommand.AddressMode = QSPI_ADDRESS_NONE; 437 | sCommand.DataMode = QSPI_DATA_NONE; 438 | sCommand.DummyCycles = 0; 439 | 440 | // Enable/disable deep sleep 441 | sCommand.Instruction = state ? POWER_DOWN : RELEASE_POWER_DOWN; 442 | result = qspi_command (hqspi_, &sCommand, TIMEOUT); 443 | return result; 444 | } 445 | 446 | /** 447 | * @brief Map the flash to the addressing space of the controller, starting at 448 | * address 0x90000000. 449 | * @return qspi::ok if successful, false otherwise. 450 | */ 451 | qspi_impl::qspi_result_t 452 | qspi_impl::enter_mem_mapped (void) 453 | { 454 | qspi_impl::qspi_result_t result = error; 455 | QSPI_CommandTypeDef sCommand; 456 | QSPI_MemoryMappedTypeDef sMemMappedCfg; 457 | 458 | if (pdevice_ != nullptr) 459 | { 460 | sCommand.AddressSize = QSPI_ADDRESS_24_BITS; 461 | sCommand.AlternateByteMode = pdevice_->alt_bytes_mode; 462 | sCommand.AlternateBytesSize = pdevice_->alt_bytes_size; 463 | sCommand.AlternateBytes = pdevice_->alt_bytes; 464 | sCommand.DdrMode = QSPI_DDR_MODE_DISABLE; 465 | sCommand.DdrHoldHalfCycle = QSPI_DDR_HHC_ANALOG_DELAY; 466 | sCommand.SIOOMode = QSPI_SIOO_INST_EVERY_CMD; 467 | sCommand.InstructionMode = QSPI_INSTRUCTION_4_LINES; 468 | sCommand.AddressMode = QSPI_ADDRESS_4_LINES; 469 | sCommand.DataMode = QSPI_DATA_4_LINES; 470 | sCommand.DummyCycles = pdevice_->dummy_cycles 471 | - pdevice_->alt_bytes_cycles; 472 | sCommand.Instruction = FAST_READ_QUAD_IN_OUT; 473 | 474 | sMemMappedCfg.TimeOutActivation = QSPI_TIMEOUT_COUNTER_DISABLE; 475 | 476 | result = (qspi_impl::qspi_result_t) HAL_QSPI_MemoryMapped ( 477 | hqspi_, &sCommand, &sMemMappedCfg); 478 | } 479 | 480 | return result; 481 | } 482 | 483 | /** 484 | * @brief Read a block of data from the flash. 485 | * @param address: start address in flash where to read from. 486 | * @param buff: buffer where to copy data to. 487 | * @param count: amount of data to be retrieved from flash. 488 | * @return qspi::ok if successful, a qspi error otherwise. 489 | */ 490 | qspi_impl::qspi_result_t 491 | qspi_impl::read (uint32_t address, uint8_t* buff, size_t count) 492 | { 493 | qspi_impl::qspi_result_t result = error; 494 | QSPI_CommandTypeDef sCommand; 495 | 496 | if (pdevice_ != nullptr) 497 | { 498 | // Read command settings 499 | sCommand.AddressSize = QSPI_ADDRESS_24_BITS; 500 | sCommand.AlternateByteMode = pdevice_->alt_bytes_mode; 501 | sCommand.AlternateBytesSize = pdevice_->alt_bytes_size; 502 | sCommand.AlternateBytes = pdevice_->alt_bytes; 503 | sCommand.DdrMode = QSPI_DDR_MODE_DISABLE; 504 | sCommand.DdrHoldHalfCycle = QSPI_DDR_HHC_ANALOG_DELAY; 505 | sCommand.SIOOMode = QSPI_SIOO_INST_EVERY_CMD; 506 | sCommand.InstructionMode = QSPI_INSTRUCTION_4_LINES; 507 | sCommand.AddressMode = QSPI_ADDRESS_4_LINES; 508 | sCommand.DataMode = QSPI_DATA_4_LINES; 509 | sCommand.DummyCycles = pdevice_->dummy_cycles 510 | - pdevice_->alt_bytes_cycles; 511 | sCommand.Address = address; 512 | sCommand.NbData = count; 513 | sCommand.Instruction = FAST_READ_QUAD_IN_OUT; 514 | 515 | // Initiate read, then wait for the event 516 | result = qspi_command (hqspi_, &sCommand, TIMEOUT); 517 | if (result == ok) 518 | { 519 | /** 520 | * Flush and clean the data cache to mitigate incoherence before 521 | * a DMA transfer (DTCM RAM is not cached) 522 | */ 523 | if ((buff + count) >= (uint8_t*) SRAM1_BASE) 524 | { 525 | invalidate_dcache (buff, count); 526 | } 527 | result = (qspi_impl::qspi_result_t) HAL_QSPI_Receive_DMA ( 528 | hqspi_, buff); 529 | if (result == ok) 530 | { 531 | result = 532 | (semaphore_.timed_wait (TIMEOUT) == rtos::result::ok) ? 533 | ok : timeout; 534 | } 535 | } 536 | } 537 | 538 | return result; 539 | } 540 | 541 | /** 542 | * @brief Write data to flash. 543 | * @param address: start address in flash where to write data to. 544 | * @param buff: source data to be written. 545 | * @param count: amount of data to be written. 546 | * @return qspi::ok if successful, a qspi error otherwise. 547 | */ 548 | qspi_impl::qspi_result_t 549 | qspi_impl::write (uint32_t address, uint8_t* buff, size_t count) 550 | { 551 | qspi_impl::qspi_result_t result = error; 552 | size_t in_block_count; 553 | 554 | if (pdevice_ != nullptr) 555 | { 556 | do 557 | { 558 | in_block_count = 0x100 - (address & 0xFF); 559 | if (in_block_count > count) 560 | { 561 | in_block_count = count; 562 | } 563 | else if (in_block_count == 0) 564 | { 565 | in_block_count = (count > 0x100) ? 0x100 : count; 566 | } 567 | if ((result = page_write (address, buff, in_block_count)) != ok) 568 | { 569 | break; 570 | } 571 | address += in_block_count; 572 | buff += in_block_count; 573 | count -= in_block_count; 574 | } 575 | while (count > 0); 576 | } 577 | 578 | return result; 579 | } 580 | 581 | /** 582 | * @brief Write a page of data to the flash (max. 256 bytes). 583 | * @param address: address of the page in flash. 584 | * @param buff: buffer of the source data. 585 | * @param count: number of bytes to be written (max 256). 586 | * @return qspi::ok if successful, a qspi error otherwise. 587 | */ 588 | qspi_impl::qspi_result_t 589 | qspi_impl::page_write (uint32_t address, uint8_t* buff, size_t count) 590 | { 591 | qspi_impl::qspi_result_t result = error; 592 | QSPI_CommandTypeDef sCommand; 593 | QSPI_AutoPollingTypeDef sConfig; 594 | 595 | // Initial command settings 596 | sCommand.AddressSize = QSPI_ADDRESS_24_BITS; 597 | sCommand.AlternateByteMode = QSPI_ALTERNATE_BYTES_NONE; 598 | sCommand.DdrMode = QSPI_DDR_MODE_DISABLE; 599 | sCommand.DdrHoldHalfCycle = QSPI_DDR_HHC_ANALOG_DELAY; 600 | sCommand.SIOOMode = QSPI_SIOO_INST_EVERY_CMD; 601 | sCommand.InstructionMode = QSPI_INSTRUCTION_4_LINES; 602 | sCommand.AddressMode = QSPI_ADDRESS_NONE; 603 | sCommand.DataMode = QSPI_DATA_NONE; 604 | sCommand.DummyCycles = 0; 605 | 606 | // Enable write 607 | sCommand.Instruction = WRITE_ENABLE; 608 | result = qspi_command (hqspi_, &sCommand, TIMEOUT); 609 | if (result == ok) 610 | { 611 | // Initiate write 612 | sCommand.Instruction = PAGE_PROGRAM; 613 | sCommand.AddressMode = QSPI_ADDRESS_4_LINES; 614 | sCommand.DataMode = QSPI_DATA_4_LINES; 615 | sCommand.Address = address; 616 | sCommand.NbData = count; 617 | result = qspi_command (hqspi_, &sCommand, TIMEOUT); 618 | if (result == ok) 619 | { 620 | /** 621 | * Clean the data cache to mitigate incoherence before DMA transfers 622 | * (DTCM RAM is not cached) 623 | */ 624 | if ((buff + count) >= (uint8_t*) SRAM1_BASE) 625 | { 626 | clean_dcache (buff, count); 627 | } 628 | result = (qspi_impl::qspi_result_t) HAL_QSPI_Transmit_DMA ( 629 | hqspi_, buff); 630 | if (result == ok) 631 | { 632 | if (semaphore_.timed_wait (TIMEOUT) == rtos::result::ok) 633 | { 634 | // Set auto-polling and wait for the event 635 | sCommand.AddressMode = QSPI_ADDRESS_NONE; 636 | sCommand.DataMode = QSPI_DATA_4_LINES; 637 | sCommand.Instruction = READ_STATUS_REGISTER; 638 | sConfig.Match = 0; 639 | sConfig.Mask = 1; 640 | sConfig.MatchMode = QSPI_MATCH_MODE_AND; 641 | sConfig.StatusBytesSize = 1; 642 | sConfig.Interval = 0x10; 643 | sConfig.AutomaticStop = QSPI_AUTOMATIC_STOP_ENABLE; 644 | result = 645 | (qspi_impl::qspi_result_t) HAL_QSPI_AutoPolling_IT ( 646 | hqspi_, &sCommand, &sConfig); 647 | if (result == ok) 648 | { 649 | result = 650 | (semaphore_.timed_wait (WRITE_TIMEOUT) 651 | == rtos::result::ok) ? ok : timeout; 652 | } 653 | } 654 | else 655 | { 656 | result = timeout; 657 | } 658 | } 659 | } 660 | } 661 | 662 | return result; 663 | } 664 | 665 | /** 666 | * @brief Erase a sector (4K), block (32K), large block (64K) or whole flash. 667 | * @param address: address of the block to be erased. 668 | * @param which: command to erase, can be either SECTOR_ERASE, BLOCK_32K_ERASE, 669 | * BLOCK_64K_ERASE or CHIP_ERASE. 670 | * @return qspi::ok if successful, or a qspi error otherwise. 671 | */ 672 | qspi_impl::qspi_result_t 673 | qspi_impl::erase (uint32_t address, uint8_t which) 674 | { 675 | qspi_impl::qspi_result_t result = error; 676 | QSPI_CommandTypeDef sCommand; 677 | QSPI_AutoPollingTypeDef sConfig; 678 | 679 | if (pdevice_ != nullptr) 680 | { 681 | // Initial command settings 682 | sCommand.AddressSize = QSPI_ADDRESS_24_BITS; 683 | sCommand.AlternateByteMode = QSPI_ALTERNATE_BYTES_NONE; 684 | sCommand.DdrMode = QSPI_DDR_MODE_DISABLE; 685 | sCommand.DdrHoldHalfCycle = QSPI_DDR_HHC_ANALOG_DELAY; 686 | sCommand.SIOOMode = QSPI_SIOO_INST_EVERY_CMD; 687 | sCommand.InstructionMode = QSPI_INSTRUCTION_4_LINES; 688 | sCommand.AddressMode = QSPI_ADDRESS_NONE; 689 | sCommand.DataMode = QSPI_DATA_NONE; 690 | sCommand.DummyCycles = 0; 691 | 692 | // Enable write 693 | sCommand.Instruction = WRITE_ENABLE; 694 | result = qspi_command (hqspi_, &sCommand, TIMEOUT); 695 | if (result == ok) 696 | { 697 | // Initiate erase 698 | sCommand.Instruction = which; 699 | sCommand.AddressMode = 700 | (which == CHIP_ERASE) ? QSPI_ADDRESS_NONE : // 701 | QSPI_ADDRESS_4_LINES; 702 | sCommand.DataMode = QSPI_DATA_NONE; 703 | sCommand.Address = address; 704 | result = qspi_command (hqspi_, &sCommand, TIMEOUT); 705 | if (result == ok) 706 | { 707 | // Set auto-polling and wait for the event 708 | sCommand.Instruction = READ_STATUS_REGISTER; 709 | sCommand.AddressMode = QSPI_ADDRESS_NONE; 710 | sCommand.DataMode = QSPI_DATA_4_LINES; 711 | sConfig.Match = 0; 712 | sConfig.Mask = 1; 713 | sConfig.MatchMode = QSPI_MATCH_MODE_AND; 714 | sConfig.StatusBytesSize = 1; 715 | sConfig.Interval = 0x10; 716 | sConfig.AutomaticStop = QSPI_AUTOMATIC_STOP_ENABLE; 717 | result = 718 | (qspi_impl::qspi_result_t) HAL_QSPI_AutoPolling_IT ( 719 | hqspi_, &sCommand, &sConfig); 720 | if (result == ok) 721 | { 722 | result = 723 | (semaphore_.timed_wait ( 724 | (which == CHIP_ERASE) ? CHIP_ERASE_TIMEOUT : // 725 | ERASE_TIMEOUT) == rtos::result::ok) ? 726 | ok : timeout; 727 | } 728 | } 729 | } 730 | } 731 | 732 | return result; 733 | } 734 | 735 | /** 736 | * @brief Read sector. 737 | * @param sector: sector number to read from. 738 | * @param buff: buffer to receive the data from flash. 739 | * @param count: number of bytes to read (buffer should be large enough). 740 | * @return qspi::ok if successful, or a qspi error otherwise. 741 | */ 742 | qspi_impl::qspi_result_t 743 | qspi_impl::read_sector (uint32_t sector, uint8_t* buff, size_t count) 744 | { 745 | return read (sector * pdevice_->sector_size, buff, count); 746 | } 747 | 748 | /** 749 | * @brief Write sector. 750 | * @param sector: sector number to write to. 751 | * @param buff: buffer containing the data to be written to flash. 752 | * @param count: number of bytes to be written. 753 | * @return qspi::ok if successful, or a qspi error otherwise. 754 | */ 755 | qspi_impl::qspi_result_t 756 | qspi_impl::write_sector (uint32_t sector, uint8_t* buff, size_t count) 757 | { 758 | return write (sector * pdevice_->sector_size, buff, count); 759 | } 760 | 761 | /** 762 | * @brief Erase sector. 763 | * @param sector: sector to be erased. 764 | * @return qspi::ok if successful, or a qspi error otherwise. 765 | */ 766 | qspi_impl::qspi_result_t 767 | qspi_impl::erase_sector (uint32_t sector) 768 | { 769 | return erase (sector * pdevice_->sector_size, SECTOR_ERASE); 770 | } 771 | 772 | /** 773 | * @brief Software reset the flash chip. 774 | * @return qspi::ok if successful, or a qspi error otherwise. 775 | */ 776 | qspi_impl::qspi_result_t 777 | qspi_impl::reset_chip (void) 778 | { 779 | qspi_impl::qspi_result_t result = busy; 780 | QSPI_CommandTypeDef sCommand; 781 | 782 | // Initial command settings 783 | sCommand.AddressSize = QSPI_ADDRESS_24_BITS; 784 | sCommand.AlternateByteMode = QSPI_ALTERNATE_BYTES_NONE; 785 | sCommand.DdrMode = QSPI_DDR_MODE_DISABLE; 786 | sCommand.DdrHoldHalfCycle = QSPI_DDR_HHC_ANALOG_DELAY; 787 | sCommand.SIOOMode = QSPI_SIOO_INST_EVERY_CMD; 788 | sCommand.InstructionMode = QSPI_INSTRUCTION_4_LINES; 789 | sCommand.AddressMode = QSPI_ADDRESS_NONE; 790 | sCommand.DataMode = QSPI_DATA_NONE; 791 | sCommand.DummyCycles = 0; 792 | 793 | // Enable reset 794 | sCommand.Instruction = RESET_ENABLE; 795 | result = qspi_command (hqspi_, &sCommand, TIMEOUT); 796 | if (result == ok) 797 | { 798 | // Send reset command 799 | sCommand.Instruction = RESET_DEVICE; 800 | result = qspi_command (hqspi_, &sCommand, TIMEOUT); 801 | } 802 | 803 | return result; 804 | } 805 | 806 | qspi_impl::qspi_result_t 807 | qspi_impl::qspi_command (QSPI_HandleTypeDef* hq, QSPI_CommandTypeDef* cmd, 808 | uint32_t timeout) 809 | { 810 | qspi_impl::qspi_result_t result = 811 | (qspi_impl::qspi_result_t) HAL_QSPI_Command (hq, cmd, timeout); 812 | 813 | if (result != ok) 814 | { 815 | /** 816 | * This is a workaround for the QSPI peripheral bug described in 817 | * the ST document ES0290 Rev 7, section 2.4.1. Even if not all 818 | * conditions apply to the description, the behavior is similar. 819 | * 820 | * Abort the QSPI operation, then retry 821 | */ 822 | hq->Instance->CR |= QUADSPI_CR_ABORT; 823 | hq->State = HAL_QSPI_STATE_READY; 824 | result = (qspi_impl::qspi_result_t) HAL_QSPI_Command (hq, cmd, 825 | timeout); 826 | } 827 | 828 | return result; 829 | } 830 | 831 | /** 832 | * @brief Return the memory type. 833 | * @return Pointer to a string representing the human readable memory type, or 834 | * null pointer if the system is not initialized. 835 | */ 836 | const char* 837 | qspi_impl::get_memory_type (void) 838 | { 839 | return (pdevice_ == nullptr) ? nullptr : pdevice_->device_name; 840 | } 841 | 842 | /** 843 | * @brief Return the sector size. 844 | * @return The sector size in bytes, zero if system is not initialized. 845 | */ 846 | size_t 847 | qspi_impl::get_sector_size (void) 848 | { 849 | return (pdevice_ == nullptr) ? 0 : pdevice_->sector_size; 850 | } 851 | 852 | /** 853 | * @brief Return the sectors count. 854 | * @return The sectors count, zero if system is not initialized. 855 | */ 856 | size_t 857 | qspi_impl::get_sector_count (void) 858 | { 859 | size_t size = 0; 860 | 861 | if (pdevice_ != nullptr) 862 | { 863 | size_t size = pdevice_->device_ID & 0xFF; 864 | size = (1 << size); 865 | return size / pdevice_->sector_size; 866 | } 867 | 868 | return size; 869 | } 870 | 871 | /** 872 | * @brief QSPI peripheral interrupt call-back. 873 | */ 874 | void 875 | qspi_impl::cb_event (void) 876 | { 877 | semaphore_.post (); 878 | } 879 | 880 | } /* namespace stm32f7 */ 881 | } /* namespace driver */ 882 | } /* namespace os */ 883 | 884 | #pragma GCC diagnostic pop 885 | 886 | -------------------------------------------------------------------------------- /src/qspi-micron.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * qspi-micron.cpp 3 | * 4 | * Copyright (c) 2016-2021 Lix N. Paulian (lix@paulian.net) 5 | * 6 | * Permission is hereby granted, free of charge, to any person 7 | * obtaining a copy of this software and associated documentation 8 | * files (the "Software"), to deal in the Software without 9 | * restriction, including without limitation the rights to use, 10 | * copy, modify, merge, publish, distribute, sublicense, and/or 11 | * sell copies of the Software, and to permit persons to whom 12 | * the Software is furnished to do so, subject to the following 13 | * conditions: 14 | * 15 | * The above copyright notice and this permission notice shall be 16 | * included in all copies or substantial portions of the Software. 17 | * 18 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 19 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 20 | * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 21 | * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 22 | * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 23 | * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 24 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 25 | * OTHER DEALINGS IN THE SOFTWARE. 26 | * 27 | * Created on: 31 Dec 2016 (LNP) 28 | */ 29 | 30 | /* 31 | * This file implements the specific basic low level functions to control 32 | * Micron QSPI flash devices (acquired from ST). 33 | */ 34 | 35 | #include 36 | #include 37 | 38 | #include "qspi-micron.h" 39 | #include "qspi-descr.h" 40 | 41 | namespace os 42 | { 43 | namespace driver 44 | { 45 | namespace stm32f7 46 | { 47 | 48 | /** 49 | * @brief Switch the flash chip to quad mode. 50 | * @return true if successful, false otherwise. 51 | */ 52 | qspi_impl::qspi_result_t 53 | qspi_micron::enter_quad_mode (qspi_impl* pq) 54 | { 55 | QSPI_CommandTypeDef sCommand; 56 | qspi_impl::qspi_result_t result = qspi_impl::busy; 57 | uint8_t datareg; 58 | 59 | // Initial command settings 60 | sCommand.AddressSize = QSPI_ADDRESS_24_BITS; 61 | sCommand.AlternateByteMode = QSPI_ALTERNATE_BYTES_NONE; 62 | sCommand.DdrMode = QSPI_DDR_MODE_DISABLE; 63 | sCommand.DdrHoldHalfCycle = QSPI_DDR_HHC_ANALOG_DELAY; 64 | sCommand.SIOOMode = QSPI_SIOO_INST_EVERY_CMD; 65 | sCommand.InstructionMode = QSPI_INSTRUCTION_1_LINE; 66 | sCommand.AddressMode = QSPI_ADDRESS_NONE; 67 | sCommand.DataMode = QSPI_DATA_NONE; 68 | sCommand.DummyCycles = 0; 69 | sCommand.NbData = 1; 70 | 71 | // Enable volatile write 72 | sCommand.Instruction = qspi_impl::WRITE_ENABLE; 73 | result = pq->qspi_command (pq->hqspi_, &sCommand, qspi_impl::TIMEOUT); 74 | if (result == qspi_impl::ok) 75 | { 76 | // Write enhanced volatile register 77 | sCommand.DataMode = QSPI_DATA_1_LINE; 78 | sCommand.Instruction = WRITE_VOLATILE_STATUS_REGISTER; 79 | result = pq->qspi_command (pq->hqspi_, &sCommand, 80 | qspi_impl::TIMEOUT); 81 | if (result == qspi_impl::ok) 82 | { 83 | // Compute dummy cycles 84 | datareg = (pq->pdevice_->dummy_cycles << 4); 85 | datareg |= 0xB; 86 | result = (qspi_impl::qspi_result_t) HAL_QSPI_Transmit ( 87 | pq->hqspi_, &datareg, qspi_impl::TIMEOUT); 88 | if (result == qspi_impl::ok) 89 | { 90 | // Enable write 91 | sCommand.DataMode = QSPI_DATA_NONE; 92 | sCommand.Instruction = qspi_impl::WRITE_ENABLE; 93 | result = pq->qspi_command (pq->hqspi_, &sCommand, 94 | qspi_impl::TIMEOUT); 95 | if (result == qspi_impl::ok) 96 | { 97 | // Set number of dummy cycles 98 | sCommand.DataMode = QSPI_DATA_1_LINE; 99 | sCommand.Instruction = 100 | WRITE_ENH_VOLATILE_STATUS_REGISTER; 101 | result = pq->qspi_command (pq->hqspi_, &sCommand, 102 | qspi_impl::TIMEOUT); 103 | if (result == qspi_impl::ok) 104 | { 105 | datareg = 0x6F; // Enable quad protocol 106 | result = 107 | (qspi_impl::qspi_result_t) HAL_QSPI_Transmit ( 108 | pq->hqspi_, &datareg, qspi_impl::TIMEOUT); 109 | if (result == qspi_impl::ok) 110 | { 111 | sCommand.DataMode = QSPI_DATA_NONE; 112 | sCommand.Instruction = ENTER_QUAD_MODE; 113 | result = pq->qspi_command (pq->hqspi_, 114 | &sCommand, 115 | qspi_impl::TIMEOUT); 116 | } 117 | } 118 | } 119 | } 120 | } 121 | } 122 | return result; 123 | } 124 | 125 | } /* namespace stm32f7 */ 126 | } /* namespace driver */ 127 | } /* namespace os */ 128 | 129 | -------------------------------------------------------------------------------- /src/qspi-micron.h: -------------------------------------------------------------------------------- 1 | /* 2 | * qspi-micron.h 3 | * 4 | * Copyright (c) 2016-2020 Lix N. Paulian (lix@paulian.net) 5 | * 6 | * Permission is hereby granted, free of charge, to any person 7 | * obtaining a copy of this software and associated documentation 8 | * files (the "Software"), to deal in the Software without 9 | * restriction, including without limitation the rights to use, 10 | * copy, modify, merge, publish, distribute, sublicense, and/or 11 | * sell copies of the Software, and to permit persons to whom 12 | * the Software is furnished to do so, subject to the following 13 | * conditions: 14 | * 15 | * The above copyright notice and this permission notice shall be 16 | * included in all copies or substantial portions of the Software. 17 | * 18 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 19 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 20 | * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 21 | * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 22 | * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 23 | * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 24 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 25 | * OTHER DEALINGS IN THE SOFTWARE. 26 | * 27 | * Created on: 31 Dec 2016 (LNP) 28 | */ 29 | 30 | #ifndef QSPI_MICRON_H_ 31 | #define QSPI_MICRON_H_ 32 | 33 | #include "qspi-flash.h" 34 | 35 | #if defined (__cplusplus) 36 | 37 | namespace os 38 | { 39 | namespace driver 40 | { 41 | namespace stm32f7 42 | { 43 | 44 | class qspi_micron : public qspi_intern 45 | { 46 | 47 | public: 48 | virtual qspi_impl::qspi_result_t 49 | enter_quad_mode (qspi_impl* pq) override; 50 | 51 | private: 52 | // Micron/ST specific commands 53 | static constexpr uint8_t READ_VOLATILE_STATUS_REGISTER = 0x85; 54 | static constexpr uint8_t READ_ENH_VOLATILE_STATUS_REGISTER = 0x65; 55 | static constexpr uint8_t WRITE_VOLATILE_STATUS_REGISTER = 0x81; 56 | static constexpr uint8_t WRITE_ENH_VOLATILE_STATUS_REGISTER = 0x61; 57 | static constexpr uint8_t ENTER_QUAD_MODE = 0x38; 58 | 59 | }; 60 | 61 | } /* namespace stm32f7 */ 62 | } /* namespace driver */ 63 | } /* namespace os */ 64 | 65 | #endif 66 | 67 | #endif /* QSPI_MICRON_H_ */ 68 | -------------------------------------------------------------------------------- /src/qspi-winbond.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * qspi-winbond.cpp 3 | * 4 | * Copyright (c) 2016-2021 Lix N. Paulian (lix@paulian.net) 5 | * 6 | * Permission is hereby granted, free of charge, to any person 7 | * obtaining a copy of this software and associated documentation 8 | * files (the "Software"), to deal in the Software without 9 | * restriction, including without limitation the rights to use, 10 | * copy, modify, merge, publish, distribute, sublicense, and/or 11 | * sell copies of the Software, and to permit persons to whom 12 | * the Software is furnished to do so, subject to the following 13 | * conditions: 14 | * 15 | * The above copyright notice and this permission notice shall be 16 | * included in all copies or substantial portions of the Software. 17 | * 18 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 19 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 20 | * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 21 | * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 22 | * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 23 | * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 24 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 25 | * OTHER DEALINGS IN THE SOFTWARE. 26 | * 27 | * Created on: 29 Dec 2016 (LNP) 28 | */ 29 | 30 | /* 31 | * This file implements the specific basic low level functions to control 32 | * Winbond QSPI flash devices. 33 | */ 34 | 35 | #include 36 | #include 37 | 38 | #include "qspi-winbond.h" 39 | #include "qspi-descr.h" 40 | 41 | namespace os 42 | { 43 | namespace driver 44 | { 45 | namespace stm32f7 46 | { 47 | 48 | /** 49 | * @brief Switch the flash chip to quad mode. 50 | * @return true if successful, false otherwise. 51 | */ 52 | qspi_impl::qspi_result_t 53 | qspi_winbond::enter_quad_mode (qspi_impl* pq) 54 | { 55 | QSPI_CommandTypeDef sCommand; 56 | qspi_impl::qspi_result_t result = qspi_impl::busy; 57 | uint8_t datareg; 58 | 59 | // Initial command settings 60 | sCommand.AddressSize = QSPI_ADDRESS_24_BITS; 61 | sCommand.AlternateByteMode = QSPI_ALTERNATE_BYTES_NONE; 62 | sCommand.DdrMode = QSPI_DDR_MODE_DISABLE; 63 | sCommand.DdrHoldHalfCycle = QSPI_DDR_HHC_ANALOG_DELAY; 64 | sCommand.SIOOMode = QSPI_SIOO_INST_EVERY_CMD; 65 | sCommand.InstructionMode = QSPI_INSTRUCTION_1_LINE; 66 | sCommand.AddressMode = QSPI_ADDRESS_NONE; 67 | sCommand.DataMode = QSPI_DATA_NONE; 68 | sCommand.DummyCycles = 0; 69 | sCommand.NbData = 1; 70 | 71 | // Enable volatile write 72 | sCommand.Instruction = VOLATILE_SR_WRITE_ENABLE; 73 | result = pq->qspi_command (pq->hqspi_, &sCommand, qspi_impl::TIMEOUT); 74 | if (result == qspi_impl::ok) 75 | { 76 | // Write status register 2 (enable Quad Mode) 77 | sCommand.DataMode = QSPI_DATA_1_LINE; 78 | sCommand.Instruction = WRITE_STATUS_REGISTER_2; 79 | result = pq->qspi_command (pq->hqspi_, &sCommand, 80 | qspi_impl::WRITE_TIMEOUT); 81 | if (result == qspi_impl::ok) 82 | { 83 | datareg = 2; 84 | result = (qspi_impl::qspi_result_t) HAL_QSPI_Transmit ( 85 | pq->hqspi_, &datareg, qspi_impl::TIMEOUT); 86 | if (result == qspi_impl::ok) 87 | { 88 | sCommand.DataMode = QSPI_DATA_NONE; 89 | sCommand.Instruction = ENTER_QUAD_MODE; 90 | result = pq->qspi_command (pq->hqspi_, &sCommand, 91 | qspi_impl::TIMEOUT); 92 | if (result == qspi_impl::ok) 93 | { 94 | sCommand.DataMode = QSPI_DATA_4_LINES; 95 | sCommand.InstructionMode = QSPI_INSTRUCTION_4_LINES; 96 | sCommand.Instruction = SET_READ_PARAMETERS; 97 | result = pq->qspi_command (pq->hqspi_, &sCommand, 98 | qspi_impl::TIMEOUT); 99 | if (result == qspi_impl::ok) 100 | { 101 | // Compute and set number of dummy cycles 102 | datareg = (pq->pdevice_->dummy_cycles / 2) - 1; 103 | datareg <<= 4; 104 | result = 105 | (qspi_impl::qspi_result_t) HAL_QSPI_Transmit ( 106 | pq->hqspi_, &datareg, qspi_impl::TIMEOUT); 107 | } 108 | } 109 | } 110 | } 111 | } 112 | return result; 113 | } 114 | 115 | } /* namespace stm32f7 */ 116 | } /* namespace driver */ 117 | } /* namespace os */ 118 | -------------------------------------------------------------------------------- /src/qspi-winbond.h: -------------------------------------------------------------------------------- 1 | /* 2 | * qspi-winbond.h 3 | * 4 | * Copyright (c) 2016-2020 Lix N. Paulian (lix@paulian.net) 5 | * 6 | * Permission is hereby granted, free of charge, to any person 7 | * obtaining a copy of this software and associated documentation 8 | * files (the "Software"), to deal in the Software without 9 | * restriction, including without limitation the rights to use, 10 | * copy, modify, merge, publish, distribute, sublicense, and/or 11 | * sell copies of the Software, and to permit persons to whom 12 | * the Software is furnished to do so, subject to the following 13 | * conditions: 14 | * 15 | * The above copyright notice and this permission notice shall be 16 | * included in all copies or substantial portions of the Software. 17 | * 18 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 19 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 20 | * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 21 | * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 22 | * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 23 | * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 24 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 25 | * OTHER DEALINGS IN THE SOFTWARE. 26 | * 27 | * Created on: 29 Dec 2016 (LNP) 28 | */ 29 | 30 | #ifndef QSPI_WINBOND_H_ 31 | #define QSPI_WINBOND_H_ 32 | 33 | #include "qspi-flash.h" 34 | 35 | #if defined (__cplusplus) 36 | 37 | namespace os 38 | { 39 | namespace driver 40 | { 41 | namespace stm32f7 42 | { 43 | 44 | class qspi_winbond : public qspi_intern 45 | { 46 | 47 | public: 48 | virtual qspi_impl::qspi_result_t 49 | enter_quad_mode (qspi_impl* pq) override; 50 | 51 | private: 52 | // Winbond specific commands 53 | static constexpr uint8_t VOLATILE_SR_WRITE_ENABLE = 0x50; 54 | static constexpr uint8_t READ_STATUS_REGISTER_2 = 0x35; 55 | static constexpr uint8_t WRITE_STATUS_REGISTER_2 = 0x31; 56 | static constexpr uint8_t READ_STATUS_REGISTER_3 = 0x15; 57 | static constexpr uint8_t WRITE_STATUS_REGISTER_3 = 0x11; 58 | static constexpr uint8_t ENTER_QUAD_MODE = 0x38; 59 | static constexpr uint8_t SET_READ_PARAMETERS = 0xC0; 60 | 61 | }; 62 | 63 | } /* namespace stm32f7 */ 64 | } /* namespace driver */ 65 | } /* namespace os */ 66 | 67 | #endif 68 | 69 | #endif /* QSPI_WINBOND_H_ */ 70 | -------------------------------------------------------------------------------- /test/test-chan-fatfs.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * test-chan-fatfs.cpp 3 | * 4 | * This code is based on code written originally by ChaN and adapted by 5 | * Liviu Ionescu. 6 | * 7 | * Created on: 31 Mar 2018 8 | */ 9 | 10 | #include 11 | #include 12 | 13 | #include 14 | #include 15 | #include 16 | #include 17 | 18 | #include "chan-fatfs/ff.h" /* Declarations of sector size */ 19 | #include "chan-fatfs/diskio.h" /* Declarations of disk functions */ 20 | 21 | #include 22 | #include 23 | #include 24 | #include 25 | 26 | #include "qspi-flash.h" 27 | #include "sysconfig.h" 28 | #include "test-chan-fatfs.h" 29 | 30 | using namespace os; 31 | using namespace os::driver::stm32f7; 32 | 33 | #if FS_ENABLED == true 34 | 35 | #if FILE_SYSTEM_TEST == true 36 | static DWORD 37 | pn (DWORD pns); 38 | 39 | int 40 | test_diskio (PDRV pdrv, UINT ncyc, DWORD* buf, UINT sz_buff); 41 | #endif 42 | 43 | #if CONSOLE_ON_VCP == false 44 | // Static manager 45 | os::posix::file_descriptors_manager descriptors_manager 46 | { 8 }; 47 | #endif 48 | 49 | // Explicit template instantiation. 50 | template class posix::block_device_lockable; 51 | using qspi = posix::block_device_lockable; 52 | 53 | os::rtos::mutex flash_mx 54 | { "flash_mx" }; 55 | 56 | // /dev/flash 57 | qspi flash 58 | { "flash", flash_mx, &hqspi }; 59 | 60 | /** 61 | * @brief Status match callback. 62 | * @param hqspi: QSPI handle 63 | * @retval None 64 | */ 65 | void 66 | HAL_QSPI_StatusMatchCallback (QSPI_HandleTypeDef* phqspi) 67 | { 68 | if (phqspi == &hqspi) 69 | { 70 | flash.impl ().cb_event (); 71 | } 72 | } 73 | 74 | /** 75 | * @brief Receive completed callback. 76 | * @param hqspi: QSPI handle 77 | * @retval None 78 | */ 79 | void 80 | HAL_QSPI_RxCpltCallback (QSPI_HandleTypeDef* phqspi) 81 | { 82 | if (phqspi == &hqspi) 83 | { 84 | flash.impl ().cb_event (); 85 | } 86 | } 87 | 88 | /** 89 | * @brief Transmit completed callback. 90 | * @param hqspi: QSPI handle 91 | * @retval None 92 | */ 93 | void 94 | HAL_QSPI_TxCpltCallback (QSPI_HandleTypeDef* phqspi) 95 | { 96 | if (phqspi == &hqspi) 97 | { 98 | flash.impl ().cb_event (); 99 | } 100 | } 101 | 102 | rtos::mutex mx_fat 103 | { "mx_fat" }; 104 | 105 | #ifdef DISCO 106 | 107 | posix::chan_fatfs_file_system_lockable fat_fs 108 | { "fat", flash, mx_fat }; 109 | 110 | #endif 111 | 112 | #ifdef M717 113 | 114 | // explicit template instantiation 115 | template class posix::block_device_partition_implementable<>; 116 | using partition = os::posix::block_device_partition_implementable<>; 117 | 118 | // instantiate the partitions 119 | // /dev/fat 120 | partition fat 121 | { "fat", flash }; 122 | 123 | // /dev/fifo 124 | partition fifo 125 | { "fifo", flash }; 126 | 127 | // /dev/config 128 | partition p_config 129 | { "config", flash }; 130 | 131 | // /dev/ro 132 | partition ro 133 | { "read-only", flash }; 134 | 135 | // /dev/log 136 | partition logp 137 | { "log", flash }; 138 | 139 | posix::chan_fatfs_file_system_lockable fat_fs 140 | { "fat-fs", fat, mx_fat }; 141 | 142 | /** 143 | * @brief Initialise all block devices, partitions, the log system and the 144 | * historians database. 145 | */ 146 | void 147 | init_block_devices (void) 148 | { 149 | posix::block_device::blknum_t bks = 0; 150 | 151 | // The number of blocks is known only after open(). 152 | if (flash.open () < 0) 153 | { 154 | os::trace::printf ("Failed to open the flash block device\n"); 155 | } 156 | else 157 | { 158 | bks = flash.blocks (); 159 | 160 | /* 161 | * Define partitions: 162 | * - FIFO partition -> 3 MBytes (768 blocks) 163 | * - Log partition -> ~1 MByte (247 blocks) 164 | * - Configuration partition -> 32 Kbytes (8 blocks) 165 | * - Read-only partition -> 4096 Bytes (1 block) 166 | * - Main partition for FAT -> the rest, i.e. 12 MBytes 167 | * (3072 blocks for 16 MB flash chips) 168 | */ 169 | 170 | static constexpr std::size_t fifo_size = 768; 171 | static constexpr std::size_t log_size = 247; 172 | static constexpr std::size_t config_size = 8; 173 | static constexpr std::size_t ro_size = 1; 174 | std::size_t fat_size = bks 175 | - (fifo_size + log_size + config_size + ro_size); 176 | 177 | // configure the partitions 178 | fat.configure (0, fat_size); 179 | 180 | fifo.configure (fat_size, fifo_size); 181 | p_config.configure (fat_size + fifo_size + log_size, config_size); 182 | ro.configure (fat_size + fifo_size + log_size + config_size, ro_size); 183 | logp.configure (fat_size + fifo_size, log_size); 184 | } 185 | } 186 | #endif 187 | 188 | #if FILE_SYSTEM_TEST == true 189 | 190 | uint8_t buff[4096 + 10]; 191 | 192 | int 193 | test_ff () 194 | { 195 | int rc; 196 | 197 | /* Check function/compatibility of the physical drive #0 */ 198 | rc = test_diskio (&flash, 3, (DWORD*) buff, sizeof buff); 199 | if (rc) 200 | { 201 | printf ( 202 | "Sorry the function/compatibility test failed. (rc=%d)\nFatFs will not work with this disk driver.\n", 203 | rc); 204 | } 205 | else 206 | { 207 | printf ("Congratulations! The disk driver works well.\n"); 208 | } 209 | 210 | return rc; 211 | } 212 | 213 | static DWORD 214 | pn ( /* Pseudo random number generator */ 215 | DWORD pns /* 0:Initialize, !0:Read */ 216 | ) 217 | { 218 | static DWORD lfsr; 219 | UINT n; 220 | 221 | if (pns) 222 | { 223 | lfsr = pns; 224 | for (n = 0; n < 32; n++) 225 | pn (0); 226 | } 227 | if (lfsr & 1) 228 | { 229 | lfsr >>= 1; 230 | lfsr ^= 0x80200003; 231 | } 232 | else 233 | { 234 | lfsr >>= 1; 235 | } 236 | return lfsr; 237 | } 238 | 239 | int 240 | test_diskio (PDRV pdrv, /* Physical drive number to be checked (all data on the drive will be lost) */ 241 | UINT ncyc, /* Number of test cycles */ 242 | DWORD* buf, /* Pointer to the working buffer */ 243 | UINT sz_buff /* Size of the working buffer in unit of byte */ 244 | ) 245 | { 246 | UINT n, cc, ns; 247 | DWORD sz_drv, lba, lba2, sz_eblk, pns = 1; 248 | WORD sz_sect; 249 | BYTE *pbuff = (BYTE*) buf; 250 | DSTATUS ds; 251 | DRESULT dr; 252 | 253 | printf ("test_diskio(%p, %u, 0x%08X, 0x%08X)\n", pdrv, ncyc, (UINT) buff, 254 | sz_buff); 255 | 256 | if (sz_buff < FF_MAX_SS + 4) 257 | { 258 | printf ("Insufficient work area to run program.\n"); 259 | return 1; 260 | } 261 | 262 | for (cc = 1; cc <= ncyc; cc++) 263 | { 264 | printf ("**** Test cycle %u of %u start ****\n", cc, ncyc); 265 | 266 | printf (" disk_initalize(%p)", pdrv); 267 | ds = disk_initialize (pdrv); 268 | if (ds & STA_NOINIT) 269 | { 270 | printf (" - failed.\n"); 271 | return 2; 272 | } 273 | else 274 | { 275 | printf (" - ok.\n"); 276 | } 277 | 278 | printf ("**** Get drive size ****\n"); 279 | printf (" disk_ioctl(%p, GET_SECTOR_COUNT, 0x%08X)", pdrv, 280 | (UINT) &sz_drv); 281 | sz_drv = 0; 282 | dr = disk_ioctl (pdrv, GET_SECTOR_COUNT, &sz_drv); 283 | if (dr == RES_OK) 284 | { 285 | printf (" - ok.\n"); 286 | } 287 | else 288 | { 289 | printf (" - failed.\n"); 290 | return 3; 291 | } 292 | if (sz_drv < 12) 293 | { 294 | printf ("Failed: Insufficient drive size to test.\n"); 295 | return 4; 296 | } 297 | printf (" Number of sectors on the drive %p is %lu.\n", pdrv, sz_drv); 298 | 299 | #if FF_MAX_SS != FF_MIN_SS 300 | printf("**** Get sector size ****\n"); 301 | printf(" disk_ioctl(%u, GET_SECTOR_SIZE, 0x%X)", pdrv, (UINT)&sz_sect); 302 | sz_sect = 0; 303 | dr = disk_ioctl(pdrv, GET_SECTOR_SIZE, &sz_sect); 304 | if (dr == RES_OK) 305 | { 306 | printf(" - ok.\n"); 307 | } 308 | else 309 | { 310 | printf(" - failed.\n"); 311 | return 5; 312 | } 313 | printf(" Size of sector is %u bytes.\n", sz_sect); 314 | #else 315 | sz_sect = FF_MAX_SS; 316 | #endif 317 | 318 | printf ("**** Get block size ****\n"); 319 | printf (" disk_ioctl(%p, GET_BLOCK_SIZE, 0x%X)", pdrv, (UINT) &sz_eblk); 320 | sz_eblk = 0; 321 | dr = disk_ioctl (pdrv, GET_BLOCK_SIZE, &sz_eblk); 322 | if (dr == RES_OK) 323 | { 324 | printf (" - ok.\n"); 325 | } 326 | else 327 | { 328 | printf (" - failed.\n"); 329 | } 330 | if (dr == RES_OK || sz_eblk >= 2) 331 | { 332 | printf (" Size of the erase block is %lu sectors.\n", sz_eblk); 333 | } 334 | else 335 | { 336 | printf (" Size of the erase block is unknown.\n"); 337 | } 338 | 339 | /* Single sector write test */ 340 | printf ("**** Single sector write test 1 ****\n"); 341 | lba = 0; 342 | for (n = 0, pn (pns); n < sz_sect; n++) 343 | pbuff[n] = (BYTE) pn (0); 344 | printf (" disk_write(%p, 0x%X, %lu, 1)", pdrv, (UINT) pbuff, lba); 345 | dr = disk_write (pdrv, pbuff, lba, 1); 346 | if (dr == RES_OK) 347 | { 348 | printf (" - ok.\n"); 349 | } 350 | else 351 | { 352 | printf (" - failed.\n"); 353 | return 6; 354 | } 355 | printf (" disk_ioctl(%p, CTRL_SYNC, NULL)", pdrv); 356 | dr = disk_ioctl (pdrv, CTRL_SYNC, 0); 357 | if (dr == RES_OK) 358 | { 359 | printf (" - ok.\n"); 360 | } 361 | else 362 | { 363 | printf (" - failed.\n"); 364 | return 7; 365 | } 366 | memset (pbuff, 0, sz_sect); 367 | printf (" disk_read(%p, 0x%X, %lu, 1)", pdrv, (UINT) pbuff, lba); 368 | dr = disk_read (pdrv, pbuff, lba, 1); 369 | if (dr == RES_OK) 370 | { 371 | printf (" - ok.\n"); 372 | } 373 | else 374 | { 375 | printf (" - failed.\n"); 376 | return 8; 377 | } 378 | for (n = 0, pn (pns); n < sz_sect && pbuff[n] == (BYTE) pn (0); n++) 379 | ; 380 | if (n == sz_sect) 381 | { 382 | printf (" Data matched.\n"); 383 | } 384 | else 385 | { 386 | printf ("Failed: Read data differs from the data written.\n"); 387 | return 10; 388 | } 389 | pns++; 390 | 391 | printf ("**** Multiple sector write test ****\n"); 392 | lba = 1; 393 | ns = sz_buff / sz_sect; 394 | if (ns > 4) 395 | ns = 4; 396 | for (n = 0, pn (pns); n < (UINT) (sz_sect * ns); n++) 397 | pbuff[n] = (BYTE) pn (0); 398 | printf (" disk_write(%p, 0x%X, %lu, %u)", pdrv, (UINT) pbuff, lba, ns); 399 | dr = disk_write (pdrv, pbuff, lba, ns); 400 | if (dr == RES_OK) 401 | { 402 | printf (" - ok.\n"); 403 | } 404 | else 405 | { 406 | printf (" - failed.\n"); 407 | return 11; 408 | } 409 | printf (" disk_ioctl(%p, CTRL_SYNC, NULL)", pdrv); 410 | dr = disk_ioctl (pdrv, CTRL_SYNC, 0); 411 | if (dr == RES_OK) 412 | { 413 | printf (" - ok.\n"); 414 | } 415 | else 416 | { 417 | printf (" - failed.\n"); 418 | return 12; 419 | } 420 | memset (pbuff, 0, sz_sect * ns); 421 | printf (" disk_read(%p, 0x%X, %lu, %u)", pdrv, (UINT) pbuff, lba, ns); 422 | dr = disk_read (pdrv, pbuff, lba, ns); 423 | if (dr == RES_OK) 424 | { 425 | printf (" - ok.\n"); 426 | } 427 | else 428 | { 429 | printf (" - failed.\n"); 430 | return 13; 431 | } 432 | for (n = 0, pn (pns); 433 | n < (UINT) (sz_sect * ns) && pbuff[n] == (BYTE) pn (0); n++) 434 | ; 435 | if (n == (UINT) (sz_sect * ns)) 436 | { 437 | printf (" Data matched.\n"); 438 | } 439 | else 440 | { 441 | printf ("Failed: Read data differs from the data written.\n"); 442 | return 14; 443 | } 444 | pns++; 445 | 446 | printf ("**** Single sector write test (misaligned address) ****\n"); 447 | lba = 5; 448 | for (n = 0, pn (pns); n < sz_sect; n++) 449 | pbuff[n + 3] = (BYTE) pn (0); 450 | printf (" disk_write(%p, 0x%X, %lu, 1)", pdrv, (UINT) (pbuff + 3), lba); 451 | dr = disk_write (pdrv, pbuff + 3, lba, 1); 452 | if (dr == RES_OK) 453 | { 454 | printf (" - ok.\n"); 455 | } 456 | else 457 | { 458 | printf (" - failed.\n"); 459 | return 15; 460 | } 461 | printf (" disk_ioctl(%p, CTRL_SYNC, NULL)", pdrv); 462 | dr = disk_ioctl (pdrv, CTRL_SYNC, 0); 463 | if (dr == RES_OK) 464 | { 465 | printf (" - ok.\n"); 466 | } 467 | else 468 | { 469 | printf (" - failed.\n"); 470 | return 16; 471 | } 472 | memset (pbuff + 5, 0, sz_sect); 473 | printf (" disk_read(%p, 0x%X, %lu, 1)", pdrv, (UINT) (pbuff + 5), lba); 474 | dr = disk_read (pdrv, pbuff + 5, lba, 1); 475 | if (dr == RES_OK) 476 | { 477 | printf (" - ok.\n"); 478 | } 479 | else 480 | { 481 | printf (" - failed.\n"); 482 | return 17; 483 | } 484 | for (n = 0, pn (pns); n < sz_sect && pbuff[n + 5] == (BYTE) pn (0); n++) 485 | ; 486 | if (n == sz_sect) 487 | { 488 | printf (" Data matched.\n"); 489 | } 490 | else 491 | { 492 | printf ("Failed: Read data differs from the data written.\n"); 493 | return 18; 494 | } 495 | pns++; 496 | 497 | printf ("**** 4GB barrier test ****\n"); 498 | if (sz_drv >= 128 + 0x80000000 / (sz_sect / 2)) 499 | { 500 | lba = 6; 501 | lba2 = lba + 0x80000000 / (sz_sect / 2); 502 | for (n = 0, pn (pns); n < (UINT) (sz_sect * 2); n++) 503 | pbuff[n] = (BYTE) pn (0); 504 | printf (" disk_write(%p, 0x%X, %lu, 1)", pdrv, (UINT) pbuff, lba); 505 | dr = disk_write (pdrv, pbuff, lba, 1); 506 | if (dr == RES_OK) 507 | { 508 | printf (" - ok.\n"); 509 | } 510 | else 511 | { 512 | printf (" - failed.\n"); 513 | return 19; 514 | } 515 | printf (" disk_write(%p, 0x%X, %lu, 1)", pdrv, 516 | (UINT) (pbuff + sz_sect), lba2); 517 | dr = disk_write (pdrv, pbuff + sz_sect, lba2, 1); 518 | if (dr == RES_OK) 519 | { 520 | printf (" - ok.\n"); 521 | } 522 | else 523 | { 524 | printf (" - failed.\n"); 525 | return 20; 526 | } 527 | printf (" disk_ioctl(%p, CTRL_SYNC, NULL)", pdrv); 528 | dr = disk_ioctl (pdrv, CTRL_SYNC, 0); 529 | if (dr == RES_OK) 530 | { 531 | printf (" - ok.\n"); 532 | } 533 | else 534 | { 535 | printf (" - failed.\n"); 536 | return 21; 537 | } 538 | memset (pbuff, 0, sz_sect * 2); 539 | printf (" disk_read(%p, 0x%X, %lu, 1)", pdrv, (UINT) pbuff, lba); 540 | dr = disk_read (pdrv, pbuff, lba, 1); 541 | if (dr == RES_OK) 542 | { 543 | printf (" - ok.\n"); 544 | } 545 | else 546 | { 547 | printf (" - failed.\n"); 548 | return 22; 549 | } 550 | printf (" disk_read(%p, 0x%X, %lu, 1)", pdrv, 551 | (UINT) (pbuff + sz_sect), lba2); 552 | dr = disk_read (pdrv, pbuff + sz_sect, lba2, 1); 553 | if (dr == RES_OK) 554 | { 555 | printf (" - ok.\n"); 556 | } 557 | else 558 | { 559 | printf (" - failed.\n"); 560 | return 23; 561 | } 562 | for (n = 0, pn (pns); 563 | pbuff[n] == (BYTE) pn (0) && n < (UINT) (sz_sect * 2); n++) 564 | ; 565 | if (n == (UINT) (sz_sect * 2)) 566 | { 567 | printf (" Data matched.\n"); 568 | } 569 | else 570 | { 571 | printf ("Failed: Read data differs from the data written.\n"); 572 | return 24; 573 | } 574 | } 575 | else 576 | { 577 | printf (" Test skipped.\n"); 578 | } 579 | pns++; 580 | 581 | printf (" disk_deinitalize(%p)", pdrv); 582 | ds = disk_deinitialize (pdrv); 583 | if (ds & STA_NOINIT) 584 | { 585 | printf (" - failed.\n"); 586 | return 2; 587 | } 588 | else 589 | { 590 | printf (" - ok.\n"); 591 | } 592 | 593 | printf ("**** Test cycle %u of %u completed ****\n\n", cc, ncyc); 594 | } 595 | 596 | return 0; 597 | } 598 | 599 | #endif // FILE_SYSTEM_TEST 600 | 601 | #endif // FS_ENABLED 602 | -------------------------------------------------------------------------------- /test/test-chan-fatfs.h: -------------------------------------------------------------------------------- 1 | /* 2 | * test-chan-fatfs.h 3 | * 4 | * Created on: 31 Mar 2018 5 | */ 6 | 7 | #ifndef TEST_TEST_CHAN_FATFS_H_ 8 | #define TEST_TEST_CHAN_FATFS_H_ 9 | 10 | void 11 | init_block_devices (void); 12 | 13 | int 14 | test_ff (); 15 | 16 | 17 | #endif /* TEST_TEST_CHAN_FATFS_H_ */ 18 | -------------------------------------------------------------------------------- /test/test-qspi-c-api.c: -------------------------------------------------------------------------------- 1 | /* 2 | * test-qspi-c-api.c 3 | * 4 | * Copyright (c) 2017-2020 Lix N. Paulian (lix@paulian.net) 5 | * 6 | * Permission is hereby granted, free of charge, to any person 7 | * obtaining a copy of this software and associated documentation 8 | * files (the "Software"), to deal in the Software without 9 | * restriction, including without limitation the rights to use, 10 | * copy, modify, merge, publish, distribute, sublicense, and/or 11 | * sell copies of the Software, and to permit persons to whom 12 | * the Software is furnished to do so, subject to the following 13 | * conditions: 14 | * 15 | * The above copyright notice and this permission notice shall be 16 | * included in all copies or substantial portions of the Software. 17 | * 18 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 19 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 20 | * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 21 | * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 22 | * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 23 | * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 24 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 25 | * OTHER DEALINGS IN THE SOFTWARE. 26 | * 27 | * Created on: 6 Feb 2017 (LNP) 28 | */ 29 | 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | 36 | #include "qspi-flash-c-api.h" 37 | #include "test-qspi-c-api.h" 38 | #include "test-qspi-config.h" 39 | 40 | #if TEST_CPLUSPLUS_API == false 41 | 42 | extern QSPI_HandleTypeDef hqspi; 43 | qspi_t* qspi_instance = NULL; 44 | 45 | /** 46 | * @brief Status match callback. 47 | * @param hqspi: QSPI handle 48 | * @retval None 49 | */ 50 | void 51 | HAL_QSPI_StatusMatchCallback ( 52 | QSPI_HandleTypeDef* hqspi __attribute__ ((unused))) 53 | { 54 | if (qspi_instance != NULL) 55 | { 56 | qspi_event_cb (qspi_instance); 57 | } 58 | } 59 | 60 | /** 61 | * @brief Receive completed callback. 62 | * @param hqspi: QSPI handle 63 | * @retval None 64 | */ 65 | void 66 | HAL_QSPI_RxCpltCallback (QSPI_HandleTypeDef* hqspi __attribute__ ((unused))) 67 | { 68 | if (qspi_instance != NULL) 69 | { 70 | qspi_event_cb (qspi_instance); 71 | } 72 | } 73 | 74 | /** 75 | * @brief Transmit completed callback. 76 | * @param hqspi: QSPI handle 77 | * @retval None 78 | */ 79 | void 80 | HAL_QSPI_TxCpltCallback (QSPI_HandleTypeDef* hqspi __attribute__ ((unused))) 81 | { 82 | if (qspi_instance != NULL) 83 | { 84 | qspi_event_cb (qspi_instance); 85 | } 86 | } 87 | 88 | /** 89 | * @brief This is a test function that exercises the qspi driver. 90 | */ 91 | void 92 | test_qspi (void) 93 | { 94 | int i; 95 | uint8_t* pf = (uint8_t*) 0x90000000; // memory-mapped flash address 96 | int sector_size; 97 | int sector_count; 98 | 99 | if ((qspi_instance = qspi_new (&hqspi)) != NULL) 100 | { 101 | do 102 | { 103 | // read memory parameters and initialize system 104 | if (qspi_initialize (qspi_instance) != qspi_ok) 105 | { 106 | trace_printf ("Failed to initialize\n"); 107 | break; 108 | } 109 | 110 | sector_size = qspi_get_sector_size (qspi_instance); 111 | sector_count = qspi_get_sector_count (qspi_instance); 112 | uint8_t version_major, version_minor, version_patch; 113 | 114 | qspi_get_version (qspi_instance, &version_major, &version_minor, 115 | &version_patch); 116 | trace_printf ("Driver version: %d.%d.%d\n", version_major, 117 | version_minor, version_patch); 118 | trace_printf ("Manufacturer: %s, type: %s, sector size: %d bytes, " 119 | "sector count: %d\n", 120 | qspi_get_manufacturer (qspi_instance), 121 | qspi_get_memory_type (qspi_instance), sector_size, 122 | sector_count); 123 | 124 | // switch mode to memory mapped 125 | if (qspi_enter_mem_mapped (qspi_instance) != qspi_ok) 126 | { 127 | trace_printf ("Failed enter memory mapped mode\n"); 128 | break; 129 | } 130 | trace_printf ("Entered memory mapped mode\n"); 131 | 132 | // check if flash is erased 133 | for (i = 0; i < (sector_count * sector_size); i++, pf++) 134 | if (*pf != 0xFF) 135 | break; 136 | trace_printf ("Checked if flash is erased\n"); 137 | 138 | if (qspi_exit_mem_mapped (qspi_instance) != qspi_ok) 139 | { 140 | trace_printf ("Failed to exit from memory mapped mode\n"); 141 | break; 142 | } 143 | else 144 | { 145 | trace_printf ("Memory mapped mode switched off\n"); 146 | } 147 | 148 | // if not clear, erase whole flash chip 149 | if (i < (sector_count * sector_size)) 150 | { 151 | trace_printf ( 152 | "Flash not empty, trying to erase (it will take some time...)\n"); 153 | if (qspi_erase_chip (qspi_instance) != qspi_ok) 154 | { 155 | trace_printf ("Failed to erase flash chip\n"); 156 | break; 157 | } 158 | trace_printf ("Erased\n"); 159 | } 160 | 161 | // get two RAM buffers 162 | uint8_t* pw = malloc (sector_size); 163 | uint8_t* pr = malloc (sector_size); 164 | int j; 165 | 166 | trace_printf ("Write/read test started...\n"); 167 | if (pw && pr) 168 | { 169 | // generate a random block of data 170 | srand (0xBABA); 171 | for (j = 0; j < sector_size; j++) 172 | { 173 | #if TEST_VERBOSE == true 174 | trace_printf ("Test block #%5d\n", j); 175 | #endif 176 | for (i = 0; i < sector_size; i++) 177 | pw[i] = (uint8_t) random (); 178 | 179 | // write block 180 | if (qspi_write_sector (qspi_instance, j, pw, sector_size) 181 | != qspi_ok) 182 | { 183 | trace_printf ("Block write error (%d)\n", j); 184 | break; 185 | } 186 | memset (pr, 0xAA, sector_size); 187 | // read block 188 | if (qspi_read_sector (qspi_instance, j, pr, sector_size) 189 | != qspi_ok) 190 | { 191 | trace_printf ("Block read error\n"); 192 | break; 193 | } 194 | // compare data 195 | if (memcmp (pw, pr, sector_size) != 0) 196 | { 197 | trace_printf ("Compare error at block %d\n", j); 198 | break; 199 | } 200 | } 201 | 202 | // done, clean-up and exit 203 | free (pr); 204 | free (pw); 205 | if (j == sector_count) 206 | { 207 | trace_printf ("Flash test passed\n"); 208 | } 209 | } 210 | else 211 | trace_printf ("Out of memory\n"); 212 | } 213 | while (false); 214 | 215 | if (qspi_sleep (qspi_instance, true) != qspi_ok) 216 | { 217 | trace_printf ("Failed to switch flash chip into deep sleep\n"); 218 | } 219 | else 220 | { 221 | trace_printf ("Flash chip successfully switched to deep sleep\n"); 222 | } 223 | 224 | qspi_delete (qspi_instance); 225 | } 226 | else 227 | trace_printf ("Could not create qspi instance\n"); 228 | } 229 | 230 | #endif 231 | -------------------------------------------------------------------------------- /test/test-qspi-c-api.h: -------------------------------------------------------------------------------- 1 | /* 2 | * test-qspi-c-api.h 3 | * 4 | * Copyright (c) 2017-2020 Lix N. Paulian (lix@paulian.net) 5 | * 6 | * Permission is hereby granted, free of charge, to any person 7 | * obtaining a copy of this software and associated documentation 8 | * files (the "Software"), to deal in the Software without 9 | * restriction, including without limitation the rights to use, 10 | * copy, modify, merge, publish, distribute, sublicense, and/or 11 | * sell copies of the Software, and to permit persons to whom 12 | * the Software is furnished to do so, subject to the following 13 | * conditions: 14 | * 15 | * The above copyright notice and this permission notice shall be 16 | * included in all copies or substantial portions of the Software. 17 | * 18 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 19 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 20 | * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 21 | * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 22 | * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 23 | * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 24 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 25 | * OTHER DEALINGS IN THE SOFTWARE. 26 | * 27 | * Created on: 6 Feb 2017 (LNP) 28 | */ 29 | 30 | #ifndef TEST_TEST_QSPI_C_API_H_ 31 | #define TEST_TEST_QSPI_C_API_H_ 32 | 33 | #include "test-qspi-config.h" 34 | 35 | #ifdef __cplusplus 36 | extern "C" 37 | { 38 | #endif 39 | 40 | #if TEST_CPLUSPLUS_API == false 41 | 42 | void 43 | test_qspi (void); 44 | 45 | #endif 46 | 47 | #ifdef __cplusplus 48 | } 49 | #endif 50 | 51 | #endif /* TEST_TEST_QSPI_C_API_H_ */ 52 | -------------------------------------------------------------------------------- /test/test-qspi-config.h: -------------------------------------------------------------------------------- 1 | /* 2 | * test-qspi-config.h 3 | * 4 | * Copyright (c) 2017-2020 Lix N. Paulian (lix@paulian.net) 5 | * 6 | * Permission is hereby granted, free of charge, to any person 7 | * obtaining a copy of this software and associated documentation 8 | * files (the "Software"), to deal in the Software without 9 | * restriction, including without limitation the rights to use, 10 | * copy, modify, merge, publish, distribute, sublicense, and/or 11 | * sell copies of the Software, and to permit persons to whom 12 | * the Software is furnished to do so, subject to the following 13 | * conditions: 14 | * 15 | * The above copyright notice and this permission notice shall be 16 | * included in all copies or substantial portions of the Software. 17 | * 18 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 19 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 20 | * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 21 | * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 22 | * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 23 | * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 24 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 25 | * OTHER DEALINGS IN THE SOFTWARE. 26 | * 27 | * Created on: 25 Feb 2017 (LNP) 28 | */ 29 | 30 | #ifndef TEST_TEST_QSPI_CONFIG_H_ 31 | #define TEST_TEST_QSPI_CONFIG_H_ 32 | 33 | #ifdef __cplusplus 34 | extern "C" 35 | { 36 | #endif 37 | 38 | #define TEST_CPLUSPLUS_API true // change to false to run the C API 39 | #define TEST_VERBOSE false 40 | 41 | #ifdef __cplusplus 42 | } 43 | #endif 44 | 45 | #endif /* TEST_TEST_QSPI_CONFIG_H_ */ 46 | -------------------------------------------------------------------------------- /test/test-qspi.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * test-qspi.cpp 3 | * 4 | * Copyright (c) 2016-2020 Lix N. Paulian (lix@paulian.net) 5 | * 6 | * Permission is hereby granted, free of charge, to any person 7 | * obtaining a copy of this software and associated documentation 8 | * files (the "Software"), to deal in the Software without 9 | * restriction, including without limitation the rights to use, 10 | * copy, modify, merge, publish, distribute, sublicense, and/or 11 | * sell copies of the Software, and to permit persons to whom 12 | * the Software is furnished to do so, subject to the following 13 | * conditions: 14 | * 15 | * The above copyright notice and this permission notice shall be 16 | * included in all copies or substantial portions of the Software. 17 | * 18 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 19 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 20 | * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 21 | * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 22 | * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 23 | * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 24 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 25 | * OTHER DEALINGS IN THE SOFTWARE. 26 | * 27 | * Created on: 8 Dec 2016 (LNP) 28 | */ 29 | 30 | /* 31 | * Test the qspi driver functionality. 32 | */ 33 | 34 | #include 35 | #include 36 | #include 37 | #include 38 | #include 39 | #include 40 | 41 | #include "sysconfig.h" 42 | #include "qspi-flash.h" 43 | #include "test-qspi.h" 44 | #include "test-qspi-config.h" 45 | 46 | #if defined M717 47 | #include "io.h" 48 | #endif 49 | 50 | #if QSPI_TEST == true 51 | #if TEST_CPLUSPLUS_API == true 52 | 53 | extern "C" 54 | { 55 | extern QSPI_HandleTypeDef hqspi; 56 | } 57 | 58 | using namespace os; 59 | using namespace os::driver::stm32f7; 60 | 61 | // Static manager 62 | os::posix::file_descriptors_manager descriptors_manager 63 | { 8 }; 64 | 65 | // Explicit template instantiation. 66 | template class posix::block_device_lockable; 67 | using qspi = posix::block_device_lockable; 68 | 69 | os::rtos::mutex flash_mx 70 | { "flash_mx" }; 71 | 72 | qspi flash 73 | { "flash", flash_mx, &hqspi }; 74 | 75 | /** 76 | * @brief Status match callback. 77 | * @param hqspi: QSPI handle 78 | * @retval None 79 | */ 80 | void 81 | HAL_QSPI_StatusMatchCallback (QSPI_HandleTypeDef* phqspi) 82 | { 83 | if (phqspi == &hqspi) 84 | { 85 | flash.impl ().cb_event (); 86 | } 87 | } 88 | 89 | /** 90 | * @brief Receive completed callback. 91 | * @param hqspi: QSPI handle 92 | * @retval None 93 | */ 94 | void 95 | HAL_QSPI_RxCpltCallback (QSPI_HandleTypeDef* phqspi) 96 | { 97 | if (phqspi == &hqspi) 98 | { 99 | flash.impl ().cb_event (); 100 | } 101 | } 102 | 103 | /** 104 | * @brief Transmit completed callback. 105 | * @param hqspi: QSPI handle 106 | * @retval None 107 | */ 108 | void 109 | HAL_QSPI_TxCpltCallback (QSPI_HandleTypeDef* phqspi) 110 | { 111 | if (phqspi == &hqspi) 112 | { 113 | flash.impl ().cb_event (); 114 | } 115 | } 116 | 117 | /** 118 | * @brief This is a test function that exercises the qspi driver. 119 | */ 120 | void 121 | test_qspi (void) 122 | { 123 | size_t sector_size; 124 | posix::block_device::blknum_t sector_count; 125 | rtos::clock::timestamp_t total_write = 0; 126 | rtos::clock::timestamp_t total_read = 0; 127 | 128 | stopwatch sw 129 | { }; 130 | 131 | do 132 | { 133 | #if FLASH_LOW_LEVEL_TEST == false 134 | os::posix::block_device* blk_dev; 135 | 136 | blk_dev = 137 | static_cast (posix::open ("/dev/flash", 0)); 138 | 139 | sector_size = blk_dev->block_physical_size_bytes (); 140 | sector_count = blk_dev->blocks (); 141 | 142 | uint8_t* pw = new uint8_t[sector_size]; 143 | uint8_t* pr = new uint8_t[sector_size]; 144 | 145 | if (pw && pr) 146 | { 147 | trace::printf ("Write %d blocks...\n", sector_count); 148 | 149 | // generate a random block of data 150 | srand (0xBABA); 151 | posix::block_device::blknum_t sector; 152 | 153 | for (sector = 0; sector < sector_count; sector++) 154 | { 155 | for (size_t i = 0; i < sector_size; i++) 156 | { 157 | pw[i] = (uint8_t) random (); 158 | } 159 | 160 | // write block 161 | sw.start (); 162 | size_t count = blk_dev->write_block (pw, sector, 1); 163 | if (count == 0) 164 | { 165 | trace::printf ("Block write error (%d)\n", sector); 166 | break; 167 | } 168 | total_write += sw.stop (); 169 | } 170 | 171 | if (sector == sector_count) 172 | { 173 | trace::printf ("Read and verify...\n"); 174 | 175 | srand (0xBABA); 176 | for (sector = 0; sector < sector_count; sector++) 177 | { 178 | for (size_t i = 0; i < sector_size; i++) 179 | { 180 | pw[i] = (uint8_t) random (); 181 | } 182 | 183 | // read block 184 | sw.start (); 185 | size_t count = blk_dev->read_block (pr, sector, 1); 186 | if (count == 0) 187 | { 188 | trace::printf ("Block read error (%d)\n", sector); 189 | break; 190 | } 191 | total_read += sw.stop (); 192 | 193 | // compare data 194 | if (memcmp (pw, pr, sector_size) != 0) 195 | { 196 | trace::printf ("Compare error at block %d\n", sector); 197 | break; 198 | } 199 | } 200 | if (sector == sector_count) 201 | { 202 | trace::printf ("Test passed\n"); 203 | } 204 | } 205 | } 206 | 207 | blk_dev->close (); 208 | 209 | #else 210 | uint32_t i; 211 | uint8_t* pf = (uint8_t*) 0x90000000; // memory-mapped flash address 212 | 213 | // read memory parameters and initialize system 214 | if (flash.impl ().initialize () != qspi_impl::ok) 215 | { 216 | trace::printf ("Failed to initialize\n"); 217 | break; 218 | } 219 | 220 | sector_size = flash.impl ().get_sector_size (); 221 | sector_count = flash.impl ().get_sector_count (); 222 | uint8_t version_major, version_minor, version_patch; 223 | 224 | flash.impl ().get_version (version_major, version_minor, version_patch); 225 | trace::printf ("QSPI driver version: %d.%d.%d\n", version_major, 226 | version_minor, version_patch); 227 | trace::printf ( 228 | "Flash chip manufacturer: %s, type: %s, sector size: %d bytes, " 229 | "sector count: %d\n", 230 | flash.impl ().get_manufacturer (), flash.impl ().get_memory_type (), 231 | sector_size, sector_count); 232 | 233 | // switch mode to memory mapped 234 | if (flash.impl ().enter_mem_mapped () != qspi_impl::ok) 235 | { 236 | trace::printf ("Failed enter memory mapped mode\n"); 237 | break; 238 | } 239 | trace::printf ("Entered memory mapped mode\n"); 240 | 241 | // check if flash is erased 242 | sw.start (); 243 | for (i = 0; i < (sector_count * sector_size); i++, pf++) 244 | { 245 | if (*pf != 0xFF) 246 | { 247 | break; 248 | } 249 | } 250 | trace::printf ("Checked if flash is erased in %.3f ms (%d)\n", 251 | sw.stop () / (float) 1000, i); 252 | 253 | if (flash.impl ().exit_mem_mapped () != qspi_impl::ok) 254 | { 255 | trace::printf ("Failed to exit from memory mapped mode\n"); 256 | break; 257 | } 258 | else 259 | { 260 | trace::printf ("Memory mapped mode switched off\n"); 261 | } 262 | 263 | // if not clear, erase whole flash chip 264 | if (i < (sector_count * sector_size)) 265 | { 266 | trace::printf ( 267 | "Flash not empty, trying to erase (it will take some time...)\n"); 268 | sw.start (); 269 | if (flash.impl ().erase_chip () != qspi_impl::ok) 270 | { 271 | trace::printf ("Failed to erase flash chip\n"); 272 | break; 273 | } 274 | trace::printf ("Erased in %.2f s\n", sw.stop () / (float) 1000000); 275 | } 276 | 277 | // get two RAM buffers 278 | uint8_t* pw = new uint8_t[sector_size]; 279 | uint8_t* pr = new uint8_t[sector_size]; 280 | uint32_t j; 281 | 282 | trace::printf ("Write/read test started...\n"); 283 | if (pw && pr) 284 | { 285 | // generate a random block of data 286 | srand (0xBABA); 287 | for (j = 0; j < sector_size; j++) 288 | { 289 | #if TEST_VERBOSE == true 290 | trace::printf ("Test block #%5d\n", j); 291 | #endif 292 | for (i = 0; i < sector_size; i++) 293 | { 294 | pw[i] = (uint8_t) random (); 295 | } 296 | 297 | // write block 298 | sw.start (); 299 | if (flash.impl ().write_sector (j, pw, sector_size) 300 | != qspi_impl::ok) 301 | { 302 | trace::printf ("Block write error (%d)\n", j); 303 | break; 304 | } 305 | total_write += sw.stop (); 306 | memset (pr, 0xAA, sector_size); 307 | 308 | // read block 309 | sw.start (); 310 | if (flash.impl ().read_sector (j, pr, sector_size) 311 | != qspi_impl::ok) 312 | { 313 | trace::printf ("Block read error\n"); 314 | break; 315 | } 316 | total_read += sw.stop (); 317 | 318 | // compare data 319 | #if TEST_VERBOSE == true 320 | size_t k; 321 | for (k = 0; k < sector_size; k++) 322 | { 323 | if (*(pr + k) != *(pw + k)) 324 | { 325 | trace::printf ("Compare error at block %d, count %d\n", j, 326 | k); 327 | for (i = 0; i < 16; i++) 328 | trace::printf ("%02X<->%02X ", *(pw + k + i), 329 | *(pr + k + i)); 330 | trace::putchar ('\n'); 331 | break; 332 | } 333 | } 334 | if (k != sector_size) 335 | break; 336 | #else 337 | if (memcmp (pw, pr, sector_size) != 0) 338 | { 339 | trace::printf ("Compare error at block %d\n", j); 340 | break; 341 | } 342 | #endif 343 | } 344 | 345 | // done, clean-up and exit 346 | delete (pr); 347 | delete (pw); 348 | if (j == sector_count) 349 | { 350 | trace::printf ( 351 | "Flash test passed\nTotal write time %.2f s, " 352 | "total read time %.2f s\n" 353 | "Avg. sector write time %.2f ms, avg. sector read time %.2f ms\n", 354 | total_write / (float) 1000000, total_read / (float) 1000000, 355 | (total_write / sector_count) / (float) 1000, 356 | (total_read / sector_count) / (float) 1000); 357 | } 358 | } 359 | else 360 | trace::printf ("Out of memory\n"); 361 | #endif 362 | } 363 | while (false); 364 | 365 | if (flash.impl ().sleep (true) != qspi_impl::ok) 366 | { 367 | trace::printf ("Failed to switch flash chip into deep sleep\n"); 368 | } 369 | else 370 | { 371 | trace::printf ("Flash chip successfully switched to deep sleep\n"); 372 | } 373 | 374 | trace::printf ("Exiting flash tests.\n"); 375 | } 376 | 377 | #endif // TEST_CPLUSPLUS_API 378 | #endif // TEST_QSPI 379 | 380 | -------------------------------------------------------------------------------- /test/test-qspi.h: -------------------------------------------------------------------------------- 1 | /* 2 | * test-qspi.h 3 | * 4 | * Copyright (c) 2016-2020 Lix N. Paulian (lix@paulian.net) 5 | * 6 | * Permission is hereby granted, free of charge, to any person 7 | * obtaining a copy of this software and associated documentation 8 | * files (the "Software"), to deal in the Software without 9 | * restriction, including without limitation the rights to use, 10 | * copy, modify, merge, publish, distribute, sublicense, and/or 11 | * sell copies of the Software, and to permit persons to whom 12 | * the Software is furnished to do so, subject to the following 13 | * conditions: 14 | * 15 | * The above copyright notice and this permission notice shall be 16 | * included in all copies or substantial portions of the Software. 17 | * 18 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 19 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 20 | * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 21 | * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 22 | * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 23 | * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 24 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 25 | * OTHER DEALINGS IN THE SOFTWARE. 26 | * 27 | * Created on: 8 Dec 2016 (LNP) 28 | */ 29 | 30 | #ifndef TEST_TEST_QSPI_H_ 31 | #define TEST_TEST_QSPI_H_ 32 | 33 | #include "test-qspi-config.h" 34 | 35 | #if TEST_CPLUSPLUS_API == true 36 | 37 | void 38 | test_qspi (void); 39 | 40 | #endif 41 | 42 | class stopwatch 43 | { 44 | public: 45 | 46 | stopwatch () = default; 47 | 48 | void 49 | start (void); 50 | 51 | os::rtos::clock::timestamp_t 52 | stop (void); 53 | 54 | private: 55 | os::rtos::clock::timestamp_t lap_ = 0; 56 | }; 57 | 58 | inline void 59 | stopwatch::start (void) 60 | { 61 | lap_ = os::rtos::hrclock.now (); 62 | } 63 | 64 | inline os::rtos::clock::timestamp_t 65 | stopwatch::stop (void) 66 | { 67 | return ((os::rtos::hrclock.now () - lap_) 68 | / (SystemCoreClock / 1000000)); 69 | } 70 | 71 | #endif /* TEST_TEST_QSPI_H_ */ 72 | --------------------------------------------------------------------------------