☰
Vivado综合阶段HDL与XDC属性设置完整指南:从KEEP到时序约束
2026/9/29 2:16:13 网站建设 项目流程

有不少人拿到一个Vivado工程,第一件事就是先跑一遍综合,看到时序报告没有大红大紫就以为万事大吉。我自己刚开始做FPGA的时候也是这样,结果在一个项目里被一个“找不到的寄存器”坑了两天:综合报告里清清楚楚显示这个信号还在,打开网表一搜,它早被优化得干干净净。从那以后,我养成一个习惯:在写RTL的时候就顺手把HDL属性声明加上,等需要做详细外部时序约束时,再在XDC里补齐对应的属性设置。这个系列的第二篇,我们就围绕HDL和XDC属性设置清单,把综合阶段最值得用的属性和约束从原理到实操完整捋一遍,内容包括每个属性的作用对象、生效时机、典型写法,以及我实际调试中踩过的坑。不管你是刚接触Vivado的新手,还是已经被时序收敛逼到头秃的老工程师,这份清单应该都能帮上忙。

1. 属性能干什么:先画清HDL属性和XDC属性的职责边界

1.1 两类属性的生效时机和作用域不一样

乍一看,HDL属性和XDC属性都是“告诉工具怎么做”,但它们在时间轴上的位置完全不同。HDL属性写在RTL的源文件里,是给综合器看的。你写一行 (* keep = "true" *) wire cnt_en; 综合器在解析这个wire时就会读到这条声明,接下来做逻辑优化、资源推断、寄存器合并时都会把它纳入考虑。所以HDL属性可以影响综合器在RTL层面的每一次结构选择,比如把这段代码推断成BRAM还是LUTRAM,把状态机编码成one-hot还是gray。

XDC属性则不一样。XDC是基于Tcl语法的约束文件,综合阶段和实现阶段都会读取,但不同类别的约束在不同的阶段起作用。像create_clock、set_input_delay这类的时序约束,综合阶段就会读,因为综合器要估算路径延迟来指导优化;而set_property PACKAGE_PIN这种管脚位置约束,虽然综合阶段也能接收,但真正落地发生在实现阶段。还有一些属性,比如DONT_TOUCH、MARK_DEBUG,综合前读和综合后读的效果还不一样,综合前读能影响综合优化,综合后读则更多是保护已经生成的网表。

这个区别非常关键。我见过很多工程师把DONT_TOUCH写进XDC,但XDC文件被设置成只参与实现阶段,结果综合阶段根本读不到这条约束,该优化的地方照样优化,到了布线阶段DONT_TOUCH才生效,网表已经变样。所以理解属性的生效时机,比背一百条语法都重要。

1.2 为什么要在综合阶段就把关键属性写到位

一个很常见的想法是:反正综合完还能改,属性晚点加也没事。这个想法在部分场景下成立,但在很多关键场景会吃亏。综合器的优化动作是不可逆的。如果综合器已经把一段乘加逻辑推断成LUT阵列,等你在综合后的网表里再设USE_DSP,往往需要重新综合才能生效,或者只能通过属性重映射原语,复杂度成倍增加。

更实际的原因是,RTL属性是跟着源码走的。你写了 (* ram_style = "block" *) 放在数组声明前,这段代码就算从这个工程复制到别的工程,属性也不会丢。而XDC文件经常因为工程结构变化、约束文件被排除等原因失去作用,属性写在RTL里相当于多了一层保险。

还有一种情况是做跨模块优化。综合器默认可以在不同模块之间做常量传播、寄存器吸收、等价逻辑合并,这是好事,但也可能把你在子模块里精心设计的结构打散。如果这是你不希望发生的,就应该在综合前用KEEP_HIERARCHY或DONT_TOUCH把边界保护起来。等综合完再处理,原始的结构信息已经部分丢失了。

1.3 一句口诀:HDL属性管“优化方向”,XDC属性管“约束条件”

我用一句话总结自己的经验:HDL属性是给综合器的优化方向说明书,XDC属性是给布局布线工具的行为约束。前者回答“这段逻辑我期望它长成什么样”,后者回答“这个端口、这个时钟、这条路径必须满足什么条件”。

