Fix UMP logger boot initialization and configurable protocol build

This commit is contained in:
2026-10-01 17:22:03 +03:00
parent 1ce42a1877
commit 34a95bc553
14 changed files with 196 additions and 8 deletions

1
.gitignore vendored
View File

@@ -1,3 +1,4 @@
/Debug/ /Debug/
/UKSSTMS320F28335.CS_/ /UKSSTMS320F28335.CS_/
/Bin/UKSSTMS320F28335_*.bin /Bin/UKSSTMS320F28335_*.bin
/Bin/LoggerFix/

View File

@@ -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` в проект и каталоги
`<templates>/c/set-protocol`, `<templates>/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 не обновлялся. Изменены исходники прошивки и SetGUI. Готовый образ в Bin не обновлялся.
Для платы необходима сборка штатным TI C2000/CCS, проверка размещения .text, Для платы необходима сборка штатным TI C2000/CCS, проверка размещения .text,
.stack, .ebss и .logg по MAP, прошивка и испытание на стенде. .stack, .ebss и .logg по MAP, прошивка и испытание на стенде.

View File

@@ -128,7 +128,8 @@ SECTIONS
{ {
/* Allocate program areas: */ /* Allocate program areas: */
.cinit : > RAML0 PAGE = 0 /* Keep initialization tables out of the code RAM block. */
.cinit : > RAML4 PAGE = 0
.pinit : > RAML0 PAGE = 0 .pinit : > RAML0 PAGE = 0
.text : > RAML0 PAGE = 0 .text : > RAML0 PAGE = 0
codestart : > BEGIN PAGE = 0 codestart : > BEGIN PAGE = 0

View File

@@ -42,6 +42,7 @@ void clear_mem();
#define UMP_LOG_LIVE_RECORDS (120U / UMP_LOG_WORDS) #define UMP_LOG_LIVE_RECORDS (120U / UMP_LOG_WORDS)
#define UMP_LOG_LIVE 0x1100U #define UMP_LOG_LIVE 0x1100U
extern unsigned int ump_dac_ramp, ump_dac_duration, ump_dac_go10, ump_dac_stop10, ump_dac_position; 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_tick(void);
void ump_log_io(void); void ump_log_io(void);
void ump_log_can(unsigned int header, unsigned int a, unsigned int b, unsigned int c); void ump_log_can(unsigned int header, unsigned int a, unsigned int b, unsigned int c);

View File

@@ -2,7 +2,7 @@
#ifndef UMP_CAN_PROTOCOL_H #ifndef UMP_CAN_PROTOCOL_H
#define UMP_CAN_PROTOCOL_H #define UMP_CAN_PROTOCOL_H
#include "log_to_mem.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_MARKER UMP_MARKER
#define UMP_CAN_MAX_WORDS UMP_MAX_WORDS #define UMP_CAN_MAX_WORDS UMP_MAX_WORDS
#define ump_can_prepare ump_tms28335_prepare #define ump_can_prepare ump_tms28335_prepare

View File

@@ -51,6 +51,26 @@ extern int CanTimeOutErrorTR;
* не пересчитывает и не изменяет управляющий алгоритм. */ * не пересчитывает и не изменяет управляющий алгоритм. */
unsigned int ump_dac_ramp, ump_dac_duration, ump_dac_go10, ump_dac_stop10, ump_dac_position; 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. /* Логические уровни входов платы нагрузки: физический ноль означает 1.
* Чтение GPIO непосредственно здесь позволяет записать вход до следующего * Чтение GPIO непосредственно здесь позволяет записать вход до следующего
* обновления регистра Inputs в основном цикле. */ * обновления регистра Inputs в основном цикле. */

View File

@@ -82,6 +82,7 @@ void main()
unsetup_leds_line(); unsetup_leds_line();
clear_mem(); clear_mem();
ump_log_init();
set_cntrl_addr(Mode,16); set_cntrl_addr(Mode,16);
timer_Init(); timer_Init();
EnableInterrupts(); EnableInterrupts();

View File

@@ -18,7 +18,7 @@
#include "caliber.h" #include "caliber.h"
#include "log_to_mem.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]; int modbus[ANSWER_LEN+1];
unsigned int param[ANSWER_LEN+1]; unsigned int param[ANSWER_LEN+1];

View File

@@ -1,3 +1,3 @@
/* Build the canonical sources directly; do not copy their implementations. */ /* Build the canonical sources directly; do not copy their implementations. */
#include "../../../../templates/c/set-protocol/src/ump_protocol.c" #include "src/ump_protocol.c"
#include "../../../../templates/c/set-protocol/ports/tms320f28335/ump_tms28335.c" #include "ports/tms320f28335/ump_tms28335.c"

42
build_logger.ps1 Normal file
View File

@@ -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"

View File

@@ -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;
}

View File

@@ -14,6 +14,7 @@ __declspec(dllexport) int run_tests(void)
unsigned int i, generation, archived, error, length; unsigned int i, generation, archived, error, length;
uint16_t reply[UMP_CAN_MAX_WORDS]; uint16_t reply[UMP_CAN_MAX_WORDS];
unsigned long sequence; unsigned long sequence;
ump_log_init();
CHECK(!ump_log_write(UMP_LOG_BASE+1, 0)); 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, 1001));
CHECK(ump_log_write(UMP_LOG_BASE+1, 1)); CHECK(ump_log_write(UMP_LOG_BASE+1, 1));

View File

@@ -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')

View File

@@ -1,21 +1,28 @@
"""Сборка и запуск исходного регистратора с подменой GPIO/IRQ на Windows. """Сборка и запуск исходного регистратора с подменой GPIO/IRQ на Windows.
Нужны Clang и lld-link в PATH. Проверяется алгоритм, но не ABI и тайминги TI. Нужны GCC либо Clang и lld-link в PATH. TEMPLATES_ROOT задаёт библиотеку. Проверяется алгоритм, но не ABI и тайминги TI.
DLL создаётся во временной папке; исходные .out/.hex прошивки не затрагиваются. DLL создаётся во временной папке; исходные .out/.hex прошивки не затрагиваются.
""" """
import ctypes import ctypes
import os import os
from pathlib import Path from pathlib import Path
import subprocess import subprocess
import shutil
import tempfile import tempfile
root = Path(__file__).resolve().parents[2] 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: with tempfile.TemporaryDirectory(prefix='ump-recorder-') as directory:
library = Path(directory) / 'recorder_test.dll' library = Path(directory) / 'recorder_test.dll'
subprocess.run([ subprocess.run([
'clang', '-shared', '-nostdlib', '-fuse-ld=lld', compiler, '-shared', *link_flags, '-Wno-unknown-pragmas', '-Wno-int-to-pointer-cast',
'-Wl,/noentry,/nodefaultlib', '-Wno-unknown-pragmas', '-Wno-int-to-pointer-cast',
'-Itests/ump_logger/stubs', '-ISource/Internal/Include', '-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), 'tests/ump_logger/recorder_test.c', 'Source/Internal/shared_ump.c', '-o', str(library),
], cwd=root, check=True) ], cwd=root, check=True)
# Отдельный процесс освобождает DLL до удаления временной папки Windows. # Отдельный процесс освобождает DLL до удаления временной папки Windows.