verilog语法笔记

这篇笔记主要是记录有关verilog的语法以及一些遇到过的问题。

1.fork...join

fork...join常用于仿真代码的编写中。fork...join 会同时启动其内部的所有 begin...end 块。块内的每个 begin...end 都是一个独立的进程。这些进程并发执行,直到所有进程都执行完毕,join 语句才会完成,仿真才会继续执行后面的代码。

复制代码
fork
    begin: join_FIFO_wr2
        FIFO_write(16'd64);  // 进程1: 向FIFO写入数据64
    end

    begin: join_RAM_wr2
        #0;                 // 进程2: 延迟0个时间单位(详见下文)
        for(i=32; i<40; i=i+1) begin
            RAM_write(i);   // 从地址32开始,连续写入8个数据
        end
    end

    begin: join_FIFO_rd2
        #0;                 // 进程3: 延迟0个时间单位
        FIFO_read(16'd32);  // 从FIFO读取32个数据
    end

    begin: join_RAM_rd2
        #160;               // 进程4: 延迟160个时间单位
        for(i=0; i<32; i=i+1) begin
            RAM_read(i);    // 从地址0开始,连续读取32个数据
        end
    end
join // 等待所有四个进程执行完毕

执行的时间轴:
Fork-Join并发执行时间线

2.task...endtask

2.1定义

复制代码
task <task_name>;
    // 端口方向声明 (input, output, inout)
    // 局部变量声明 (reg, integer, etc.)
    //声明要放在begin的前面
    begin
        // 过程语句
    end
endtask

2.2调用

复制代码
// 调用任务
<task_name>;

// 带参数的调用
<task_name>(expr1, expr2, ..., exprN);

需要注意调用任务只能是一个独立的语句,不能用在表达式中。

实际例子:

复制代码
module tb_test;

    // 输入数据
    reg [31:0] data_in;
    // 使用 reg 类型接收任务输出
    reg is_even;
    reg [4:0] ones_count;

    // 任务定义
    task analyze_data;
        input [31:0] data_in;
        output is_even;
        output [4:0] ones_count;
        
        integer i;
        begin
            // 检查是否为偶数
            is_even = (data_in[0] == 1'b0);
            
            // 计算1的个数
            ones_count = 0;
            for (i = 0; i < 32; i = i + 1) begin
                if (data_in[i] == 1'b1) begin
                    ones_count = ones_count + 1;
                end
            end
        end
    endtask

    // 调用示例
    initial begin
        // 设置输入
        data_in = 32'hA5A5A5A5;
        
        // 调用任务 - 现在传递的是 reg 变量
        analyze_data(data_in, is_even, ones_count);
        
        // 立即显示结果(任务完成后)
        $display("Even: %b, Ones count: %d", is_even, ones_count);
        $display("Input data: %h", data_in);
        
        // 测试另一个值
        #10;
        data_in = 32'h0000000F;
        analyze_data(data_in, is_even, ones_count);
        $display("Even: %b, Ones count: %d", is_even, ones_count);
        $display("Input data: %h", data_in);
    end

endmodule

在Verilog 中,任务的输出参数必须是能够存储值的变量类型(reginteger 等),而不能是 wire 类型。在过程赋值中, taskalwaysinitial 基本类似,使用 =<=,而不使用assign。

还有一个很奇怪的点,task里面输入是默认为wire类型,但是你不能自己再定义为wire类型,比如说:

复制代码
task analyze_data;
    input wire [31:0] data_in;  // 这样子写,modelsim仿真会报错

不知道为什么会这样子,所以输入一般都不定义,使用默认类型。

2.3注意事项

在仿真代码中,任务可以包含时序控制,这是任务与函数的根本区别。

复制代码
task pulse_generate;
    input start;
    output pulse;
    begin
        pulse = 1'b0;
        @(posedge start);   // 等待 start 的上升沿
        #5 pulse = 1'b1;    // 延迟 5 个时间单位
        #10 pulse = 1'b0;   // 再延迟 10 个时间单位
    end
endtask

3.Block RAM(块 RAM)和 Distributed RAM(分布式 RAM)的区别

Block RAM(块RAM)

  • 专用硬件:FPGA芯片中预制的专用存储电路

  • 大容量存储:单个BRAM通常为18Kb或36Kb,可级联成更大容量

  • 独立资源:使用BRAM不消耗逻辑资源(LUT/FF)

  • 真正双端口:支持两个完全独立的读写端口

  • 适用场景

    • 大型数据缓冲区

    • FIFO存储器

    • 处理器缓存

    • 大型查找表

Distributed RAM(分布式RAM)

  • 逻辑资源构建:使用可编程逻辑单元(LUT)实现存储功能

  • 小容量灵活分布:每个LUT可配置为少量存储(如64位)

  • 消耗逻辑资源:使用分布式RAM会占用可用于其他逻辑的LUT

  • 低延迟访问:由于分布在逻辑单元中,访问延迟极低

  • 适用场景

    • 小型寄存器文件

    • 移位寄存器

    • 浅FIFO

    • 延迟线

    • 小容量查找表

|------|--------------------|-----------------|
| 特性 | Block RAM | Distributed RAM |
| 物理本质 | 专用的硬件存储模块 | 由逻辑单元(LUT)配置而成 |
| 资源类型 | 专用存储资源 | 可编程逻辑资源 |
| 容量 | 大容量(通常18Kb/36Kb每块) | 小容量(每个LUT约64位) |
| 位置 | 芯片中固定的专用位置 | 分布在所有逻辑单元中 |
| 端口配置 | 支持真正双端口 | 通常为单端口或简单双端口 |
| 性能 | 较高的时钟频率 | 较低的访问延迟 |
| 功耗 | 静态功耗低 | 静态功耗相对较高 |
| 使用场景 | 大数据缓冲区、FIFO | 小型寄存器、移位寄存器 |

4.CLB Logic 与 XtremeDSP Slices 对比

4.1 CLB Logic(可配置逻辑块)

组成结构:

LUT:实现组合逻辑功能

触发器:实现时序逻辑和寄存器

多路器:数据选择器

进位链:快速算术运算

主要特点:

高度灵活:可以配置为实现几乎任何数字逻辑功能

通用性:适合控制逻辑、状态机、数据通路等

资源丰富:在FPGA中数量最多

编程自由:支持复杂的逻辑表达式

典型应用:

状态机和控制逻辑

数据路由和选择

接口协议实现

自定义算法逻辑

有限脉冲响应滤波器

4.2XtremeDSP Slices(DSP切片)

组成结构:

专用乘法器:高性能乘法运算

累加器:乘积累加操作

算术逻辑单元:各种算术运算

预加器:优化对称滤波器

流水线寄存器:提高时序性能

主要特点:

高性能:专门优化的数学运算硬件

低功耗:相比用CLB实现同样功能功耗更低

高密度:单个DSP切片可完成复杂运算

确定性时序:预定义的时序特性

典型应用:

复数乘法

有限脉冲响应滤波器

快速傅里叶变换

数字上下变频

矩阵运算

相关器

特性 CLB Logic(可配置逻辑块) XtremeDSP Slices(DSP切片)
本质 通用可编程逻辑资源 专用数字信号处理硬件
主要功能 实现任意逻辑功能 高性能数学运算
核心组件 LUT、触发器、多路器 乘法器、累加器、ALU
灵活性 极高 有限但高度优化
性能 中等,依赖设计 极高,针对特定运算优化
功耗效率 相对较低 非常高

4.3例子

用CLB实现18×18乘法器

需要约200-300个LUT,最大频率约200-300MHz,功耗相对较高。

用DSP切片实现18×18乘法器

只需要1个DSP切片,最大频率500-700MHz, 功耗极低

5.UART

UART 负责完成数据的串并转换,而信号的传输则由外部驱动电路 实现。电信号的传输过程有着不同的电平标准和接口规范,针对异步串行通信的接口标准有 RS232、RS422、 RS485 等,它们定义了接口不同的电气特性,如 RS-232 是单端输入输出,而 RS-422/485 为差分输入输出 等。

特性 RS-232 RS-422 RS-485
信号类型 单端(对地电压) 差分 差分
传输线数量 1根Tx,1根Rx,共地线 1对Tx,1对Rx(4线) 1对数据线(2线)
通信方式 全双工 全双工 半双工(主流)
驱动能力 1个发送器,1个接收器 1个发送器,最多10个接收器 最多32个发送器/接收器
通信距离 短(<15米) 长(约1200米) 长(约1200米)
最大速率 低(< 20 m) 高(10 Mbps @ 12 m) 高(10 Mbps @ 12 m)
抗干扰能力
逻辑电平 +3V to +15V = '0' -3V to -15V = '1' ±2V to ±6V(两线间电压差) ±1.5V to ±5V(两线间电压差)
典型应用 电脑串口、老式调制解调器 工业环境长距离点对点通信 工业总线(PLC、传感器网络)