Устранить пересечение образа логгера с параметрами EEPROM и проверять размер BIN
This commit is contained in:
2
.gitignore
vendored
2
.gitignore
vendored
@@ -2,3 +2,5 @@
|
|||||||
/UKSSTMS320F28335.CS_/
|
/UKSSTMS320F28335.CS_/
|
||||||
/Bin/UKSSTMS320F28335_*.bin
|
/Bin/UKSSTMS320F28335_*.bin
|
||||||
/Bin/LoggerFix/
|
/Bin/LoggerFix/
|
||||||
|
/Bin/LoggerEepromFix/
|
||||||
|
/Bin/*.pending
|
||||||
|
|||||||
@@ -215,3 +215,27 @@ TI. Отдельно выполнена синтаксическая прове
|
|||||||
|
|
||||||
Для сборки v3 нужен templates/main начиная с коммита c0ee7c4
|
Для сборки v3 нужен templates/main начиная с коммита c0ee7c4
|
||||||
(поддержка CAN-чтения 42/84 слов и порт TMS320F28335).
|
(поддержка 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)
|
static int receive_ump(RS_DATA *rs_arr)
|
||||||
{
|
{
|
||||||
unsigned int address, value, saved, length, count, error;
|
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];
|
address = (rs_arr->RS_Header[2] << 8) | rs_arr->RS_Header[3];
|
||||||
if (address < UMP_BASE) return 0;
|
if (address < UMP_BASE) return 0;
|
||||||
value = (rs_arr->RS_Header[4] << 8) | rs_arr->RS_Header[5];
|
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
|
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%"
|
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
|
popd
|
||||||
exit /b %BUILD_RESULT%
|
exit /b %BUILD_RESULT%
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
param(
|
param(
|
||||||
[string]$TemplatesRoot = "$PSScriptRoot/../newProject/SETGUI/third_party/templates",
|
[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'
|
$ErrorActionPreference = 'Stop'
|
||||||
$protocol = Join-Path (Resolve-Path -LiteralPath $TemplatesRoot).Path 'c/set-protocol'
|
$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."
|
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'
|
$objects = Join-Path $output 'obj'
|
||||||
New-Item -ItemType Directory -Force -Path $objects | Out-Null
|
New-Item -ItemType Directory -Force -Path $objects | Out-Null
|
||||||
$includes = @('Source/Internal/Include', 'Source/External/v120/DSP2833x_headers/include',
|
$includes = @('Source/Internal/Include', 'Source/External/v120/DSP2833x_headers/include',
|
||||||
'Source/External/v120/DSP2833x_common/include') | ForEach-Object { "--include_path=$PSScriptRoot/$_" }
|
'Source/External/v120/DSP2833x_common/include') | ForEach-Object { "--include_path=$PSScriptRoot/$_" }
|
||||||
$includes += @("--include_path=$CompilerRoot/include", "--include_path=$protocol",
|
$includes += @("--include_path=$CompilerRoot/include", "--include_path=$protocol",
|
||||||
"--include_path=$protocol/ports/tms320f28335")
|
"--include_path=$protocol/ports/tms320f28335")
|
||||||
$flags = @('-v28', '-ml', '-mt', '--float_support=fpu32', '-O3', '--define=LARGE_MODEL',
|
# Match the CCS Debug configuration used for 34a95bc5.
|
||||||
'--abi=coffabi', '--disable_dp_load_opt', '--opt_for_speed=0', '--gen_func_subsections=on')
|
$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',
|
$external = @('DSP2833x_ADC_cal.asm', 'DSP2833x_Adc.c', 'DSP2833x_CpuTimers.c',
|
||||||
'DSP2833x_PieCtrl.c', 'DSP2833x_SWPrioritizedDefaultIsr.c', 'DSP2833x_SWPrioritizedPieVect.c',
|
'DSP2833x_PieCtrl.c', 'DSP2833x_SWPrioritizedDefaultIsr.c', 'DSP2833x_SWPrioritizedPieVect.c',
|
||||||
'DSP2833x_SysCtrl.c', 'DSP2833x_Xintf.c', 'DSP2833x_usDelay.asm')
|
'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" }
|
if ($LASTEXITCODE -ne 0) { throw "Compilation failed: $source" }
|
||||||
$objectFiles += Join-Path $objects ([IO.Path]::GetFileNameWithoutExtension($source) + '.obj')
|
$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 `
|
& "$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 `
|
"--search_path=$CompilerRoot/lib" --reread_libs --entry_point=_c_int00 --rom_model `
|
||||||
"--output_file=$output/$name.out" @objectFiles "$PSScriptRoot/F28335.cmd" `
|
"--output_file=$output/$name.out" @objectFiles "$PSScriptRoot/F28335.cmd" `
|
||||||
"$PSScriptRoot/Source/External/v120/DSP2833x_headers/cmd/DSP2833x_Headers_nonBIOS.cmd" `
|
"$PSScriptRoot/Source/External/v120/DSP2833x_headers/cmd/DSP2833x_Headers_nonBIOS.cmd" `
|
||||||
"$PSScriptRoot/Libraries/rts2800_fpu32.lib"
|
"$PSScriptRoot/Libraries/rts2800_fpu32.lib"
|
||||||
if ($LASTEXITCODE -ne 0) { throw 'Link failed' }
|
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' }
|
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"
|
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