Устранить пересечение образа логгера с параметрами EEPROM и проверять размер BIN

This commit is contained in:
2026-10-01 20:20:00 +03:00
parent e3d43ef279
commit 419d6b17dc
7 changed files with 134 additions and 8 deletions

2
.gitignore vendored
View File

@@ -2,3 +2,5 @@
/UKSSTMS320F28335.CS_/
/Bin/UKSSTMS320F28335_*.bin
/Bin/LoggerFix/
/Bin/LoggerEepromFix/
/Bin/*.pending

View File

@@ -215,3 +215,27 @@ TI. Отдельно выполнена синтаксическая прове
Для сборки v3 нужен templates/main начиная с коммита c0ee7c4
(поддержка CAN-чтения 42/84 слов и порт TMS320F28335).
## Пересечение образа 34a95bc5 с параметрами EEPROM
Образ `UKSSTMS320F28335_34a95bc5.bin` имеет размер 32518 байт.
`Save_params` записывает 129 слов с адреса `0x3FFF-(128+1)`;
драйвер EEPROM умножает адрес на 2. Параметры начинаются с байта 32508.
Последние 10 байт BIN пересекаются с параметрами: два последних адреса
таблицы переходов `ump_log_read` (регистры 0x100E/0x100F) и завершающее
нулевое слово загрузочной таблицы. Сохранение параметров повреждает эту
часть образа для последующей загрузки. Это установленный дефект образа;
причину таймаута на конкретной плате нужно подтвердить повторной проверкой.
В исправлении временный снимок RS перенесён со стека в статический буфер:
обработчики RS вызываются последовательно из main, а перед возвратом
снимок полностью кодируется в собственный буфер UART. CAN имеет свой буфер.
Сборка `build_logger.ps1` использует параметры CCS Debug (-O1, speed=3)
и создаёт отдельный `Bin/LoggerEepromFix/UKSSTMS320F28335_logger_eeprom_fix.bin`.
Проверенный размер — 32494 байта, запас до параметров — 14 байт.
Оба сценария сборки вызывают `check_firmware.ps1` до публикации BIN.
Проверяются размер, формат SCI8, полнота блоков и конец загрузочной таблицы.
При превышении границы остаётся только файл `.pending`; готовый BIN
не публикуется. Проверка: `python tests/ump_logger/test_firmware_image.py`.
После перепрошивки требуется проверка на плате; проверка на ПК её не заменяет.

View File

@@ -200,7 +200,9 @@ static void modbus_exception(RS_DATA *rs_arr, unsigned int function, unsigned in
static int receive_ump(RS_DATA *rs_arr)
{
unsigned int address, value, saved, length, count, error;
uint16_t words[UMP_MAX_WORDS];
/* Main-loop only; encoded into the UART buffer before the next request.
* Keep the 124-word snapshot off the shared interrupt stack. */
static uint16_t words[UMP_MAX_WORDS];
address = (rs_arr->RS_Header[2] << 8) | rs_arr->RS_Header[3];
if (address < UMP_BASE) return 0;
value = (rs_arr->RS_Header[4] << 8) | rs_arr->RS_Header[5];

View File

@@ -10,7 +10,11 @@ if not defined GIT_COMMIT (
)
pushd "%SRC_ROOT%Bin" || exit /b 1
hex2000.exe UKSSTMS320F28335.out -boot -sci8 -map UKSSTMS320F28335.map -o UKSSTMS320F28335_%GIT_COMMIT%.bin -b
hex2000.exe UKSSTMS320F28335.out -boot -sci8 -map UKSSTMS320F28335.map -o UKSSTMS320F28335_%GIT_COMMIT%.bin.pending -b
set "BUILD_RESULT=%errorlevel%"
if not "%BUILD_RESULT%"=="0" goto finish
powershell.exe -NoProfile -ExecutionPolicy Bypass -File "%SRC_ROOT%check_firmware.ps1" -Path "UKSSTMS320F28335_%GIT_COMMIT%.bin.pending" -PublishPath "UKSSTMS320F28335_%GIT_COMMIT%.bin"
set "BUILD_RESULT=%errorlevel%"
:finish
popd
exit /b %BUILD_RESULT%

View File

@@ -1,6 +1,7 @@
param(
[string]$TemplatesRoot = "$PSScriptRoot/../newProject/SETGUI/third_party/templates",
[string]$CompilerRoot = 'C:/ti/ccs1230/ccs/tools/compiler/ti-cgt-c2000_22.6.0.LTS'
[string]$CompilerRoot = 'C:/ti/ccs1230/ccs/tools/compiler/ti-cgt-c2000_22.6.0.LTS',
[string]$OutputDirectory = "$PSScriptRoot/Bin/LoggerEepromFix"
)
$ErrorActionPreference = 'Stop'
$protocol = Join-Path (Resolve-Path -LiteralPath $TemplatesRoot).Path 'c/set-protocol'
@@ -9,15 +10,16 @@ foreach ($required in @('src/ump_protocol.c', 'ports/tms320f28335/ump_tms28335.c
throw "Missing $required in $protocol. Use templates with the UMP v3 port."
}
}
$output = Join-Path $PSScriptRoot 'Bin/LoggerFix'
$output = $OutputDirectory
$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')
# Match the CCS Debug configuration used for 34a95bc5.
$flags = @('-v28', '-ml', '-mt', '--float_support=fpu32', '-O1', '--define=LARGE_MODEL',
'--define=_DEBUG', '--symdebug:dwarf', '--abi=coffabi', '--opt_for_speed=3')
$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')
@@ -30,13 +32,14 @@ foreach ($source in $sources) {
if ($LASTEXITCODE -ne 0) { throw "Compilation failed: $source" }
$objectFiles += Join-Path $objects ([IO.Path]::GetFileNameWithoutExtension($source) + '.obj')
}
$name = 'UKSSTMS320F28335_logger_fix'
$name = 'UKSSTMS320F28335_logger_eeprom_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
& "$CompilerRoot/bin/hex2000.exe" "$output/$name.out" -boot -sci8 -map "$output/${name}_hex.map" -o "$output/$name.bin.pending" -b
if ($LASTEXITCODE -ne 0) { throw 'Binary conversion failed' }
& "$PSScriptRoot/check_firmware.ps1" -Path "$output/$name.bin.pending" -PublishPath "$output/$name.bin"
Write-Output "Firmware: $output/$name.bin"

