# Added: sdram service test
This commit is contained in:
parent
bf324b2aa2
commit
0a45805350
10 changed files with 1035 additions and 6 deletions
|
|
@ -16,6 +16,7 @@ add_subdirectory(opto)
|
|||
add_subdirectory(can)
|
||||
add_subdirectory(button)
|
||||
add_subdirectory(usb_cdc)
|
||||
add_subdirectory(sdram)
|
||||
|
||||
# Подавляем предупреждения при компиляции собственных .c файлов библиотеки
|
||||
target_compile_options(bsp_board PRIVATE -w)
|
||||
|
|
|
|||
25
bsp/sdram/CMakeLists.txt
Normal file
25
bsp/sdram/CMakeLists.txt
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
# bsp/sdram/CMakeLists.txt bsp_sdram — верификация и тест внешней SDRAM
|
||||
# (MT48LC16M16A2, 32 MB)
|
||||
#
|
||||
# Архитектурное решение [DECISION]: SEMC инициализируется DCD до main().
|
||||
# bsp_sdram_init() только верифицирует доступность памяти — не трогает SEMC
|
||||
# регистры.
|
||||
#
|
||||
# Зависимости: sdk_semc — fsl_semc.h нужен для SEMC_GetStatusFlags() в
|
||||
# верификации bsp_tick — bsp_tick_get_ms() для замера времени тест-паттернов
|
||||
|
||||
if(BUILD_TESTS_HOST)
|
||||
return()
|
||||
endif()
|
||||
|
||||
add_library(bsp_sdram STATIC src/sdram.c)
|
||||
|
||||
target_include_directories(
|
||||
bsp_sdram
|
||||
PUBLIC include/
|
||||
PRIVATE src/)
|
||||
|
||||
target_link_libraries(
|
||||
bsp_sdram
|
||||
PUBLIC bsp_status
|
||||
PRIVATE bsp_board bsp_tick sdk_semc)
|
||||
|
|
@ -0,0 +1,85 @@
|
|||
/**
|
||||
* @file sdram.h
|
||||
* @brief BSP для внешней SDRAM MT48LC16M16A2 (32 МБ, шина 16 бит).
|
||||
*
|
||||
* Архитектурное ограничение [DECISION]:
|
||||
* SEMC инициализируется DCD до вызова main(). Этот модуль не трогает
|
||||
* регистры SEMC — только верифицирует работоспособность памяти.
|
||||
*
|
||||
* Карта памяти:
|
||||
* 0x80000000 — начало SDRAM (SEMC BR0)
|
||||
* 0x81FFFFFF — конец SDRAM (32 MB)
|
||||
* 0x81E00000 — начало non-cacheable региона (USB DMA, 2 MB)
|
||||
*
|
||||
* Тестовый регион (не пересекается с .data/.bss и non-cacheable):
|
||||
* 0x80200000 — начало (2 MB offset от базы)
|
||||
*
|
||||
* Использование:
|
||||
* @code
|
||||
* bsp_sdram_result_t result;
|
||||
*
|
||||
* if (bsp_sdram_init() != BSP_OK) { // DCD не отработал
|
||||
* handle_critical_error();
|
||||
* }
|
||||
*
|
||||
* bsp_sdram_test_fast(&result); // ~50 мс, 64 KB
|
||||
* bsp_sdram_test_full(&result); // ~2–5 с, 1 MB
|
||||
* @endcode
|
||||
*/
|
||||
|
||||
#ifndef BSP_SDRAM_H_
|
||||
#define BSP_SDRAM_H_
|
||||
|
||||
#include "bsp/status.h"
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
/**
|
||||
* @brief Размер расширенного тестового региона, байт (27 MB).
|
||||
*
|
||||
* От BSP_SDRAM_TEST_BASE_ADDR (0x80200000) до 0x81D00000.
|
||||
* Оставляет 1 MB защитную зону перед heap и стеком firmware_test
|
||||
* (stack top = 0x81E00000 − 4KB heap − 4KB stack ≈ 0x81DFE000).
|
||||
*/
|
||||
#define BSP_SDRAM_TEST_EXTENDED_SIZE 0x01B00000UL
|
||||
|
||||
/* ── Константы ─────────────────────────────────────────────────────────── */
|
||||
|
||||
/** @brief Базовый адрес SDRAM (SEMC BR0). */
|
||||
#define BSP_SDRAM_BASE_ADDR 0x80000000UL
|
||||
|
||||
/** @brief Полный размер SDRAM, байт (32 MB). */
|
||||
#define BSP_SDRAM_SIZE_BYTES 0x02000000UL
|
||||
|
||||
/**
|
||||
* @brief Базовый адрес тестового региона.
|
||||
*
|
||||
* Смещение 2 MB от базы — гарантированно выше .data/.bss firmware_test
|
||||
* и ниже non-cacheable региона (0x81E00000).
|
||||
*/
|
||||
#define BSP_SDRAM_TEST_BASE_ADDR 0x80200000UL
|
||||
|
||||
/** @brief Размер быстрого теста, байт (64 KB). */
|
||||
#define BSP_SDRAM_TEST_FAST_SIZE 0x00010000UL
|
||||
|
||||
/** @brief Размер полного теста, байт (1 MB). */
|
||||
#define BSP_SDRAM_TEST_FULL_SIZE 0x00100000UL
|
||||
|
||||
/* ── Public API ────────────────────────────────────────────────────────── */
|
||||
|
||||
/**
|
||||
* @brief Верифицировать доступность SDRAM.
|
||||
*
|
||||
* Проверяет что SEMC контроллер инициализирован DCD и SDRAM отвечает —
|
||||
* выполняет минимальный write/read/verify на первых 4 байтах тестового
|
||||
* региона. Не затрагивает .data/.bss прошивки.
|
||||
*
|
||||
* @note Не реинициализирует SEMC — DCD уже сделал это до main().
|
||||
*
|
||||
* @retval BSP_OK SDRAM доступна и отвечает корректно.
|
||||
* @retval BSP_ERR_INIT SEMC не готов (DCD не отработал).
|
||||
* @retval BSP_ERR_TIMEOUT SEMC занят дольше ожидаемого.
|
||||
*/
|
||||
bsp_status_t bsp_sdram_init(void);
|
||||
|
||||
#endif /* BSP_SDRAM_H_ */
|
||||
|
|
@ -0,0 +1,122 @@
|
|||
/**
|
||||
* @file sdram.c
|
||||
* @brief Верификация внешней SDRAM MT48LC16M16A2 (32 МБ, шина 16 бит).
|
||||
*
|
||||
* SEMC инициализируется DCD до main() — этот модуль только проверяет
|
||||
* доступность памяти. Регистры SEMC не модифицируются.
|
||||
*
|
||||
* Кэш: SDRAM настроена как Normal Write-Back cacheable (MPU Region 8).
|
||||
* Верификация требует явного cache maintenance перед readback — иначе
|
||||
* чтение попадает в кэш и физическая SDRAM не тестируется.
|
||||
*/
|
||||
|
||||
#include "bsp/sdram.h"
|
||||
|
||||
#include "bsp/tick.h"
|
||||
#include "fsl_semc.h"
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
/* ── Константы ─────────────────────────────────────────────────────────── */
|
||||
|
||||
/** @brief Таймаут ожидания готовности SEMC контроллера, мс. */
|
||||
#define SDRAM_SEMC_IDLE_TIMEOUT_MS 10U
|
||||
|
||||
/**
|
||||
* @brief Размер кэш-линии Cortex-M7, байт.
|
||||
*
|
||||
* SCB_CleanDCache_by_Addr / SCB_InvalidateDCache_by_Addr требуют
|
||||
* выравнивания адреса и размера по кэш-линии.
|
||||
* BSP_SDRAM_TEST_BASE_ADDR = 0x80200000 — кратен 32 байтам. ✓
|
||||
*/
|
||||
#define SDRAM_CACHE_LINE_BYTES 32U
|
||||
|
||||
/** @brief Эталонный паттерн верификации (чередующиеся биты). */
|
||||
#define SDRAM_VERIFY_PATTERN_A 0xA5A5A5A5UL
|
||||
|
||||
/** @brief Инверсия эталонного паттерна. */
|
||||
#define SDRAM_VERIFY_PATTERN_B 0x5A5A5A5AUL
|
||||
|
||||
/* ── Состояние модуля ──────────────────────────────────────────────────── */
|
||||
|
||||
static bool s_initialised = false;
|
||||
|
||||
/* ── Внутренние функции ────────────────────────────────────────────────── */
|
||||
|
||||
/**
|
||||
* @brief Дождаться перехода SEMC в состояние IDLE.
|
||||
*
|
||||
* @return BSP_OK при успехе, BSP_ERR_TIMEOUT если SEMC не ответил.
|
||||
*/
|
||||
static bsp_status_t wait_semc_idle(void)
|
||||
{
|
||||
const uint32_t START_MS = bsp_tick_get_ms();
|
||||
|
||||
while ((SEMC->STS0 & SEMC_STS0_IDLE_MASK) == 0U)
|
||||
{
|
||||
if ((bsp_tick_get_ms() - START_MS) >= SDRAM_SEMC_IDLE_TIMEOUT_MS)
|
||||
{
|
||||
return BSP_ERR_TIMEOUT;
|
||||
}
|
||||
}
|
||||
|
||||
return BSP_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Сбросить кэш-линию по адресу тестового региона.
|
||||
*
|
||||
* Clean (запись dirty линии в SDRAM) + Invalidate (следующее чтение
|
||||
* пойдёт в SDRAM, не в кэш) + DSB (барьер завершения операции).
|
||||
*/
|
||||
static void flush_cache_at_test_base(void)
|
||||
{
|
||||
uint32_t *const p_addr = (uint32_t *) BSP_SDRAM_TEST_BASE_ADDR;
|
||||
|
||||
SCB_CleanDCache_by_Addr(p_addr, (int32_t) SDRAM_CACHE_LINE_BYTES);
|
||||
SCB_InvalidateDCache_by_Addr(p_addr, (int32_t) SDRAM_CACHE_LINE_BYTES);
|
||||
__DSB();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Записать слово в тестовый адрес, сбросить кэш, прочитать обратно.
|
||||
*
|
||||
* @param[in] pattern Значение для записи и верификации.
|
||||
* @return BSP_OK если readback совпал, BSP_ERR_INIT при расхождении.
|
||||
*/
|
||||
static bsp_status_t verify_word(uint32_t pattern)
|
||||
{
|
||||
volatile uint32_t *const p_test = (volatile uint32_t *) BSP_SDRAM_TEST_BASE_ADDR;
|
||||
|
||||
*p_test = pattern;
|
||||
flush_cache_at_test_base();
|
||||
|
||||
return (*p_test == pattern) ? BSP_OK : BSP_ERR_INIT;
|
||||
}
|
||||
|
||||
/* ── Public API ────────────────────────────────────────────────────────── */
|
||||
|
||||
bsp_status_t bsp_sdram_init(void)
|
||||
{
|
||||
bsp_status_t status = wait_semc_idle();
|
||||
if (status != BSP_OK)
|
||||
{
|
||||
return status;
|
||||
}
|
||||
|
||||
status = verify_word(SDRAM_VERIFY_PATTERN_A);
|
||||
if (status != BSP_OK)
|
||||
{
|
||||
return BSP_ERR_INIT;
|
||||
}
|
||||
|
||||
status = verify_word(SDRAM_VERIFY_PATTERN_B);
|
||||
if (status != BSP_OK)
|
||||
{
|
||||
return BSP_ERR_INIT;
|
||||
}
|
||||
|
||||
s_initialised = true;
|
||||
return BSP_OK;
|
||||
}
|
||||
|
|
@ -9,22 +9,22 @@ add_executable(
|
|||
src/cli.c
|
||||
src/protocol.c
|
||||
src/test_runner.c
|
||||
src/tests/test_sdram.c
|
||||
${BSP_GENERATED}/clock_config.c
|
||||
${BSP_STARTUP_FILE}
|
||||
${BSP_SYSCALLS_FILE})
|
||||
|
||||
target_include_directories(firmware_test PRIVATE src/)
|
||||
|
||||
target_compile_definitions(${TARGET_NAME}
|
||||
PRIVATE BSP_UART_HOST_RX_BUFFER_SIZE=512)
|
||||
target_compile_definitions(
|
||||
${TARGET_NAME} PRIVATE BSP_UART_HOST_RX_BUFFER_SIZE=512 BOARD_MPU_SDRAM=1)
|
||||
#
|
||||
# -----------------------------------------------------------------------------
|
||||
# Зависимости — только то что нужно для входного контроля bsp_board транзитивно
|
||||
# даёт: sdk_device, sdk_clock, sdk_common, CPU_MIMXRT1052CVJ5B, XIP_* дефайны
|
||||
# -----------------------------------------------------------------------------
|
||||
target_link_libraries(
|
||||
${TARGET_NAME} PRIVATE bsp_board bsp_led bsp_tick bsp_boot_xip bsp_status
|
||||
bsp_usb_cdc)
|
||||
target_link_libraries(${TARGET_NAME} PRIVATE bsp_board bsp_led bsp_tick
|
||||
bsp_boot_xip bsp_usb_cdc bsp_sdram)
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# Linker script
|
||||
|
|
|
|||
|
|
@ -41,9 +41,10 @@
|
|||
* ─────────────────────────────────────────────────────────────────────────*/
|
||||
|
||||
#ifndef UNIT_TEST
|
||||
|
||||
extern const test_module_t K_TEST_SDRAM;
|
||||
static const test_module_t *const k_registry[] = {
|
||||
/* populated starting from Этап 2 */
|
||||
&K_TEST_SDRAM,
|
||||
};
|
||||
|
||||
#define REGISTRY_SIZE (sizeof(k_registry) / sizeof(k_registry[0]))
|
||||
|
|
|
|||
418
firmware/test/src/tests/README.md
Normal file
418
firmware/test/src/tests/README.md
Normal file
|
|
@ -0,0 +1,418 @@
|
|||
# firmware_test — Тест-модули
|
||||
|
||||
> Расположение: `firmware/test/src/tests/README.md`
|
||||
>
|
||||
> Каждый тест-модуль реализует интерфейс `test_module_t` и регистрируется
|
||||
> в реестре `test_runner.c`. Этот документ описывает что именно проверяет
|
||||
> каждый тест, какие гарантии даёт, какие аппаратные инварианты должны
|
||||
> соблюдаться, и каковы ограничения.
|
||||
|
||||
---
|
||||
|
||||
## Содержание
|
||||
|
||||
- [Как читать эту таблицу](#как-читать-эту-таблицу)
|
||||
- [test_sdram — SDRAM 32 MB](#test_sdram--sdram-32-mb)
|
||||
- [test_qspi — QSPI Flash 8 MB](#test_qspi--qspi-flash-8-mb) *(запланирован)*
|
||||
- [test_usd — uSD SDIO](#test_usd--usd-sdio) *(запланирован)*
|
||||
- [test_display — Display RGB888](#test_display--display-rgb888) *(запланирован)*
|
||||
- [test_buttons — Test_But_1 / Test_But_2](#test_buttons--test_but_1--test_but_2) *(запланирован)*
|
||||
- [test_can — CAN loopback](#test_can--can-loopback) *(запланирован)*
|
||||
- [test_uart_ttl — UART TTL](#test_uart_ttl--uart-ttl) *(запланирован)*
|
||||
- [test_uart_iso — UART ISO / RS_RX](#test_uart_iso--uart-iso--rs_rx) *(запланирован)*
|
||||
- [test_opto — Opto-in EXT_IN1/IN2](#test_opto--opto-in-ext_in1in2) *(запланирован)*
|
||||
|
||||
---
|
||||
|
||||
## Как читать эту таблицу
|
||||
|
||||
**Critical** — при провале в `run_all` все последующие тесты получают `SKIP`.
|
||||
Некритические тесты могут упасть без остановки прогона.
|
||||
|
||||
**HIL** — тест требует внешних сигналов от M5StampPLC.
|
||||
Без стенда тест вернёт `SKIP` или `FAIL`.
|
||||
|
||||
**Тип confirm:**
|
||||
|
||||
- `pre_confirm` — test_runner ждёт JSON-ответа оператора до запуска `run()`.
|
||||
- `в run()` — тест сам вызывает `test_runner_wait_confirm()` внутри.
|
||||
- `prompt only` — отправляет `confirm_request` как UI-подсказку,
|
||||
ответ не ожидается (тест детектирует физическое событие сам).
|
||||
- `—` — неинтерактивный тест.
|
||||
|
||||
---
|
||||
|
||||
## test_sdram — SDRAM 32 MB
|
||||
|
||||
**Файл:** `test_sdram.c`
|
||||
**ID:** `sdram`
|
||||
**Critical:** ✅ | **HIL:** ❌ | **Confirm:** —
|
||||
|
||||
### Аппаратный контекст
|
||||
|
||||
| Параметр | Значение |
|
||||
|---|---|
|
||||
| Чип | MT48LC16M16A2 |
|
||||
| Объём | 32 MB |
|
||||
| Шина данных | 16 бит |
|
||||
| Интерфейс MCU | SEMC (0x402F0000) |
|
||||
| Базовый адрес | 0x80000000 |
|
||||
| Тестовый регион | 0x80200000 — 0x80A00000 |
|
||||
| MPU | Region 8: Normal WB cacheable (BOARD_MPU_SDRAM=1) |
|
||||
|
||||
### Инварианты выполнения
|
||||
|
||||
- SEMC инициализирован DCD **до** `main()` — `bsp_sdram_init()` только верифицирует.
|
||||
- MPU Region 8 (`Normal WB`) активен — кэш включён для SDRAM.
|
||||
- MPU Region 9 (`Non-cacheable`, 0x81E00000, 2MB) активен — USB DMA изолирован.
|
||||
- Тестовый регион не пересекается с `.data`/`.bss` прошивки (смещение +2 MB от базы).
|
||||
- Тестовый регион не пересекается с non-cacheable регионом (граница 0x81E00000).
|
||||
- Сброс кэша (`SCB_CleanDCache_by_Addr`) выполняется **после каждого** write-прохода —
|
||||
readback всегда из физической SDRAM, не из кэша.
|
||||
|
||||
### Что тестирует — четыре фазы
|
||||
|
||||
#### Фаза 1: Address bus (~1 мс)
|
||||
|
||||
Записывает уникальный байт в 24 позиции на степенях двойки
|
||||
(2^0..2^23 от TEST_BASE), каждую с индивидуальным cache line flush.
|
||||
|
||||
**Покрытие:** все 24 адресных бита MT48LC16M16A2 (13 row + 9 col + 2 bank).
|
||||
|
||||
**Ловит:**
|
||||
|
||||
- Address aliasing — замыкание адресных линий SEMC.
|
||||
- Неправильное подключение адресных линий к чипу.
|
||||
|
||||
**Не ловит:**
|
||||
|
||||
- Деградацию отдельных ячеек вне точек степеней двойки.
|
||||
|
||||
---
|
||||
|
||||
#### Фаза 2: Data bus (~1 с)
|
||||
|
||||
Walking ones (0x01, 0x02, ..., 0x80, 0x01, ...) и его инверсия
|
||||
на регионе 64 KB.
|
||||
|
||||
**Покрытие:** все 8 бит шины данных.
|
||||
|
||||
**Ловит:**
|
||||
|
||||
- Stuck-at-0 и stuck-at-1 фолты на битах шины данных.
|
||||
- Обрыв линии DATA между MCU и чипом.
|
||||
|
||||
**Не ловит:**
|
||||
|
||||
- Coupling между несмежными битами (для этого — фаза 3).
|
||||
|
||||
---
|
||||
|
||||
#### Фаза 3: Sequential integrity (~25 с)
|
||||
|
||||
Address pattern (`offset & 0xFF`) и его инверсия на регионе 2 MB,
|
||||
два паттерна × два прохода (write → flush → verify).
|
||||
|
||||
**Покрытие:** 2 MB непрерывного адресного пространства.
|
||||
|
||||
**Ловит:**
|
||||
|
||||
- Coupling faults между соседними ячейками.
|
||||
- Деградированные ячейки в тестируемом регионе.
|
||||
- Частичный address aliasing внутри 2 MB.
|
||||
|
||||
**Не ловит:**
|
||||
|
||||
- Деградацию ячеек вне тестируемых 2 MB (всего 32 MB в чипе).
|
||||
|
||||
---
|
||||
|
||||
#### Фаза 4: Retention (~2 с)
|
||||
|
||||
Address pattern на 256 KB: запись → `flush_dcache` → ожидание 200 мс → верификация.
|
||||
|
||||
200 мс ≈ 3 полных refresh-периода MT48LC16M16A2 (период = 64 мс).
|
||||
|
||||
**Покрытие:** 256 KB с задержкой на несколько refresh-циклов.
|
||||
|
||||
**Ловит:**
|
||||
|
||||
- Refresh timing failures — ячейки теряют данные между refresh-циклами.
|
||||
- Деградацию конденсаторов ячеек памяти (capacitor leakage).
|
||||
|
||||
**Не ловит:**
|
||||
|
||||
- Retention failures при температурных крайностях.
|
||||
|
||||
---
|
||||
|
||||
### Гарантии теста при PASS
|
||||
|
||||
- Все 24 адресных бита работают независимо без aliasing.
|
||||
- Все 8 бит шины данных переключаются корректно.
|
||||
- 2 MB последовательных ячеек не имеют coupling faults.
|
||||
- 256 KB удерживают данные минимум через 3 refresh-цикла.
|
||||
- SEMC контроллер инициализирован и отвечает.
|
||||
|
||||
### Интерпретация FAIL
|
||||
|
||||
`detail` содержит: `addr=0xXXXXXXXX exp=0xXX got=0xXX`
|
||||
|
||||
| Диапазон addr | Вероятная фаза | Диагноз |
|
||||
|---|---|---|
|
||||
| `0x80200000` — `0x80A00000` (степени двойки) | Фаза 1 | Address aliasing |
|
||||
| `0x80200000` — `0x80210000` | Фаза 2 | Stuck-at на шине данных |
|
||||
| `0x80200000` — `0x80400000` | Фаза 3 | Coupling или деградация ячейки |
|
||||
| `0x80200000` — `0x80240000` | Фаза 4 | Refresh timing failure |
|
||||
|
||||
**Анализ `exp` XOR `got`:** биты где `(exp ^ got) != 0` — сбойные линии шины данных.
|
||||
|
||||
### Ограничения
|
||||
|
||||
- Не покрывает все 32 MB (только 2 MB для sequential).
|
||||
- Не проверяет retention при нагреве или низком напряжении питания.
|
||||
- Не является заменой полного March C− алгоритма.
|
||||
- Во время теста (~28 с) USB CDC занят, `ping` не отвечает.
|
||||
|
||||
### Типичное время выполнения
|
||||
|
||||
| Фаза | ~Время |
|
||||
|---|---|
|
||||
| Address bus | < 1 мс |
|
||||
| Data bus | ~1 с |
|
||||
| Sequential | ~25 с |
|
||||
| Retention | ~2 с |
|
||||
| **Итого** | **~28 с** |
|
||||
|
||||
---
|
||||
|
||||
## test_qspi — QSPI Flash 8 MB
|
||||
|
||||
**Файл:** `test_qspi.c` *(не реализован — Этап 3)*
|
||||
**ID:** `qspi`
|
||||
**Critical:** ✅ | **HIL:** ❌ | **Confirm:** —
|
||||
|
||||
### Аппаратный контекст
|
||||
|
||||
| Параметр | Значение |
|
||||
|---|---|
|
||||
| Чип | W25Q64FVSSIG |
|
||||
| Объём | 8 MB |
|
||||
| Интерфейс | FlexSPI |
|
||||
| BSP | `bsp_qspi` |
|
||||
|
||||
### Что будет тестировать
|
||||
|
||||
- JEDEC ID верификация: manufacturer=`0xEF`, device=`0x4017`.
|
||||
- Erase последнего сектора (4 KB, не затрагивает прошивку).
|
||||
- Write 256 байт → Read → Compare.
|
||||
|
||||
### Гарантии при PASS
|
||||
|
||||
- FlexSPI контроллер инициализирован.
|
||||
- Чип отвечает с ожидаемым JEDEC ID.
|
||||
- Базовые операции чтения/записи/стирания работают.
|
||||
|
||||
---
|
||||
|
||||
## test_usd — uSD SDIO
|
||||
|
||||
**Файл:** `test_usd.c` *(не реализован — Этап 4)*
|
||||
**ID:** `usd`
|
||||
**Critical:** ❌ | **HIL:** ❌ | **Confirm:** pre_confirm
|
||||
|
||||
### Аппаратный контекст
|
||||
|
||||
| Параметр | Значение |
|
||||
|---|---|
|
||||
| Интерфейс | USDHC |
|
||||
| Файловая система | FatFS (SDK middleware) |
|
||||
| BSP | `bsp_usd` |
|
||||
| Pre-confirm prompt | "Вставьте карту microSD и нажмите OK" |
|
||||
|
||||
### Что будет тестировать
|
||||
|
||||
- Mount FAT — карта вставлена и файловая система читаема.
|
||||
- Write тестового файла → Read → Compare (деструктивно для одного файла).
|
||||
- Unmount.
|
||||
|
||||
### Поведение без карты
|
||||
|
||||
Оператор отказался от pre_confirm или таймаут 30 с → `TEST_STATUS_SKIP`.
|
||||
|
||||
---
|
||||
|
||||
## test_display — Display RGB888
|
||||
|
||||
**Файл:** `test_display.c` *(не реализован — Этап 5)*
|
||||
**ID:** `display`
|
||||
**Critical:** ❌ | **HIL:** ❌ | **Confirm:** в run()
|
||||
|
||||
### Что будет тестировать
|
||||
|
||||
Четыре шага с визуальным подтверждением оператора:
|
||||
|
||||
| Шаг | ID confirm | Промпт | Таймаут |
|
||||
|---|---|---|---|
|
||||
| Красный | `display_red` | "Экран залит красным?" | 15 с |
|
||||
| Зелёный | `display_green` | "Экран залит зелёным?" | 15 с |
|
||||
| Синий | `display_blue` | "Экран залит синим?" | 15 с |
|
||||
| Белый | `display_white` | "Экран залит белым?" | 15 с |
|
||||
|
||||
Итог = AND всех четырёх подтверждений.
|
||||
|
||||
### Гарантии при PASS
|
||||
|
||||
- RGB888 интерфейс выводит каждый цвет независимо.
|
||||
- Подсветка PWM активна (оператор видит экран).
|
||||
|
||||
### Интерпретация FAIL
|
||||
|
||||
`detail` содержит ID шага, который не был подтверждён: `"display_blue not confirmed"`.
|
||||
Это указывает на конкретный цветовой канал с дефектом.
|
||||
|
||||
---
|
||||
|
||||
## test_buttons — Test_But_1 / Test_But_2
|
||||
|
||||
**Файл:** `test_buttons.c` *(не реализован — Этап 5)*
|
||||
**ID:** `buttons`
|
||||
**Critical:** ❌ | **HIL:** ❌ | **Confirm:** prompt only
|
||||
|
||||
### Аппаратный контекст
|
||||
|
||||
| Кнопка | Пин MCU | GPIO |
|
||||
|---|---|---|
|
||||
| Test_But_1 | GPIO_B1_14 | GPIO2[30] |
|
||||
| Test_But_2 | GPIO_B1_15 | GPIO2[31] |
|
||||
|
||||
### Что будет тестировать
|
||||
|
||||
Два шага. Таргет отправляет `confirm_request` как инструкцию оператору
|
||||
и детектирует нажатие через `bsp_button` — JSON confirm не нужен.
|
||||
|
||||
| Шаг | ID | Промпт | Таймаут |
|
||||
|---|---|---|---|
|
||||
| Кнопка 1 | `btn1_press` | "Нажмите кнопку Test_But_1" | 10 с |
|
||||
| Кнопка 2 | `btn2_press` | "Нажмите кнопку Test_But_2" | 10 с |
|
||||
|
||||
### Гарантии при PASS
|
||||
|
||||
- Оба GPIO входа корректно регистрируют нажатие.
|
||||
- `bsp_button` debounce логика работает.
|
||||
|
||||
---
|
||||
|
||||
## test_can — CAN loopback
|
||||
|
||||
**Файл:** `test_can.c` *(не реализован — Этап 6)*
|
||||
**ID:** `can`
|
||||
**Critical:** ❌ | **HIL:** ✅ | **Confirm:** —
|
||||
|
||||
### Аппаратный контекст
|
||||
|
||||
| Параметр | Значение |
|
||||
|---|---|
|
||||
| BSP | `bsp_can` ✅ |
|
||||
| M5 | CAN интерфейсная плата → шина платы |
|
||||
|
||||
### Что будет тестировать
|
||||
|
||||
M5StampPLC отправляет CAN фрейм → плата принимает → сравниваем ID и данные.
|
||||
|
||||
### Гарантии при PASS
|
||||
|
||||
- CAN контроллер и трансивер работают.
|
||||
- Принятый фрейм совпадает с отправленным по ID и payload.
|
||||
|
||||
---
|
||||
|
||||
## test_uart_ttl — UART TTL
|
||||
|
||||
**Файл:** `test_uart_ttl.c` *(не реализован — Этап 6)*
|
||||
**ID:** `uart_ttl`
|
||||
**Critical:** ❌ | **HIL:** ✅ | **Confirm:** —
|
||||
|
||||
### Аппаратный контекст
|
||||
|
||||
| Параметр | Значение |
|
||||
|---|---|
|
||||
| BSP | `bsp_uart_host` ✅ |
|
||||
| M5 | UART ↔ UART TTL платы |
|
||||
|
||||
### Что будет тестировать
|
||||
|
||||
M5 отправляет пакет → плата получает → echo обратно → M5 верифицирует.
|
||||
|
||||
---
|
||||
|
||||
## test_uart_iso — UART ISO / RS_RX
|
||||
|
||||
**Файл:** `test_uart_iso.c` *(не реализован — Этап 6)*
|
||||
**ID:** `uart_iso`
|
||||
**Critical:** ❌ | **HIL:** ✅ | **Confirm:** —
|
||||
|
||||
### Аппаратный контекст
|
||||
|
||||
| Параметр | Значение |
|
||||
|---|---|
|
||||
| BSP | `bsp_opto` (rs_as_gpio=true) ✅ |
|
||||
| Пин MCU | GPIO_AD_B1_07 / GPIO1[23] |
|
||||
| Оптопара | PS2801-4 (неинвертирующая, active-HIGH) |
|
||||
| M5 | RLY2 → RS_RX |
|
||||
|
||||
### Что будет тестировать
|
||||
|
||||
M5 RLY2 активирует оптовход RS_RX → плата детектирует через `bsp_opto`.
|
||||
M5 RLY2 деактивирует → плата детектирует inactive.
|
||||
|
||||
### Инварианты
|
||||
|
||||
- `bsp_opto` инициализирован с `rs_as_gpio=true`.
|
||||
- Пин GPIO_AD_B1_07 переведён в GPIO INPUT (не LPUART3_RX).
|
||||
|
||||
---
|
||||
|
||||
## test_opto — Opto-in EXT_IN1/IN2
|
||||
|
||||
**Файл:** `test_opto.c` *(не реализован — Этап 6)*
|
||||
**ID:** `opto`
|
||||
**Critical:** ❌ | **HIL:** ✅ | **Confirm:** —
|
||||
|
||||
### Аппаратный контекст
|
||||
|
||||
| Канал | Пин MCU | GPIO | Оптопара | M5 |
|
||||
|---|---|---|---|---|
|
||||
| EXT_IN1 | GPIO_AD_B1_06 | GPIO1[22] | PS2801-4 | RLY3 |
|
||||
| EXT_IN2 | GPIO_AD_B1_05 | GPIO1[21] | PS2801-4 | RLY4 |
|
||||
|
||||
### Что будет тестировать
|
||||
|
||||
Каждый канал независимо: M5 активирует реле → плата детектирует ACTIVE →
|
||||
M5 деактивирует → плата детектирует INACTIVE.
|
||||
|
||||
### Гарантии при PASS
|
||||
|
||||
- Оба оптоизолированных входа корректно детектируют HIGH/LOW.
|
||||
- Debounce логика `bsp_opto` (MODE_LEVEL) работает корректно.
|
||||
|
||||
---
|
||||
|
||||
## Порядок в реестре `test_runner.c`
|
||||
|
||||
```c
|
||||
static const test_module_t *const k_registry[] = {
|
||||
&K_TEST_SDRAM, /* critical — первым */
|
||||
&K_TEST_QSPI, /* critical */
|
||||
&K_TEST_USD, /* non-critical, interactive, pre_confirm */
|
||||
&K_TEST_DISPLAY, /* non-critical, interactive, confirm в run() */
|
||||
&K_TEST_BUTTONS, /* non-critical, interactive, prompt only */
|
||||
&K_TEST_CAN, /* non-critical, HIL */
|
||||
&K_TEST_UART_TTL, /* non-critical, HIL */
|
||||
&K_TEST_UART_ISO, /* non-critical, HIL */
|
||||
&K_TEST_OPTO, /* non-critical, HIL */
|
||||
};
|
||||
```
|
||||
|
||||
Critical тесты идут первыми — при их провале HIL и интерактивные тесты
|
||||
пропускаются автоматически, экономя время диагностики.
|
||||
377
firmware/test/src/tests/test_sdram.c
Normal file
377
firmware/test/src/tests/test_sdram.c
Normal file
|
|
@ -0,0 +1,377 @@
|
|||
/**
|
||||
* @file test_sdram.c
|
||||
* @brief Тест-модуль firmware_test: SDRAM MT48LC16M16A2 (32 МБ, 16-bit bus).
|
||||
*
|
||||
* Четыре фазы (~30 с):
|
||||
*
|
||||
* 1. Address bus — 24 степени двойки (2^0..2^23 от TEST_BASE),
|
||||
* каждая с individual cache line flush.
|
||||
* Покрывает все 24 адресных бита MT48LC16M16A2
|
||||
* (13 row + 9 col + 2 bank). Время: < 1 мс.
|
||||
*
|
||||
* 2. Data bus — walking ones + инверсия, 64 KB.
|
||||
* Ловит stuck-at faults на всех 8 битах шины данных.
|
||||
* Время: ~0.8 с.
|
||||
*
|
||||
* 3. Sequential — address pattern + инверсия, 2 MB.
|
||||
* Ловит coupling faults между соседними ячейками.
|
||||
* Время: ~24 с.
|
||||
*
|
||||
* 4. Retention — address pattern, 256 KB: запись → flush → 200 мс → verify.
|
||||
* Ловит refresh timing failures.
|
||||
* Время: ~2 с.
|
||||
*
|
||||
* Кэш: тот же подход что в рабочей версии — SCB_CleanDCache_by_Addr + DSB.
|
||||
* USB: bsp_usb_cdc_poll() каждые SDRAM_USB_POLL_INTERVAL_BYTES.
|
||||
*/
|
||||
|
||||
#include "bsp/sdram.h"
|
||||
#include "bsp/tick.h"
|
||||
#include "bsp/usb_cdc.h"
|
||||
#include "fsl_common.h"
|
||||
#include "test_module.h"
|
||||
|
||||
#include <inttypes.h>
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
|
||||
/* ── Константы ─────────────────────────────────────────────────────────── */
|
||||
|
||||
/** @brief Интервал вызова bsp_usb_cdc_poll() при записи/чтении (4 KB). */
|
||||
#define SDRAM_USB_POLL_INTERVAL_BYTES 0x1000U
|
||||
|
||||
/** @brief Ширина walking ones паттерна, бит. */
|
||||
#define SDRAM_WALKING_ONES_BITS 8U
|
||||
|
||||
/**
|
||||
* @brief Количество адресных бит для address bus теста.
|
||||
*
|
||||
* MT48LC16M16A2: 13 row + 9 col + 2 bank = 24 бита.
|
||||
* Тестируем 2^0..2^23 байтовых смещений от TEST_BASE.
|
||||
* Максимальное смещение: 2^23 = 8 MB → 0x80200000 + 0x800000 = 0x80A00000
|
||||
* Граница SDRAM: 0x82000000. ✓
|
||||
*/
|
||||
#define SDRAM_ADDR_BUS_BITS 24U
|
||||
|
||||
/** @brief Размер кэш-линии Cortex-M7, байт. */
|
||||
#define SDRAM_CACHE_LINE_BYTES 32U
|
||||
|
||||
/**
|
||||
* @brief Размер sequential теста, байт (2 MB).
|
||||
*
|
||||
* Больший регион (vs 1 MB в оригинале) — лучше ловит coupling faults.
|
||||
*/
|
||||
#define SDRAM_SEQUENTIAL_SIZE 0x00200000UL
|
||||
|
||||
/**
|
||||
* @brief Размер retention теста, байт (256 KB).
|
||||
*
|
||||
* Достаточно для проверки refresh timing при 200 мс задержке.
|
||||
*/
|
||||
#define SDRAM_RETENTION_SIZE 0x00040000UL
|
||||
|
||||
/**
|
||||
* @brief Время удержания данных в retention тесте, мс.
|
||||
*
|
||||
* MT48LC16M16A2: период авто-refresh 64 мс.
|
||||
* 200 мс ≈ 3 refresh-периода.
|
||||
*/
|
||||
#define SDRAM_RETENTION_DELAY_MS 200U
|
||||
|
||||
/** @brief Шаг ожидания в retention тесте (для USB poll), мс. */
|
||||
#define SDRAM_RETENTION_POLL_STEP_MS 10U
|
||||
|
||||
/* ── Локальные типы ────────────────────────────────────────────────────── */
|
||||
|
||||
/**
|
||||
* @brief Информация о первой ошибке паттерн-прохода.
|
||||
*
|
||||
* fail_addr == 0 означает отсутствие ошибок.
|
||||
*/
|
||||
typedef struct
|
||||
{
|
||||
uint32_t fail_addr; /**< Адрес первой ошибки. */
|
||||
uint8_t expected; /**< Ожидаемое значение. */
|
||||
uint8_t got; /**< Прочитанное значение. */
|
||||
} sdram_fail_info_t;
|
||||
|
||||
/** @brief Функция-генератор одного байта тестового паттерна. */
|
||||
typedef uint8_t (*pattern_fn_t)(uint32_t offset);
|
||||
|
||||
/* ── Состояние модуля ──────────────────────────────────────────────────── */
|
||||
|
||||
static bool g_s_ready = false;
|
||||
|
||||
/* ── Паттерн-функции ───────────────────────────────────────────────────── */
|
||||
|
||||
static uint8_t pattern_walking_ones(uint32_t offset)
|
||||
{
|
||||
return (uint8_t) (1U << (offset % SDRAM_WALKING_ONES_BITS));
|
||||
}
|
||||
|
||||
static uint8_t pattern_walking_ones_inv(uint32_t offset)
|
||||
{
|
||||
return (uint8_t) (~(1U << (offset % SDRAM_WALKING_ONES_BITS)) & 0xFFU);
|
||||
}
|
||||
|
||||
static uint8_t pattern_addr(uint32_t offset)
|
||||
{
|
||||
return (uint8_t) (offset & 0xFFU);
|
||||
}
|
||||
|
||||
static uint8_t pattern_addr_inv(uint32_t offset)
|
||||
{
|
||||
return (uint8_t) (~offset & 0xFFU);
|
||||
}
|
||||
|
||||
/* ── Вспомогательные функции ───────────────────────────────────────────── */
|
||||
|
||||
/**
|
||||
* @brief Clean + Invalidate кэш для региона от TEST_BASE.
|
||||
*
|
||||
* Тот же подход что в рабочей версии — SCB_CleanDCache_by_Addr.
|
||||
*/
|
||||
static void flush_dcache(uint32_t size)
|
||||
{
|
||||
uint32_t *const P_BASE = (uint32_t *) BSP_SDRAM_TEST_BASE_ADDR;
|
||||
|
||||
SCB_CleanDCache_by_Addr(P_BASE, (int32_t) size);
|
||||
SCB_InvalidateDCache_by_Addr(P_BASE, (int32_t) size);
|
||||
__DSB();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Clean + Invalidate одну кэш-линию по произвольному байтовому адресу.
|
||||
*
|
||||
* Используется в address bus тесте для точечного сброса после каждой записи.
|
||||
* Адрес выравнивается на SDRAM_CACHE_LINE_BYTES автоматически.
|
||||
*
|
||||
* @param[in] byte_addr Любой байтовый адрес внутри нужной кэш-линии.
|
||||
*/
|
||||
static void flush_dcache_line_at(uint32_t byte_addr)
|
||||
{
|
||||
uint32_t *const P_LINE = (uint32_t *) (byte_addr & ~(SDRAM_CACHE_LINE_BYTES - 1U));
|
||||
|
||||
SCB_CleanDCache_by_Addr(P_LINE, (int32_t) SDRAM_CACHE_LINE_BYTES);
|
||||
SCB_InvalidateDCache_by_Addr(P_LINE, (int32_t) SDRAM_CACHE_LINE_BYTES);
|
||||
__DSB();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Ожидание с периодическим USB poll.
|
||||
*/
|
||||
static void delay_with_poll(uint32_t ms)
|
||||
{
|
||||
const uint32_t START_MS = bsp_tick_get_ms();
|
||||
|
||||
while ((bsp_tick_get_ms() - START_MS) < ms)
|
||||
{
|
||||
bsp_delay(SDRAM_RETENTION_POLL_STEP_MS);
|
||||
bsp_usb_cdc_poll();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Записать паттерн в тестовый регион.
|
||||
*/
|
||||
static void write_pattern(uint32_t size, pattern_fn_t p_fn)
|
||||
{
|
||||
volatile uint8_t *const P_BASE = (volatile uint8_t *) BSP_SDRAM_TEST_BASE_ADDR;
|
||||
|
||||
for (uint32_t i = 0U; i < size; i++)
|
||||
{
|
||||
if ((i & (SDRAM_USB_POLL_INTERVAL_BYTES - 1U)) == 0U)
|
||||
{
|
||||
bsp_usb_cdc_poll();
|
||||
}
|
||||
P_BASE[i] = p_fn(i);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Верифицировать паттерн. Останавливается на первом несовпадении.
|
||||
*/
|
||||
static bool verify_pattern(uint32_t size, pattern_fn_t p_fn, sdram_fail_info_t *p_fail)
|
||||
{
|
||||
volatile const uint8_t *const P_BASE = (volatile const uint8_t *) BSP_SDRAM_TEST_BASE_ADDR;
|
||||
|
||||
for (uint32_t i = 0U; i < size; i++)
|
||||
{
|
||||
if ((i & (SDRAM_USB_POLL_INTERVAL_BYTES - 1U)) == 0U)
|
||||
{
|
||||
bsp_usb_cdc_poll();
|
||||
}
|
||||
|
||||
const uint8_t EXPECTED = p_fn(i);
|
||||
const uint8_t GOT = P_BASE[i];
|
||||
|
||||
if (GOT != EXPECTED)
|
||||
{
|
||||
p_fail->fail_addr = BSP_SDRAM_TEST_BASE_ADDR + i;
|
||||
p_fail->expected = EXPECTED;
|
||||
p_fail->got = GOT;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Один паттерн-проход: запись → flush → верификация.
|
||||
*/
|
||||
static bool run_pass(uint32_t size, pattern_fn_t p_fn, sdram_fail_info_t *p_fail)
|
||||
{
|
||||
write_pattern(size, p_fn);
|
||||
flush_dcache(size);
|
||||
return verify_pattern(size, p_fn, p_fail);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Сформировать FAIL-результат.
|
||||
*/
|
||||
static test_result_t make_fail_result(const sdram_fail_info_t *p_fail, uint32_t duration_ms)
|
||||
{
|
||||
test_result_t result = {
|
||||
.status = TEST_STATUS_FAIL,
|
||||
.duration_ms = duration_ms,
|
||||
};
|
||||
(void) snprintf(result.detail, TEST_DETAIL_SIZE, "addr=0x%08" PRIX32 " exp=0x%02X got=0x%02X",
|
||||
p_fail->fail_addr, (unsigned int) p_fail->expected, (unsigned int) p_fail->got);
|
||||
return result;
|
||||
}
|
||||
|
||||
/* ── Фазы теста ────────────────────────────────────────────────────────── */
|
||||
|
||||
/**
|
||||
* @brief Фаза 1: шина адреса — 24 бита.
|
||||
*
|
||||
* Записывает уникальный байт (bit+1) в каждую из 24 позиций степеней двойки.
|
||||
* После каждой записи сбрасывает только одну кэш-линию → мгновенно.
|
||||
* Максимальное смещение: 2^23 = 8 MB → 0x80A00000 — внутри SDRAM. ✓
|
||||
*/
|
||||
static bool run_phase_address_bus(sdram_fail_info_t *p_fail)
|
||||
{
|
||||
volatile uint8_t *const P_BASE = (volatile uint8_t *) BSP_SDRAM_TEST_BASE_ADDR;
|
||||
|
||||
for (uint32_t bit = 0U; bit < SDRAM_ADDR_BUS_BITS; bit++)
|
||||
{
|
||||
const uint32_t OFFSET = (1UL << bit);
|
||||
const uint8_t PATTERN = (uint8_t) (bit + 1U);
|
||||
|
||||
P_BASE[OFFSET] = PATTERN;
|
||||
flush_dcache_line_at(BSP_SDRAM_TEST_BASE_ADDR + OFFSET);
|
||||
|
||||
const uint8_t GOT = P_BASE[OFFSET];
|
||||
if (GOT != PATTERN)
|
||||
{
|
||||
p_fail->fail_addr = BSP_SDRAM_TEST_BASE_ADDR + OFFSET;
|
||||
p_fail->expected = PATTERN;
|
||||
p_fail->got = GOT;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Фаза 2: шина данных — 64 KB.
|
||||
*
|
||||
* Walking ones + инверсия. Ловит stuck-at faults на всех битах.
|
||||
*/
|
||||
static bool run_phase_data_bus(sdram_fail_info_t *p_fail)
|
||||
{
|
||||
return run_pass(BSP_SDRAM_TEST_FAST_SIZE, pattern_walking_ones, p_fail) &&
|
||||
run_pass(BSP_SDRAM_TEST_FAST_SIZE, pattern_walking_ones_inv, p_fail);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Фаза 3: sequential integrity — 2 MB.
|
||||
*
|
||||
* Address pattern + инверсия. Ловит coupling faults между соседними ячейками.
|
||||
*/
|
||||
static bool run_phase_sequential(sdram_fail_info_t *p_fail)
|
||||
{
|
||||
return run_pass(SDRAM_SEQUENTIAL_SIZE, pattern_addr, p_fail) &&
|
||||
run_pass(SDRAM_SEQUENTIAL_SIZE, pattern_addr_inv, p_fail);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Фаза 4: retention — 256 KB, 200 мс.
|
||||
*
|
||||
* Запись → flush → ожидание → верификация.
|
||||
* Ловит refresh timing failures.
|
||||
*/
|
||||
static bool run_phase_retention(sdram_fail_info_t *p_fail)
|
||||
{
|
||||
write_pattern(SDRAM_RETENTION_SIZE, pattern_addr);
|
||||
flush_dcache(SDRAM_RETENTION_SIZE);
|
||||
delay_with_poll(SDRAM_RETENTION_DELAY_MS);
|
||||
return verify_pattern(SDRAM_RETENTION_SIZE, pattern_addr, p_fail);
|
||||
}
|
||||
|
||||
/* ── Реализация тест-модуля ────────────────────────────────────────────── */
|
||||
|
||||
static void sdram_test_init(void)
|
||||
{
|
||||
g_s_ready = (bsp_sdram_init() == BSP_OK);
|
||||
}
|
||||
|
||||
static test_result_t sdram_test_run(void)
|
||||
{
|
||||
if (!g_s_ready)
|
||||
{
|
||||
test_result_t result = { .status = TEST_STATUS_FAIL, .duration_ms = 0U };
|
||||
(void) snprintf(result.detail, TEST_DETAIL_SIZE, "%s", "SEMC not ready — DCD failed?");
|
||||
return result;
|
||||
}
|
||||
|
||||
const uint32_t START_MS = bsp_tick_get_ms();
|
||||
sdram_fail_info_t fail = { 0U, 0U, 0U };
|
||||
|
||||
bool is_ok = run_phase_address_bus(&fail);
|
||||
if (is_ok)
|
||||
{
|
||||
is_ok = run_phase_data_bus(&fail);
|
||||
}
|
||||
if (is_ok)
|
||||
{
|
||||
is_ok = run_phase_sequential(&fail);
|
||||
}
|
||||
if (is_ok)
|
||||
{
|
||||
is_ok = run_phase_retention(&fail);
|
||||
}
|
||||
|
||||
const uint32_t DURATION_MS = bsp_tick_get_ms() - START_MS;
|
||||
|
||||
if (!is_ok)
|
||||
{
|
||||
return make_fail_result(&fail, DURATION_MS);
|
||||
}
|
||||
|
||||
return (test_result_t){
|
||||
.status = TEST_STATUS_PASS,
|
||||
.duration_ms = DURATION_MS,
|
||||
.detail = { 0 },
|
||||
};
|
||||
}
|
||||
|
||||
static void sdram_test_deinit(void)
|
||||
{
|
||||
g_s_ready = false;
|
||||
}
|
||||
|
||||
/* ── Дескриптор модуля ─────────────────────────────────────────────────── */
|
||||
|
||||
const test_module_t K_TEST_SDRAM = {
|
||||
.id = "sdram",
|
||||
.name = "SDRAM 32 MB",
|
||||
.critical = true,
|
||||
.requires_hil = false,
|
||||
.pre_confirm_prompt = NULL,
|
||||
.init = sdram_test_init,
|
||||
.run = sdram_test_run,
|
||||
.deinit = sdram_test_deinit,
|
||||
};
|
||||
Loading…
Reference in a new issue