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/
/UKSSTMS320F28335.CS_/
/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 не обновлялся.
Для платы необходима сборка штатным TI C2000/CCS, проверка размещения .text,
.stack, .ebss и .logg по MAP, прошивка и испытание на стенде.

View File

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

View File

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

View File

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

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;
/* 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 в основном цикле. */

View File

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

View File

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

View File

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

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;
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));

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.
Нужны 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.