这一篇在干嘛?

这是 PH1P 原语库两连的第 1 篇,覆盖 UG1310 的逻辑族(PH1P_LOGIC_*)里最常用的部分:缓冲、运算、RAM、乘法器和 FIFO。阅读姿势是”按功能族查”——遇到高扇出信号想插缓冲、想手搓一块 RAM 或 FIFO 时回来翻。第 2 篇讲 IO、时钟与 DSP 原语

先搞懂:原语是什么、什么时候用

全局缓冲:BUF 与 BUFG

运算单元:除法器、移位器和 SLICE

分布式 RAM:DRAM

块存储:ERAM20K

硬件乘法器:18x18 MULT

FIFO:RAMFIFO 和 FIFO

先搞懂:原语是什么、什么时候用

原语就是 FPGA 内部硬件资源在 HDL 世界里的”零件编号”。你平时写的 always @(posedge clk) 会被综合器翻译成 SLICE 里的 LUT + DFF,assign a = b; 可能被翻译成一根走线——这些”翻译”综合器做得很成熟,大多数时候你不需要手动干预。但有三类场景综合器会失手或不够聪明:

  1. 物理资源有专属走线:全局时钟缓冲 BUFG、IO 延迟 IDELAY,这些资源综合器不一定知道你想用,需要显式例化;
  2. 时序要求苛刻:低延时的 DRAM、带 Showahead 模式的 FIFO,综合器从你的 RTL 推断出来的结构可能多一级寄存器;
  3. 器件专属功能:读芯片 DNA、多启动 MBOOT、ECC 校验,这些功能根本没有对应的 RTL 写法,只能例化原语。

UG1310 覆盖 PH1P100、PH1P35、PH1P50 三个系列的器件,手册开头有一张器件支持总表——动手前先查你的型号支不支持目标原语。比如 MBOOT 只在 PH1P100 上有,MBOOT_V2 只在 PH1P35/50 上有,搞反了编译直接报错。

原语的使用方式有两种:原语例化(直接在 Verilog 里实例化模块)和 IP Generator(在 TD 软件里图形化配置生成)。每个原语的手册条目都会标明支持哪些方式。经验法则:有 IP 的优先用 IP(参数有图形界面、不易写错),只有原语的才手写例化。

全局缓冲:BUF 与 BUFG

PH1P_LOGIC_BUFG 全局时钟缓冲器结构图

这一族只有两个端口 io,但作用完全不同:

原语定位典型用途IP Generator
PH1P_LOGIC_BUF低延时布线资源关键路径上强制插一级缓冲不支持
PH1P_LOGIC_BUFG高扇出全局时钟缓冲时钟、复位、时钟使能等高扇出网络支持

BUFG 的手册描述是”延时和抖动小”,这是时钟网络的命根子。FPGA 内部有专用的全局时钟树,只有通过 BUFG 才能挂上去。如果你的设计里某时钟是从普通逻辑生成的(比如分频出来的时钟),综合器有时不会自动给它配 BUFG,时钟偏斜会大到难以收敛——这时手动例化一级 BUFG 就能解决:

PH1P_LOGIC_BUFG u_PH1P_LOGIC_BUFG (
    .i (i),
    .o (o)
);

常见坑:BUFG 是稀缺资源,不是越多越好

全局时钟网络数量有限(每个时钟区域就那么几条),把普通数据信号也挂到 BUFG 上会挤占时钟资源,布局布线时反而报”BUFG 不够用”。只给时钟、全局复位、全局使能这类真正高扇出的信号用。

运算单元:除法器、移位器和 SLICE

SEQ_DIV:会算除法的”外设”

PH1P_LOGIC_SEQ_DIV 除法器结构图

Verilog 里的 / 运算符综合出来又慢又占资源,因为除法本质是多周期迭代运算。PH1P_LOGIC_SEQ_DIV 把它做成了带握手协议的专用模块:给 start 脉冲,等 done 拉高,取走 quotient(商)和 remain(余数)。

端口方向位宽描述
clkinput1同步时钟输入
rstinput1复位信号,高有效
startinput1计算开始
numerinputNUMER_WIDTH除数
denominputDENOM_WIDTH被除数
quotientoutputNUMER_WIDTH
remainoutputDENOM_WIDTH余数
doneoutput1计算完成标志,高有效
PH1P_LOGIC_SEQ_DIV #(
    .NUMER_WIDTH(16),
    .DENOM_WIDTH(16)
) u_PH1P_LOGIC_SEQ_DIV (
    .clk(clk), .rst(rst), .start(start),
    .numer(numer), .denom(denom),
    .quotient(quotient), .remain(remain), .done(done)
);