在实际操作中,我的习惯是:凡是跟结构选择、资源类型、防优化相关的属性,尽量写进RTL,比如KEEP、DONT_TOUCH、RAM_STYLE、USE_DSP、FSM_ENCODING;凡是跟外部接口、时序参数、物理位置相关的约束,写在XDC里,比如IOSTANDARD、PACKAGE_PIN、时钟周期、输入输出延时。遇到两边都能写的,比如ASYNC_REG和MARK_DEBUG,就两个地方都写上,保证在不同阶段的一致性。这个习惯帮我减少了很多“属性失效”问题。

2. 综合阶段最常用HDL属性解析

2.1 KEEP、PRESERVE和DONT_TOUCH:三种“保留”到底有什么区别

很多朋友分不清KEEP、PRESERVE和DONT_TOUCH之间的关系,我理解它们其实分两个维度:KEEP和PRESERVE偏向“防止综合优化把它删掉”,DONT_TOUCH是“把整个对象保护起来,综合和实现阶段都不准动”。

KEEP最常用在wire和reg这类信号上。当信号在代码里生成,但最终没有扇出,或者只是作为调试信号时,综合器很容易把它优化掉。你如果后续还要用这个信号做约束、做ILA调试,或者在网表里手工分析,就需要声明 (* keep = "true" *) wire sig; 保住它。注意KEEP只保证综合阶段网络被保留,到了布局布线阶段它不一定能挡住优化,所以如果是要跨实现阶段保护,还得用DONT_TOUCH。

PRESERVE更偏向保留寄存器或某些逻辑单元不被合并。比如两个寄存器因为功能等价被综合器合并,这通常能省面积,但如果你明确希望保留独立的寄存器,可以用 (* preserve = "true" *) 声明。Vivado综合里,PRESERVE对寄存器、状态单元和某些组合逻辑的保留效果比KEEP更具体。

DONT_TOUCH则是大杀器,它可以放在wire、cell、instance、module上,告诉综合器和实现器:这个对象不能优化、不能合并、不能删除。典型写法是 (* dont_touch = "true" *) my_module u_inst (...); 或者加在wire上。这个属性在保护特定模块、防止跨层次优化时非常好用,但千万别随手给整个顶层模块加dont_touch,那样会关闭几乎所有优化空间,面积和时序都会变得非常难看。

(* keep = "true" *) wire cnt_en; (* preserve = "true" *) reg [3:0] sync_cnt; (* dont_touch = "true" *) my_module u_inst (...);

我自己的经验是,KEEP和PRESERVE能解决的问题,优先不要动用DONT_TOUCH。前者优化的范围小,对面积和时序的副作用也小;DONT_TOUCH更像最后一道防线,用它的时候心里要想清楚:如果这段逻辑完全不做优化,后果我能不能接受?

2.2 RAM、ROM和DSP:资源推断不是你猜出来的,是声明出来的

Vivado综合器有很强的RAM/ROM/DSP推断能力。但“能推断”不代表“推断得对”。它判断的标准往往跟你预期的资源类型不完全一致。比如一段简单的双端口RAM,综合器可能因为写读地址风格、使能信号组合等问题,最终推断成LUTRAM而不是BRAM。这时候你不需要把代码重写一遍,直接用属性声明期望的资源类型即可。

RAM_STYLE属性是最常用的。(* ram_style = "block") 强制使用BRAM;(ram_style = "distributed") 强制使用LUTRAM;在UltraScale+系列里还可以用 (ram_style = "ultra") 强制使用URAM。ROM也有类似属性,(rom_style = "block") 或 (rom_style = "distributed" *)。

DSP也类似。乘加运算默认由综合器根据资源和性能做分配,可以用 (* use_dsp = "yes") 把乘加推向DSP48E,用 (use_dsp = "no") 让它回到LUT逻辑,(use_dsp = "max" *) 则是尽可能多用DSP。

(* ram_style = "block" *) reg [15:0] buff [0:1023]; (* rom_style = "distributed" *) reg [7:0] lut_data [0:255]; (* use_dsp = "yes" *) wire [31:0] mul_res;

用这类属性时要留意一个前提:它只对“综合器能识别为RAM/ROM/DSP”的结构有效。如果你的代码风格比较随意,地址倒换、读写端口很乱,哪怕声明了block,综合器也会报warning然后Fallback到其他实现。所以遇到资源用不上,先去看warning,再看看代码是不是能优化成标准模板,这比盲目加属性更有效。

