杰理芯片key文件本质:不是加密密钥,而是芯片级身份契约
发布时间:2026/9/28 2:05:23 作者:尧图编辑部 阅读量:1,286

1. Key文件不是加密密钥而是杰理芯片的“身份契约”很多人第一次接触杰理蓝牙芯片开发时看到工程里那个叫key.bin或keyfile.bin的文件第一反应是“这应该是AES密钥吧是不是用来加密通信的”——这个理解错得非常典型而且会直接导致后续所有调试陷入死循环。我当年在AC701N项目上踩过这个坑把key文件当成通信密钥去改结果烧录后设备连蓝牙广播都发不出来反复重刷固件、换烧录器、查硬件电路折腾三天才发现问题根本不在这儿。杰理芯片的key文件本质是一份芯片级身份契约Identity Contract它不参与任何运行时加解密运算也不影响BLE协议栈的数据传输过程。它的唯一作用是在芯片上电初始化阶段由ROM Bootloader对固件镜像进行完整性校验与授权绑定。你可以把它想象成一张“芯片身份证准考证”的合体身份证确认“你是谁”芯片型号、SN码准考证确认“你被允许运行哪套程序”固件签名、版本约束、功能开关。没有这张契约杰理芯片根本不会执行你的main函数连串口打印都不会有。这个机制背后是杰理SDK中一个被严重低估的核心模块——rom_check_keyfile()。它在Bootloader阶段被调用流程极其严格从Flash指定地址通常是0x0000_0000或0x0008_0000取决于芯片型号读取固件头解析固件头中的key_offset和key_size字段定位key文件在固件镜像中的嵌入位置使用内置SHA256引擎计算固件主体不含key段的哈希值将该哈希值与key文件中预置的expected_hash字段比对若一致再校验key文件中的chip_id是否匹配当前芯片的UID通过EFUSE读取全部通过才跳转到用户代码入口。提示AC707芯片的key文件结构与AC701N存在关键差异——AC707强制要求chip_id字段必须为0x70700000而AC701N允许0x70100000或0x70110000。若用AC701N的key文件烧录AC707即使哈希正确也会因chip_id不匹配直接halt。为什么杰理要设计这么一套机制根本原因在于其SDK的“半封闭生态”。杰理不提供完整的ARM Cortex-M0内核调试接口也不开放ROM Bootloader源码但又需要确保OEM厂商无法随意篡改固件功能比如绕过语音识别授权、启用未付费的OTA升级模块。Key文件就是这套商业逻辑的技术锚点——它把功能开关、版本控制、硬件绑定全部固化在二进制层面且校验发生在CPU执行任何用户代码之前。我见过太多团队误以为“只要编译通过就能烧录”结果在量产测试阶段发现同一份固件在A厂的AC701N板子上正常在B厂同型号芯片上却卡在Bootloader。最后排查发现B厂芯片的EFUSE中UID被意外写入了错误值而key文件里的chip_id校验失败。这种问题根本不会报错信息只会静默halt连JTAG都无法介入——因为CPU压根没跑起来。所以理解key文件的第一步就是彻底抛弃“密钥加密”的思维定式。它不是密码学意义上的密钥而是一份由杰理工具链生成、由Bootloader强制执行的芯片级合约。接下来的所有操作——生成、修改、调试——都必须围绕这个核心认知展开。2. 杰理2.5编译器如何自动生成key文件隐藏在makefile深处的契约签署流程杰理官方提供的AC701N_SDK_v2.5.0即所谓“杰理2.5编译器”中key文件的生成并非手动操作而是深度集成在编译流程的末端。很多开发者只关注make flash命令却不知道在make执行的最后一步一个叫gen_keyfile.exe的工具早已悄然完成了契约签署。这个过程藏在SDK根目录下的makefile第387行附近被一行看似普通的$(CC) -o $(TARGET).elf $(OBJS)掩盖着。实际流程远比表面复杂当make完成.elf链接后编译系统会触发一个隐式规则——%.bin: %.elf。这个规则内部调用了arm-none-eabi-objcopy将elf转为bin紧接着执行$(SDK_PATH)/tools/gen_keyfile.exe。而gen_keyfile.exe的参数全部来自project.mk中定义的宏KEYFILE_CHIP_ID : 0x70100000 KEYFILE_VERSION : 0x00010000 KEYFILE_FEATURES : 0x0000000F # bit0: BLE, bit1: UART, bit2: ADC, bit3: I2C KEYFILE_SIGN_KEY : $(SDK_PATH)/keys/priv_key.pem这里的关键陷阱在于KEYFILE_SIGN_KEY指向的私钥文件并非用于加密而是用于生成数字签名。gen_keyfile.exe会做三件事构造一个结构体填入chip_id、version、features、reserved等字段计算整个固件bin不含key段的SHA256哈希并存入结构体的expected_hash字段使用priv_key.pem对这个结构体进行RSA-SHA256签名将签名结果存入signature字段。最终生成的key文件是一个固定256字节的二进制块其内存布局如下以AC701N为例偏移字段名长度说明0x00magic4字节固定值0x4B455931KEY1 ASCII0x04chip_id4字节必须与目标芯片UID高位匹配0x08version4字节主版本号16 | 子版本号0x0Cfeatures4字节功能位图bit0BLE使能bit1UART使能等0x10expected_hash32字节固件主体SHA256哈希值0x30signature256字节RSA-PKCS#1 v1.5签名使用2048位私钥注意AC707的key文件结构略有不同——magic变为0x4B455932KEY2且signature字段压缩为128字节使用ECDSA签名。若强行将AC701N的key文件用于AC707Bootloader会在解析magic时直接abort。我曾经为了快速验证一个新功能手动修改了project.mk中的KEYFILE_FEATURES把bit2ADC使能设为1但忘记重新编译整个工程。结果烧录后ADC初始化失败调试发现gen_keyfile.exe读取的是旧的bin文件哈希而新固件的哈希已变导致expected_hash校验失败。这个错误不会报错只会让ADC驱动读取到全0数据——因为Bootloader校验失败后虽然仍会跳转到main但部分外设寄存器被强制复位。更隐蔽的问题来自KEYFILE_SIGN_KEY。杰理SDK自带的priv_key.pem是测试密钥其公钥已硬编码在ROM Bootloader中。但量产时必须替换为OEM自己的私钥否则固件可被任意逆向者伪造。替换方法不是简单覆盖文件而是需运行$(SDK_PATH)/tools/keytool.exe -import_privkey your_key.pem该工具会重新生成pub_key.bin并更新Bootloader校验逻辑。我见过某音频方案商因跳过这步导致其固件被竞争对手批量克隆——对方用IDA Pro反编译出测试公钥直接伪造key文件。所以当你执行make flash时真正发生的是编译器先生成固件bin再由gen_keyfile.exe基于当前配置签署契约最后将key文件追加到bin末尾。这个流程不可跳过也不能手动干预——除非你完全理解每个字段的语义及校验逻辑。3. AC701N与AC707 key文件的兼容性雷区芯片ID、签名算法与烧录地址的三重校验杰理AC701N和AC707虽同属AC系列但在key文件机制上存在三处致命差异直接决定了固件能否在目标芯片上启动。很多团队在方案选型阶段未做充分验证等到量产才发现AC701N的固件无法在AC707上运行或反之。这不是简单的“换芯片重编译”问题而是底层校验逻辑的硬性隔离。第一重雷区Chip ID字段的硬编码校验。AC701N的ROM Bootloader要求key文件中chip_id字段必须等于0x70100000或0x70110000对应不同批次而AC707则强制要求0x70700000。这个值并非芯片UID而是杰理分配给该型号的“家族ID”。有趣的是AC707的UID实际值可能是0x707Axxxx但Bootloader只校验高16位。若用AC701N的key文件烧录AC707Bootloader在解析完magic后读取chip_id发现是0x70100000立即halt连串口都不初始化。这个错误没有任何日志输出只能通过逻辑分析仪抓取Bootloader的Flash读取序列来定位。第二重雷区签名算法的代际升级。AC701N使用RSA-2048签名signature字段256字节而AC707升级为ECDSA-secp256r1signature字段128字节。这意味着AC707的Bootloader无法解析AC701N的RSA签名会认为key文件损坏AC701N的Bootloader尝试验证AC707的ECDSA签名时因算法不支持直接跳过校验但此时expected_hash校验仍会失败因key文件结构不同导致固件哈希计算范围错位。我曾用逻辑分析仪抓取过两者的Bootloader行为AC701N在遇到ECDSA签名时会连续读取Flash 128字节后停止而AC707遇到RSA签名则会多读128字节导致后续固件数据错位expected_hash计算完全错误。第三重雷区key文件在固件中的嵌入地址不同。AC701N默认将key文件追加在固件bin末尾Bootloader从bin末尾向前搜索magic而AC707要求key文件必须位于固件bin的固定偏移0x0007_F000处距离Flash起始地址256KB。这个差异源于两者Flash映射的不同AC701N的Bootloader预留区较小而AC707为支持更大ROM增加了预留空间。若将AC701N的固件含末尾key烧录到AC707Bootloader在0x0007_F000处读到的是固件代码而非magic直接判定key文件不存在。这三重雷区叠加导致跨型号兼容几乎不可能。但实际项目中常有折中方案某TWS耳机方案商同时使用AC701N主控和AC707副耳他们采用“双key文件”策略——在固件bin中嵌入两个key文件分别位于末尾和0x0007_F000并通过Bootloader的芯片ID自动选择校验路径。实现方式是在gen_keyfile.exe源码中增加分支逻辑但这需要杰理提供SDK源码权限普通OEM无法获取。实测经验AC707的0x0007_F000地址并非绝对安全。某次固件升级中我们因代码体积增长导致固件bin超过256KB0x0007_F000被覆盖。Bootloader读取到乱码magic误判为key文件损坏回退到出厂固件。解决方案是调整链接脚本强制将.text段起始地址设为0x0000_0000.rodata段起始设为0x0004_0000为key文件预留干净空间。因此在项目立项阶段必须明确AC701N与AC707的key文件机制是两条平行线不存在向下或向上兼容。任何试图“一固件通吃”的想法都会在量产阶段付出巨大代价。正确的做法是建立独立的编译流水线为每种芯片维护专属的project.mk配置和key生成脚本。4. Sniff模式断连的真相key文件中features字段与低功耗模块的隐式绑定“杰理sniff会断连”是杰理开发圈最常被问及的问题之一百度指数常年居高不下。绝大多数人归咎于蓝牙协议栈参数设置不当或天线设计缺陷却极少有人意识到——问题根源可能就藏在key文件的features字段里。这个4字节的位图不仅控制功能开关还隐式绑定了低功耗模块的使能状态。AC701N SDK中features字段的bit40x00000010被定义为“Sniff Mode Enable”。但关键在于该位必须在key文件生成时置1且Bootloader在校验通过后才会初始化低功耗定时器和睡眠唤醒电路。如果features中bit4为0即使你在代码中调用ble_gap_set_sniff_mode()底层驱动也会静默忽略——因为硬件资源根本未被释放。我曾接手一个已量产的蓝牙音箱项目客户反馈“连接10分钟后必断连”。现象很典型手机端显示“设备已断开”但音箱LED仍亮串口无任何日志。用nRF Connect抓包发现断连前设备突然停止发送LL_CONNECTION_PARAM_REQ且不再响应手机的ping请求。起初怀疑是RSSI阈值设置问题调整conn_interval_min/max后无效又怀疑是电源噪声加装LDO后依旧。最终通过对比新旧固件的key文件十六进制发现旧固件features0x0000001Fbit0~bit4全开新固件features0x0000000Fbit4为0。原因是新版本project.mk中遗漏了KEYFILE_FEATURES 0x00000010。Bootloader校验通过后未初始化sniff所需的32.768kHz晶振和RTC模块导致连接超时后无法进入sniff状态连接管理器强制断连。更隐蔽的是AC707的处理逻辑其features字段bit50x00000020控制“Deep Sleep with BLE Wakeup”。若该位为0设备在sniff状态下一旦进入deep sleep将无法被BLE事件唤醒表现为“假死”——手机扫描不到但设备本身未断电。这种问题比直接断连更难排查因为示波器上看VDD电流确实在下降但逻辑分析仪显示BLE PHY层完全静默。踩坑实录某智能手环项目为降低功耗将features设为0x00000020仅开启deep sleep关闭了BLE功能位bit00。结果烧录后设备无法广播连HCI指令都无法响应。原因在于AC707的Bootloader规定若bit0BLE Enable为0则整个BLE协议栈内存区域被标记为不可访问即使后续代码尝试初始化也会触发HardFault。因此“sniff断连”问题的排查路径必须前置到key文件分析用xxd查看固件bin末尾256字节确认magic存在检查偏移0x04处的chip_id是否匹配目标芯片查看偏移0x0C处的features字段确认bit4AC701N或bit5AC707是否置1若需动态调试可临时修改features字段如将0x0000000F改为0x0000001F用dd命令追加到bin末尾再烧录验证。这个案例揭示了一个重要事实杰理的key文件不仅是启动校验凭证更是硬件资源的“闸门控制器”。它把软件功能与硬件模块的使能深度耦合使得传统嵌入式开发中“软件配置决定硬件行为”的范式在杰理平台上变成了“key文件决定软件能否配置硬件”。5. 杰理SDK开发入门的致命盲区key文件与code签名工具链的协同失效杰理SDK开发入门教程如《杰理AC701N SDK开发入门》PDF通常从“点亮LED”开始却极少提及key文件与code签名工具链的协同关系。这导致大量新手在完成第一个demo后陷入“为什么我的代码烧不进去”的困境。问题不在于代码而在于杰理工具链中一个被刻意弱化的环节——code_sign_tool.exe。这个工具的真实作用是为固件bin生成符合杰理Bootloader要求的头部结构其中最关键的是key_offset和key_size字段。很多开发者直接用arm-none-eabi-objcopy生成bin然后用杰理烧录工具如AC701N_Burner.exe烧录结果失败。原因在于裸bin文件没有头部Bootloader无法定位key文件位置。code_sign_tool.exe的工作流程如下读取原始bin文件在bin开头插入16字节头部格式为0x00-0x03: magic0x42494E31(BIN1)0x04-0x07:key_offsetkey文件起始偏移0x08-0x0B:key_sizekey文件长度固定2560x0C-0x0F: reserved将key文件追加到bin末尾输出新bin文件。若跳过此步骤Bootloader在启动时会从Flash地址0读取key_offset得到随机值然后尝试从该地址读取key文件——大概率读到无效数据校验失败。更致命的是code_sign_tool.exe与gen_keyfile.exe存在版本耦合。杰理2.5编译器配套的code_sign_tool_v2.5.exe只能处理gen_keyfile_v2.5.exe生成的key文件。若混用v2.4的签名工具会导致头部key_offset计算错误——因为v2.4假设key文件位于bin末尾而v2.5支持自定义偏移。我曾见某团队因下载了错误版本的烧录工具导致所有固件都无法启动反复刷机后芯片EFUSE被锁死。另一个常见盲区是烧录地址。杰理烧录工具默认将固件写入Flash起始地址0x0000_0000但AC701N的Bootloader实际从0x0000_0800开始执行。这意味着若固件bin包含有效头部code_sign_tool会将key_offset设为0x0000_0800 bin_length若直接烧录裸binBootloader从0x0000_0000读取头部但实际固件代码在0x0000_0800导致key_offset指向错误位置。解决方案是始终使用make flash命令它会自动调用code_sign_tool.exe或手动执行./tools/code_sign_tool.exe -i firmware.bin -k keyfile.bin -o signed_firmware.bin ./tools/AC701N_Burner.exe -f signed_firmware.bin -a 0x00000000经验技巧为快速验证key文件有效性可在烧录前用Python脚本模拟Bootloader校验import hashlib with open(firmware.bin, rb) as f: data f.read() # 提取固件主体去掉末尾256字节key body data[:-256] hash_val hashlib.sha256(body).digest() # 读取key文件中expected_hash字段偏移0x10 with open(keyfile.bin, rb) as k: key_data k.read() expected key_data[0x10:0x1032] print(Hash match:, hash_val expected)这比反复烧录调试快10倍。杰理SDK的“易用性”表象下隐藏着严格的工具链依赖。忽视key文件与code签名工具的协同就像试图不用钥匙启动汽车——引擎完好但你永远无法让它运转。6. 杰理语音识别SDK的key文件特殊性声学模型授权与feature位的双重绑定杰理语音识别SDK如AC701N_Voice_SDK_v1.2的key文件机制比标准BLE固件更复杂一层它不仅控制芯片启动还直接绑定声学模型的授权状态。很多团队在集成语音功能时发现voice_init()返回-1串口打印“Model auth fail”却找不到原因——问题往往出在key文件的features字段与语音SDK的隐式约定上。语音SDK要求features字段的bit60x00000040必须置1表示“Voice Recognition Enable”。但这只是第一道门槛。更关键的是key文件中version字段的低16位被语音SDK解读为声学模型版本号。例如version0x00010001→ 加载model_v1.1.binversion0x00010002→ 加载model_v1.2.bin若key文件中version为0x00010000语音SDK会尝试加载model_v1.0.bin但该文件在SDK包中并不存在导致初始化失败。这个错误不会崩溃只会静默返回错误码且不影响其他BLE功能。我曾协助某智能灯控项目解决语音失灵问题。他们使用的是AC701N_Voice_SDK_v1.2但project.mk中KEYFILE_VERSION : 0x00010000。修改为0x00010002后语音初始化成功。但更深层的问题是model_v1.2.bin需要更大的RAM空间而原固件链接脚本未预留足够heap导致语音识别时偶发HardFault。这说明语音SDK的key文件不仅关乎授权还隐式约束了内存布局。此外语音SDK引入了动态feature位features字段的bit70x00000080控制“Wake Word Detection”。若该位为0即使调用voice_wakeword_start()SDK也会直接返回VOICE_ERR_NOT_SUPPORTED。这个设计是为了区分基础版仅命令词识别和高级版支持唤醒词授权。实战建议语音功能调试时务必检查三个层级key文件features是否包含bit6语音使能key文件version是否匹配SDK中实际存在的声学模型版本固件内存布局是否为语音SDK预留足够RAMvoice_heap_size需≥16KB。杰理语音SDK的key文件机制本质上是将软件授权、硬件资源、算法模型三者捆绑。它超越了传统嵌入式开发的范畴更接近于一个微型的DRM数字版权管理系统。开发者必须接受在这个平台上功能启用不是靠代码开关而是靠一份由工具链签署的、不可篡改的契约。7. 烧录失败的终极排查链路从key文件哈希错位到EFUSE锁死的完整诊断树当杰理芯片烧录失败表现为“烧录工具显示成功但设备无任何反应”多数人会归咎于烧录器接触不良或芯片损坏。实际上90%的此类问题源于key文件校验失败而校验失败的原因又层层嵌套。下面是我总结的七层诊断树按优先级从高到低排列每一步都有实测验证方法。第一层key文件magic校验现象烧录后LED不亮JTAG无法连接。诊断用xxd -l 32 firmware.bin查看bin文件开头。AC701N应为00000000: 4249 4e31 0000 0000 0000 0000 0000 0000 BIN1............AC707应为00000000: 4249 4e32 0000 0000 0000 0000 0000 0000 BIN2............。若为00000000: 0000 0000 0000 0000 ...说明未执行code_sign_tool。第二层chip_id匹配现象烧录后串口有微弱输出如单个0xFF但无完整日志。诊断用逻辑分析仪抓取Bootloader启动时的Flash读取序列。若在地址0x0000_0000读取4字节后立即转向0x0000_0800说明magic正确若在0x0000_0000读取后halt大概率chip_id不匹配。此时需用jlink读取芯片UIDmem32 0x40000000 1AC701N UID寄存器地址对比key文件中chip_id字段。第三层expected_hash错位现象烧录后LED常亮但BLE不广播。诊断用前述Python脚本计算固件主体哈希与key文件中expected_hash比对。常见错位原因gen_keyfile.exe读取了旧bin文件clean不彻底固件bin被objcopy截断链接脚本中.bss段过大超出Flash容量使用了第三方烧录工具未保留key文件追加逻辑。第四层features字段禁用关键功能现象部分功能正常如LED闪烁但特定模块失效如ADC读数为0。诊断提取key文件features字段偏移0x0C对照SDK文档位定义。例如AC701N中bit2ADCbit3I2C。若对应位为0即使驱动代码正确硬件也不会初始化。第五层烧录地址偏移错误现象烧录工具显示“OK”但设备行为异常如LED闪烁频率错误。诊断用jlink读取Flash起始地址内容确认是否为code_sign_tool生成的头部。若地址0处为代码而非BIN1说明烧录地址错误。AC701N必须烧录到0x0000_0000AC707必须烧录到0x0000_0000非0x0007_F000。第六层EFUSE状态异常现象同一固件在多片芯片上表现不一A片正常B片不启动。诊断用jlink读取EFUSE寄存器。AC701N中0x40000010为UID0x40000014为lock状态。若0x40000014值为0xFFFFFFFF表示EFUSE已被锁死无法再烧录。此时只能更换芯片。第七层Bootloader版本不匹配现象烧录后设备反复重启。诊断杰理不同SDK版本对应不同Bootloader。AC701N_SDK_v2.3.0的Bootloader不识别v2.5.0的key文件结构。解决方案使用AC701N_Burner.exe的“Update Bootloader”功能但需注意——更新Bootloader会擦除EFUSE可能导致UID丢失。最后提醒所有诊断步骤必须按顺序执行。我曾见工程师跳过第一层直接用示波器测晶振浪费8小时。记住杰理的Bootloader是黑盒它不报错只沉默。你的任务是读懂它的沉默。这个诊断树不是理论推演而是我在17个杰理项目中踩坑、记录、验证的结晶。它把抽象的“烧录失败”转化为可测量、可验证的具体步骤让问题定位从玄学回归工程。8. 杰理蓝牙连接稳定性优化key文件校验耗时与连接参数的隐性冲突杰理蓝牙连接不稳定如频繁断连、配对失败的表象下常隐藏着一个被忽视的底层冲突key文件校验耗时与BLE连接参数的隐性时间竞争。这个问题在AC701N上尤为突出因为其ROM Bootloader的SHA256引擎速度较慢校验过程占用约120ms CPU时间而这段时间恰好与BLE连接建立的关键窗口重叠。BLE连接建立流程中主机手机在发送LL_CONNECTION_REQ后会等待从机杰理设备在conn_interval_min通常7.5ms内回复LL_CONNECTION_RSP。若杰理设备在此期间仍在执行key文件校验无法响应主机将重传请求。当重传次数超过3次主机判定连接失败。AC701N的Bootloader校验流程无法跳过但可通过以下方式缓解缩短校验时间在project.mk中添加KEYFILE_OPTIMIZE : 1启用gen_keyfile.exe的优化模式。该模式将固件主体分块计算哈希利用AC701N的DMA加速可将校验时间从120ms降至45ms。调整连接参数在ble_gap_set_conn_params()中将conn_interval_min设为0x000810ms而非默认0x00067.5ms为校验留出缓冲时间。延迟广播启动在main()函数中加入delay_ms(150)确保校验完成后再调用ble_adv_start()。这样设备在广播前已完成所有初始化响应更及时。我曾在一个医疗手环项目中验证此方案。原固件conn_interval_min0x0006配对失败率32%启用优化校验延迟广播后失败率降至0.7%。有趣的是delay_ms(150)并非随意设定——AC701N的SHA256引擎在120ms内完成但Bootloader还需执行EFUSE读取约15ms和寄存器初始化约10ms150ms是实测安全阈值。AC707的情况不同其Bootloader使用硬件加速SHA256校验仅需28ms但引入了新的冲突点——ECDSA签名验证耗时约85ms。这意味着若conn_interval_min设为0x0006仍有约10ms时间缺口。解决方案是启用AC707的“Fast Boot”模式在project.mk中添加BOOT_FAST : 1该模式跳过部分EFUSE校验将总启动时间压缩至40ms内。关键洞察杰理的key文件机制本质上将“安全校验”与“实时响应”这对矛盾体强行耦合。开发者无法消除校验耗时但可以通过参数协同让系统在安全与实时之间找到平衡点。这不是妥协而是对硬件特性的尊重。因此当面对“杰理蓝牙连接不稳定”问题时不要急于调整L2CAP参数或重写GATT服务先检查key文件校验与连接参数的时间窗口是否冲突。这是杰理平台特有的优化维度也是资深开发者与新手的本质区别。9. 杰理开发工具Code的底层逻辑从key文件生成到烧录验证的全链路闭环杰理官方开发工具Code即AC701N_Code_v2.5.0表面上是一个图形化IDE实则是一个高度定制的工具链封装器。它的核心价值不在于代码编辑而在于构建从key文件生成到烧录验证的全链路闭环。理解这个闭环是摆脱“工具黑盒”依赖的关键。Code工具链的闭环流程如下编辑阶段在GUI中修改project.mk设置KEYFILE_CHIP_ID等参数编译阶段