"""Execute the linked Thumb du_ok with frozen MMIO snapshots, count instructions.
Not a Cortex-M4 cycle simulator. Not real ADC/TIM1/SPI timing evidence.
Requires external unicorn==2.1.4; emulator code is not bundled into firmware.
"""
from pathlib import Path
import subprocess,re,json,hashlib
from unicorn import Uc,UC_ARCH_ARM,UC_MODE_THUMB,UC_MODE_MCLASS,UC_HOOK_CODE
from unicorn.arm_const import UC_ARM_REG_SP,UC_ARM_REG_LR,UC_ARM_REG_R0,UC_CPU_ARM_CORTEX_M4
import unicorn
p=Path(__file__).resolve().parent.parent;t=p/'firmware/固件/drill_target/build'
elf=t/'drill_control_g431cb.elf';binary=t/'drill_control_g431cb.bin'
syms={}
for line in subprocess.check_output(['arm-none-eabi-nm',str(elf)],text=True).splitlines():
 m=re.match(r'([0-9a-fA-F]+)\s+\w\s+(\S+)$',line)
 if m:syms[m[2]]=int(m[1],16)

import struct
rows=[]
for wrap in (False,True):
 for cycles in (0,1,3839,3840,3841):
  for us in (29,30,31):
   u=Uc(UC_ARCH_ARM,UC_MODE_THUMB|UC_MODE_MCLASS);u.ctl_set_cpu_model(UC_CPU_ARM_CORTEX_M4)
   for address,size in [(0x08000000,0x20000),(0x20000000,0x10000),(0x40000000,0x30000),(0x48000000,0x2000),(0x50000000,0x1000),(0xe0000000,0x100000)]:u.mem_map(address,size)
   u.mem_write(0x08000000,binary.read_bytes())
   for name,address in [('FLASH',0x40022000),('PWR',0x40007000),('RCC',0x40021000),('GPIOA',0x48000000),('GPIOB',0x48000400),('GPIOF',0x48001400),('TIM1',0x40012c00),('TIM2',0x40000000),('SPI2',0x40003800),('ADC1',0x50000000),('ADC2',0x50000100),('ADC_COMMON',0x50000300)]:u.mem_write(address,(p/'performance_model'/ (name+'.bin')).read_bytes())
   for name,value in [('owned',1),('sampling_owned',1),('state',1),('drill_config',5),('drill_dual_state',9),('drill_adc_state',0)]:u.mem_write(syms[name],bytes([value]))
   # Model the recorded locked 6x-feedback readbacks, not a physical device.
   u.mem_write(syms['drill_config']+20,bytes([22,8,0,1,0x40,1]))
   u.mem_write(syms['SystemCoreClock'],(128000000).to_bytes(4,'little'))
   origin_cycle=0xfffffff0 if wrap else 1000;origin_us=0xfffffff0 if wrap else 60
   for name,val in [('timing_owned',1),('timing_open',1),('timing_failed',0),('timebase_owned',1)]:u.mem_write(syms[name],bytes([val]))
   for name,val in [('begin_cycles',origin_cycle),('begin_us',origin_us)]:u.mem_write(syms[name],val.to_bytes(4,'little'))
   u.mem_write(syms['drill_debug_timing'],struct.pack('<16I',0x544d3830,1,1,0,0,0,0,0,0,0,0,0,128000000,3840,30,1))
   u.mem_write(0xe0001000,(1).to_bytes(4,'little'));u.mem_write(0xe0001004,((origin_cycle+cycles)&0xffffffff).to_bytes(4,'little'));u.mem_write(0xe000edfc,(1<<24).to_bytes(4,'little'))
   u.mem_write(0xe000e010,(7).to_bytes(4,'little'));u.mem_write(0xe000e014,(127999).to_bytes(4,'little'));u.mem_write(0x40000024,((origin_us+us)&0xffffffff).to_bytes(4,'little'))
   trace=[];u.hook_add(UC_HOOK_CODE,lambda uc,address,size,data:trace.append(address))
   u.reg_write(UC_ARM_REG_SP,0x20005800);u.reg_write(UC_ARM_REG_LR,0x0801fff1);u.reg_write(UC_ARM_REG_R0,0)
   u.emu_start(syms['drill_timing_end']|1,0x0801fff0,count=10000)
   good=0<cycles<=3840 and us<=30;r=u.reg_read(UC_ARM_REG_R0);record=struct.unpack('<16I',u.mem_read(syms['drill_debug_timing'],64))
   assert r==int(good) and record[2]==2
   assert record[11]==(0 if good else (2 if cycles==0 else 3))
   assert record[5]==cycles and record[7]==us and record[15]==(3 if good else 1)
   rows.append({'wrap':wrap,'supplied_cycles':cycles,'supplied_us':us,'accepted':good,'instructions':len(trace),'fault':record[11]})
result={'binary_sha256':hashlib.sha256(binary.read_bytes()).hexdigest(),'status':'FROZEN_ARM_END_BOUNDARY_PASS','cases':len(rows),'rows':rows,'counters_supplied_not_measured':True,'dynamic_counter_model_validated':False,'physical_validation':False,'actual_hardware_capture':False,'30us_service_deadline_qualified':False}
print(json.dumps({k:v for k,v in result.items() if k!='rows'},indent=2));(p/'performance_model/frozen_monitor_boundaries_A83.json').write_text(json.dumps(result,indent=2)+'\n')
