03verilog基础语法

Verilog基础语法详解

本文档详细介绍Verilog硬件描述语言的基础语法,包括代码风格、标识符、关键字、数据类型等核心概念,为深入学习Verilog打下坚实基础。

目录

代码规范与风格

项目文件组织结构

推荐采用以下目录结构来组织Verilog项目:

project_name/
├── src/           # RTL源代码
│   ├── core/      # 核心模块
│   ├── interface/ # 接口模块
│   └── utils/     # 工具模块
├── tb/            # 测试平台代码
│   ├── unit/      # 单元测试
│   └── system/    # 系统测试
├── sim/           # 仿真脚本和结果
├── syn/           # 综合脚本
├── doc/           # 文档
└── Makefile       # 构建脚本

命名规范

🔤 大小写规则

  • 模块名: 小写,用下划线分隔 (cpu_core, memory_controller)
  • 信号名: 小写,描述性命名 (clk, reset_n, data_valid)
  • 常量/参数: 大写,下划线分隔 (DATA_WIDTH, ADDR_BITS)
  • 状态机状态: 大写,前缀区分 (IDLE, FETCH, DECODE)

📝 命名约定

// 推荐的命名风格
module uart_transmitter #(
    parameter DATA_WIDTH = 8,
    parameter BAUD_RATE  = 9600
)(
    input  wire                  clk,
    input  wire                  reset_n,
    input  wire [DATA_WIDTH-1:0] tx_data,
    input  wire                  tx_valid,
    output reg                   tx_ready,
    output wire                  uart_tx
);

代码格式化

✨ 格式要求

  • 语句结束: 每个语句必须以分号(;)结尾
  • 缩进: 使用4个空格或1个Tab,保持一致
  • 行长度: 建议不超过80-100字符
  • 空白字符: 适当使用空格和空行增强可读性

📖 注释规范

单行注释

// 计算下一个状态
next_state = IDLE;  // 返回空闲状态

多行注释

/*
 * 模块功能描述:
 * 实现8位UART发送器
 * 支持可配置波特率
 */

文档注释

/**
 * @brief UART发送模块
 * @param DATA_WIDTH 数据位宽,默认8位
 * @param BAUD_RATE  波特率,默认9600
 * @input clk        系统时钟
 * @input reset_n    异步复位,低有效
 * @output uart_tx   UART发送线
 */

标识符与关键字

标识符规则

🔤 字符要求

合法字符: 字母、数字、下划线(_)、美元符号($)

命名规则:

  • 首字符必须是字母或下划线
  • 区分大小写 (Datadata)
  • 长度不限,但建议保持简洁明了
// 合法标识符示例
wire data_bus;          // ✅ 下划线分隔
reg clock_enable;       // ✅ 描述性命名
wire _internal_signal;  // ✅ 下划线开头
reg $monitor_signal;    // ✅ 美元符号(系统任务用)

// 非法标识符示例
wire 2_bit_data;        // ❌ 数字开头
reg data-bus;           // ❌ 包含连字符
wire data bus;          // ❌ 包含空格

Verilog关键字分类

🏗️ 模块结构关键字

关键字 功能 示例
module 模块定义开始 module cpu_core(...)
endmodule 模块定义结束 endmodule
function 函数定义 function [7:0] add8bit(...)
endfunction 函数结束 endfunction
task 任务定义 task display_result(...)
endtask 任务结束 endtask

🔄 控制流关键字

关键字 功能 应用场景
begin...end 块语句 顺序语句组合
fork...join 并行块 并行语句执行
if...else 条件判断 条件执行
case...endcase 多路选择 状态机、译码器
for 循环语句 数组操作
while 条件循环 动态循环
forever 无限循环 时钟生成

📊 数据类型关键字

关键字 类型 用途
wire 线网类型 组合逻辑连接
reg 寄存器类型 存储元件
integer 整数类型 循环变量、计算
real 实数类型 仿真中的实数运算
parameter 参数 编译时常量
localparam 本地参数 模块内部常量

⚡ 时序控制关键字

// 时序控制示例
always @(posedge clk or negedge reset_n) begin  // posedge, negedge
    if (!reset_n)
        counter <= 0;
    else
        counter <= counter + 1;
end

initial begin      // initial: 仿真初始化
    clk = 0;
    reset_n = 0;
    #10 reset_n = 1;  // # 延时控制
end

assign result = enable ? data_in : 8'b0;  // assign: 连续赋值

🔧 生成语句关键字

// generate...endgenerate 示例
generate
    genvar i;  // genvar: 生成变量
    for (i = 0; i < 8; i = i + 1) begin : gen_loop
        and gate_inst (out[i], a[i], b[i]);
    end
