Обеспечь единый процесс обновления для STM32F103/F407/G474 и TMS320F2812/F28335: discovery, служебные ответы, BUSY, тайм-аут сессии и подтверждение приложения. Добавь самостоятельные проекты CCS, аппаратные хранилища и Python-клиент. Совместимость: структуры конфигурации и порта расширены, поэтому потребители должны пересобраться и обнулить новые поля. Для C28x callbacks используют pcan_boot_byte_t; периодический pcan_boot_poll обслуживает discovery и настроенный тайм-аут.
70 lines
11 KiB
C
70 lines
11 KiB
C
#include "boot2812.h" /* Ограничивает аппаратный драйвер разделами обновления конкретного профиля ПМ67. */
|
|
#if defined(BOOT2812_HOST) /* Host-модель воспроизводит команды NOR без обращения к физическим CPU-адресам. */
|
|
extern unsigned int NorModel_Read(unsigned long word); /* Модель отдаёт данные, ID и аппаратные биты готовности чипа. */
|
|
extern void NorModel_Write(unsigned long word, unsigned int value); /* Модель принимает настоящую последовательность unlock/program/erase драйвера. */
|
|
#define NOR_READ(word) NorModel_Read(word) /* Подменяет только чтение шины, сохраняя алгоритм драйвера. */
|
|
#define NOR_WRITE(word, value) NorModel_Write(word, value) /* Подменяет физическую запись при проверке границ и отказов. */
|
|
#else /* Production использует 16-битную параллельную XINTF-шину. */
|
|
#define NOR_READ(word) (*(volatile unsigned int *)(word)) /* Один CPU-адрес C28x соответствует одному x16 слову AM29LV800BT. */
|
|
#define NOR_WRITE(word, value) (*(volatile unsigned int *)(word) = (value)) /* Volatile сохраняет каждый командный цикл AMD в исходном порядке. */
|
|
#endif /* Завершает выбор физической шины либо модели. */
|
|
static void reset_read(void) /* Выводит чип из autoselect либо неудачной операции в обычный режим чтения. */
|
|
{ /* Reset не стирает слова памяти и допустим по любому адресу чипа. */
|
|
NOR_WRITE(BOOT2812_NOR_BASE, 0x00F0U); /* F0 — Read/Reset из таблицы команд AMD Am29LV800B. */
|
|
} /* Все ошибки возвращают NOR в состояние, пригодное для следующей recovery-сессии. */
|
|
static void unlock(unsigned int command) /* Выполняет общий префикс x16-команд согласно datasheet AMD. */
|
|
{ /* Адреса ниже являются смещениями в словах, а не октетными AAA/555 режима x8. */
|
|
NOR_WRITE(BOOT2812_NOR_BASE + 0x555UL, 0x00AAU); /* Первый unlock-цикл AA по смещению 555h в режиме x16. */
|
|
NOR_WRITE(BOOT2812_NOR_BASE + 0x2AAUL, 0x0055U); /* Второй unlock-цикл 55 по смещению 2AAh. */
|
|
NOR_WRITE(BOOT2812_NOR_BASE + 0x555UL, command); /* Третий цикл выбирает autoselect, программирование либо начало стирания. */
|
|
} /* Последующий цикл данных или адрес сектора выполняет вызывающая операция. */
|
|
unsigned int Boot2812_NorRead(unsigned long word) /* Читает только физический диапазон установленной NOR ёмкостью 1 МиБ. */
|
|
{ /* Верхняя исключающая граница равна BASE + 512K слов. */
|
|
return word >= BOOT2812_NOR_BASE && word < BOOT2812_NOR_BASE + 0x80000UL ? NOR_READ(word) : 0xFFFFU; /* Вне чипа не создаёт шинного чтения по неизвестному адресу. */
|
|
} /* Возвращает полное x16 слово без перестановки октетов. */
|
|
bool Boot2812_NorIdentify(void) /* Идентификация обязательна перед разрешением изменяющих операций. */
|
|
{ /* Не подменяет top-boot геометрию похожим bottom-boot или NAND-чипом. */
|
|
unsigned int maker, device; /* Хранит два слова autoselect, прежде чем вернуть чип к чтению. */
|
|
reset_read(); unlock(0x0090U); /* 90h включает чтение manufacturer/device ID после обычного unlock. */
|
|
maker = NOR_READ(BOOT2812_NOR_BASE); device = NOR_READ(BOOT2812_NOR_BASE + 1UL); /* AMD: offset 0 содержит 0001, offset 1 — 22DA для Am29LV800BT x16. */
|
|
reset_read(); /* Обычные чтения образа после проверки не должны видеть таблицу autoselect. */
|
|
return maker == 0x0001U && device == 0x22DAU; /* Отличающийся чип получает отказ до первого sector erase. */
|
|
} /* Не выполняет разрушительных пробных записей для определения типа памяти. */
|
|
static bool wait_word(unsigned long word, unsigned int expected, unsigned long timeout) /* Ограничивает DQ7/DQ5 polling временем и числом проходов. */
|
|
{ /* DQ7 отражает готовность программируемого старшего бита; DQ5 требует завершающего повторного чтения. */
|
|
unsigned long started = Boot2812_Now(), budget = 50000000UL; /* Итерационный предел обеспечивает выход даже при остановленном Timer1. */
|
|
unsigned int value; /* Снимок status считывается один раз на основной проход. */
|
|
do { /* Не вызывает никаких функций из программируемой внешней NOR: код загрузчика находится во внутренней Flash. */
|
|
value = NOR_READ(word); /* Читает именно адрес программирования либо начало стираемого сектора. */
|
|
if ((value & 0x0080U) == (expected & 0x0080U)) return NOR_READ(word) == expected; /* После совпадения DQ7 подтверждает все 16 бит результата. */
|
|
if (value & 0x0020U) { /* DQ5 сообщает превышение внутреннего времени алгоритма AMD. */
|
|
if ((NOR_READ(word) & 0x0080U) == (expected & 0x0080U)) return NOR_READ(word) == expected; /* Повтор исключает нормальное завершение операции одновременно с чтением DQ5. */
|
|
break; /* После подтверждённой ошибки больше не ждёт неопределённое время. */
|
|
} /* Завершает трактовку аппаратного статуса текущего прохода. */
|
|
Boot2812_Busy(); /* Долгое стирание продолжает давать клиенту живой ответ и не блокирует CAN ISR. */
|
|
} while (--budget && Boot2812_Now() - started < timeout); /* Беззнаковое вычитание сохраняет корректность при переполнении миллисекунд. */
|
|
reset_read(); /* Отказ возвращает чип в режим чтения перед следующей recovery-попыткой. */
|
|
return false; /* Не сообщает успех по одному окончанию тайм-аута. */
|
|
} /* Завершает ограниченное ожидание аппаратной операции. */
|
|
bool Boot2812_NorProgram(unsigned long word, unsigned int value) /* Программирует только выделенный CAN-образ либо manifest. */
|
|
{ /* Старые данные приложения, FPGA и остальные сектора чипа не входят в разрешённые диапазоны. */
|
|
unsigned int before; /* Предварительное чтение исключает попытку перевода NOR-бита из нуля в единицу. */
|
|
if (!((word >= BOOT2812_IMAGE_WORD && word < BOOT2812_IMAGE_WORD + BOOT2812_IMAGE_BYTES / 2UL) || (word >= BOOT2812_META_WORD && word < BOOT2812_META_WORD + 0x2000UL))) return false; /* Проверяет word-адрес до любого unlock или командного цикла. */
|
|
before = NOR_READ(word); /* Проверяет текущее содержимое перед началом операции. */
|
|
if (before == value) return true; /* Повтор одинакового блока не выполняет лишнее программирование. */
|
|
if ((before & value) != value) return false; /* Бит 0→1 требует отдельного sector erase и не должен маскироваться чтением старого слова. */
|
|
unlock(0x00A0U); NOR_WRITE(word, value); /* A0 запускает word program; следующий цикл содержит адрес и полные 16 бит данных. */
|
|
return wait_word(word, value, 100UL); /* Сто миллисекунд дают ограниченный запас для программирования одного слова. */
|
|
} /* Каждый успех означает фактическое совпадение прочитанного слова. */
|
|
bool Boot2812_NorErase(unsigned long word) /* Не принимает произвольный адрес или команду полного стирания чипа. */
|
|
{ /* Обновление ограничено секторами 12/13/14 и последним top-boot сектором 18. */
|
|
unsigned long i, count = word == BOOT2812_META_WORD ? 0x2000UL : 0x8000UL; /* AM29LV800BT: обычный сектор 32K слов, последний boot-сектор 8K слов. */
|
|
if (!(word == BOOT2812_IMAGE_WORD || word == BOOT2812_IMAGE_WORD + 0x8000UL || word == BOOT2812_IMAGE_WORD + 0x10000UL || word == BOOT2812_META_WORD)) return false; /* Только точные разрешённые начала секторов исключают округление в соседнюю область. */
|
|
unlock(0x0080U); /* 80h начинает шеститактовую последовательность Sector Erase AMD. */
|
|
NOR_WRITE(BOOT2812_NOR_BASE + 0x555UL, 0x00AAU); NOR_WRITE(BOOT2812_NOR_BASE + 0x2AAUL, 0x0055U); /* Повторяет вторую unlock-пару перед выбором сектора. */
|
|
NOR_WRITE(word, 0x0030U); /* 30h по sector address выбирает один сектор; команда Chip Erase 10h отсутствует. */
|
|
if (!wait_word(word, 0xFFFFU, 20000UL)) return false; /* Ограничивает ожидание двадцатью секундами с периодическими BUSY-ответами. */
|
|
for (i = 0; i < count; ++i) if (NOR_READ(word + i) != 0xFFFFU) return false; /* Проверяет всю стёртую область, а не только её первое слово. */
|
|
return true; /* Разрешает запись лишь после полного аппаратного readback сектора. */
|
|
} /* При сбое сохранённый manifest уже недействителен, загрузчик остаётся в recovery. */
|