bsp_V8M_flash.c 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362
  1. #include "board.h"
  2. #include "hpm_romapi.h"
  3. #include "hpm_l1c_drv.h"
  4. #include "hpm_soc.h"
  5. #include "bsp_V8M_flash.h"
  6. #include "test.h"
  7. // 这里要重新安排,程序什么的所有都要写在外部flash里 且有一些字段是芯片定义的不可写入地区,比如
  8. // __attribute__ ((section(".nor_cfg_option"))) const uint32_t option[4] = {0xfcf90002, 0x00000007, 0xE, 0x0};
  9. // define region NOR_CFG_OPTION = [ from 0x80000400 size 0x0C00 ];
  10. // define region BOOT_HEADER = [ from 0x80001000 size 0x2000 ];
  11. // 这个字段就是narflash 的描述符,不能覆盖 所以程序得后移
  12. // 从链接字段里可以发现 define region XPI0 = [from 0x80003000 size (_flash_size - 0x3000) ]; /* XPI0 */
  13. // app是从0x80003000开始的 也就是boot只能从这开始
  14. //#define BOARD_APP_XPI_NOR_XPI_BASE (HPM_XPI0)
  15. //#define BOARD_APP_XPI_NOR_CFG_OPT_HDR (0xfcf90002U) 0xfcf9 - FLASH configuration option tag 2 option words 有效选项为2
  16. //#define BOARD_APP_XPI_NOR_CFG_OPT_OPT0 (0x00000007U) 7 - 133MHz
  17. //#define BOARD_APP_XPI_NOR_CFG_OPT_OPT1 (0x0000000EU)
  18. // 起始地址 0x80000000
  19. // IAP标志地址 - 选择Flash中一个合适的扇区
  20. // HPM6750 Flash通常映射在0x80000000开始 程序从64k开始
  21. // #define IAP_FLAG_ADDR ((uint32_t)0x8000C000) // 30k的擦除标志地址
  22. // 后续发现好像可以去掉这些初始化的配置字节???
  23. #define IAP_FLAG_ADDR ((uint32_t)0x80050000)
  24. #define IAP_FLAG_NEED_UPDATE ((uint16_t)0xABCD)
  25. // Flash大小配置 - 根据实际Flash芯片修改
  26. #define FLASH_BASE_ADDR 0x80000000
  27. #define FLASH_SECTOR_SIZE 0x1000 // 4KB,常见QSPI Flash扇区大小
  28. // XPI Nor Flash 配置结构体
  29. static xpi_nor_config_t s_xpi_nor_config;
  30. /**
  31. * @brief 初始化Flash控制器并获取配置 bootrom在初始化时已经对应的xpi配置字节
  32. */
  33. static void flash_init(void)
  34. {
  35. static bool s_flash_inited = false;
  36. if (!s_flash_inited) { // 是默认配置字节 也可以手动修改xpi的参数
  37. xpi_nor_config_option_t option;
  38. option.header.U = BOARD_APP_XPI_NOR_CFG_OPT_HDR;
  39. option.option0.U = BOARD_APP_XPI_NOR_CFG_OPT_OPT0;
  40. option.option1.U = BOARD_APP_XPI_NOR_CFG_OPT_OPT1;
  41. disable_global_irq(CSR_MSTATUS_MIE_MASK);
  42. hpm_stat_t status = rom_xpi_nor_auto_config(HPM_XPI0, &s_xpi_nor_config, &option);
  43. fencei(); // 刷新指令缓存
  44. enable_global_irq(CSR_MSTATUS_MIE_MASK);
  45. if (status != status_success) {
  46. printf("ERROR: Flash init failed\n");
  47. while(1);
  48. }
  49. s_flash_inited = true;
  50. }
  51. }
  52. /**
  53. * @brief 根据绝对地址获取扇区起始偏移
  54. * @param address 绝对地址
  55. */
  56. static uint32_t get_sector_start_offset(uint32_t address)
  57. {
  58. uint32_t offset = address - FLASH_BASE_ADDR;
  59. return (offset / FLASH_SECTOR_SIZE) * FLASH_SECTOR_SIZE;
  60. }
  61. /**
  62. * @brief 编程半字(2字节)到Flash
  63. */
  64. static void flash_program_halfword(uint32_t address, uint16_t data)
  65. {
  66. flash_init();
  67. uint32_t sector_start_offset = get_sector_start_offset(address);
  68. uint32_t offset_in_sector = (address - FLASH_BASE_ADDR) - sector_start_offset;
  69. printf("Programming: addr=0x%x, sector=0x%x, offset=%d, data=0x%04x\n",
  70. address, sector_start_offset, offset_in_sector, data);
  71. static uint8_t sector_buffer[FLASH_SECTOR_SIZE];
  72. hpm_stat_t status;
  73. disable_global_irq(CSR_MSTATUS_MIE_MASK);
  74. // 1. 读取整个扇区
  75. status = rom_xpi_nor_read(HPM_XPI0,
  76. xpi_xfer_channel_auto,
  77. &s_xpi_nor_config,
  78. (uint32_t *)sector_buffer,
  79. sector_start_offset,
  80. FLASH_SECTOR_SIZE);
  81. if (status != status_success) {
  82. enable_global_irq(CSR_MSTATUS_MIE_MASK);
  83. printf("ERROR: read sector failed\n");
  84. return;
  85. }
  86. // 2. 读取当前值(按小端序解析)
  87. uint16_t current_value = sector_buffer[offset_in_sector] |
  88. (sector_buffer[offset_in_sector + 1] << 8);
  89. printf("Current value: 0x%04X\n", current_value);
  90. // 3. 检查是否需要更新(使用小端序比较)
  91. if (current_value == data) {
  92. enable_global_irq(CSR_MSTATUS_MIE_MASK);
  93. printf("Data unchanged, skip\n");
  94. return;
  95. }
  96. // 4. 准备新数据(小端序存储)
  97. sector_buffer[offset_in_sector] = data & 0xFF; // 低字节
  98. sector_buffer[offset_in_sector + 1] = (data >> 8) & 0xFF; // 高字节
  99. // 5. 擦除扇区
  100. status = rom_xpi_nor_erase_sector(HPM_XPI0,
  101. xpi_xfer_channel_auto,
  102. &s_xpi_nor_config,
  103. sector_start_offset);
  104. if (status != status_success) {
  105. enable_global_irq(CSR_MSTATUS_MIE_MASK);
  106. printf("ERROR: erase failed\n");
  107. return;
  108. }
  109. // 6. 写回整个扇区
  110. status = rom_xpi_nor_program(HPM_XPI0,
  111. xpi_xfer_channel_auto,
  112. &s_xpi_nor_config,
  113. (uint32_t *)sector_buffer,
  114. sector_start_offset,
  115. FLASH_SECTOR_SIZE);
  116. fencei();
  117. enable_global_irq(CSR_MSTATUS_MIE_MASK);
  118. if (status != status_success) {
  119. printf("ERROR: program failed: %ld\n", status);
  120. } else {
  121. printf("Halfword written successfully\n");
  122. }
  123. }
  124. int hpm_flash_read_nbytes(uint32_t read_addr, uint8_t *read_buf, int32_t read_num)
  125. {
  126. flash_init();
  127. // 参数检查
  128. if (read_buf == NULL || read_num <= 0) {
  129. printf("ERROR: invalid parameters\n");
  130. return -1;
  131. }
  132. // 转换为偏移地址
  133. uint32_t offset = read_addr - FLASH_BASE_ADDR;
  134. // 地址范围检查
  135. uint32_t flash_size;
  136. rom_xpi_nor_get_property(HPM_XPI0, &s_xpi_nor_config,
  137. xpi_nor_property_total_size, &flash_size);
  138. if (offset + read_num > flash_size) {
  139. printf("ERROR: address out of range\n");
  140. return -1;
  141. }
  142. hpm_stat_t status = rom_xpi_nor_read(HPM_XPI0,
  143. xpi_xfer_channel_auto,
  144. &s_xpi_nor_config,
  145. (uint32_t *)read_buf,
  146. offset,
  147. read_num);
  148. if (status != status_success) {
  149. printf("ERROR: flash read failed at offset 0x%x\n", offset);
  150. return -1;
  151. }
  152. return read_num;
  153. }
  154. /**
  155. * @brief 清除升级标志
  156. */
  157. void V8M_clear_iap_flag(void)
  158. {
  159. disable_global_irq(CSR_MSTATUS_MIE_MASK);
  160. // 写入0xFFFF表示无升级标志
  161. flash_program_halfword(IAP_FLAG_ADDR, 0xFFFF);
  162. enable_global_irq(CSR_MSTATUS_MIE_MASK);
  163. }
  164. /**
  165. * @brief 记录升级标志
  166. */
  167. void V8M_record_iap_flag(void)
  168. {
  169. disable_global_irq(CSR_MSTATUS_MIE_MASK);
  170. flash_program_halfword(IAP_FLAG_ADDR, IAP_FLAG_NEED_UPDATE);
  171. enable_global_irq(CSR_MSTATUS_MIE_MASK);
  172. }
  173. /**
  174. * @brief 检查升级标志位
  175. */
  176. void V8M_check_iap_flag(void)
  177. {
  178. uint16_t iap_flag;
  179. // 读取标志
  180. hpm_flash_read_nbytes(IAP_FLAG_ADDR, (uint8_t*)&iap_flag, sizeof(iap_flag));
  181. // 如果需要升级,清除标志
  182. if (iap_flag == IAP_FLAG_NEED_UPDATE) {
  183. V8M_clear_iap_flag();
  184. }
  185. }
  186. #ifdef FLASH_TEST
  187. // 20250324 test pass
  188. /**
  189. * @brief 简单快速测试(只测试一次写入和读取)
  190. */
  191. int test_flash_simple(void)
  192. {
  193. uint16_t test_value = 0xABCD;
  194. uint16_t read_value = 0;
  195. printf("\nSimple Flash Test\n");
  196. printf("Write address: 0x%08X\n", IAP_FLAG_ADDR);
  197. printf("Write value: 0x%04X\n", test_value);
  198. // 写入
  199. printf("Writing... ");
  200. flash_program_halfword(IAP_FLAG_ADDR, test_value);
  201. // flash_program_halfword(IAP_FLAG_ADDR+2, 0xCDAB);
  202. board_delay_ms(10);
  203. printf("Done\n");
  204. // 读取
  205. printf("Reading... ");
  206. int ret = hpm_flash_read_nbytes(IAP_FLAG_ADDR, (uint8_t*)&read_value, sizeof(read_value));
  207. if (ret != sizeof(read_value)) {
  208. printf("Failed\n");
  209. return -1;
  210. }
  211. printf("Read: 0x%04X\n", read_value);
  212. // 验证
  213. if (read_value == test_value) {
  214. printf("Result: PASSED\n");
  215. return 0;
  216. } else {
  217. printf("Result: FAILED (expected 0x%04X, got 0x%04X)\n", test_value, read_value);
  218. return -1;
  219. }
  220. }
  221. /**
  222. * @brief 测试 IAP 标志功能
  223. */
  224. int test_iap_flag(void)
  225. {
  226. printf("\nIAP Flag Test\n");
  227. printf("========================================\n");
  228. // 1. 清除标志
  229. printf("1. Clearing flag...\n");
  230. V8M_clear_iap_flag();
  231. board_delay_ms(10);
  232. // 2. 检查标志(应该没有升级标志)
  233. printf("2. Checking flag (should be no update)...\n");
  234. uint16_t flag;
  235. hpm_flash_read_nbytes(IAP_FLAG_ADDR, (uint8_t*)&flag, sizeof(flag));
  236. printf(" Flag value: 0x%04X\n", flag);
  237. if (flag != 0xFFFF) {
  238. printf(" ERROR: Flag should be 0xFFFF\n");
  239. return -1;
  240. }
  241. printf(" PASS\n");
  242. // 3. 记录升级标志
  243. printf("3. Setting update flag...\n");
  244. V8M_record_iap_flag();
  245. board_delay_ms(10);
  246. // 4. 检查标志(应该为 0xABCD)
  247. printf("4. Checking flag (should be update needed)...\n");
  248. hpm_flash_read_nbytes(IAP_FLAG_ADDR, (uint8_t*)&flag, sizeof(flag));
  249. printf(" Flag value: 0x%04X\n", flag);
  250. if (flag != IAP_FLAG_NEED_UPDATE) {
  251. printf(" ERROR: Flag should be 0x%04X\n", IAP_FLAG_NEED_UPDATE);
  252. return -1;
  253. }
  254. printf(" PASS\n");
  255. // 5. 检查并清除标志
  256. printf("5. Checking and clearing flag...\n");
  257. V8M_check_iap_flag();
  258. board_delay_ms(10);
  259. // 6. 验证已清除
  260. printf("6. Verifying flag cleared...\n");
  261. hpm_flash_read_nbytes(IAP_FLAG_ADDR, (uint8_t*)&flag, sizeof(flag));
  262. printf(" Flag value: 0x%04X\n", flag);
  263. if (flag != 0xFFFF) {
  264. printf(" ERROR: Flag should be 0xFFFF\n");
  265. return -1;
  266. }
  267. printf(" PASS\n");
  268. printf("========================================\n");
  269. printf("IAP Flag Test PASSED\n");
  270. return 0;
  271. }
  272. /**
  273. * @brief 主测试函数,可选择运行全部测试或单个测试
  274. */
  275. void run_flash_tests(void)
  276. {
  277. printf("\n");
  278. printf("╔══════════════════════════════════════════════════════╗\n");
  279. printf("║ HPM6750 Flash Read/Write Test Suite ║\n");
  280. printf("╚══════════════════════════════════════════════════════╝\n");
  281. // 先运行简单测试
  282. if (test_flash_simple() != 0) {
  283. printf("\nSimple test failed! Aborting...\n");
  284. return;
  285. }
  286. printf("\n");
  287. // 运行 IAP 标志测试
  288. if (test_iap_flag() != 0) {
  289. printf("\nIAP flag test failed!\n");
  290. return;
  291. }
  292. printf("\n");
  293. }
  294. #endif