关于 claude-skill-verilog
面向 RTL 与测试平台开发者的 Verilog 和 SystemVerilog 编码指南,涵盖代码规范、Verilator 验证及 Yosys 综合兼容性。
主要功能
- 01RTL 文档与命名规范
- 02定点数 Q 格式说明
- 03always_ff 与 always_comb 模式
- 04Verilator lint 和测试平台指南
- 05Yosys 兼容综合实践
- 06CDC、复位、FSM 与存储器模式
适用场景
- 01规划新的 SystemVerilog 模块
- 02编写 Verilator 测试平台并检查 RTL
- 03为 Yosys 综合准备 RTL
- 04审查时钟域交叉与复位逻辑
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ londey/ claude-skill-verilog --skill claude-skill-verilog安装后在 Claude Code 中输入/claude-skill-verilog确认已加载。
附带文件README.mdLICENSE
详细介绍
Verilog/SystemVerilog 指南
适用于处理 .v、.sv、.vh、.svh 文件或运行 Verilator 时。
文档
所有模块、连线和寄存器都必须有注释:
// Module: counter
// Purpose: Simple up-counter with synchronous reset
module counter #(
parameter WIDTH = 8 // Counter bit width
) (
input logic clk, // System clock
input logic rst_n, // Active-low reset
output logic [WIDTH-1:0] count // Current count value
);
定点表示法
使用 TI 风格的 Q 表示法记录所有定点值:
Qm.n— 有符号:m 个整数位(包括符号位),n 个小数位,总宽度 = m + n 位。UQm.n— 无符号:m 个整数位,n 个小数位,总宽度 = m + n 位。
在信号注释、localparam 描述和模块级文档中使用 Q 表示法。
logic signed [15:0] scale_factor; // Scaling multiplier, Q4.12
logic [15:0] timer_count; // Free-running tick counter, UQ16.0
logic signed [15:0] delta; // Sample-to-sample difference, Q4.12
命名约定
- 低电平有效信号:使用
_n后缀(例如rst_n、chip_select_n) - 时钟:使用
clk或clk_<domain> - 使用描述性名称,避免缩写
always_ff:仅限简单赋值
always_ff 块只能包含简单的非阻塞赋值。不得包含逻辑或表达式——这样可确保 Verilator 仿真与综合行为一致。(例外:存储器推断和异步复位同步器需要条件逻辑——请参阅相应章节。)
// CORRECT - simple assignment
always_ff @(posedge clk) begin
count <= count_next;
state <= state_next;
end
// WRONG - logic in always_ff
always_ff @(posedge clk) begin
count <= count + 1; // Move to always_comb
state <= enable ? RUNNING : IDLE; // Move to always_comb
end
always_comb:所有逻辑都在此处
所有组合逻辑都必须放在 always_comb 块中:
always_comb begin
count_next = count + 8'd1;
state_next = enable ? RUNNING : IDLE;
end
格式规范
- 每行一条语句——绝不在同一行串写多条语句或多个赋值
- 每行声明一个变量
- 所有字面量都要显式指定位宽
- 文件以
`default_nettype none开头 if、else和case项始终使用begin/end块(避免日后添加代码时引入错误)- 尽量将模块控制在约 500 行以内;如果模块明显变长,可考虑重构为更小的子模块
`default_nettype none
module example (
input logic clk,
input logic rst_n,
input logic [7:0] data_in,
output logic [7:0] data_out
);
logic [7:0] data_reg; // Registered data
logic [7:0] data_next; // Next state value
logic valid; // Data valid flag
localparam logic [7:0] INIT_VAL = 8'd0;
endmodule
`default_nettype wire
Yosys 综合兼容性
可综合的 RTL 应同时适用于 Verilator(检查/仿真)和 Yosys(用于开源 FPGA 流程的综合)。
Yosys 通过 read_verilog -sv 支持部分 SystemVerilog。
通过 Verilator 的代码仍可能无法通过 Yosys 综合。
可综合 RTL 中应避免的结构:
| 避免使用 | 改用 |
|---|---|
函数中的 return <expr>; | function_name = <expr>;(Verilog-2005 风格) |
interface / modport | 显式端口列表 |
unique case / priority case | 带 default 的普通 case |
| 端口中的多维打包数组 | 展平为单个向量 |
// CORRECT - Yosys-compatible function
function automatic logic [7:0] add_saturate(input logic [7:0] a, input logic [7:0] b);
logic [8:0] sum;
sum = {1'b0, a} + {1'b0, b};
add_saturate = sum[8] ? 8'hFF : sum[7:0];
endfunction
// WRONG - return statement (Yosys rejects this)
function automatic logic [7:0] add_saturate(input logic [7:0] a, input logic [7:0] b);
logic [8:0] sum;
sum = {1'b0, a} + {1'b0, b};
return sum[8] ? 8'hFF : sum[7:0];
endfunction
使用 SystemVerilog 特性时,务必通过实际综合流程(例如 yosys -p "synth_ecp5 ..."、yosys -p "synth_ice40 ..." 或项目的构建目标)进行验证,而不只是运行 verilator --lint-only。Lint 无警告不代表可综合。
使用 Verilator 进行测试
每个模块都必须有测试平台。使用 Verilator 构建并运行:
# Build testbench
verilator --binary -Wall module_tb.sv module.sv
# Run simulation
./obj_dir/Vmodule_tb
测试平台结构:
module counter_tb;
logic clk = 1'b0; // System clock
logic rst_n; // Active-low reset
logic [7:0] count; // DUT output
counter dut (
.clk(clk),
.rst_n(rst_n),
.count(count)
);
always begin
#5 clk = ~clk;
end
initial begin
rst_n = 1'b0;
#20 rst_n = 1'b1;
#100;
$display("Test complete, count=%d", count);
$finish;
end
endmodule
Verilator 检查
检查所有文件并修复所有警告:
verilator --lint-only -Wall module.sv
- 修复所有警告——不要使用 pragma 屏蔽
- 重点关注的警告:WIDTH(位宽不匹配)、UNUSED、UNDRIVEN
Verilator 仿真标志
仿真构建推荐使用以下标志:
verilator --binary \
-Wall \
-Wno-fatal \
-j 0 \
--assert \
--timing \
--trace-fst \
--trace-structs \
--main-top-name "-" \
--x-assign unique \
--x-initial unique \
module_tb.sv module.sv
| 标志 | 用途 |
|---|---|
-Wall | 启用所有警告 |
-Wno-fatal | 有警告时不退出(允许完整显示报告) |
-j 0 | 完全并行编译 |
--assert | 启用 SystemVerilog 断言 |
--timing | 启用时序结构 |
--trace-fst | 将波形转储为 FST(压缩格式) |
--trace-structs | 输出易读的结构体转储 |
--main-top-name "-" | 移除额外的 TOP 模块封装 |
--x-assign unique | 将 X 替换为每次构建随机生成的常量 |
--x-initial unique | 随机初始化未初始化的变量 |
模块实例化
- 每个文件包含一个模块,文件名与模块名一致
- 始终使用命名端口连接(绝不使用位置连接)
// CORRECT - named connections
counter #(
.WIDTH(16)
) u_counter (
.clk (clk),
.rst_n (rst_n),
.count (count_value)
);
// WRONG - positional connections
counter u_counter (clk, rst_n, count_value);
避免锁存器
如果信号并非在所有路径中都赋值,就会推断出锁存器。可通过以下方式避免:
- 在
always_comb开始处进行默认赋值 - 覆盖所有情况,包括
default
always_comb begin
// Default assignments first
data_next = data_reg;
valid_next = 1'b0;
case (state)
IDLE: begin
data_next = 8'd0;
end
LOAD: begin
data_next = data_in;
end
default: begin
data_next = data_reg;
end
endcase
end
复位处理
尽可能使用同步复位。对于外部异步复位,先进行同步处理。
**注意:**异步复位同步器需要在 always_ff 中针对复位条件使用条件逻辑——这是必要的例外,类似于存储器推断。
// Synchronous reset (preferred)
logic [7:0] count; // Counter register
logic [7:0] count_next; // Next counter value
always_comb begin
count_next = rst_n ? (count + 8'd1) : 8'd0;
end
always_ff @(posedge clk) begin
count <= count_next;
end
// Reset synchronizer for external async reset
logic [1:0] rst_sync; // Synchronizer flip-flops
logic [1:0] rst_sync_next; // Next synchronizer value
always_comb begin
rst_sync_next = {rst_sync[0], 1'b1};
end
always_ff @(posedge clk or negedge rst_async_n) begin
if (!rst_async_n) begin
rst_sync <= 2'b00;
end else begin
rst_sync <= rst_sync_next;
end
end
assign rst_n = rst_sync[1];
FSM 模式
将状态寄存器与下一状态逻辑分开。使用枚举进行状态编码。
typedef enum logic [1:0] {
IDLE,
RUN,
DONE
} state_t;
state_t state; // Current state register
state_t state_next; // Next state value
// Next-state logic (combinational)
always_comb begin
state_next = state;
case (state)
IDLE: begin
if (start) begin
state_next = RUN;
end
end
RUN: begin
if (finish) begin
state_next = DONE;
end
end
DONE: begin
state_next = IDLE;
end
default: begin
state_next = IDLE;
end
endcase
end
// State register (sequential)
always_ff @(posedge clk) begin
state <= state_next;
end
时钟域跨越(CDC)
单比特信号:使用 2-FF 同步器。多比特信号:使用格雷码或握手。
// 2-FF synchronizer for single-bit CDC
logic [1:0] sync_reg; // Synchronizer flip-flops
logic [1:0] sync_reg_next; // Next synchronizer value
logic signal_sync; // Synchronized output
always_comb begin
sync_reg_next = {sync_reg[0], signal_src};
end
always_ff @(posedge clk_dst) begin
sync_reg <= sync_reg_next;
end
assign signal_sync = sync_reg[1];
// Gray code for multi-bit counters crossing domains
function automatic logic [WIDTH-1:0] bin2gray(input logic [WIDTH-1:0] bin);
bin2gray = bin ^ (bin >> 1);
endfunction
存储器推断
使用标准模式,以便综合工具推断 RAM/ROM。
**注意:**存储器模式是 always_ff“仅限简单赋值”规则的例外。综合工具需要这些特定模式,才能正确推断 RAM/ROM 原语。
// Single-port RAM
logic [DATA_WIDTH-1:0] mem [0:DEPTH-1]; // Memory array
always_ff @(posedge clk) begin
if (we) begin
mem[addr] <= wdata;
end
rdata <= mem[addr];
end
// ROM (initialized memory)
logic [7:0] rom [0:255]; // ROM array
initial $readmemh("rom_data.hex", rom);
always_ff @(posedge clk) begin
rdata <= rom[addr];
end
断言(SVA)
使用断言进行验证。在 Verilator 中通过 --assert 启用断言。
// Immediate assertion
always_comb begin
assert (count < MAX_COUNT) else $error("Count overflow");
end
// Concurrent assertions
property p_valid_handshake;
@(posedge clk) disable iff (!rst_n)
valid |-> ##[1:3] ready;
endproperty
assert property (p_valid_handshake)
else $error("Handshake timeout");
// Cover property (for functional coverage)
cover property (@(posedge clk) state == DONE);