from pathlib import Path
import hashlib,json,xml.etree.ElementTree as E,zipfile,shutil
R=Path(__file__).resolve().parent;B=R.parent;H=B/'engineering_handoff_local';sha=lambda p:hashlib.sha256(p.read_bytes()).hexdigest()
N=Path('work/tri-product-continuation-2026-10-09.nosync/hardware_source_localization_v140/early_drc/lamp_safety_root_netlist.xml')
r=E.parse(N).getroot(); nets={n.get('pin'):net.get('name') for net in r.findall('./nets/net') for n in net.findall('node') if n.get('ref')=='U1'}
assert nets['16']=='/WDOG_KICK' and nets['17']=='/THERM_OK'
shutil.copy2(N,R/'lamp_safety_PCA_prior_netlist.xml')
build=json.loads((R/'firmware/固件/lamp_target/build/build_verification.json').read_text())
checks={'status':'A36_LOCAL_BOOT_MAPPING_PASS_FABRICATION_HOLD','physical_verified_requirements':0,'reference_requirements':20,'boot_cases':8,'host_sanitizers':'ASAN_UBSAN_PASS','negative_control':'Original boot rejected by physical pin16 MODER assertion','prior_netlist_sha256':sha(N),'pin_net_match':{'U1.16':nets['16'],'U1.17':nets['17']},'fresh_CAD_checks_run':False,'target_build':build,'A35_IR_policy_in_this_image':False,'IR_GPIO_bound':False,'scope':'PCA9956B lamp_safety branch; active-high PA7 blank. IS31FL3236A branch excluded; different SDB polarity. No hardware flashed or powered.'}
(R/'checks.json').write_text(json.dumps(checks,ensure_ascii=False,indent=2)+'\n')
(R/'run_checks.sh').write_text('''#!/bin/sh
set -eu
HERE=$(CDPATH= cd -- "$(dirname -- "$0")" && pwd)
python3 "$HERE/test_boot.py"
sh "$HERE/firmware/固件/lamp_target/build.sh"
''')
report=f'''# 灯具安全 MCU 物理脚映射修订 A36

状态：本地主机寄存器回归和 Cortex-M0+ 完整编译链接通过；FABRICATION_HOLD。三款整机页面声明的 20 项实测验收仍为 0 项通过。

## 本轮实际改变

旧代码对 PA9 输出看门狗低电平、对 PA10 配置温度输入，但未设置重映射。STM32G030F6P6 TSSOP20 默认物理脚16对应 PA11、脚17对应 PA12。A36 启动时开启 SYSCFG 时钟，先将 PA9/PA11 均预置输出低，将 PA10/PA12 均配置输入下拉，再清除 PA11_RMP/PA12_RMP。看门狗不踢，LED_PWR_REQ 低，PCA 的 LED_OE_N 高，LED_RESET_N 低。

原厂依据：[ST DS12991 Rev6 Table12 footnote4](https://www.st.com/resource/en/datasheet/stm32g030f6.pdf)。配对证据为本地已保存 PCA lamp_safety netlist：U1.16=/WDOG_KICK、U1.17=/THERM_OK，sha256 `{sha(N)}`。这次没有修改 PCB 铜皮，也没有新跑灯具安全板 ERC/DRC；旧 netlist 不代表整板已通过生产审核。

## 验证证据与限制

- 8组实际启动函数测试：GPIO寄存器全0/全1 × 两个重映射位的4种组合。使用厂商寄存器布局，模拟 BSRR 对 ODR 的硬件副作用，检查映射切换前的双方别名状态、最终脚模式、控制输出及无关 CFGR1 位保留。ASan/UBSan 通过。
- 原始启动函数负对照在物理脚16必须为输出的断言失败。寄存器模型不是测得的电压、脉冲或复位波形，不能证明模拟电路安全或任意运行状态下绝无毛刺。
- 完整目标固件编译链接及已有向量/符号/调用路径静态验证通过，FLASH {build['flash_binary_bytes']} B。BIN sha256 `{build['sha256_bin']}`。策略仍未配置、未授权，输出适配器未绑定，没有踢看门狗，没有 LED 点亮。
- 本包只配对 PCA9956B 安全控制分支。PA7 高是该分支的 blank 状态；不能与 IS31FL3236A 的 SDB_N 控制分支混用。
- A35 红外门控仍是独立主机策略候选，本次目标固件未合入；物理脚3/PC15仍 NC。当前不具备红外自动点亮效果，也没有可工作的 AI/LED 输出运行闭环。

## 工程交付和下一步

包内包含可编辑目标源码、完整所需控制源码、ELF/BIN/MAP、测试和日志。运行 `sh run_checks.sh` 重新执行；需要 cc/Python3/arm-none-eabi GCC工具链。本次不增加BOM器件，不作整机成本已达标的结论。下一步先完成红外接口及安全输入采样接入，再完成输出闭环；整机结构、LED光学与温升、实际电机/风机匹配和20项台架验证仍未完成。没有制造或通电测试放行，没有采购或供应商联系。
'''
(R/'README.md').write_text(report);(H/'A36-lamp-physical-pin-mapping.md').write_text(report)
files=[p for p in R.rglob('*') if p.is_file() and p.name not in ('prepare.py','finish.py') and not p.name.startswith('.') and p.name not in ('fixed','original')]
manifest=''.join(f'{sha(p)}  {p.relative_to(R).as_posix()}\n' for p in sorted(files));(H/'A36-SHA256SUMS.txt').write_text(manifest)
z=H/'Lamp-physical-pin-mapping-review-A36.zip'
with zipfile.ZipFile(z,'w',zipfile.ZIP_DEFLATED) as a:
 for p in files:a.write(p,p.relative_to(R).as_posix())
 a.writestr('SHA256SUMS.txt',manifest)
