Исправить восстановление CAN после тайм-аута передачи в 166
Перенесено из коммита 985804607d40b84f985eb04fc1f1b0a0dcfbe795 ветки 167. Конфликты разрешены с сохранением обработчиков проекта 166.
This commit is contained in:
@@ -1,8 +1,8 @@
|
||||
/* Команды и телеметрия eCAN. MBOX1 принимает кадры, MBOX0 передаёт
|
||||
* технологические регистры, MBOX2 выделен под ответы логгера.
|
||||
* Слово 127 проходит UMP_commands_write непосредственно в CAN ISR.
|
||||
* Запрос архива ISR лишь ставит на обслуживание; фрагментация
|
||||
* ответов выполняется UMP_CAN_service из фонового цикла.
|
||||
/* Команды и телеметрия eCAN. MBOX1 принимает кадры, MBOX0 передаёт
|
||||
* технологические регистры, MBOX2 выделен под ответы логгера.
|
||||
* Слово 127 проходит UMP_commands_write непосредственно в CAN ISR.
|
||||
* Запрос архива ISR лишь ставит на обслуживание; фрагментация
|
||||
* ответов выполняется UMP_CAN_service из фонового цикла.
|
||||
*/
|
||||
#include "DSP2833x_Device.h" // DSP2833x Headerfile Include File
|
||||
#include "DSP2833x_SWPrioritizedIsrLevels.h"
|
||||
@@ -39,9 +39,9 @@ int CanTimeOutErrorTR = 0;
|
||||
|
||||
int wait=0;
|
||||
|
||||
/* MBOX2 выделен под ответы регистратора; штатный MBOX0 продолжает
|
||||
* передавать технологические данные независимо. ISR только ставит запрос,
|
||||
* а снимок и отправка короткими шагами выполняются в основном цикле. */
|
||||
/* MBOX2 выделен под ответы регистратора; штатный MBOX0 продолжает
|
||||
* передавать технологические данные независимо. ISR только ставит запрос,
|
||||
* а снимок и отправка короткими шагами выполняются в основном цикле. */
|
||||
static volatile unsigned int ump_can_pending;
|
||||
static volatile unsigned int ump_can_tag, ump_can_address, ump_can_value;
|
||||
static unsigned int ump_can_reply[UMP_CAN_MAX_WORDS];
|
||||
@@ -86,17 +86,17 @@ void InitCan(int Port, int DevNum)
|
||||
// Required before writing the MSGIDs
|
||||
ECanRegs->CANME.all = 0;
|
||||
|
||||
// задаем адрес 0 ащикa на передачу
|
||||
// задаем адрес 0 ащикa на передачу
|
||||
ECanMboxes->MBOX0.MSGID.all = id + 0x10 + DevNum;
|
||||
|
||||
// задаем адрес 1 ащикa на прием
|
||||
ECanMboxes->MBOX1.MSGID.all = id + DevNum; //поменать!!!! 1 и 0!!!
|
||||
// задаем адрес 1 ащикa на прием
|
||||
ECanMboxes->MBOX1.MSGID.all = id + DevNum; //поменать!!!! 1 и 0!!!
|
||||
|
||||
// MBOX0 и MBOX2 передают данные; MBOX1 принимает команды.
|
||||
// MBOX0 и MBOX2 передают данные; MBOX1 принимает команды.
|
||||
ECanMboxes->MBOX2.MSGID.all = id + 0x20 + DevNum;
|
||||
ECanRegs->CANMD.all = 0xFFFFFFFA;
|
||||
|
||||
// Включаем три mailbox: телеметрия, команды и ответы логгера.
|
||||
// Включаем три mailbox: телеметрия, команды и ответы логгера.
|
||||
ECanRegs->CANME.all = 0x00000007;
|
||||
|
||||
// Clear all TAn bits
|
||||
@@ -121,7 +121,7 @@ void InitCan(int Port, int DevNum)
|
||||
ECanRegs->CANMC.all = ECanShadow.CANMC.all;
|
||||
while(!ECanRegs->CANES.bit.CCE); // Wait for CCE bit to be set..
|
||||
|
||||
// настриваем скорость CAN
|
||||
// настриваем скорость CAN
|
||||
ECanShadow.CANBTC.all = ECanRegs->CANBTC.all;
|
||||
ECanShadow.CANBTC.bit.SJWREG=1;
|
||||
|
||||
@@ -134,12 +134,12 @@ void InitCan(int Port, int DevNum)
|
||||
ECanRegs->CANMC.all = ECanShadow.CANMC.all;
|
||||
while(ECanRegs->CANES.bit.CCE); // Wait for CCE bit to be cleared..
|
||||
|
||||
// задаем таймауты дла ожиданиа отправки получениа посылки
|
||||
// задаем таймауты дла ожиданиа отправки получениа посылки
|
||||
ECanMOTORegs->MOTO0 = 550000;
|
||||
ECanMOTORegs->MOTO1 = 550000;
|
||||
|
||||
ECanRegs->CANTOC.all = 1;
|
||||
ECanRegs->CANTOS.all = 0; // clear all time-out flags
|
||||
/* До первой передачи отключает тайм-ауты всех mailbox; по SPRUI07, 13.8.18.3, сброс TOC снимает TOS и MTOF. */
|
||||
ECanRegs->CANTOC.all = 0;
|
||||
ECanRegs->CANTSC = 0; // clear time-out counter
|
||||
|
||||
ECanShadow.CANGIM.all = 0;
|
||||
@@ -161,7 +161,7 @@ void InitCan(int Port, int DevNum)
|
||||
IER |= M_INT9; // Enable CPU INT
|
||||
EDIS;
|
||||
|
||||
// завершили настройку CAN ащиков
|
||||
// завершили настройку CAN ащиков
|
||||
|
||||
MessageReceivedCount = 0;
|
||||
ErrorCount = 0;
|
||||
@@ -169,8 +169,8 @@ void InitCan(int Port, int DevNum)
|
||||
MessageTransivedCount=0;
|
||||
}
|
||||
|
||||
/* При занятом MBOX0 возвращается без ожидания ACK в цикле.
|
||||
* Это отличается от блокирующих UART/EEPROM операций main.
|
||||
/* При занятом MBOX0 возвращается без ожидания ACK в цикле.
|
||||
* Это отличается от блокирующих UART/EEPROM операций main.
|
||||
*/
|
||||
void CAN_send(int Port, int data[], int Addr)
|
||||
{
|
||||
@@ -181,6 +181,10 @@ void CAN_send(int Port, int data[], int Addr)
|
||||
ECanRegs = &ECanaRegs;
|
||||
ECanMboxes = &ECanaMboxes;
|
||||
|
||||
/* Не меняет данные mailbox 0, пока передача либо её аппаратная отмена ещё выполняется; бит 0 соответствует штатному TX. */
|
||||
if ((ECanRegs->CANTRS.all | ECanRegs->CANTRR.all) & 1UL)
|
||||
return; /* Сохраняет текущий кадр до завершения CANTRS/CANTRR, не ожидая аппаратного ответа в главном цикле. */
|
||||
|
||||
if(wait)
|
||||
if(!(ECanRegs->CANTA.all & 1))
|
||||
if(!(ECanRegs->CANAA.all & 1))
|
||||
@@ -198,10 +202,15 @@ void CAN_send(int Port, int data[], int Addr)
|
||||
ECanMboxes->MBOX0.MDL.all = loword;
|
||||
|
||||
EALLOW;
|
||||
/* Отключает старый контроль mailbox 0 перед перезапуском счётчика; остальные mailbox используют собственный контроль. */
|
||||
ECanRegs->CANTOC.all = 0;
|
||||
/* Записывает ECanRegs->CANTSC значением 0; E CAN Regs CANTSC. */
|
||||
ECanRegs->CANTSC = 0; // clear time-out counter
|
||||
/* Заново разрешает тайм-аут штатного TX с порогом MOTO0, установленным в InitCan, только для следующего кадра. */
|
||||
ECanRegs->CANTOC.all = 1UL;
|
||||
EDIS;
|
||||
|
||||
ECanRegs->CANTRS.all = 1; // запустить передачу
|
||||
ECanRegs->CANTRS.all = 1; // запустить передачу
|
||||
|
||||
wait=1;
|
||||
led1_toggle();
|
||||
@@ -220,9 +229,9 @@ void Handlai(volatile struct ECAN_MBOXES * ECanMboxes)
|
||||
hiword = ECanMboxes->MBOX1.MDH.all;
|
||||
loword = ECanMboxes->MBOX1.MDL.all;
|
||||
|
||||
/* Служебный запрос логгера не является командой УМП и сам не должен
|
||||
* заполнять журнал CAN-событиями при онлайн-чтении. Короткие кадры
|
||||
* отбрасываются: незаполненные байты mailbox могли остаться от прошлого. */
|
||||
/* Служебный запрос логгера не является командой УМП и сам не должен
|
||||
* заполнять журнал CAN-событиями при онлайн-чтении. Короткие кадры
|
||||
* отбрасываются: незаполненные байты mailbox могли остаться от прошлого. */
|
||||
if ((unsigned int)(hiword >> 16) == UMP_CAN_MARKER) {
|
||||
if (ECanMboxes->MBOX1.MSGCTRL.bit.DLC == 8) {
|
||||
ump_can_tag = (unsigned int)hiword;
|
||||
@@ -244,10 +253,10 @@ void Handlai(volatile struct ECAN_MBOXES * ECanMboxes)
|
||||
Data[1] = (loword>>16) & 0xffff;
|
||||
Data[2] = (loword ) & 0xffff;
|
||||
|
||||
/* Каждое разрешённое слово имеет собственный адрес. Не опрашиваем
|
||||
* Commands после кадра безусловно: пакет телеметрии не должен
|
||||
* превращать внутренний сброс параметров в командный фронт.
|
||||
* Адрес 127 проходит через единый обработчик полного слова. */
|
||||
/* Каждое разрешённое слово имеет собственный адрес. Не опрашиваем
|
||||
* Commands после кадра безусловно: пакет телеметрии не должен
|
||||
* превращать внутренний сброс параметров в командный фронт.
|
||||
* Адрес 127 проходит через единый обработчик полного слова. */
|
||||
{
|
||||
unsigned int i;
|
||||
for (i = 0; i < 3; i++, adr++) {
|
||||
@@ -258,7 +267,7 @@ void Handlai(volatile struct ECAN_MBOXES * ECanMboxes)
|
||||
}
|
||||
}
|
||||
|
||||
/* Фиксируем применённую команду до выхода из обработчика CAN. */
|
||||
/* Фиксируем применённую команду до выхода из обработчика CAN. */
|
||||
ump_log_can((unsigned int)(hiword >> 16), Data[0], Data[1], Data[2]);
|
||||
led2_toggle();
|
||||
}
|
||||
@@ -292,8 +301,16 @@ interrupt void CANa_reset_err(void)
|
||||
PieCtrlRegs.PIEACK.all = 0xFFFF; // Enable PIE interrupts
|
||||
EINT;
|
||||
|
||||
ECanaRegs.CANTRR.all = 1;
|
||||
CanTimeOutErrorTR++;
|
||||
/* Проверяет реальный тайм-аут mailbox 0: отложенное PIE-прерывание после успешного TX уже может не иметь причины. */
|
||||
if (ECanaRegs.CANTOS.all & 1UL)
|
||||
{ /* Обрабатывает только штатный передатчик; служебный mailbox 2 контролируется отдельно в UMP_CAN_service. */
|
||||
EALLOW; /* Разрешает изменение управляющего регистра eCAN перед снятием источника прерывания. */
|
||||
ECanaRegs.CANTOC.all = 0; /* Сброс TOC снимает TOS и MTOF согласно SPRUI07, 13.8.18.3; одной отмены TX недостаточно. */
|
||||
EDIS; /* Восстанавливает защиту регистров до выхода из ветви обработки тайм-аута. */
|
||||
if (ECanaRegs.CANTRS.all & 1UL) /* Передача могла завершиться между чтением TOS и снятием контроля. */
|
||||
ECanaRegs.CANTRR.all = 1UL; /* Запрашивает отмену только ещё занятого mailbox 0, не задерживая прерывание ожиданием CANAA. */
|
||||
CanTimeOutErrorTR++; /* Считает обнаруженный тайм-аут один раз, исключая фантомные повторные входы PIE. */
|
||||
} /* Следующая штатная передача сама заново включит TOC после сброса временного счётчика. */
|
||||
|
||||
PieCtrlRegs.PIEACK.bit.ACK9 |= 1;
|
||||
|
||||
@@ -307,10 +324,10 @@ interrupt void CANa_reset_err(void)
|
||||
//===========================================================================
|
||||
|
||||
|
||||
/* Один вызов обслуживает максимум один кадр и никогда не ждёт ACK в цикле.
|
||||
* Снимок фиксируется один раз на запрос: CAN-фрагменты не смешивают
|
||||
* значения до/после переключения кольцевого банка. Новые запросы забираем
|
||||
* после завершения предыдущего ответа. GUI держит только один запрос. */
|
||||
/* Один вызов обслуживает максимум один кадр и никогда не ждёт ACK в цикле.
|
||||
* Снимок фиксируется один раз на запрос: CAN-фрагменты не смешивают
|
||||
* значения до/после переключения кольцевого банка. Новые запросы забираем
|
||||
* после завершения предыдущего ответа. GUI держит только один запрос. */
|
||||
void UMP_CAN_service(void)
|
||||
{
|
||||
unsigned int saved, tag, address, value, error;
|
||||
@@ -321,11 +338,11 @@ void UMP_CAN_service(void)
|
||||
ump_can_waiting = 0;
|
||||
} else if ((ECanaRegs.CANAA.all & 4UL) ||
|
||||
((unsigned long)CpuTimer1.InterruptCount - ump_can_sent_at > 250UL)) {
|
||||
/* Нет подтверждения шины: отменяем только служебный mailbox.
|
||||
* GUI обнаружит неполный ответ по тайм-ауту; управление MBOX0
|
||||
* и приём команд MBOX1 продолжаются. */
|
||||
/* Нет подтверждения шины: отменяем только служебный mailbox.
|
||||
* GUI обнаружит неполный ответ по тайм-ауту; управление MBOX0
|
||||
* и приём команд MBOX1 продолжаются. */
|
||||
ECanaRegs.CANTRR.all = 4UL;
|
||||
/* До фактического завершения отмены mailbox не переиспользуем. */
|
||||
/* До фактического завершения отмены mailbox не переиспользуем. */
|
||||
if (!(ECanaRegs.CANAA.all & 4UL)) return;
|
||||
ECanaRegs.CANAA.all = 4UL;
|
||||
ump_can_waiting = 0;
|
||||
@@ -342,7 +359,7 @@ void UMP_CAN_service(void)
|
||||
__restore_interrupts(saved);
|
||||
}
|
||||
if (ump_can_index >= ump_can_length) return;
|
||||
/* Перед следующим фрагментом уступаем штатной телеметрии MBOX0. */
|
||||
/* Перед следующим фрагментом уступаем штатной телеметрии MBOX0. */
|
||||
if (ECanaRegs.CANTRS.all & 1UL) return;
|
||||
hiword = ((unsigned long)(UMP_CAN_MARKER + ump_can_index/2) << 16) | ump_can_reply_tag;
|
||||
loword = ((unsigned long)ump_can_reply[ump_can_index] << 16) | ump_can_reply[ump_can_index+1];
|
||||
|
||||
58
tests/can_timeout/model.inc
Normal file
58
tests/can_timeout/model.inc
Normal file
@@ -0,0 +1,58 @@
|
||||
#include <stdint.h> /* Задаёт размеры 16- и 32-битных типов C28x в хостовой модели. */
|
||||
#include <stdio.h> /* Печатает конкретное нарушенное условие и итог сценариев. */
|
||||
#include <string.h> /* Обнуляет модель между независимыми сценариями. */
|
||||
typedef uint16_t Uint16; /* Воспроизводит тип масок PIE, используемый обработчиком TI. */
|
||||
typedef uint32_t Uint32; /* Воспроизводит упаковку двух технологических слов в MDH/MDL. */
|
||||
#define interrupt /* Убирает только аппаратный атрибут функции при компиляции на ПК. */
|
||||
#define EALLOW ((void)0) /* Модель не воспроизводит механизм защиты регистров ядра. */
|
||||
#define EDIS ((void)0) /* Защита EALLOW проверяется сборкой настоящего модуля под TI. */
|
||||
#define EINT ((void)0) /* Входы ISR вызывает сценарий, не поток операционной системы. */
|
||||
#define DINT ((void)0) /* Отключение прерываний не требует имитации планировщика на ПК. */
|
||||
#define M_INT9 0x0100U /* Маска CPU INT9 соответствует девятой группе PIE. */
|
||||
#define MINT9 0x0100U /* Для проверки жизненного цикла сохраняет разрешение группы CAN. */
|
||||
#define MG96 0x00DFU /* Исключает повторную вложенную обработку INTx6 той же группы. */
|
||||
struct REG { uint32_t all; }; /* Представляет 32-битные регистры eCAN без зависимости от размеров long на хосте. */
|
||||
struct ECAN_REGS { struct REG CANTA, CANAA, CANTRR, CANTRS, CANTOC, CANTOS; uint32_t CANTSC; }; /* Даёт рабочим функциям реальные имена используемых регистров. */
|
||||
static volatile struct ECAN_REGS ECanaRegs; /* Позволяет наблюдать все записи production-функций при оптимизации O3. */
|
||||
struct ECAN_MBOXES { struct { struct REG MDH, MDL; } MBOX0; }; /* Моделирует только штатный передатчик, который проверяет этот тест. */
|
||||
static volatile struct ECAN_MBOXES ECanaMboxes; /* Содержимое mailbox остаётся доступным для проверок неизменности занятого кадра. */
|
||||
static struct { struct { Uint16 all; } PIEIER9; union { unsigned all; struct { unsigned reserved:8, ACK9:1; } bit; } PIEACK; } PieCtrlRegs; /* Связывает запись всей маски PIEACK с битом подтверждения девятой группы в позиции 8. */
|
||||
static Uint16 IER; /* Представляет маску разрешённых прерываний ядра. */
|
||||
static int wait, CanTimeOutErrorTR; /* Повторяет состояние штатного передатчика и счётчик обнаруженных тайм-аутов. */
|
||||
static void led1_toggle(void) {} /* Светодиод передачи не влияет на завершение аппаратной транзакции. */
|
||||
/* PRODUCTION */
|
||||
#define CHECK(condition) do { if (!(condition)) { printf("FAIL line %d: %s\n", __LINE__, #condition); return 1; } } while (0) /* Не отключает проверку условий при оптимизации или NDEBUG. */
|
||||
static void reset_model(void) { /* Начинает сценарий без остаточных запросов и подтверждений предыдущего теста. */
|
||||
memset((void *)&ECanaRegs, 0, sizeof(ECanaRegs)); /* Сбрасывает моделируемые аппаратные регистры CAN. */
|
||||
memset((void *)&ECanaMboxes, 0, sizeof(ECanaMboxes)); /* Убирает ранее переданное содержимое mailbox. */
|
||||
memset(&PieCtrlRegs, 0, sizeof(PieCtrlRegs)); /* Исключает ложное подтверждение PIE из предыдущего сценария. */
|
||||
PieCtrlRegs.PIEIER9.all = 0x0030U; IER = M_INT9; /* Разрешает оба CAN-прерывания для проверки восстановления маски ISR. */
|
||||
wait = 0; CanTimeOutErrorTR = 0; /* Повторяет начальные значения production-переменных. */
|
||||
} /* Завершает начальную установку независимого сценария. */
|
||||
int main(void) { /* Проверяет потерю ACK, фантомное прерывание, занятый mailbox и восстановление отправки. */
|
||||
int words[128] = {0}; unsigned calls; /* Хранит данные кнопок и ограничивает имитацию повторных входов ISR. */
|
||||
reset_model(); ECanaRegs.CANTOC.all = 1; ECanaRegs.CANTOS.all = 1; ECanaRegs.CANTRS.all = 5; wait = 1; /* Тайм-аут относится к mailbox 0, пока служебный mailbox 2 тоже занят. */
|
||||
for (calls = 0; calls < 4 && ECanaRegs.CANTOS.all; calls++) { /* Повторяет IRQ, пока источник не снят, с явной защитой от зависания старого кода. */
|
||||
CANa_reset_err(); /* Выполняет настоящий обработчик проекта, включая отмену TX и подтверждение PIE. */
|
||||
ECanaRegs.CANTOS.all &= ECanaRegs.CANTOC.all; /* По SPRUI07 13.8.18.3 сброс TOC аппаратно снимает TOS и MTOF; одна запись CANTRR этого не гарантирует. */
|
||||
} /* Проверяет, что обработчик освобождает процессор после первого тайм-аута. */
|
||||
CHECK(calls == 1 && ECanaRegs.CANTOS.all == 0 && CanTimeOutErrorTR == 1); /* Повторные входы без снятия источника означают блокирование основного цикла. */
|
||||
CHECK(ECanaRegs.CANTRR.all == 1 && ECanaRegs.CANTRS.all == 5); /* Отмена запрошена только для штатного TX; модель намеренно ещё не завершила аппаратную отмену. */
|
||||
CHECK(PieCtrlRegs.PIEIER9.all == 0x0030U && PieCtrlRegs.PIEACK.bit.ACK9 == 1); /* ISR сохраняет прежние разрешения обоих каналов CAN и подтверждает свою группу. */
|
||||
words[16] = 8; ECanaMboxes.MBOX0.MDH.all = 0x12345678UL; /* Подготавливает новое состояние кнопки при ещё занятом предыдущем кадре. */
|
||||
CAN_send(0, words, 16); CHECK(ECanaMboxes.MBOX0.MDH.all == 0x12345678UL); /* Не допускает перезаписи данных до завершения отмены. */
|
||||
ECanaRegs.CANTRS.all = 0; ECanaRegs.CANTRR.all = 0; ECanaRegs.CANAA.all = 1; ECanaRegs.CANTSC = 999; /* Имитирует аппаратное завершение отмены и подтверждение CANAA0. */
|
||||
CAN_send(0, words, 16); /* Проверяет повторный запуск тем же реальным передатчиком после исчезновения тайм-аута. */
|
||||
CHECK(ECanaRegs.CANTOC.all == 1 && ECanaRegs.CANTSC == 0 && ECanaRegs.CANTRS.all == 1); /* Новая передача вновь получает собственный отсчёт тайм-аута. */
|
||||
CHECK(ECanaMboxes.MBOX0.MDH.all == 0xE0100008UL && ECanaMboxes.MBOX0.MDL.all == 0); /* После восстановления передаются свежие данные R16, а не старый нулевой кадр. */
|
||||
reset_model(); ECanaRegs.CANTOC.all = 1; ECanaRegs.CANTRS.all = 1; /* Фантомное PIE-прерывание может прийти, когда начат следующий кадр без тайм-аута. */
|
||||
CANa_reset_err(); CHECK(ECanaRegs.CANTRR.all == 0 && CanTimeOutErrorTR == 0 && ECanaRegs.CANTOC.all == 1); /* Не отменяет исправную передачу при уже снятом TOS0. */
|
||||
reset_model(); ECanaRegs.CANTOC.all = 1; ECanaRegs.CANTOS.all = 1; /* Передача успела завершиться, но сценарий сохраняет зафиксированный флаг тайм-аута. */
|
||||
CANa_reset_err(); CHECK(ECanaRegs.CANTRR.all == 0 && ECanaRegs.CANTOC.all == 0); /* Снимает контроль, не отменяя уже свободный mailbox. */
|
||||
reset_model(); ECanaRegs.CANTRR.all = 1; ECanaMboxes.MBOX0.MDH.all = 0x11223344UL; /* Аппаратная отмена ещё активна, даже если CANTRS уже снят. */
|
||||
CAN_send(0, words, 16); CHECK(ECanaMboxes.MBOX0.MDH.all == 0x11223344UL && ECanaRegs.CANTRS.all == 0); /* Не запускает повторный кадр до снятия CANTRR0. */
|
||||
reset_model(); wait = 1; ECanaRegs.CANTA.all = 1; /* Проверяет обычную передачу после успешного подтверждения предыдущего кадра. */
|
||||
CAN_send(0, words, 16); CHECK(ECanaRegs.CANTRS.all == 1 && ECanaRegs.CANTOC.all == 1); /* Исправление не блокирует штатный обмен при наличии CAN ACK. */
|
||||
puts("PASS CAN timeout: IRQ released, phantom ignored, busy mailbox preserved, TX recovered"); /* Сообщает только о проверенных сценариях программной модели. */
|
||||
return 0; /* Подтверждает прохождение всех проверок оптимизированного рабочего кода. */
|
||||
} /* Завершает тест без доступа к реальному USB-CAN или питанию платы. */
|
||||
31
tests/can_timeout/run_test.py
Normal file
31
tests/can_timeout/run_test.py
Normal file
@@ -0,0 +1,31 @@
|
||||
"""Проверяет тайм-аут штатного CAN на настоящих функциях проекта 166.""" # Модель не заменяет испытание аппаратного eCAN.
|
||||
import os # Передаёт пути библиотек GCC только дочерним процессам.
|
||||
from pathlib import Path # Находит исходники независимо от рабочего каталога запуска.
|
||||
import re # Удаляет комментарии и выделяет тела проверяемых функций.
|
||||
import subprocess # Запускает компилятор и проверяет поведенческий результат модели.
|
||||
import tempfile # Изолирует сгенерированные файлы от образов прошивки пользователя.
|
||||
import sys # Позволяет проверить отдельно сохранённую старую версию ecan.c.
|
||||
|
||||
root = Path(__file__).resolve().parents[2] # Корень проекта содержит рабочую реализацию eCAN.
|
||||
source_path = Path(sys.argv[1]) if len(sys.argv) > 1 else root / "Source/Internal/ecan.c" # Дополнительный путь нужен для проверки чувствительности к старой ошибке.
|
||||
source = re.sub(r"/\*.*?\*/|//[^\n]*", "", source_path.read_text(encoding="utf-8"), flags=re.S) # Комментарии со скобками не участвуют в выделении функций.
|
||||
|
||||
def function(name): # Извлекает текущий алгоритм вместо поддержания отдельной копии в тесте.
|
||||
match = re.search(r"(?:interrupt\s+)?void\s+" + name + r"\([^;{}]*\)\s*\{", source) # Отличает определение от вызова и объявления.
|
||||
assert match is not None, name # Исчезновение функции должно останавливать проверку.
|
||||
end, depth = match.end(), 1 # Начинает поиск после открывающей скобки тела.
|
||||
while depth: # Учитывает вложенные условия до закрытия внешнего блока.
|
||||
depth += (source[end] == "{") - (source[end] == "}") # Считает только скобки кода без комментариев.
|
||||
end += 1 # Включает найденную закрывающую скобку в результат.
|
||||
return source[match.start():end] # Сохраняет все реальные обращения к регистрам и условия.
|
||||
|
||||
model = Path(__file__).with_name("model.inc").read_text(encoding="utf-8") # Загружает минимальное аппаратное окружение и сценарии потери ACK.
|
||||
model = model.replace("/* PRODUCTION */", function("CAN_send") + "\n" + function("CANa_reset_err")) # Проверяет передачу и прерывание в одном жизненном цикле.
|
||||
compiler = os.environ.get("CC", "C:/msys64/mingw64/bin/gcc.exe") # Использует установленный GCC, сохраняя возможность явно выбрать другой.
|
||||
environment = dict(os.environ) # Не меняет окружение приложения и проекта CCS.
|
||||
environment["PATH"] = str(Path(compiler).parent) + os.pathsep + environment.get("PATH", "") # Делает библиотеки MinGW доступными тестовой программе.
|
||||
with tempfile.TemporaryDirectory(prefix="can-timeout-") as directory: # Удаляет только собственные временные файлы после завершения проверки.
|
||||
c_path, exe_path = Path(directory) / "test.c", Path(directory) / "test.exe" # Разделяет исходник модели и исполняемый результат.
|
||||
c_path.write_text(model, encoding="utf-8") # Сохраняет русские пояснения модели в требуемой кодировке.
|
||||
subprocess.run([compiler, "-std=c99", "-O3", "-Wall", "-Wextra", "-Wno-unused-parameter", "-Werror", str(c_path), "-o", str(exe_path)], env=environment, check=True) # Проверяет реальные функции под оптимизацией и без скрытых предупреждений.
|
||||
subprocess.run([str(exe_path)], env=environment, check=True, timeout=10) # Зависание и нарушенное условие делают сценарий неуспешным.
|
||||
Reference in New Issue
Block a user