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发送线
*/
标识符与关键字
标识符规则
🔤 字符要求
合法字符: 字母、数字、下划线(_
)、美元符号($
)
命名规则:
- 首字符必须是字母或下划线
- 区分大小写 (
Data
≠data
) - 长度不限,但建议保持简洁明了
// 合法标识符示例
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);
编程最佳实践
🎯 设计原则
-
时序逻辑使用非阻塞赋值
always @(posedge clk) begin shift_reg <= {shift_reg[6:0], serial_in}; // ✅ end
-
组合逻辑使用阻塞赋值
always @(*) begin temp = a + b; // ✅ result = temp * c; end
-
避免锁存器(Latch)
// ❌ 会产生锁存器 always @(*) begin if (enable) out = in; // 缺少else分支 end // ✅ 正确的组合逻辑 always @(*) begin if (enable) out = in; else out = 0; end
-
状态机编程规范
// 三段式状态机(推荐) 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的区别
✅ 运算符: 掌握各类运算符的使用
✅ 模块结构: 学会模块化设计
✅ 语句类型: 区分时序和组合逻辑
✅ 最佳实践: 避免常见设计错误