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. 02Verilatorのテストベンチ作成とRTLのlint
  3. 03Yosys合成に向けた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

書式

  • 1 行に 1 つのステートメントを記述します。複数のステートメントや代入を 1 行に連結しないでください
  • 1 行に 1 つの宣言を記述します
  • すべてのリテラルでビット幅を明示します
  • ファイルの先頭に `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 casedefault を含む通常の 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 の機能を使用する場合は、verilator --lint-only だけでなく、実際の合成フロー(例:yosys -p "synth_ecp5 ..."、yosys -p "synth_ice40 ..."、またはプロジェクトのビルドターゲット)で必ず検証してください。リントに問題がないことは、合成可能であることを意味しません。

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
  • すべての警告を修正してください。プラグマで抑制しないでください
  • 主な警告: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コンパイルを最大限並列化します
--assertSystemVerilog のアサーションを有効にします
--timingタイミング構文を有効にします
--trace-fst波形を FST(圧縮形式)として出力します
--trace-structs人が読みやすい構造体のダンプを出力します
--main-top-name "-"余分な TOP モジュールラッパーを削除します
--x-assign uniqueX をビルドごとにランダムな定数に置き換えます
--x-initial unique未初期化変数をランダムに初期化します

モジュールのインスタンス化

  • 1 ファイルにつき 1 つのモジュールを記述し、ファイル名をモジュール名と一致させます
  • 常に名前付きポート接続を使用します(位置指定は禁止)
// 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)

1 ビット信号には 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);

コメント