Add embedded storage drivers and extend firmware metadata
This commit is contained in:
123
c/spi-nor/PORTING.md
Normal file
123
c/spi-nor/PORTING.md
Normal 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.
|
||||
Reference in New Issue
Block a user