这一篇在干嘛?
UG1211《DR1 系列 FPSoC Libraries Guide》前半部分的精华。原语(Primitive)是 FPGA 内部最底层的硬件单元,综合器平时会”悄悄”帮你推断,但涉及到 IO 时序、高速接口、存储器精细控制时,就必须手动例化。这一篇覆盖基础逻辑、IO 串并转换和存储三大族;时钟与 DSP 族见原语库:DR1(二)。
布线与基础逻辑:BUF、BUFG、SEQ_DIV、SHIFTER、SLICE
IO 串并转换:IDDR / ODDR / x2 / x2I 全家桶
原语是什么、怎么读这一族手册
原语就是芯片厂留给你的”裸硬件接口”。写 Verilog 时你例化一个 DR1_LOGIC_BUFG,综合器不会把它优化掉,而是直接映射到芯片里真实的一个全局时钟缓冲单元。这和写 assign o = i; 让工具自己安排是完全不同的掌控级别。
UG1211 里每个原语都按固定五段式介绍:简介 → 端口描述 → 参数描述 → 使用方式 → Verilog 例化示例。读任何原语都建议按这个顺序扫一遍:
- 简介:一句话说清它是什么硬件。先看这句,判断”我要不要用它”。
- 端口描述:Name / Direction / Width / Function 四列表格。注意 Direction 里有 inout 的(如 IOTRIBUF 的 io)。
- 参数描述:Attribute / Allowed Values / Default / Descriptions。默认值往往就是你该用的值。
- 使用方式:这张表告诉你两种路径——“原语例化”所有原语都支持;“IP Generator”只有存储、PLL、ADC 等大块头支持。像
DR1_LOGIC_SEQ_DIV、DR1_LOGIC_SHIFTER这类,图形界面里是找不到的,只能手写例化。 - 例化示例:直接抄,改信号名即可。
常见坑:手册里的原语名以
DR1_开头,这是 DR1 系列专属前缀。不同系列(PH1A 的PH1A_系列、PH1 系列各自不同)原语名不通用,从别的工程抄例化代码时先确认系列,否则综合直接报错。
布线与基础逻辑:BUF、BUFG、SEQ_DIV、SHIFTER、SLICE
BUF 与 BUFG:一根线的两种走法
DR1_LOGIC_BUF 是 PL 内部的低延时布线资源,就两个端口 i/o,没参数。它对应的是普通局部缓冲,适合短距离信号。
DR1_LOGIC_BUFG 则是高扇出全局时钟缓冲器,延时和抖动小,官方明确说它用于”时钟网络和其他高扇出的网路(如复位信号、时钟使能等)“,而且可以把普通信号走到 BUFG 上。一个复位信号要扇出到全片几万个触发器时,走普通布线会到处都是长路径,插一个 BUFG 让它走全局时钟树是标准操作:
DR1_LOGIC_BUFG u_LOGIC_BUFG (
.o (o),
.i (i)
);
什么时候需要手动插 BUFG?大多数时候综合器会把”被当成时钟用的信号”自动放上全局网络;但复位、时钟使能这类非时钟的高扇出信号,以及内部逻辑生成的时钟(比如分频时钟),工具不一定猜得到你的意图,手动例化最保险。
SEQ_DIV:软件集成的软核除法器
DR1_LOGIC_SEQ_DIV 是一个多周期时序除法器,不占 DSP 资源,靠 LUT 一拍一拍算。它的握手协议要记牢:
| 端口 | 方向 | 位宽 | 功能 |
|---|---|---|---|
| clk / rst | input | 1 | 时钟;复位(rst=1 时 quotient/remain 置 0,done 置 1) |
| start | input | 1 | start=1 时缓存输入;start 由 1 变 0 时开始计算 |
| numer / denom | input | NUMER_WIDTH / DENOM_WIDTH | 被除数 / 除数 |
| quotient / remain | output | 同上 | 商 / 余数,done=1 时才有效 |
| done | output | 1 | 计算完成标志 |
参数只有两个:NUMER_WIDTH、DENOM_WIDTH,默认都是 16。注意它的使用方式表里 IP Generator 是”否”——只能在代码里例化。除法在 FPGA 里是出了名的慢操作,如果你的算法里只有零星几次除法(比如求平均值后归一化),用这个软核除法器省事;如果是每拍都要除的流水线,考虑改定点数或查表。
SHIFTER:可变深度移位寄存器
DR1_LOGIC_SHIFTER 是内部移位寄存器模块,本质是”SRL(移位查找表)“的官方封装。核心参数:
| 参数 | 可选值 | 默认 | 说明 |
|---|---|---|---|
| DATA_WIDTH | >0 | 16 | 数据位宽 |
| DEPTH_WIDTH | >1 | 2 | 深度控制字位宽 |
| SHIFT_TYPE | ”FIXED” / “VARIABLE" | "FIXED” | 固定深度 / 动态深度 |
| DATA_DEPTH | — | 32 | FIXED 模式下的实际深度 |
| INIT_FILE | 路径或 “NONE" | "NONE” | 预加载初始值文件 |

