Verilog中函数与任务的设计区别与综合结果对比指南

二牛学FPGA
文章2026-04-25
66

Quick Start(快速上手)

  1. 准备开发环境:安装 Vivado / Vivado ML 或 Quartus Prime,新建空白工程,目标器件选择 Xilinx Artix-7 (xc7a35ticsg324-1L) 或 Altera Cyclone V (5CEBA4F23C7)。
  2. 新建源文件:创建两个 Verilog 文件——func_demo.v(函数版 8-bit 加法器)和 task_demo.v(任务版带握手信号的 8-bit 加法器)。
  3. 实现函数版本:在 func_demo.v 中定义 function [7:0] add_func;,输入两个 8-bit 参数,输出和。在顶层模块中通过连续赋值调用。
  4. 实现任务版本:在 task_demo.v 中定义 task add_task;,包含 input [7:0] a, boutput [7:0] sum。在 always 块中使用事件控制(如 @(posedge clk))调用。
  5. 编写测试顶层:创建顶层测试模块,例化两个子模块,分别连接相同的输入激励(例如 8'hA58'h3C),观察输出。
  6. 运行行为仿真:使用 Vivado Simulator 或 ModelSim 进行仿真,验证函数输出 sum_func 与任务输出 sum_task 逻辑一致(均为 8'hE1)。
  7. 执行综合:对两个模块分别执行综合(Synthesis),查看综合后的 RTL 原理图与资源报告。
  8. 对比综合结果:函数通常被展开为组合逻辑(LUT 实现),任务则可能被综合为组合逻辑或有限状态机(取决于内部时序控制)。记录 LUT 数量、路径延迟。
  9. 验收点:函数版本无时钟域,输出纯组合;任务版本若包含 @(posedge clk) 则输出寄存器化(时序逻辑)。
  10. 排障:若仿真结果不一致,检查任务中是否遗漏了敏感列表或事件控制;检查函数是否在连续赋值中正确使用。

前置条件与环境

项目推荐值替代方案
器件/板卡Xilinx Artix-7 (xc7a35ticsg324-1L)Altera Cyclone V, Lattice iCE40
EDA 版本Vivado 2023.1Quartus Prime 22.1, Vivado ML 2022.2
仿真器Vivado Simulator (xsim)ModelSim SE-64 2020.1, Verilator
时钟/复位时钟 50 MHz (周期 20 ns),异步复位低有效100 MHz 时钟,同步复位
接口依赖无外部接口,纯内部信号可扩展为 AXI-Stream 接口
约束文件XDC 约束:仅时钟周期 20 ns,无 I/O 约束SDC 约束(Quartus)
综合策略Vivado 默认综合(synth_design -mode out_of_context)Quartus 标准综合

目标与验收标准

  1. 功能点:函数与任务均实现 8-bit 无符号加法,输出 sum = a + b(无进位输出)。
  2. 性能指标:函数版本组合路径延迟 ≤ 5 ns(满足 20 ns 周期);任务版本(时序版)寄存器到寄存器延迟 ≤ 10 ns。
  3. 资源:函数版本使用 ≤ 8 个 LUT(8-bit 加法器典型值);任务版本(时序版)使用 ≤ 8 个 LUT + 8 个 FF。
  4. 波形验收:仿真波形中,函数输出 sum_func 在输入变化后立即更新(无延迟);任务输出 sum_task 在时钟上升沿后更新(寄存器输出)。
  5. 综合日志验收:综合报告无警告(WARNING),无 LUT 或 FF 被意外优化掉(如被优化为常数)。

实施步骤

工程结构

project/
├── rtl/
│   ├── func_demo.v    // 函数版本加法器
│   ├── task_demo.v    // 任务版本加法器
│   └── top.v          // 顶层模块,例化两个子模块
├── sim/
│   └── tb.v           // 测试激励
├── constr/
│   └── top.xdc        // 时钟约束
└── vivado/
    └── run.tcl        // 自动运行脚本

说明:顶层模块 top.v 负责连接输入激励(如按键或计数器)到两个子模块,并输出结果到 LED 或仿真观察点。

关键模块:函数版本(func_demo.v)

module func_demo (
    input [7:0] a,
    input [7:0] b,
    output [7:0] sum
);

// 函数定义:纯组合逻辑,无时序控制
function [7:0] add_func;
    input [7:0] x, y;
    begin
        add_func = x + y;  // 连续赋值风格
    end
endfunction

assign sum = add_func(a, b);

endmodule

注意点:函数内不能包含 @(posedge clk) 等时序控制语句;函数调用在连续赋值中直接展开为组合逻辑。

关键模块:任务版本(task_demo.v)

module task_demo (
    input clk,
    input rst_n,
    input [7:0] a,
    input [7:0] b,
    output reg [7:0] sum
);

// 任务定义:可包含时序控制
task add_task;
    input [7:0] x, y;
    output [7:0] z;
    begin
        @(posedge clk);  // 同步到时钟上升沿
        z = x + y;
    end
endtask

always @(posedge clk or negedge rst_n) begin
    if (!rst_n)
        sum <= 8'd0;
    else
        add_task(a, b, sum);  // 调用任务,输出通过参数传递
end

endmodule

注意点:任务可以包含 @(posedge clk) 等时序控制,因此适合描述需要时钟同步的操作。输出通过任务参数传递,而非函数返回值。

验证结果

仿真波形应显示:

  • 函数输出 sum_func 在输入 ab 变化后立即更新(组合逻辑行为)。
  • 任务输出 sum_task 在时钟上升沿后更新(寄存器输出行为)。
  • 两者逻辑值一致(例如输入 8'hA58'h3C 时,输出均为 8'hE1)。

综合后资源报告显示:

  • 函数版本:仅使用 LUT(典型值 8 个),无 FF。
  • 任务版本:使用 LUT + FF(典型值 8 个 LUT + 8 个 FF),因为时序控制导致输出寄存器化。

排障指南

  • 仿真结果不一致:检查任务中是否遗漏了 @(posedge clk) 或敏感列表;检查函数是否在连续赋值中正确使用。
  • 综合警告:如果出现“inferred latch”警告,说明组合逻辑中缺少默认赋值,检查函数或任务中的条件分支是否完整。
  • 资源意外优化:检查综合设置中是否启用了“flatten_hierarchy”或“keep_equivalent_registers”等选项,必要时添加 (* keep = "true" *) 属性。
  • 时序违例:如果函数版本路径延迟超过 5 ns,考虑将加法器拆分为多级流水线(但此时需改用任务或 always 块实现时序逻辑)。

扩展实践

  • 多周期任务:在任务中使用多个 @(posedge clk) 实现多周期操作(例如乘法累加器)。
  • 函数递归:Verilog 2001 支持递归函数,可用于实现树形加法器或查找表。
  • 接口封装:将任务封装为 AXI-Stream 从机接口,实现握手信号控制的数据通路。
  • 综合优化:对比不同综合策略(如面积优化 vs 速度优化)对函数和任务综合结果的影响。

参考资源

  • IEEE Std 1364-2001 Verilog HDL 语言参考手册,第 10 章(任务与函数)。
  • Xilinx UG901 Vivado 综合用户指南。
  • Altera Quartus Prime 标准版综合用户指南。

附录:完整代码清单

以下为完整可综合的 Verilog 代码,可直接用于实验。

func_demo.v

module func_demo (
    input [7:0] a,
    input [7:0] b,
    output [7:0] sum
);

function [7:0] add_func;
    input [7:0] x, y;
    begin
        add_func = x + y;
    end
endfunction

assign sum = add_func(a, b);

endmodule

task_demo.v

module task_demo (
    input clk,
    input rst_n,
    input [7:0] a,
    input [7:0] b,
    output reg [7:0] sum
);

task add_task;
    input [7:0] x, y;
    output [7:0] z;
    begin
        @(posedge clk);
        z = x + y;
    end
endtask

always @(posedge clk or negedge rst_n) begin
    if (!rst_n)
        sum <= 8'd0;
    else
        add_task(a, b, sum);
end

endmodule

top.v

module top (
    input clk,
    input rst_n,
    input [7:0] a,
    input [7:0] b,
    output [7:0] sum_func,
    output [7:0] sum_task
);

func_demo u_func (
    .a(a),
    .b(b),
    .sum(sum_func)
);

task_demo u_task (
    .clk(clk),
    .rst_n(rst_n),
    .a(a),
    .b(b),
    .sum(sum_task)
);

endmodule

tb.v(测试激励示例)

module tb;

reg clk;
reg rst_n;
reg [7:0] a, b;
wire [7:0] sum_func, sum_task;

top u_top (
    .clk(clk),
    .rst_n(rst_n),
    .a(a),
    .b(b),
    .sum_func(sum_func),
    .sum_task(sum_task)
);

initial begin
    clk = 0;
    forever #10 clk = ~clk;  // 50 MHz
end

initial begin
    rst_n = 0;
    #20 rst_n = 1;
    a = 8'hA5;
    b = 8'h3C;
    #40;
    a = 8'h01;
    b = 8'hFF;
    #40;
    $finish;
end

endmodule

运行说明:将上述文件放入对应目录,在 Vivado 中创建工程并添加源文件,运行仿真即可观察波形差异。

分类
技术分享
标签
Verilog任务函数
浏览 66
分享:

相关推荐

同频道 · 相近分类

暂无相关推荐

作者

二牛学FPGA查看主页

同分类阅读

文章

延伸阅读与实操

  • 文章 + 课程联动深度文章常对应体系课章节,可一键选课。
  • 学习产出可参考笔记与作业案例在学习产出广场持续更新。

探索全站