跳过正文

Verilog 基础语法

·1565 字·4 分钟· loading · loading ·
加绒
作者
加绒
融雪之前,牧神搭上春色的火车,而日光在我们之间。
Verilog - 这篇文章属于一个选集。
§ 1: 本文

Verilog HDL(Hardware Description Language)是用于描述数字电路的硬件描述语言。

模块结构
#

基本语法
#

module 模块名 (
    input wire 输入端口1,
    input wire 输入端口2,
    output wire 输出端口1,
    output wire 输出端口2
);
// 模块内部逻辑endmodule
endmodule

端口类型
#

  • input:输入端口
  • output:输出端口
  • inout:双向端口

模块实例化
#

模块可以作为独立的硬件单元被其他模块实例化使用:

// 命名端口连接方式
half_adder ha_uut (.a(ha_a), .b(ha_b), .sum(ha_sum), .carry(ha_carry));

// 位置端口连接方式
half_adder ha_uut (ha_a, ha_b, ha_sum, ha_carry);

数据类型
#

线网类型 (Net Types)
#

类型用途特点
wire表示连接线不能存储数据,用于模块间连接
tri三态线网支持多驱动源

寄存器类型 (Register Types)
#

类型用途特点
reg可存储数据的变量可在 always 块中赋值
integer32 位有符号整数用于计数、循环等
real双精度浮点数仿真时使用,不可综合

数字表示方法
#

<位宽>'<进制><数值>

// 示例:
4'b1010     // 4位二进制数 1010
8'hFF       // 8位十六进制数 FF
16'd255     // 16位十进制数 255
4'o17       // 4位八进制数 17
'b1         // 位宽不指定的二进制数

特殊状态
#

  • X:不定态,表示未知或不确定的值
  • Z:高阻态,表示三态缓冲器的高阻抗输出

运算符分类
#

算术运算符
#

运算符功能示例
+加法a + b
-减法a - b
*乘法a * b
/除法a / b
%取模a % b
**幂运算2 ** 3 = 8

对于 2 的幂次取模可优化为位掩码操作:

$$ n \% 2^k = n \&(2^k-1) $$

image

n % 8 = n & 7    // 8 = 2³, 7 = 111₂
n % 16 = n & 15  // 16 = 2⁴, 15 = 1111₂

位运算符
#

运算符功能示例
&按位与a & b
``按位或
^按位异或a ^ b
~按位取反~a

逻辑运算符
#

运算符功能示例
&&逻辑与a && b
``
!逻辑非!a

关系运算符
#

运算符功能示例
<小于a < b
<=小于等于a <= b
>大于a > b
>=大于等于a >= b
==逻辑相等a == b
!=逻辑不等a != b

等式运算符
#

运算符功能说明
===Case 相等精确比较,包括 X/Z 状态
!==Case 不等精确比较不等
==?通配符相等X/Z 作为 don’ t care
!=?通配符不等X/Z 作为 don’ t care

移位运算符
#

运算符功能说明
<<逻辑左移右边补 0
>>逻辑右移左边补 0
<<<算术左移与逻辑左移相同
>>>算术右移保持符号位

缩减运算符
#

缩减运算符对单个操作数的所有位执行运算:

运算符功能示例
&缩减与&8'b11111111 = 1
``缩减或
^缩减异或^8'b11001100 = 0 (偶校验)
~&缩减与非~&bits
`~`缩减或非
~^^~缩减异或非~^bits

赋值语句
#

连续赋值 (assign)
#

用于组合逻辑,信号变化时立即更新:

assign sum = a ^ b;// 异或运算
assign carry = a & b;// 与运算

阻塞赋值 (=)
#

  • 使用 = 操作符
  • 按顺序执行,每步等待前一步完成
  • 主要用于组合逻辑
  • 在 always @(*) 块中使用
always @(*) begin
    a = 8'h10;
    b = a + 1;// b 立即得到 a 的新值
    c = b + 1;// c 立即得到 b 的新值
end

非阻塞赋值 (<=)
#

  • 使用 <= 操作符
  • 所有赋值同时调度,使用信号的旧值
  • 主要用于时序逻辑
  • 在 always @(posedge clk) 块中使用
always @(posedge clk) begin
    x <= 8'h20;
    y <= x + 1;// y 使用 x 的旧值
    z <= y + 1;// z 使用 y 的旧值
end

黄金规则
#

  1. 组合逻辑 always @(*) - 使用阻塞赋值 =
  2. 时序逻辑 always @(posedge clk) - 使用非阻塞赋值 <=
  3. 初始化块 initial - 通常使用阻塞赋值 =

编译器指令
#

时间单位设置
#

`timescale 1ns/1ps// 时间单位1ns,精度1ps

宏定义
#

`define WORD_WIDTH 16
`define BYTE_WIDTH 8
`define DEBUG_MODE

// 使用宏
reg [`WORD_WIDTH-1:0] word_data;

条件编译
#

`ifdef DEBUG_MODE
    `define DEBUG_PRINT(msg) $display("[DEBUG] %s", msg)
`else
    `define DEBUG_PRINT(msg)  // 空宏
`endif

系统任务
#

显示任务
#

任务功能
$display显示信息并换行
$write显示信息不换行
$monitor监控信号变化
$time获取当前仿真时间

文件操作
#

integer file_handle;
file_handle = $fopen("output.txt", "w");
$fwrite(file_handle, "数据: %h\n", data);
$fclose(file_handle);

仿真控制
#

任务功能
$finish结束仿真
$stop暂停仿真
$dumpfile指定 VCD 波形文件名
$dumpvars转储变量到波形文件

随机数生成
#

data = $random;// 生成随机数
data = $random(seed);// 使用种子生成随机数

测试平台 (Testbench)
#

测试平台是验证模块功能的仿真环境:

module simple_module_tb;
    // 测试信号声明
    reg a, b;                           // 输入用reg
    wire and_out, or_out, xor_out;      // 输出用wire

    // 实例化被测模块
    simple_module uut (
        .a(a), .b(b),
        .and_out(and_out),
        .or_out(or_out),
        .xor_out(xor_out)
    );

    // 测试激励
    initial begin
        $dumpfile("test.vcd");
        $dumpvars(0, simple_module_tb);

        // 测试向量
        a = 0; b = 0; #10;
        a = 0; b = 1; #10;
        a = 1; b = 0; #10;
        a = 1; b = 1; #10;

        $finish;
    end
endmodule

设计实例分析
#

半加器 (Half Adder)
#

实现两个 1 位数的加法:

module half_adder (
    input wire a, b,
    output wire sum, carry
);
    assign sum = a ^ b;// 异或得到和
    assign carry = a & b;// 与运算得到进位
endmodule

全加器 (Full Adder)
#

使用两个半加器构建:

module full_adder (
    input wire a, b, cin,
    output wire sum, cout
);
    wire sum1, carry1, carry2;

    half_adder ha1 (.a(a), .b(b), .sum(sum1), .carry(carry1));
    half_adder ha2 (.a(sum1), .b(cin), .sum(sum), .carry(carry2));

    assign cout = carry1 | carry2;
endmodule
Verilog - 这篇文章属于一个选集。
§ 1: 本文