S&B Volcano vaporizer remote control with Pi Pico W
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.

cache.c 7.8KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308
  1. /*
  2. * cache.c
  3. *
  4. * Copyright (c) 2023 Thomas Buck (thomas@xythobuz.de)
  5. *
  6. * This program is free software: you can redistribute it and/or modify
  7. * it under the terms of the GNU General Public License as published by
  8. * the Free Software Foundation, either version 3 of the License, or
  9. * (at your option) any later version.
  10. *
  11. * This program is distributed in the hope that it will be useful,
  12. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  14. * GNU General Public License for more details.
  15. *
  16. * See <http://www.gnu.org/licenses/>.
  17. */
  18. #include <stdbool.h>
  19. #include <string.h>
  20. #include "pico/flash.h"
  21. #include "config.h"
  22. #include "log.h"
  23. #include "mem.h"
  24. #include "cache.h"
  25. #define CACHE_MAX_AGE_MS (5 * 1000)
  26. #define PAGE_SIZE FLASH_SECTOR_SIZE // 4K
  27. #define CACHE_ENTRIES 10 // 40K in RAM
  28. // 384 * 512 = 196608 bytes = 48 Pages (Sectors) in Flash
  29. #define DISK_SIZE (DISK_BLOCK_SIZE * DISK_BLOCK_COUNT)
  30. #define DISK_PAGES (DISK_SIZE / PAGE_SIZE)
  31. static_assert((DISK_SIZE % PAGE_SIZE) == 0, "Disk blocks need to fit cleanly into flash pages.");
  32. // PICO_FLASH_SIZE_BYTES is 2MB = 512 * 4K pages
  33. // BTstack uses the last two pages, we use one more for EEPROM config.
  34. // So 509 pages remain, minus the 48 pages for the FAT disk.
  35. // --> 461 pages remain for bootloader and application.
  36. // --> 461 * 4096 = 1888256 bytes
  37. #define CACHE_FLASH_OFFSET (EEPROM_FLASH_OFFSET - (DISK_PAGES * PAGE_SIZE))
  38. static_assert(DISK_PAGES == 48, "TODO conversions are hard-coded currently");
  39. struct cache_entry {
  40. bool set;
  41. size_t page;
  42. bool dirty;
  43. uint32_t age;
  44. uint8_t buff[PAGE_SIZE];
  45. };
  46. static struct cache_entry cache[CACHE_ENTRIES] = {0};
  47. static const uint8_t *disk = (const uint8_t *)(XIP_BASE + CACHE_FLASH_OFFSET);
  48. void cache_init(void) {
  49. for (size_t i = 0; i < CACHE_ENTRIES; i++) {
  50. cache[i].set = false;
  51. }
  52. }
  53. void cache_status(void) {
  54. size_t count = 0;
  55. for (size_t i = 0; i < CACHE_ENTRIES; i++) {
  56. if (!cache[i].set) {
  57. continue;
  58. }
  59. println("Cache Entry %d:", count);
  60. println(" Page: %d", cache[i].page);
  61. println(" Dirty: %s", cache[i].dirty ? "Yes" : "No");
  62. uint32_t now = to_ms_since_boot(get_absolute_time());
  63. println(" Age: %.1fs", (now - cache[i].age) / 1000.0f);
  64. count++;
  65. }
  66. println("Total entries: %d", count);
  67. }
  68. struct cache_write_data {
  69. uint32_t addr;
  70. uint8_t *buff;
  71. };
  72. static void cache_write_flash(void *param) {
  73. struct cache_write_data *tmp = param;
  74. flash_range_erase(tmp->addr, PAGE_SIZE);
  75. flash_range_program(tmp->addr, tmp->buff, PAGE_SIZE);
  76. }
  77. static void cache_flush(size_t i) {
  78. // after this the cache entry is gone
  79. cache[i].set = false;
  80. // if it is not actually modified, we can bail out here
  81. if (!cache[i].dirty) {
  82. return;
  83. }
  84. // now actually write contents back to flash
  85. uint32_t addr = CACHE_FLASH_OFFSET + (cache[i].page * PAGE_SIZE);
  86. debug("flushing entry %d page %d at 0x%08lX", i, cache[i].page, addr);
  87. struct cache_write_data tmp = { .addr = addr, .buff = cache[i].buff };
  88. int r = flash_safe_execute(cache_write_flash, &tmp, FLASH_LOCK_TIMEOUT_MS);
  89. if (r != PICO_OK) {
  90. debug("error calling cache_write_flash: %d", r);
  91. }
  92. }
  93. void cache_sync(void) {
  94. for (size_t i = 0; i < CACHE_ENTRIES; i++) {
  95. if (!cache[i].set) {
  96. continue;
  97. }
  98. // just flush out all available entries
  99. cache_flush(i);
  100. }
  101. }
  102. void cache_run(void) {
  103. for (size_t i = 0; i < CACHE_ENTRIES; i++) {
  104. if (!cache[i].set) {
  105. continue;
  106. }
  107. // only flush out entries that are too old
  108. uint32_t now = to_ms_since_boot(get_absolute_time());
  109. if ((now - cache[i].age) >= CACHE_MAX_AGE_MS) {
  110. cache_flush(i);
  111. }
  112. }
  113. }
  114. static ssize_t cache_read_single(uint8_t *buf, size_t page, size_t off, size_t len) {
  115. if (page >= DISK_PAGES) {
  116. debug("error: invalid page %d", page);
  117. return -1;
  118. }
  119. if ((off + len) > PAGE_SIZE) {
  120. debug("error: too long %d %d", off, len);
  121. return -1;
  122. }
  123. // is it in the cache?
  124. for (size_t i = 0; i < CACHE_ENTRIES; i++) {
  125. if (!cache[i].set) {
  126. continue;
  127. }
  128. if (cache[i].page != page) {
  129. continue;
  130. }
  131. // it is in the cache!
  132. memcpy(buf, cache[i].buff + off, len);
  133. return len;
  134. }
  135. // not in cache, read directly from flash
  136. memcpy(buf, disk + (page * PAGE_SIZE) + off, len);
  137. return len;
  138. }
  139. static ssize_t write_into_cache(size_t idx, const uint8_t *buf, size_t off, size_t len) {
  140. bool changed = false;
  141. for (size_t i = 0; i < len; i++) {
  142. if (cache[idx].buff[off + i] != buf[i]) {
  143. changed = true;
  144. }
  145. cache[idx].buff[off + i] = buf[i];
  146. }
  147. if (changed) {
  148. cache[idx].dirty = true;
  149. cache[idx].age = to_ms_since_boot(get_absolute_time());
  150. }
  151. return len;
  152. }
  153. static ssize_t cache_write_single(const uint8_t *buf, size_t page, size_t off, size_t len) {
  154. if (page >= DISK_PAGES) {
  155. debug("error: invalid page %d", page);
  156. return -1;
  157. }
  158. if ((off + len) > PAGE_SIZE) {
  159. debug("error: too long %d %d", off, len);
  160. return -1;
  161. }
  162. // is it in the cache?
  163. for (size_t i = 0; i < CACHE_ENTRIES; i++) {
  164. if (!cache[i].set) {
  165. continue;
  166. }
  167. if (cache[i].page != page) {
  168. continue;
  169. }
  170. // it is in the cache!
  171. return write_into_cache(i, buf, off, len);
  172. }
  173. // not in cache yet, find free cache entry
  174. ssize_t free = -1, oldest = -1;
  175. uint32_t min_age = 0xFFFFFFFF;
  176. for (size_t i = 0; i < CACHE_ENTRIES; i++) {
  177. if (!cache[i].set) {
  178. if (free < 0) {
  179. free = i;
  180. }
  181. }
  182. if (cache[i].age < min_age) {
  183. min_age = cache[i].age;
  184. oldest = i;
  185. }
  186. }
  187. if (free < 0) {
  188. // flush oldest current entry and use its place
  189. if (oldest < 0) {
  190. debug("error, no oldest entry?!");
  191. return -1;
  192. }
  193. cache_flush(oldest);
  194. free = oldest;
  195. }
  196. // populate new cache entry
  197. cache[free].set = true;
  198. cache[free].page = page;
  199. cache[free].dirty = false;
  200. cache[free].age = to_ms_since_boot(get_absolute_time());
  201. memcpy(cache[free].buff, disk + (page * PAGE_SIZE), PAGE_SIZE);
  202. debug("add cache %d for page %d", free, page);
  203. // perform write operation into cache entry
  204. return write_into_cache(free, buf, off, len);
  205. }
  206. ssize_t cache_read(uint8_t *buf, size_t addr, size_t len) {
  207. size_t page = addr / PAGE_SIZE;
  208. size_t off = addr % PAGE_SIZE;
  209. size_t count = 0;
  210. while ((off + len) > PAGE_SIZE) {
  211. debug("split cache page read");
  212. size_t chunk = PAGE_SIZE - off;
  213. ssize_t r = cache_read_single(buf + count, page, off, chunk);
  214. if (r < 0) {
  215. return r;
  216. }
  217. count += r;
  218. len -= r;
  219. off = 0;
  220. page++;
  221. }
  222. ssize_t r = cache_read_single(buf + count, page, off, len);
  223. if (r < 0) {
  224. return r;
  225. }
  226. count += r;
  227. return count;
  228. }
  229. ssize_t cache_write(const uint8_t *buf, size_t addr, size_t len) {
  230. size_t page = addr / PAGE_SIZE;
  231. size_t off = addr % PAGE_SIZE;
  232. size_t count = 0;
  233. while ((off + len) > PAGE_SIZE) {
  234. debug("split cache page write");
  235. size_t chunk = PAGE_SIZE - off;
  236. ssize_t r = cache_write_single(buf + count, page, off, chunk);
  237. if (r < 0) {
  238. return r;
  239. }
  240. count += r;
  241. len -= r;
  242. off = 0;
  243. page++;
  244. }
  245. ssize_t r = cache_write_single(buf + count, page, off, len);
  246. if (r < 0) {
  247. return r;
  248. }
  249. count += r;
  250. return count;
  251. }