☰
Vivado F13报错解析:信号类型与Site匹配是关键
2026/9/28 12:45:01 网站建设 项目流程

1. F13报错到底在说什么:从一条约束说起

很多人第一次看到Invalid placement site或者F13 is not a valid placement site这类报错,第一反应是"我明明照着开发板手册写的引脚,怎么会错"。我当年也是这么想的,直到被这个错误卡了整整一个下午,才明白 Vivado 说的"无效放置点"根本不是"这个引脚不存在",而是"你让一个信号去的地方,和这个信号本身的性质对不上"。

先把结论摆在前面:F13 这类报错,九成以上不是引脚号写错了,而是信号类型和引脚所属的 Site 类型不匹配。Vivado 的引脚约束(XDC 里的set_property PACKAGE_PIN)本质上是两件事:一是把端口映射到封装上的某个物理引脚,二是把这个端口绑定到芯片内部某个 IO Bank 里的具体 Site。当这两件事里任何一件和信号的实际用途冲突时,工具就会抛出"无效放置点"。

举个最常见的场景。你写了一个时钟输入,端口叫sys_clk,然后随手在 XDC 里写了:

set_property PACKAGE_PIN F13 [get_ports sys_clk] set_property IOSTANDARD LVCMOS33 [get_ports sys_clk]

综合能过,实现阶段就红了。为什么?因为 F13 这个引脚在芯片内部可能连到的是普通 IO Site,而你的sys_clk被综合工具推断成了需要走全局时钟网络(BUFG)或者区域时钟网络(BUFIO/BUFR)的信号。时钟信号对引脚有特殊要求——它必须落在MRCC(Multi-Region Clock Capable)或SRCC(Single-Region Clock Capable)类型的 Site 上,普通 IO Site 是接不了时钟输入的。F13 如果恰好是个普通 IO,那这个约束就是"让时钟信号去一个它去不了的地方",Vivado 自然报无效放置点。

所以诊断这个问题的第一步,不是去翻手册找 F13 在哪,而是先问自己:我这个信号是什么类型?它需要什么样的 Site?时钟、普通数据、差分对、专用功能引脚(比如配置引脚、JTAG、DDR 的 DQS),它们对 Site 的要求完全不同。把信号类型搞清楚了,再去核对 F13 的 Site 属性,问题往往一眼就能看出来。

这里有个容易被忽略的点:Vivado 的报错信息有时候会指向一个中间信号而不是你的顶层端口。比如你写的是sys_clk,但工具在优化过程中把它连到了sys_clk_IBUF或者sys_clk_IBUF_BUFG上,报错里出现的名字就变成了这些内部网表名。看到这种名字不要慌,顺着get_nets或者get_ports往回找,最终都能定位到你的原始端口。我一般习惯在 Tcl Console 里直接敲:

get_ports sys_clk get_nets -of [get_ports sys_clk]

把这条链路打印出来,报错里那个陌生的名字立刻就对上了。

2. 为什么偏偏是 F13:Site 类型与信号需求的匹配逻辑

要真正搞懂这个错误,得先理解 FPGA 引脚的三层结构:封装引脚(Package Pin)→ IO Bank → Site。F13 是封装上的物理位置,它属于某个 IO Bank,而这个 Bank 里有一堆 Site,每个 Site 有自己的类型。你写PACKAGE_PIN F13只是指定了物理位置,但 Vivado 还要决定这个信号放在 F13 对应的哪个 Site 上,以及这个 Site 能不能承载你的信号。

Site 类型大致分这么几类,理解它们的分工是排错的关键:

Site 类型全称能接什么信号典型用途
IO普通 IO普通数据、控制信号GPIO、LED、按键
MRCCMulti-Region Clock Capable全局时钟输入系统主时钟、需要跨区域驱动的时钟
SRCCSingle-Region Clock Capable区域时钟输入局部时钟、BUFIO/BUFR 输入
BUFGGlobal Clock Buffer全局时钟网络驱动时钟树根节点
BUFIOIO Clock BufferIO 区域内的快速时钟源同步接口、高速采集
BUFRRegional Clock Buffer区域时钟局部区域时钟分发