2.3 FSM编码属性:状态机面积与速度的权衡

状态机是FPGA逻辑里最常见的结构,默认情况下Vivado综合器会根据状态数量和目标频率自动选择编码方式(FSM_ENCODING AUTO)。这个自动选择通常不差,但当你对时序、面积有明确预期时,手动指定编码属性能带来更稳定的结果。

常用的FSM_ENCODING取值包括:

  • one_hot:一位状态一比特,状态寄存器数量多,但组合逻辑简单、路径短,适合速度敏感和状态数较少的设计。
  • gray:相邻状态变化时只有一位翻转,跳转时间短,状态寄存器少,适合连续跳转的状态机。
  • sequential:状态编码按顺序递增,面积最省,但组合逻辑可能更复杂,适合面积优先的设计。
  • johnson:折中方案,解码逻辑比较简单。

代码写法是把属性加在状态寄存器上:

(* fsm_encoding = "one_hot" *) reg [7:0] state;

如果你是沿用Verilog的parameter写状态,而没用enum类型,Vivado也能识别,只要状态寄存器的赋值满足状态机的典型写法。我的建议是:只有当综合报告明显偏离你的预期、或者某个状态机的组合逻辑成为了关键路径,才考虑手动指定。不要每个状态机都去手动指定,尤其是灰色编码和one-hot,选错反而会让面积和时序一起变差。

2.4 同步器和CDC打拍:ASYNC_REG属性的正确用法

跨时钟域处理,最基础的方案就是两级甚至三级同步打拍。但很多人的同步器只是“代码上写了两级寄存器”,综合器可不会自动把它们当成同步器。如果这两个寄存器之间插入了组合逻辑,或者它们被当作普通寄存器参与优化,同步效果就可能被破坏。

处理CDC信号的正确做法是在RTL里给同步器的寄存器加ASYNC_REG属性。Vivado会据此把这个寄存器标记为异步寄存器,不参与普通寄存器的等价合并、重定时等优化,而且在时序分析时也作特殊处理。常用写法如下:

(* async_reg = "true" *) reg sync_a, sync_b; // 或对向量 (* async_reg = "true" *) reg [1:0] sync_vec;

需要注意,RTL里加了ASYNC_REG属性只是第一层,XDC里最好也同步设置set_property ASYNC_REG TRUE,并在CDC路径上添加合理的set_false_path或者set_clock_groups,否则即使寄存器保住了,时序分析报告也会对这一串寄存器报出异常路径,影响其他路径的收敛判断。这是很多新手容易漏的第二步。

2.5 还有一些虽不常用但很救命的HDL属性

除了前面几个大块头,综合阶段还有几个属性值得收藏:

  • MAX_FANOUT:限制信号最大扇出,通常配合高扇出复位、使能信号使用。写法是 (* max_fanout = 64 *) reg rst_n; 综合器会根据条件复制寄存器。注意扇出限制不是越小越好,复制太多寄存器反而会消耗面积,也会影响时钟树和布局。

  • SHREG_EXTRACT / SRL_STYLE:控制移位寄存器是推断成SRL16/SRLC32还是普通寄存器链。在有些工艺或时序要求下,强制SRL风格可能更省LUT,但后续无法用FF资源;如果代码里还有异步复位需求,可以用 (* shreg_extract = "no" *) 关掉SRL推断。

  • KEEP_HIERARCHY:写在子模块上,(* keep_hierarchy = "yes" *) 可以防止综合器跨模块吸收逻辑。需要保持模块边界做后续调试和复用的时候很有用。

  • EQUIVALENT_REGISTER_REMOVAL:默认等价寄存器会被合并,如果你明确希望保留,可以用 (* equivalent_register_removal = "no" *) 关闭。不过大多数情况下默认就好,别乱关。

  • DONT_RETIME:防止综合器对这个寄存器做重定时,在处理一些对延迟敏感的逻辑时能派上用场。

  • REGISTER_BALANCING:控制寄存器平衡优化,对流水线结构调整很有用,但需要搭配时序分析一起看。

这些属性不常用,但遇到具体问题时,比绕道改代码高效得多。我会在后面的速查清单里把它们补全。

3. XDC属性设置的实操要点

3.1 XDC里怎么写KEEP、DONT_TOUCH和MARK_DEBUG

XDC文件是Tcl语法,属性设置用set_property。单个对象最简单的方式:

