"""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)
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 [('DMA',0x40020000),('DMA_CHANNEL',0x40020008),('MUX',0x40020800),('MUX_STATUS',0x40020880),('SYSTICK',0xe000e010),('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]))
u.mem_write(syms['SystemCoreClock'],(128000000).to_bytes(4,'little'))

from unicorn import UC_HOOK_MEM_WRITE
from unicorn.arm_const import UC_ARM_REG_R1
for name,val in [('ds_owned',1),('timebase_owned',1),('ds_pending',0)]:u.mem_write(syms[name],bytes([val]))
for name,val in [('du_last',60),('du_phase',60),('ds_last',60),('ds_progress',60),('ds_remaining',2),('ds_sequence',0)]:u.mem_write(syms[name],val.to_bytes(4,'little'))
u.mem_write(0x40020010,(0x5000030c).to_bytes(4,'little'))
u.mem_write(0x40020014,syms['ds_ring'].to_bytes(4,'little'))
u.mem_write(0x40000024,(80).to_bytes(4,'little'))
# Model one fully completed slot before the foreground services it.
u.mem_write(0x4002000c,(1).to_bytes(4,'little'))
u.mem_write(0x40020000,(4).to_bytes(4,'little'))
u.mem_write(syms['ds_ring'],(0x04560123).to_bytes(4,'little'))
def w1c(uc,access,address,size,value,data):
 if address==0x40020004:
  flags=int.from_bytes(uc.mem_read(0x40020000,4),'little');uc.mem_write(0x40020000,(flags&~value).to_bytes(4,'little'))
u.hook_add(UC_HOOK_MEM_WRITE,w1c)
trace=[]
def count(uc,address,size,data):trace.append(address)
u.hook_add(UC_HOOK_CODE,count)
def call(name,r0=80,r1=0):
 trace.clear();u.reg_write(UC_ARM_REG_SP,0x20005800);u.reg_write(UC_ARM_REG_LR,0x0801fff1);u.reg_write(UC_ARM_REG_R0,r0);u.reg_write(UC_ARM_REG_R1,r1)
 u.emu_start(syms[name]|1,0x0801fff0,count=50000)
 return {'function':name,'instructions':len(trace),'return_r0':u.reg_read(UC_ARM_REG_R0)}
rows=[]
rows.append(call('drill_timebase_get',0x20004000))
rows.append(call('drill_foreground_service'))
assert rows[-1]['return_r0']==0, 'streaming foreground must remain awake'
assert int.from_bytes(u.mem_read(syms['drill_debug_stream_received'],4),'little')==1, 'one complete sample must be published'
assert int.from_bytes(u.mem_read(syms['drill_debug_stream_epoch'],4),'little')==2, 'publication epoch must be stable and even'
assert u.mem_read(syms['drill_dual_state'],1)==bytes([9])
count_sum=sum(r['instructions'] for r in rows)
r={'binary_sha256':hashlib.sha256(binary.read_bytes()).hexdigest(),'model':'Unicorn 2.1.4, frozen MMIO plus DMA IFCR W1C; one completed pair','core_clock_candidate_Hz':128000000,'calls':rows,'executed_instruction_sum':count_sum,'optimistic_one_instruction_per_cycle_us':count_sum/128,'includes_real_foreground_glue_debug_publication_idle_decision':True,'excludes_FOC':True,'excludes_flash_bus_IRQ_latency':True,'cycle_accurate':False,'physical_validation':False,'30us_service_deadline_qualified':False}
print(json.dumps(r,indent=2));(p/'performance_model/foreground_instruction_receipt_A79.json').write_text(json.dumps(r,indent=2)+'\n')
