lift_indicator_suite/bsp/sdram/README.md

11 KiB
Raw Permalink Blame History

bsp_sdram — внешняя SDRAM MT48LC16M16A2 (32 МБ)

Подъём SEMC (для прошивок без DCD) и минимальная верификация доступности внешней SDRAM, подключённой к SEMC. Подробное тестирование (паттерны, шина адреса/данных, retention) выполняется в тест-модуле firmware_test/test_sdram.c, который использует API этого модуля (путь с DCD, см. ниже).


Аппаратура

Параметр Значение
Чип MT48LC16M16A2
Объём 32 МБ
Ширина шины данных 16 бит
Интерфейс MCU SEMC, регион BR0
Базовый адрес 0x80000000 (BSP_SDRAM_BASE_ADDR)

Карта тестового региона:

Адрес Назначение
0x80000000 Начало SDRAM (SEMC BR0)
0x80200000 BSP_SDRAM_TEST_BASE_ADDR — база тестового региона
0x81E00000 Начало non-cacheable региона (USB DMA, 2 MB)
0x81FFFFFF Конец SDRAM

Тестовая база смещена на 2 МБ от начала — гарантированно выше .data/.bss прошивки и ниже non-cacheable региона.


Контракт: кто поднимает SEMC

Два независимых пути, в зависимости от того, есть ли у прошивки DCD:

  • С DCD (firmware_test, bsp_boot_xip): SEMC поднят DCD до вызова main(). Этот модуль в этом случае регистры SEMC не трогает — только bsp_sdram_init() для верификации.
  • Без DCD (bootloader, bsp_boot_xip_no_dcd; в будущем tft_app): SEMC не поднимает никто, пока не будет явно вызван bsp_sdram_configure() — побитовый порт проверенной в производстве DCD-последовательности (tools/host/dcd/dcd.bin, «блок 2» — первый блок там мёртвый код, полностью перезаписывается вторым до какого-либо использования, поэтому не переносился). Источник истины — сам DCD, а не пересчёт по формулам SDK из наносекунд: значения регистров контроллера SEMC скопированы дословно.

Почему bsp_sdram_configure() сама поднимает тактирование. Штатный BOARD_BootClockRUN() (его вызывает board_hw_init() в каждой прошивке) НЕ настраивает PLL2 → PFD2 → делитель SEMC — этот блок в clock_config.c выключен макросом SKIP_SYSCLK_INIT, который определён для всех таргетов сборки (bsp/CMakeLists.txt), включая bsp_boot_xip_no_dcd. Смысл макроса — не глитчить PLL, пока на нём уже висит поднятая DCD SDRAM (случай firmware_test); но для пути без DCD это побочно означает, что тактирование SEMC не настраивает вообще никто, кроме bsp_sdram_configure(). Результат — SEMC ≈135.77 МГц (PLL2 528 МГц → PFD2 FRAC=35 ≈271.54 МГц → SEMC_PODF ÷2), под эту частоту тюнингованы все timing-регистры DCD.

Вызывать bsp_sdram_configure() в пути с DCD не нужно и не имеет смысла — DCD уже сделала эту работу раньше, а bsp_sdram_configure() дублировала бы её же на живой памяти.


API

bsp_status_t bsp_sdram_configure(void); /* поднять SEMC: тактирование → пины → контроллер → init-команды SDRAM */
bsp_status_t bsp_sdram_init(void);      /* верифицировать SDRAM (SEMC уже поднят — DCD либо bsp_sdram_configure()) */

Публичные константы:

#define BSP_SDRAM_BASE_ADDR          0x80000000UL  /* SEMC BR0  */
#define BSP_SDRAM_SIZE_BYTES         0x02000000UL  /* 32 MB     */
#define BSP_SDRAM_TEST_BASE_ADDR     0x80200000UL  /* +2 MB     */
#define BSP_SDRAM_TEST_FAST_SIZE     0x00010000UL  /* 64 KB     */
#define BSP_SDRAM_TEST_FULL_SIZE     0x00100000UL  /* 1 MB      */
#define BSP_SDRAM_TEST_EXTENDED_SIZE 0x01B00000UL  /* 27 MB     */

Константы размеров — для потребителей; bsp_sdram не запускает по ним внутренних проходов.

