Сохранить состояние TESTLAMP при кратких провалах входа и исключить повторный вход Timer1 во время передачи индикации. Явно сбрасывать служебный обмен CAN при запуске и выбирать соседнюю фазу по локальному индексу канала АЦП. Сохранить текущую последовательность DATA/CLK и проверочные варианты таймингов, добавить поведенческие тесты и обновить образ прошивки с картой памяти.
88 lines
17 KiB
C++
88 lines
17 KiB
C++
#include <stdint.h> /* Задаёт точные размеры регистров и слов протокола при исполнении модели на ПК. */
|
||
#include "ump_tms28335.h" /* Подключает настоящий интерфейс проверки запросов и упаковки фрагментов CAN. */
|
||
#define UMP_CAN_MAX_WORDS UMP_MAX_WORDS /* Повторяет максимальную длину ответа из рабочего заголовка адаптера. */
|
||
#define UMP_CAN_MARKER UMP_MARKER /* Использует каноническую метку отличия служебного кадра от технологических данных. */
|
||
#define ump_can_prepare ump_tms28335_prepare /* Сохраняет штатное соответствие имени адаптера канонической функции порта. */
|
||
#define ANSWER_LEN 128U /* Моделирует границу штатных технологических регистров для компиляции настоящего Handlai. */
|
||
#define dsk_LOAD 6 /* Повторяет номер платы нагрузки из package.h для компиляции технологической ветви Handlai; сценарии используют служебные кадры. */
|
||
#define CHECK(condition) do { if (!(condition)) return __LINE__; } while (0) /* Сохраняет проверки при O3 и возвращает точное место регрессии вызывающему Python. */
|
||
|
||
struct MODEL_REG { uint32_t all; }; /* Моделирует атомарное 32-битное чтение и запись слова аппаратного регистра. */
|
||
struct MODEL_MAILBOX { struct MODEL_REG MDL, MDH; struct { struct { unsigned int DLC; } bit; } MSGCTRL; }; /* Предоставляет реальные имена полей данных и длины кадра, читаемые рабочим кодом. */
|
||
struct ECAN_MBOXES { struct MODEL_MAILBOX MBOX1, MBOX2; }; /* Отделяет входящий запрос в mailbox 1 от служебного ответа в mailbox 2. */
|
||
static struct { struct MODEL_REG CANTA, CANAA, CANTRR, CANTRS; } ECanaRegs; /* Хранит подтверждения, отмену и факт запуска передачи для наблюдения сценарием. */
|
||
static struct ECAN_MBOXES ECanaMboxes; /* Предоставляет обработчикам адресуемую память вместо физической периферии. */
|
||
static struct { unsigned long InterruptCount; } CpuTimer1; /* Задаёт миллисекундное время, от которого сервис считает тайм-аут ожидания ответа. */
|
||
static int modbus[ANSWER_LEN], Desk, Lonely; /* Обеспечивает зависимости технологической ветви Handlai, не исполняемой при служебном запросе. */
|
||
static unsigned int model_reads, model_writes, model_unexpected, model_irq_mask; /* Позволяет отличить ложную команду, корректное чтение и несбалансированную блокировку IRQ. */
|
||
|
||
static unsigned int __disable_interrupts(void) { unsigned int saved = model_irq_mask; model_irq_mask = 1; return saved; } /* Имитирует сохранение предыдущей маски, чтобы вложенные вызовы оставались проверяемыми. */
|
||
static void __restore_interrupts(unsigned int saved) { model_irq_mask = saved; } /* Возвращает исходную маску и позволяет сценарию обнаружить оставленную блокировку. */
|
||
static void Load_voltage_can(unsigned int header, unsigned int a, unsigned int b, unsigned int c) { (void)header; (void)a; (void)b; (void)c; model_unexpected++; } /* Считает ошибочным попадание служебного запроса в обработку технологического напряжения. */
|
||
static void ump_log_can(unsigned int header, unsigned int a, unsigned int b, unsigned int c) { (void)header; (void)a; (void)b; (void)c; model_unexpected++; } /* Обнаруживает лишнюю регистрацию служебного опроса как управляющего CAN-события. */
|
||
static void led2_toggle(void) { model_unexpected++; } /* Отмечает проход по обычной ветви CAN вместо раннего возврата служебного запроса. */
|
||
static unsigned int Load_protection_prepare(unsigned int fn, uint16_t address, uint16_t value, uint16_t *reply, unsigned int *error) { (void)fn; (void)address; (void)value; (void)reply; *error = 2; model_unexpected++; return 0; } /* Запросы теста адресуют журнал, поэтому попадание в карту защиты является ошибкой маршрутизации. */
|
||
#if EEPROM_M95M02 /* Компилирует зависимости дополнительной ветви так же, как прошивка профиля M95M02. */
|
||
static int gas167_contains(uint16_t address) { return address >= 0x5000U && address < 0x5080U; } /* Отделяет область GAS от проверяемого журнала, не копируя реализацию профиля. */
|
||
static unsigned int gas167_prepare(unsigned int fn, uint16_t address, uint16_t value, int can, uint16_t *reply, unsigned int *error) { (void)fn; (void)address; (void)value; (void)can; (void)reply; *error = 2; model_unexpected++; return 0; } /* Делает неожиданное обращение к GAS наблюдаемой ошибкой в тесте запуска журнала. */
|
||
#endif /* Завершает минимальные зависимости профиля, не подменяя проверку общей CAN-маршрутизации. */
|
||
unsigned int ump_log_read(unsigned int address) { model_reads++; return 0x5500U + address - UMP_BASE; } /* Возвращает узнаваемые слова backend, чтобы ответ нельзя было спутать со старым содержимым RAM. */
|
||
int ump_log_write(unsigned int address, unsigned int value) { (void)address; (void)value; model_writes++; return 1; } /* Регистрирует любую нежелательную запись, случайно возникшую из остаточного запроса. */
|
||
|
||
/* PRODUCTION_CAN */
|
||
|
||
static void model_reset_hardware(void) /* Очищает только модель периферии; управляющую RAM оставляет сценарию для воспроизведения холодного старта. */
|
||
{ /* Начинает воспроизводимое состояние аппаратных mailbox после сброса контроллера. */
|
||
ECanaRegs.CANTA.all = 0; ECanaRegs.CANAA.all = 0; ECanaRegs.CANTRR.all = 0; ECanaRegs.CANTRS.all = 0; /* Убирает подтверждения и запросы, которые аппаратный сброс не сохраняет вместе с RAM. */
|
||
ECanaMboxes.MBOX2.MDL.all = 0; ECanaMboxes.MBOX2.MDH.all = 0; /* Обнуляет наблюдаемые данные ответа для обнаружения каждой новой передачи. */
|
||
CpuTimer1.InterruptCount = 1000; model_reads = 0; model_writes = 0; model_unexpected = 0; model_irq_mask = 0; /* Задаёт миллисекундный такт и независимые счётчики нового сценария. */
|
||
} /* Завершает аппаратный сброс без скрытой инициализации проверяемых переменных прошивки. */
|
||
|
||
static void model_poison_state(void) /* Воссоздаёт допустимые остатки RAM, ранее приводившие к ответу без входного запроса. */
|
||
{ /* Начинает заполнение всех управляющих полей ненулевыми либо специально подобранными значениями. */
|
||
ump_can_pending = 0; ump_can_tag = 0xA506U; ump_can_address = UMP_BASE; ump_can_value = 1; /* Сохраняет след прежней команды записи, но отмечает отсутствие нового принятого запроса. */
|
||
ump_can_length = 2; ump_can_index = 0; ump_can_reply_tag = 0x0100U; ump_can_waiting = 0; ump_can_sent_at = 777; /* Представляет старый двухсловный ответ с token 1, который сервис ошибочно считает готовым. */
|
||
ump_can_reply[0] = 0x1234U; ump_can_reply[1] = 0x5678U; /* Создаёт отличимый от backend payload для доказательства отправки именно остаточного буфера. */
|
||
} /* Завершает заполнение состояния, которое настоящий сброс обязан обезвредить. */
|
||
|
||
static int model_first_request(unsigned int token) /* Проверяет весь первый ответ после сброса через настоящий приёмник, валидатор и упаковщик. */
|
||
{ /* Начинает независимую транзакцию чтения 16 слов заголовка журнала. */
|
||
unsigned int fragment; /* Перебирает восемь CAN-фрагментов, каждый из которых содержит два 16-битных слова. */
|
||
ECanaMboxes.MBOX1.MDH.all = ((uint32_t)UMP_CAN_MARKER << 16) | ((uint32_t)token << 8) | 3U; /* Формирует служебную метку, новый token и штатную функцию чтения FC03. */
|
||
ECanaMboxes.MBOX1.MDL.all = ((uint32_t)UMP_BASE << 16) | 16U; ECanaMboxes.MBOX1.MSGCTRL.bit.DLC = 8; /* Запрашивает допустимый восьмибайтный кадр чтения полного 16-словного заголовка. */
|
||
Handlai(&ECanaMboxes); /* Передаёт запрос настоящему разборщику mailbox без прямой установки поля pending. */
|
||
CHECK(ump_can_pending == 1 && model_reads == 0 && model_writes == 0); /* Проверяет, что ISR только опубликовал запрос и не исполнил его вместо главного цикла. */
|
||
for (fragment = 0; fragment < 8; fragment++) { /* Проверяет все фрагменты, включая последний, чтобы сброс счётчиков не скрывал укороченный ответ. */
|
||
UMP_CAN_service(); /* Готовит снимок при первом вызове и далее отправляет по одному настоящему фрагменту. */
|
||
CHECK(ECanaRegs.CANTRS.all == 4 && ump_can_waiting == 1); /* Требует запуска mailbox 2 и ожидания подтверждения именно отправленного кадра. */
|
||
CHECK(ECanaMboxes.MBOX2.MDL.all == (((uint32_t)(0x5500U + fragment * 2U) << 16) | (0x5501U + fragment * 2U))); /* Сравнивает оба слова кадра с содержимым нового снимка, исключая остатки прежнего ответа. */
|
||
CHECK(ECanaMboxes.MBOX2.MDH.all == (((uint32_t)(UMP_CAN_MARKER + fragment) << 16) | ((uint32_t)token << 8))); /* Проверяет порядок фрагментов, token текущего запроса и отсутствие кода ошибки. */
|
||
ECanaRegs.CANTRS.all = 0; ECanaRegs.CANTA.all = 4; /* Имитирует окончание передачи и подтверждение только проверенного фрагмента аппаратурой CAN. */
|
||
} /* Завершает передачу всех 16 запрошенных слов. */
|
||
UMP_CAN_service(); ECanaRegs.CANTA.all = 0; /* Позволяет сервису принять последнее подтверждение; затем моделирует очистку W1C-регистра аппаратурой. */
|
||
CHECK(ECanaRegs.CANTRS.all == 0 && ump_can_waiting == 0 && ump_can_pending == 0); /* Убеждается, что после последнего ACK не отправляется лишний либо старый фрагмент. */
|
||
CHECK(model_reads == 16 && model_writes == 0 && model_unexpected == 0 && model_irq_mask == 0); /* Требует единственного согласованного снимка, отсутствия ложных команд и восстановленной маски IRQ. */
|
||
return 0; /* Сообщает вызывающему сценарию о полном успешном ответе после инициализации. */
|
||
} /* Завершает поведенческую проверку первой транзакции. */
|
||
|
||
#ifdef _WIN32 /* Экспортирует тест явно для загрузки MinGW DLL через ctypes в Windows. */
|
||
__declspec(dllexport) /* Делает run_tests видимой снаружи библиотеки без таблицы экспорта проекта прошивки. */
|
||
#endif /* На остальных платформах используется обычная видимость глобальной функции. */
|
||
int run_tests(void) /* Возвращает ноль только при выполнении всех сценариев запуска и повторного сброса. */
|
||
{ /* Начинает проверку реального дефекта до сброса и его устранения рабочим helper. */
|
||
int result; /* Передаёт точную строку сбоя из сценария полной CAN-транзакции. */
|
||
model_reset_hardware(); model_poison_state(); UMP_CAN_service(); /* Воспроизводит старое поведение на грязной RAM без вызова добавленного сброса. */
|
||
CHECK(ECanaRegs.CANTRS.all == 4 && ECanaMboxes.MBOX2.MDL.all == 0x12345678UL && ump_can_pending == 0); /* Доказывает самопроизвольный ответ, чтобы тест не оказался нечувствителен к исходной регрессии. */
|
||
model_reset_hardware(); model_poison_state(); ump_can_init(); /* Вызывает настоящий новый сброс при тех же остатках RAM и чистой периферии. */
|
||
CHECK(ump_can_pending == 0 && ump_can_tag == 0 && ump_can_address == 0 && ump_can_value == 0); /* Проверяет, что старые параметры не смогут превратиться в ложную принятую команду. */
|
||
CHECK(ump_can_length == 0 && ump_can_index == 0 && ump_can_reply_tag == 0 && ump_can_waiting == 0 && ump_can_sent_at == 0); /* Требует очистки всех управляющих полей незавершённой передачи и тайм-аута. */
|
||
UMP_CAN_service(); UMP_CAN_service(); /* Даёт главному циклу повторно обработать состояние до появления первого запроса. */
|
||
CHECK(ECanaRegs.CANTRS.all == 0 && ECanaRegs.CANTRR.all == 0 && model_reads == 0 && model_writes == 0); /* Запрещает ложную передачу, отмену несуществующего кадра и исполнение остаточной команды. */
|
||
result = model_first_request(0x37U); if (result) return result; /* Проверяет приём и все фрагменты первого запроса после холодного запуска. */
|
||
model_reset_hardware(); model_poison_state(); ump_can_waiting = 1; ump_can_pending = 1; /* Воспроизводит перезапуск во время передачи с ещё не обработанным старым запросом в RAM. */
|
||
ump_can_init(); CpuTimer1.InterruptCount += 1000; UMP_CAN_service(); /* Проверяет сброс ожидания даже после истечения прежнего тайм-аута в 250 мс. */
|
||
CHECK(ECanaRegs.CANTRS.all == 0 && ECanaRegs.CANTRR.all == 0 && ump_can_pending == 0 && ump_can_waiting == 0); /* Не допускает зависания в отмене старого mailbox либо исполнения устаревшего pending. */
|
||
result = model_first_request(0x59U); if (result) return result; /* Доказывает, что после такого перезапуска новый token обслуживается полной последовательностью фрагментов. */
|
||
return 0; /* Подтверждает исправление холодного старта без изменения алгоритма штатного CAN-ответа. */
|
||
} /* Завершает экспортированную проверку регрессии для выбранного профиля EEPROM. */
|