with zipfile.ZipFile(z) as a:
 assert a.testzip() is None
 for line in manifest.splitlines():
  digest,name=line.split('  ',1);assert hashlib.sha256(a.read(name)).hexdigest()==digest
 count=len(a.namelist())
idx=H/'LOCAL-PACKAGES.json';j=json.loads(idx.read_text());j['status']=checks['status'];j['latest_report']='A36-lamp-physical-pin-mapping.md';j['latest_machine_report']='../lamp_pinmap_A36/checks.json';j['latest_manifest']='A36-SHA256SUMS.txt'
j['packages'].append({'archive':z.name,'sha256':sha(z),'bytes':z.stat().st_size,'zip_entries':count,'manifest_entries':len(files),'zip_crc':'PASS','manifest_readback':'PASS','manufacturing_status':'FABRICATION_HOLD'})
j['latest_lamp_safe_firmware_A36']={'source':'../lamp_pinmap_A36/firmware/固件/lamp_target','binary_sha256':build['sha256_bin'],'branch':'PCA9956B lamp_safety','IR_GPIO_binding':'UNIMPLEMENTED','A35_policy_linked':False,'led_output_enabled':False,'physical_test_release':False}
j['lamp_clean_build']={'report':'../lamp_pinmap_A36/firmware/固件/lamp_target/build/build_verification.json','binary_sha256':build['sha256_bin'],'physical_validation':'NOT_RUN_NO_SAMPLE','led_output_enabled':False,'scope':'A36 supersedes old retained A18 hard-off image for PCA branch review only'}
j['manufacturing_exclusions']['lamp_prior_PA9_PA10_boot']='Prior lamp MCU boot lacked PA11/PA12 remapping; A36 defaults PA11/PA12 for PCA branch only. No candidate physically qualified.'
idx.write_text(json.dumps(j,ensure_ascii=False,indent=2)+'\n')
print(json.dumps(j['packages'][-1],indent=2))