注意手册表格里 numer 标注的是”除数”、denom 是”被除数”,与直觉相反(英文 numerator 本意是被除数)——以中文描述为准,写代码前用一个具体数字(如 100/7)仿真验证一遍商和余数。

SHIFTER:动态深度的移位寄存器

PH1P_LOGIC_SHIFTER 移位寄存器结构图

data <= {data[DATA_WIDTH-2:0], din} 强的地方在于深度可调:SHIFT_TYPE 配成 “DYNAMIC” 时,depth 端口实时控制移位深度。深度的换算公式是:

移位寄存器位宽(bit)最大深度深度属性值(depth)实际深度(shift_depth)
16330x002
16330x1F33
公式shift_depth = depth[4:0] + 2
8650x3F65
公式shift_depth = depth[5:0] + 2

SHIFT_TYPE=“FIXED” 模式下 depth 端口无效,深度由 DATA_DEPTH 参数静态决定。

PH1P_LOGIC_SHIFTER #(
    .DATA_WIDTH  (16),
    .DEPTH_WIDTH (2),
    .SHIFT_TYPE  ("FIXED")
) u_PH1P_LOGIC_SHIFTER (
    .clk(clk), .en(en),
    .datain(datain),
    .depth ({2{1'b0}}),
    .dataout(dataout)
);

SLICE:最底层的”积木”

SLICE 是 PH1P 逻辑阵列的基本单元:内部包含两个非独立的双 LUT5(可拼成 LUT6)和两个独立的 DFF,外加进位链。

PH1P_LOGIC_SLICE 基本逻辑单元结构图

平时综合器自动分配,但理解它的参数表对你看懂时序报告、做底层优化有帮助。关键端口分组:

  • 组合输入:ia/ib/ic/id/ie/imf(LUT 输入),ima/imb(组合/时序复用);
  • 时序控制:clkcea/ceb(时钟使能)、ssr(同步置位/复位)、asr(异步置位/复位);
  • 输出:oqa/oqb(时序输出)、ofa/ofb(组合输出);
  • 进位链:fci 进位输入、fco 进位输出,做加法器时逐级串联。

参数表里最有意思的是 LUTMODE(“LUT6” / “DLUT5”)和一堆 *_MUX 选择参数(CLKMUX、SSRMUX、ASRMUX 等可选 “0/1/SIG/INV”,即接地、接高、接信号、取反)。例化 SLICE 配 LUT6 输出的写法:

PH1P_LOGIC_SLICE #(
    .REGB_SD   ("LUT6"),
    .LUTMODE   ("LUT6"),
    .CLKMUX    ("SIG"),
    .CEBMUX    ("SIG"),
    .SSRMUX    ("SIG"),
    .ASRMUX    ("SIG"),
    .OFB_SD    ("LUTX"),
    .OQBMUX    ("QB")
) u_PH1P_LOGIC_SLICE (
    .imf(imf), .ie(ie), .id(id), .ic(ic), .ib(ib), .ia(ia),
    .ceb(1'b1), .clk(clk), .ssr(1'b0), .asr(1'b0),
    .oqb(oqb), .ofb(ofb)
);

常见坑:SLICE 手写例化性价比极低

SLICE 支持 IP Generator=否、参数几十个,除非你在做极特殊的底层优化(比如手工映射进位链),否则让综合器自己排布就行。理解它的意义在于看懂 Chip Viewer 里的资源占用。

分布式 RAM:DRAM

PH1P_LOGIC_DRAM 分布式 RAM 结构图

DRAM(Distributed RAM)用 LUT 阵列实现存储,特点是读写都是组合路径、延时极低,适合小容量(几十~几千 bit)、对延迟敏感的场景,比如系数表、查找表、小型状态缓存。端口分写侧和读侧两组:

端口方向位宽描述
diinputDATA_WIDTH_W写数据
waddrinputADDR_WIDTH_W写地址
wclkinput1写时钟
weinput1写使能,高有效
raddrinputADDR_WIDTH_R读地址
rdooutputDATA_WIDTH_R读数据
rclkinput1读时钟
rrstinput1读复位,可配置同步/异步
rceinput1读使能,高有效
rdoqoutputDATA_WIDTH_R读数据(带寄存器)

关键参数:READREG 选 “ENABLE” 时数据从 rdoq 输出(多一级寄存器,时序好但延迟大);RESETMODE 选 “SYNC”/“ASYNC”;INIT_FILE 可指定初始化文件、FILL_ALL 可把未初始化空间填成固定图案(如 “0101”)。

PH1P_LOGIC_DRAM #(
    .DATA_WIDTH_W (9),   .ADDR_WIDTH_W (10),  .DATA_DEPTH_W (1024),
    .DATA_WIDTH_R (9),   .ADDR_WIDTH_R (10),  .DATA_DEPTH_R (1024),
    .INIT_FILE ("NONE"), .FILL_ALL ("NONE"),
    .READREG ("DISABLE"), .RESETMODE ("SYNC")
) u_PH1P_LOGIC_DRAM (
    .di(di), .waddr(waddr), .we(we), .wclk(wclk),
    .raddr(raddr), .rclk(rclk), .rrst(rrst), .rce(rce),
    .rdo(rdo), .rdoq(rdoq)
);

选型口诀:容量小、要快 → DRAM;容量大(KB 级以上)→ ERAM

块存储:ERAM20K

PH1P_LOGIC_ERAM 块 RAM 结构图

ERAM 是芯片里真实的 20Kbit 存储硬核,一个 ERAM20K 支持四种工作模式(MODE 参数):

MODE含义
”SP”单口 RAM
”DP”双口 RAM(A/B 两个口,默认)
“PDPW”伪双口(A 口只写、B 口只读)
“FIFO”当 FIFO 硬核用

双口模式下 A、B 各有一整套端口:dia/addra/cea/ocea/clka/wea/rsta/beadib/.../beb 对称,bea/beb 是字节使能(BYTE_ENABLE 参数配 0/8/10)。此外还带 ECC 校验端口(ecc_sbiterr/ecc_dbiterr 及注错端口)。

写模式(WRITEMODE_A/B)有三个选项,处理”写的时候读同地址”的行为:

  • “NORMAL”:写时读出的是旧数据或不定(默认);
  • “READBEFOREWRITE”:先输出旧数据再写入;
  • “WRITETHROUGH”:写的同时新数据直接透传到读出口。
PH1P_LOGIC_ERAM #(
    .DATA_WIDTH_A(16), .DATA_WIDTH_B(16),
    .ADDR_WIDTH_A(11), .ADDR_WIDTH_B(11),
    .DATA_DEPTH_A(2048), .DATA_DEPTH_B(2048),
    .MODE("PDPW"),
    .REGMODE_A("NOREG"), .REGMODE_B("NOREG"),
    .WRITEMODE_A("NORMAL"), .WRITEMODE_B("NORMAL"),
    .IMPLEMENT("20K"),
    .ECC_ENCODE("DISABLE"), .ECC_DECODE("DISABLE"),
    .CLKMODE("ASYNC"), .SSROVERCE("DISABLE"),
    .OREGSET_A("SET"), .OREGSET_B("SET"),
    .RESETMODE_A("ASYNC"), .RESETMODE_B("ASYNC"),
    .ASYNC_RESET_RELEASE_A("SYNC"), .ASYNC_RESET_RELEASE_B("SYNC"),
    .INIT_FILE("NONE"), .FILL_ALL("NONE")
) u_PH1P_LOGIC_ERAM (
    .dia(dia), .dib(dib), .addra(addra), .addrb(addrb),
    .cea(cea), .ceb(ceb), .ocea(ocea), .oceb(oceb),
    .clka(clka), .clkb(clkb),
    .wea(wea), .web(web), .bea(bea), .beb(beb),
    .rsta(rsta), .rstb(rstb),
    .ecc_sbiterr(ecc_sbiterr), .ecc_dbiterr(ecc_dbiterr),
    .ecc_sbiterrinj(ecc_sbiterrinj), .ecc_dbiterrinj(ecc_dbiterrinj),
    .doa(doa), .dob(dob)
);

常见坑:输出不带寄存器时不能用同步复位

手册特别注明:REGMODE 选 “NOREG”(输出不带寄存器)时,RESETMODE_A/B 只能选 “ASYNC”;选 “OUTREG” 后同步/异步均可。配错了工具会报参数错误。

硬件乘法器:18x18 MULT

PH1P_LOGIC_MULT 18x18 硬件乘法器结构图

PH1P_LOGIC_MULT 是 18x18 的硬件乘法器,A/B 输入各 18bit,输出 36bit。它与 DSP 大模块的区别:MULT 是轻量版(只能做纯乘法),DSP(见第 2 篇)是带预加器、三输入加法器、逻辑单元的完整运算链。

端口分三组寄存器,各自有独立的片选和复位:A 口(cea/rstan)、B 口(ceb/rstbn)、乘积输出(cepd/rstpdn)。注意复位都是低有效(名字里的 n)。

参数里的门道:

  • INPUTFORMAT:“SIGNED”/“UNSIGNED”,决定乘法语义;
  • INPUTREGA/INPUTREGB/OUTPUTREG:三级流水各配 “ENABLE”/“DISABLE”,全开是 3 级流水、频率最高,全关是纯组合乘法、延迟最小;
  • IMPLEMENT:“AUTO” 让工具选,“DSP” 强制用 DSP 硬核,“GATE” 用 LUT 搭(仅在 DSP 资源耗尽时考虑)。
PH1P_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_PH1P_LOGIC_MULT (
    .p(p), .a(a), .b(b),
    .cea(cea), .ceb(ceb), .cepd(cepd), .clk(clk),
    .rstan(rstan), .rstbn(rstbn), .rstpdn(rstpdn)
);

FIFO:RAMFIFO 和 FIFO

PH1P_LOGIC_FIFO 控制器结构图

PH1P 有两个 FIFO 原语,选型是本篇的重点:

对比项PH1P_LOGIC_RAMFIFOPH1P_LOGIC_FIFO
本质软核 FIFO 控制器硬核 FIFO 控制器
时钟模式仅同步(单时钟)同步/异步(CLKMODE)
位宽DATA_WIDTH 任意1~320
深度位宽ADDR_WIDTH 任意9~14(大深度)
特色实时深度指示 + Showahead 模式几乎空/满标志、ECC、读指针输出

RAMFIFO 的独门本事是 Showahead 模式(SHOWAHEAD=“1”):数据先放到输出口等着,re 拉高表示”这个数据我拿走了”,省去一拍读延迟,做流式解析协议时非常好用。它还输出 rdusedw/wrusedw(可读/已写个数),可以实时掌握队列水位:

PH1P_LOGIC_RAMFIFO #(
    .DATA_WIDTH(8), .ADDR_WIDTH(8),
    .SHOWAHEAD("0"), .IMPLEMENT("20K")
) u_PH1P_LOGIC_RAMFIFO (
    .rst(rst), .di(di), .clk(clk), .we(we), .dout(dout),
    .re(re),
    .empty_flag(empty_flag), .full_flag(full_flag),
    .rdusedw(rdusedw), .wrusedw(wrusedw)
);

