Fix UMP logger boot initialization and configurable protocol build
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -1,3 +1,4 @@
|
||||
/Debug/
|
||||
/UKSSTMS320F28335.CS_/
|
||||
/Bin/UKSSTMS320F28335_*.bin
|
||||
/Bin/LoggerFix/
|
||||
|
||||
@@ -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, прошивка и испытание на стенде.
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 в основном цикле. */
|
||||
|
||||
@@ -82,6 +82,7 @@ void main()
|
||||
unsetup_leds_line();
|
||||
|
||||
clear_mem();
|
||||
ump_log_init();
|
||||
set_cntrl_addr(Mode,16);
|
||||
timer_Init();
|
||||
EnableInterrupts();
|
||||
|
||||
@@ -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];
|
||||
|
||||
@@ -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
42
build_logger.ps1
Normal 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"
|
||||
56
tests/ump_logger/init_test.c
Normal file
56
tests/ump_logger/init_test.c
Normal 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;
|
||||
}
|
||||
@@ -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));
|
||||
|
||||
35
tests/ump_logger/run_init_test.py
Normal file
35
tests/ump_logger/run_init_test.py
Normal 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')
|
||||
@@ -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.
|
||||
|
||||
Reference in New Issue
Block a user