Files
templates/c/spi-nor/PORTING.md

7.0 KiB
Raw Blame History

Портирование SPI NOR на другую платформу

1. Добавить переносимое ядро

В сборку новой цели включите Src/spi_nor.c, а Inc добавьте в include path. Это единственные обязательные файлы. Они используют только C99-заголовки stddef.h, stdint.h и string.h.

2. Реализовать platform callbacks

Создайте собственный контекст шины и заполните spi_nor_io_t:

  • transfer — полный синхронный SPI-обмен при уже активном CS;
  • chip_select — active=1 опускает CS, active=0 поднимает его;
  • tick_ms — монотонный 32-битный счётчик миллисекунд;
  • delay_ms — задержка выхода из power-down и пауза BUSY polling;
  • lock/unlock — необязательная пара для RTOS, оба либо заданы, либо NULL.

transfer должен поддерживать следующие варианты:

tx rx Действие
не NULL NULL передать command или payload
NULL не NULL передавать dummy и сохранить входящие байты
не NULL не NULL full-duplex обмен, допустим для порта

Callback не должен самостоятельно переключать CS. Ядро может вызвать transfer дважды под одним CS: сначала command, затем data. Ошибка должна возвращаться как SPI_NOR_IO_ERROR или SPI_NOR_IO_TIMEOUT.

3. Настроить SPI

Для W25Q/SST25 используйте master, 8 бит, MSB first и режим, разрешённый datasheet. Частота не должна превышать предел микросхемы и возможности разводки. CS перед spi_nor_init() должен быть настроен output push-pull и находиться в 1. Проверьте питание, общую землю и отсутствие конфликта MISO с другими CS.

4. Инициализировать и выполнить probe

spi_nor_t device;
spi_nor_io_t io = MakeMyPlatformIo();
spi_nor_config_t config = spi_nor_default_config();

config.erase_timeout_ms = 10000U; /* Пример для медленной микросхемы. */
if (spi_nor_init(&device, &io, &config) != SPI_NOR_OK) {
    HandleConfigurationError();
}
if (spi_nor_probe(&device) != SPI_NOR_OK) {
    HandleAbsentOrUnsupportedFlash();
}

До успешного probe read/program/erase отклоняются. Для диагностики неизвестной памяти вызовите spi_nor_get_last_jedec() после неуспешного probe.

5. Подключить FlashStorage при необходимости

Добавьте в сборку:

  • Libraries/FlashStorage/Src/flash_storage.c;
  • Libraries/FlashStorage/Adapter/SPI_NOR/Src/flash_storage_spi_nor_adapter.c;
  • include paths FlashStorage/Inc и FlashStorage/Adapter/SPI_NOR/Inc.

После probe создайте flash_storage_spi_nor_adapter_t, вызовите flash_storage_spi_nor_adapter_init() и передайте flash_storage_spi_nor_ops в flash_storage_init() или eeprom_store_init(). Layout должен лежать внутри фактической capacity_bytes и быть выровнен по 4096 байтам.

6. STM32 HAL

Для другого STM32 можно использовать существующий порт, если семейство имеет совместимый HAL API. Подключите spi_nor_stm32f4_hal.c, замените HAL include на заголовок нужного семейства и переименуйте порт. Имена конкретных hspi, GPIO и pin передавайте из приложения; не добавляйте их как extern в библиотеку.

Если используется DMA или RTOS, простой STM32F4 порт следует заменить:

  • transfer обязан дождаться завершения DMA до возврата;
  • lock/unlock должны использовать mutex, не semaphore из ISR;
  • mutex должен быть общим для всех устройств на одной SPI-шине;
  • callback delay должен освобождать CPU через RTOS delay, если это допустимо.

7. Приёмочные проверки

  1. На пустой шине получить SPI_NOR_E_NOT_FOUND и CS=1.
  2. Прочитать ожидаемый JEDEC.
  3. Проверить чтение первого и последнего адреса.
  4. В тестовом секторе выполнить erase и полный verify 0xFF.
  5. Записать данные через границу страницы и сравнить readback.
  6. Имитировать SPI error/timeout и убедиться, что CS поднят.
  7. Проверить отказ для диапазона за концом памяти и невыровненного erase.
  8. При RTOS параллельно запустить два клиента и проверить mutex-анализатором.

Для текущего репозитория host mock запускается через python -m unittest tests.test_spi_nor_host и не требует STM32 HAL.

8. Перенос read-only command service

Если нужен сервисный просмотр команд, добавьте в сборку Diagnostics/Src/spi_nor_command_service.c и include path Diagnostics/Inc. Передавайте ему уже успешно probed spi_nor_t; отдельный HAL-порт не нужен:

spi_nor_command_service_request_t request = {0};
spi_nor_command_service_response_t response;

request.sequence = 1U;
request.tx_length = 1U;
request.rx_length = 3U;
request.tx[0] = 0x9FU;
(void)spi_nor_command_service_process(&device, &request, &response);

Не расширяйте существующий allowlist ради записи. Если продукту когда-нибудь потребуются изменяющие операции, они должны иметь отдельный API, threat model, защиту диапазонов и аппаратные тесты. Текущий сервис не содержит compile-time или runtime переключателя, снимающего блокировку.

Проверьте mock-тестом своей платформы: TX/RX порядок под одним CS, освобождение CS при timeout каждого transfer, общий mutex SPI bus, строгие максимумы 4/64 и отказ для 06, 02, всех erase, status/protection writes, power/reset и неизвестных opcode.