| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362 |
- #include "board.h"
- #include "hpm_romapi.h"
- #include "hpm_l1c_drv.h"
- #include "hpm_soc.h"
- #include "bsp_V8M_flash.h"
- #include "test.h"
- // 这里要重新安排,程序什么的所有都要写在外部flash里 且有一些字段是芯片定义的不可写入地区,比如
- // __attribute__ ((section(".nor_cfg_option"))) const uint32_t option[4] = {0xfcf90002, 0x00000007, 0xE, 0x0};
- // define region NOR_CFG_OPTION = [ from 0x80000400 size 0x0C00 ];
- // define region BOOT_HEADER = [ from 0x80001000 size 0x2000 ];
- // 这个字段就是narflash 的描述符,不能覆盖 所以程序得后移
- // 从链接字段里可以发现 define region XPI0 = [from 0x80003000 size (_flash_size - 0x3000) ]; /* XPI0 */
- // app是从0x80003000开始的 也就是boot只能从这开始
- //#define BOARD_APP_XPI_NOR_XPI_BASE (HPM_XPI0)
- //#define BOARD_APP_XPI_NOR_CFG_OPT_HDR (0xfcf90002U) 0xfcf9 - FLASH configuration option tag 2 option words 有效选项为2
- //#define BOARD_APP_XPI_NOR_CFG_OPT_OPT0 (0x00000007U) 7 - 133MHz
- //#define BOARD_APP_XPI_NOR_CFG_OPT_OPT1 (0x0000000EU)
- // 起始地址 0x80000000
- // IAP标志地址 - 选择Flash中一个合适的扇区
- // HPM6750 Flash通常映射在0x80000000开始 程序从64k开始
- // #define IAP_FLAG_ADDR ((uint32_t)0x8000C000) // 30k的擦除标志地址
- // 后续发现好像可以去掉这些初始化的配置字节???
- #define IAP_FLAG_ADDR ((uint32_t)0x80050000)
- #define IAP_FLAG_NEED_UPDATE ((uint16_t)0xABCD)
- // Flash大小配置 - 根据实际Flash芯片修改
- #define FLASH_BASE_ADDR 0x80000000
- #define FLASH_SECTOR_SIZE 0x1000 // 4KB,常见QSPI Flash扇区大小
- // XPI Nor Flash 配置结构体
- static xpi_nor_config_t s_xpi_nor_config;
- /**
- * @brief 初始化Flash控制器并获取配置 bootrom在初始化时已经对应的xpi配置字节
- */
- static void flash_init(void)
- {
- static bool s_flash_inited = false;
-
- if (!s_flash_inited) { // 是默认配置字节 也可以手动修改xpi的参数
- xpi_nor_config_option_t option;
- option.header.U = BOARD_APP_XPI_NOR_CFG_OPT_HDR;
- option.option0.U = BOARD_APP_XPI_NOR_CFG_OPT_OPT0;
- option.option1.U = BOARD_APP_XPI_NOR_CFG_OPT_OPT1;
-
- disable_global_irq(CSR_MSTATUS_MIE_MASK);
- hpm_stat_t status = rom_xpi_nor_auto_config(HPM_XPI0, &s_xpi_nor_config, &option);
- fencei(); // 刷新指令缓存
- enable_global_irq(CSR_MSTATUS_MIE_MASK);
-
- if (status != status_success) {
- printf("ERROR: Flash init failed\n");
- while(1);
- }
- s_flash_inited = true;
- }
- }
- /**
- * @brief 根据绝对地址获取扇区起始偏移
- * @param address 绝对地址
- */
- static uint32_t get_sector_start_offset(uint32_t address)
- {
- uint32_t offset = address - FLASH_BASE_ADDR;
- return (offset / FLASH_SECTOR_SIZE) * FLASH_SECTOR_SIZE;
- }
- /**
- * @brief 编程半字(2字节)到Flash
- */
- static void flash_program_halfword(uint32_t address, uint16_t data)
- {
- flash_init();
-
- uint32_t sector_start_offset = get_sector_start_offset(address);
- uint32_t offset_in_sector = (address - FLASH_BASE_ADDR) - sector_start_offset;
-
- printf("Programming: addr=0x%x, sector=0x%x, offset=%d, data=0x%04x\n",
- address, sector_start_offset, offset_in_sector, data);
-
- static uint8_t sector_buffer[FLASH_SECTOR_SIZE];
- hpm_stat_t status;
-
- disable_global_irq(CSR_MSTATUS_MIE_MASK);
-
- // 1. 读取整个扇区
- status = rom_xpi_nor_read(HPM_XPI0,
- xpi_xfer_channel_auto,
- &s_xpi_nor_config,
- (uint32_t *)sector_buffer,
- sector_start_offset,
- FLASH_SECTOR_SIZE);
- if (status != status_success) {
- enable_global_irq(CSR_MSTATUS_MIE_MASK);
- printf("ERROR: read sector failed\n");
- return;
- }
-
- // 2. 读取当前值(按小端序解析)
- uint16_t current_value = sector_buffer[offset_in_sector] |
- (sector_buffer[offset_in_sector + 1] << 8);
-
- printf("Current value: 0x%04X\n", current_value);
-
- // 3. 检查是否需要更新(使用小端序比较)
- if (current_value == data) {
- enable_global_irq(CSR_MSTATUS_MIE_MASK);
- printf("Data unchanged, skip\n");
- return;
- }
-
- // 4. 准备新数据(小端序存储)
- sector_buffer[offset_in_sector] = data & 0xFF; // 低字节
- sector_buffer[offset_in_sector + 1] = (data >> 8) & 0xFF; // 高字节
-
- // 5. 擦除扇区
- status = rom_xpi_nor_erase_sector(HPM_XPI0,
- xpi_xfer_channel_auto,
- &s_xpi_nor_config,
- sector_start_offset);
- if (status != status_success) {
- enable_global_irq(CSR_MSTATUS_MIE_MASK);
- printf("ERROR: erase failed\n");
- return;
- }
-
- // 6. 写回整个扇区
- status = rom_xpi_nor_program(HPM_XPI0,
- xpi_xfer_channel_auto,
- &s_xpi_nor_config,
- (uint32_t *)sector_buffer,
- sector_start_offset,
- FLASH_SECTOR_SIZE);
-
- fencei();
- enable_global_irq(CSR_MSTATUS_MIE_MASK);
-
- if (status != status_success) {
- printf("ERROR: program failed: %ld\n", status);
- } else {
- printf("Halfword written successfully\n");
- }
- }
- int hpm_flash_read_nbytes(uint32_t read_addr, uint8_t *read_buf, int32_t read_num)
- {
- flash_init();
-
- // 参数检查
- if (read_buf == NULL || read_num <= 0) {
- printf("ERROR: invalid parameters\n");
- return -1;
- }
-
- // 转换为偏移地址
- uint32_t offset = read_addr - FLASH_BASE_ADDR;
-
- // 地址范围检查
- uint32_t flash_size;
- rom_xpi_nor_get_property(HPM_XPI0, &s_xpi_nor_config,
- xpi_nor_property_total_size, &flash_size);
- if (offset + read_num > flash_size) {
- printf("ERROR: address out of range\n");
- return -1;
- }
-
- hpm_stat_t status = rom_xpi_nor_read(HPM_XPI0,
- xpi_xfer_channel_auto,
- &s_xpi_nor_config,
- (uint32_t *)read_buf,
- offset,
- read_num);
-
- if (status != status_success) {
- printf("ERROR: flash read failed at offset 0x%x\n", offset);
- return -1;
- }
-
- return read_num;
- }
- /**
- * @brief 清除升级标志
- */
- void V8M_clear_iap_flag(void)
- {
-
- disable_global_irq(CSR_MSTATUS_MIE_MASK);
-
- // 写入0xFFFF表示无升级标志
- flash_program_halfword(IAP_FLAG_ADDR, 0xFFFF);
-
- enable_global_irq(CSR_MSTATUS_MIE_MASK);
- }
- /**
- * @brief 记录升级标志
- */
- void V8M_record_iap_flag(void)
- {
- disable_global_irq(CSR_MSTATUS_MIE_MASK);
-
- flash_program_halfword(IAP_FLAG_ADDR, IAP_FLAG_NEED_UPDATE);
-
- enable_global_irq(CSR_MSTATUS_MIE_MASK);
- }
- /**
- * @brief 检查升级标志位
- */
- void V8M_check_iap_flag(void)
- {
- uint16_t iap_flag;
-
- // 读取标志
- hpm_flash_read_nbytes(IAP_FLAG_ADDR, (uint8_t*)&iap_flag, sizeof(iap_flag));
-
- // 如果需要升级,清除标志
- if (iap_flag == IAP_FLAG_NEED_UPDATE) {
- V8M_clear_iap_flag();
-
- }
- }
- #ifdef FLASH_TEST
- // 20250324 test pass
- /**
- * @brief 简单快速测试(只测试一次写入和读取)
- */
- int test_flash_simple(void)
- {
- uint16_t test_value = 0xABCD;
- uint16_t read_value = 0;
-
- printf("\nSimple Flash Test\n");
- printf("Write address: 0x%08X\n", IAP_FLAG_ADDR);
- printf("Write value: 0x%04X\n", test_value);
-
- // 写入
- printf("Writing... ");
- flash_program_halfword(IAP_FLAG_ADDR, test_value);
- // flash_program_halfword(IAP_FLAG_ADDR+2, 0xCDAB);
- board_delay_ms(10);
- printf("Done\n");
-
- // 读取
- printf("Reading... ");
- int ret = hpm_flash_read_nbytes(IAP_FLAG_ADDR, (uint8_t*)&read_value, sizeof(read_value));
- if (ret != sizeof(read_value)) {
- printf("Failed\n");
- return -1;
- }
- printf("Read: 0x%04X\n", read_value);
-
- // 验证
- if (read_value == test_value) {
- printf("Result: PASSED\n");
- return 0;
- } else {
- printf("Result: FAILED (expected 0x%04X, got 0x%04X)\n", test_value, read_value);
- return -1;
- }
- }
- /**
- * @brief 测试 IAP 标志功能
- */
- int test_iap_flag(void)
- {
- printf("\nIAP Flag Test\n");
- printf("========================================\n");
-
- // 1. 清除标志
- printf("1. Clearing flag...\n");
- V8M_clear_iap_flag();
- board_delay_ms(10);
-
- // 2. 检查标志(应该没有升级标志)
- printf("2. Checking flag (should be no update)...\n");
- uint16_t flag;
- hpm_flash_read_nbytes(IAP_FLAG_ADDR, (uint8_t*)&flag, sizeof(flag));
- printf(" Flag value: 0x%04X\n", flag);
-
- if (flag != 0xFFFF) {
- printf(" ERROR: Flag should be 0xFFFF\n");
- return -1;
- }
- printf(" PASS\n");
-
- // 3. 记录升级标志
- printf("3. Setting update flag...\n");
- V8M_record_iap_flag();
- board_delay_ms(10);
-
- // 4. 检查标志(应该为 0xABCD)
- printf("4. Checking flag (should be update needed)...\n");
- hpm_flash_read_nbytes(IAP_FLAG_ADDR, (uint8_t*)&flag, sizeof(flag));
- printf(" Flag value: 0x%04X\n", flag);
-
- if (flag != IAP_FLAG_NEED_UPDATE) {
- printf(" ERROR: Flag should be 0x%04X\n", IAP_FLAG_NEED_UPDATE);
- return -1;
- }
- printf(" PASS\n");
-
- // 5. 检查并清除标志
- printf("5. Checking and clearing flag...\n");
- V8M_check_iap_flag();
- board_delay_ms(10);
-
- // 6. 验证已清除
- printf("6. Verifying flag cleared...\n");
- hpm_flash_read_nbytes(IAP_FLAG_ADDR, (uint8_t*)&flag, sizeof(flag));
- printf(" Flag value: 0x%04X\n", flag);
-
- if (flag != 0xFFFF) {
- printf(" ERROR: Flag should be 0xFFFF\n");
- return -1;
- }
- printf(" PASS\n");
-
- printf("========================================\n");
- printf("IAP Flag Test PASSED\n");
-
- return 0;
- }
- /**
- * @brief 主测试函数,可选择运行全部测试或单个测试
- */
- void run_flash_tests(void)
- {
- printf("\n");
- printf("╔══════════════════════════════════════════════════════╗\n");
- printf("║ HPM6750 Flash Read/Write Test Suite ║\n");
- printf("╚══════════════════════════════════════════════════════╝\n");
-
- // 先运行简单测试
- if (test_flash_simple() != 0) {
- printf("\nSimple test failed! Aborting...\n");
- return;
- }
-
- printf("\n");
-
- // 运行 IAP 标志测试
- if (test_iap_flag() != 0) {
- printf("\nIAP flag test failed!\n");
- return;
- }
-
- printf("\n");
-
- }
- #endif
|