from pathlib import Path
import shutil,re,subprocess
ROOT=Path(__file__).resolve().parent;BASE=ROOT.parent;SRC=BASE.parent.parent/'outputs/三款PCBA工程包-v1.39-2026-10-08/固件';DST=ROOT/'firmware/固件'
# Sources are local bytes, not iCloud placeholders; retain originals unchanged.
for n in ['drill_target','drill_driver','drill_charge']:shutil.copytree(SRC/n,DST/n,dirs_exist_ok=True,ignore=shutil.ignore_patterns('build','__pycache__'))
for n in ['CMSIS','include']:shutil.copytree(SRC/'lamp_target'/n,DST/'lamp_target'/n,dirs_exist_ok=True)
T=ROOT/'host_boot_test';T.mkdir(exist_ok=True)
h=(DST/'drill_target/Device/Include/stm32g431xx.h').read_text();h=re.sub(r'^#include "(?:core_cm4|system_stm32g4xx)\.h"[^\n]*','',h,flags=re.M)
(T/'device_registers.h').write_text('#include <stdint.h>\n#define __IO volatile\n#define __I volatile const\n#define __O volatile\n'+h)
(T/'stm32g4xx.h').write_text('''#include "device_registers.h"
#undef RCC
#undef GPIOA
#undef GPIOB
#undef GPIOC
extern RCC_TypeDef host_rcc;
extern GPIO_TypeDef host_gpio_a,host_gpio_b,host_gpio_c;
#define RCC (&host_rcc)
#define GPIOA (&host_gpio_a)
#define GPIOB (&host_gpio_b)
#define GPIOC (&host_gpio_c)
#define __DSB() ((void)0)
#define __WFI() ((void)0)
''')
(T/'test_boot.c').write_text('''#include "stm32g4xx.h"
#include <assert.h>
#include <stdio.h>
#include <string.h>
void drill_safe_boot(void);
RCC_TypeDef host_rcc;
GPIO_TypeDef host_gpio_a,host_gpio_b,host_gpio_c;
static void check(GPIO_TypeDef *p,unsigned pin,unsigned high) {
 unsigned b=1u<<pin;
 assert(((p->MODER>>(2*pin))&3)==1);
 assert((p->OTYPER&b)==0);
 assert(((p->OSPEEDR>>(2*pin))&3)==0);
 assert(((p->PUPDR>>(2*pin))&3)==0);
 assert((p->BSRR&(high?b:b<<16))!=0);
 assert((p->BSRR&(high?b<<16:b))==0);
}
int main(void) {
 for(unsigned prior=0;prior<2;prior++) {
  memset(&host_rcc,0,sizeof host_rcc);
  memset(&host_gpio_a,prior?255:0,sizeof host_gpio_a);
  memset(&host_gpio_b,prior?255:0,sizeof host_gpio_b);
  memset(&host_gpio_c,prior?255:0,sizeof host_gpio_c);
  unsigned a=host_gpio_a.MODER,b=host_gpio_b.MODER,c=host_gpio_c.MODER;
  drill_safe_boot();
  /* Independent expected physical pins from netlist and DS12589 LQFP48. */
  check(&host_gpio_a,5,0); /* U1pin13 RUN request to connector10 */
  check(&host_gpio_c,14,0); /* U1pin3 sleep to connector9 */
  check(&host_gpio_a,8,0);check(&host_gpio_a,9,0);check(&host_gpio_a,10,0);
  check(&host_gpio_a,7,0);check(&host_gpio_b,0,0);check(&host_gpio_b,1,0);
  check(&host_gpio_a,12,1); /* U1pin34 charger enable_N default disabled */
  assert((host_rcc.AHB2ENR&(RCC_AHB2ENR_GPIOAEN|RCC_AHB2ENR_GPIOBEN|RCC_AHB2ENR_GPIOCEN))==(RCC_AHB2ENR_GPIOAEN|RCC_AHB2ENR_GPIOBEN|RCC_AHB2ENR_GPIOCEN));
  unsigned am=(3u<<10)|(3u<<14)|(3u<<16)|(3u<<18)|(3u<<20)|(3u<<24);
  unsigned bm=3u|(3u<<2),cm=3u<<28;
  assert((host_gpio_a.MODER&~am)==(a&~am));assert((host_gpio_b.MODER&~bm)==(b&~bm));assert((host_gpio_c.MODER&~cm)==(c&~cm));
 }
 puts("PASS: actual drill_safe_boot writes physical RUN/sleep/PWM-off and charger-disable pins from both prior register states; unrelated GPIO modes preserved. No physical voltages measured.");
}
''')
cmd=['cc','-std=c11','-Wall','-Wextra','-Werror','-I'+str(T),'-I'+str(DST/'drill_target/src')]
source=(DST/'drill_target/src/main.c').read_text();source=source[:source.index('int main(void)')];(T/'target_boot_translation_unit.c').write_text(source)
subprocess.run(cmd+['-c',str(T/'target_boot_translation_unit.c'),'-o',str(T/'target_main.o')],check=True)
subprocess.run(cmd+[str(T/'test_boot.c'),str(DST/'drill_target/src/drill_safe_gpio.c'),str(T/'target_main.o'),'-o',str(T/'test_boot')],check=True)
p=subprocess.run([str(T/'test_boot')],capture_output=True,text=True,check=True);(ROOT/'boot_register_test.log').write_text(p.stdout);print(p.stdout)
s=(BASE/'cleanbuild_drill_target_r1.sh').read_text();s=re.sub(r"ROOT='[^']+'",'ROOT=$(CDPATH= cd -- "$(dirname -- "$0")/firmware" && pwd)',s,count=1);s=re.sub(r"OUT='[^']+'",'OUT="$TARGET/build"',s,count=1);(ROOT/'build_target.sh').write_text(s)