两种模式要分清:
- FIXED 模式:深度在编译期由
DATA_DEPTH定死,depth端口是无效信号(例化时接{0{1'b0}})。 - VARIABLE 模式:移位深度为 2**DEPTH_WIDTH 量级,
depth端口是运行时的真实深度控制字。手册的属性表给了换算公式:16 位宽时shift_depth = depth[4:0] + 2(深度 233),8 位宽时65)。shift_depth = depth[5:0] + 2(深度 2
典型用途:视频行缓冲、延迟线匹配、串行协议里的对齐延迟。用 depth 动态可调这一点,写一个”可编程延迟线”就不用自己手搓寄存器链了。
SLICE:最底层的逻辑单元
DR1_LOGIC_SLICE 是内部 SLICE 模块的端口级封装:两个非独立的双 LUT5 + 两个独立 DFF,还带进位链(fci/fco)。除非你在做极度的手动面积/时序优化,一般不需要例化它——综合器对你写的组合/时序逻辑会自动映射到 SLICE。但它值得看,因为参数表就是一本”SLICE 能做什么”的说明书:LUTMODE 可选 “LUT6”/“DLUT5”(一个 SLICE 可以拼成 6 输入 LUT 或两个 5 输入 LUT)、LATCHMODE 可选 “DFF”/“LATCH”、进位链支持 ADDERMUX 配置,甚至 INIT_LUT5A/B 可以直接指定 LUT 的 32 位真值表。看到这里你就明白为什么”两个 LUT5 非独立”——某些输入是共享复用的(LUT5A_SD0 等参数就在选输入来源)。
IO 串并转换:IDDR / ODDR / x2 / x2I 全家桶
这一族解决同一个问题:芯片引脚上是 DDR(双沿)数据,内部逻辑只跑单沿时钟,中间必须有个转换器。FPGA 的 IO 单元里专门为此放了硬件。
IDDR / ODDR:1:2 与 2:1

DR1_LOGIC_IDDR 把 IO 上的双沿采样数据变成两路单沿数据:d 在时钟上升沿的值走 q0,下降沿的值走 q1。参数两个:
ASYNCRST:“ENABLE”/“DISABLE”,控制 rst 是异步复位还是被忽略;PIPEMODE:“PIPED”/“NONE”,q0/q1 的数据对齐模式,默认 “PIPED”(打一拍对齐后再出,时序更好)。
DR1_LOGIC_ODDR 反过来,把内部两路单沿数据 d0(上升沿)、d1(下降沿)合成一路 DDR 输出 q,只有 ASYNCRST 一个参数。输出 DDR 源同步接口(比如往 Camera 输出带时钟的数据)必用。
DR1_LOGIC_ODDR #(
.ASYNCRST("ENABLE")
) u_LOGIC_ODDR (
.q(q), .d1(d1), .d0(d0), .clk(clk), .rst(rst)
);x2 / x2I:速度不够,串并来凑
DR1_LOGIC_IDDRx2 是 1:4 串转并单元:串行输入 d 被 sclk/pclk 两相时钟拆成 q0q3 四路并行输出。d3 四路并行按先低 bit 后高 bit 的顺序串行输出到 DR1_LOGIC_ODDRx2 则是 4:1 并转串:d0q。它们与 IDDR/ODDR 功能同源,但支持更高的 IO 速度——原因也很直觉:IO 触发器只跑半速率的 sclk,靠两相时钟交替采样,内部时序压力小一半。

时钟接法要特别注意端口表里的约定:pclk 频率为 sclk 的 1/2,sclk 为 1/2 数据率。也就是说 800 Mbps 的数据率对应 sclk=200MHz、pclk=100MHz,两个时钟必须严格同源同相,通常都从同一个 PLL 的不同输出通道拿。
x2I 是省时钟的变体:DR1_LOGIC_IDDRx2I / DR1_LOGIC_ODDRx2I 与 x2 功能相同,但”直接使用内部 sclk 的 2 分频作为 pclk,节省 1 个 clk”——少占一个全局时钟资源,代价是少一个 pclk 端口(因此也少一次外部对齐的机会)。时钟资源紧张的高速设计优先考虑 x2I 版本。
常见坑:x2/x2I 的 rst 是 高有效复位,而很多同学的习惯是低有效 rst_n。直接把 rst_n 接上去会导致芯片一上电"永远处于复位",现象是 IO 数据全为初始值且不来数据。接反之前先看一眼端口表 Function 列。
IO 辅助三件套:动态延迟、三态、IOBUF
DR1_LOGIC_DYNAMIC_IDELAY:IO 输入端口上的动态延迟调节模块。端口in_dly[4:0]可设 0~31 级延迟,indly_en拉高使能动态调节。源同步接口(如 LVDS 接收)里采样时钟和数据skew不匹配时,靠它微调数据眼图位置,比改 PCB 走线便宜得多。手册例化示例就是一行.in_dly(5'd28)——28 级延迟。DR1_LOGIC_IOTRIBUF:三态 IO 控制模块。t=0时 io 输出 i,t=1时 io 呈高阻(变输入,o 可以读回)。任何双向总线(如外挂 SRAM/并口 ADC 的数据线)都靠它。注意 1.1 版手册特别更新了三态控制信号的说明——t 的语义是”1 为高阻”,和某些厂家的 OE(输出使能)正好相反。DR1_LOGIC_IOBUF:输入输出控制模块,就 i/o 两个端口,是最简单的 IO 路径封装,通常在需要明确约束 IO 时序路径或手工控制 IO 单元行为时使用。
这三个原语都只支持原语例化,IP Generator 里没有。
存储三兄弟:DRAM、ERAM、FIFO/RAMFIFO
DRAM:分布式 RAM
DR1_LOGIC_DRAM 是内部的分布式 RAM 模块(用 LUT 资源实现),可配置为单口 RAM。它天生”写一口、读一口”分离(waddr/wclk/we 写口,raddr/rclk/rce/rdo 读口),小容量、深度浅的存储(系数表、小缓冲)用它最省 BRAM 资源。关键参数:
| 参数 | 默认 | 说明 |
|---|---|---|
| DATA_WIDTH_W / ADDR_WIDTH_W | 8 / 6 | 写侧位宽 / 地址位宽 |
| DATA_WIDTH_R / ADDR_WIDTH_R | 8 / 6 | 读侧位宽 / 地址位宽 |
| INIT_FILE | ”NONE” | 预加载初始值文件(.mi) |
| FILL_ALL | ”NONE” | 未初始化空间填充(二进制串如 “0101”) |
| READREG | ”DISABLE” | 读寄存器使能(使能后 rdoq 多一拍) |
| RESETMODE | ”SYNC” | 读侧复位同步/异步 |
注意它有 rdo(组合读)和 rdoq(带寄存器读)两个输出口:READREG=“DISABLE” 时用 rdo,地址一给数据就出,适合”真单口”行为;ENABLE 时数据打一拍从 rdoq 出,时序更容易收敛。
ERAM:20K 大块头
DR1_LOGIC_ERAM 是内部 ERAM20K 资源,DR1 片上块 RAM 的正式名字。端口是完整的双口结构(A 口、B 口各自有 dia/addra/cea/ocea/clka/wea/rsta/bea 与 doa),参数表非常长,核心是这几个:
| 参数 | 可选值 | 说明 |
|---|---|---|
| MODE | ”DP”/“SP”/“PDPW”/“FIFO” | 真双口 / 单口 / 简单双口(一写一读)/ FIFO 模式 |
| DATA_WIDTH_A/B | >0(默认 20) | 两口数据位宽,支持不对称位宽 |
| WRITEMODE_A/B | ”NORMAL”/“READBEFOREWRITE”/“WRITETHROUGH” | 写操作时读口的行为 |
| REGMODE_A/B | ”NOREG”/“OUTREG” | 输出寄存器模式 |
| RESETMODE_A/B | ”SYNC”/“ASYNC”(默认 ASYNC) | 复位模式 |
| ECC_ENCODE/DECODE | ”DISABLE”/“ENABLE” | ECC 编解码 |
| BYTE_ENABLE | 0, 8, 10 | 字节使能位宽 |
| IMPLEMENT | ”20K” | 实现物理单元选择 |

手册的时序图一节给了四种代表性时序:单口 Write-Before-Read、单口 Read-Before-Write、简单双口、真双口。WRITEMODE 决定”同一地址又读又写时读口看到什么”:NORMAL(读旧值不变)、READBEFOREWRITE(新旧穿透)、WRITETHROUGH(写数据立即穿透到读口)。做交换矩阵、深度缓冲时这是必须想清楚的行为差异。
常见坑:手册注 1 写明—— 当 ERAM 输出不带寄存器(NOREG)时,RESETMODE 只能选异步(ASYNC),不能选同步。选错了综合可能报错或行为不符。反之输出带寄存器(OUTREG)时同步/异步都可以。另记住:输出加寄存器后数据延迟一个时钟周期,和上游逻辑对拍时序时要算进去。
ECC 相关端口(ecc_sbiterr/ecc_dbiterr 输出标志,ecc_sbiterrinj/ecc_dbiterrinj 注错输入)平时不用接死值即可;使能双 bit 注错时两个注错信号要同时拉高。
FIFO 与 RAMFIFO:硬件队列的两种做法
DR1_LOGIC_FIFO 是 FPGA 内部硬 FIFO 单元(吃 ERAM 资源),特征是大而快:DATA_WIDTH 1320 位、ADDR_WIDTH 914(深度 512~16K),带 empty/aempty/full/afull 四个标志(AE/AF 参数可设”几乎空/几乎满”阈值,默认 96),还输出读写指针 fifo_wrpointer/fifo_rdpointer。注意 csw/csr 两个 3 位片选必须接 3’b111——这是手册端口表里明说的硬性要求,忘了接 FIFO 就不工作。
DR1_LOGIC_RAMFIFO 则是软件集成的软核 FIFO 控制器,特征是功能灵活:支持实时深度指示(rdusedw/wrusedw 直接告诉你队列里有几个数)与 Showahead 模式(SHOWAHEAD=1 时数据先摆到 dout 上、re 只是确认消费,省一拍读延迟)。跨时钟域场景它的 CLKMODE(“SYNC”/“ASYNC”)配合读写双时钟使用。
选择原则:大吞吐、只关心空满标志 → 硬 FIFO;需要知道”队列里还剩多少数据”来做流量控制、或想要 Showahead 的零延迟读 → RAMFIFO。
可靠性外设:ECC 校验与 DNA 芯片指纹
DR1_LOGIC_ECC:给配置存储器做体检
DR1_LOGIC_ECC 是 DR1 系列 PL 侧的 ECC 单元,专门校验 Configuration Memory(配置帧)有没有被单粒子翻转打坏。它的使用模型是一套小握手:
- 用户拉低
ecc_enable_n_i并保持 → ECC 开始扫配置帧; ecc_clk_o是它自己的主时钟(内部晶振分频而来,DR1 上 2~11 MHz,由ECC_FREQ参数配置);ecc_stop_o默认高(停止态);主动停止后必须等 ecc_stop_o 变高才能重新发起;- 校验到最后一个配置帧进入 global crc check 时
ecc_done_o拉高; ecc_info_o[37:0]打包了出错现场:[37:15] 配置帧地址、[14:8] syndrome 对应帧内 81 个 DWORD、[7:3] 对应 DWORD 内 32 个 bit、[2] 单 bit 错、[1] 双 bit 错、[0] gcrc 错。
最有用的参数是 SEC_OPTION,四个模式决定发现单 bit 错误后怎么办:
| SEC_OPTION | 行为 |
|---|---|
| ”CONTINUE” | 不处理,继续校验(gcrc 出错也不报) |
| “HOLD” | 停下来等人来 |
| ”CORRECT_AND_CONTINUE” | 修复该 bit 并回写 Configuration Memory,继续扫 |
| ”CORRECT_AND_HOLD” | 修复回写后停下,等重启 |
高可靠应用(长期上电不停机)选 CORRECT_AND_CONTINUE 定期扫描即可;ECCERR_REPORT_EN="ENABLE" 还能把 ECC 错误映射到 INITN 管脚电平上,外部 MCU 看一眼引脚就知道芯片健康状况。
DR1_LOGIC_DNA:读出 64 位芯片唯一 ID
DR1_LOGIC_DNA 是芯片唯一 DNA 模块,接口是个极简移位寄存器:dna_shift_n_i 低有效使能移位、dna_din_i 移入、dna_do_o 在 dna_clk_i 驱动下逐位移出 64 位 DNA。典型用法:做加密狗/授权校验,把 DNA 与板上 MCU 里存的密钥做哈希比对,防止 bit 文件被拷到别的板子上跑。手册还有配套时序图,注意 shift_n 拉低期间才移位。
通关标准:
- 能不看手册例化 BUFG、IDDR、ODDR,并说出 PIPEMODE/ASYNCRST 各自控制什么。
- 能根据容量和接口需求在 DRAM / ERAM / RAMFIFO 之间做出正确选型,并解释 WRITEMODE 三种行为的差别。
- 看到 IO 数据”不来”能想起三件事:rst 是不是高有效、csw/csr 是否接了 3’b111、x2 系列的 pclk/sclk 频率关系对不对。
自测:内部逻辑生成的 50MHz 分频时钟要驱动全片逻辑,应该怎么做?
用 DR1_LOGIC_BUFG 把分频输出接到全局时钟网络。BUFG 延时抖动小、专为高扇出网络设计,普通信号也允许走 BUFG。若不插,工具可能走普通布线,时钟偏斜大,时序分析也不准。
自测:IO 上是 400Mbps 的 DDR 数据,内部逻辑跑 100MHz,选哪个原语、时钟怎么接?
选 DR1_LOGIC_IDDRx2(1:4 串转并)。400Mbps 数据率对应 sclk=100MHz(1/2 数据率)、pclk=50MHz(sclk 的一半),两时钟同源。若想省一个时钟资源,可用 IDDRx2I——它内部直接用 sclk 二分频当 pclk。
自测:ERAM 输出不加寄存器时,RESETMODE 能选 "SYNC" 吗?
不能。手册注 1 明确:输出不带寄存器(REGMODE=“NOREG”)时复位模式只能是异步 “ASYNC”;只有加了输出寄存器(“OUTREG”)才允许同步复位。同时记住加输出寄存器会让读数据晚一拍。
自测:需要一个能实时报告"队列里还剩多少数据"的 FIFO,用 DR1_LOGIC_FIFO 还是 RAMFIFO?
用 DR1_LOGIC_RAMFIFO。它的 rdusedw/wrusedw 端口直接输出可读/已写数据个数,还支持 Showahead 模式;而 DR1_LOGIC_FIFO 只提供空/满/几乎空/几乎满标志和原始读写指针,需要自己算余量。
自测:ECC 扫描发现单 bit 错误后希望自动修复并继续运行,SEC_OPTION 选什么?
选 “CORRECT_AND_CONTINUE”:修复错误 bit 并把修复后的配置帧回写 Configuration Memory,然后继续下一个配置帧的校验。如果希望修完就停等重启,则选 “CORRECT_AND_HOLD”。