7、IP核使用入门:PLL锁相环配置、Block RAM例化、FIFO生成器使用、DSP48原语调用
好,咱们今天聊聊紫光同创FPGA里最常用的几个IP核。说实话,IP核这东西,用好了能让你开发效率翻倍,用不好嘛……嗯,我见过不少新手在这上面栽跟头。今天我把PLL、Block RAM、FIFO和DSP48这四个核心IP的用法掰开揉碎了讲,都是实战经验。
7.1 PLL锁相环配置——别让时钟成为瓶颈
PLL(锁相环)是FPGA的时钟心脏。你想想看,外部晶振通常就50MHz或100MHz,但你的设计可能需要200MHz、75MHz甚至多个不同频率的时钟域。这时候就得靠PLL了。
我个人习惯,在紫光同创的PDS软件里,调用PLL IP核时,先关注三个参数:
- 输入时钟频率:必须和板级晶振一致,别搞错
- 输出时钟频率:注意VCO范围,紫光的PLL VCO通常在400MHz-1000MHz之间
- 时钟相位和占空比:一般默认50%占空比,相位偏移按需设置
配置界面里,我建议你勾上“锁定指示”信号。这个信号高电平时,才代表PLL输出稳定。我在项目中遇到过,没等锁定信号就复位其他模块,结果系统启动时总出随机错误。
// PLL例化示例(Verilog)
PLL_IP u_pll (
.clkin (clk_50m), // 输入50MHz
.clkout0 (clk_200m), // 输出200MHz
.clkout1 (clk_100m), // 输出100MHz
.clkout2 (clk_25m), // 输出25MHz
.pll_lock (pll_locked), // 锁定指示
.reset (~rst_n) // 注意:高电平复位
);
7.2 Block RAM例化——存储资源要用对地方
Block RAM是FPGA里最宝贵的资源之一。紫光同创的BRAM每个18Kb,可以配置成单口、简单双口或真双口。说白了,就是根据你的读写需求选模式。
我一般遵循这个原则:
- 单口RAM:同一端口读写,适合FIFO或简单缓存
- 简单双口RAM:一个端口只读,一个端口只写,适合跨时钟域数据传输
- 真双口RAM:两个端口都能读写,适合复杂数据交换
配置时有个细节——输出寄存器。勾上它,读数据会多一个时钟周期延迟,但时序会好很多。我在做高速数据采集时,没勾这个选项,结果读路径时序一直不过。后来加上输出寄存器,问题就解决了。
// 简单双口RAM例化
DPRAM_IP u_dpram (
.clka (wr_clk),
.wea (wr_en),
.addra (wr_addr),
.dina (wr_data),
.clkb (rd_clk),
.addrb (rd_addr),
.doutb (rd_data)
);
7.3 FIFO生成器使用——跨时钟域的利器
FIFO(先进先出)本质上就是双口RAM加上读写指针控制。紫光同创的FIFO IP核支持同步和异步两种模式。异步FIFO用于跨时钟域,同步FIFO用于同频不同相位。
配置时,这几个参数要特别留意:
| 参数 | 说明 | 我的建议 |
|---|---|---|
| 数据宽度 | 每个数据多少位 | 8/16/32位常用,注意对齐 |
| 深度 | 能存多少个数据 | 2的幂次方,如16/32/64/128 |
| 满/空标志 | 几乎满、几乎空 | 建议勾上,提前预警 |
| 读写时钟 | 异步FIFO需独立时钟 | 注意时钟域约束 |
嗯,这里要注意——空标志和满标志在异步FIFO里是有延迟的。因为跨时钟域同步需要时间,所以标志信号不是即时的。我曾经在调试时发现,明明空标志还没拉高,读数据却读到了无效值。后来才明白,这是同步延迟导致的。解决办法是:读之前先判断空标志,再等一个时钟周期再读。
// 异步FIFO例化
FIFO_ASYNC u_fifo (
.wr_clk (wr_clk),
.wr_en (wr_en),
.din (wr_data),
.full (fifo_full),
.almost_full(fifo_almost_full),
.rd_clk (rd_clk),
.rd_en (rd_en),
.dout (rd_data),
.empty (fifo_empty),
.almost_empty(fifo_almost_empty)
);
7.4 DSP48原语调用——算力加速的核心
DSP48是FPGA里的硬核乘法器,紫光同创的DSP48支持25×18位乘法,还能做乘加、乘减、累加等操作。说白了,就是专门为数字信号处理准备的。
调用DSP48有两种方式:
- IP核生成器:图形化配置,适合新手
- 原语例化:直接写代码,更灵活
我个人更推荐用原语。为什么?因为IP核生成器会隐藏一些细节,比如流水线级数、寄存器位置。而原语让你完全掌控。
// DSP48原语例化(乘加模式)
DSP48 u_dsp (
.A (data_a), // 25位输入
.B (data_b), // 18位输入
.C (data_c), // 48位输入(累加值)
.P (result), // 48位输出
.CLK (clk),
.CE (1'b1),
.RST (~rst_n),
.OPMODE (6'b011001), // 乘加模式:P = A*B + C
.ALUMODE (4'b0000) // 加法模式
);
使用DSP48时,流水线级数很重要。默认情况下,DSP48内部有三级寄存器:输入寄存器、乘法器寄存器、输出寄存器。如果你需要高频率,建议全部使能。如果对延迟敏感,可以跳过部分寄存器。但要注意,跳寄存器会降低最高工作频率。
举个例子,我在做FIR滤波器时,把DSP48的三级流水线全用上了,跑到了300MHz。后来为了降低延迟,去掉了输入寄存器,结果最高只能跑到200MHz。这就是权衡。
好了,这四个IP核是紫光同创FPGA开发的基础。PLL管时钟,BRAM管存储,FIFO管跨时钟域,DSP48管运算。把这四个用熟了,大部分设计都能搞定。下一章咱们聊聊时序约束,那才是真正考验功力的地方。