这一篇在干嘛?
这是 UG910《PH1A 系列 FPGA Libraries Guide for HDL Designs》精读的第一篇,讲时钟和运算两大族原语。原语是芯片里真实存在的硬件单元,例化一个原语就是”占用”一个硬件资源——所以要先搞清它是什么、什么时候用、参数怎么填。IO、存储与器件管理原语见 原语库 PH1A(2),目录页在 原语库 PH1A。
原语怎么用:例化模板与两种支持方式
每个原语在手册里都按固定五件套介绍:简介、端口描述、参数描述、使用方式、Verilog 例化示例。例化的写法和普通 module 一样,只是模块名换成原语名,参数用 #(...) 传:
PH1_LOGIC_BUFG u_BUFG (
.i (clk_in),
.o (clk_out)
);手册里每个原语都有一个”使用方式”表,说明两种支持方式:
| 使用方式 | 含义 |
|---|---|
| 原语例化 | 直接在 Verilog 里例化原语模块 |
| IP Generator | 在 TD 的 IP 界面里图形化配置生成 |
注意:不是所有原语都支持 IP Generator(如 BUFG、GCLK、LCLK、OSCDIV、SLICE、MULT 都只支持原语例化),这些只能手写例化。参数取值多为字符串枚举(如 "ENABLE"/"DISABLE"),照抄手册别自由发挥。
常见坑:手册例化代码是 OCR 扫描件
MinerU 解析出的例化代码常有空格错乱(如
. c/k(clk)、u_ LOGIC_BUFG)。照抄时务必自己整理成合法 Verilog:端口名无空格、.与端口名紧挨、逗号齐全,否则综合直接报语法错。
全局缓冲 BUFG:时钟和高扇出信号的专属高速路
PH1_LOGIC_BUF 是 FPGA 内部的低延时布线资源;PH1_LOGIC_BUFG 则是高扇出的全局时钟缓冲器,延时和抖动小。手册明确:BUFG 常用于时钟网络和其他高扇出网络(如复位信号、时钟使能),且可以将普通信号走线到 BUFG 上。

两者端口都只有 i/o 各 1 位,无参数。什么时候该用?判断标准是扇出和时序要求:
- 时钟信号 → 必须(否则时钟树偏斜大,时序难以收敛);
- 全局复位、全局时钟使能这类扇出几百上千的信号 → 强烈建议;
- 普通数据信号 → 不用,浪费全局布线资源。
常见坑:所有信号都往 BUFG 上挂
全局时钟网络数量有限,普通低扇出信号走 BUFG 既占资源也不带来任何好处。只有”几乎每个触发器都要用”的信号才配得上 BUFG。
GCLK:两路全局时钟的无毛刺切换
PH1_PHY_GCLK_V2 是全局时钟缓冲选择器:从两路输入时钟 clkin[1:0] 里选一路输出到 clkout,并且可以控制切换过程是否产生毛刺。

端口与控制逻辑:
| 端口 | 位宽 | 作用 |
|---|---|---|
| clkin | 2 | 两路输入时钟 |
| seln | 2 | 时钟选择,低电平有效 |
| cen | 2 | 寄存器时钟使能,低电平有效 |
| drct | 2 | 无毛刺切换使能:2'b00 无毛刺模式(切换会丢失一个下降沿);2'b11 普通模式(切换可能出毛刺) |
| clkout | 1 | 输出时钟 |
输出选择真值表( cen/seln 组合决定输出哪一路):
| cen[0] | seln[0] | cen[1] | seln[1] | clkout |
|---|---|---|---|---|
| 0 | 0 | 1 | X | clkin[0] |
| 0 | 0 | X | 1 | clkin[0] |
| 1 | X | 0 | 0 | clkin[1] |
| X | 1 | 0 | 0 | clkin[1] |
参数:PRESELECT(初始输出选 CLK0 还是 CLK1,默认 CLK0)、HOLD(无有效输入时输出保持高/低电平,默认 YES)、SEL_TRIGGER(采样边沿 POS/NEG)、GSR(全局复位使能)。
手册给了 4 条硬性注意事项,逐条记下:
- 两路输入中任何一路为恒定高电平,GCLK 无法正常工作,必须强迫一路输入为低;
- 不建议 seln 和 cen 同时使能,可能导致模块无法正常工作;
drct=2'b00(无毛刺模式)时,输入若是电平信号模块将无法工作——无毛刺模式不支持电平输入;drct=2'b11时,若 seln 输入2'b00,模块无法工作,输出未知。
常见坑:指望"无毛刺"模式一粒毛刺都没有
无毛刺模式只是保证切换点不输出残缺脉冲,代价是输出会丢失一个下降沿。对切换期间的频率连续性有要求的系统要把这个丢失的周期算进协议里。
区域时钟 MLCLK、SCLK、IOCLK
PH1A 的时钟网络是分层的,三种区域时钟原语各管一层:
| 原语 | 角色 | 输入来源 | 输出去向 |
|---|---|---|---|
| PH1_PHY_MLCLK_V2 | 多区域时钟模块 | 本地全局时钟输入管脚、本地 PLL 输出 | 上下相邻两个时钟区域、IOCLK、LCLK |
| PH1_PHY_SCLK_V2 | GCLK 的分支时钟 | 全局时钟网络 | 内部逻辑资源和 IP 模块 |
| PH1_PHY_IOCLK | IO 列独立时钟网络 | 本区域/相邻区域 MLCLK、本区域 PLL、全局时钟输入管脚 | 本区域所有 IO 块,支持高速串并转换(如 LVDS 接收) |



