FPGA学习笔记——Verilog中可综合和不可综合语句

目录

[一、 绝对可综合的语句(RTL核心)](#一、 绝对可综合的语句(RTL核心))

[1. 模块声明 (Module Declaration)](#1. 模块声明 (Module Declaration))

[2. 端口定义 (Input, Output, Inout)](#2. 端口定义 (Input, Output, Inout))

[3. wire和reg变量声明](#3. wire和reg变量声明)

[4. 参数和局部参数 (Parameter, Localparam)](#4. 参数和局部参数 (Parameter, Localparam))

[5. 赋值语句 (Assignments)](#5. 赋值语句 (Assignments))

[6. 过程块 (Always Blocks)](#6. 过程块 (Always Blocks))

[7. 条件语句 (If-Else)](#7. 条件语句 (If-Else))

[8. 多路选择语句 (Case)](#8. 多路选择语句 (Case))

[9. 循环语句 (For Loops)](#9. 循环语句 (For Loops))

[10. 算术和逻辑运算符](#10. 算术和逻辑运算符)

[11. 位选择和部分选择](#11. 位选择和部分选择)

[12. 模块实例化 (Module Instantiation)](#12. 模块实例化 (Module Instantiation))

[13. Generate 语句](#13. Generate 语句)

[二、 通常(或绝对)不可综合的语句](#二、 通常(或绝对)不可综合的语句)

[1.初始语句 (initial):](#1.初始语句 (initial):)

2.时间延迟 (#delay)::)

[3.仿真系统任务 (System Tasks):](#3.仿真系统任务 (System Tasks):)

[4.实时事件触发器 (event):](#4.实时事件触发器 (event):)

[5.wait 语句:](#5.wait 语句:)

[6.while, repeat, forever 循环(非固定边界):](#6.while, repeat, forever 循环(非固定边界):)

[7.force 和 release:](#7.force 和 release:)

[三、 需要谨慎使用或工具相关的语句](#三、 需要谨慎使用或工具相关的语句)

可综合的意思是,这些代码能够被EDA工具(如Synopsys Design Compiler, Vivado, Quartus等)转换成确定的数字电路网表(由基本门、触发器、宏单元等组成)。其核心是描述硬件结构寄存器传输级(RTL)行为


一、 绝对可综合的语句(RTL核心)

1. 模块声明 (Module Declaration)

rust 复制代码
module module_name (
    input  clk,       // 输入端口
    input  rst_n,
    input  [7:0] data_in,
    output reg [7:0] data_out // 输出端口
);
// ... 逻辑代码 ...
endmodule

2. 端口定义 (Input, Output, Inout)

input, output, inout 关键字用于定义模块的端口方向。

3. wire和reg变量声明

wire 表示电路中的物理连线,用于连接元件。通常由assign语句或模块实例化驱动。

reg:不一定代表触发器! 它表示一个保持值的存储体 。在always块中赋值的变量必须声明为reg类型,它可能被综合成触发器(时序逻辑)或组合逻辑(如多路选择器)。

4. 参数和局部参数 (Parameter, Localparam)

parameter WIDTH = 8;
localparam STATE_IDLE = 2'b00;

5. 赋值语句 (Assignments)

连续赋值 (Continuous Assignment assign): 用于描述组合逻辑,等式右端操作数的任何变化都会立即导致左端更新。

rust 复制代码
wire a, b, c;
assign c = a & b; // 综合为一个与门

过程赋值 (Procedural Assignment) : 在alwaysinitial块中使用(但initial通常不可综合)。

阻塞赋值 (=) :顺序执行,用于组合逻辑建模。非阻塞赋值 (<=):并行执行,用于时序逻辑建模(触发器)。

6. 过程块 (Always Blocks)

时序逻辑 (Flip-Flops) :使用时钟(clk)或时钟+复位(rst)的边沿作为敏感列表。

rust 复制代码
always @(posedge clk or negedge rst_n) begin
    if (!rst_n) begin
        // 复位逻辑:异步复位
        data_out <= 8'b0;
    end else begin
        // 时钟上升沿触发的逻辑
        data_out <= data_in; // 一个8位寄存器
    end
end

组合逻辑 (Combinational Logic) :使用电平敏感的通配符*或列出所有输入信号。

rust 复制代码
always @(*) begin // 或 always @(a, b, sel)
    case (sel)
        2'b00: y = a;
        2'b01: y = b;
        default: y = 1'b0;
    endcase
end
// 这会综合为一个多路选择器(MUX)

注意描述组合逻辑的always块中,必须确保所有条件下每个输出都有赋值,否则会隐含锁存器(Latch)!可以使用default分支或赋默认值来避免。

7. 条件语句 (If-Else)

通常被综合为多路选择器(MUX)或优先级编码逻辑。

rust 复制代码
if (enable) begin
    out = in1;
end else begin
    out = in2;
end

8. 多路选择语句 (Case)

通常被综合为一个多路选择器。比同等功能的if-else结构更并行,面积可能更优。

rust 复制代码
case (opcode)
    3'b000: result = a + b;
    3'b001: result = a - b;
    3'b010: result = a & b;
    default: result = 8'b0; // 防止产生锁存器!
endcase

9. 循环语句 (For Loops)

可综合,但有严格条件 :循环次数必须在编译时是固定 的(即循环边界是常量)。

它会被综合工具展开(Unroll),生成多份硬件电路。

rust 复制代码
// 可综合:循环8次,被展开为8个相同的逻辑单元
integer i;
always @(*) begin
    for (i = 0; i < 8; i = i + 1) begin
        parity[i] = ^data[((i+1)*8)-1 : i*8]; // 计算每个字节的奇偶校验位
    end
end

不可综合示例 :循环次数在运行时才确定的for循环。

10. 算术和逻辑运算符

+, -, *, &, |, ^, ~, <<, >> 等都可综合。

注意:*(乘法)和 /(除法)会综合成乘法器/除法器,可能很耗资源。对于大位宽操作,需要谨慎。

<< n(逻辑左移n位)通常综合为连线的重排,不消耗逻辑资源。

>> n(逻辑右移n位)同上。

11. 位选择和部分选择

rust 复制代码
wire [31:0] bus;
wire [7:0] byte = bus[15:8]; // 部分选择
wire bit = bus[0];           // 位选择

12. 模块实例化 (Module Instantiation)

这是层次化设计的基础,用于调用其他模块或供应商提供的IP核(如PLL, RAM等)。

rust 复制代码
and_gate u_and_gate (
    .a (input_a), // 端口连接
    .b (input_b),
    .c (output_c)
);

13. Generate 语句

用于生成重复的硬件结构或有条件地实例化代码,在编译时确定。

rust 复制代码
genvar i;
generate
    for (i=0; i<4; i=i+1) begin : gen_loop
        my_module u_mod (.in(a[i]), .out(b[i]));
    end
endgenerate

二、 通常(或绝对)不可综合的语句

1.初始语句 (initial):

用于testbench初始化信号,生成激励。

除FPGA上用于初始化reg变量的少数情况外(如reg var = 0;),绝大多数initial块不能被ASIC综合工具接受。

rust 复制代码
// 在模块中尝试初始化寄存器(ASIC综合通常不支持)
reg [7:0] counter = 8'b0; // 这种写法在FPGA综合中可能被接受,用于上电初始化

initial begin
    // 这种在initial块中对信号进行赋值的写法绝对不可综合
    state = IDLE;
    data_bus = 32'hFFFF_FFFF;
    enable = 0;
end

2.时间延迟 (#delay):

#5 a = b;。RTL设计是功能描述,不关心绝对延时。

rust 复制代码
initial begin
    clk = 0;
    #10;        // 延迟10个时间单位
    rst_n = 1;  // 10个时间单位后,将复位信号拉高
    #5 data = 8'hFF; // 再延迟5个时间单位后赋值
end

3.仿真系统任务 (System Tasks):

$display, $monitor, $finish, $stop, $random等,用于仿真调试和测试。

rust 复制代码
always @(posedge clk) begin
    if (data_valid) begin
        $display("At time %t, data = %h", $time, data_out); // 打印信息
        $monitor("Signal changed: a = %b, b = %b", a, b);   // 监控信号变化
        data_array[addr] = $random;                         // 使用随机数
    end
end

initial begin
    #1000 $finish; // 仿真1000个单位后结束
end

4.实时事件触发器 (event):

用于进程间同步,无直接硬件对应。

rust 复制代码
event start_operation; // 声明一个事件

initial begin
    -> start_operation; // 触发事件
end

always @(start_operation) begin // 等待事件发生
    // ... 执行操作
end

5.wait 语句:

基于电平的无限等待,综合工具无法处理。

rust 复制代码
// 等待"start"信号变为高电平
always @(posedge clk) begin
    wait (start == 1'b1); // 等待start变高
    // ... 后续操作
end

6.while, repeat, forever 循环(非固定边界):

综合工具无法确定其运行时间和硬件规模。

rust 复制代码
// 不确定循环次数,直到满足条件
always @(posedge clk) begin
    while (data != 8'hAA) begin // 循环次数在编译时未知
        data <= data + 1;
    end
end

// 无限循环
initial begin
    forever begin // 永远运行,没有边界
        #5 clk = ~clk; // 常用于testbench生成时钟
    end
end

7.forcerelease:

仿真中的强制信号操作。

rust 复制代码
initial begin
    #100;
    force top.dut.counter = 8'd255; // 强制将层次化路径中的某个信号拉高
    #200;
    release top.dut.counter;        // 释放强制
end

三、 需要谨慎使用或工具相关的语句

  1. integerreal:

    • integer 通常可综合,它只是一个32位的寄存器。

    • real(浮点数)基本不可综合,除非使用特殊的IP核。

  2. 用户自定义原语 (UDP):

    • 理论上可综合,但支持度因工具而异,现在基本被模块替代,不推荐使用。
  3. tri (三态线):

    • 可综合,但通常只用于顶层接口(连接片外总线)或FPGA内部的一些特殊资源(如IOB)。芯片内部绝大多数是单向信号。

以上就是Verilog中可综合和不可综合语句。(如果有错误,还请大家指出来,谢谢!)