3. 认识 .lpf 文件:基本结构、语法规则与实战要点
好,咱们今天来聊聊 Lattice 的 .lpf 文件。说实话,我刚从 Xilinx 转到 Lattice 平台那会儿,第一眼看到 .lpf 文件,心里是有点懵的。它跟 Xilinx 的 .ucf 或者 Vivado 的 .xdc 长得不太一样,语法也自成一派。但用顺手了之后,你会发现它其实很简洁、很直接。
.lpf 的全称是 Lattice Preference File,说白了就是给 FPGA 的引脚分配和物理约束用的。它不像 .sdc 那样主要管时序,.lpf 更关心的是「哪个信号焊到了哪个引脚上」、「这个引脚的电气特性是什么」、「这个时钟走哪条全局时钟网络」。嗯,咱们一个一个来看。
3.1 .lpf 文件的基本结构
一个典型的 .lpf 文件,结构其实挺清晰的。我习惯把它分成三大块:
- 引脚分配区:把顶层模块的端口名映射到芯片的物理引脚上
- 电气特性区:设置 I/O 标准、驱动能力、上下拉等
- 物理约束区:指定时钟网络、PLL 位置、差分对等高级约束
举个例子,一个最简单的 .lpf 文件长这样:
# 引脚分配
LOCATE COMP "clk" SITE "P3";
LOCATE COMP "rst_n" SITE "P5";
LOCATE COMP "led[0]" SITE "P10";
LOCATE COMP "led[1]" SITE "P11";
# 电气特性
IOBUF COMP "clk" IO_TYPE=LVCMOS33;
IOBUF COMP "rst_n" IO_TYPE=LVCMOS33 PULLMODE=UP;
IOBUF COMP "led[*]" IO_TYPE=LVCMOS33 DRIVE=8;
# 时钟约束
FREQUENCY NET "clk" 50 MHz;
你看,每条语句都以分号结尾,大小写不敏感,但引号里的名字必须跟顶层模块的端口名完全一致。我在项目中遇到过好几次因为大小写不匹配导致编译报错的情况,后来就养成了习惯——所有名字都用小写,省心。
3.2 语法规则:你必须知道的几个要点
.lpf 的语法其实不复杂,但有几个坑,我当年都踩过。咱们一条条说。
3.2.1 引脚分配:LOCATE 语句
这是最常用的语句。格式是:
LOCATE COMP "端口名" SITE "引脚编号";
注意,引脚编号要用双引号括起来,比如 "P3"、"A12"、"R5" 这种。Lattice 的引脚命名规则跟芯片封装有关,你可以在数据手册或者 Lattice Diamond 的 Floor Planner 里查到。
3.2.2 电气特性:IOBUF 语句
这个用来设置引脚的电气参数。格式是:
IOBUF COMP "端口名" IO_TYPE=LVCMOS33 DRIVE=8 SLEWRATE=FAST;
常用的参数有:
| 参数 | 说明 | 常用值 |
|---|---|---|
| IO_TYPE | I/O 标准 | LVCMOS33, LVCMOS25, LVDS, SSTL 等 |
| DRIVE | 驱动能力 (mA) | 4, 8, 12, 16 (取决于 IO_TYPE) |
| SLEWRATE | 压摆率 | SLOW, FAST |
| PULLMODE | 上下拉 | UP, DOWN, NONE |
3.2.3 时钟约束:FREQUENCY 语句
这个语句用来告诉工具,某个时钟网络的频率是多少。格式是:
FREQUENCY NET "时钟名" 100 MHz;
注意,这里用的是 NET 而不是 COMP。因为时钟信号在布线之后会变成一条网络,而不是一个端口。嗯,这个细节容易搞混。
3.3 注释方式:三种方法,随你选
.lpf 支持三种注释方式,跟 C 语言有点像:
- 单行注释:用
#开头,整行都是注释 - 行内注释:在语句后面加
//,后面的内容被注释掉 - 块注释:用
/* ... */包裹多行注释
举个例子:
# 这是单行注释
LOCATE COMP "clk" SITE "P3"; // 这是行内注释
/*
这是块注释
可以写多行
比如说明这个引脚为什么这么分配
*/
我个人习惯用 # 做单行注释,因为一眼就能看出来。块注释我一般用来写版本历史或者修改记录。
3.4 .lpf 与 .sdc 的区别:各司其职
很多刚接触 Lattice 的朋友会问:.lpf 和 .sdc 到底有什么区别?能不能混着用?
我的回答是:它们分工不同,但需要配合使用。
咱们用一张图来理解:
说白了,.lpf 管的是「物理世界」——你的信号到底接在哪个引脚上,这个引脚用什么电平标准。而 .sdc 管的是「时间世界」——你的时钟频率是多少,输入输出信号跟时钟的时序关系是什么。
它们之间有一个交集:时钟约束。在 .lpf 里,你可以用 FREQUENCY 语句指定时钟频率,但这个约束只用于物理综合时的时钟网络分配。真正的时序分析,还是要靠 .sdc 里的 create_clock 来定义。
3.5 实战中的一些经验
最后,分享几个我在实际项目中积累的小经验:
- 版本管理:.lpf 文件一定要纳入版本控制。我习惯在文件头部写一段注释,记录修改日期、修改人和修改原因。
- 批量操作:如果有很多引脚需要分配,可以用
LOCATE COMP "data[*]" SITE "P[*]"这种通配符写法,但要注意引脚编号的对应关系。 - 差分信号:对于 LVDS 等差分信号,只需要分配正极 (P) 的引脚,负极 (N) 会自动分配。但如果你需要手动指定,可以用
LOCATE COMP "rx_p" SITE "A1"; LOCATE COMP "rx_n" SITE "A2";这样写。 - 未使用引脚:建议在 .lpf 里显式地处理未使用引脚,比如设置成弱上拉或者三态,避免悬空导致功耗问题。
嗯,关于 .lpf 文件的基本内容,差不多就这些了。它不像 .sdc 那么复杂,但却是 FPGA 设计里不可或缺的一环。你想想看,如果引脚分配错了,时序约束写得再好,板子也跑不起来,对吧?
公众号:蓝海资料掘金营,微信deep3321