Добавить прошивку F28335 через SCI и подготовку Python

Подключить сборку Flash BIN и RAM-ядра TI через templates.
Передавать настройки CAN и RS из JSON и проверять окружение CCS/Keil.
Добавить общий C99-протокол и проверки обмена SCI.
This commit is contained in:
2026-11-13 17:02:33 +03:00
parent f9d369d032
commit d057df7698
20 changed files with 863 additions and 10 deletions
+51
View File
@@ -0,0 +1,51 @@
"""Эталонные обмены SCI ROM и TI Flash Kernel A без настоящей платы.""" # Проверки не открывают COM и не вызывают внешнюю программу C2Prog.
import struct # Формирует независимые тестовые SCI8-векторы с явно заданными словами и адресами.
import pytest # Проверяет отказ при повреждениях и граничные размеры порций ядра TI.
from protocan.c2000_sci import Session, decode, validate, ulitco_enter # Испытывает настоящий ctypes-порт общего C99-ядра.
def sci8(address, words): # Готовит минимальную адресную таблицу для эталонных данных теста.
pointer = struct.pack("<HH", address >> 16, address & 65535) # SCI8 хранит старшее слово адреса перед младшим.
return b"\xaa\x08" + bytes(16) + pointer + struct.pack("<H", len(words)) + pointer + struct.pack("<" + "H" * len(words), *words) + bytes(2) # Вектор содержит ключ, резерв, entry, одну секцию и терминатор.
@pytest.mark.parametrize("words", [1, 1024, 1025, 2048, 2050]) # Границы проверяют обычную порцию, остаток и дополнительный нулевой ACK официального TI kernel.
def test_complete_echo_and_checksum_transcript(words): # Фактические ответы подтверждают порядок и неизменность всего приложения.
kernel, image = sci8(0x9000, [0x1234, 0x5678]), sci8(0x328000, [0xA55A] * words) # RAM-ядро и Flash занимают разные разрешённые области.
session, received, pauses, zero_acks = Session(kernel, image), bytearray(), [], 0 # Наблюдения не используют внутреннюю структуру C-контекста.
for _ in range(len(kernel) + words + 100): # Конечный предел выявляет зацикливание автомата.
state, tx, expected, delay = session.next() # Проверяет настоящий сформированный C-пакет.
if state == 0: break # Успех допустим лишь после отправки нулевого терминатора приложения.
if state == 2: pauses.append(delay); continue # Внешний транспорт выполняет только заданную алгоритмом задержку.
if expected == 1: session.accept(tx) # ROM и autobaud должны вернуть точное эхо отправленного октета.
else: # Kernel A подтверждает сумму октетов после erase/program, без побайтового эха приложения.
received.extend(tx) # Снимок подтверждает отсутствие пропущенных, повторённых либо изменённых октетов SCI8.
if expected: session.accept(struct.pack("<H", sum(tx) & 65535)) # Независимый эталон суммы следует опубликованному исходнику TI SendCheckSum.
if not tx: zero_acks += 1 # Пустой остаток TI подтверждается отдельным нулевым словом.
else: pytest.fail("Автомат SCI не завершился") # Отсутствие терминального перехода считается ошибкой протокола.
assert bytes(received) == image # MCU получил ровно исходный Flash SCI8 без переименования или подмены содержимого.
assert pauses == [3000, 3000] and session.progress == 100 # Порядок ожиданий совпадает с TI f05_DownloadImage.
assert zero_acks == int(words > 1024 and words % 1024 == 0) # Обрабатывает особенность исходного TI Kernel A на кратной порции.
def test_corrupt_echo_and_checksum_stop_transfer(): # Повреждённое подтверждение не должно разрешать следующий пакет.
kernel, image = sci8(0x9000, [1]), sci8(0x328000, [2]) # Минимальные векторы отделяют проверку ответа от больших файлов.
session = Session(kernel, image); session.next() # Первый пакет требует эхо ASCII A.
with pytest.raises(ValueError, match="не совпали"): session.accept(b"B") # Чужой байт autobaud не принимается как связь с ROM.
session = Session(kernel, image) # Независимый сеанс проверяет повреждение двухоктетной суммы Flash.
while True: # Доходит до первой операции erase/header без использования внутренних фаз автомата.
state, tx, expected, _ = session.next() # Паузы не требуют аппаратного ответа.
if state == 2: continue # Номер стадии остаётся внутри C-ядра.
if expected == 1: session.accept(tx) # Корректный ROM позволяет загрузить RAM-ядро.
else: break # Первый двухоктетный ответ должен подтвердить заголовок приложения.
with pytest.raises(ValueError, match="не совпали"): session.accept(b"\xff\xff") # Неверная сумма не разрешает программирование следующей секции.
def test_ascii_decode_memory_ranges_and_monitor_command(): # Проверяет переносимость форматов и аппаратные границы до открытия порта.
image = sci8(0x328000, [0x1234]) # Обычный Flash-вектор не пересекает CSM.
ascii_image = b"\x02\n" + " ".join(f"{value:02X}" for value in image).encode("ascii") + b"\n\x03\n" # Формат соответствует выводу TI hex2000 -boot -sci8 -a.
assert decode(ascii_image) == image and validate(image) == (0x328000, 4) # Сектор C имеет маску 0x0004 в TI Flash API.
with pytest.raises(ValueError): decode(ascii_image[:-2]) # Усечённый ETX не должен оставить частично декодированное ядро.
with pytest.raises(ValueError): validate(sci8(0x33FFF8, [0x1234])) # Область CSM-паролей запрещена независимо от имени файла.
with pytest.raises(ValueError): validate(sci8(0x8000, [0x1234])) # RAM-образ не может стать Flash-приложением из-за выбранного loader.
assert ulitco_enter() == bytes([247, 213, 255, 93, 0, 0, 1, 0, 0, 0]) # Эталон совпадает с десятиоктетным Monitor-запросом FLASH_NUM=93 текущей прошивки UlitCo.