set_property KEEP TRUE [get_nets {u_top/u_inst/enable}] set_property DONT_TOUCH TRUE [get_cells {u_top/u_inst/u_ram_ctrl}] set_property MARK_DEBUG TRUE [get_nets {u_top/debug_counter[7:0]}]

KEEP在XDC里也能设,但我更建议KEEP放在RTL里声明,因为RTL信号名是源码层面的名字,写起来最直观,而且跟着代码走不会丢。DONT_TOUCH则经常在综合后设置,因为你可以先打开综合网表看看哪些cell被保留下来了,再决定对哪个具体实例加保护。如果是综合前XDC里设置DONT_TOUCH,则要确保对象路径在RTL里就能被get_cells对应上,否则会静默失败。

MARK_DEBUG是调试时非常常用的属性。当你需要在Vivado里把某个内部信号接到ILA核上时,可以先用set_property MARK_DEBUG TRUE标记这个信号,然后在硬件管理器里自动生成ILA。MARK_DEBUG的前提是信号在网络中存在,如果它在综合时已经被优化掉,设置也不会生效。所以怀疑信号被优化时,先回到RTL给它加KEEP,再用MARK_DEBUG。

还有一个容易让人困惑的属性是CLOCK_DEDICATED_ROUTE。当某个时钟信号没有满足专用的时钟路由规则,Vivado会在实现阶段报错或警告。如果确认你的时钟来源没有问题,只是绕了某一小段路径,可以用set_property CLOCK_DEDICATED_ROUTE FALSE [get_nets clk_net] 放宽检查。但这属于“下不为例”型操作,每次放宽都应当在代码或注释里写清楚为什么这么做,否则风险很大。

3.2 IO约束不只是管脚位置

IO约束是XDC里最基础的部分,很多新手只写了PACKAGE_PIN和IOSTANDARD就算完事。但真实项目中,IO标准、驱动能力、翻转速率、上下拉这些属性,同样会影响到板级信号的完整性和时序。

举个例子:

set_property PACKAGE_PIN U18 [get_ports clk_200m_p] set_property IOSTANDARD LVDS [get_ports clk_200m_p] set_property PACKAGE_PIN U19 [get_ports clk_200m_n] set_property IOSTANDARD LVDS [get_ports clk_200m_n] set_property PACKAGE_PIN W20 [get_ports data_out[0]] set_property IOSTANDARD LVCMOS18 [get_ports data_out[0]] set_property SLEW FAST [get_ports data_out[0]] set_property DRIVE 12 [get_ports data_out[0]] set_property PULLUP TRUE [get_ports key_in]

SLEW控制信号翻转速率,FAST会给更陡峭的沿,适合高速输出;但太陡又会带来反射,板上走线不长时没必要强行FAST。DRIVE是输出驱动强度,一般根据负载选择。PULLUP用在按键输入、拨码开关等上拉需求场景,用一个外部电阻往往更稳,FPGA内部的弱上拉只是辅助。

很多人不知道set_property还支持-dict参数,可以把多个属性一次性设置:

set_property -dict {PACKAGE_PIN W20 IOSTANDARD LVCMOS18 SLEW FAST DRIVE 12} [get_ports data_out[0]]

这在工程中大量IO约束时能省不少建模时间。IO约束还会牵涉到IO Bank电压,不同电平标准要求不同的VCCO,如果XDC里设置了LVCMOS33但Bank实际供的1.8V,那大概率会烧坏IO或者直接崩塌。所以IO约束不仅是“写上去”,还要和原理图对照。

3.3 时序例外约束:从综合阶段就要考虑

时序约束是XDC里的重头戏,但很多人习惯在综合完之后再写时序约束。我建议反过来,在做综合之前就把关键时序约束写好。因为综合器需要时钟周期、输入输出延迟、例外路径来计算路径延迟,从而决定逻辑结构和优化力度。

最基本的时钟约束:

create_clock -period 5.000 -name clk200 [get_ports clk_200m] create_clock -period 8.000 -name clk125 [get_ports clk_125m]

然后是输入输出延时:

set_input_delay -clock clk200 -max 2.5 [get_ports data_in] set_output_delay -clock clk200 -min 0.5 [get_ports data_out]

跨时钟域路径如果确实不需要做时序收敛,用set_false_path或set_clock_groups显式告诉工具:

set_false_path -from [get_clocks clk200] -to [get_clocks clk125] set_clock_groups -asynchronous -group [get_clocks clk200] -group [get_clocks clk125]

对于某些路径,数据不是每个时钟周期都有效,可以用set_multicycle_path扩展建立和保持关系。比如一个只在每两个周期采一次数据的寄存器:

set_multicycle_path -setup 2 -from [get_pins u_top/reg_a_reg/C] -to [get_pins u_top/reg_b_reg/D]

还有一个容易被忽视的命令是set_case_analysis。它告诉工具,某个信号在正常运行时恒定为0或1,让工具把相关逻辑门剪掉以利于优化:

set_case_analysis 0 [get_pins u_inst/mode_sel_reg/Q]

不过这个约束一定要谨慎,信号真不是恒定的情况下,综合结果会完全不正确。

3.4 控制XDC文件参与综合和仿真的范围

Vivado里每个XDC文件都有一个参与阶段的属性。默认情况下,XDC文件会同时参与综合和实现。但某些XDC可能只想在实现阶段生效,或者只想在仿真里用,这时就可以用File Properties里的USED_IN_SYNTHESIS / USED_IN_SIMULATION来控制。

set_property used_in_synthesis false [get_files io_only.xdc] set_property used_in_simulation false [get_files timing_only.xdc]

这对工程管理很有价值。比如你有一份只做IO管脚位置约束的文件,它不影响综合逻辑,但又不想被综合器反复读取增加编译时间,就可以把它设为只在实现阶段使用。仿真专用约束文件更是可以通过设置让综合阶段完全不加载。

在源文件较多的工程里,当我怀疑某个属性没生效时,第一件事就是检查它的XDC是不是被排除了,或者是不是因为文件优先级问题被其他XDC里的同名属性覆盖了。这个问题在后面还会专门展开。

4. 属性速查清单与排查技巧

4.1 HDL属性速查清单

我整理了一份自己在项目里常用的HDL属性表,按作用对象排列:

属性作用对象典型取值主要功能
keepwire / regtrue防止信号被优化删除
preservereg / celltrue保留寄存器或单元不被合并
dont_touchwire / instance / moduletrue综合和实现阶段都禁止优化
ram_stylememory变量block / distributed / ultra指定RAM实现资源
rom_stylememory变量block / distributed指定ROM实现资源
use_dsp乘加运算yes / no / max控制乘加是否映射到DSP
fsm_encoding状态寄存器auto / one_hot / gray / sequential / johnson状态机编码方式
async_reg同步器寄存器true标记异步同步寄存器
shreg_extract移位寄存器yes / no是否推断SRL
srl_style移位寄存器reg / srl / srl_reg等SRL和FF的组合方式
max_fanoutreg / instance整数限制最高扇出
keep_hierarchymoduleyes / no保持模块层次
equivalent_register_removalregno禁止等价寄存器合并
dont_retimeregtrue禁止重定时优化

这份表看起来条目不少,实际项目里高频使用的也就前八行。有需要时再把后面的翻出来。

4.2 XDC属性速查清单

XDC常用属性和命令整理如下:

约束 / 属性作用对象典型值主要功能
set_property KEEPnetTRUE保留网络不被综合删除
set_property DONT_TOUCHcell / netTRUE综合和实现阶段保留对象
set_property MARK_DEBUGnetTRUE标记信号用于ILA调试
set_property MAX_FANOUTcell32 / 64限制扇出
set_property ASYNC_REGcellTRUE标记异步同步寄存器
set_property PACKAGE_PINport如 U18管脚位置
set_property IOSTANDARDportLVCMOS33等IO电平标准
set_property SLEWportFAST / LOW翻转速率
set_property DRIVEport4 / 8 / 12 / 16输出驱动强度
set_property PULLUPportTRUE内部上拉
set_property CLOCK_DEDICATED_ROUTEnetFALSE放宽时钟专用路由检查
create_clock时钟Port-period / -name创建时钟
set_input_delayInput port-max / -min输入延迟约束
set_output_delayOutput port-max / -min输出延迟约束
set_false_pathPathfrom / to定义伪路径
set_clock_groupsClock group-asynchronous定义异步时钟组
set_multicycle_pathPath-setup / -hold多周期路径
set_case_analysisPin / Port0 / 1静态逻辑约束
set_property USED_IN_SYNTHESISXDC filefalse排除综合阶段
set_property USED_IN_SIMULATIONXDC filefalse排除仿真阶段

