firmwareloop 0.0.8__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- firmwareloop-0.0.8.dist-info/METADATA +268 -0
- firmwareloop-0.0.8.dist-info/RECORD +26 -0
- firmwareloop-0.0.8.dist-info/WHEEL +5 -0
- firmwareloop-0.0.8.dist-info/entry_points.txt +5 -0
- firmwareloop-0.0.8.dist-info/licenses/LICENSE +21 -0
- firmwareloop-0.0.8.dist-info/top_level.txt +1 -0
- tools/__init__.py +5 -0
- tools/acceptance-scenario.ps1 +455 -0
- tools/build.ps1 +208 -0
- tools/can.ps1 +130 -0
- tools/check-qoder-mcp.ps1 +111 -0
- tools/common/build-backends.psm1 +294 -0
- tools/common/fw.psm1 +311 -0
- tools/common/uart_probe.py +100 -0
- tools/doctor.ps1 +148 -0
- tools/flash.ps1 +181 -0
- tools/fw_mcp_server.py +1272 -0
- tools/instrument_cli.py +417 -0
- tools/lib/__init__.py +3 -0
- tools/lib/instruments.py +181 -0
- tools/logic_capture.ps1 +261 -0
- tools/logic_decode.ps1 +307 -0
- tools/reset.ps1 +141 -0
- tools/setup-agent-mcp.ps1 +147 -0
- tools/test-backends.ps1 +100 -0
- tools/test.ps1 +194 -0
tools/flash.ps1
ADDED
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
<#
|
|
2
|
+
.SYNOPSIS
|
|
3
|
+
Flash fallback adapter (Spec §10). Preferred path is Agentic HIL; this
|
|
4
|
+
wrapper exists only for targets Agentic HIL does not support yet.
|
|
5
|
+
|
|
6
|
+
Usage:
|
|
7
|
+
.\tools\flash.ps1 -Backend openocd -Artifact artifacts\build\firmware.elf -Json
|
|
8
|
+
.\tools\flash.ps1 -Backend agentic-hil -Artifact artifacts\build\firmware.elf -Json
|
|
9
|
+
|
|
10
|
+
Hard constraints (Spec §10/§24):
|
|
11
|
+
- artifact must be one of the conventional names under artifacts/build/
|
|
12
|
+
- mass erase / security programming flags are rejected before execution
|
|
13
|
+
- every external process has a timeout
|
|
14
|
+
- returns JSON (firmware-flash-result/v1) and saves the raw log
|
|
15
|
+
|
|
16
|
+
Without a configured backend this exits CONFIG_ERROR - flash is intentionally
|
|
17
|
+
not configured until real hardware integration (Spec §31 step 7).
|
|
18
|
+
#>
|
|
19
|
+
[CmdletBinding()]
|
|
20
|
+
param(
|
|
21
|
+
[Parameter(Mandatory = $true)][ValidateSet('simulator', 'agentic-hil', 'openocd', 'stm32cubeprogrammer', 'esptool', 'jlink', 'vendor')]
|
|
22
|
+
[string]$Backend,
|
|
23
|
+
[string]$Artifact = 'artifacts/build/firmware.elf',
|
|
24
|
+
[switch]$Json,
|
|
25
|
+
[int]$TimeoutMs = 120000
|
|
26
|
+
)
|
|
27
|
+
|
|
28
|
+
Set-StrictMode -Version Latest
|
|
29
|
+
$ErrorActionPreference = 'Stop'
|
|
30
|
+
Import-Module (Join-Path $PSScriptRoot 'common\fw.psm1') -Force
|
|
31
|
+
|
|
32
|
+
$repoRoot = Get-FwRepoRoot
|
|
33
|
+
$artifactPath = if ([System.IO.Path]::IsPathRooted($Artifact)) { $Artifact } else { Join-Path $repoRoot $Artifact }
|
|
34
|
+
|
|
35
|
+
function Exit-WithError {
|
|
36
|
+
param([string]$Class, [string]$Message, [string]$Detail)
|
|
37
|
+
$body = New-FwError -ErrorClass $Class -Message $Message -Detail $Detail
|
|
38
|
+
if ($Json) { Write-FwJson $body -Compact } else { Write-FwJson $body; Write-Error "$Class : $Message" -ErrorAction Continue }
|
|
39
|
+
exit 2
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
# --- artifact guard (Spec §10) -------------------------------------------------
|
|
43
|
+
$allowed = @('firmware.elf', 'firmware.hex', 'firmware.bin')
|
|
44
|
+
$name = Split-Path $artifactPath -Leaf
|
|
45
|
+
if ($name -notin $allowed) {
|
|
46
|
+
Exit-WithError -Class 'ARTIFACT_NOT_FOUND' -Message "artifact name '$name' is not flashable; allowed: $($allowed -join ', ')" -Detail $artifactPath
|
|
47
|
+
}
|
|
48
|
+
if (-not (Test-Path -LiteralPath $artifactPath)) {
|
|
49
|
+
Exit-WithError -Class 'ARTIFACT_NOT_FOUND' -Message 'artifact does not exist; run tools/build.ps1 first.' -Detail $artifactPath
|
|
50
|
+
}
|
|
51
|
+
$full = (Resolve-Path -LiteralPath $artifactPath).Path
|
|
52
|
+
$buildRoot = (Join-Path $repoRoot 'artifacts\build')
|
|
53
|
+
if (-not $full.StartsWith((Resolve-Path -LiteralPath $buildRoot).Path, [System.StringComparison]::OrdinalIgnoreCase)) {
|
|
54
|
+
Exit-WithError -Class 'PERMISSION_DENIED' -Message 'artifact is outside artifacts/build; refusing to flash.' -Detail $full
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
# --- backend dispatch ----------------------------------------------------------
|
|
58
|
+
$stamp = Get-Date -Format 'yyyyMMdd-HHmmss'
|
|
59
|
+
$logFile = Join-Path $repoRoot "artifacts\logs\flash-$stamp.log"
|
|
60
|
+
New-Item -ItemType Directory -Force -Path (Split-Path $logFile) | Out-Null
|
|
61
|
+
|
|
62
|
+
switch ($Backend) {
|
|
63
|
+
'simulator' {
|
|
64
|
+
Save-FwLog -Path $logFile -Content 'simulator backend: flash validated via firmware build + HIL run (no real programmer involved)'
|
|
65
|
+
$result = [ordered]@{
|
|
66
|
+
schema = 'firmware-flash-result/v1'
|
|
67
|
+
ok = $true
|
|
68
|
+
backend = 'simulator'
|
|
69
|
+
artifact = $full
|
|
70
|
+
artifact_sha256 = (Get-FileHash -LiteralPath $full -Algorithm SHA256).Hash.ToLowerInvariant()
|
|
71
|
+
simulated = $true
|
|
72
|
+
hardware_validated = $false
|
|
73
|
+
log = (Resolve-Path -LiteralPath $logFile).Path
|
|
74
|
+
generated_at = Get-FwTimestamp
|
|
75
|
+
}
|
|
76
|
+
if ($Json) { Write-FwJson ([pscustomobject]$result) -Compact } else { Write-FwJson ([pscustomobject]$result) }
|
|
77
|
+
exit 0
|
|
78
|
+
}
|
|
79
|
+
'pyocd' {
|
|
80
|
+
$pyocdExe = Join-Path $repoRoot '.venv\Scripts\pyocd.exe'
|
|
81
|
+
if (-not (Test-Path -LiteralPath $pyocdExe)) {
|
|
82
|
+
$cmd = Get-Command pyocd -ErrorAction SilentlyContinue
|
|
83
|
+
if ($cmd) { $pyocdExe = $cmd.Source }
|
|
84
|
+
}
|
|
85
|
+
if (-not (Test-Path -LiteralPath $pyocdExe)) {
|
|
86
|
+
Exit-WithError -Class 'TOOLCHAIN_NOT_FOUND' -Message 'pyocd 未找到。请在 Python 环境中安装 pyocd (`uv pip install pyocd`)。' -Detail 'pyOCD 用于通过 ST-LINK / CMSIS-DAP 烧录 STM32 芯片。'
|
|
87
|
+
}
|
|
88
|
+
$targetChip = 'stm32f103rc'
|
|
89
|
+
$labYaml = Join-Path $repoRoot 'lab\lab.yaml'
|
|
90
|
+
if (Test-Path -LiteralPath $labYaml) {
|
|
91
|
+
$labContent = Get-Content -LiteralPath $labYaml -Raw
|
|
92
|
+
if ($labContent -match 'target_chip:\s*["'']?([^"''\r\n]+)') {
|
|
93
|
+
$targetChip = $matches[1].Trim()
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
$res = Invoke-FwProcess -FilePath $pyocdExe -Arguments @('flash', '-t', $targetChip.ToLowerInvariant(), $full) -WorkingDirectory $repoRoot -TimeoutMs $TimeoutMs -StdoutFile $logFile
|
|
97
|
+
if ($res.timed_out) {
|
|
98
|
+
Exit-WithError -Class 'TIMEOUT' -Message "pyocd 烧录超时(${TimeoutMs}ms)。" -Detail $logFile
|
|
99
|
+
}
|
|
100
|
+
if ($res.exit_code -ne 0) {
|
|
101
|
+
Exit-WithError -Class 'FLASH_ERROR' -Message "pyocd 烧录失败:未检测到物理探针或目标 MCU 无响应。请确保 ST-LINK / DAPLink 已插入 USB 接口并正确连接 SWD 信号线(SWCLK/SWDIO/GND/3V3)。" -Detail ($res.stdout + "`n" + $res.stderr)
|
|
102
|
+
}
|
|
103
|
+
$result = [ordered]@{
|
|
104
|
+
schema = 'firmware-flash-result/v1'
|
|
105
|
+
ok = $true
|
|
106
|
+
backend = 'pyocd'
|
|
107
|
+
target = $targetChip
|
|
108
|
+
artifact = $full
|
|
109
|
+
artifact_sha256 = (Get-FileHash -LiteralPath $full -Algorithm SHA256).Hash.ToLowerInvariant()
|
|
110
|
+
log = (Resolve-Path -LiteralPath $logFile).Path
|
|
111
|
+
generated_at = Get-FwTimestamp
|
|
112
|
+
}
|
|
113
|
+
if ($Json) { Write-FwJson ([pscustomobject]$result) -Compact } else { Write-FwJson ([pscustomobject]$result) }
|
|
114
|
+
exit 0
|
|
115
|
+
}
|
|
116
|
+
'jlink' {
|
|
117
|
+
$jlinkExe = (Get-Command JLink.exe -ErrorAction SilentlyContinue)?.Source
|
|
118
|
+
if (-not $jlinkExe) {
|
|
119
|
+
$candidates = @(
|
|
120
|
+
"C:\Program Files\SEGGER\JLink\JLink.exe",
|
|
121
|
+
"C:\Program Files (x86)\SEGGER\JLink\JLink.exe"
|
|
122
|
+
)
|
|
123
|
+
foreach ($c in $candidates) {
|
|
124
|
+
if (Test-Path -LiteralPath $c) { $jlinkExe = $c; break }
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
if (-not $jlinkExe) {
|
|
128
|
+
Exit-WithError -Class 'TOOLCHAIN_NOT_FOUND' -Message '未找到 SEGGER JLink.exe。请安装官方 SEGGER J-Link 驱动软件包。' -Detail '下载地址: https://www.segger.com/downloads/jlink/'
|
|
129
|
+
}
|
|
130
|
+
$cmdScript = Join-Path $repoRoot "artifacts\logs\jlink_flash_$stamp.jlink"
|
|
131
|
+
$scriptContent = "r`nh`nloadfile $full`nr`ng`nq`n"
|
|
132
|
+
Set-Content -LiteralPath $cmdScript -Value $scriptContent -Encoding ASCII
|
|
133
|
+
$targetChip = 'STM32F103C8'
|
|
134
|
+
$res = Invoke-FwProcess -FilePath $jlinkExe -Arguments @('-device', $targetChip, '-if', 'SWD', '-speed', '4000', '-autoconnect', '1', '-CommanderScript', $cmdScript) -WorkingDirectory $repoRoot -TimeoutMs $TimeoutMs -StdoutFile $logFile
|
|
135
|
+
if ($res.timed_out) {
|
|
136
|
+
Exit-WithError -Class 'TIMEOUT' -Message "J-Link 烧录超时(${TimeoutMs}ms)。" -Detail $logFile
|
|
137
|
+
}
|
|
138
|
+
if ($res.exit_code -ne 0) {
|
|
139
|
+
Exit-WithError -Class 'FLASH_ERROR' -Message "J-Link 烧录失败:未连接 J-Link 探针或目标板未供电。" -Detail ($res.stdout + "`n" + $res.stderr)
|
|
140
|
+
}
|
|
141
|
+
$result = [ordered]@{
|
|
142
|
+
schema = 'firmware-flash-result/v1'
|
|
143
|
+
ok = $true
|
|
144
|
+
backend = 'jlink'
|
|
145
|
+
target = $targetChip
|
|
146
|
+
artifact = $full
|
|
147
|
+
artifact_sha256 = (Get-FileHash -LiteralPath $full -Algorithm SHA256).Hash.ToLowerInvariant()
|
|
148
|
+
log = (Resolve-Path -LiteralPath $logFile).Path
|
|
149
|
+
generated_at = Get-FwTimestamp
|
|
150
|
+
}
|
|
151
|
+
if ($Json) { Write-FwJson ([pscustomobject]$result) -Compact } else { Write-FwJson ([pscustomobject]$result) }
|
|
152
|
+
exit 0
|
|
153
|
+
}
|
|
154
|
+
'agentic-hil' {
|
|
155
|
+
Exit-WithError -Class 'REAL_HARDWARE_REQUIRED' `
|
|
156
|
+
-Message "Agentic HIL 烧录请直接使用 MCP 工具 flash_firmware,或执行 test-plans/real-smoke.yaml。" `
|
|
157
|
+
-Detail "禁止随意使用伪造的 CLI。"
|
|
158
|
+
}
|
|
159
|
+
default {
|
|
160
|
+
Exit-WithError -Class 'CONFIG_ERROR' -Message "烧录后端 '$Backend' 尚未在本机配置。" -Detail '请在 lab/lab.yaml 中配置有效的烧录器后端(pyocd, jlink, stm32cubeprogrammer 等)。'
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
if ($res.timed_out) {
|
|
165
|
+
Exit-WithError -Class 'FLASH_ERROR' -Message 'flash backend timed out and was killed.' -Detail $logFile
|
|
166
|
+
}
|
|
167
|
+
if ($res.exit_code -ne 0) {
|
|
168
|
+
Exit-WithError -Class 'FLASH_ERROR' -Message 'flash backend reported failure.' -Detail ($res.stdout + $res.stderr)
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
$result = [ordered]@{
|
|
172
|
+
schema = 'firmware-flash-result/v1'
|
|
173
|
+
ok = $true
|
|
174
|
+
backend = $Backend
|
|
175
|
+
artifact = $full
|
|
176
|
+
artifact_sha256 = (Get-FileHash -LiteralPath $full -Algorithm SHA256).Hash.ToLowerInvariant()
|
|
177
|
+
log = (Resolve-Path -LiteralPath $logFile).Path
|
|
178
|
+
generated_at = Get-FwTimestamp
|
|
179
|
+
}
|
|
180
|
+
if ($Json) { Write-FwJson ([pscustomobject]$result) -Compact } else { Write-FwJson ([pscustomobject]$result) }
|
|
181
|
+
exit 0
|