现在回到 F13。假设你查了芯片的引脚规划表(Package File 或者 Pinout 文档),发现 F13 属于某个 Bank 的普通 IO Site,而你的信号是个时钟。这时候矛盾就出现了:时钟信号在实现阶段会被工具自动插入 BUFG 或 BUFIO,而 BUFG 的输入必须来自 MRCC/SRCC,普通 IO Site 根本连不到 BUFG 的输入端口。工具尝试把时钟放到 F13,发现放不下去,于是报"F13 不是有效的放置点"。

反过来也有一种情况:F13 本身是个 MRCC 引脚,但你把它当普通数据用了,而且这个数据信号被约束到了某个 Pblock 里,Pblock 的范围和 F13 所在的 Bank 不重叠。这时候报错的原因就变成了"信号被约束到了 A 区域,但引脚在 B 区域",本质还是 Site 和约束的匹配问题。

还有一种更隐蔽的:差分对。如果你用的是差分信号,比如LVDS,那么 P 端和 N 端必须落在同一个差分对的两个 Site 上。F13 如果是某个差分对的 P 端,你只约束了 P 端没约束 N 端,或者把 N 端约束到了不相邻的引脚,Vivado 也会报无效放置点。差分对的 Site 是成对出现的,不能拆开用。

我自己的经验是,遇到 F13 报错,按这个顺序排查最快:

  1. 确认信号类型(时钟?差分?普通 IO?专用功能?)
  2. 查 F13 在芯片手册里的 Site 类型
  3. 看信号类型和 Site 类型是否匹配
  4. 如果不匹配,换引脚或者改信号处理方式

这个顺序看起来简单,但很多人一上来就跳到第 4 步乱换引脚,结果换了好几个还是报错,因为根本没搞清楚第 1 步和第 2 步。

3. 手把手定位:从报错日志到根因的完整链路

光讲原理不够,我拿一个真实案例把排查过程走一遍。这个案例是我帮一个朋友调板子时遇到的,现象很典型。

现象:一个 SPI Flash 的时钟输出flash_clk,约束到 F13,实现阶段报Invalid placement site。

第一步,看完整报错。Vivado 的报错往往不止一行,Critical Warning 下面会跟着一串信息。我习惯把 Tcl Console 里的内容全部复制出来,重点看这几项:

  • 报错的网表名(是端口还是内部信号)
  • 报错的 Site 名(是 F13 还是别的)
  • 有没有提到 BUFG、BUFIO、BUFMR 这些关键词

这个案例里,报错信息提到了BUFIO。这就说明工具试图给flash_clk插入一个 BUFIO,但 F13 所在的 Site 不支持 BUFIO 输入。

第二步,查 F13 的 Site 属性。在 Vivado 里可以直接用 Tcl 查:

get_package_pins F13 get_property SITE_TYPE [get_package_pins F13]

如果返回的是IOB_X0Y12这种普通 IO,那就确认了 F13 不是时钟能力引脚。也可以打开 Device 视图,在 Package 窗口里找到 F13,看它的颜色标记——时钟能力引脚通常有特殊标识。

第三步,确认信号需求。flash_clk是 SPI 时钟,速率不高(一般几十 MHz),其实不一定需要 BUFIO。BUFIO 是给高速源同步接口用的,SPI 这种低速时钟用普通 BUFG 甚至直接走普通 IO 都行。问题出在综合工具看到flash_clk是个时钟,自动给它推断了一个 BUFIO,而 F13 又接不了 BUFIO,于是冲突。

第四步,解决方案。这里有三条路:

  • 换引脚:把flash_clk换到一个 MRCC/SRCC 引脚上,让 BUFIO 能接进去
  • 改约束:用set_property CLOCK_DEDICATED_ROUTE FALSE允许时钟走普通布线,但会牺牲时钟质量
  • 改代码:在 RTL 里明确告诉工具这个时钟不需要专用时钟资源

我朋友最后选了第一条,换到了一个 SRCC 引脚,问题解决。但这里有个坑:换引脚之后,PCB 已经打样了,飞线很麻烦。所以更稳妥的做法其实是在设计初期就把时钟引脚规划好,别等到实现阶段才发现。

