Устранить пересечение образа логгера с параметрами EEPROM и проверять размер BIN
This commit is contained in:
2
.gitignore
vendored
2
.gitignore
vendored
@@ -2,3 +2,5 @@
|
||||
/UKSSTMS320F28335.CS_/
|
||||
/Bin/UKSSTMS320F28335_*.bin
|
||||
/Bin/LoggerFix/
|
||||
/Bin/LoggerEepromFix/
|
||||
/Bin/*.pending
|
||||
|
||||
@@ -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`.
|
||||
После перепрошивки требуется проверка на плате; проверка на ПК её не заменяет.
|
||||
|
||||
@@ -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];
|
||||
|
||||
@@ -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%
|
||||
|
||||
@@ -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
36
check_firmware.ps1
Normal 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."
|
||||
55
tests/ump_logger/test_firmware_image.py
Normal file
55
tests/ump_logger/test_firmware_image.py
Normal 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()
|
||||
Reference in New Issue
Block a user