endgenerate

系统任务和函数

📺 显示系统任务

$display("Counter value = %d", counter);  // 格式化输出
$monitor("Time=%0t, clk=%b", $time, clk); // 监控信号变化
$write("Partial output: ");               // 不换行输出

🕐 时间系统函数

$time      // 当前仿真时间
$realtime  // 实数格式时间
$stime     // 32位时间

📁 文件操作系统任务

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

数据类型系统

基本数据类型

🔗 Wire类型(线网)

特性: 表示硬件连接,不能存储值,必须被驱动

// wire声明和使用
wire [7:0] data_bus;           // 8位数据总线
wire       chip_select;        // 单bit控制信号
wire [15:0] address_bus;       // 16位地址总线

// 连续赋值驱动wire
assign data_bus = enable ? input_data : 8'bz;
assign chip_select = (address_bus[15:12] == 4'hF);

📦 Reg类型(寄存器)

特性: 可以存储值,用于时序逻辑和过程赋值

// reg声明和使用
reg [31:0] instruction;        // 32位指令寄存器
reg        cpu_enable;         // CPU使能信号
reg [2:0]  state, next_state;  // 状态机状态

// 在always块中赋值
always @(posedge clk) begin
    if (reset)
        instruction <= 32'b0;
    else if (load_instr)
        instruction <= memory_data;
end

🔢 整数和实数类型

integer loop_counter;          // 32位有符号整数
integer file_handle;           // 文件句柄
real    voltage_level;         // 实数类型(仿真用)
time    current_time;          // 时间类型

数值表示法

📊 数值格式

// 格式:<位宽>'<进制><数值>
4'b1010        // 4位二进制:1010
8'h2F          // 8位十六进制:00101111
16'd1024       // 16位十进制:0000010000000000
32'o777        // 32位八进制

🎯 特殊值

1'bx           // 不定态(未知)
1'bz           // 高阻态(三态)
8'hxx          // 8位全不定
4'bzzzz        // 4位全高阻

数组和存储器

📚 一维数组

reg [7:0] memory [0:255];      // 256个8位寄存器数组
wire [31:0] cache_line [0:15]; // 16个32位cache行

// 数组访问
memory[address] = write_data;   // 写入
read_data = memory[address];    // 读取

🏗️ 多维数组

reg [7:0] matrix [0:7][0:7];   // 8x8的字节矩阵
// 访问:matrix[row][col] = data;

运算符详解

算术运算符

运算符 功能 示例 说明
+ 加法 result = a + b 位宽自动扩展
- 减法 diff = a - b 支持有符号运算
* 乘法 product = a * b 结果位宽翻倍
/ 除法 quotient = a / b 整数除法
% 取模 remainder = a % b 求余运算
** 幂运算 power = a ** b a的b次方

逻辑运算符

// 位逻辑运算符
assign and_result = a & b;      // 按位与
assign or_result  = a | b;      // 按位或
assign xor_result = a ^ b;      // 按位异或
assign not_result = ~a;         // 按位非

// 逻辑运算符
assign logic_and = a && b;      // 逻辑与
assign logic_or  = a || b;      // 逻辑或
assign logic_not = !a;          // 逻辑非

移位运算符

assign left_shift  = data << 2;  // 逻辑左移
assign right_shift = data >> 2;  // 逻辑右移
assign arith_right = data >>> 2; // 算术右移(保持符号位)
assign left_rotate = data <<< 2; // 算术左移

关系和相等运算符

// 关系运算符
assign greater = a > b;
assign less_eq = a <= b;

// 相等运算符
assign equal     = a == b;       // 逻辑相等
assign not_equal = a != b;       // 逻辑不等
assign case_eq   = a === b;      // 全等(包括x,z)
assign case_neq  = a !== b;      // 全不等

条件运算符

// 三目运算符
assign result = condition ? true_value : false_value;

// 嵌套条件
assign priority_out = (high_pri) ? high_data :
                     (med_pri)  ? med_data  :
                     (low_pri)  ? low_data  : default_data;

模块结构

基本模块框架

module module_name #(
    // 参数声明
    parameter DATA_WIDTH = 8,
    parameter ADDR_WIDTH = 16
)(
    // 端口声明
    input  wire                    clk,
    input  wire                    reset_n,
    input  wire [DATA_WIDTH-1:0]   data_in,
    input  wire [ADDR_WIDTH-1:0]   address,
    output reg  [DATA_WIDTH-1:0]   data_out,
    output wire                    valid
);

    // 内部信号声明
    reg [DATA_WIDTH-1:0] internal_reg;
    wire [ADDR_WIDTH-1:0] decoded_addr;
    
    // 功能实现
    always @(posedge clk or negedge reset_n) begin
        // 时序逻辑
    end
    
    assign decoded_addr = address & {ADDR_WIDTH{1'b1}};
    assign valid = |internal_reg;  // 归约或运算

endmodule

端口声明方式

🔌 ANSI-C风格(推荐)

module uart_rx (
    input  wire       clk,
    input  wire       reset_n,
    input  wire       rx_pin,
    output reg  [7:0] rx_data,
    output wire       rx_valid
);

🔌 传统风格

module uart_rx (clk, reset_n, rx_pin, rx_data, rx_valid);
    input       clk;
    input       reset_n;
    input       rx_pin;
    output [7:0] rx_data;
    output      rx_valid;
    
    reg  [7:0] rx_data;
    wire       rx_valid;

语句类型

过程语句块

⚡ Always块

// 时序逻辑 - 时钟边沿触发
always @(posedge clk or negedge reset_n) begin
    if (!reset_n)
        counter <= 0;
    else
        counter <= counter + 1;
end

// 组合逻辑 - 信号变化触发
always @(*) begin  // 或 always_comb
    case (opcode)
        3'b000: alu_out = a + b;
        3'b001: alu_out = a - b;
        3'b010: alu_out = a & b;
        default: alu_out = 0;
    endcase
end

🚀 Initial块(仅用于仿真)

initial begin
    // 初始化信号
    clk = 0;
    reset_n = 0;
    data_in = 0;
    
    // 释放复位
    #100 reset_n = 1;
    
    // 测试序列
    #50 data_in = 8'hAA;
    #50 data_in = 8'h55;
    
    // 结束仿真
    #1000 $finish;
end

赋值语句

📝 阻塞赋值(=)

// 用于组合逻辑
always @(*) begin
    temp = a + b;        // 立即执行
    result = temp * c;   // 使用更新后的temp值
end

⏰ 非阻塞赋值(<=)

// 用于时序逻辑
always @(posedge clk) begin
    q1 <= d;           // 在时钟边沿同时更新
    q2 <= q1;          // 使用q1的旧值
end

🔗 连续赋值

// wire类型的驱动
assign sum = a + b + cin;
assign carry = (a & b) | (a & cin) | (b & cin);

编程最佳实践

🎯 设计原则

  1. 时序逻辑使用非阻塞赋值

    always @(posedge clk) begin
        shift_reg <= {shift_reg[6:0], serial_in};  // ✅
    end
    
  2. 组合逻辑使用阻塞赋值

    always @(*) begin
        temp = a + b;     // ✅
        result = temp * c;
    end
    
  3. 避免锁存器(Latch)

    // ❌ 会产生锁存器
    always @(*) begin
        if (enable)
            out = in;
        // 缺少else分支
    end
    
    // ✅ 正确的组合逻辑
    always @(*) begin
        if (enable)
            out = in;
        else
            out = 0;
    end
    
  4. 状态机编程规范

    // 三段式状态机(推荐)
    typedef enum reg [1:0] {
        IDLE = 2'b00,
        WORK = 2'b01,
        DONE = 2'b10
    } state_t;
    
    state_t current_state, next_state;
    
    // 状态寄存器
    always_ff @(posedge clk or negedge reset_n) begin
        if (!reset_n)
            current_state <= IDLE;
        else
            current_state <= next_state;
    end
    
    // 次态逻辑
    always_comb begin
        next_state = current_state;
        case (current_state)
            IDLE: if (start) next_state = WORK;
            WORK: if (done)  next_state = DONE;
            DONE: next_state = IDLE;
        endcase
    end
    
    // 输出逻辑
    always_comb begin
        busy = (current_state != IDLE);
        ready = (current_state == IDLE);
    end
    

🔍 常见错误避免

❌ 多驱动源

// 错误:多个always块驱动同一信号
always @(posedge clk) data <= input1;  // ❌
always @(posedge clk) data <= input2;  // ❌

❌ 敏感列表不完整

// 错误:敏感列表不完整
always @(a) begin  // ❌ 缺少b
    result = a + b;
end

// 正确:使用通配符
always @(*) begin  // ✅ 自动包含所有输入
    result = a + b;
end

总结

掌握Verilog基础语法是进行数字IC设计的重要基础。本章重点内容:

代码规范: 建立良好的编程习惯
数据类型: 理解wire和reg的区别
运算符: 掌握各类运算符的使用
模块结构: 学会模块化设计
语句类型: 区分时序和组合逻辑
最佳实践: 避免常见设计错误

posted @ 2025-07-03 15:45  SiliconDragon  阅读(38)  评论(0)    收藏  举报