Smart-Home am Beispiel der Präsenzerkennung im Raum Projektarbeit Lennart Heimbs, Johannes Krug, Sebastian Dohle und Kevin Holzschuh bei Prof. Oliver Hofmann SS2019
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

VirtualPage.cpp 10KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349
  1. /*
  2. VirtualPage.cpp - Flash page management
  3. Original Copyright (c) 2017 Frank Holtz. All right reserved.
  4. This library is free software; you can redistribute it and/or
  5. modify it under the terms of the GNU Lesser General Public
  6. License as published by the Free Software Foundation; either
  7. version 2.1 of the License, or (at your option) any later version.
  8. This library is distributed in the hope that it will be useful,
  9. but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  11. Lesser General Public License for more details.
  12. You should have received a copy of the GNU Lesser General Public
  13. License along with this library; if not, write to the Free Software
  14. Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
  15. */
  16. #include "VirtualPage.h"
  17. VirtualPageClass VirtualPage;
  18. #ifndef NVM_VIRTUAL_PAGE_SIZE_BITS
  19. #define NVM_VIRTUAL_PAGE_SIZE_BITS 12
  20. #elif NVM_VIRTUAL_PAGE_SIZE_BITS < 12
  21. #error "NVM_VIRTUAL_PAGE_SIZE_BITS must be >= 12"
  22. #endif
  23. // Calculate virtual page count, when mcuboot is present
  24. #if defined(MCUBOOT_PRESENT) && !defined(NVM_VIRTUAL_PAGE_COUNT)
  25. // mcuboot zephyr build via generated_dts_board.h
  26. #include "generated_dts_board.h"
  27. // Calculate number of free pages after scratch area
  28. #define NVM_VIRTUAL_PAGE_COUNT (((CONFIG_FLASH_SIZE_0<<10)-(FLASH_AREA_IMAGE_SCRATCH_OFFSET_0+FLASH_AREA_IMAGE_SCRATCH_SIZE_0)) >> NVM_VIRTUAL_PAGE_SIZE_BITS)
  29. #endif
  30. // check page size
  31. #ifndef NVM_VIRTUAL_PAGE_COUNT
  32. #if FLASH_ERASE_CYCLES >= 20000
  33. // use 16k of flash memory
  34. #define NVM_VIRTUAL_PAGE_COUNT 4
  35. #else
  36. // use 32k of flash memory
  37. #define NVM_VIRTUAL_PAGE_COUNT 8
  38. #endif
  39. #endif
  40. /*
  41. * How many virtual pages are skipped from top of flash
  42. */
  43. #ifndef NVM_VIRTUAL_PAGE_SKIP_FROM_TOP
  44. #define NVM_VIRTUAL_PAGE_SKIP_FROM_TOP 0
  45. #endif
  46. /*
  47. * Calculate things around NVM_VIRTUAL_PAGE_SIZE
  48. */
  49. #define NVM_VIRTUAL_PAGE_SIZE (1 << (NVM_VIRTUAL_PAGE_SIZE_BITS))
  50. #define NVM_VIRTUAL_PAGE_ADDRESS_MASK (~(NVM_VIRTUAL_PAGE_SIZE - 1))
  51. #define NVM_VIRTUAL_PAGE_ALIGN(address) \
  52. { address = (uint32_t *)((uint32_t)address & NVM_VIRTUAL_PAGE_ADDRESS_MASK); }
  53. /*
  54. * Defines the position of status words in a page.
  55. * Offsets are defined in words!
  56. */
  57. #ifdef FLASH_SUPPORTS_RANDOM_WRITE
  58. // use first 8 byte for magic, erase counter and status
  59. #define OFFSET_MAGIC 0
  60. #define OFFSET_ERASE_COUNTER 1
  61. #if FLASH_WRITES_PER_WORD > 2
  62. // use first 12 bytes for magic, erase counter and status
  63. #define MASK_ERASE_COUNTER 0x00FFFFFF
  64. #define OFFSET_STATUS_RELEASE_PREPARE 1
  65. #define OFFSET_STATUS_RELEASE_END 1
  66. #define METADATA_SIZE 8
  67. #define OFFSET_DATA 2
  68. #elif FLASH_WRITES_PER_WORD == 2
  69. #define MASK_ERASE_COUNTER 0x00FFFFFF
  70. #define OFFSET_STATUS_RELEASE_PREPARE 2
  71. #define OFFSET_STATUS_RELEASE_END 2
  72. #define METADATA_SIZE 12
  73. #define OFFSET_DATA 3
  74. #else
  75. // use first 12 bytes for erase counter, and magic
  76. #define OFFSET_MAGIC 1
  77. #define OFFSET_COUNTER 0
  78. #define MASK_ERASE_COUNTER 0x00FFFFFF
  79. #define OFFSET_STATUS_RELEASE_PREPARE NVM_VIRTUAL_PAGE_SIZE - 8
  80. #define OFFSET_STATUS_RELEASE_END NVM_VIRTUAL_PAGE_SIZE - 4
  81. #define METADATA_SIZE 16
  82. #define OFFSET_DATA 4
  83. #endif
  84. #define BIT_STATUS_RELEASE_PREPARE (1 << 30)
  85. #define BIT_STATUS_RELEASE_END (1 << 31)
  86. #define NVM_VIRTUAL_PAGE_DATA_SIZE (NVM_VIRTUAL_PAGE_SIZE - METADATA_SIZE)
  87. #else
  88. // use first 8 byte for magic and erase counter and last 8 byte for page release
  89. #define OFFSET_MAGIC 1
  90. #define OFFSET_ERASE_COUNTER 0
  91. #define OFFSET_DATA 2
  92. #define OFFSET_STATUS_RELEASE_PREPARE \
  93. ((NVM_VIRTUAL_PAGE_SIZE - 8) / sizeof(uint32_t))
  94. #define OFFSET_STATUS_RELEASE_END \
  95. ((NVM_VIRTUAL_PAGE_SIZE - 4) / sizeof(uint32_t))
  96. #define MASK_ERASE_COUNTER 0xFFFFFFFF
  97. #define BIT_STATUS_RELEASE_PREPARE 1
  98. #define BIT_STATUS_RELEASE_END 1
  99. #define NVM_VIRTUAL_PAGE_DATA_SIZE (NVM_VIRTUAL_PAGE_SIZE - 16)
  100. #endif
  101. uint16_t VirtualPageClass::size() const
  102. {
  103. return (NVM_VIRTUAL_PAGE_DATA_SIZE);
  104. }
  105. uint16_t VirtualPageClass::length() const
  106. {
  107. return (NVM_VIRTUAL_PAGE_DATA_SIZE / 4);
  108. }
  109. uint16_t VirtualPageClass::page_count() const
  110. {
  111. return (NVM_VIRTUAL_PAGE_COUNT - 1);
  112. }
  113. uint32_t VirtualPageClass::wear_level()
  114. {
  115. uint32_t max_erase_cycles = 0;
  116. for (int i = 1; i <= NVM_VIRTUAL_PAGE_COUNT; i++) {
  117. uint32_t erase_cycles = get_page_erase_cycles(get_page_address(i));
  118. if (erase_cycles > max_erase_cycles) {
  119. max_erase_cycles = erase_cycles;
  120. }
  121. }
  122. return (uint32_t)((((uint64_t)max_erase_cycles * 10000)) /
  123. Flash.specified_erase_cycles());
  124. }
  125. uint32_t *VirtualPageClass::get(uint32_t magic)
  126. {
  127. // Give back a page prepared for release and not closed
  128. for (int i = 1; i <= NVM_VIRTUAL_PAGE_COUNT; i++) {
  129. uint32_t *page = get_page_address(i);
  130. if (
  131. // correct magic is set
  132. (page[OFFSET_MAGIC] == magic) &&
  133. // page is in release_prepare mode
  134. ((page[OFFSET_STATUS_RELEASE_PREPARE] & BIT_STATUS_RELEASE_PREPARE) ==
  135. 0) &&
  136. // page is not released
  137. ((page[OFFSET_STATUS_RELEASE_END] & BIT_STATUS_RELEASE_END) > 0)) {
  138. // Return page in release process with priority
  139. return &page[OFFSET_DATA];
  140. }
  141. }
  142. // check if a unreleased page is available
  143. for (int i = 1; i <= NVM_VIRTUAL_PAGE_COUNT; i++) {
  144. uint32_t *page = get_page_address(i);
  145. if (
  146. // correct magic is set
  147. (page[OFFSET_MAGIC] == magic) &&
  148. // page is not released
  149. ((page[OFFSET_STATUS_RELEASE_END] & BIT_STATUS_RELEASE_END) > 0)) {
  150. // return page in normal operation
  151. return &page[OFFSET_DATA];
  152. }
  153. }
  154. return (uint32_t *)(~0);
  155. }
  156. uint32_t *VirtualPageClass::allocate(uint32_t magic)
  157. {
  158. uint32_t *return_page = (uint32_t *)(~0);
  159. uint32_t max_erase_cycles = (uint32_t)~0;
  160. // Avoid duplicate allocation of pages, look for the less used page
  161. for (int i = 1; i <= NVM_VIRTUAL_PAGE_COUNT; i++) {
  162. uint32_t *page = get_page_address(i);
  163. // Delete duplicated pages
  164. if (
  165. // same magic
  166. (page[OFFSET_MAGIC] == magic) &&
  167. // Not in release_end state
  168. ((page[OFFSET_STATUS_RELEASE_END] & BIT_STATUS_RELEASE_END) > 0) &&
  169. // Not in release_prepare state
  170. (!release_started(page))) {
  171. // clear the page
  172. build_page(page, (uint32_t)~0);
  173. }
  174. uint32_t erase_cycles = get_page_erase_cycles(page);
  175. // When the page has less erase cycles and is not marked as failed
  176. if ((erase_cycles < max_erase_cycles) && (page[OFFSET_MAGIC] > 0) &&
  177. (
  178. // magic is empty
  179. (page[OFFSET_MAGIC] == (uint32_t)~0) ||
  180. // marked as released
  181. ((page[OFFSET_STATUS_RELEASE_END] & BIT_STATUS_RELEASE_END) ==
  182. 0))) {
  183. max_erase_cycles = erase_cycles;
  184. return_page = page;
  185. }
  186. }
  187. // return if no page was found
  188. if (return_page == (uint32_t *)~0) {
  189. return return_page;
  190. }
  191. build_page(return_page, magic);
  192. return &return_page[OFFSET_DATA];
  193. }
  194. uint32_t *VirtualPageClass::allocate(uint32_t magic, uint32_t max_writes)
  195. {
  196. // max_writes is not implemented yet -> page is erased with every allocate
  197. (void)max_writes;
  198. return allocate(magic);
  199. }
  200. void VirtualPageClass::release_prepare(uint32_t *address)
  201. {
  202. // move pointer to beginning of the page
  203. NVM_VIRTUAL_PAGE_ALIGN(address);
  204. // Nothing to do at a empty page
  205. if (address[OFFSET_MAGIC] == (uint32_t)~0) {
  206. return;
  207. }
  208. if (release_started(address) == false) {
  209. // Clear bit BIT_PAGE_RELEASED
  210. Flash.write(&address[OFFSET_STATUS_RELEASE_PREPARE],
  211. address[OFFSET_STATUS_RELEASE_PREPARE] &
  212. ~BIT_STATUS_RELEASE_PREPARE);
  213. }
  214. return;
  215. }
  216. void VirtualPageClass::release(uint32_t *address)
  217. {
  218. // move pointer to beginning of the page
  219. NVM_VIRTUAL_PAGE_ALIGN(address);
  220. // Nothing to do at a empty page
  221. if (address[OFFSET_MAGIC] == (uint32_t)~0) {
  222. return;
  223. }
  224. // Check if status bit already cleared
  225. if ((address[OFFSET_STATUS_RELEASE_END] & BIT_STATUS_RELEASE_END) > 0) {
  226. // Clear bit BIT_PAGE_RELEASED
  227. Flash.write(&address[OFFSET_STATUS_RELEASE_END],
  228. address[OFFSET_STATUS_RELEASE_END] & ~BIT_STATUS_RELEASE_END);
  229. }
  230. return;
  231. }
  232. bool VirtualPageClass::release_started(uint32_t *address)
  233. {
  234. // move pointer to beginning of the page
  235. NVM_VIRTUAL_PAGE_ALIGN(address);
  236. return (address[OFFSET_STATUS_RELEASE_PREPARE] &
  237. BIT_STATUS_RELEASE_PREPARE) == 0;
  238. }
  239. void VirtualPageClass::fail(uint32_t *address)
  240. {
  241. // move pointer to beginning of the page
  242. NVM_VIRTUAL_PAGE_ALIGN(address);
  243. build_page(address, 0x00000000);
  244. return;
  245. }
  246. void VirtualPageClass::clean_up()
  247. {
  248. // No page found -> try to give back a page prepared for release
  249. for (int i = 1; i <= NVM_VIRTUAL_PAGE_COUNT; i++) {
  250. uint32_t *page = get_page_address(i);
  251. if ((page[OFFSET_STATUS_RELEASE_END] & BIT_STATUS_RELEASE_END) == 0) {
  252. build_page(get_page_address(i), ~0);
  253. return; // a maximum of a page is cleaned -> return
  254. }
  255. }
  256. }
  257. void VirtualPageClass::format()
  258. {
  259. for (int i = 1; i <= NVM_VIRTUAL_PAGE_COUNT; i++) {
  260. uint32_t *address = get_page_address(i);
  261. build_page(address, (uint32_t)~0);
  262. }
  263. }
  264. uint32_t *VirtualPageClass::get_page_address(uint16_t page)
  265. {
  266. return (uint32_t *)(Flash.top_app_page_address() -
  267. ((page + NVM_VIRTUAL_PAGE_SKIP_FROM_TOP)
  268. << NVM_VIRTUAL_PAGE_SIZE_BITS));
  269. }
  270. void VirtualPageClass::build_page(uint32_t *address, uint32_t magic)
  271. {
  272. // move pointer to beginning of the page
  273. NVM_VIRTUAL_PAGE_ALIGN(address);
  274. // get erase counter
  275. uint32_t erase_counter = get_page_erase_cycles(address);
  276. // Check if a magic is set
  277. if (address[OFFSET_MAGIC] != (uint32_t)~0) {
  278. Flash.erase(address, NVM_VIRTUAL_PAGE_SIZE);
  279. } else {
  280. // check if page is empty
  281. for (int i = OFFSET_DATA; i < (NVM_VIRTUAL_PAGE_SIZE / 4); i++) {
  282. if (address[i] != (uint32_t)~0) {
  283. Flash.erase(address, NVM_VIRTUAL_PAGE_SIZE);
  284. break;
  285. }
  286. }
  287. }
  288. // write a new page
  289. Flash.write(&address[OFFSET_MAGIC], magic);
  290. if (address[OFFSET_ERASE_COUNTER] == (uint32_t)~0) {
  291. Flash.write(&address[OFFSET_ERASE_COUNTER],
  292. erase_counter | ~MASK_ERASE_COUNTER);
  293. }
  294. }
  295. uint32_t VirtualPageClass::get_page_erase_cycles(uint32_t *address)
  296. {
  297. // Return number of cycles
  298. return ((((uint32_t)address[OFFSET_ERASE_COUNTER])+1) &
  299. (uint32_t)MASK_ERASE_COUNTER);
  300. }