From 34a95bc553642026c71e755fe9e209f8637235dd Mon Sep 17 00:00:00 2001 From: Andrey Date: Thu, 1 Oct 2026 17:22:03 +0300 Subject: [PATCH] Fix UMP logger boot initialization and configurable protocol build --- .gitignore | 1 + Doc/UMP_LOGGER.md | 23 +++++++++ F28335.cmd | 3 +- Source/Internal/Include/log_to_mem.h | 1 + Source/Internal/Include/ump_can_protocol.h | 2 +- Source/Internal/log_to_mem.c | 20 ++++++++ Source/Internal/main.c | 1 + Source/Internal/message.c | 2 +- Source/Internal/shared_ump.c | 4 +- build_logger.ps1 | 42 ++++++++++++++++ tests/ump_logger/init_test.c | 56 ++++++++++++++++++++++ tests/ump_logger/recorder_test.c | 1 + tests/ump_logger/run_init_test.py | 35 ++++++++++++++ tests/ump_logger/run_test.py | 13 +++-- 14 files changed, 196 insertions(+), 8 deletions(-) create mode 100644 build_logger.ps1 create mode 100644 tests/ump_logger/init_test.c create mode 100644 tests/ump_logger/run_init_test.py diff --git a/.gitignore b/.gitignore index 0c1bee7..a73b741 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ /Debug/ /UKSSTMS320F28335.CS_/ /Bin/UKSSTMS320F28335_*.bin +/Bin/LoggerFix/ diff --git a/Doc/UMP_LOGGER.md b/Doc/UMP_LOGGER.md index b6e9910..e11f7bc 100644 --- a/Doc/UMP_LOGGER.md +++ b/Doc/UMP_LOGGER.md @@ -168,6 +168,29 @@ CAN-валидация общей библиотеки расширена раз ## Проверка и запуск +Полная сборка исправления: `./build_logger.ps1`. По умолчанию используется +`../newProject/SETGUI/third_party/templates`; другую копию можно указать +параметром `-TemplatesRoot`. В ней нужны `c/set-protocol/src/ump_protocol.c` +и `c/set-protocol/ports/tms320f28335/ump_tms28335.c` (порт УМП v3). +Само наличие папки templates не гарантирует наличие этого порта. +Результат — `Bin/LoggerFix/UKSSTMS320F28335_logger_fix.bin` (SCI8 boot), +рядом OUT и MAP. Старые образы в Bin не заменяются. +Скрипт использует TI C2000 22.6 COFF, оптимизацию размера и секции функций; +таблица `.cinit` размещена в RAML4 вместе с константами, код — в RAML0. +Для сборки из CCS добавьте `Source/Internal/shared_ump.c` в проект и каталоги +`/c/set-protocol`, `/c/set-protocol/ports/tms320f28335` +в include path; используйте параметры оптимизации из скрипта. + +При загрузке `main()` вызывает `ump_log_init()` до включения прерываний. +Это обязательно для TI COFF: стартовый код не обнуляет неинициализированные +переменные `.ebss`. Без явной инициализации состояние, счётчики и номера +банков могут содержать остаточные значения RAM. Очистка `.logg` функцией +`clear_mem()` эти переменные не затрагивает. Повторный запуск записи через +FC06 не вызывает загрузочную инициализацию и сохраняет архив и шкалу времени. +Регрессия `python tests/ump_logger/run_init_test.py` (GCC в PATH) проверяет +запуск с ненулевой RAM и отрицательный контроль без инициализации; +внешняя библиотека `templates/c/set-protocol` для этого теста не нужна. + Изменены исходники прошивки и SetGUI. Готовый образ в Bin не обновлялся. Для платы необходима сборка штатным TI C2000/CCS, проверка размещения .text, .stack, .ebss и .logg по MAP, прошивка и испытание на стенде. diff --git a/F28335.cmd b/F28335.cmd index fffa02b..4912802 100644 --- a/F28335.cmd +++ b/F28335.cmd @@ -128,7 +128,8 @@ SECTIONS { /* Allocate program areas: */ - .cinit : > RAML0 PAGE = 0 + /* Keep initialization tables out of the code RAM block. */ + .cinit : > RAML4 PAGE = 0 .pinit : > RAML0 PAGE = 0 .text : > RAML0 PAGE = 0 codestart : > BEGIN PAGE = 0 diff --git a/Source/Internal/Include/log_to_mem.h b/Source/Internal/Include/log_to_mem.h index f03aac9..383ff49 100644 --- a/Source/Internal/Include/log_to_mem.h +++ b/Source/Internal/Include/log_to_mem.h @@ -42,6 +42,7 @@ void clear_mem(); #define UMP_LOG_LIVE_RECORDS (120U / UMP_LOG_WORDS) #define UMP_LOG_LIVE 0x1100U extern unsigned int ump_dac_ramp, ump_dac_duration, ump_dac_go10, ump_dac_stop10, ump_dac_position; +void ump_log_init(void); void ump_log_tick(void); void ump_log_io(void); void ump_log_can(unsigned int header, unsigned int a, unsigned int b, unsigned int c); diff --git a/Source/Internal/Include/ump_can_protocol.h b/Source/Internal/Include/ump_can_protocol.h index 01e80e5..ef2aaf6 100644 --- a/Source/Internal/Include/ump_can_protocol.h +++ b/Source/Internal/Include/ump_can_protocol.h @@ -2,7 +2,7 @@ #ifndef UMP_CAN_PROTOCOL_H #define UMP_CAN_PROTOCOL_H #include "log_to_mem.h" -#include "../../../../../templates/c/set-protocol/ports/tms320f28335/ump_tms28335.h" +#include "ump_tms28335.h" #define UMP_CAN_MARKER UMP_MARKER #define UMP_CAN_MAX_WORDS UMP_MAX_WORDS #define ump_can_prepare ump_tms28335_prepare diff --git a/Source/Internal/log_to_mem.c b/Source/Internal/log_to_mem.c index 53cd187..ff30687 100644 --- a/Source/Internal/log_to_mem.c +++ b/Source/Internal/log_to_mem.c @@ -51,6 +51,26 @@ extern int CanTimeOutErrorTR; * íå ïåðåñ÷èòûâàåò è íå èçìåíÿåò óïðàâëÿþùèé àëãîðèòì. */ unsigned int ump_dac_ramp, ump_dac_duration, ump_dac_go10, ump_dac_stop10, ump_dac_position; +/* Called once at boot, before enabling interrupts. TI COFF startup does + * not clear uninitialized globals in .ebss. Do not use for record restart: + * restart must preserve the archive, time and latest received voltages. */ +void ump_log_init(void) +{ + unsigned int i; + ump_state = 0; ump_count = 0; ump_cursor = 0; ump_next = 0; + ump_period = 20; ump_divider = 0; + ump_time = 0; ump_sequence = 0; + ump_bank = 0; ump_archive_bank = 1; + ump_archive_count = 0; ump_archive_next = 0; ump_generation = 0; + ump_source = 0; ump_post = 0; ump_post_dropped = 0; + ump_can_header = 0; ump_can_count = 0; + for (i = 0; i < 3; i++) ump_can_data[i] = 0; + for (i = 0; i < 2; i++) ump_pm67_voltage[i] = 0; + ump_prev_commands = 0; ump_prev_inputs = 0; ump_prev_outputs = 0; + ump_dac_ramp = 0; ump_dac_duration = 0; ump_dac_go10 = 0; + ump_dac_stop10 = 0; ump_dac_position = 0; +} + /* Ëîãè÷åñêèå óðîâíè âõîäîâ ïëàòû íàãðóçêè: ôèçè÷åñêèé íîëü îçíà÷àåò 1. * ×òåíèå GPIO íåïîñðåäñòâåííî çäåñü ïîçâîëÿåò çàïèñàòü âõîä äî ñëåäóþùåãî * îáíîâëåíèÿ ðåãèñòðà Inputs â îñíîâíîì öèêëå. */ diff --git a/Source/Internal/main.c b/Source/Internal/main.c index dbcaf66..d51ee0d 100644 --- a/Source/Internal/main.c +++ b/Source/Internal/main.c @@ -82,6 +82,7 @@ void main() unsetup_leds_line(); clear_mem(); + ump_log_init(); set_cntrl_addr(Mode,16); timer_Init(); EnableInterrupts(); diff --git a/Source/Internal/message.c b/Source/Internal/message.c index 7921912..a2947ff 100644 --- a/Source/Internal/message.c +++ b/Source/Internal/message.c @@ -18,7 +18,7 @@ #include "caliber.h" #include "log_to_mem.h" -#include "../../../../templates/c/set-protocol/ports/tms320f28335/ump_tms28335.h" +#include "ump_tms28335.h" int modbus[ANSWER_LEN+1]; unsigned int param[ANSWER_LEN+1]; diff --git a/Source/Internal/shared_ump.c b/Source/Internal/shared_ump.c index 743f05b..3c01a81 100644 --- a/Source/Internal/shared_ump.c +++ b/Source/Internal/shared_ump.c @@ -1,3 +1,3 @@ /* Build the canonical sources directly; do not copy their implementations. */ -#include "../../../../templates/c/set-protocol/src/ump_protocol.c" -#include "../../../../templates/c/set-protocol/ports/tms320f28335/ump_tms28335.c" +#include "src/ump_protocol.c" +#include "ports/tms320f28335/ump_tms28335.c" diff --git a/build_logger.ps1 b/build_logger.ps1 new file mode 100644 index 0000000..bc34efa --- /dev/null +++ b/build_logger.ps1 @@ -0,0 +1,42 @@ +param( + [string]$TemplatesRoot = "$PSScriptRoot/../newProject/SETGUI/third_party/templates", + [string]$CompilerRoot = 'C:/ti/ccs1230/ccs/tools/compiler/ti-cgt-c2000_22.6.0.LTS' +) +$ErrorActionPreference = 'Stop' +$protocol = Join-Path (Resolve-Path -LiteralPath $TemplatesRoot).Path 'c/set-protocol' +foreach ($required in @('src/ump_protocol.c', 'ports/tms320f28335/ump_tms28335.c')) { + if (!(Test-Path -LiteralPath (Join-Path $protocol $required))) { + throw "Missing $required in $protocol. Use templates with the UMP v3 port." + } +} +$output = Join-Path $PSScriptRoot 'Bin/LoggerFix' +$objects = Join-Path $output 'obj' +New-Item -ItemType Directory -Force -Path $objects | Out-Null +$includes = @('Source/Internal/Include', 'Source/External/v120/DSP2833x_headers/include', + 'Source/External/v120/DSP2833x_common/include') | ForEach-Object { "--include_path=$PSScriptRoot/$_" } +$includes += @("--include_path=$CompilerRoot/include", "--include_path=$protocol", + "--include_path=$protocol/ports/tms320f28335") +$flags = @('-v28', '-ml', '-mt', '--float_support=fpu32', '-O3', '--define=LARGE_MODEL', + '--abi=coffabi', '--disable_dp_load_opt', '--opt_for_speed=0', '--gen_func_subsections=on') +$external = @('DSP2833x_ADC_cal.asm', 'DSP2833x_Adc.c', 'DSP2833x_CpuTimers.c', + 'DSP2833x_PieCtrl.c', 'DSP2833x_SWPrioritizedDefaultIsr.c', 'DSP2833x_SWPrioritizedPieVect.c', + 'DSP2833x_SysCtrl.c', 'DSP2833x_Xintf.c', 'DSP2833x_usDelay.asm') +$sources = @(Get-ChildItem -LiteralPath "$PSScriptRoot/Source/Internal" -Filter '*.c' | Sort-Object Name | ForEach-Object FullName) +$sources += $external | ForEach-Object { "$PSScriptRoot/Source/External/v120/DSP2833x_common/source/$_" } +$sources += "$PSScriptRoot/Source/External/v120/DSP2833x_headers/source/DSP2833x_GlobalVariableDefs.c" +$objectFiles = @() +foreach ($source in $sources) { + & "$CompilerRoot/bin/cl2000.exe" @flags @includes --compile_only "--obj_directory=$objects" $source + if ($LASTEXITCODE -ne 0) { throw "Compilation failed: $source" } + $objectFiles += Join-Path $objects ([IO.Path]::GetFileNameWithoutExtension($source) + '.obj') +} +$name = 'UKSSTMS320F28335_logger_fix' +& "$CompilerRoot/bin/cl2000.exe" @flags -z "--map_file=$output/$name.map" --stack_size=0x3f0 --warn_sections ` + "--search_path=$CompilerRoot/lib" --reread_libs --entry_point=_c_int00 --rom_model ` + "--output_file=$output/$name.out" @objectFiles "$PSScriptRoot/F28335.cmd" ` + "$PSScriptRoot/Source/External/v120/DSP2833x_headers/cmd/DSP2833x_Headers_nonBIOS.cmd" ` + "$PSScriptRoot/Libraries/rts2800_fpu32.lib" +if ($LASTEXITCODE -ne 0) { throw 'Link failed' } +& "$CompilerRoot/bin/hex2000.exe" "$output/$name.out" -boot -sci8 -map "$output/${name}_hex.map" -o "$output/$name.bin" -b +if ($LASTEXITCODE -ne 0) { throw 'Binary conversion failed' } +Write-Output "Firmware: $output/$name.bin" diff --git a/tests/ump_logger/init_test.c b/tests/ump_logger/init_test.c new file mode 100644 index 0000000..b3a604b --- /dev/null +++ b/tests/ump_logger/init_test.c @@ -0,0 +1,56 @@ +#include "../../Source/Internal/log_to_mem.c" + +struct mock_gpio GpioDataRegs; +int READY = 1, Desk = 6, Mode = 5; +int modbus[128], CanTimeOutErrorTR; +unsigned long IMPowse, WAKEpowse; +unsigned int ADC_table[4]; +#define CHECK(x) if (!(x)) return __LINE__ + +int main(void) +{ + unsigned int i; + /* Simulate COFF boot with dirty RAM, including a stale active bank. */ + ump_state = ump_count = ump_cursor = ump_next = 0xA5A5; + ump_period = ump_divider = 0xA5A5; + ump_time = ump_sequence = 0xA5A5A5A5UL; + ump_bank = ump_archive_bank = 0xA5A5; + ump_archive_count = ump_archive_next = ump_generation = 0xA5A5; + ump_source = ump_post = ump_post_dropped = 0xA5A5; + ump_can_header = ump_can_count = 0xA5A5; + for (i = 0; i < 3; i++) ump_can_data[i] = 0xA5A5; + for (i = 0; i < 2; i++) ump_pm67_voltage[i] = 0xA5A5; + ump_prev_commands = ump_prev_inputs = ump_prev_outputs = 0xA5A5; + ump_dac_ramp = ump_dac_duration = ump_dac_go10 = 0xA5A5; + ump_dac_stop10 = ump_dac_position = 0xA5A5; + + ump_log_init(); + CHECK(ump_log_read(UMP_LOG_BASE) == 0x554D); + CHECK(ump_log_read(UMP_LOG_BASE+1) == 3); + CHECK(ump_log_read(UMP_LOG_BASE+2) == 0); + CHECK(ump_log_read(UMP_LOG_BASE+3) == 0); + CHECK(ump_log_read(UMP_LOG_BASE+6) == 20); + for (i = 7; i <= 8; i++) CHECK(ump_log_read(UMP_LOG_BASE+i) == 0); + for (i = 12; i <= 15; i++) CHECK(ump_log_read(UMP_LOG_BASE+i) == 0); + CHECK(ump_bank == 0 && ump_archive_bank == 1); + CHECK(ump_cursor == 0 && ump_next == 0 && ump_archive_next == 0); + CHECK(ump_divider == 0 && ump_sequence == 0 && ump_post_dropped == 0); + CHECK(ump_can_header == 0 && ump_can_count == 0); + for (i = 0; i < 3; i++) CHECK(ump_can_data[i] == 0); + for (i = 0; i < 2; i++) CHECK(ump_pm67_voltage[i] == 0); + CHECK(ump_prev_commands == 0 && ump_prev_inputs == 0 && ump_prev_outputs == 0); + CHECK(ump_dac_ramp == 0 && ump_dac_duration == 0 && ump_dac_go10 == 0); + CHECK(ump_dac_stop10 == 0 && ump_dac_position == 0); + CHECK(ump_log_write(UMP_LOG_BASE+1, 20)); + CHECK(ump_log_write(UMP_LOG_BASE, 1)); + CHECK(ump_state == 1 && ump_count == 1); + ump_log_tick(); + CHECK(ump_time == 1); + CHECK(ump_log_write(UMP_LOG_BASE, 0)); + ump_archive_count = 7; ump_generation = 2; + ump_pm67_voltage[0] = 123; + CHECK(ump_log_write(UMP_LOG_BASE, 1)); + CHECK(ump_time == 1 && ump_archive_count == 7 && ump_generation == 2); + CHECK(ump_word(0, 0, 40) == 123); + return 0; +} diff --git a/tests/ump_logger/recorder_test.c b/tests/ump_logger/recorder_test.c index ba5268c..8e1a8b2 100644 --- a/tests/ump_logger/recorder_test.c +++ b/tests/ump_logger/recorder_test.c @@ -14,6 +14,7 @@ __declspec(dllexport) int run_tests(void) unsigned int i, generation, archived, error, length; uint16_t reply[UMP_CAN_MAX_WORDS]; unsigned long sequence; + ump_log_init(); CHECK(!ump_log_write(UMP_LOG_BASE+1, 0)); CHECK(!ump_log_write(UMP_LOG_BASE+1, 1001)); CHECK(ump_log_write(UMP_LOG_BASE+1, 1)); diff --git a/tests/ump_logger/run_init_test.py b/tests/ump_logger/run_init_test.py new file mode 100644 index 0000000..512cc47 --- /dev/null +++ b/tests/ump_logger/run_init_test.py @@ -0,0 +1,35 @@ +"""Boot regression independent of the external shared protocol checkout. + +Run with GCC in PATH: python tests/ump_logger/run_init_test.py +The negative control must fail without the boot initialization call. +""" +import os +from pathlib import Path +import subprocess +import tempfile + +root = Path(__file__).resolve().parents[2] +source = root / 'tests/ump_logger/init_test.c' +compiler = os.environ.get('CC', 'gcc') +with tempfile.TemporaryDirectory(prefix='ump-init-') as directory: + directory = Path(directory) + text = source.read_text().replace( + '../../Source/Internal/log_to_mem.c', + (root / 'Source/Internal/log_to_mem.c').as_posix(), + ) + for initialize in (False, True): + case = directory / 'init_test.c' + case.write_text(text if initialize else text.replace('ump_log_init();', ';')) + executable = directory / 'init_test.exe' + subprocess.run([ + compiler, '-Wno-unknown-pragmas', '-Wno-int-to-pointer-cast', + '-I' + str(root / 'tests/ump_logger/stubs'), + '-I' + str(root / 'Source/Internal/Include'), + str(case), '-o', str(executable), + ], check=True) + result = subprocess.run([str(executable)]) + if initialize: + assert result.returncode == 0, f'Initialization failed: {result.returncode}' + else: + assert result.returncode != 0, 'Negative control unexpectedly passed' + print(f'{"Initialized boot" if initialize else "Dirty RAM negative control"}: PASS') diff --git a/tests/ump_logger/run_test.py b/tests/ump_logger/run_test.py index ffcebe2..601e716 100644 --- a/tests/ump_logger/run_test.py +++ b/tests/ump_logger/run_test.py @@ -1,21 +1,28 @@ """Сборка и запуск исходного регистратора с подменой GPIO/IRQ на Windows. -Нужны Clang и lld-link в PATH. Проверяется алгоритм, но не ABI и тайминги TI. +Нужны GCC либо Clang и lld-link в PATH. TEMPLATES_ROOT задаёт библиотеку. Проверяется алгоритм, но не ABI и тайминги TI. DLL создаётся во временной папке; исходные .out/.hex прошивки не затрагиваются. """ import ctypes import os from pathlib import Path import subprocess +import shutil import tempfile root = Path(__file__).resolve().parents[2] +templates = Path(os.environ.get('TEMPLATES_ROOT', root.parent / 'newProject/SETGUI/third_party/templates')) +protocol = templates / 'c/set-protocol' +compiler = os.environ.get('CC') or shutil.which('clang') or shutil.which('gcc') +if not compiler: + raise SystemExit('Clang or GCC is required in PATH') +link_flags = [] if 'gcc' in Path(compiler).name else ['-nostdlib', '-fuse-ld=lld', '-Wl,/noentry,/nodefaultlib'] with tempfile.TemporaryDirectory(prefix='ump-recorder-') as directory: library = Path(directory) / 'recorder_test.dll' subprocess.run([ - 'clang', '-shared', '-nostdlib', '-fuse-ld=lld', - '-Wl,/noentry,/nodefaultlib', '-Wno-unknown-pragmas', '-Wno-int-to-pointer-cast', + compiler, '-shared', *link_flags, '-Wno-unknown-pragmas', '-Wno-int-to-pointer-cast', '-Itests/ump_logger/stubs', '-ISource/Internal/Include', + '-I' + str(protocol), '-I' + str(protocol / 'ports/tms320f28335'), 'tests/ump_logger/recorder_test.c', 'Source/Internal/shared_ump.c', '-o', str(library), ], cwd=root, check=True) # Отдельный процесс освобождает DLL до удаления временной папки Windows.