Add embedded storage drivers and extend firmware metadata

This commit is contained in:
2026-09-27 01:44:59 +03:00
parent 795a1279b1
commit 2ad29e7ffd
52 changed files with 5801 additions and 3 deletions

123
c/spi-nor/PORTING.md Normal file
View File

@@ -0,0 +1,123 @@
# Портирование 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
```c
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-порт не нужен:
```c
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.