bsp_V8M_flash.c 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179
  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. // 这里要重新安排,程序什么的所有都要写在外部flash里 且有一些字段是芯片定义的不可写入地区,比如
  7. // __attribute__ ((section(".nor_cfg_option"))) const uint32_t option[4] = {0xfcf90002, 0x00000007, 0xE, 0x0};
  8. // define region NOR_CFG_OPTION = [ from 0x80000400 size 0x0C00 ];
  9. // define region BOOT_HEADER = [ from 0x80001000 size 0x2000 ];
  10. // 这个字段就是narflash 的描述符,不能覆盖 所以程序得后移
  11. // 从链接字段里可以发现 define region XPI0 = [from 0x80003000 size (_flash_size - 0x3000) ]; /* XPI0 */
  12. // app是从0x80003000开始的 也就是boot只能从这开始
  13. #if 0
  14. // 起始地址 0x80000000
  15. // IAP标志地址 - 选择Flash中一个合适的扇区
  16. // HPM6750 Flash通常映射在0x80000000开始
  17. #define IAP_FLAG_ADDR ((uint32_t)0x8000C000)
  18. #define IAP_FLAG_NEED_UPDATE ((uint16_t)0xABCD)
  19. // Flash大小配置 - 根据实际Flash芯片修改
  20. #define FLASH_BASE_ADDR 0x80000000
  21. #define FLASH_SECTOR_SIZE 0x1000 // 4KB,常见QSPI Flash扇区大小
  22. #define FLASH_PAGE_SIZE 256 // 编程页大小
  23. // XPI Nor Flash 配置结构体
  24. static xpi_nor_config_t s_flash_config;
  25. /**
  26. * @brief 初始化Flash控制器并获取配置
  27. */
  28. static void flash_init(void)
  29. {
  30. static bool s_flash_inited = false;
  31. if (!s_flash_inited) {
  32. // 获取Flash配置(从ROM API读取)
  33. rom_xpi_nor_get_config(HPM_XPI0, &s_flash_config, NULL);
  34. s_flash_inited = true;
  35. }
  36. }
  37. /**
  38. * @brief 根据地址计算所在扇区(HPM6750版)
  39. *
  40. * HPM6750的外部Flash扇区划分与具体Flash芯片有关
  41. * 这里以常见的4KB扇区为例
  42. */
  43. static uint32_t get_sector_index(uint32_t address)
  44. {
  45. // 计算相对于Flash基址的偏移
  46. uint32_t offset = address - FLASH_BASE_ADDR;
  47. // 扇区索引 = 偏移 / 扇区大小
  48. return offset / FLASH_SECTOR_SIZE;
  49. }
  50. /**
  51. * @brief 擦除包含指定地址的扇区
  52. */
  53. static void flash_erase_sector(uint32_t address)
  54. {
  55. flash_init();
  56. // HPM6750需要先禁用缓存,因为我们在擦除Flash
  57. l1c_dc_invalidate_all();
  58. // 确保地址对齐到扇区边界
  59. uint32_t sector_start = address & ~(FLASH_SECTOR_SIZE - 1);
  60. // 调用ROM API擦除扇区
  61. rom_xpi_nor_erase_sector(HPM_XPI0, &s_flash_config, sector_start);
  62. // 擦除后清理缓存
  63. l1c_dc_invalidate_all();
  64. }
  65. /**
  66. * @brief 编程半字(2字节)到Flash
  67. */
  68. static void flash_program_halfword(uint32_t address, uint16_t data)
  69. {
  70. flash_init();
  71. // 准备要写入的数据(需要是32位对齐)
  72. uint32_t prog_data[2];
  73. prog_data[0] = (uint32_t)data; // 低16位有效
  74. // HPM6750要求编程地址对齐到编程单元
  75. // 这里我们读取整个页,修改后再写回
  76. uint8_t page_buffer[FLASH_PAGE_SIZE] __attribute__((aligned(4)));
  77. // 计算页起始地址
  78. uint32_t page_start = address & ~(FLASH_PAGE_SIZE - 1);
  79. uint32_t offset_in_page = address - page_start;
  80. // 先读取整个页
  81. rom_xpi_nor_read(HPM_XPI0, &s_flash_config, page_start, page_buffer, FLASH_PAGE_SIZE);
  82. // 修改目标位置
  83. *(uint16_t*)(page_buffer + offset_in_page) = data;
  84. // 擦除整个页所在的扇区
  85. flash_erase_sector(address);
  86. // 重新编程整个页
  87. rom_xpi_nor_program(HPM_XPI0, &s_flash_config, page_start, page_buffer, FLASH_PAGE_SIZE);
  88. // 清理缓存
  89. l1c_dc_invalidate_all();
  90. }
  91. /**
  92. * @brief 从Flash读取N个字节
  93. */
  94. int hpm_flash_read_nbytes(uint32_t read_addr, uint8_t *read_buf, int32_t read_num)
  95. {
  96. flash_init();
  97. // HPM6750可以直接通过内存映射读取
  98. // 但需要确保缓存一致性
  99. l1c_dc_invalidate((uint32_t)read_addr, read_num);
  100. memcpy(read_buf, (void*)read_addr, read_num);
  101. return read_num;
  102. }
  103. /**
  104. * @brief 清除升级标志
  105. */
  106. void hpm_clear_iap_flag(void)
  107. {
  108. uint32_t mask;
  109. // 保存当前中断状态并关闭所有中断
  110. mask = disable_global_irq(CSR_MSTATUS_MIE_MASK);
  111. // 写入0xFFFF表示无升级标志
  112. flash_program_halfword(IAP_FLAG_ADDR, 0xFFFF);
  113. // 恢复中断状态
  114. restore_global_irq(mask);
  115. }
  116. /**
  117. * @brief 记录升级标志
  118. */
  119. void hpm_record_iap_flag(void)
  120. {
  121. uint32_t mask;
  122. // 保存当前中断状态并关闭所有中断
  123. mask = disable_global_irq(CSR_MSTATUS_MIE_MASK);
  124. flash_program_halfword(IAP_FLAG_ADDR, IAP_FLAG_NEED_UPDATE);
  125. // 恢复中断状态
  126. restore_global_irq(mask);
  127. }
  128. /**
  129. * @brief 检查升级标志位
  130. */
  131. void hpm_check_iap_flag(void)
  132. {
  133. uint16_t iap_flag;
  134. // 读取标志
  135. hpm_flash_read_nbytes(IAP_FLAG_ADDR, (uint8_t*)&iap_flag, sizeof(iap_flag));
  136. // 如果需要升级,清除标志
  137. if (iap_flag == IAP_FLAG_NEED_UPDATE) {
  138. hpm_clear_iap_flag();
  139. // 这里可以触发跳转到Bootloader的逻辑
  140. // printf("IAP flag detected, jumping to bootloader...\n");
  141. }
  142. }
  143. #endif