PH1P_LOGIC_FIFO 则是跨时钟域场景的正解:CLKMODE=“ASYNC” 时读写两侧各自独立时钟/复位(clkw/wrst 与 clkr/rrst),标志信号各自同步到本侧时钟(full_flag 同步于 clkw、empty_flag 同步于 clkr)。afull_flag/aempty_flag 是可编程的”几乎满/空”水位报警(AE/AF 参数设定阈值),提前留出反应时间避免真满真空。

PH1P_LOGIC_FIFO #(
    .AE(AE), .AF(AF),
    .DATA_WIDTH(9), .ADDR_WIDTH(10),
    .CLKMODE("SYNC"), .REGMODE_R("NOREG"),
    .ECC_ENCODE("DISABLE"), .ECC_DECODE("DISABLE"),
    .SSROVERCE("DISABLE"), .FIRSTWRITE_RD("DISABLE"),
    .ASYNC_RESET_RELEASE("SYNC")
) u_PH1P_LOGIC_FIFO (
    .clkw(clkw), .wrst(wrst), .we(we), .owe(owe), .csw(csw), .di(di),
    .clkr(clkr), .rrst(rrst), .re(re), .ore(ore), .csr(csr),
    .dout(dout),
    .fifo_wrpointer(fifo_wrpointer), .fifo_rdpointer(fifo_rdpointer),
    .empty_flag(empty_flag), .aempty_flag(aempty_flag),
    .full_flag(full_flag), .afull_flag(afull_flag),
    .ecc_sbiterrinj(ecc_sbiterrinj), .ecc_dbiterrinj(ecc_dbiterrinj),
    .ecc_sbiterr(ecc_sbiterr), .ecc_dbiterr(ecc_dbiterr)
);

选型口诀:同步小队列 → RAMFIFO(要 Showahead/水位就用它);跨时钟域或大深度 → FIFO

通关标准:

① 能不看手册说出 BUFG 的两个端口和用途,并解释为什么高扇出信号要挂 BUFG; ② 能根据”容量、时钟域数、是否要 Showahead”三个条件在 DRAM/ERAM/RAMFIFO/FIFO 里做出正确选型; ③ 能正确例化一个 SEQ_DIV,并解释 numer/denom 的真实含义与 done 握手时序。