From 085b58716749c418642db0a8b0e1d8b47720ecd5 Mon Sep 17 00:00:00 2001 From: andrey Date: Tue, 22 Sep 2026 12:37:24 +0300 Subject: [PATCH] =?UTF-8?q?=D0=98=D1=81=D0=BF=D1=80=D0=B0=D0=B2=D0=BB?= =?UTF-8?q?=D0=B5=D0=BD=D0=B0=20=D0=BE=D0=B1=D1=80=D0=B0=D0=B1=D0=BE=D1=82?= =?UTF-8?q?=D0=BA=D0=B0=20=D1=84=D1=80=D0=BE=D0=BD=D1=82=D0=BE=D0=B2=20STA?= =?UTF-8?q?RT/STOP=20=D0=B2=20166?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Добавлены локальные копии команд и обновлены файлы прошивки. Сохранены анализ слабых мест START/STOP и тесты воспроизведения ошибки ограничения ЦАП проекта 167. --- Bin/UKSSTMS320F28335.bin | Bin 27324 -> 27312 bytes Bin/UKSSTMS320F28335.hex | 766 +++++++++++++++---------------- Bin/UKSSTMS320F28335.map | 8 +- Bin/UKSSTMS320F28335.out | Bin 288221 -> 288300 bytes Doc/DAC167_limit_test_results.md | 109 +++++ Doc/START_STOP_weak_points.md | 144 ++++++ Source/Internal/main.c | 12 +- tests/test_dac167_limit.py | 105 +++++ 8 files changed, 753 insertions(+), 391 deletions(-) create mode 100644 Doc/DAC167_limit_test_results.md create mode 100644 Doc/START_STOP_weak_points.md create mode 100644 tests/test_dac167_limit.py diff --git a/Bin/UKSSTMS320F28335.bin b/Bin/UKSSTMS320F28335.bin index 544c2c0fd6bf370626898184c3ca4dd0938b222d..ac9196dd69a8c248621f5375c71ce6171ac34191 100644 GIT binary patch delta 1741 zcmZuyYfKbZ6uxI>+2!H#l8S)Y1&t!vQmBg(-GamNu!s~~C@N?iD%KEfRwKDsZHldu z78<&;m%0fw2n(z`RNP%A#r`M)8%f$G3Pxz-qgpXl;t&5c7Nd#Ndl#(s@ndezeCNC0 z`R2}JRlGs_V-8@X)a zeiDk9O*b-`KSI)|z=}}1%4MO&8hXj0cj@q%N6`LE*D704ETyNM`S zLHUPi<#e=Yb)rC(BboB>;Bf!2Q@{-_bS=`v+>LoxVT97?CE$}j0rz+We7cQQN9{Ty z;4P#uI~VMvCEJ)&W-Jy=hE-{791L}lvlC0I13rizk3blG z-|8}TwW>^2P2)t~%EbpGWSiyn_$Z(44($Gzr04v!_(G@3+TASRJDqUDY3D5Wlv6}& z&Voi#WPV29^rqZ;j^2#qr)bO12rZ*G8zx%~^eM>W{8p%xKP%vQFv?#P#sO{9zRk(B z?=ElNP2<#}Ga$;Bi`GK^qaJ7%@Y5bdR~mBy?((ttX^$=ttLcaL@{VyIEoA0*1XyHK zaXvUmOK~h+`-)TOI#HZva(MG}(xBNtid;SnbG+nD) zy&{DR*wCxT$M{Hk?#Dzh@icRtww0l`&Khagh-eCNjQf%Y7L5q6pj7K$49-#^QXPu2 zU8PhfSWHyDTaJRKU|x!pl*ckN;qyjbigT895mK0mL`4Y{U2SE>Lw(E)n9!7JdQrgM zyy<`$6Z`{Zn$hZ4HPvFYnrSl3Gjoxr7v~|4Lj7xO1|VCtQ;*l8(1l5IP>y>ymkV=v zkWZ#Z}&C+vv;+F^#8IGV|?v1xmK?$roE oNJ8}}raz&Ys-R>#gYx8@y^R2fjKBXYm`TsRGU$@$_KkCY0j8IoJOBUy delta 1691 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 RNJ)u@KRmMU$T1A^{Xe>Na%=zq delta 16896 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 diff --git a/Doc/DAC167_limit_test_results.md b/Doc/DAC167_limit_test_results.md new file mode 100644 index 0000000..1d7c90c --- /dev/null +++ b/Doc/DAC167_limit_test_results.md @@ -0,0 +1,109 @@ +# Проверка ошибки ограничения DAC_stop в 167 + +Дата: 22.09.2026. + +## Результат + +Выполнены 7 тестов, все прошли. Это тесты воспроизведения существующей +ошибки: успешный результат означает, что описанные сценарии подтверждены +расчётной моделью. Код прошивки не изменялся, проверки на устройстве не было. + +Ошибка возникает при **DAC_stop > 20** в момент захвата параметров на +фронте START. В коде сначала вычисляется `dac_stop = DAC_stop - 4`, +поэтому условие `dac_stop > 16` впервые выполняется при `DAC_stop = 21`. +Оно ошибочно присваивает `dac_go = 16`, отменяя ранее применённое +ограничение начального значения до 5. Конечное значение не ограничивается. + +Для неотрицательных значений DAC_stop проверен весь диапазон 0..32767 +и 10 вариантов DAC_go: -100, 0, 4, 6, 8, 9, 10, 20, 100, 32767. +Всего перебрано **327 680 пар**. Отличие от варианта с исправленной +строкой обнаруживается ровно при DAC_stop > 20. + +## Граничные примеры + +`DAC_go` и `DAC_stop` — входные параметры, `dac_go` и `dac_stop` — +внутренние значения после вычитания 4 и проверок. Таблица показывает +именно внутренние значения, а не измеренный ток и не 12-битный код ЦАП. + +| DAC_go | DAC_stop | Сейчас: dac_go → dac_stop | При исправлении опечатки | Проявление | +|---:|---:|---:|---:|---| +| 6 | 16 | 2 → 12 | 2 → 12 | Штатные значения из Default_params(), ошибки нет | +| 6 | 19 | 2 → 15 | 2 → 15 | Ошибки нет | +| 6 | 20 | 2 → 16 | 2 → 16 | Верхняя граница без ошибки | +| 6 | 21 | 16 → 17 | 2 → 16 | Первый входной уровень, вызывающий ошибку | +| 6 | 22 | 16 → 18 | 2 → 16 | Начало поднято до 16, конец не ограничен | +| 6 | 30 | 16 → 26 | 2 → 16 | То же, с большим превышением | +| 4 | 21 | 16 → 17 | 0 → 16 | Начало 0 заменено на 16 | +| 9 | 21 | 16 → 17 | 5 → 16 | Разрешённое начало 5 заменено на 16 | +| 20 | 21 | 16 → 17 | 5 → 16 | Предварительное ограничение начала до 5 отменено | + +Под «исправлением» здесь понимается только замена +`if(dac_stop>16) dac_go=16;` на `if(dac_stop>16) dac_stop=16;`. +Другие правила расчёта не менялись. + +## Что получает Anal_output + +Для DAC_go=6, DAC_stop=21, LOAD_TIME=30 и DAC_FREQ=200: +`time_dac = 6000`. При запуске из состояния с обнулённым count_load +первый расчёт выполняется при count_load=1. + +| Точка | Текущий kod | kod при исправлении | maxx | +|---|---:|---:|---:| +| Первый расчёт | 6000 | 750 | 6000 | +| Конец нарастания | 6375 | 6000 | 6000 | + +Таким образом, вместо начала с 12,5% шкалы интерполяции код сразу +задаёт 100%, а в конце — 106,25%. Это аргументы функции Anal_output, +не непосредственные значения, отправляемые в ЦАП. + +Физический ток определяется калибровкой DAC_min()/DAC_max() и аппаратной +схемой. Формула в Anal_output интерполирует между этими значениями; +направление изменения численного кода ЦАП зависит от калибровки. +В режиме cCalibrDac результат заменяется DAC_cal(), поэтому рассчитанное +нарастание не определяет выход. Последующее `out & 0xFFF` маскирует +число, а не насыщает его на границе диапазона; при выходе за 12 бит +возможен переход через границу. Для утверждений о фактическом токе +нужны реальные калибровочные параметры и измерения. + +## Как сценарий может возникнуть в работе + +1. В регистр modbus[0x51] (DAC_stop) записано 21 или больше; + modbus[0x50] (DAC_go), например, равен 6. +2. После завершения начальной паузы обработчик Load_runner наблюдает + неактивный START, а затем активный: preStart=0 и cUMPstart=1. +3. Параметры захватываются, выполняется ошибочное ограничение и начинается + расчёт с завышенным начальным значением. + +В ReceiveCommandModbus6 есть прямая запись значения регистра, а в приёме +CAN — проверка адреса без ограничения значения DAC_stop до 20. +Поэтому по этим путям код 167 сам не исключает такую настройку. +Ограничения внешнего пульта/ПО и текущие параметры устройства не проверены. + +Если изменить DAC_stop во время непрерывно активного START, локальные +параметры не перечитываются на каждом вызове. Ошибка проявится при +следующем захвате параметров. Стандартные значения DAC_go=6 и DAC_stop=16 +эту ветку не вызывают. + +## Файлы и запуск + +Тест: `tests/test_dac167_limit.py` в проекте 166. Он читает соседний +`167/Source/Internal/DAC.c` и проверяет совпадение моделируемого блока +с исходником, чтобы изменение кода не осталось незамеченным. + +Запуск из корня 166: + +```powershell +py -3 -B -m unittest discover -s tests -p test_dac167_limit.py -v +``` + +Модель проверяет ограничения параметров и выбранные точки формулы. +Это не запуск скомпилированной прошивки и не эмуляция C2000. Перебор +больших значений проверяет только блок ограничения: дальнейшее +преобразование float в 16-битный int может выйти за диапазон и модель +не делает прогнозов для такого выхода. Значения -32768..-32765 исключены +из модели вычитания 4 из signed int из-за переполнения; это проверяется +отдельным тестом. Конкурентные обновления CAN и временные характеристики +прерываний этими тестами не моделируются. + +В прошивке 166 рассматриваемого блока нет. Тест и отчёт сохранены здесь +для анализа 167; изменения DAC.c из 167 в 166 не переносились. diff --git a/Doc/START_STOP_weak_points.md b/Doc/START_STOP_weak_points.md new file mode 100644 index 0000000..88436cf --- /dev/null +++ b/Doc/START_STOP_weak_points.md @@ -0,0 +1,144 @@ +# Слабые места обработки START/STOP + +Дата: 22.09.2026. Проверен код проекта 166 после переноса исправления +из коммита `799e6ba` проекта 167. Документ описывает результаты просмотра +кода; сценарии на устройстве не воспроизводились. Предлагаемые ниже +изменения не внесены. + +## Что исправлено переносом + +В `Source/Internal/main.c:166` команды копируются в локальные +`umpstart` и `umpstop`. Проверка фронта и сохранение предыдущего состояния +используют одну копию каждой команды за проход основного цикла. + +Раньше команда читалась дважды. При обновлении из CAN-прерывания между +проверкой и сохранением состояния переход `0 → 1` мог быть пропущен: +проверка видела 0, а предыдущее состояние уже сохранялось как 1. +Перенос устраняет эту ошибку на уровне алгоритма, но не обеспечивает +накопление событий и не решает остальные вопросы доступа из прерываний. + +## 1. Команда может целиком пройти между опросами + +**Код:** `Source/Internal/main.c:166`, `Source/Internal/ecan.c:211`. + +CAN-приём записывает текущее значение в `modbus`. Основной цикл проверяет +только последнее состояние. Если до следующего опроса придут включение +и выключение команды (`0 → 1 → 0`), цикл увидит 0 и не выдаст импульс. +Локальная копия этот случай не закрывает. + +**Когда существенно:** если отправитель не гарантирует достаточное время +удержания команды или основной цикл задерживается. + +**Возможное исправление:** фиксировать фронт при приёме команды и хранить +событие до обработки. Передача и сброс такого события должны быть +согласованы с прерыванием. Если важен каждый повторный фронт, одного +флага недостаточно: нужен счётчик либо очередь. Альтернатива — протокол +с подтверждением обработки. + +## 2. START и STOP могут быть активны одновременно + +**Код:** `Source/Internal/main.c:169–183`. + +Для START и STOP используются два независимых `if`. Каждый включает +свой выход, не снимая противоположный. Оба выхода сбрасываются только +после обнуления общего `IMPowse`. + +Возможны два сценария: + +- Одновременно обнаружены оба фронта: включаются оба выхода. Значение + `RES_OUT_2` в итоге соответствует STOP, поскольку его обработка идёт второй. +- STOP приходит во время активного START: включается STOP, START остаётся + включённым, общий таймер запускается заново. Импульс START удлиняется. + Аналогичное перекрытие возможно при обратном порядке команд. + +**Возможное исправление:** определить по требованиям СИФУ приоритет +команд, допустимость перекрытия и поведение повторной команды. Затем +реализовать переключение состояний либо отдельные таймеры. Выбирать +приоритет STOP без подтверждённых требований к устройству нельзя. + +## 3. Длительность импульса зависит от основного цикла + +**Код:** `Source/Internal/DAC.c:107`, `Source/Internal/main.c:183`. + +Счётчик `IMPowse` уменьшается в `Load_runner()`, вызываемом из обработчика +таймера (`Source/Internal/measure.c:137`). Однако физические выходы +сбрасываются в основном цикле, когда он обнаруживает нулевой счётчик. + +Если цикл задержался, счётчик уже может быть равен нулю, а выход остаётся +активным. Перед проверкой команд выполняются, в частности, сохранение +и загрузка параметров, расчёт коэффициентов. Время их выполнения в этой +проверке не измерялось. Сброс также расположен внутри условия +`Desk == dsk_LOAD`; если допускается смена режима во время импульса, +нужен явный сброс при выходе из режима. + +**Возможное исправление:** завершать импульс в обработчике таймера либо +аппаратно. При этом запуск и завершение должны быть синхронизированы, +чтобы обработчик не снял только что запущенный импульс. + +## 4. Данные, общие с прерываниями, не имеют volatile + +**Код:** + +- `Source/Internal/message.c:21` — `int modbus[ANSWER_LEN+1]`. +- `Source/Internal/Include/message.h:31` — объявление массива. +- `Source/Internal/Include/package.h:58` — `Modbus` приводится к `WORDE *`. +- `Source/Internal/DAC.c:21` и `Source/Internal/Include/DAC.h:6` — `IMPowse`. + +Команды изменяются в CAN-прерывании и читаются основным циклом. +`IMPowse` записывается основным циклом и уменьшается обработчиком таймера. +Обычные обращения к памяти не описывают компилятору асинхронное изменение +этих объектов. Конкретное проявление зависит от оптимизации и машинного +кода; сбой из-за этого в ходе проверки не подтверждён. + +**Возможное исправление:** согласовать `volatile` в определениях, +объявлениях и указателях доступа. Изменение только массива недостаточно, +если макрос `Modbus` продолжит обращаться через `WORDE *` без `volatile`. +Потребуется проверить и функции, которым передаётся массив. + +`volatile` не делает последовательность операций атомарной и не заменяет +синхронизацию. Для общего счётчика отдельно нужно проверить машинные +инструкции доступа и допустимые точки прерывания. + +## 5. START и STOP не образуют согласованный снимок + +**Код:** `Source/Internal/main.c:166–167`, +`Source/Internal/Include/package.h:134`. + +Обе команды находятся в одном слове `Commands`, но читаются двумя +выражениями. Если CAN обновит слово между ними, `umpstart` может относиться +к старому состоянию, а `umpstop` — к новому. Например, при смене START на +STOP локальные копии могут содержать две единицы, хотя ни одно полученное +слово не содержало их одновременно. Фактическое число чтений памяти +зависит от сгенерированного кода. + +**Возможное исправление:** один раз прочитать всё слово команд через +корректный `volatile`-доступ, затем выделить биты START и STOP из локальной +копии. Это согласует снимок, но не решает потерю коротких событий. + +## Дополнительное замечание только к проекту 167 + +В `../167/Source/Internal/DAC.c:139` обнаружена вероятная опечатка: + +```c +if(dac_stop > 16) dac_go = 16; +``` + +По соседним ограничениям ожидается присваивание `dac_stop = 16`. +Сейчас превышение конечного значения меняет начальное, а следующая +проверка может дополнительно изменить конечное значение. + +Этот участок отсутствует в текущем коде 166 и не перенесён вместе с +`799e6ba`. Он не относится к изменениям рассматриваемого коммита. + +## Порядок дальнейших действий + +1. Согласовать доступ к общим данным и чтение одного слова команд. +2. Определить требования к удержанию команд, повторным фронтам и + одновременным START/STOP. +3. По этим требованиям выбрать фиксацию событий и управление выходами. +4. Проверить на стенде короткую команду, смену START на STOP, оба фронта + одновременно, повторную команду до завершения импульса и задержку + основного цикла. Измерить длительность и перекрытие выходных импульсов. + +Успешная сборка подтверждает собираемость перенесённого исправления, +но не проверяет перечисленные временные сценарии. diff --git a/Source/Internal/main.c b/Source/Internal/main.c index 829e052..45f53d9 100644 --- a/Source/Internal/main.c +++ b/Source/Internal/main.c @@ -40,6 +40,7 @@ void main() int i,j,mask; static unsigned int cancount[2], cancell[2]; static int preCstart=1,preCstop=1; + int umpstart,umpstop; RS_DATA * rs; int presbyt=0; @@ -162,19 +163,22 @@ void main() if(Desk==dsk_LOAD) { - if(cUMPstart & !preCstart) + umpstart = cUMPstart; + umpstop = cUMPreset; + + if(umpstart & !preCstart) { set_START_DPTB(); set_RES_OUT_2(); IMPowse = READY_FREQ; } - preCstart = cUMPstart; + preCstart = umpstart; - if(cUMPreset & !preCstop) + if(umpstop & !preCstop) { set_STOP_DPTB(); clear_RES_OUT_2(); IMPowse = READY_FREQ; } - preCstop = cUMPreset; + preCstop = umpstop; if(!IMPowse) { clear_START_DPTB(); clear_STOP_DPTB(); } } diff --git a/tests/test_dac167_limit.py b/tests/test_dac167_limit.py new file mode 100644 index 0000000..8871d50 --- /dev/null +++ b/tests/test_dac167_limit.py @@ -0,0 +1,105 @@ +"""Host-side reproduction of 167 DAC endpoint logic; no firmware changes. + +Run: py -3 -m unittest discover -s tests -p test_dac167_limit.py -v +The source guard ties the model to the inspected C block. These tests reproduce +the bug (passing means reproduced), not execution on a C2000 or physical DAC. +""" +import re +import unittest +from pathlib import Path + + +SOURCE = Path(__file__).resolve().parents[2] / '167/Source/Internal/DAC.c' + + +def endpoints(start, stop, corrected=False): + # Signed C2000 int subtraction must remain representable. Deliberately + # exclude inputs -32768..-32765, where subtracting 4 overflows signed int. + if not all(-32764 <= value <= 32767 for value in (start, stop)): + raise ValueError('Outside modeled, non-overflowing input range') + go = float(start - 4) + end = float(stop - 4) + if go < 0: + go = 0.0 + if go > 5: + go = 5.0 + if end > 16: + if corrected: + end = 16.0 + else: + go = 16.0 + if end <= go: + end = go + 1 + return go, end + + +def ramp_code(go, end, count, duration=6000): + # Source: LOAD_TIME=30, DAC_FREQ=200. int conversion truncates. + # Only selected exactly representable / far-from-boundary cases tested. + value = (end - go) / 16.0 * count + go / 16.0 * duration + if not -32768 <= value < 32768: + raise ValueError('C2000 int conversion out of range') + return int(value) + + +class Dac167LimitTests(unittest.TestCase): + def test_source_matches_reproduced_block(self): + code = re.sub(r'\s+', '', SOURCE.read_bytes().decode('latin1')) + expected = '''dac_go=DAC_go-4; dac_stop=DAC_stop-4; + if(dac_go<0) dac_go=0; + if(dac_go>5) dac_go=5; + if(dac_stop>16) dac_go=16; + if(dac_stop<=dac_go) dac_stop=dac_go+1;''' + self.assertIn(re.sub(r'\s+', '', expected), code) + + def test_defaults_and_threshold(self): + for stop, actual, fixed in [ + (16, (2, 12), (2, 12)), + (19, (2, 15), (2, 15)), + (20, (2, 16), (2, 16)), + (21, (16, 17), (2, 16)), + (22, (16, 18), (2, 16)), + (30, (16, 26), (2, 16)), + ]: + with self.subTest(stop=stop): + self.assertEqual(endpoints(6, stop), actual) + self.assertEqual(endpoints(6, stop, True), fixed) + + def test_start_limit_is_overwritten(self): + for start in (-100, 0, 4, 6, 9, 10, 20, 100, 32767): + with self.subTest(start=start): + self.assertEqual(endpoints(start, 21), (16, 17)) + self.assertLessEqual(endpoints(start, 21, True)[0], 5) + + def test_full_nonnegative_stop_range(self): + # 327,680 input pairs. Negative stop values do not trigger end > 16. + for start in (-100, 0, 4, 6, 8, 9, 10, 20, 100, 32767): + for stop in range(32768): + actual = endpoints(start, stop) + fixed = endpoints(start, stop, True) + self.assertEqual(actual != fixed, stop > 20, + (start, stop, actual, fixed)) + + def test_first_and_last_ramp_codes(self): + actual = endpoints(6, 21) + fixed = endpoints(6, 21, True) + self.assertEqual(ramp_code(*actual, 1), 6000) + self.assertEqual(ramp_code(*fixed, 1), 750) + self.assertEqual(ramp_code(*actual, 6000), 6375) + self.assertEqual(ramp_code(*fixed, 6000), 6000) + + def test_low_stop_guard_is_unchanged(self): + self.assertEqual(endpoints(6, 4), (2, 3)) + self.assertEqual(endpoints(6, 4), endpoints(6, 4, True)) + self.assertEqual(endpoints(20, 0), (5, 6)) + + def test_model_rejects_signed_subtraction_overflow(self): + for value in range(-32768, -32764): + with self.assertRaises(ValueError): + endpoints(value, 21) + with self.assertRaises(ValueError): + endpoints(6, value) + + +if __name__ == '__main__': + unittest.main(verbosity=2)