这个清单不是让你全背下来,而是遇到问题的时候知道去哪里查。我电脑里就长期放着一份这样的表,每次新项目开始前过一遍。

4.3 属性写了却没生效怎么办:排查思路

我在技术答疑群见过最多的问题是:“我明明加了KEEP/DONT_TOUCH/RAM_STYLE,为什么没有效果?”排查这类问题是有套路的,按顺序做基本都能定位:

第一步,先确认属性的作用对象对不对。KEEP必须作用在net或wire上,你写在module上大概率不生效;DONT_TOUCH作用在instance或cell上,你写在reg变量上就不是同一个概念。打开综合报告,搜索“property”或“attribute”关键词,看工具是否识别了这条属性。

第二步,确认路径是否正确。综合后的网表对象路径和RTL里的信号路径不一定完全一致。在Vivado的Tcl Console里执行get_nets和get_cells验证对象是否存在。比如你想给 u_inst 下的某根线加KEEP,先执行:

get_nets -hierarchical {*u_inst*enable*}

如果返回为空,说明信号名已经被综合器改造成了别的名字,需要根据网表去查名字,再重新设置属性。

第三步,检查综合日志有没有warning。比如RAM_STYLE指定block但综合器发现代码不能被推断为BRAM,会打印类似“The RAM will be implemented in distributed”的信息,原因都会写在警告里。

第四步,检查XDC是否真的参与了当前阶段。在Vivado里选中XDC文件,查看它的USED_IN_SYNTHESIS / USED_IN_SIMULATION属性。如果文件被设置成只参与实现,那综合阶段读取不到它,自然不生效。

第五步,检查是否有其他约束覆盖了它。同一个对象被两个XDC设置了相同属性但值不同,Vivado默认后读入的会覆盖先读入的。这时候需要理清文件的读取顺序,或者把冲突的属性归并到一个文件里。

这个排查流程几乎能覆盖九成以上的“属性没生效”问题。剩下的特殊情况,往往都是还没理清生效时机造成的。

4.4 我踩过的几个真实的坑

第一个坑是乱加KEEP导致网表膨胀。当时为了调试,我在RTL里给十几个内部信号全加了 (* keep = "true" *),结果综合完发现网表体积比预期大了一半,布局布线时间也明显拉长。原因是这些信号原本是会被优化的中间节点,KEEP一加,所有关联逻辑都失去了简化机会。后来我只保留了真正需要观察的几个信号,网表立刻瘦身成功。所以KEEP是按需使用,不是多多益善。

第二个坑是给整个顶层模块加DONT_TOUCH。那是个已经验证过的IP核,我怕综合器动它的结构,就加上了DONT_TOUCH。结果模块内部的常量传播彻底失效,很多简单逻辑都保留成电阻网,时序收敛变得极差,折腾了一整天才发现问题。DONT_TOUCH的作用范围尽量缩小到具体实例或关键网络,不要图省事直接包住大模块。

第三个坑是使用RAM_STYLE强制block,但代码里的RAM实际上是一个单端口异步读的复杂模式,综合器根本不能推断成BRAM。我盯着warning看了很久才明白:属性只能影响推断的倾向,不能改变代码的结构。后来把读端口改成同步读,BRAM才真正生效。这说明在加属性之前,先把代码往标准模板上靠,是更高优先级的动作。

还要提醒一句,很多属性在综合前和综合后设置效果完全不同。比如MAX_FANOUT在综合前设置可以让综合器帮你复制寄存器,而在综合后设置更多是指导布局器处理已有寄存器。什么时候用哪种方式,取决于你想让工具在哪一步做结构调整,想清楚了再写。

最后再分享一个我觉得非常值的小技巧:在Vivado里做综合功能属性设置时,我很喜欢用set_property -dict把同一对象的所有属性一次性写完,比如说对某个时钟端口同时设置位置和电平标准。这个习惯能大幅减少Tcl的执行时间,也让约束文件更干净。另外,综合完以后别急着关掉IDE,打开Schematic视图,搜索你加了KEEP的信号,如果还能在原理图里找到它,说明属性确实生效了,这比盯着日志看半天都直观。希望这份清单能帮你在综合阶段少走一点弯路。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询