这一篇在干嘛?
这篇笔记带你把安路官方的 Soft FIFO IP 用户手册(IPUG012,IP 版本 3.0)从头到尾吃透。先搞懂 FIFO 是个啥、什么时候需要它,再看这个 IP 支持哪些配置、每个信号是干嘛的,最后学会看时序图、在 TD 软件里把它生成出来并正确例化。
FIFO 是什么?什么时候需要它?
FIFO 的全称是 First Input First Output,先入先出队列。你可以把它想象成一根水管:数据从写口(写侧)一端流进去,从读口(读侧)一端流出来,先进去的先出来,顺序不会乱。
在系统设计里,FIFO 主要用来处理大量数据流、匹配传输速率不同的系统,从而提高整体性能。具体来说,最常见的三类场景是:
- 速率缓冲:写数据的一方快、读数据的一方慢(或反过来),FIFO 在中间当蓄水池,两边各按自己的节奏存取,只要平均速率守得住、池子不溢出就行。
- 跨时钟域:写侧和读侧用的是两个不同的时钟。直接把数据从 A 时钟域搬到 B 时钟域很容易出错,异步 FIFO 的内部逻辑(比如格雷码地址同步)会替你处理这种危险。
- 数据位宽变换:写进去是窄数据(比如 2 bit),读出来是宽数据(比如 8 bit),Soft FIFO 支持读写位宽不对称,自动帮你拼/拆数据。
怎么选 RAM 类型?
Soft FIFO 底层存储可以选 ERAM 或 DRAM:ERAM 是器件里的块存储资源,速度快、占用少(8 bit × 1024 深度时只占 1 个 ERAM);DRAM 用 mslice 逻辑搭出来,不吃 ERAM 但费逻辑资源,时序性能也比 ERAM 差。手册给出的实测数据:PH1A180 器件、异步时钟、读写宽度 8、深度 1024、未使能 show ahead 时,ERAM 方案占 LUT 101 / Register 184 / SLICE 139 / ERAM 1,而 DRAM 方案占 LUT 272 / Register 221 / SLICE 332 / ERAM 0。
Soft FIFO IP 有哪些特性?
按手册的描述,Soft FIFO 是一个经过验证、功能完整的软 FIFO,主要特性如下:
- 支持同步时钟和异步时钟模式,可配置;
- 支持不对称读写位宽,读写侧位宽比最高 16:1 或 1:16;
- 数据位宽受器件 RAM 资源限制,最大读写位宽 1024;
- 支持 ERAM 和 DRAM 可选,ERAM 的类型和综合策略也可选;
- 支持 full_flag、empty_flag 标志位输出,以及可配置的 afull(将满)、aempty(将空) 标志位输出;
- 支持输出寄存器模式可配置;
- 支持 show ahead 模式可配置(第一个数据提前出现在输出口);
- 支持 wrusedw(已写入数据量)和 rdusedw(可读数据量) 输出;
- 支持读写握手信号。
位宽比例的限制
不对称读写位宽只支持 2 的 n 次方比 1,即 16:1、8:1、4:1、2:1、1:1、1:2、1:4、1:8、1:16 这些比例。像 5:1 这种”歪”的比例是不支持的。真有别的比例需求,手册建议改用 UniversalGearbox IP。
时序性能方面,手册在 TD 6.2、PH1A400 器件下给了一组参考值(读 8 bit、深度 1024 这类典型配置):ERAM 方案的写侧时钟最高能跑到 572.7 MHz、读侧 568.8 MHz;DRAM 方案就低一些,比如 528.8 MHz / 489.9 MHz;使能 show ahead 后 ERAM 方案降到 436.7 MHz / 386.7 MHz。这些数字仅供参考,实际要看你自己的工程约束。不对称位宽(读 8 bit、写 16 bit)时也有 500+ MHz 的水平。
参数配置:每个参数都在调什么?
Soft FIFO 的所有配置都能通过 TD 的 GUI 界面完成,界面上主要选项如下:
| 参数名 | 选择 | 默认值 | 描述 |
|---|---|---|---|
| Ram Option | ERAM / DRAM / ERAM_144K | ERAM | 选择 RAM 类型 |
| CLKMOD | ASYNC / SYNC | ASYNC | 同步 FIFO 或异步 FIFO |
| Data Port Option | Write Width | 8 | FIFO 写位宽 |
| Data Port Option | Write Depth | 8 | FIFO 写深度 |
| Data Port Option | Read Width | 256 | FIFO 读位宽 |
| Data Port Option | Read Depth | 256 | FIFO 读深度 |
| Show Ahead Option | Enable / Disable | Disable | 提前输出使能控制 |
| Out Reg | Enable / Disable | Disable | 输出寄存器使能控制 |
| Almost num | Almost full num | 8 | afull 的边界值 |
| Almost num | Almost empty num | 7 | aempty 的边界值 |
| Reset Option | Reset Type | ASYNC Reset | 选择复位方式 |
| Reset Option | Dout Reset Value(hxe) | 0 | 数据输出复位值 |
对应到 IP 内部的 PARAMETER 定义(写 Verilog 例化时会遇到):
| 信号 | 默认值 | 描述 |
|---|---|---|
| COMMON_CLK_EN | 0 | 0:异步时钟;1:同步时钟 |
| MEMORY_TYPE | 0 | 0:ERAM(area);1:ERAM(fast timing);2:ERAM(9K, area);3:ERAM(32K, area);4:ERAM(9K, timing);5:DRAM;6:ERAM 144K(仅 PH2A 器件有效) |
| RST_TYPE | 0 | 0:无复位;1:异步复位;2:同步复位 |
| DATA_WIDTH_W / ADDR_WIDTH_W | 8 / 10 | 写位宽 / 写深度 |
| DATA_WIDTH_R / ADDR_WIDTH_R | 8 / 10 | 读位宽 / 读深度 |
| DOUT_INITVAL | 8’d0 | 复位值 |
| OUTREG_EN | ”NOREG" | "NOREG”:未使能输出寄存器;“OUT_REG”:使能输出寄存器 |
| SHOW_AHEAD_EN | 0 | 0:未使能;1:使能 SHOW AHEAD 模式 |
| AL_FULL_NUM / AL_EMPTY_NUM | 10 / 10 | 将满水线 / 将空水线 |
| RDUSEDW_WIDTH / WRUSEDW_WIDTH | 10 / 10 | 读侧可读数据计数位宽 / 写侧已写入数据计数位宽 |
| ASYNC_RST_SYNC_RELS | 0 | 1:使能异步复位同步释放模式(复位在异步模式下使能) |
| SYNC_STAGE | 2 | 异步模式下跨时钟域打排寄存器级数,可选 2~8 |
其中 Implement Option(ERAM 的具体实现方式)因器件而异:EF2、EG4 器件有 EMB9K(Area Opt) / EMB9K(Timing Opt) / EMB32K / AUTO 可选,EF3、SF1 只有 9K 的 Area/Timing 两种,PH1A/DR1/PH1P/PH2A/SF2 则是 EMB20K 的 Area/Timing 两种。9K 或 20K 的 Timing Opt 比 Area Opt 时序性能更好;auto 模式下 TD 会优先用 9K 来完成 Soft FIFO。
另外注意:9K 和 32K 的 ERAM 仅支持小器件;ERAM 144K 只在 PH2A 器件上有效。
信号端口:写口、读口和一堆标志位
Soft FIFO 的端口可以按”写侧 / 读侧 / 状态指示”三堆来看:
复位和时钟(输入)
| 端口 | 方向 | 描述 |
|---|---|---|
| wrst / rrst | input | 复位信号,高电平有效,内部采用异步复位 |
| srst | input | 复位信号,高电平有效,内部采用同步复位 |
| rst | input | 复位信号,高电平有效,内部采用异步复位同步释放 |
| clk | input | 同步时钟下的时钟输入,上升沿触发 |
| clkw / clkr | input | 异步时钟下的写侧 / 读侧输入时钟,上升沿触发 |
写口与读口
| 端口 | 方向 | 描述 |
|---|---|---|
| we | input | 写使能,高电平有效 |
| di[W1-1:0] | input | 数据输入信号 |
| re | input | 读使能,高电平有效 |
| dout[W2-1:0] | output | 数据输出端口 |
| valid | output | 数据有效指示,高电平有效 |
状态指示(输出)
| 端口 | 方向 | 描述 |
|---|---|---|
| full_flag | output | 满指示信号,高电平有效 |
| empty_full | output | 空指示信号,高电平有效 |
| afull | output | almost full 指示,高电平有效 |
| aempty | output | almost empty 指示,高电平有效 |
| wrusedw[D1-1:0] | output | 同步至写时钟域的已写入数据量 |
| rdusedw[D2-1:0] | output | 同步至读时钟域的可读数据量 |
| overflow | output | 指示数据写溢出 |
| underflow | output | 指示数据读溢出 |
| wr_success | output | 指示数据写入成功 |
满了还写、空了还读会怎样?
- 满写:full_flag 拉高后再写,数据不会进去,overflow 拉高、wr_success 拉低,这一笔直接被丢掉。
- 空读:empty_flag 拉高后再读,underflow 拉高、valid 拉低,dout 上的数据是无效的。
- usedw 是”保守值”:wrusedw 和 rdusedw 报出的值永远少于 FIFO 中实际可读的数据量,就是为了保守起见保证 FIFO 永远不会溢出。另外写侧报的已写入数据量按写位宽 W1 计算,读侧报的可读数据量按读位宽 W2 计算,两侧位宽不同时数字含义不一样。
正确的读写姿势
写侧:先看 full_flag / afull,不满再拉 we 送数据。读侧:先看 empty_flag / aempty,不空再拉 re 取数;数据是否真的有效要看 valid——empty_flag 为 0 时,re 拉高后下一周期 valid 拉高,表示 dout 有效。
复位操作:别把复位搞砸了
Soft FIFO 支持多种复位模式,不同时钟模式下可用的选择是:
- 同步时钟:同步复位、异步复位同步释放、无复位;
- 异步时钟:异步复位、异步复位同步释放、无复位。
几条关键行为规则:
- 无复位模式在工作过程中无法对 FIFO 进行复位,选了就再也”喊不醒”它,要谨慎。
- 异步复位同步释放模式:复位信号无效后,要过四个时钟周期才真正释放。
- 异步复位模式下读写时钟域各自单独复位,读写操作必须等读写侧复位都释放后才能进行。
- 同步复位模式下,复位信号必须持续至少一个时钟周期(异步复位模式下按读写侧最小时钟算)。
- 复位只对内部地址、信号标志、dout 输出生效,不会清空内部存储单元。
复位释放前别动手
最容易踩的坑是复位还没释放完就开始读写。尤其异步模式下读写两侧释放时间可能不同步,务必等两侧都完成复位,否则标志位和地址指针状态不可信。
读写时序图:一张张看懂
满了会怎样:写溢出时序
异步和同步模式下的写溢出时序如下图(图 3-1 为异步 FIFO、图 3-2 为同步 FIFO):