第五步,验证。改完之后重新跑实现,看 Critical Warning 是否消失,然后打开 Implementation 的 Clock Network 报告,确认时钟确实走了预期的网络。我一般还会跑一下report_clock_networks,看看时钟树的结构对不对。

这个案例里还有一个细节值得说:报错信息里的网表名是flash_clk_OBUF,不是flash_clk。如果你直接get_ports flash_clk去改约束,可能改不到点子上。正确的做法是先用get_nets找到这个内部信号,再顺着它找到驱动它的端口:

get_nets flash_clk_OBUF get_ports -of [get_nets flash_clk_OBUF]

这样才能确保约束改对了地方。

4. BUFIO、BUFG、BUFMR:时钟资源选错才是重灾区

F13 报错里出现频率最高的关键词就是 BUFIO 和 BUFG,这两个东西搞混是新手最容易踩的坑。我把它们的区别和适用场景讲清楚,以后看到报错就能直接判断该往哪个方向改。

BUFG(Global Clock Buffer)是全局时钟缓冲,它的输出能驱动整个芯片的所有时钟区域。特点是覆盖范围大、偏斜(Skew)小,但功耗相对高。系统主时钟、需要跨多个 Bank 使用的时钟,都应该走 BUFG。BUFG 的输入必须来自 MRCC 或者 SRCC 引脚,或者来自 MMCM/PLL 的输出。

BUFIO(IO Clock Buffer)是 IO 区域内的时钟缓冲,只能驱动同一个 IO Bank 内的 IO 逻辑。它的特点是延迟极低、抖动小,专门用于源同步接口——比如你从外部 ADC 采数据,ADC 同时给你数据和随路时钟,这个随路时钟就需要用 BUFIO 来采数据,保证建立保持时间。BUFIO 的输入只能来自 SRCC 引脚,不能来自 MRCC,也不能来自普通 IO。

BUFR(Regional Clock Buffer)介于两者之间,能驱动一个时钟区域内的多个 Bank,常用于需要跨 Bank 但不需要全局的场景。

BUFMR(Multi-Region Clock Buffer)更特殊,它能把时钟驱动到相邻的几个 Bank,常用于需要跨 Bank 的源同步接口。

现在问题就清楚了:如果你的信号被工具推断成了 BUFIO 输入,那它必须落在 SRCC 引脚上。F13 如果是普通 IO 或者 MRCC,就接不了 BUFIO,报错就来了。同理,如果信号需要 BUFG,那它必须落在 MRCC 或 SRCC 上,普通 IO 也不行。

那怎么知道工具给信号推断成了什么?两个办法:

一是看综合后的网表。打开 Synthesized Design,在 Netlist 窗口里找到你的时钟信号,看它连到了什么类型的 Buffer 上。如果看到BUFIO、BUFG、BUFR这些原语,就说明工具已经给它分配了时钟资源。

二是看报错信息。Vivado 在报Invalid placement site的时候,通常会在附近提到它试图使用的时钟资源类型。比如BUFIO相关的报错,往往会说The clock pin of a BUFIO must be driven by a SRCC pin之类的话。

我自己的习惯是,在 RTL 阶段就把时钟资源的意图写清楚,别让工具去猜。比如:

// 明确告诉工具这个时钟要走 BUFG (* clock_buffer_type = "BUFG" *) wire sys_clk_bufg;

或者在 XDC 里直接约束:

set_property CLOCK_DEDICATED_ROUTE FALSE [get_nets sys_clk]

但CLOCK_DEDICATED_ROUTE FALSE是把双刃剑,它允许时钟走普通布线,能绕过报错,但时钟质量会下降,高速设计里慎用。低速设计(比如几十 MHz 的 SPI)用用无妨,高速设计还是老老实实换引脚。

