with one click
systemverilog
SystemVerilog 语言使用技能 - 设计和验证最佳实践
Install with Codex or Claude Copy this prompt, paste it into Codex, Claude, or another assistant, and let it review the skill page and install it for you.
Menu
SystemVerilog 语言使用技能 - 设计和验证最佳实践
Install with Codex or Claude Copy this prompt, paste it into Codex, Claude, or another assistant, and let it review the skill page and install it for you.
Based on SOC occupation classification
物理设计全流程技能 - 从floorplan到GDSII,完整数字芯片物理设计指导
芯片架构设计技能 - 从需求定义到模块划分,完整指导芯片架构开发
EDA工具脚本开发技能 - TCL/Python/Makefile 自动化流程最佳实践
功能安全分析技能 - FMEDA 分析、安全机制设计、ISO 26262 符合
静态时序分析技能 - 时序诊断、收敛优化、签收标准
UVM 验证方法论技能 - 从验证计划到功能覆盖率,完整 UVM 验证平台开发
| name | systemverilog |
| description | SystemVerilog 语言使用技能 - 设计和验证最佳实践 |
| description.zh | SystemVerilog 语言使用技能 - 设计和验证最佳实践 |
| origin | ICER |
| categories | front-end |
当用户需要使用 SystemVerilog 进行设计或验证时,启用此技能。此技能提供 SystemVerilog 最佳实践,区分设计可综合子集和验证特性。
以下特性用于 RTL 设计,可被综合工具综合为硬件。
做什么: 定义接口,封装相关信号。
// 定义 AXI 接口
interface axi_if #(
parameter DATA_WIDTH = 32,
parameter ADDR_WIDTH = 32
)(
input wire clk,
input wire rst_n
);
// 写地址通道
logic [ADDR_WIDTH-1:0] awaddr;
logic [2:0] awprot;
logic awvalid;
logic awready;
// 写数据通道
logic [DATA_WIDTH-1:0] wdata;
logic [DATA_WIDTH/8-1:0] wstrb;
logic wvalid;
logic wready;
// 写响应通道
logic [1:0] bresp;
logic bvalid;
logic bready;
// 读地址通道
logic [ADDR_WIDTH-1:0] araddr;
logic [2:0] arprot;
logic arvalid;
logic arready;
// 读数据通道
logic [DATA_WIDTH-1:0] rdata;
logic [1:0] rresp;
logic rvalid;
logic rready;
// 定义 modport 明确方向
modport master (
output awaddr, awprot, awvalid,
input awready,
output wdata, wstrb, wvalid,
input wready,
input bresp, bvalid,
output bready,
output araddr, arprot, arvalid,
input arready,
input rdata, rresp, rvalid,
output rready
);
modport slave (
input awaddr, awprot, awvalid,
output awready,
input wdata, wstrb, wvalid,
output wready,
output bresp, bvalid,
input bready,
input araddr, arprot, arvalid,
output arready,
output rdata, rresp, rvalid,
input rready
);
endinterface
优点:
做什么: 在模块中使用接口。
// 定义使用接口的模块
module axi_master #(
parameter DATA_WIDTH = 32,
parameter ADDR_WIDTH = 32
)(
input wire clk,
input wire rst_n,
axi_if.master axi // 使用接口
);
// 直接使用接口中的信号
always_ff @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
axi.awvalid <= 1'b0;
axi.awaddr <= '0;
end else begin
// 写地址逻辑
axi.awvalid <= w_aw_valid;
axi.awaddr <= w_aw_addr;
end
end
endmodule
// 顶层连接
module top;
logic clk;
logic rst_n;
// 实例化接口
axi_if #(.DATA_WIDTH(32), .ADDR_WIDTH(32)) axi_bus (.clk(clk), .rst_n(rst_n));
// 实例化主设备
axi_master u_master (
.clk(clk),
.rst_n(rst_n),
.axi(axi_bus) // 连接接口
);
// 实例化从设备
axi_slave u_slave (
.clk(clk),
.rst_n(rst_n),
.axi(axi_bus) // 连接接口
);
endmodule
做什么: 使用 always_ff 描述时序逻辑,让工具自动检查。
// 时序逻辑:使用 always_ff
always_ff @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
r_state <= ST_IDLE;
r_data <= '0;
end else begin
r_state <= w_next_state;
r_data <= w_next_data;
end
end
优点:
做什么: 使用 always_comb 描述组合逻辑,让工具自动检查锁存。
// 组合逻辑:使用 always_comb
always_comb begin
case (sel)
2'b00: w_mux = a;
2'b01: w_mux = b;
2'b10: w_mux = c;
default: w_mux = '0;
endcase
end
优点:
做什么: 如果确实需要锁存器,使用 always_latch 明确告诉工具。
// 锁存器:使用 always_latch(确实需要时才用)
always_latch begin
if (enable) begin
latched_data = data_in;
end
end
做什么: 定义自定义类型,提高代码可读性。
// 定义地址类型
typedef logic [31:0] addr_t;
// 定义数据类型
typedef logic [63:0] data_t;
// 定义状态枚举
typedef enum logic [2:0] {
ST_IDLE = 3'b000,
ST_DATA = 3'b001,
ST_WAIT = 3'b010,
ST_DONE = 3'b011,
ST_ERROR = 3'b100
} state_t;
// 定义结构体
typedef struct packed {
addr_t address;
data_t data;
logic valid;
logic ready;
} transaction_t;
// 使用自定义类型
module my_module (
input wire clk,
input wire rst_n,
input addr_t i_addr, // 使用 addr_t
input data_t i_data, // 使用 data_t
output transaction_t o_trans // 使用 transaction_t
);
state_t r_state; // 使用 state_t
// ...
endmodule
优点:
做什么: 把相关数据放在一起。
// 定义配置结构体
typedef struct packed {
logic [3:0] mode;
logic [7:0] threshold;
logic enable;
logic interrupt_en;
} config_t;
// 定义状态结构体
typedef struct packed {
logic busy;
logic error;
logic [7:0] counter;
logic done;
} status_t;
// 使用
config_t r_config;
status_t r_status;
// 赋值
always_ff @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
r_config <= '0;
r_status <= '0;
end else begin
r_config.mode <= i_mode;
r_config.threshold <= i_threshold;
r_config.enable <= i_enable;
end
end
做什么: 使用参数让模块可配置。
module fifo #(
parameter DATA_WIDTH = 32,
parameter DEPTH = 16,
// 派生参数
parameter ADDR_WIDTH = $clog2(DEPTH)
)(
input wire i_clk,
input wire i_rst_n,
// 写接口
input wire [DATA_WIDTH-1:0] i_wr_data,
input wire i_wr_en,
output logic o_full,
// 读接口
output logic [DATA_WIDTH-1:0] o_rd_data,
input wire i_rd_en,
output logic o_empty
);
// 使用参数
logic [DATA_WIDTH-1:0] r_mem[DEPTH];
logic [ADDR_WIDTH-1:0] r_wr_addr;
logic [ADDR_WIDTH-1:0] r_rd_addr;
// ...
endmodule
// 实例化时配置参数
fifo #(
.DATA_WIDTH (64),
.DEPTH (32)
) u_fifo (
.i_clk (clk),
.i_rst_n (rst_n),
// ...
);
做什么: 使用 generate 生成重复结构。
module multi_channel #(
parameter NUM_CHANNELS = 4,
parameter DATA_WIDTH = 32
)(
input wire i_clk,
input wire i_rst_n,
input wire [NUM_CHANNELS-1:0] i_valid,
input wire [NUM_CHANNELS*DATA_WIDTH-1:0] i_data,
output logic [NUM_CHANNELS-1:0] o_ready
);
genvar i;
generate
for (i = 0; i < NUM_CHANNELS; i = i + 1) begin: gen_channels
// 每个通道实例化一个处理模块
channel_processor #(
.DATA_WIDTH (DATA_WIDTH)
) u_processor (
.i_clk (i_clk),
.i_rst_n (i_rst_n),
.i_valid (i_valid[i]),
.i_data (i_data[i*DATA_WIDTH +: DATA_WIDTH]),
.o_ready (o_ready[i])
);
end
endgenerate
endmodule
要点:
genvar 用于 generate 循环变量begin: name 给生成的块命名,方便访问设计使用的 SystemVerilog 特性必须是可综合的。
| 特性 | 可综合? | 说明 |
|---|---|---|
logic | ✅ | 替代 reg 和 wire |
always_ff | ✅ | 时序逻辑 |
always_comb | ✅ | 组合逻辑 |
always_latch | ✅ | 锁存器 |
interface | ✅ | 接口封装 |
modport | ✅ | 接口方向定义 |
typedef | ✅ | 类型定义 |
struct | ✅ | 结构体 |
enum | ✅ | 枚举 |
parameter | ✅ | 参数 |
localparam | ✅ | 局部参数 |
generate | ✅ | 生成 |
$clog2() | ✅ | 编译时计算对数 |
以下特性只能用于验证,不能用于设计:
| 特性 | 可综合? | 说明 |
|---|---|---|
class | ❌ | 类,只能用于验证 |
dynamic array | ❌ | 动态数组 |
associative array | ❌ | 关联数组 |
queue | ❌ | 队列 |
string | ❌ | 字符串 |
virtual | ❌ | 虚方法 |
program | ❌ | 程序块 |
clocking block | ❌ | 时钟块(设计不用) |
initial | ❌ | 初始块(综合忽略) |
#delay | ❌ | 延迟(综合忽略) |
fork/join | ❌ | 并发块 |
initial 赋初值#delay以下特性用于验证平台,不可综合。
做什么: 使用类封装验证组件。
// 定义事务类
class transaction;
rand logic [31:0] addr;
rand logic [31:0] data;
rand bit read; // 0=write, 1=read
// 约束
constraint addr_align {
addr[1:0] == 2'b00; // 地址 4 字节对齐
}
constraint addr_range {
addr inside {[0:32'hFFFF_FFFF]};
}
// 打印函数
function void print();
$display("Transaction: addr=0x%h, data=0x%h, read=%b", addr, data, read);
endfunction
endclass
做什么: 复用基类代码。
// 基类
class base_transaction;
rand logic [31:0] addr;
rand logic [31:0] data;
function void print();
$display("addr=0x%h, data=0x%h", addr, data);
endfunction
endclass
// 派生类
class burst_transaction extends base_transaction;
rand int burst_length;
constraint burst_range {
burst_length inside {[1:16];
}
function void print();
$display("addr=0x%h, data=0x%h, burst_length=%0d", addr, data, burst_length);
endfunction
endclass
做什么: 运行时选择不同实现。
// 基类
virtual class sequence;
pure virtual task body();
endclass
// 派生类 1
class read_sequence extends sequence;
virtual task body();
// 读序列
endtask
endclass
// 派生类 2
class write_sequence extends sequence;
virtual task body();
// 写序列
endtask
endclass
// 使用多态
sequence seq;
seq = read_sequence::new();
seq.body(); // 执行读序列
seq = write_sequence::new();
seq.body(); // 执行写序列
做什么: 定义随机约束,生成合法激励。
class axi_transaction;
rand logic [31:0] addr;
rand logic [31:0] data;
rand logic [3:0] strb;
rand bit read; // 0=write, 1=read
rand int delay;
// 地址对齐约束
constraint addr_align {
addr % 4 == 0; // 4 字节对齐
}
// 地址范围约束
constraint addr_range {
addr inside {[32'h0000_0000:32'h0000_FFFF]}; // 64KB 范围
}
// 写选通约束
constraint strb_valid {
if (read) {
strb == 4'hF; // 读操作,选通全 1
} else {
strb inside {4'h1, 4'h3, 4'hF}; // 写操作,合法选通
}
}
// 延迟约束
constraint delay_range {
delay inside {[0:10]};
}
// 读写比例约束
constraint read_write_ratio {
read dist {0 := 60, 1 := 40}; // 60% 写,40% 读
}
endclass
program test;
axi_transaction trans;
initial begin
trans = new();
repeat(100) begin
if (!trans.randomize()) begin
$error("Randomization failed");
end
trans.print();
// 发送事务
drive_transaction(trans);
end
end
endprogram
做什么: 定义功能覆盖点。
class axi_transaction;
rand logic [31:0] addr;
rand logic [31:0] data;
rand bit read;
// 覆盖组
covergroup addr_cg @(posedge clk);
option.per_instance = 1;
// 地址覆盖点
addr_cp: coverpoint addr {
bins low = {[0:32'h0000_3FFF]};
bins mid = {[32'h0000_4000:32'h0000_BFFF]};
bins high = {[32'h0000_C000:32'h0000_FFFF]};
bins others = default;
}
// 读写覆盖点
read_cp: coverpoint read {
bins write = {0};
bins read = {1};
}
// 交叉覆盖
addr_read_cross: cross addr_cp, read_cp;
endgroup
// 构造函数
function new();
addr_cg = new();
endfunction
endclass
program test;
axi_transaction trans;
initial begin
trans = new();
repeat(1000) begin
trans.randomize();
drive_transaction(trans);
trans.addr_cg.sample(); // 采样覆盖组
end
// 报告覆盖率
$display("Address coverage: %0.2f%%", trans.addr_cg.get_coverage());
end
endprogram
规则:
// ✅ 好:缩进 4 空格,行宽合理
always_ff @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
r_state <= ST_IDLE;
r_data <= '0;
end else begin
r_state <= w_next_state;
r_data <= w_next_data;
end
end
// ✅ 好:长行换行对齐
assign w_result = (sel == 2'b00) ? a :
(sel == 2'b01) ? b :
(sel == 2'b10) ? c : '0;
规则: 每个端口单独一行,加注释。
module my_module #(
parameter DATA_WIDTH = 32
)(
// 时钟和复位
input wire i_clk, // 时钟
input wire i_rst_n, // 异步复位,低有效
// 配置接口
input wire [DATA_WIDTH-1:0] i_config, // 配置数据
// 数据输入
input wire [DATA_WIDTH-1:0] i_data, // 输入数据
input wire i_valid, // 输入有效
// 数据输出
output logic [DATA_WIDTH-1:0] o_data, // 输出数据
output logic o_valid // 输出有效
);
文件头注释:
//-----------------------------------------------------------------------------
// Module: module_name
// Description: 模块功能描述(一句话)
// Author: 作者名
// Date: 创建日期
// Version: 版本号
// Modification History:
// Date | Author | Description
// -----------|---------|-------------------------------------------
// 2024-01-15 | XXX | Initial version
//-----------------------------------------------------------------------------
端口注释: 每个端口后面加注释
代码注释: 注释解释为什么,不解释做什么
// ✅ 好:注释解释为什么
// 格雷码计数器,降低功耗,因为只有一位翻转
always_ff @(posedge clk) begin
r_gray <= r_binary ^ (r_binary >> 1);
end
// ❌ 坏:注释解释做什么(代码已经说明了)
// 这是一个计数器
always_ff @(posedge clk) begin
r_counter <= r_counter + 1'b1;
end
| 工具 | 公司 | 用途 |
|---|---|---|
| VCS | Synopsys | 仿真 |
| Xcelium | Cadence | 仿真 |
| Questa | Siemens | 仿真 |
| Design Compiler | Synopsys | 综合 |
| Genus | Cadence | 综合 |
| 工具 | 用途 |
|---|---|
| Verilator | Lint 检查、仿真 |
| Icarus Verilog | 仿真 |
| Yosys | 综合 |
always_ff 描述时序逻辑always_comb 描述组合逻辑case 语句有 default 分支❌ 在设计中使用验证特性:动态数组、类、队列等不可综合
// ❌ 设计中错误使用
int dynamic_array[]; // 不可综合
class my_class; // 不可综合
❌ 没有给 generate 块命名:生成的模块层次不好访问
// ❌ 不好
generate
for (i = 0; i < 4; i++) begin // 没有名字
// ...
end
endgenerate
// ✅ 好
generate
for (i = 0; i < 4; i++) begin: gen_channels // 有名字
// ...
end
endgenerate
❌ 混合 Verilog 和 SystemVerilog 不必要:统一都用 SystemVerilog 更好
❌ 文件和模块名不一致:找代码困难
rtl-designer 代理进行 RTL 设计verification-engineer 代理进行验证平台开发code-reviewer 代理进行代码审查