碎碎思 · 2 天前

FPGA 资源爆表了?10 个 RTL 优化实战技巧

做 FPGA 项目,最怕啥?

资源爆表!Timing 炸裂!布线卡死!

今天我给大家总结 10 个实战级优化技巧,每条都有具体案例,助你从根源上搞定资源问题!

技巧一:减少不必要的总线宽度

问题实例:

原代码:

reg [255:0] data_bus;

实际上每次只用前 32 位。

优化做法,改成需要的实际位宽:

改成:

reg [31:0] data_bus;

👉 LUT 减少了 90%,走线压力降低!

技巧二:移位替代小乘法

问题实例:

assign out =in* 4;

Vivado 推导成 DSP48。

优化做法:

assign out =in<< 2;

👉 完全用 LUT 实现,DSP 零占用!

技巧三:启用资源共享

设置方法(Vivado):

在 opt_design 阶段加参数:

opt_design -resource_sharing on

如果你只想对某些模块资源共享,可以在 Verilog 代码里加属性控制:

(* use_dsp ="yes", resource_sharing ="yes"*)
module your_compute_block (...);

这样可以更精细地控制哪些逻辑共享,哪些不共享。

✅ 好处 LUT/FF/DSP 资源大幅节省,特别适合大面积重复运算设计。

⚠️ 风险 可能带来额外 MUX 切换逻辑,使得时序(Timing)稍微恶化。

🚫 禁用场景 极限高速设计(>400MHz 以上)、低延迟关键路径模块,建议慎用或局部使用。

🔎 验证 需要配合 Report(比如 report_utilization 和 report_timing_summary)检查,确保资源节省大于时序代价。

👉 适用于大量重复的加法、乘法逻辑,资源利用率直接提升 20%以上。

技巧四:优化状态机编码

问题实例:

默认综合大状态机,导致占用大量触发器。

优化做法: 在 Verilog 中加指令:

(* fsm_encoding ="onehot"*) reg [7:0] state;

👉 One-Hot 编码,时序更好,LUT 使用下降!

技巧五:降低组合逻辑深度

问题实例:

大量 if-else 嵌套:

if(a) begin
if(b) begin
 if(c) begin
   ...

导致综合出的 LUT 链超长,Timing 很难收敛。

优化做法:

拆分成多个小模块,每层只管一件事。

技巧六:充分利用 Block RAM 和 UltraRAM

问题实例:

有些人用 reg 数组实现大规模存储,比如:

reg [31:0] mem_array [0:1023];

Vivado 可能默认推成触发器堆栈,严重浪费 LUT/FF 资源。

优化做法:

强制指示综合器用 Block RAM:

(* ram_style ="block"*) reg [31:0] mem_array [0:1023];

或者写成标准双端口 RAM 结构,Vivado 自然推导。

👉 存储转 BRAM/URAM,节省 90%以上的逻辑资源!

技巧七:精简控制逻辑,少写“变态大 if-else”

问题实例:

复杂判断逻辑:

if(mode1 &&enable) begin
 ...
endelseif(mode2 && ~enable&& ready) begin
 ...
endelseif(...)

导致大量 LUT 拼接、布线恶化。

优化做法:

改成干净的 case 语句或者简单解码器方式处理:

case(current_mode)
 MODE1:if(enable) ...;
 MODE2:if(ready) ...;
 ...
endcase

👉 逻辑清晰,综合优化空间大,减少综合时间!

技巧八:审查 Reset 逻辑,减少全局复位

问题实例:

所有寄存器都强制带 Reset 信号,像这样:

always @(posedge clk or posedge rst) begin
if(rst)
  q <= 0;
else
  q <= d;
end

Vivado 需要为每个复位信号单独布线,增加布线拥堵和时序压力。

优化做法:

不重要的寄存器(如数据路径暂存器)去掉 Reset

重要控制信号保持 Reset

可以考虑使用异步小范围复位,减少全局影响

👉 减少 Reset 数量,布线更容易,Fmax 提高明显!

技巧九:保持同步设计,避免异步逻辑污染

问题实例:

写异步模块,比如:

always @(posedge clk1) begin
 data1 <= input;
end

always @(posedge clk2) begin
 output <= data1;
end

不同 Clock 域硬怼在一起,没有同步器,极易出错,而且 Vivado 综合器无法优化,资源浪费严重。

优化做法:

用标准两级同步器跨 Clock 域

控制时序收敛,明确时钟区域分界

同步跨域例子:

always @(posedge clk2) begin
 sync_stage1 <= data1;
 sync_stage2 <= sync_stage1;
end

👉 避免隐性时序错误,同时资源更可控。

技巧十:及早加约束,及时做时序仿真

问题实例:

很多项目前期只堆代码,不加任何 XDC/SDC 约束,最后实现时才发现:

WNS(Worst Negative Slack)严重

TNS(Total Negative Slack)爆表

布线卡住,资源乱用

优化做法:

每新增模块,立刻补充基本时序约束(比如 create_clock、set_input_delay、set_output_delay)

每次综合后跑一次时序仿真(Functional/Timing 仿真)

早发现逻辑、早调整设计架构!

👉 综合收敛早、实现时间短、避免后期爆炸性加班!

END

作者:碎碎思
原文:OpenFPGA

相关文章推荐

更多 FPGA 干货请关注FPGA 的逻辑技术专栏。欢迎添加极术小姐姐微信(id:aijishu20)加入技术交流群,请备注研究方向。
推荐阅读
关注数
10665
内容数
625
FPGA Logic 二三事
目录
极术微信服务号
关注极术微信号
实时接收点赞提醒和评论通知
安谋科技学堂公众号
关注安谋科技学堂
实时获取安谋科技及 Arm 教学资源
安谋科技招聘公众号
关注安谋科技招聘
实时获取安谋科技中国职位信息