还有一个容易忽略的点:同一个 Bank 里的时钟资源是有限的。一个 Bank 里可能只有一对 MRCC 和一对 SRCC,如果你有多个时钟都要进这个 Bank,资源就不够用了。这时候要么换 Bank,要么用 MMCM/PLL 把时钟合并。我见过一个设计,一个 Bank 里塞了四个时钟输入,结果后两个怎么都过不了,最后发现是 SRCC 资源用完了。

5. 约束文件里的隐形陷阱:PACKAGE_PIN 和 IOSTANDARD 的配合

很多人以为引脚约束就是写个PACKAGE_PIN完事,其实IOSTANDARD和PACKAGE_PIN是绑在一起的,任何一个写错都可能导致 F13 报错。

先说IOSTANDARD。F13 所在的 Bank 有一个VCCO 电压,这个电压决定了这个 Bank 能用的 IO 标准。比如 Bank 的 VCCO 是 3.3V,你写IOSTANDARD LVCMOS18,那这个约束就是非法的,因为 1.8V 的标准在 3.3V 的 Bank 里用不了。Vivado 在实现阶段会检查这个匹配关系,不匹配就报错,有时候报的就是"无效放置点"。

查 Bank 电压的方法:

get_property VCCIO [get_iobanks 34]

把 Bank 号换成 F13 所在的 Bank,就能看到这个 Bank 的 VCCO 是多少。然后对照芯片手册里的 IO 标准表,确认你写的IOSTANDARD在这个电压下是合法的。

再说PACKAGE_PIN本身。有一种情况是引脚号写对了,但引脚被其他功能占用了。比如 F13 在芯片里可能默认是某个配置引脚(比如INIT_B或者DONE),如果你没有在约束里明确把它释放成普通 IO,Vivado 就会认为这个引脚不可用,报无效放置点。解决办法是在 XDC 里加:

set_property BITSTREAM.CONFIG.UNUSEDPIN PULLNONE [current_design]

或者针对具体引脚:

set_property IS_LOC_FIXED FALSE [get_ports your_port]

还有一种更隐蔽的:引脚的 Site 被 Pblock 排除了。如果你用了 Pblock 做布局约束,而 Pblock 的范围没有覆盖 F13 所在的区域,那即使 F13 本身没问题,信号也放不进去。这时候报错信息里通常会提到 Pblock 的名字,顺着查就能找到。

我整理了一个约束检查清单,每次遇到引脚报错就过一遍:

检查项检查方法常见问题
引脚号是否存在查 Package File手误写错,比如 F13 写成 E13
Site 类型是否匹配get_property SITE_TYPE时钟信号放到普通 IO
IOSTANDARD 是否合法查 Bank VCCO 和手册电压不匹配
引脚是否被占用查配置引脚列表配置引脚未释放
Pblock 是否覆盖查 Pblock 范围布局约束冲突
差分对是否成对查差分对表只约束了 P 端

这个清单看起来繁琐,但真到排错的时候,按顺序过一遍比瞎试快得多。我自己的经验是,前两项能解决 80% 的问题,后四项解决剩下的 20%。

6. 换引脚不是万能药:几种替代方案的取舍

遇到 F13 报错,很多人的第一反应是"换个引脚"。换引脚确实能解决大部分问题,但有些场景下换不了——比如 PCB 已经打样了,或者所有时钟能力引脚都被占用了。这时候就得考虑替代方案。

方案一:换到 MRCC/SRCC 引脚。这是最干净的方案,前提是 PCB 允许改。选引脚的时候注意,MRCC 比 SRCC 更灵活,能驱动 BUFG 和 BUFIO,SRCC 只能驱动 BUFIO 和 BUFR。如果拿不准,优先选 MRCC。

方案二:用CLOCK_DEDICATED_ROUTE FALSE。这个约束告诉 Vivado"我知道这个时钟没走专用时钟引脚,你允许它走普通布线吧"。加上之后报错会消失,但时钟会走普通 IO 布线,偏斜和抖动都会变大。低速设计(<50MHz)问题不大,高速设计慎用。用法:

set_property CLOCK_DEDICATED_ROUTE FALSE [get_nets your_clock_net]

注意这里要用get_nets而不是get_ports,因为报错往往发生在内部网表上。

