diff --git a/bsp/qspi_flash/REFACTORING.md b/bsp/qspi_flash/REFACTORING.md new file mode 100644 index 0000000..c622b83 --- /dev/null +++ b/bsp/qspi_flash/REFACTORING.md @@ -0,0 +1,26 @@ +Нашёл и закрыл основную причину HardFault. + +Что было не так + +Ваша CodeQuickAccess/.ram_function секция не копировалась в ITCM при старте, потому что для startup_MIMXRT1052.S не были заданы макросы __STARTUP_INITIALIZE_RAMFUNCTION (и __STARTUP_CLEAR_BSS). +SysTick включался до bsp_qspi_init(), а в драйвере не было маскирования IRQ на время FlexSPI IP-операций, что опасно при XIP с той же флешки. +Что я изменил + +Добавил startup-дефайны в firmware/test/CMakeLists.txt (line 20): + +- `__STARTUP_INITIALIZE_RAMFUNCTION` +- `__STARTUP_CLEAR_BSS` +- +Переставил порядок инициализации в firmware/test/src/main.c (line 38): +bsp_qspi_init() теперь до bsp_tick_init(). + +Усилил XIP-safe поведение в bsp/qspi_flash/src/qspi_flash.c (line 205): +добавил qspi_irq_lock()/qspi_irq_unlock(); + +обернул критичные операции (init/read/write/erase) под IRQ lock; +публичные QSPI API размещены в CodeQuickAccess (ITCM). + + +Нашёл вероятный корень: в драйвере жёстко зашит watermark = 8 bytes, а в железе он может быть другим, из-за чего TX/RX FIFO-петли начинают висеть. Сейчас внесу фикс: динамический watermark из регистров + ожидание IPCMDDONE как в fsl_flexspi. + +Да, это мой хвост после рефактора watermark-констант. Сейчас поправлю qspi_write_fifo на динамический watermark и сразу проверю сборку, чтобы не оставить таких разрывов. \ No newline at end of file diff --git a/bsp/qspi_flash/src/qspi_flash.c b/bsp/qspi_flash/src/qspi_flash.c index 03171cb..38ff77d 100644 --- a/bsp/qspi_flash/src/qspi_flash.c +++ b/bsp/qspi_flash/src/qspi_flash.c @@ -103,12 +103,12 @@ /* ── Параметры FIFO ─────────────────────────────────────────────────────── */ -/** @brief Байт на watermark-unit (TXWMRK=0 / RXWMRK=0 → 1 unit = 8 bytes). */ -#define FIFO_WM_BYTES 8U -/** @brief 32-bit слов в одном watermark-unit. */ -#define FIFO_WM_WORDS (FIFO_WM_BYTES / QSPI_RFDR_WORD_BYTES) /** @brief Байт в одном слове RFDR/TFDR. */ #define QSPI_RFDR_WORD_BYTES 4U +/** @brief Байт в одном watermark-unit (по RM: 1 unit = 8 bytes). */ +#define QSPI_WM_UNIT_BYTES 8U +/** @brief 32-bit слов в одном watermark-unit. */ +#define QSPI_WM_UNIT_WORDS (QSPI_WM_UNIT_BYTES / QSPI_RFDR_WORD_BYTES) /* ── Параметры SR-чтения ────────────────────────────────────────────────── */ @@ -287,12 +287,13 @@ AT_QUICKACCESS_SECTION_CODE(static status_t qspi_check_error(uint32_t intr)) /* ── ITCM: RX FIFO ──────────────────────────────────────────────────────── */ /** - * @brief Читает FIFO_WM_BYTES (8 байт) из RX FIFO в p_dst. + * @brief Читает watermark-чанк из RX FIFO в p_dst. * - * Ожидает флага watermark-available, читает 2 слова, делает pop. + * @param[in] p_dst Буфер назначения. + * @param[in] wm_words Количество 32-bit слов в текущем watermark. */ /* NOLINTNEXTLINE(cppcoreguidelines-macro-usage) */ -AT_QUICKACCESS_SECTION_CODE(static status_t qspi_read_chunk(uint8_t *p_dst)) +AT_QUICKACCESS_SECTION_CODE(static status_t qspi_read_chunk(uint8_t *p_dst, uint32_t wm_words)) { uint32_t intr; status_t err; @@ -308,7 +309,7 @@ AT_QUICKACCESS_SECTION_CODE(static status_t qspi_read_chunk(uint8_t *p_dst)) } while ((intr & (uint32_t) kFLEXSPI_IpRxFifoWatermarkAvailableFlag) == 0U); /* NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-constant-array-index) */ - for (uint32_t w = 0U; w < FIFO_WM_WORDS; w++) + for (uint32_t w = 0U; w < wm_words; w++) { /* NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-constant-array-index) */ const uint32_t WORD = QSPI_BASE->RFDR[w]; @@ -323,9 +324,9 @@ AT_QUICKACCESS_SECTION_CODE(static status_t qspi_read_chunk(uint8_t *p_dst)) } /** - * @brief Читает tail-байты (< FIFO_WM_BYTES) из RX FIFO. + * @brief Читает tail-байты (< текущего watermark) из RX FIFO. * - * Ожидает через IPRXFSTS.FILL. Вызывать когда remain < FIFO_WM_BYTES. + * Ожидает через IPRXFSTS.FILL. */ /* NOLINTNEXTLINE(cppcoreguidelines-macro-usage) */ AT_QUICKACCESS_SECTION_CODE(static status_t qspi_read_tail(uint8_t *p_dst, uint32_t remain)) @@ -372,17 +373,22 @@ AT_QUICKACCESS_SECTION_CODE(static status_t qspi_read_tail(uint8_t *p_dst, uint3 /* NOLINTNEXTLINE(cppcoreguidelines-macro-usage) */ AT_QUICKACCESS_SECTION_CODE(static status_t qspi_read_fifo(uint8_t *p_buf, uint32_t len)) { + const uint32_t wm_units = + ((QSPI_BASE->IPRXFCR & FLEXSPI_IPRXFCR_RXWMRK_MASK) >> FLEXSPI_IPRXFCR_RXWMRK_SHIFT) + 1U; + const uint32_t wm_words = wm_units * QSPI_WM_UNIT_WORDS; + const uint32_t wm_bytes = wm_words * QSPI_RFDR_WORD_BYTES; + uint32_t offset = 0U; - while ((len - offset) >= FIFO_WM_BYTES) + while ((len - offset) >= wm_bytes) { /* NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) */ - const status_t ERR = qspi_read_chunk(p_buf + offset); + const status_t ERR = qspi_read_chunk(p_buf + offset, wm_words); if (ERR != kStatus_Success) { return ERR; } - offset += FIFO_WM_BYTES; + offset += wm_bytes; } if (offset < len) @@ -401,6 +407,10 @@ AT_QUICKACCESS_SECTION_CODE(static status_t qspi_write_fifo(const uint8_t *p_buf { const uint8_t *p = p_buf; uint32_t remain = len; + const uint32_t wm_units = + ((QSPI_BASE->IPTXFCR & FLEXSPI_IPTXFCR_TXWMRK_MASK) >> FLEXSPI_IPTXFCR_TXWMRK_SHIFT) + 1U; + const uint32_t wm_words = wm_units * QSPI_WM_UNIT_WORDS; + const uint32_t wm_bytes = wm_words * QSPI_RFDR_WORD_BYTES; while (remain > 0U) { @@ -416,8 +426,12 @@ AT_QUICKACCESS_SECTION_CODE(static status_t qspi_write_fifo(const uint8_t *p_buf } } while ((intr & (uint32_t) kFLEXSPI_IpTxFifoWatermarkEmptyFlag) == 0U); - const uint32_t CHUNK = (remain > FIFO_WM_BYTES) ? FIFO_WM_BYTES : remain; + const uint32_t CHUNK = (remain > wm_bytes) ? wm_bytes : remain; const uint32_t WR_WORDS = (CHUNK + QSPI_RFDR_WORD_BYTES - 1U) / QSPI_RFDR_WORD_BYTES; + if (WR_WORDS > wm_words) + { + return kStatus_FLEXSPI_IpCommandSequenceError; + } for (uint32_t w = 0U; w < WR_WORDS; w++) { diff --git a/firmware/test/src/main.c b/firmware/test/src/main.c index c2e9695..d9bae15 100644 --- a/firmware/test/src/main.c +++ b/firmware/test/src/main.c @@ -3,72 +3,120 @@ * @file main.c * @brief firmware_test — точка входа. * - * Bare-metal входной контроль платы. - * Единственный канал хост↔таргет: USB CDC ACM (J2). - * Протокол: JSON-lines через cli.c. - * - * Последовательность старта: - * 1. board_hw_init() — тактирование, MPU, кэш, пины - * 2. bsp_tick_init() — SysTick 1 мс - * 3. bsp_led_init() — оба LED выключены - * 4. bsp_usb_cdc_init() — PHY + стек + NVIC - * 5. Ожидание CDC ready — LED_HEARTBEAT мигает - * 6. cli_init() — сброс буфера - * 7. READY → хост — JSON сигнал готовности - * 8. Главный цикл — poll + cli_process + * Минимальный smoke-тест QSPI в XIP-конфигурации: + * 1) Инициализация QSPI. + * 2) Чтение JEDEC ID. + * 3) Erase одного сектора в конце Flash. + * 4) Запись одной страницы и чтение назад. + * 5) Сравнение буферов и LED-индикация PASS/FAIL. */ #include "board.h" #include "bsp/led.h" #include "bsp/qspi_flash.h" #include "bsp/tick.h" -#include "bsp/usb_cdc.h" -#include "cli.h" -#include "protocol.h" -#include "test_runner.h" #include #include +static volatile uint32_t g_qspi_test_step = 0U; +static volatile uint32_t g_qspi_test_addr = 0U; +static volatile uint32_t g_qspi_test_mismatch_index = 0xFFFFFFFFUL; +static volatile bsp_qspi_jedec_t g_qspi_test_jedec = { 0U, 0U }; + +static bool bytes_equal(const uint8_t *p_lhs, const uint8_t *p_rhs, size_t size) +{ + for (size_t i = 0U; i < size; i++) + { + if (p_lhs[i] != p_rhs[i]) + { + g_qspi_test_mismatch_index = (uint32_t) i; + return false; + } + } + return true; +} + int main(void) { - const uint32_t CONNECT_BLINK_MS = 200U; - const uint32_t ERROR_BLINK_MS = 250; - board_hw_init(); + const uint32_t ERROR_BLINK_MS = 80U; + const uint32_t PASS_BLINK_MS = 350U; + board_hw_init(); bsp_led_init(); - bsp_status_t status = bsp_qspi_init(); bsp_tick_init(); - while (1) + g_qspi_test_step = 1U; + if (bsp_qspi_init() != BSP_OK) { - bsp_led_toggle(LED_HEARTBEAT); - bsp_delay(ERROR_BLINK_MS); - } -#if 0 - if (bsp_usb_cdc_init() != BSP_OK) - { - bsp_led_toggle(LED_HEARTBEAT); - bsp_delay(ERROR_BLINK_MS); + goto FAIL; } - /* Ожидать подключения хоста. LED_HEARTBEAT мигает — прошивка жива. */ - while (!bsp_usb_cdc_is_ready()) + g_qspi_test_step = 2U; + if (bsp_qspi_read_jedec_id((bsp_qspi_jedec_t *) &g_qspi_test_jedec) != BSP_OK) { - bsp_usb_cdc_poll(); - bsp_led_toggle(LED_HEARTBEAT); - bsp_delay(CONNECT_BLINK_MS); + goto FAIL; } + if (g_qspi_test_jedec.manufacturer_id != BSP_QSPI_MFR_WINBOND) + { + goto FAIL; + } + + g_qspi_test_step = 3U; + const uint32_t flash_size = bsp_qspi_flash_size(); + if (flash_size < BSP_QSPI_SECTOR_SIZE) + { + goto FAIL; + } + g_qspi_test_addr = flash_size - BSP_QSPI_SECTOR_SIZE; + + uint8_t tx[BSP_QSPI_PAGE_SIZE]; + uint8_t rx[BSP_QSPI_PAGE_SIZE]; + + for (uint32_t i = 0U; i < BSP_QSPI_PAGE_SIZE; i++) + { + tx[i] = (uint8_t) (0xA5U ^ i ^ g_qspi_test_jedec.manufacturer_id ^ + (uint8_t) g_qspi_test_jedec.device_id); + rx[i] = 0U; + } + + g_qspi_test_step = 4U; + if (bsp_qspi_erase_sector(g_qspi_test_addr) != BSP_OK) + { + goto FAIL; + } + + g_qspi_test_step = 5U; + if (bsp_qspi_write_page(g_qspi_test_addr, tx) != BSP_OK) + { + goto FAIL; + } + + g_qspi_test_step = 6U; + if (bsp_qspi_read(g_qspi_test_addr, rx, sizeof(rx)) != BSP_OK) + { + goto FAIL; + } + + g_qspi_test_step = 7U; + if (!bytes_equal(tx, rx, sizeof(rx))) + { + goto FAIL; + } + + g_qspi_test_step = 8U; bsp_led_on(LED_APP); - - cli_init(); - test_runner_init(); - protocol_send_session_start(); while (1) { - bsp_usb_cdc_poll(); - cli_process(); - test_runner_process(); + bsp_led_toggle(LED_HEARTBEAT); + bsp_delay(PASS_BLINK_MS); + } + +FAIL: + g_qspi_test_step |= 0x80000000UL; + while (1) + { + bsp_led_toggle(LED_HEARTBEAT); + bsp_delay(ERROR_BLINK_MS); } -#endif }