#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