方案三:在 RTL 里降级时钟资源。如果这个时钟其实不需要 BUFIO,可以在代码里明确用 BUFG 或者干脆不用专用缓冲。比如:

// 强制用 BUFG 而不是让工具推断 BUFIO wire clk_bufg; BUFG bufg_inst (.I(clk_in), .O(clk_bufg));

这样工具就不会去尝试 BUFIO,F13 作为普通 IO 也能接。但前提是这个时钟确实不需要 BUFIO 的低延迟特性。

方案四:用 MMCM/PLL 重新生成时钟。如果 F13 是个普通 IO,但你需要一个高质量时钟,可以把 F13 上的信号当普通数据或者低速参考,然后用 MMCM/PLL 从另一个时钟源生成所需时钟。这样 F13 就不需要承担时钟输入的角色了。

方案五:改 Bank 电压。如果 F13 报错是因为 IOSTANDARD 和 Bank VCCO 不匹配,而硬件上 Bank 电压是可调的(有些板子有跳线或者可调 LDO),改电压也能解决。但这个方案硬件改动大,一般不考虑。

这几种方案里,我个人的优先级是:换引脚 > RTL 降级 > CLOCK_DEDICATED_ROUTE FALSE > 改硬件。换引脚最干净,RTL 降级次之,CLOCK_DEDICATED_ROUTE FALSE是应急手段,改硬件是最后的选择。

还有一个经验:在项目初期就把所有时钟引脚规划好,画一张表,列出每个时钟信号的频率、需要的时钟资源类型、对应的引脚、Bank 电压。这张表在后期排错时能省大量时间。我现在的项目里都会维护这么一张表,每次加新时钟先查表,避免临时抱佛脚。

7. 几个真实踩坑记录和收尾心得

最后分享几个我自己踩过的坑,都是文档里不会写的。

坑一:报错指向的引脚和实际约束的引脚不一致。有一次我约束的是 F13,但报错里说的是 G13。查了半天才发现,是差分对的问题——我约束了 P 端 F13,工具自动把 N 端分配到了 G13,但 G13 的 Site 类型和 F13 不匹配。差分对的 N 端不能随便放,必须和 P 端在同一个差分对里。解决办法是用set_property DIFF_TERM或者直接约束差分对的 N 端。

坑二:综合能过,实现才报错。这是因为综合阶段不做引脚分配,只做逻辑综合。引脚分配是在实现阶段的place_design步骤做的。所以看到 F13 报错,不要怀疑综合,直接查约束和引脚规划。

坑三:同一个引脚在不同工程里表现不一样。有一次同样的约束,在一个工程里能过,在另一个工程里报 F13 无效。后来发现是两个工程的 Part 不一样,一个是xc7a35t,一个是xc7a100t,引脚规划不同。所以换芯片型号的时候,引脚约束一定要重新核对。

坑四:Vivado 版本差异。不同版本的 Vivado 对时钟资源的推断策略可能不同。我遇到过同一个设计在 2019.1 能过,在 2022.2 报 F13 错误的情况。原因是新版本对时钟资源的检查更严格了。遇到这种情况,要么改约束,要么在综合设置里调整-flatten_hierarchy或者-directive参数。

坑五:get_ports和get_nets用混。改约束的时候,如果报错信息里是内部网表名,用get_ports是改不到的。一定要先用get_nets找到那个网表,再顺着找端口。我一般会写个小脚本:

set err_net [get_nets -hier *your_signal*] puts "Net: $err_net" puts "Driver: [get_ports -of $err_net]"

把这条链路打印出来,改约束就不会改错地方。

说到底,F13 这类报错的核心就一句话:信号要去的 Site,和信号本身需要的资源对不上。把信号类型搞清楚,把 Site 类型查明白,两者一对照,问题就现形了。换引脚能解决大部分问题,但理解背后的匹配逻辑,才能在下一次遇到类似报错时不用再从头查一遍。我现在看到Invalid placement site,第一反应已经不是"哪个引脚错了",而是"这个信号需要什么资源,它现在被放到了哪里",这个思维转变帮我省了很多时间。

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

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

立即咨询