10verilog过程结构
Verilog过程结构详解
📑 目录
- 1. 过程结构概述
- 2. initial语句详解
- 3. always语句详解
- 4. 过程块的组织结构
- 5. 敏感性列表详解
- 6. 变量类型与赋值
- 7. 过程结构的应用模式
- 8. 综合性与可实现性
- 9. 最佳实践
- 10. 常见错误与调试
1. 过程结构概述
1.1 什么是过程结构
过程结构(Procedural Construct)是Verilog HDL中实现行为级建模的核心机制,用于描述数字电路的动态行为和时序逻辑。
1.2 过程结构的特点
- 🔄 顺序执行: 过程内部语句按顺序执行
- ⚡ 并行处理: 多个过程之间并行运行
- 🎯 事件驱动: 通过敏感性列表响应信号变化
- 📊 状态记忆: 支持寄存器类型的状态存储
1.3 过程结构分类
graph TD
A[Verilog过程结构] --> B[initial语句]
A --> C[always语句]
A --> D[forever语句]
B --> B1[仅执行一次]
B --> B2[仿真初始化]
B --> B3[测试激励生成]
C --> C1[重复执行]
C --> C2[时序逻辑建模]
C --> C3[组合逻辑建模]
D --> D1[无限循环]
D --> D2[时钟生成]
D --> D3[连续监控]
1.4 执行模型
module execution_model_demo;
reg signal_a, signal_b, signal_c;
// 三个并行执行的过程
initial begin // 过程1:初始化
signal_a = 0;
signal_b = 0;
signal_c = 0;
end
always #10 signal_a = ~signal_a; // 过程2:时钟生成
always @(signal_a) begin // 过程3:事件响应
signal_b <= signal_a;
end
endmodule
2. initial语句详解
2.1 基本语法与特性
2.1.1 语法格式
initial begin
// 语句序列
// 仅在仿真开始时执行一次
end
2.1.2 执行特性
- ⏰ 一次性执行: 仿真开始时执行一次后结束
- 🚀 零时刻启动: 从时刻0开始执行
- 🔄 不可重复: 执行完成后不会再次运行
- ❌ 不可综合: 仅用于仿真,不能生成硬件
2.2 基本应用
2.2.1 信号初始化
module signal_initialization;
reg clk, rst_n, enable;
reg [7:0] data, counter;
// 信号初始化
initial begin
clk = 0;
rst_n = 0;
enable = 0;
data = 8'h00;
counter = 8'h00;
// 时序控制的初始化
#100 rst_n = 1; // 100ns后释放复位
#50 enable = 1; // 再过50ns使能信号
end
endmodule
2.2.2 多个initial块
module multiple_initial_blocks;
reg signal1, signal2, signal3;
// 第一个initial块:信号1控制
initial begin
signal1 = 0;
#10 signal1 = 1;
#20 signal1 = 0;
#30 signal1 = 1;
end
// 第二个initial块:信号2控制(并行执行)
initial begin
signal2 = 1;
#15 signal2 = 0;
#25 signal2 = 1;
#35 signal2 = 0;
end
// 第三个initial块:监控输出
initial begin
$monitor("Time=%0t: sig1=%b, sig2=%b, sig3=%b",
$time, signal1, signal2, signal3);
end
endmodule
2.3 时序控制
2.3.1 延时控制
module initial_delay_control;
reg [3:0] test_vector;
initial begin
// 绝对延时
test_vector = 4'b0000;
#10 test_vector = 4'b0001; // 10ns后
#10 test_vector = 4'b0010; // 20ns后
#10 test_vector = 4'b0100; // 30ns后
#10 test_vector = 4'b1000; // 40ns后
// 相对延时
#50; // 等待50ns
$display("Test sequence completed at time %0t", $time);
end
endmodule
2.3.2 事件控制
module initial_event_control;
reg clk, data_ready;
reg [7:0] test_data;
// 时钟生成
initial begin
clk = 0;
forever #5 clk = ~clk; // 10ns周期时钟
end
// 测试序列
initial begin
data_ready = 0;
test_data = 8'h00;
// 等待时钟上升沿
@(posedge clk);
data_ready = 1;
test_data = 8'hAA;
// 等待下一个时钟上升沿
@(posedge clk);
test_data = 8'h55;
// 等待多个时钟周期
repeat(3) @(posedge clk);
data_ready = 0;
end
endmodule
2.4 测试台应用
2.4.1 激励生成
module testbench_stimulus;
reg [7:0] test_input;
reg test_enable;
wire [7:0] test_output;
// 被测模块实例化
dut_module dut(
.input_port(test_input),
.enable(test_enable),
.output_port(test_output)
);
// 测试激励生成
initial begin
// 初始化
test_input = 8'h00;
test_enable = 0;
// 测试序列1:基本功能
#100;
test_enable = 1;
for (integer i = 0; i < 16; i = i + 1) begin
test_input = i;
#20;
end
// 测试序列2:边界条件
test_input = 8'hFF;
#20;
test_input = 8'h00;
#20;
// 测试序列3:随机数据
repeat(10) begin
test_input = $random;
#20;
end
// 结束仿真
test_enable = 0;
#100;
$finish;
end
endmodule
2.4.2 结果检查
module result_checker;
reg [7:0] expected_result;
wire [7:0] actual_result;
integer error_count;
initial begin
error_count = 0;
// 等待稳定
#10;
// 检查测试用例1
expected_result = 8'h55;
@(actual_result);
if (actual_result !== expected_result) begin
$error("Test case 1 failed: Expected %h, got %h",
expected_result, actual_result);
error_count = error_count + 1;
end
// 检查测试用例2
expected_result = 8'hAA;
@(actual_result);
if (actual_result !== expected_result) begin
$error("Test case 2 failed: Expected %h, got %h",
expected_result, actual_result);
error_count = error_count + 1;
end
// 最终报告
#1000;
if (error_count == 0) begin
$display("All tests passed!");
end else begin
$display("%0d tests failed!", error_count);
end
end
endmodule
3. always语句详解
3.1 基本语法与特性
3.1.1 语法格式
always @(敏感性列表) begin
// 语句序列
// 当敏感性列表中的信号变化时执行
end
3.1.2 执行特性
- 🔄 循环执行: 符合条件时重复执行
- ⚡ 事件驱动: 由敏感性列表控制执行时机
- 🎯 立即响应: 敏感信号变化立即触发执行
- ✅ 可综合: 遵循规则时可以综合为硬件
3.2 敏感性列表类型
3.2.1 边沿敏感
module edge_sensitive_examples;
reg clk, rst_n, data_in, data_out;
// 时钟上升沿敏感
always @(posedge clk) begin
if (!rst_n) begin
data_out <= 1'b0;
end else begin
data_out <= data_in;
end
end
// 复位下降沿敏感
always @(negedge rst_n) begin
data_out <= 1'b0;
end
// 多边沿敏感
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
data_out <= 1'b0;
end else begin
data_out <= data_in;
end
end
endmodule
3.2.2 电平敏感
module level_sensitive_examples;
reg a, b, c, sel;
reg result;
// 组合逻辑:所有输入敏感
always @(a or b or c or sel) begin
if (sel) begin
result = a & b;
end else begin
result = a | c;
end
end
// 等价的通配符写法
always @(*) begin
if (sel) begin
result = a & b;
end else begin
result = a | c;
end
end
endmodule
3.2.3 通配符敏感性列表
module wildcard_sensitivity;
reg [7:0] a, b, c;
reg [1:0] op;
reg [7:0] result;
// always @(*) 自动包含所有读取的信号
always @(*) begin
case (op)
2'b00: result = a + b;
2'b01: result = a - b;
2'b10: result = a & b;
2'b11: result = a | b;
endcase
end
endmodule
3.3 always块的应用模式
3.3.1 组合逻辑建模
module combinational_logic_always;
input wire [3:0] a, b;
input wire [1:0] sel;
output reg [3:0] result;
output reg carry;
// 组合逻辑always块
always @(*) begin
carry = 0; // 默认值
case (sel)
2'b00: result = a & b; // 逻辑与
2'b01: result = a | b; // 逻辑或
2'b10: result = a ^ b; // 逻辑异或
2'b11: {carry, result} = a + b; // 加法
endcase
end
endmodule
3.3.2 时序逻辑建模
module sequential_logic_always;
input wire clk, rst_n;
input wire [7:0] data_in;
input wire load, shift_en;
output reg [7:0] shift_reg;
// 时序逻辑always块
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
shift_reg <= 8'h00;
end else if (load) begin
shift_reg <= data_in;
end else if (shift_en) begin
shift_reg <= {shift_reg[6:0], 1'b0}; // 左移
end
// 其他情况保持不变
end
endmodule
3.3.3 状态机建模
module state_machine_always;
input wire clk, rst_n, start, done;
output reg busy, error;
// 状态定义
typedef enum reg [1:0] {
IDLE = 2'b00,
ACTIVE = 2'b01,
WAIT = 2'b10,
ERROR = 2'b11
} state_t;
state_t current_state, next_state;
// 状态寄存器
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
current_state <= IDLE;
end else begin
current_state <= next_state;
end
end
// 次态逻辑
always @(*) begin
next_state = current_state; // 默认保持当前状态
case (current_state)
IDLE: begin
if (start) next_state = ACTIVE;
end
ACTIVE: begin
if (done) next_state = IDLE;
else if (!start) next_state = ERROR;
end
WAIT: begin
next_state = IDLE;
end
ERROR: begin
if (!start) next_state = IDLE;
end
endcase
end
// 输出逻辑
always @(*) begin
busy = (current_state == ACTIVE) || (current_state == WAIT);
error = (current_state == ERROR);
end
endmodule
4. 过程块的组织结构
4.1 块语句
4.1.1 顺序块(begin...end)
module sequential_block_demo;
reg [7:0] data;
reg clk, enable;
always @(posedge clk) begin
if (enable) begin
// 顺序执行的语句块
data <= data + 1;
$display("Data incremented to %d", data + 1);
end
end
// 带标签的块
always @(posedge clk) begin : increment_block
reg [3:0] local_counter; // 块内局部变量
if (enable) begin
local_counter = local_counter + 1;
data <= {data[6:0], local_counter[0]};
end
end
endmodule
4.1.2 并行块(fork...join)
module parallel_block_demo;
reg signal_a, signal_b, signal_c;
initial begin
// 并行执行的语句块
fork
begin // 线程1
#10 signal_a = 1;
#20 signal_a = 0;
end
begin // 线程2
#15 signal_b = 1;
#25 signal_b = 0;
end
begin // 线程3
#5 signal_c = 1;
#30 signal_c = 0;
end
join
$display("All parallel threads completed at time %0t", $time);
end
endmodule
4.2 局部变量与作用域
4.2.1 块内变量声明
module block_variables;
reg [7:0] global_var;
always @(*) begin : processing_block
reg [3:0] temp_var; // 块内局部变量
integer loop_counter; // 块内局部变量
temp_var = global_var[3:0];
for (loop_counter = 0; loop_counter < 4; loop_counter = loop_counter + 1) begin
temp_var = temp_var + 1;
end
global_var = {global_var[7:4], temp_var};
end
endmodule
4.2.2 层次化命名
module hierarchical_naming;
reg [7:0] result;
always @(*) begin : outer_block
reg [3:0] outer_temp;
begin : inner_block
reg [3:0] inner_temp;
inner_temp = 4'b0001;
outer_temp = inner_temp + 4'b0010;
end
result = {4'b0000, outer_temp};
end
// 可以通过层次化路径访问
// outer_block.outer_temp
// outer_block.inner_block.inner_temp
endmodule
5. 敏感性列表详解
5.1 敏感性列表的重要性
敏感性列表决定了always块的执行时机,是实现正确硬件行为的关键。
5.2 敏感性列表类型详解
5.2.1 时钟边沿敏感
module clock_edge_sensitivity;
input wire clk, rst_n, data_in;
output reg data_out, delayed_out;
// 单时钟边沿
always @(posedge clk) begin
data_out <= data_in;
end
// 时钟和异步复位
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
delayed_out <= 1'b0;
end else begin
delayed_out <= data_out;
end
end
endmodule
5.2.2 组合逻辑敏感性
module combinational_sensitivity;
input wire [3:0] a, b, c;
input wire sel1, sel2;
output reg [3:0] result;
// ❌ 不完整的敏感性列表
/*
always @(a or b) begin
if (sel1) begin
result = a + b;
end else begin
result = a + c; // c不在敏感性列表中!
end
end
*/
// ✅ 完整的敏感性列表
always @(a or b or c or sel1 or sel2) begin
if (sel1) begin
result = a + b;
end else if (sel2) begin
result = a + c;
end else begin
result = 4'b0000;
end
end
// ✅ 更好的方法:使用通配符
always @(*) begin
if (sel1) begin
result = a + b;
end else if (sel2) begin
result = a + c;
end else begin
result = 4'b0000;
end
end
endmodule
5.2.3 混合敏感性
module mixed_sensitivity;
input wire clk, rst_n, async_set;
input wire data_in;
output reg q, q_async;
// 同步复位和异步置位的触发器
always @(posedge clk or negedge rst_n or posedge async_set) begin
if (!rst_n) begin
q <= 1'b0; // 异步复位
end else if (async_set) begin
q <= 1'b1; // 异步置位
end else begin
q <= data_in; // 正常时钟操作
end
end
endmodule
5.3 敏感性列表的常见错误
5.3.1 不完整的敏感性列表
// ❌ 错误示例
module incomplete_sensitivity_bad;
input wire [7:0] a, b, c;
input wire sel;
output reg [7:0] result;
// sel不在敏感性列表中
always @(a or b) begin
if (sel) begin
result = a + b;
end else begin
result = a + c; // c也不在敏感性列表中
end
end
endmodule
// ✅ 正确示例
module complete_sensitivity_good;
input wire [7:0] a, b, c;
input wire sel;
output reg [7:0] result;
always @(*) begin // 自动包含所有输入
if (sel) begin
result = a + b;
end else begin
result = a + c;
end
end
endmodule
5.3.2 错误的时钟敏感性
// ❌ 错误:时钟在组合逻辑敏感性列表中
module wrong_clock_sensitivity_bad;
input wire clk, data;
output reg result;
always @(clk or data) begin // 错误!
if (clk) begin
result = data;
end else begin
result = ~data;
end
end
endmodule
// ✅ 正确:使用时钟边沿
module correct_clock_sensitivity_good;
input wire clk, data;
output reg result;
always @(posedge clk) begin
result <= data;
end
endmodule
6. 变量类型与赋值
6.1 过程块中的变量类型
6.1.1 寄存器类型变量
module register_variables;
// 过程块中只能使用reg类型变量作为赋值目标
reg single_bit;
reg [7:0] byte_reg;
reg [31:0] word_reg;
integer int_var;
real real_var;
always @(*) begin
// ✅ 正确:给reg类型赋值
single_bit = 1'b1;
byte_reg = 8'hFF;
word_reg = 32'h12345678;
int_var = 100;
real_var = 3.14159;
end
/*
wire wire_signal;
always @(*) begin
wire_signal = 1'b1; // ❌ 错误:不能给wire类型赋值
end
*/
endmodule
6.2 赋值类型
6.2.1 阻塞赋值 (=)
module blocking_assignment;
reg [3:0] a, b, c, d;
always @(*) begin
// 阻塞赋值:立即执行,按顺序
a = 4'b0001;
b = a + 1; // b = 2,使用a的新值
c = b + 1; // c = 3,使用b的新值
d = c + 1; // d = 4,使用c的新值
end
// 阻塞赋值用于组合逻辑
always @(*) begin
case ({a, b})
8'b00010010: c = 4'b1111;
8'b00110100: c = 4'b0000;
default: c = 4'b1010;
endcase
end
endmodule
6.2.2 非阻塞赋值 (<=)
module nonblocking_assignment;
input wire clk, rst_n;
reg [3:0] shift_reg [0:3];
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
// 非阻塞赋值用于时序逻辑
shift_reg[0] <= 4'b0000;
shift_reg[1] <= 4'b0000;
shift_reg[2] <= 4'b0000;
shift_reg[3] <= 4'b0000;
end else begin
// 并行赋值:所有赋值同时调度
shift_reg[0] <= data_in;
shift_reg[1] <= shift_reg[0]; // 使用旧值
shift_reg[2] <= shift_reg[1]; // 使用旧值
shift_reg[3] <= shift_reg[2]; // 使用旧值
end
end
endmodule
6.2.3 赋值类型对比
module assignment_comparison;
reg clk, a, b, c, d;
// 阻塞赋值示例
always @(posedge clk) begin
a = 1'b1;
b = a; // b获得a的新值(1)
c = b; // c获得b的新值(1)
end
// 非阻塞赋值示例
always @(posedge clk) begin
a <= 1'b1;
b <= a; // b获得a的旧值
c <= b; // c获得b的旧值
end
// 混合使用(不推荐)
always @(posedge clk) begin
a <= 1'b1;
b = a; // 可能产生意外结果
end
endmodule
7. 过程结构的应用模式
7.1 数字电路基本模块
7.1.1 计数器设计
module counter_design #(
parameter WIDTH = 8,
parameter MAX_COUNT = 255
)(
input wire clk, rst_n, enable,
input wire up_down, // 1:上计数, 0:下计数
output reg [WIDTH-1:0] count,
output reg carry, borrow
);
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
count <= {WIDTH{1'b0}};
carry <= 1'b0;
borrow <= 1'b0;
end else if (enable) begin
if (up_down) begin // 上计数
if (count == MAX_COUNT) begin
count <= {WIDTH{1'b0}};
carry <= 1'b1;
borrow <= 1'b0;
end else begin
count <= count + 1;
carry <= 1'b0;
borrow <= 1'b0;
end
end else begin // 下计数
if (count == 0) begin
count <= MAX_COUNT;
carry <= 1'b0;
borrow <= 1'b1;
end else begin
count <= count - 1;
carry <= 1'b0;
borrow <= 1'b0;
end
end
end else begin
carry <= 1'b0;
borrow <= 1'b0;
end
end
endmodule
7.1.2 FIFO设计
module fifo_design #(
parameter DATA_WIDTH = 8,
parameter DEPTH = 16,
parameter ADDR_WIDTH = $clog2(DEPTH)
)(
input wire clk, rst_n,
input wire wr_en, rd_en,
input wire [DATA_WIDTH-1:0] wr_data,
output reg [DATA_WIDTH-1:0] rd_data,
output reg full, empty,
output reg [ADDR_WIDTH:0] count
);
// 内部存储器
reg [DATA_WIDTH-1:0] memory [0:DEPTH-1];
reg [ADDR_WIDTH-1:0] wr_ptr, rd_ptr;
// 写指针控制
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
wr_ptr <= {ADDR_WIDTH{1'b0}};
end else if (wr_en && !full) begin
wr_ptr <= wr_ptr + 1;
end
end
// 读指针控制
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
rd_ptr <= {ADDR_WIDTH{1'b0}};
end else if (rd_en && !empty) begin
rd_ptr <= rd_ptr + 1;
end
end
// 写操作
always @(posedge clk) begin
if (wr_en && !full) begin
memory[wr_ptr] <= wr_data;
end
end
// 读操作
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
rd_data <= {DATA_WIDTH{1'b0}};
end else if (rd_en && !empty) begin
rd_data <= memory[rd_ptr];
end
end
// 计数器和状态标志
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
count <= {(ADDR_WIDTH+1){1'b0}};
end else begin
case ({wr_en && !full, rd_en && !empty})
2'b10: count <= count + 1; // 只写
2'b01: count <= count - 1; // 只读
// 2'b11: count保持不变(同时读写)
// 2'b00: count保持不变(无操作)
endcase
end
end
// 状态标志
always @(*) begin
full = (count == DEPTH);
empty = (count == 0);
end
endmodule
7.2 通信协议实现
7.2.1 UART发送器
module uart_transmitter #(
parameter CLOCK_FREQ = 50000000, // 50MHz
parameter BAUD_RATE = 115200 // 115200 bps
)(
input wire clk, rst_n,
input wire [7:0] data_in,
input wire start_tx,
output reg tx_serial,
output reg tx_busy,
output reg tx_done
);
// 波特率生成
localparam BAUD_TICKS = CLOCK_FREQ / BAUD_RATE;
reg [$clog2(BAUD_TICKS)-1:0] baud_counter;
reg baud_tick;
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
baud_counter <= 0;
baud_tick <= 1'b0;
end else if (baud_counter == BAUD_TICKS - 1) begin
baud_counter <= 0;
baud_tick <= 1'b1;
end else begin
baud_counter <= baud_counter + 1;
baud_tick <= 1'b0;
end
end
// 状态机
typedef enum reg [2:0] {
IDLE = 3'b000,
START_BIT = 3'b001,
DATA_BITS = 3'b010,
STOP_BIT = 3'b011,
DONE = 3'b100
} uart_state_t;
uart_state_t current_state, next_state;
reg [7:0] shift_reg;
reg [2:0] bit_counter;
// 状态寄存器
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
current_state <= IDLE;
end else begin
current_state <= next_state;
end
end
// 次态逻辑
always @(*) begin
next_state = current_state;
case (current_state)
IDLE: begin
if (start_tx) next_state = START_BIT;
end
START_BIT: begin
if (baud_tick) next_state = DATA_BITS;
end
DATA_BITS: begin
if (baud_tick && bit_counter == 7) next_state = STOP_BIT;
end
STOP_BIT: begin
if (baud_tick) next_state = DONE;
end
DONE: begin
next_state = IDLE;
end
endcase
end
// 数据寄存器和计数器
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
shift_reg <= 8'h00;
bit_counter <= 3'b000;
end else begin
case (current_state)
IDLE: begin
if (start_tx) begin
shift_reg <= data_in;
bit_counter <= 3'b000;
end
end
DATA_BITS: begin
if (baud_tick) begin
shift_reg <= {1'b0, shift_reg[7:1]};
bit_counter <= bit_counter + 1;
end
end
endcase
end
end
// 输出逻辑
always @(*) begin
case (current_state)
IDLE: tx_serial = 1'b1; // 空闲高电平
START_BIT: tx_serial = 1'b0; // 起始位
DATA_BITS: tx_serial = shift_reg[0]; // 数据位
STOP_BIT: tx_serial = 1'b1; // 停止位
DONE: tx_serial = 1'b1; // 完成
default: tx_serial = 1'b1;
endcase
tx_busy = (current_state != IDLE) && (current_state != DONE);
tx_done = (current_state == DONE);
end
endmodule
8. 综合性与可实现性
8.1 可综合的过程结构
8.1.1 可综合的always块模式
// ✅ 可综合:组合逻辑
always @(*) begin
// 组合逻辑规则
end
// ✅ 可综合:同步时序逻辑
always @(posedge clk) begin
// 时序逻辑规则
end
// ✅ 可综合:异步复位的时序逻辑
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
// 复位逻辑
end else begin
// 正常时序逻辑
end
end
8.1.2 不可综合的结构
// ❌ 不可综合:initial块
initial begin
// 初始化代码,仅用于仿真
end
// ❌ 不可综合:延时语句
always @(posedge clk) begin
#10 data <= input_data; // 延时不可综合
end
// ❌ 不可综合:forever循环
always begin
forever begin
#5 clk = ~clk; // 时钟生成,仅仿真
end
end
// ❌ 不可综合:while循环(无明确退出条件)
always @(posedge clk) begin
while (condition) begin // 可能无限循环
// 某些操作
end
end
8.2 综合优化指导
8.2.1 避免锁存器的产生
// ❌ 错误:会产生意外的锁存器
module latch_problem_bad;
input wire [1:0] sel;
input wire [7:0] a, b, c;
output reg [7:0] result;
always @(*) begin
case (sel)
2'b00: result = a;
2'b01: result = b;
// 缺少其他情况的处理
endcase
end // result在某些条件下保持原值,产生锁存器
endmodule
// ✅ 正确:避免锁存器
module no_latch_good;
input wire [1:0] sel;
input wire [7:0] a, b, c;
output reg [7:0] result;
always @(*) begin
case (sel)
2'b00: result = a;
2'b01: result = b;
2'b10: result = c;
default: result = 8'h00; // 默认值
endcase
end
endmodule
8.2.2 资源共享优化
module resource_sharing;
input wire clk, rst_n;
input wire [7:0] a, b, c, d;
input wire sel;
output reg [8:0] result;
// ❌ 不好:两个独立的加法器
/*
always @(posedge clk) begin
if (sel) begin
result <= a + b;
end else begin
result <= c + d;
end
end
*/
// ✅ 更好:共享加法器
reg [7:0] operand1, operand2;
always @(*) begin
if (sel) begin
operand1 = a;
operand2 = b;
end else begin
operand1 = c;
operand2 = d;
end
end
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
result <= 9'h000;
end else begin
result <= operand1 + operand2;
end
end
endmodule
9. 最佳实践
9.1 编码规范
9.1.1 always块的使用原则
module coding_standards;
input wire clk, rst_n, data_in;
output reg data_out;
output wire comb_out;
// ✅ 好的实践
// 1. 组合逻辑使用always @(*)
reg temp_signal;
always @(*) begin
temp_signal = data_in & rst_n;
end
// 2. 时序逻辑使用always @(posedge clk)
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
data_out <= 1'b0;
end else begin
data_out <= temp_signal;
end
end
// 3. 简单组合逻辑使用连续赋值
assign comb_out = data_in | data_out;
// ❌ 避免的做法
/*
// 不要在同一个always块中混合组合和时序逻辑
always @(posedge clk or data_in) begin // 错误!
// ...
end
// 不要使用多个时钟边沿
always @(posedge clk or posedge other_clk) begin // 避免!
// ...
end
*/
endmodule
9.1.2 复位策略
module reset_strategies;
input wire clk, rst_n, async_rst_n;
input wire [7:0] data_in;
output reg [7:0] sync_reset_reg, async_reset_reg;
// 同步复位
always @(posedge clk) begin
if (!rst_n) begin
sync_reset_reg <= 8'h00;
end else begin
sync_reset_reg <= data_in;
end
end
// 异步复位(推荐)
always @(posedge clk or negedge async_rst_n) begin
if (!async_rst_n) begin
async_reset_reg <= 8'h00;
end else begin
async_reset_reg <= data_in;
end
end
endmodule
9.2 性能优化
9.2.1 关键路径优化
module critical_path_optimization;
input wire clk, rst_n;
input wire [31:0] a, b, c, d;
output reg [31:0] result;
// ❌ 不好:长的关键路径
/*
always @(posedge clk) begin
result <= ((a + b) * (c + d)) + ((a - b) * (c - d));
end
*/
// ✅ 更好:流水线处理
reg [31:0] sum1, sum2, diff1, diff2;
reg [31:0] product1, product2;
// 第一级流水线
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
sum1 <= 32'h00000000;
sum2 <= 32'h00000000;
diff1 <= 32'h00000000;
diff2 <= 32'h00000000;
end else begin
sum1 <= a + b;
sum2 <= c + d;
diff1 <= a - b;
diff2 <= c - d;
end
end
// 第二级流水线
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
product1 <= 32'h00000000;
product2 <= 32'h00000000;
end else begin
product1 <= sum1 * sum2;
product2 <= diff1 * diff2;
end
end
// 第三级流水线
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
result <= 32'h00000000;
end else begin
result <= product1 + product2;
end
end
endmodule
9.2.2 存储器推断优化
module memory_inference;
input wire clk, we;
input wire [9:0] addr;
input wire [7:0] data_in;
output reg [7:0] data_out;
// 推断块RAM
reg [7:0] memory [0:1023];
always @(posedge clk) begin
if (we) begin
memory[addr] <= data_in;
end
data_out <= memory[addr]; // 注册输出
end
endmodule
10. 常见错误与调试
10.1 语法和语义错误
10.1.1 敏感性列表错误
// ❌ 常见错误示例
module sensitivity_errors_bad;
input wire a, b, c, sel;
output reg result;
// 错误1:不完整的敏感性列表
always @(a) begin
if (sel) begin
result = a + b; // b和sel不在敏感性列表中
end else begin
result = c; // c不在敏感性列表中
end
end
// 错误2:时钟信号在组合逻辑中
always @(a or b or clk) begin // clk不应该在这里
result = clk ? a : b;
end
endmodule
// ✅ 正确的写法
module sensitivity_correct_good;
input wire a, b, c, sel, clk;
output reg result, clocked_result;
// 正确1:完整的敏感性列表
always @(a or b or c or sel) begin
if (sel) begin
result = a + b;
end else begin
result = c;
end
end
// 或者使用通配符
always @(*) begin
if (sel) begin
result = a + b;
end else begin
result = c;
end
end
// 正确2:时钟用于时序逻辑
always @(posedge clk) begin
clocked_result <= sel ? a : b;
end
endmodule
10.1.2 赋值类型错误
// ❌ 错误的赋值使用
module assignment_errors_bad;
input wire clk, rst_n, data;
output reg q1, q2;
wire comb_out;
// 错误1:时序逻辑中使用阻塞赋值
always @(posedge clk) begin
q1 = data; // 应该使用非阻塞赋值
end
// 错误2:组合逻辑中使用非阻塞赋值
always @(*) begin
q2 <= data; // 应该使用阻塞赋值
end
// 错误3:给wire类型赋值
/*
always @(*) begin
comb_out = data; // 错误:不能给wire赋值
end
*/
endmodule
// ✅ 正确的赋值使用
module assignment_correct_good;
input wire clk, rst_n, data;
output reg q1, q2;
output wire comb_out;
// 正确1:时序逻辑使用非阻塞赋值
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
q1 <= 1'b0;
end else begin
q1 <= data;
end
end
// 正确2:组合逻辑使用阻塞赋值
always @(*) begin
q2 = data;
end
// 正确3:wire使用连续赋值
assign comb_out = data;
endmodule
10.2 逻辑错误
10.2.1 竞争条件
// ❌ 存在竞争条件的代码
module race_condition_bad;
reg clk, a, b;
always @(posedge clk) begin
a = 1'b1;
b = a; // b的值取决于a何时更新
end
endmodule
// ✅ 避免竞争条件
module race_condition_good;
reg clk, a, b;
always @(posedge clk) begin
a <= 1'b1;
b <= a; // b获得a的旧值,避免竞争
end
endmodule
10.2.2 无限循环
// ❌ 可能导致无限循环
module infinite_loop_bad;
reg clk, enable;
reg [7:0] counter;
always @(posedge clk) begin
while (enable) begin // 危险:可能无限循环
counter = counter + 1;
// 如果enable永远不变为0...
end
end
endmodule
// ✅ 安全的循环控制
module safe_loop_good;
reg clk, enable;
reg [7:0] counter;
integer i;
always @(posedge clk) begin
if (enable) begin
for (i = 0; i < 8; i = i + 1) begin // 明确的循环界限
counter[i] = ~counter[i];
end
end
end
endmodule
10.3 调试技巧
10.3.1 添加调试信号
module debug_techniques;
input wire clk, rst_n, start;
output reg busy, done;
// 状态机
typedef enum reg [1:0] {
IDLE = 2'b00,
ACTIVE = 2'b01,
FINISH = 2'b10
} state_t;
state_t current_state, next_state;
// 调试信号
`ifdef DEBUG
reg [31:0] state_counter; // 状态持续时间计数
reg state_changed; // 状态变化标志
`endif
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
current_state <= IDLE;
`ifdef DEBUG
state_counter <= 0;
state_changed <= 1'b0;
`endif
end else begin
`ifdef DEBUG
if (current_state != next_state) begin
state_changed <= 1'b1;
state_counter <= 0;
$display("State change at time %0t: %s -> %s",
$time, current_state.name(), next_state.name());
end else begin
state_changed <= 1'b0;
state_counter <= state_counter + 1;
end
`endif
current_state <= next_state;
end
end
// 状态机逻辑...
always @(*) begin
// 次态逻辑
case (current_state)
IDLE: next_state = start ? ACTIVE : IDLE;
ACTIVE: next_state = FINISH;
FINISH: next_state = IDLE;
default: next_state = IDLE;
endcase
// 输出逻辑
busy = (current_state == ACTIVE);
done = (current_state == FINISH);
end
endmodule
10.3.2 断言和检查
module assertion_checks;
input wire clk, rst_n, req, ack;
output reg grant;
// 基本握手协议
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
grant <= 1'b0;
end else if (req && !grant) begin
grant <= 1'b1;
end else if (ack && grant) begin
grant <= 1'b0;
end
end
// 协议检查(仅仿真)
`ifdef SIMULATION
// 检查:grant为高时,req必须保持
always @(posedge clk) begin
if (grant && !req) begin
$error("Protocol violation: req dropped while grant active at time %0t", $time);
end
end
// 检查:ack只能在grant为高时出现
always @(posedge clk) begin
if (ack && !grant) begin
$error("Protocol violation: ack without grant at time %0t", $time);
end
end
`endif
endmodule
🎯 总结
核心要点回顾
- initial语句: 仅执行一次,用于初始化和测试激励
- always语句: 重复执行,用于组合逻辑和时序逻辑建模
- 敏感性列表: 控制always块的执行时机,是正确建模的关键
- 赋值类型: 阻塞赋值用于组合逻辑,非阻塞赋值用于时序逻辑
- 综合性: initial块不可综合,always块需遵循特定规则才可综合
设计原则总结
- 🎯 职责分离: 组合逻辑和时序逻辑分开设计
- 📝 规范编码: 遵循标准的敏感性列表和赋值规则
- 🔧 避免锁存器: 确保组合逻辑的完整性
- ⚡ 性能优化: 考虑关键路径和资源共享
- 🛡️ 调试友好: 添加适当的调试和检查机制
学习路径建议
- 掌握initial和always的基本语法和用法
- 理解敏感性列表的重要性和正确写法
- 练习阻塞赋值和非阻塞赋值的区别
- 学习常见数字电路模块的设计模式
- 掌握可综合代码的编写规范
💡 重要提醒: 过程结构是Verilog行为建模的核心,正确理解和使用initial和always语句是设计可靠数字电路的基础。记住:硬件描述语言描述的是硬件行为,而不是软件程序!