mcpField
C
claude-skill-verilog

claude-skill-verilog

github.com/londey开发19194 次安装27 周前更新
代码仓库

关于 claude-skill-verilog

面向 RTL 与测试平台开发者的 Verilog 和 SystemVerilog 编码指南,涵盖代码规范、Verilator 验证及 Yosys 综合兼容性。

主要功能

  1. 01RTL 文档与命名规范
  2. 02定点数 Q 格式说明
  3. 03always_ff 与 always_comb 模式
  4. 04Verilator lint 和测试平台指南
  5. 05Yosys 兼容综合实践
  6. 06CDC、复位、FSM 与存储器模式

适用场景

  1. 01规划新的 SystemVerilog 模块
  2. 02编写 Verilator 测试平台并检查 RTL
  3. 03为 Yosys 综合准备 RTL
  4. 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);

评论

支持 Markdown