三者端口都极简(clkin、ce 可选、clkout)。参数方面:MLCLK_V2 有 BYPASS(输入直通输出)和 GSR;SCLK_V2 在 BYPASS/GSR 之外多一个 DELAY(0~3 级延迟),手册注明单独调节 SCLK 延迟需要与 PH1_LOGIC_BUFG 配合使用。HP IO 区域另有 PH1_PHY_HP_MLCLK 和 PH1_PHY_HP_IOCLK 变体,端口一致;其 MODE 参数(“HPS”/“HPD”)手册注明无实际用途,例化时可忽略。
V2 与老版本的关系:MLCLK_V2、GCLK_V2 相比无 V2 版本均增加了 GSR(全局复位使能)参数;SCLK_V2 增加了延迟参数。
常见坑:时钟区域想跨就跨
IOCLK 只能驱动本区域的 IO 块,MLCLK 只能覆盖上下相邻两个时钟区域。布局约束前先想清楚时钟源和消费者在哪个区域,跨区域走时钟要靠 MLCLK 级联,不能想当然直连。
分频原语 LCLK 与 OSCDIV
LCLK:IO 分组上的外部时钟分频器
PH1_PHY_LCLK_V2 是 IO 上的外部时钟分频器,每个 IO 分组有 4 个,分频输出 clkdivout 可供全局时钟网使用,驱动逻辑资源和 IP 模块资源。

关键参数:
| 参数 | 取值 | 说明 |
|---|---|---|
| DIV | 1~8, 35 | 分频系数,支持 1-8 分频和 3.5 分频 |
| LCLK | ”BYPASS”/“DIV2T08”/“DIV1”/“GND” | clkout 输出模式:BYPASS 不分频直接输出;DIV2T08 输出 clkdivout;DIV1 与原时钟相同且占空比接近 50%;GND 输出低电平 |
| CEMD | ”SYNC”/“ASYNC” | SYNC 时 ce 信号延迟 4 个时钟周期传入内部 DFF |
| CLKSEL | ”ORG”/“INV” | INV 表示输入时钟取反 |
手册特别提醒:想让 clkout 和分频输出一致时,DIV=1 要配 LCLK=“DIV1”,DIV=2~8 要配 LCLK=“DIV2TO8”,两个参数是联动的,只改一个必然出错。
OSCDIV:内部振荡器分频出”开机时钟”
PH1A 内部有一个 OSC 振荡器(中心频率 266MHz),PH1_PHY_OSCDIV 把它分频输出,不依赖任何外部时钟就能产生时钟——上电初始化逻辑、看门狗类电路的好帮手。

端口:rstn(低有效复位)、stdby(1 时暂停输出,0 时正常输出)、osc_clk。参数:DIV(1~128,默认 128)、EXT_RSTN。
常见坑:把 OSC 时钟当高精度时钟用
内部 OSC 是 RC 振荡器,频率精度远低于晶振,266MHz 只是中心频率。它可以用来”让逻辑先跑起来”,但凡涉及协议波特率、采样率的场景必须用晶振+PLL。
PLL:倍频、分频、相移一把抓
PH1_PHY_PLL 是 FPGA 内部锁相环,功能:输出时钟频率调整、占空比调整、相位调整、动态相位移动,还支持小数分频和展频(SSC)。