Поведение bsp_sdram_configure():

  1. Тактирование: CLOCK_InitSysPll() (PLL2, 528 МГц) → CLOCK_InitSysPfd() (PFD2, FRAC=35) → CLOCK_SetMux/CLOCK_SetDiv (SEMC ← alt ← PFD2, ÷2).
  2. IOMUX: пины GPIO_EMC_00..41 → ALT0 (функция SEMC), SW_PAD_CTL_PAD = 0x000110F9; GPIO_EMC_39 (SEMC_DQS) дополнительно получает SION (иначе SEMC не сможет читать собственный read-strobe).
  3. Регистры контроллера SEMC (MCR, BMCR0/1, BR[0..8], IOCR, SDRAMCR0..3, DBICR0/1, IPCR1/2) — побитово из DCD.
  4. Командная последовательность SDRAM через SEMC_SendIPCommand(): precharge-all → 2×auto-refresh → mode-set (0x33 = BL8/sequential/CL3, согласуется с SDRAMCR0 и даташитом MT48LC16M16A2) → включение авто-refresh (SDRAMCR3 = 0x50210A09).
  5. AXI-QoS приоритеты SDRAM-мастеров (0x41044100/104 LCD, 0x41442100/104 Cortex-M7 read/write_qos) — хвост DCD. Это не SEMC и не в заголовках sdk/devices/MIMXRT1052 (NXP не заворачивает ARM'овский NIC-301 IP в CMSIS-структуру), но регистры реальные и документированы (i.MX RT1050 RM, гл. 29 "Network Interconnect Bus System (NIC-301)" — адреса сверены день-в- день: 0x41044100 = GPV0_BASE(0x41000000)+0x44000+0x100 = SIM_MAIN.LCD.read_qos). Для bootloader инертны (нет конкуренции LCD/DMA vs CPU за шину), но входят в проверенную последовательность и понадобятся tft_app.

Требует MPU Region 11 (board_mpu_init(), bsp/generated/board.c, 0x41000000, 8 МБ) — штатный Region 10 (периферия, только 4 МБ от 0x40000000 = 4 домена AIPSTZ) NIC-301 GPV не покрывает; без Region 11 шаг 5 фолтит (deny-all errata-регион 0 перехватывает всё, что не покрыто более специфичным регионом — PRIVDEFENA тут не спасает, т.к. регион 0 покрывает весь диапазон 0x0..0xFFFFFFFF и потому всегда matched). DCD это переживает — ROM пишет регистры до включения MPU. Уже добавлен в board_mpu_init().

Поведение bsp_sdram_init():

  1. Ждёт перехода SEMC в IDLE (SEMC->STS0 & SEMC_STS0_IDLE_MASK), таймаут 10 мс.
  2. Записывает 0xA5A5A5A5 по BSP_SDRAM_TEST_BASE_ADDR, делает SCB_CleanDCache_by_Addr + SCB_InvalidateDCache_by_Addr + __DSB(), читает обратно и сверяет.
  3. Повторяет для инверсного паттерна 0x5A5A5A5A.

Коды возврата:

Функция Код Условие
bsp_sdram_configure() BSP_OK Тактирование/пины/регистры/команды прошли
bsp_sdram_configure() BSP_ERR_INIT IP-команда SEMC вернула ошибку (precharge/refresh/mode-set)
bsp_sdram_init() BSP_OK SDRAM доступна, оба паттерна совпали
bsp_sdram_init() BSP_ERR_TIMEOUT SEMC не перешёл в IDLE за 10 мс
bsp_sdram_init() BSP_ERR_INIT Хотя бы один паттерн не совпал при readback

Быстрый старт

#include "bsp/sdram.h"

/* Путь С DCD (firmware_test) — SEMC уже поднят до main(): */
if (bsp_sdram_init() != BSP_OK) {
    handle_critical_error();
}

/* Путь БЕЗ DCD (bootloader smoke-test и т.п.) — сначала поднять SEMC сами: */
if (bsp_sdram_configure() != BSP_OK) {
    handle_semc_bringup_error();
}
if (bsp_sdram_init() != BSP_OK) {
    handle_sdram_error();
}

/* Работа с памятью по адресам внутри
   [BSP_SDRAM_BASE_ADDR, BSP_SDRAM_BASE_ADDR + BSP_SDRAM_SIZE_BYTES) */

CMake

Сегодня линкует только firmware_test (путь с DCD). Для пути без DCD потребитель (например, bootloader) добавляет зависимость сам — модуль никого за собой не тянет:

target_link_libraries(firmware_test PRIVATE bsp_sdram)

Зависимости модуля:

Зависимость Тип Описание
bsp_status PUBLIC bsp_status_t в публичном API
bsp_tick PRIVATE bsp_tick_get_ms() для таймаута SEMC IDLE
sdk_semc PRIVATE fsl_semc.h — регистры SEMC->, SEMC_SendIPCommand()
bsp_board PRIVATE Транзитивно даёт sdk_clock (CLOCK_Init*()) и sdk_device (IOMUXC) — отдельных PRIVATE-строк на них не заводили