36
check_firmware.ps1 Normal file
View File

@@ -0,0 +1,36 @@
param(
[Parameter(Mandatory=$true)][string]$Path,
[string]$PublishPath
)
$ErrorActionPreference = 'Stop'
# Load_params/Save_params use word address 0x3FFF-(ANSWER_LEN+1).
# Seeprom_write converts that address to wire bytes (WORD_LEN=2).
$header = Get-Content -Raw -LiteralPath "$PSScriptRoot/Source/Internal/Include/message.h"
if ($header -notmatch '(?m)^#define\s+ANSWER_LEN\s+(0x[0-9a-fA-F]+|[0-9]+)') {
throw 'Cannot determine the EEPROM parameter reservation from ANSWER_LEN'
}
$registerCount = if ($Matches[1].StartsWith('0x')) {
[Convert]::ToInt32($Matches[1].Substring(2), 16)
} else { [int]$Matches[1] }
$limit = (0x3FFF - ($registerCount + 1)) * 2
$bytes = [IO.File]::ReadAllBytes((Resolve-Path -LiteralPath $Path).Path)
if ($bytes.Length -gt $limit) {
throw "Firmware size $($bytes.Length) exceeds EEPROM firmware area $limit bytes; overlaps saved parameters by $($bytes.Length - $limit) bytes."
}
if ($bytes.Length -lt 30 -or [BitConverter]::ToUInt16($bytes, 0) -ne 0x08AA) {
throw 'Expected a C2000 SCI8 boot image'
}
$offset = 22
$blocks = 0
while ($true) {
if ($offset + 2 -gt $bytes.Length) { throw 'Missing boot table terminator' }
$words = [BitConverter]::ToUInt16($bytes, $offset)
$offset += 2
if (!$words) { break }
$offset += 4 + 2 * $words
if ($offset -gt $bytes.Length) { throw 'Truncated boot table block' }
$blocks++
}
if (!$blocks -or $offset -ne $bytes.Length) { throw 'Invalid boot table length' }
if ($PublishPath) { Move-Item -LiteralPath $Path -Destination $PublishPath -Force }
Write-Output "Verified SCI8 image: $($bytes.Length) bytes, $blocks blocks, $($limit - $bytes.Length) bytes before saved parameters."

View File

@@ -0,0 +1,55 @@
"""Exercise the release gate without TI tools or hardware."""
from pathlib import Path
import shutil
import struct
import subprocess
import tempfile
import unittest
ROOT = Path(__file__).resolve().parents[2]
LIMIT = (0x3FFF - (128 + 1)) * 2
def image(size):
header = struct.pack('<H', 0x08AA) + bytes(16) + struct.pack('<HH', 0, 0x8000)
words = (size - 30) // 2
return header + struct.pack('<HHH', words, 0, 0x8000) + bytes(words * 2) + bytes(2)
class FirmwareGateTest(unittest.TestCase):
def check_image(self, data, success):
with tempfile.TemporaryDirectory() as directory:
source = Path(directory) / 'candidate.pending'
output = Path(directory) / 'firmware.bin'
source.write_bytes(data)
result = subprocess.run([
shutil.which('powershell') or 'pwsh', '-NoProfile', '-ExecutionPolicy', 'Bypass',
'-File', str(ROOT / 'check_firmware.ps1'), '-Path', str(source),
'-PublishPath', str(output),
], capture_output=True)
self.assertEqual(result.returncode == 0, success, result.stderr.decode(errors='replace'))
self.assertEqual(output.exists(), success)
if success:
self.assertEqual(output.read_bytes(), data)
self.assertFalse(source.exists())
return result.stdout + result.stderr
def test_exact_boundary_is_accepted(self):
self.check_image(image(LIMIT), True)
def test_34a95bc5_size_is_rejected(self):
diagnostic = self.check_image(image(32518), False)
self.assertIn(b'overlaps saved parameters by 10 bytes', diagnostic)
def test_missing_boot_terminator_is_rejected(self):
self.check_image(image(100)[:-2], False)
def test_partial_block_is_rejected(self):
self.check_image(image(100)[:-10], False)
def test_wrong_format_is_rejected(self):
self.check_image(bytes(100), False)
if __name__ == '__main__':
unittest.main()