11 KiB
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():
- Тактирование:
CLOCK_InitSysPll()(PLL2, 528 МГц) →CLOCK_InitSysPfd()(PFD2,FRAC=35) →CLOCK_SetMux/CLOCK_SetDiv(SEMC ← alt ← PFD2, ÷2). - IOMUX: пины
GPIO_EMC_00..41→ ALT0 (функция SEMC),SW_PAD_CTL_PAD=0x000110F9;GPIO_EMC_39(SEMC_DQS) дополнительно получаетSION(иначе SEMC не сможет читать собственный read-strobe). - Регистры контроллера SEMC (
MCR,BMCR0/1,BR[0..8],IOCR,SDRAMCR0..3,DBICR0/1,IPCR1/2) — побитово из DCD. - Командная последовательность SDRAM через
SEMC_SendIPCommand(): precharge-all → 2×auto-refresh → mode-set (0x33= BL8/sequential/CL3, согласуется сSDRAMCR0и даташитом MT48LC16M16A2) → включение авто-refresh (SDRAMCR3 = 0x50210A09). - AXI-QoS приоритеты SDRAM-мастеров (
0x41044100/104LCD,0x41442100/104Cortex-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():
- Ждёт перехода SEMC в IDLE (
SEMC->STS0 & SEMC_STS0_IDLE_MASK), таймаут 10 мс. - Записывает
0xA5A5A5A5поBSP_SDRAM_TEST_BASE_ADDR, делаетSCB_CleanDCache_by_Addr+SCB_InvalidateDCache_by_Addr+__DSB(), читает обратно и сверяет. - Повторяет для инверсного паттерна
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-строк на них не заводили |