1. Verilog基础规则解析
1.1 输入信号类型规则
在Verilog设计中,输入信号必须声明为net类型(通常使用wire)。这条规则适用于两种场景:
- 模块的input端口信号
- 子模块output回连到当前模块的信号(从当前模块视角看也是输入)
注意:在Verilog中,wire类型用于表示组合逻辑连接,而reg类型用于表示存储元件。输入信号必须使用wire类型是因为它们通常来自外部连接或组合逻辑输出。
1.2 同步寄存器赋值规则
同步寄存器信号必须遵循以下原则:
- 只能在一个always块中使用非阻塞赋值(<=)
- 每个时钟上升沿最多更新一次(取最后一次赋值)
- 更新后的值在该上升沿之后才能被组合逻辑使用
- 如果要用作另一个寄存器的采样条件,必须等到下一个时钟上升沿
verilog复制// 正确示例
always @(posedge clk) begin
reg_a <= input_a; // 非阻塞赋值
reg_b <= input_b;
end
// 错误示例:同一寄存器在多个always块赋值
always @(posedge clk) begin
reg_a <= input_a;
end
always @(posedge clk) begin
reg_a <= input_b; // 违反单一驱动原则
end
1.3 reg信号综合规则
reg信号的综合结果取决于always块的敏感列表和赋值完整性:
| always块类型 | 赋值完整性 | 综合结果 |
|---|---|---|
| 边沿敏感(posedge/negedge) | 完整/不完整 | D触发器 |
| 组合敏感(@*) | 完整 | 纯组合逻辑 |
| 组合敏感(@*) | 不完整 | 锁存器 |
实际经验:在FPGA设计中应尽量避免产生锁存器,因为它们可能导致时序问题和不可预测的行为。确保组合逻辑always块中所有可能的路径都有明确的赋值。
2. 基本电路模块实现
2.1 异步SR锁存器
异步SR锁存器是最基本的存储元件之一,其特性方程为:Q = Q·R' + S
verilog复制module sr_latch_async (
input wire i_reset, // 高有效复位(A信号)
input wire i_set, // 高有效置位(B信号)
output wire o_state // 输出状态(C信号)
);
assign o_state = o_state & ~i_reset | i_set;
endmodule
工作原理:
- 当i_set=1时,输出o_state被置1
- 当i_reset=1时,输出o_state被清零
- 两者同时为1时,行为取决于具体实现(可能不稳定)
2.2 边沿检测电路
2.2.1 上升沿检测
verilog复制module edge_detect_pos_xxx (
input wire i_clk,
input wire i_rst_n,
input wire i_xxx,
output wire o_xxx_pos
);
reg r_xxx_dly;
always @(posedge i_clk or negedge i_rst_n) begin
if (!i_rst_n) r_xxx_dly <= 1'b0;
else r_xxx_dly <= i_xxx;
end
assign o_xxx_pos = i_xxx && !r_xxx_dly;
endmodule
时序分析:
- 输入信号i_xxx从0变为1
- 在下一个时钟上升沿,r_xxx_dly捕获到0(前一状态)
- 当前i_xxx=1,前一状态r_xxx_dly=0,产生一个时钟周期的高脉冲
2.2.2 下降沿检测
verilog复制module edge_detect_neg_xxx (
input wire i_clk,
input wire i_rst_n,
input wire i_xxx,
output wire o_xxx_neg
);
reg r_xxx_dly;
always @(posedge i_clk or negedge i_rst_n) begin
if (!i_rst_n) r_xxx_dly <= 1'b0;
else r_xxx_dly <= i_xxx;
end
assign o_xxx_neg = !i_xxx && r_xxx_dly;
endmodule
应用场景:
- 按键消抖检测
- 外部事件触发检测
- 同步信号状态变化监测
3. 同步化设计技术
3.1 单比特同步器
3.1.1 双拍同步方案1
verilog复制module sync_2stage_bit_xxx (
input wire i_clk,
input wire i_rst_n,
input wire i_xxx,
output reg o_xxx_sync
);
reg r_xxx_stage1;
always @(posedge i_clk or negedge i_rst_n) begin
if (!i_rst_n) begin
r_xxx_stage1 <= 1'b0;
o_xxx_sync <= 1'b0;
end else begin
r_xxx_stage1 <= i_xxx;
o_xxx_sync <= r_xxx_stage1;
end
end
endmodule
3.1.2 双拍同步方案2
verilog复制module sync_2stage_bit_xxx (
input wire i_clk,
input wire i_rst_n,
input wire i_xxx,
output wire o_xxx_sync
);
reg [1:0] r_xxx;
always @(posedge i_clk or negedge i_rst_n) begin
if (!i_rst_n) r_xxx <= 2'b00;
else r_xxx <= {r_xxx[0], i_xxx};
end
assign o_xxx_sync = r_xxx[1];
endmodule
重要提示:同步器输出应使用r_xxx[1]而非r_xxx[0],因为r_xxx[0]可能仍处于亚稳态。同步器只能降低亚稳态概率,不能完全消除。
3.2 多比特总线同步
verilog复制module sync_2stage_bus_xxx #(
parameter WIDTH = 8
)(
input wire i_clk,
input wire i_rst_n,
input wire [WIDTH-1:0] i_xxx,
output reg [WIDTH-1:0] o_xxx_sync
);
reg [WIDTH-1:0] r_xxx_stage1;
always @(posedge i_clk or negedge i_rst_n) begin
if (!i_rst_n) begin
r_xxx_stage1 <= {WIDTH{1'b0}};
o_xxx_sync <= {WIDTH{1'b0}};
end else begin
r_xxx_stage1 <= i_xxx;
o_xxx_sync <= r_xxx_stage1;
end
end
endmodule
设计要点:
- 多比特信号同步需要使用握手协议或FIFO,简单的双拍同步可能导致数据错位
- 对于相关控制信号,建议使用格雷码编码后再同步
- 同步后的信号应在同一时钟域内使用
3.3 三拍同步下降沿检测
verilog复制module sync_3stage_neg_edge_xxx (
input wire i_clk,
input wire i_rst_n,
input wire i_xxx,
output wire o_xxx_sync,
output wire o_xxx_neg
);
reg [2:0] r_xxx;
always @(posedge i_clk or negedge i_rst_n) begin
if (!i_rst_n) r_xxx <= 3'b000;
else r_xxx <= {r_xxx[1:0], i_xxx};
end
assign o_xxx_sync = r_xxx[1];
assign o_xxx_neg = r_xxx[2] & ~r_xxx[1];
endmodule
同步器级数选择:
- 一般应用:2级足够
- 高可靠性应用:3级或更多
- 关键信号:考虑使用专用同步器IP核
4. 常用功能模块设计
4.1 参数化计数器
verilog复制module counter_xxx #(
parameter WIDTH = 8,
parameter MAX = 255
)(
input wire i_clk,
input wire i_rst_n,
input wire i_en,
output reg [WIDTH-1:0] o_cnt,
output wire o_tc
);
assign o_tc = (o_cnt == MAX);
always @(posedge i_clk or negedge i_rst_n) begin
if (!i_rst_n) o_cnt <= {WIDTH{1'b0}};
else if (i_en) begin
if (o_cnt == MAX) o_cnt <= {WIDTH{1'b0}};
else o_cnt <= o_cnt + 1'b1;
end
end
endmodule
应用场景:
- 时钟分频
- 超时计数
- 事件间隔测量
- PWM周期控制
4.2 状态机设计
4.2.1 Moore状态机
verilog复制module fsm_moore_xxx #(
parameter WIDTH = 3
)(
input wire i_clk,
input wire i_rst_n,
input wire i_start,
output reg [WIDTH-1:0] o_state,
output reg o_done
);
localparam S_IDLE = 3'b000,
S_RUN = 3'b001,
S_WAIT = 3'b010,
S_FIN = 3'b011,
S_ERROR = 3'b100;
always @(posedge i_clk or negedge i_rst_n) begin
if (!i_rst_n) o_state <= S_IDLE;
else begin
case(o_state)
S_IDLE: o_state <= i_start ? S_RUN : S_IDLE;
S_RUN: o_state <= S_WAIT;
S_WAIT: o_state <= S_FIN;
S_FIN: o_state <= S_IDLE;
S_ERROR: o_state <= S_IDLE;
default: o_state <= S_IDLE;
endcase
end
end
always @(*) begin
o_done = (o_state == S_FIN);
end
endmodule
4.2.2 Mealy状态机
verilog复制module fsm_mealy_xxx #(
parameter WIDTH = 3
)(
input wire i_clk,
input wire i_rst_n,
input wire i_start,
input wire i_ack,
output reg [WIDTH-1:0] o_state,
output wire o_done
);
localparam S_IDLE = 3'b000,
S_RUN = 3'b001,
S_WAIT = 3'b010,
S_FIN = 3'b011,
S_ERROR = 3'b100;
always @(posedge i_clk or negedge i_rst_n) begin
if (!i_rst_n) o_state <= S_IDLE;
else begin
case(o_state)
S_IDLE: o_state <= i_start ? S_RUN : S_IDLE;
S_RUN: o_state <= S_WAIT;
S_WAIT: o_state <= i_ack ? S_FIN : S_WAIT;
S_FIN: o_state <= S_IDLE;
S_ERROR: o_state <= S_IDLE;
default: o_state <= S_IDLE;
endcase
end
end
assign o_done = (o_state == S_WAIT) && i_ack;
endmodule
状态机设计要点:
- 使用独热码(one-hot)编码状态可以提高FPGA中的性能
- 为每个状态添加默认转换路径,增强鲁棒性
- 状态寄存器与下一状态逻辑分离,提高可读性
- 输出信号根据需要使用寄存器输出以减少毛刺
4.3 PWM生成器
verilog复制module pwm_gen_xxx #(
parameter WIDTH = 8
)(
input wire i_clk,
input wire i_rst_n,
input wire [WIDTH-1:0] i_duty,
output reg o_pwm
);
reg [WIDTH-1:0] r_cnt;
always @(posedge i_clk or negedge i_rst_n) begin
if (!i_rst_n) r_cnt <= {WIDTH{1'b0}};
else r_cnt <= r_cnt + 1'b1;
end
always @(posedge i_clk or negedge i_rst_n) begin
if (!i_rst_n) o_pwm <= 1'b0;
else o_pwm <= (r_cnt < i_duty);
end
endmodule
PWM参数计算:
- 计数器位宽决定PWM频率分辨率
- PWM频率 = 时钟频率 / (2^WIDTH)
- 占空比 = i_duty / (2^WIDTH)
4.4 单拍脉冲生成
verilog复制module pulse_gen_xxx (
input wire i_clk,
input wire i_rst_n,
input wire i_trig,
output wire o_pulse
);
reg r_trig_dly;
always @(posedge i_clk or negedge i_rst_n) begin
if (!i_rst_n) r_trig_dly <= 1'b0;
else r_trig_dly <= i_trig;
end
assign o_pulse = i_trig && !r_trig_dly;
endmodule
应用技巧:
- 可用于将长信号转换为单时钟周期脉冲
- 常用于启动信号生成、边沿检测后的脉冲生成
- 可扩展为可编程宽度脉冲发生器
5. 设计验证与调试
5.1 在线仿真工具
-
DigitalJS:基于浏览器的数字电路仿真工具,支持Verilog代码可视化和仿真
- 网址:https://digitaljs.tilk.eu/
- 特点:快速验证小型设计,可视化电路结构
-
Logicly:交互式逻辑电路仿真器
- 网址:https://logic.ly/demo/
- 特点:适合验证基本逻辑电路,直观展示信号传播
5.2 常见设计问题排查
5.2.1 锁存器意外生成
现象:
- 综合报告中出现非预期的锁存器
- 时序分析显示保持时间违规
原因:
- 组合逻辑always块中未覆盖所有条件分支
- case语句缺少default分支
- if语句缺少else分支
解决方案:
verilog复制// 错误示例:会产生锁存器
always @(*) begin
if (enable) out = in;
end
// 正确示例:完整赋值
always @(*) begin
if (enable) out = in;
else out = 1'b0;
end
5.2.2 多驱动冲突
现象:
- 仿真结果不符合预期
- 综合警告"multi-driven net"
原因:
- 同一信号在多个always块或assign语句中被赋值
- 组合逻辑和时序逻辑同时驱动同一信号
解决方案:
verilog复制// 错误示例:多驱动
always @(posedge clk) begin
reg_a <= in1;
end
always @(posedge clk) begin
reg_a <= in2; // 冲突
end
// 正确示例:单一驱动
always @(posedge clk) begin
if (sel) reg_a <= in1;
else reg_a <= in2;
end
5.2.3 亚稳态问题
现象:
- 系统偶尔出现不可预测的行为
- 跨时钟域信号采样不稳定
解决方案:
- 对异步信号使用两级或多级同步器
- 对多比特总线使用握手协议或异步FIFO
- 关键信号使用专用同步器IP核
verilog复制// 两级同步器示例
reg [1:0] sync_reg;
always @(posedge clk or negedge rst_n) begin
if (!rst_n) sync_reg <= 2'b00;
else sync_reg <= {sync_reg[0], async_signal};
end
wire sync_signal = sync_reg[1];
6. 高级设计技巧
6.1 参数化设计
Verilog的参数化设计可以大大提高代码复用性:
verilog复制module generic_module #(
parameter WIDTH = 8,
parameter DEPTH = 16,
parameter INIT_VAL = 0
)(
input wire [WIDTH-1:0] data_in,
output reg [WIDTH-1:0] data_out
);
// 使用参数的实现...
endmodule
应用场景:
- 可配置位宽的数据处理模块
- 可调整深度的存储器
- 可编程时序参数的状态机
6.2 生成块(Generate)应用
Generate语句可用于创建可配置的硬件结构:
verilog复制module shift_register #(parameter LENGTH = 8) (
input clk,
input rst_n,
input din,
output dout
);
reg [LENGTH-1:0] sr;
genvar i;
generate
for (i=1; i<LENGTH; i=i+1) begin : shift
always @(posedge clk or negedge rst_n) begin
if (!rst_n) sr[i] <= 1'b0;
else sr[i] <= sr[i-1];
end
end
endgenerate
always @(posedge clk or negedge rst_n) begin
if (!rst_n) sr[0] <= 1'b0;
else sr[0] <= din;
end
assign dout = sr[LENGTH-1];
endmodule
6.3 时序约束考虑
良好的Verilog设计应考虑时序约束:
-
寄存器到寄存器路径:
- 确保组合逻辑延迟不超过时钟周期
- 使用流水线技术分割长组合路径
-
输入延迟约束:
- 对外部输入信号设置合理的输入延迟约束
- 考虑板级信号传播延迟
-
输出延迟约束:
- 根据外部器件要求设置输出延迟
- 考虑时钟到输出时间
-
多周期路径:
- 对明确的多周期路径添加约束
- 避免工具过度优化导致功能错误
7. 代码风格与可维护性
7.1 命名约定
| 元素类型 | 前缀 | 示例 |
|---|---|---|
| 时钟信号 | clk_ | clk_main, clk_50m |
| 复位信号 | rst_ | rst_n, rst_async |
| 低有效信号 | _n | enable_n, reset_n |
| 寄存器输出 | r_ | r_counter, r_state |
| 线型信号 | w_ | w_data, w_valid |
| 模块输入 | i_ | i_start, i_data |
| 模块输出 | o_ | o_ready, o_result |
| 参数 | PARAM_ | PARAM_WIDTH, PARAM_DEPTH |
7.2 注释规范
- 模块头注释:
verilog复制/////////////////////////////////////////////////////////////////////////////
// Module Name: edge_detect_pos
// Description: 上升沿检测模块,输出一个时钟周期宽度的脉冲
// Parameters:
// None
// Inputs:
// i_clk - 系统时钟
// i_rst_n - 异步复位,低有效
// i_sig - 输入信号
// Outputs:
// o_pulse - 上升沿检测脉冲
// Author: Your Name
// Date: 2023-11-15
/////////////////////////////////////////////////////////////////////////////
- 代码段注释:
verilog复制// 两级同步器降低亚稳态概率
reg [1:0] r_sync;
always @(posedge clk or negedge rst_n) begin
if (!rst_n) r_sync <= 2'b00;
else r_sync <= {r_sync[0], async_in};
end
7.3 测试平台设计要点
- 时钟生成:
verilog复制initial begin
clk = 1'b0;
forever #10 clk = ~clk; // 50MHz时钟
end
- 复位控制:
verilog复制initial begin
rst_n = 1'b0;
#100 rst_n = 1'b1; // 100ns后释放复位
end
- 测试用例:
verilog复制task test_case1;
input [7:0] test_data;
begin
@(posedge clk);
data_in = test_data;
valid_in = 1'b1;
@(posedge clk);
valid_in = 1'b0;
wait(ready_out);
$display("Test case1: Input=%h, Output=%h", test_data, data_out);
end
endtask
- 自动验证:
verilog复制always @(posedge clk) begin
if (valid_out) begin
if (data_out !== expected_data) begin
$error("Mismatch at time %t: Expected=%h, Got=%h",
$time, expected_data, data_out);
error_count = error_count + 1;
end
end
end
8. 性能优化技巧
8.1 面积优化
-
资源共享:
- 对不并行的操作使用同一计算单元
- 使用多路复用器共享资源
-
状态编码优化:
- 小型状态机使用二进制编码
- 大型状态机使用独热码(one-hot)编码
-
运算符选择:
- 使用位操作代替算术运算
- 避免不必要的宽位运算
8.2 速度优化
- 流水线设计:
verilog复制// 非流水线设计
always @(posedge clk) begin
result <= a * b + c * d;
end
// 流水线设计
reg [15:0] stage1, stage2;
always @(posedge clk) begin
stage1 <= a * b; // 第一级:乘法
stage2 <= stage1 + c * d; // 第二级:加法
result <= stage2; // 第三级:输出
end
-
关键路径分割:
- 识别时序报告中的关键路径
- 添加寄存器分割长组合路径
-
并行计算:
- 展开循环增加并行度
- 使用多操作数同时计算
8.3 功耗优化
-
时钟门控:
- 对不工作的模块停止时钟
- 使用专用时钟门控单元
-
数据使能:
- 只有数据变化时才更新寄存器
- 使用使能信号控制数据通路
-
存储器分区:
- 将大存储器分为多个小块
- 只激活当前使用的存储块
9. 跨时钟域设计
9.1 单比特信号同步
- 电平同步:
verilog复制module sync_level #(
parameter STAGES = 2
)(
input clk,
input rst_n,
input async_in,
output sync_out
);
reg [STAGES-1:0] sync_reg;
always @(posedge clk or negedge rst_n) begin
if (!rst_n) sync_reg <= {STAGES{1'b0}};
else sync_reg <= {sync_reg[STAGES-2:0], async_in};
end
assign sync_out = sync_reg[STAGES-1];
endmodule
- 边沿检测同步:
verilog复制module sync_edge #(
parameter STAGES = 2
)(
input clk,
input rst_n,
input async_in,
output pos_edge,
output neg_edge
);
reg [STAGES:0] sync_reg;
always @(posedge clk or negedge rst_n) begin
if (!rst_n) sync_reg <= {(STAGES+1){1'b0}};
else sync_reg <= {sync_reg[STAGES-1:0], async_in};
end
assign pos_edge = ~sync_reg[STAGES] & sync_reg[STAGES-1];
assign neg_edge = sync_reg[STAGES] & ~sync_reg[STAGES-1];
endmodule
9.2 多比特信号传输
- 握手协议:
verilog复制module handshake_sync #(
parameter WIDTH = 8
)(
input src_clk,
input dst_clk,
input rst_n,
input [WIDTH-1:0] src_data,
input src_valid,
output src_ready,
output [WIDTH-1:0] dst_data,
output dst_valid,
input dst_ready
);
// 实现握手逻辑...
endmodule
- 异步FIFO:
verilog复制module async_fifo #(
parameter WIDTH = 8,
parameter DEPTH = 16
)(
input wr_clk,
input rd_clk,
input rst_n,
input [WIDTH-1:0] wr_data,
input wr_en,
output full,
output [WIDTH-1:0] rd_data,
input rd_en,
output empty
);
// FIFO实现...
endmodule
9.3 格雷码计数器
verilog复制module gray_counter #(
parameter WIDTH = 4
)(
input clk,
input rst_n,
input inc,
output [WIDTH-1:0] gray_out
);
reg [WIDTH-1:0] binary_cnt;
wire [WIDTH-1:0] gray_cnt;
always @(posedge clk or negedge rst_n) begin
if (!rst_n) binary_cnt <= {WIDTH{1'b0}};
else if (inc) binary_cnt <= binary_cnt + 1'b1;
end
// 二进制转格雷码
assign gray_cnt = binary_cnt ^ (binary_cnt >> 1);
// 输出寄存器
always @(posedge clk or negedge rst_n) begin
if (!rst_n) gray_out <= {WIDTH{1'b0}};
else gray_out <= gray_cnt;
end
endmodule
10. 实用设计模式
10.1 数据流水线
verilog复制module pipeline #(
parameter WIDTH = 8,
parameter STAGES = 3
)(
input clk,
input rst_n,
input [WIDTH-1:0] din,
input valid_in,
output [WIDTH-1:0] dout,
output valid_out
);
reg [WIDTH-1:0] pipe_reg [0:STAGES-1];
reg [STAGES-1:0] valid_reg;
integer i;
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
for (i=0; i<STAGES; i=i+1) begin
pipe_reg[i] <= {WIDTH{1'b0}};
end
valid_reg <= {STAGES{1'b0}};
end else begin
pipe_reg[0] <= din;
for (i=1; i<STAGES; i=i+1) begin
pipe_reg[i] <= pipe_reg[i-1];
end
valid_reg <= {valid_reg[STAGES-2:0], valid_in};
end
end
assign dout = pipe_reg[STAGES-1];
assign valid_out = valid_reg[STAGES-1];
endmodule
10.2 仲裁器设计
verilog复制module round_robin_arbiter #(
parameter REQ_WIDTH = 4
)(
input clk,
input rst_n,
input [REQ_WIDTH-1:0] req,
output [REQ_WIDTH-1:0] grant
);
reg [REQ_WIDTH-1:0] last_grant;
wire [REQ_WIDTH-1:0] mask_higher_pri = (last_grant - 1) | last_grant;
wire [REQ_WIDTH-1:0] masked_req = req & ~mask_higher_pri;
wire [REQ_WIDTH-1:0] unmasked_req = req & mask_higher_pri;
wire [REQ_WIDTH-1:0] next_grant =
(masked_req != 0) ? (masked_req & -masked_req) :
(unmasked_req != 0) ? (unmasked_req & -unmasked_req) :
{REQ_WIDTH{1'b0}};
always @(posedge clk or negedge rst_n) begin
if (!rst_n) last_grant <= {REQ_WIDTH{1'b0}};
else if (|req) last_grant <= next_grant;
end
assign grant = next_grant;
endmodule
10.3 错误检测与纠正
verilog复制module ecc_encoder #(
parameter DATA_WIDTH = 8
)(
input [DATA_WIDTH-1:0] data_in,
output [DATA_WIDTH+$clog2(DATA_WIDTH):0] code_out
);
// 计算校验位
// 实现汉明码编码逻辑...
endmodule
module ecc_decoder #(
parameter DATA_WIDTH = 8
)(
input [DATA_WIDTH+$clog2(DATA_WIDTH):0] code_in,
output [DATA_WIDTH-1:0] data_out,
output [1:0] err_flag // 00:无错, 01:单错, 10:双错
);
// 错误检测与纠正逻辑...
endmodule
11. 系统集成考虑
11.1 模块接口标准化
-
时钟与复位:
- 统一使用i_clk和i_rst_n作为接口信号名
- 复位极性一致(推荐低有效)
-
数据总线:
- 使用valid/ready握手信号
- 数据信号命名包含位宽信息(如data_8b)
-
控制信号:
- 使能信号使用enable后缀
- 完成指示使用done后缀
11.2 参数传递策略
- 模块参数化:
verilog复制module fifo #(
parameter WIDTH = 8,
parameter DEPTH = 256,
parameter AFULL_THRESH = 240,
parameter AEMPTY_THRESH = 16
)(
// 接口定义...
);
- 参数验证:
verilog复制initial begin
if (DEPTH & (DEPTH - 1)) begin
$error("FIFO depth must be power of 2");
$finish;
end
end
11.3 测试点插入
- 观测信号定义:
verilog复制// 测试点信号
`ifdef SIMULATION
wire [7:0] debug_counter = u_counter.o_cnt;
wire [2:0] debug_state = u_fsm.o_state;
`endif
- 覆盖率收集:
verilog复制// 功能覆盖率定义
covergroup fsm_cov @(posedge clk);
state: coverpoint u_fsm.o_state {
bins states[] = {[0:7]};
}
transition: coverpoint u_fsm.o_state {
bins trans[] = ([0:7] => [0:7]);
}
endgroup
12. 验证与调试技巧
12.1 波形调试技巧
- 信号分组:
verilog复制// 在波形查看器中创建信号组
initial begin
$dumpvars(0, testbench); // 顶层信号
$dumpvars(1, testbench.u_dut.ctrl_path); // 控制路径
$dumpvars(1, testbench.u_dut.data_path); // 数据路径
end
- 触发条件设置:
verilog复制// 设置复杂触发条件
initial begin
$display("Setting trigger condition");
$trigger_set(
"u_fsm.o_state == 3'b101 && u_counter.o_cnt == 8'hFF",
"Trigger on state 5 and counter max"
);
end
12.2 断言验证
verilog复制// 立即断言
always @(posedge clk) begin
assert (bus_valid || !bus_grant)
else $error("Grant without valid at %t", $time);
end
// 并发断言
property p_no_glitch;
@(posedge clk) disable iff (!rst_n)
$rose(signal) |=> $stable(signal);
endproperty
assert property (p_no_glitch);
12.3 性能分析
- 资源利用率分析:
verilog复制// 综合后分析
initial begin
$display("LUT usage: %d", `LUT_COUNT);
$display("FF usage: %d", `FF_COUNT);
$display("Max freq: %f MHz", 1000.0/`WORST_SLACK);
end
- 功耗估算:
verilog复制// 开关活动数据记录
initial begin
$saif_start("activity.saif");
#1000000 $saif_stop;
end
13. 代码生成与重用
13.1 脚本辅助生成
perl复制#!/usr/bin/perl
# 生成参数化模块实例
print "module wrapper #(\n";
print " parameter WIDTH = 8\n";
print ") (\n";
print " input clk,\n";
print " input rst_n,\n";
print " input [WIDTH-1:0] din,\n";
print " output [WIDTH-1:0] dout\n";
print ");\n\n";
print " core_module #(\n";
print " .DATA_WIDTH(WIDTH)\n";
print " ) u_core (\n";
print " .i_clk(clk),\n";
print " .i_rst_n(rst_n),\n";
print " .i_data(din),\n";
print " .o_data(dout)\n";
print " );\n\n";
print "endmodule\n";
13.2 模板库管理
- 模块模板分类:
- 接口模块(UART, SPI, I2C)
- 处理模块