看图要点:full_flag 拉高后一个周期,wr_success 拉低、overflow 拉高,图中数据 33 没能写进 FIFO。另外异步 FIFO 和同步 FIFO 的实际数据深度不同,所以同样写满后 wrusedw 的值不一样。
读操作与下溢

读侧逻辑:empty_flag 为 0 时拉高 re,下一周期 valid 拉高,dout 上的数据有效;等 empty 拉高后 rdusedw 变 0,再读一个周期 underflow 拉高指示下溢,valid 拉低表示数据无效。
afull 和 aempty:提前预警线
afull 指示的是 FIFO 内部剩余有效数据量与 AL_FULL_NUM 的关系:剩余量大于 AL_FULL_NUM 时拉高,反之拉低。异步时钟、AL_FULL_NUM 配置为 10 时的波形:

aempty 则要求在界面上设置 Almost empty num。下图配置为 4:写入 4 个读侧数据后 re 信号有效,rdusedw 大于 4 时 aempty 拉低、小于 4 时拉高。

afull / aempty 的边界可能和直觉相反
当 Almost full num 大于 Almost empty num 时,afull 和 aempty 与通常理解的 almost full / almost empty 一致;但当 Almost full num 等于或小于 Almost empty num 时,aempty 信号翻转的时间点会先于 afull,和直觉不一样,配置水线时要注意。
输出寄存器模式与 Show Ahead 模式
使能输出寄存器(Out Reg)后,dout 比 normal 模式延迟一个时钟输出,代价是时序性能更好:

Show Ahead 模式则相反,dout 比 normal 模式提前一个时钟出现:

结合上图:写入数据 “D1”、“D2”、“D3” 后,empty_flag 拉低、valid 拉高,第一个数据 “D1” 已经自动出现在输出端口,re 拉高后可直接取走端口数据,比 normal 模式省一拍。使能 Show Ahead 时,输出寄存器不会影响数据提前输出——这一点与旧版本的 Show Ahead 功能不同。
读写位宽不对称:数据怎么拼、怎么拆?
读:写 = 4:1(窄进宽出)
当读位宽 8 bit、写位宽 2 bit(比例 4:1)时,写入 4 个 2 bit 数据后才拼出一个 8 bit 读出。写入顺序 2’b00 → 2’b01 → 2’b10 → 2’b11,先写入的 2’b00 放在低位,最后写入的 2’b11 放在高位:

对应的读写数量规则要记牢:
- 写入数据不足 8 bit 时,读不出数据(empty 信号 = 1);
- 若写入个数 wr_num 不是 4 的整数倍,读空后 FIFO 内部还剩 wr_num%4 个 2 bit 数据”滞留”。
它对应的时序图如下:

读:写 = 1:4(宽进窄出)
反过来,读位宽 2 bit、写位宽 8 bit(比例 1:4)时,写入一个 8’b11100111,读出顺序是 2’b11 → 2’b01 → 2’b10 → 2’b11:

对应时序图:

位宽变换时的深度也别忘了换算
位宽不对称时,写侧深度和读侧深度不是一回事:读 8 bit、写 16 bit 的配置里,读深度 1024 对应写深度 512。配参数时按两侧位宽分别算清楚,免得实际容量比预期小一半。
实际深度:界面配置 ≠ 实际容量
Soft FIFO 的实际深度和界面选择的配置不一定一致,对应关系如下:
| 界面选择配置 | 实际深度 |
|---|---|
| 同步时钟,未使能 show ahead | write/read depth |
| 同步时钟,使能 show ahead | write/read depth + 1 |
| 异步时钟,未使能 show ahead(写侧) | write depth − 1 |
| 异步时钟,未使能 show ahead(读侧) | read depth − (read depth / write depth) |
| 异步时钟,使能 show ahead | write/read depth + 1 |
也就是说异步 FIFO 会”吃掉”一点深度用于内部地址同步管理(这与前文写溢出时序中”异步与同步 FIFO 实际数据深度不同”的说明是呼应的),设计余量时按实际深度来算。
在 TD 里生成并例化 Soft FIFO
打开配置界面
在 TD(手册以 6.2 版本为例)里依次点击 Tools → IP Generator → Primitive IP → Memory → Soft_FIFO,双击进入配置界面。Component Name 在进入配置界面后依旧可以修改。
配置界面有 Basic 和 Local Ports 两个 Tab。
Basic Tab 包含 Ram Option、Implement Option、Clock Mode、Interface Type 四个选项:

- Ram Option:三个可选项(ERAM / DRAM / ERAM_144K);
- Implement Option:六个选项,具体可选项由器件拥有的资源决定(比如 EF2 器件有 9K 和 32K 资源,就有 4 个可选项);
- CLKMODE:同步时钟(SYNC)或异步时钟(ASYNC)二选一;
- Addr Domain Across Control:异步时钟模式下,读写地址格雷码跨时钟同步的拍数。
Local Ports Tab 包含五组配置:

- Data Port Option:设置位宽和深度,超出器件资源限制会报错;
- Show Ahead Option:使能后第一个数据提前输出;Show Ahead 模式支持 ERAM、DRAM、同步时钟、异步时钟;
- Out Reg:数据输出加一级寄存器,可提升时序性能;同样支持 ERAM、DRAM、同步/异步时钟;
- Almost num(Almost full num 和 Almost empty num):配置 afull 和 aempty 信号的 assert / deassert 门限,可配置范围与读写深度相关;
- Reset Option:选择复位模式及 RAM 的输出复位值。
配置完成后的输出文件
参数配好、无报错后点击 OK,会生成以下文件:
| 文件 | 备注 |
|---|---|
| .v | IP 工具生成的 wrapper.v 文件 |
| .xml | IP Gen 界面配置参数记录文件 |
| .v | Soft FIFO IP 核 |
| .tcl | IP 时序约束文件 |
时序约束:基本不用你操心
异步 Soft FIFO 模式下,IPGEN 会在 IP 同级目录下生成 .tcl 时序约束文件。当前版本的 Soft FIFO IP 会自动获取用户顶层的时序约束,例化之后无需手动修改;自带约束文件由 IPC 文件调用,不必单独加入工程,且约束路径不可更改。
绝对不要自己加跨时钟域豁免
在用户 SDC 文件里,不可直接对 Soft FIFO IP 的读写时钟使用 set_clock_groups 或 set_fault_path 命令,更不能用 set_cross_domain_timing self/domain 之类命令让工具不分析这两个异步时钟域——否则 IP 内部格雷码跨时钟域的 set_max_delay 约束会失效,FIFO 逻辑存在时序风险。如果工程里除了 Soft FIFO 还有别的逻辑跨这两个时钟域,正确做法是在用户 SDC 里加
set_max_delay -from [get_clocks A] -to [get_clocks B] -datapath_only 100来放松时序(A/B 是驱动 FIFO 的读写时钟名称)。
收尾清单
- Soft FIFO 支持全系列安路 FPGA 器件(除 ELF、AL3 外);
- IP 会限制单个 FIFO 的位宽和深度,多次例化时注意器件资源会不会超(大工程配小器件尤其小心);
- 配深度时按上表换算实际深度,别按界面数值估算容量。
自测
- 异步时钟模式下,界面配置写深度 512、未使能 show ahead,写侧实际深度是多少?(答案:write depth − 1 = 511;异步 FIFO 的实际深度与配置不一致,估算容量时要按实际深度算。)
- 用户 SDC 里为什么不能对 Soft FIFO 的读写时钟加 set_clock_groups?应该怎么做?(答案:加了之后 IP 内部格雷码跨时钟域的 set_max_delay 约束会失效,FIFO 有时序风险;如果确实有其他跨这两个时钟域的逻辑,用 set_max_delay -from [get_clocks A] -to [get_clocks B] -datapath_only 100 放松时序。)