端口按功能分组看,不然 30 多个端口根本记不住:
| 分组 | 端口 | 说明 |
|---|---|---|
| 基本时钟 | refclk、fbclk、pllpd、pllreset、lock | 参考时钟、反馈时钟、电源开关(高电平关断)、复位(高有效)、锁定指示(高有效) |
| 输出时钟 | clkc[7:0]、clkcb[7:0]、clkc_en[7:0]、clkc_rst[1:0] | 8 路输出及反相输出、使能、复位(clkc_rst[0] 管 0 |
| 动态重配置 | drp_clk/rstn/sel/rd/wr/addr/wdata/rdy/err/rdata | 运行时改 PLL 配置的寄存器接口 |
| 动态相移 | psclk、psclksel[2:0]、psstep、psdown、psdone | 选择某路输出做相移,psstep 上升沿触发 |
| 展频 | ssc_en、ext_freq_mod_* | SSC 使能(开启时固定接 1)与外部调制频率 |
参数更多达上百个,常用核心:REFCLK_DIV(1-128 输入分频)、FBCLK_DIV(1-128 反馈分频)、FRAC_ENABLE+SDM_FRAC(0-4095 小数分频系数)、PLL_FEED_TYPE(INTERNAL/EXTERNAL 反馈模式)、SSC_ENABLE/SSC_MODE(展频)、各路 CLKCn_ENABLE。带宽由 LPF_RES/LPF_CAP/ICP_CUR/GMC_GAIN 四个参数共同决定。
使用限制(手册原文要点):切断输入时钟时,PLL 的 VCO 无法完全关闭,会存在最低震荡频率,通道输出频率由每通道分频系数和具体芯片决定。
常见坑:手写 PLL 例化参数
PLL 参数体系庞大,手册也支持 IP Generator 方式。除非必须用原语例化(如参数动态重配置场景),日常倍频分频需求建议走 TD 的 PLL IP 图形界面,频率计算和带宽参数由工具帮你算,不容易错。
运算原语速查:除法器、移位器、SLICE、乘法器
SEQ_DIV 软核除法器
PH1_LOGIC_SEQ_DIV 是软件集成的软核除法器,多周期握手协议:
PH1_LOGIC_SEQ_DIV #(
.NUMER_WIDTH (16),
.DENOM_WIDTH (16)
) u_SEQ_DIV (
.clk (clk),
.rst (rst),
.start (start),
.numer (numer),
.denom (denom),
.quotient (quotient),
.remain (remain),
.done (done)
);时序协议:rst=1 时 quotient/remain 清零、done 置 1;start=1 时输入缓存进内部寄存器,start 由 1 变 0 时才开始计算;done=1 时 quotient/remain 才是最终结果。参数 NUMER_WIDTH/DENOM_WIDTH 均默认 16。手册提醒:start 为 0 期间改 numer/denom 不影响计算,但为了波形可读,请在 done=1 之后再给下一组输入。
SHIFTER 移位寄存器
PH1_LOGIC_SHIFTER 支持固定/可变两种深度模式,但PH1A 器件只支持 “FIXED” 模式(“VARIABLE” 不可用):
PH1_LOGIC_SHIFTER #(
.DATA_WIDTH (16),
.DEPTH_WIDTH (2),
.SHIFT_TYPE ("FIXED")
) u_SHIFTER (
.clk (clk),
.en (en),
.datain (datain),
.depth ({2{1'b0}}),
.dataout (dataout)
);FIXED 模式下 depth 端口无用,深度由 DATA_DEPTH 参数决定(默认 32)。深度换算有公式:16 位宽时最大深度 33,shift_depth = depth[4:0] + 2;8 位宽时最大深度 65,shift_depth = depth[5:0] + 2。还支持 INIT_FILE 预加载初始值。
SLICE:最底层的逻辑单元
PH1_LOGIC_SLICE 是 FPGA 内部 SLICE 模块的端口,内部含两个非独立的双 LUT5 和两个独立的 DFF,另有进位链(fci/fco)。参数 20 多个,核心是 LUTMODE(“LUT6”/“DLUT5”)、各 LUT 初始值 INIT_LUT5A/B、各种 MUX 选择。它支持原语例化、不支持 IP Generator。
正常写 Verilog 时综合器会自动帮你映射 SLICE,不需要手动例化。需要手动例化的场景:要精确控制进位链、复用 LUT/寄存器输出等底层优化。手册给出了配置为 LUT6 输出的示例(寄存器输出 oqb + 组合输出 ofb 同时引出)。
MULT 乘法器与 DSP 家族速览
PH1_LOGIC_MULT 是 FPGA 内部乘法器(支持 IP Generator 的只有它,DSP 系列原语例外)。核心参数:
| 参数 | 默认 | 说明 |
|---|---|---|
| INPUT_WIDTH_A/B | 18/18 | 两个乘数位宽 |
| OUTPUT_WIDTH | 36 | 乘积位宽 |
| INPUTFORMAT | ”SIGNED” | 有符号/无符号 |
| INPUTREGA/B、OUTPUTREG | ”ENABLE” | 输入/输出寄存器,加寄存器输出延迟一拍 |
| SRMODE | ”ASYNC” | 复位置位模式 |
| IMPLEMENT | ”AUTO” | AUTO/DSP/GATE 实现方式 |
PH1_LOGIC_MULT #(
.INPUT_WIDTH_A(18), .INPUT_WIDTH_B(18), .OUTPUT_WIDTH(36),
.INPUTFORMAT("SIGNED"), .INPUTREGA("ENABLE"),
.INPUTREGB("ENABLE"), .OUTPUTREG("ENABLE"),
.SRMODE("ASYNC"), .IMPLEMENT("AUTO")
) u_MULT (
.p(p), .a(a), .b(b),
.cea(cea), .ceb(ceb), .cepd(cepd), .clk(clk),
.rstan(rstan), .rstbn(rstbn), .rstpdn(rstpdn)
);注意三个复位都是低有效(rstan/rstbn/rstpdn)。此外 DSP 硬核还有更细粒度的原语族:PH1_PHY_DSPADD(27 位加减法)、PH1_PHY_DSPMULT(27×18 乘法)、PH1_PHY_DSPLOGIC(54 位逻辑运算)、PH1_PHY_DSPREG/DSPMREG(DSP 寄存器)、PH1_PHY_DSPTADD(54 位三输入加减法器,opctrl[3:0] 选择 x/y/z 的符号组合)——与 MULT 不同,DSP 家族同时支持原语例化和 IP Generator,属于流水线级拼装积木,竞赛里用 MULT 或让综合器推断即可。
通关标准:
- 能说出 BUFG 与 BUF 的区别,并解释为什么时钟和全局复位必须上 BUFG;
- 能照抄模板例化 SEQ_DIV,并描述 start/done 握手时序;
- 能解释 GCLK 无毛刺切换的代价(丢失一个下降沿)以及 LCLK 的 DIV 与 LCLK 参数联动规则。
start 拉高期间改 numer,除法器会算错吗?
不会。start=1 时输入被缓存进内部寄存器,start 由 1 变 0 时计算开始,此后改 numer 不影响本次计算。但为了波形可读性,手册建议 done=1 之后再提供下一组输入。
GCLK 的 drct 配成 2'b00 和 2'b11 有什么区别?
2’b00 是无毛刺切换模式:切换过程不出毛刺,但输出会丢失一个下降沿,且不支持电平输入;2’b11 是普通模式:切换可能产生毛刺,且 seln=2’b00 时模块无法工作、输出未知。
SHIFTER 想要可变深度移位,PH1A 上能实现吗?
不能直接用。PH1A 器件的 PH1_LOGIC_SHIFTER 只支持 “FIXED” 模式,深度由 DATA_DEPTH 参数固定;“VARIABLE” 模式不受支持。可变深度需要自己用 RAM 或寄存器链实现。
OSCDIV 输出的 266MHz/128 时钟能用来跑 UART 吗?
不建议。内部 OSC 是 RC 振荡器,中心频率 266MHz 但精度有限,UART 对波特率误差敏感(一般要求 <2%)。OSCDIV 适合上电初始化、看门狗等对频率精度不敏感的场景,通信时钟应走晶振+PLL。
为什么 MULT 的复位端口叫 rstan 而不是 rsta?
结尾的 n 表示低电平有效(negative)。MULT 的 A 口复位 rstan、B 口复位 rstbn、乘积输出复位 rstpdn 全部低有效,接高电平复位信号时要取反或选 SRMODE 相应配置。