#include "f28335_flash_update.h" /* Разделяет проверяемый алгоритм и аппаратные вызовы TI Flash API. */ #include /* Явно очищает состояние источника перед разбором нового SCI8. */ static unsigned int octet(f28335_flash_update_t *s, uint32_t offset) /* Читает октет через страничный кеш, исключая SPI-транзакцию на каждый байт CRC. */ { /* Все смещения ограничены фактической длиной принятого файла. */ if (offset >= s->length) { s->fault = true; return 0U; } /* Обрезанное поле не превращается в данные за концом staging. */ if (!s->cache_size || offset < s->cache_at || offset - s->cache_at >= s->cache_size) { /* Загружает новую страницу только при выходе за текущую. */ s->cache_at = offset & ~255UL; /* Выравнивает смещение на страницу из 256 физических октетов. */ s->cache_size = s->length - s->cache_at > 256UL ? 256U : (unsigned int)(s->length - s->cache_at); /* Последнее чтение не включает паддинг CAN за концом образа. */ if (!s->io.read(s->io.user, s->base + s->cache_at, s->cache, s->cache_size)) { s->fault = true; s->cache_size = 0U; return 0U; } /* Любой неполный аппаратный обмен запрещает erase. */ } /* После заполнения кеша извлечение октета не обращается к SPI. */ return s->cache[offset - s->cache_at] & 255U; /* Маска сохраняет восьмибитный wire-формат на C28x. */ } /* Завершает ограниченное чтение октета. */ static uint16_t word(f28335_flash_update_t *s, uint32_t offset) /* SCI8 передаёт младший октет каждого 16-битного слова первым. */ { /* Объединяет два октета только через проверяемый источник. */ unsigned int low = octet(s, offset); /* Явный порядок вызовов не зависит от порядка вычисления операндов компилятором. */ return (uint16_t)(low | (octet(s, offset + 1UL) << 8)); /* Восстанавливает одно слово CPU из двух октетов SCI8. */ } /* Завершает декодирование слова. */ static uint32_t address_at(f28335_flash_update_t *s, uint32_t offset) /* Адрес таблицы TI содержит старшее слово перед младшим. */ { /* Порядок слов адреса отличается от порядка октетов внутри слова. */ uint32_t high = word(s, offset); /* Читает старшие 16 бит адреса раньше младших. */ return (high << 16) | word(s, offset + 2UL); /* Получает CPU word-address без перевода в байты. */ } /* Завершает восстановление адреса SCI8. */ static uint16_t image_word(void *user, uint32_t address) /* Предоставляет общему валидатору будущий вид Flash, включая стёртые промежутки. */ { /* Отсортированный индекс позволяет проверять весь CRC-диапазон последовательно. */ f28335_flash_update_t *s = user; f28335_flash_section_t *p; /* Контекст содержит только предварительно проверенные секции. */ if (s->cursor >= s->count || address < s->section[s->cursor].address) s->cursor = 0U; /* Возврат от заголовка к началу диапазона корректно перематывает индекс. */ while (s->cursor < s->count) { /* Пропускает только секции, целиком лежащие перед запрошенным словом. */ p = &s->section[s->cursor]; /* Обращается к существующему элементу проверенного индекса. */ if (address < p->address) return 0xFFFFU; /* Flash_Erase оставляет незагружаемый промежуток заполненным единицами. */ if (address - p->address < p->words) return word(s, p->offset + 2UL * (address - p->address)); /* Переводит смещение слов секции в октеты SCI8. */ ++s->cursor; /* Следующий запрос не должен повторно сканировать уже пройденную секцию. */ } /* После последней секции остаётся только стёртая Flash. */ return 0xFFFFU; /* CRC учитывает также завершающий промежуток диапазона FAB1. */ } /* Сам этот callback никогда не читает аппаратную Flash. */ bool f28335_flash_update_validate(f28335_flash_update_t *s, const f28335_boot_io_t *io, uint32_t base, const pcan_boot_manifest_t *m) /* Проверяет транспорт, таблицу, допустимые адреса и FAB1 перед erase. */ { /* Любая ошибка оставляет объект непригодным для программирования. */ uint32_t offset = 22UL, crc = 0UL, entry, checked_entry, bytes; unsigned int i, j, headers = 0U, have_entry = 0U; /* Заголовок SCI8 занимает 11 слов, то есть 22 октета. */ f28335_flash_section_t p; bool ended = false; /* Временная секция нужна для вставки в отсортированный индекс. */ if (!s || !io || !io->read || !m) return false; /* Не разыменовывает отсутствующие callbacks и manifest. */ memset(s, 0, sizeof(*s)); s->io = *io; s->base = base; s->length = m->image_size; s->crc = m->image_crc32; /* Отбрасывает кеш и разрешение записи предыдущего образа. */ if (s->length < 24UL || (s->length & 1UL) || s->length > F28335_AB_SLOT_BYTES || word(s, 0UL) != 0x08AAU) return false; /* Принимает только полный чётный SCI8 в пределах staging-слота. */ for (offset = 0UL; offset < s->length; offset += bytes) { /* Независимо проверяет фактически записанную EEPROM, а не только принятые CAN-кадры. */ bytes = s->length - offset > 256UL ? 256UL : s->length - offset; /* Делит чтение на ограниченные страницы без выхода за файл. */ if (!s->io.read(s->io.user, base + offset, s->cache, (unsigned int)bytes)) return false; /* Аппаратная ошибка запрещает последующий erase. */ crc = pcan_boot_crc32_update(crc, s->cache, (size_t)bytes); /* Использует общий CRC32 IEEE, совпадающий с клиентом SETGUI. */ } /* Все октеты staging включены в транспортную CRC32. */ s->cache_size = 0U; if (crc != s->crc) return false; /* Сбрасывает кеш после прямого чтения и отклоняет повреждённую EEPROM. */ entry = address_at(s, 18UL); offset = 22UL; /* Точка входа следует после ключа и восьми резервных слов SCI8. */ if (entry < F28335_FLASH_APP_DATA || entry >= F28335_FLASH_APP_STOP) return false; /* Запуск разрешён только из приложения ниже постоянного сектора A. */ while (offset + 2UL <= s->length) { /* Для каждой секции сначала проверяет наличие поля длины. */ p.words = word(s, offset); offset += 2UL; /* Длина секции выражена адресуемыми словами C28x. */ if (!p.words) { ended = offset == s->length; break; } /* Нулевой терминатор обязан завершать файл без лишнего хвоста. */ if (s->count == F28335_AB_MAX_SECTIONS || offset + 4UL > s->length) return false; /* Не допускает переполнение индекса либо обрезанный адрес секции. */ p.address = address_at(s, offset); offset += 4UL; p.offset = offset; bytes = 2UL * p.words; /* Хранит CPU-адрес и байтовое смещение полезных данных отдельно. */ if (bytes > s->length - offset || p.address < F28335_FLASH_APP_START || p.address >= F28335_FLASH_APP_STOP || p.words > F28335_FLASH_APP_STOP - p.address) return false; /* Исключает RAM, сектор A, CSM, OTP и выход за конец staging. */ if (p.address == F28335_FLASH_APP_START) { if (p.words < F28335_FLASH_APP_HEADER_WORDS) return false; ++headers; } /* Полные 16 слов FAB1 должны публиковаться одной заключительной операцией. */ if (entry >= p.address && entry - p.address < p.words) have_entry = 1U; /* Код entry обязан присутствовать именно в принятой таблице. */ for (i = 0U; i < s->count; ++i) if (p.address < s->section[i].address + s->section[i].words && s->section[i].address < p.address + p.words) return false; /* Повторное программирование пересекающихся диапазонов запрещено. */ for (j = s->count; j && s->section[j - 1U].address > p.address; --j) s->section[j] = s->section[j - 1U]; /* Сортирует индекс по адресу независимо от порядка секций в SCI8. */ s->section[j] = p; ++s->count; offset += bytes; /* Запоминает непересекающуюся секцию и переходит к следующему заголовку. */ } /* Структура файла полностью разобрана без записи Flash. */ if (!ended || headers != 1U || !have_entry || s->fault) return false; /* Требует терминатор, единственный FAB1 и фактический исполняемый entry. */ s->cursor = 0U; if (!f28335_flash_app_validate(image_word, s, &checked_entry) || s->fault || checked_entry != entry) return false; /* Общий валидатор проверяет magic, границы, инверсию entry и CRC будущей Flash. */ s->entry = entry; s->valid = true; return true; /* Разрешает программирование только после всех независимых проверок. */ } /* Ошибка проверки не меняет текущую Flash и журнал активной EEPROM. */ bool f28335_flash_update_program(f28335_flash_update_t *s, const f28335_flash_ops_t *ops) /* Выполняет запись B–H только из полностью проверенного staging. */ { /* Заголовок FAB1 фиксируется после успешной проверки каждой порции данных. */ uint16_t buffer[128]; unsigned int i, j, n; uint32_t at, left, entry; pcan_boot_manifest_t m; f28335_boot_io_t io; /* Одна порция содержит не более 128 слов, то есть 256 физических октетов. */ if (!s || !s->valid || !ops || !ops->erase || !ops->program || !ops->read) return false; /* Неполный аппаратный контракт не должен начать стирание. */ memset(&m, 0, sizeof(m)); m.image_size = s->length; m.image_crc32 = s->crc; io = s->io; at = s->base; /* Сохраняет исходные параметры перед полной повторной проверкой EEPROM. */ if (!f28335_flash_update_validate(s, &io, at, &m)) return false; /* Изменение staging между VERIFY и COMMIT обнаруживается до первого erase. */ s->valid = false; if (!ops->erase(ops->user)) return false; /* Одно разрешение записи расходуется до изменения Flash; повтор после отказа требует нового VERIFY. */ for (i = 0U; i < s->count; ++i) { /* Программирует обычные данные каждой непересекающейся секции. */ at = s->section[i].address; left = s->section[i].words; /* CPU-курсор и остаток выражены словами. */ if (at == F28335_FLASH_APP_START) { at += F28335_FLASH_APP_HEADER_WORDS; left -= F28335_FLASH_APP_HEADER_WORDS; } /* Пропускает FAB1 до подтверждения всех остальных данных. */ while (left) { /* Большие секции разбивает на порции рабочего буфера. */ n = left > 128UL ? 128U : (unsigned int)left; /* Последняя порция может быть короче 128 слов. */ for (j = 0U; j < n; ++j) buffer[j] = word(s, s->section[i].offset + 2UL * (at + j - s->section[i].address)); /* Распаковывает слова непосредственно из staging EEPROM. */ if (s->fault || !ops->program(ops->user, at, buffer, n)) return false; /* Ошибка чтения, записи или readback оставляет FAB1 неопубликованным. */ at += n; left -= n; /* Продвигает курсоры только после подтверждённой порции. */ } /* Завершает запись данных секции. */ } /* Все обычные слова уже записаны и проверены аппаратным портом. */ for (i = 0U; i < F28335_FLASH_APP_HEADER_WORDS; ++i) buffer[i] = image_word(s, F28335_FLASH_APP_START + i); /* Собирает единственное проверенное описание FAB1 для заключительной записи. */ if (s->fault || !ops->program(ops->user, F28335_FLASH_APP_START, buffer, F28335_FLASH_APP_HEADER_WORDS)) return false; /* Публикует описание только после успешной записи остальных секций. */ return f28335_flash_app_validate(ops->read, ops->user, &entry) && entry == s->entry; /* Итоговый успех требует общей CRC уже физической Flash, а не только поблочного сравнения. */ } /* Решение о CAN-подтверждении и перезапуске остаётся существующему протоколу. */