Files
templates/c/global-time/tests/test_f28335.c
T
Andrey 2da7e7b525 feat(global-time): Добавь общие часы и порт TMS320F28335
Вынеси программные часы, коррекцию по ProtoCAN RTCSETUP и атомарную метку логгера в переносимое ядро без глобального состояния.

Подключи eCAN-A и CPU Timer2 отдельным портом F28335; зафиксируй календарные границы, переполнение, задержку RX и защиту IRQ в хостовых тестах.
2026-10-23 14:16:21 +03:00

89 lines
13 KiB
C

#include "DSP2833x_Device.h" /* Использует host-модель регистров, а не альтернативную реализацию драйвера. */
#include "global_time_f28335.h" /* Тестирует публичные hooks настоящего аппаратного порта. */
#ifdef NDEBUG /* Не позволяет Release-сборке превратить аппаратные проверки в пустую программу. */
#undef NDEBUG /* Включает assert независимо от флагов конфигурации CMake. */
#endif /* Завершает настройку обязательных проверяющих утверждений. */
#include <assert.h> /* Проверяет сохранность соседних ресурсов, обработку CAN и период Timer2. */
#include <stdio.h> /* Выводит краткое подтверждение проверенных сценариев. */
struct gt_test_mboxes ECanaMboxes; /* Выделяет модельные MBOX7 и MBOX8. */
struct gt_test_ecan ECanaRegs; /* Выделяет модель управляющих регистров eCAN-A. */
struct gt_test_lam ECanaLAMRegs; /* Выделяет локальную маску RX. */
struct gt_test_timer CpuTimer2Regs; /* Выделяет модель CPU Timer2. */
struct gt_test_vectors PieVectTable; /* Хранит реальный адрес ISR, назначенный портом. */
uint16_t IER, IFR; /* Выделяет модель CPU-масок INT14 и соседних прерываний. */
static uint16_t masked; /* Сохраняет модельное состояние глобальной маски INTM. */
uint16_t __disable_interrupts(void) /* Моделирует атомарный запрет IRQ и возвращаемый предыдущий INTM. */
{ /* Повторный запрет из ISR обязан вернуть уже установленную маску. */
uint16_t old = masked; /* Сохраняет прежнюю маску до её изменения. */
masked = 1U; /* Имитирует запрет маскируемых IRQ. */
return old; /* Позволяет реальному порту точно восстановить предыдущий режим. */
} /* Завершает модель intrinsic запрета. */
void __restore_interrupts(uint16_t state) /* Восстанавливает маску, полученную соответствующим входом. */
{ /* Проверяет, что порт действительно вызвал вход в критическую секцию. */
assert(masked == 1U); /* Восстановление без предшествующего запрета нарушило бы вложенность. */
masked = state; /* Сохраняет исходный ISR-контекст без безусловного разрешения IRQ. */
} /* Завершает модель intrinsic восстановления. */
static void inject(const gt_frame *frame) /* Доставляет wire-данные в аппаратный RX и вызывает настоящий ISR hook. */
{ /* Не вызывает gt_receive напрямую, проверяя весь путь распаковки MDL/MDH. */
uint16_t i; /* Перебирает восемь проводных октетов фиксированного classic CAN. */
ECanaMboxes.MBOX7.MSGID.all = 0xC0000000UL | frame->id; /* Имитирует расширенный ID и включённую локальную фильтрацию eCAN. */
ECanaMboxes.MBOX7.MSGCTRL.all = frame->dlc | ((uint32_t)frame->remote << 4U); /* Укладывает DLC и RTR в модельный регистр сообщения. */
ECanaMboxes.MBOX7.MDL.all = ECanaMboxes.MBOX7.MDH.all = 0UL; /* Сбрасывает старый payload перед имитацией нового приёма. */
for (i = 0U; i < 8U; ++i) { /* Укладывает данные младшим октетом вперёд, как eCAN при DBO=0. */
if (i < 4U) ECanaMboxes.MBOX7.MDL.all |= (uint32_t)frame->data[i] << (8U * i); /* Первые четыре октета занимают MDL. */
else ECanaMboxes.MBOX7.MDH.all |= (uint32_t)frame->data[i] << (8U * (i - 4U)); /* Следующие четыре октета занимают MDH. */
} /* Завершает аппаратное размещение кадра. */
ECanaRegs.CANRMP.all = 0x82UL; /* Выставляет RX7 и независимый pending legacy mailbox1. */
gt_f28335_rx_isr(); /* Захватывает временную привязку и кадр в очередь настоящего порта. */
assert(ECanaRegs.CANRMP.all == 0x80UL); /* Запись W1C должна содержать только бит собственного mailbox, сохраняя pending соседа на аппаратуре. */
ECanaRegs.CANRMP.all = 2UL; /* Вручную применяет W1C к обычной RAM модели: RX7 снят, RX1 сохранён. */
} /* Завершает модель получения кадра без подмены алгоритма порта. */
int main(void) /* Проверяет аппаратную настройку и общий путь ISR → main → метка логгера. */
{ /* Модель не утверждает электрические тайминги или CAN ACK настоящего трансивера. */
gt_stamp stamp; /* Читает время через тот же атомарный API, которым будет пользоваться логгер. */
gt_datetime date = {2026U, 10U, 19U, 1U, 12U, 34U, 56U, 0U}; /* Независимый календарный эталон: понедельник 19 октября 2026 года. */
gt_frame frame; /* Содержит broadcast мастера для доставки через модель eCAN. */
uint16_t i; /* Управляет известным числом тиков до обработки кадра в main. */
gt_f28335_tick_1ms(); gt_f28335_rx_isr(); gt_f28335_service(); /* Ранние hooks до init обязаны безопасно завершаться. */
ECanaRegs.CANME.all = 0x7FUL; /* Резервирует существующие mailbox0..6 для legacy CAN, Boot и pulse. */
ECanaRegs.CANMD.all = 0xFFFFFFFAUL; /* Воспроизводит исходные направления legacy mailbox проекта 28335_periph. */
ECanaRegs.CANMIM.all = 0x4AUL; /* Имитирует разрешённые RX1/RX3/RX6 других CAN-сервисов. */
ECanaRegs.CANMIL.all = 0x40UL; /* Фиксирует чужую маршрутизацию IRQ, которую порт обязан сохранить. */
assert(!gt_f28335_init_periph(32U)); /* Некорректный физический адрес не должен открыть новый CAN-сервис. */
assert(gt_f28335_init_periph(5U)); /* Настраивает УМП1 как PERIPH5, Type1/ID5. */
assert(ECanaRegs.CANME.all == 0x1FFUL && (ECanaRegs.CANMD.all & 0x7FUL) == 0x7AUL); /* Проверяет добавление только mailbox7/8 и сохранность чужих направлений. */
assert(ECanaRegs.CANMIM.all == 0xCAUL && ECanaRegs.CANMIL.all == 0x40UL); /* Проверяет RX7 IRQ line0 без изменения RX1/RX3/RX6. */
assert(ECanaMboxes.MBOX7.MSGID.all == 0xC0000030UL && ECanaLAMRegs.LAM7.all == 0x10000000UL); /* Фильтр принимает только время мастера с любым приоритетом. */
ECanaRegs.CANTA.all = ECanaRegs.CANAA.all = ECanaRegs.CANRMP.all = 0UL; /* Применяет к RAM модели W1C, выполненные init на реальной аппаратуре. */
gt_f28335_service(); /* Проверяет первый проход main при отсутствии входящего кадра. */
assert(ECanaRegs.CANTRS.all == 0UL); /* Сам init не должен объявить готовность прикладной платы. */
gt_f28335_ready(); gt_f28335_service(); /* Публикует прикладной успех и запускает передачу из main. */
assert(ECanaRegs.CANTRS.all == 0x100UL && ECanaMboxes.MBOX8.MSGID.all == 0x99513000UL && ECanaMboxes.MBOX8.MDL.all == 1UL); /* Проверяет точный аппаратный запрос READY с правильным адресом. */
assert(gt_make_rtc(&date, &frame)); /* Строит штатный RTCSETUP без аппаратного RTC мастера в тесте. */
inject(&frame); /* Проверяет разбор всех календарных полей через настоящий RX ISR. */
for (i = 0U; i < 37U; ++i) gt_f28335_tick_1ms(); /* Имитирует 37 мс задержки main при продолжающемся такте. */
gt_f28335_service(); /* Применяет отложенный RX и сохраняет привязку к исходному моменту приёма. */
gt_get(gt_f28335_clock(), &stamp); /* Снимает тот же экземпляр часов, что обслуживает порт. */
assert(gt_calendar(&stamp, &date) && date.year == 2026U && date.month == 10U && date.day == 19U && date.hour == 12U && date.minute == 34U && date.second == 56U && date.millisecond == 37U); /* Проверяет календарь и компенсацию задержки всего пути приёма. */
assert(stamp.sync_uptime_ms == 0UL && stamp.uptime_ms == 37UL); /* Очередь не должна подменить время RX временем обработки main. */
ECanaRegs.CANTA.all = 0x100UL; ECanaRegs.CANTRS.all = 0UL; /* Имитирует аппаратный ACK ранее отправленной готовности. */
gt_f28335_service(); /* Освобождает собственный TX без повторной отправки после синхронизации. */
ECanaRegs.CANTA.all = ECanaRegs.CANAA.all = 0UL; /* Применяет аппаратное W1C к RAM модели ACK. */
assert(ECanaRegs.CANTRS.all == 0UL); /* Синхронизация прекращает повторную стартовую рассылку. */
IER = 0x1001U; IFR = 0x3001U; /* Подготавливает чужие CPU IRQ и старый pending INT14. */
assert(!gt_f28335_timer2_start(150000001UL)); /* Некратная 1000 частота не должна давать скрытый дробный период. */
assert(gt_f28335_timer2_start(150000000UL)); /* Настраивает аппаратные 1 мс для типичной частоты F28335 150 МГц. */
assert(CpuTimer2Regs.PRD.all == 149999UL && CpuTimer2Regs.TPR.all == 0UL && CpuTimer2Regs.TPRH.all == 0UL); /* Проверяет включительный период PRD..0 и отсутствие предделителей. */
assert(IER == 0x3001U && IFR == 0x1001U && CpuTimer2Regs.TCR.all == 0xC020UL); /* Сохраняет чужие CPU маски и запускает только Timer2. */
PieVectTable.TINT2(); /* Вызывает реальное тело ISR через вектор, назначенный портом. */
gt_get(gt_f28335_clock(), &stamp); /* Проверяет единственный миллисекундный шаг собственного ISR. */
assert(stamp.uptime_ms == 38UL && stamp.millisecond == 38U); /* Timer2 должен добавлять ровно один тик на прерывание. */
assert(!gt_f28335_init_periph(6U)); /* Повторный init не должен потерять владение работающим Timer2. */
gt_f28335_timer2_stop(); /* Освобождает ранее захваченный аппаратный такт. */
assert(IER == 0x1001U && CpuTimer2Regs.TCR.all == 0x8010UL && masked == 0U); /* Остановка сохраняет чужие IRQ и исходный глобальный INTM. */
ECanaRegs.CANMC.bit.DBO = 1U; /* Имитирует несовместимый порядок октетов контроллера. */
assert(!gt_f28335_init_periph(6U)); /* Не позволяет молча декодировать другую аппаратную раскладку payload. */
puts("global-time F28335: eCAN isolation, RX delay, ACK and Timer2 OK"); /* Выводит компактный результат аппаратных сценариев. */
return 0; /* Завершает проверку после всех обязательных утверждений. */
} /* Завершает host-регрессию порта TI F28335. */