con un clic
systemverilog
SystemVerilog 语言使用技能 - 设计和验证最佳实践
Instalar con Codex o Claude Copia este prompt, pégalo en Codex, Claude u otro asistente, y deja que revise la página de la skill y la instale por ti.
Menú
SystemVerilog 语言使用技能 - 设计和验证最佳实践
Instalar con Codex o Claude Copia este prompt, pégalo en Codex, Claude u otro asistente, y deja que revise la página de la skill y la instale por ti.
Basado en la clasificación ocupacional SOC
物理设计全流程技能 - 从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 代理进行代码审查