claude-skill-verilog について
RTLやテストベンチを作成するエンジニア向けの、VerilogとSystemVerilogのガイドです。コーディング規約に加え、Verilatorでの検証とYosys合成への対応を扱います。
主な機能
- 01RTLの文書化と命名規則
- 02固定小数点のQ表記ガイド
- 03always_ffとalways_combのパターン
- 04Verilatorのlintとテストベンチ指針
- 05Yosys互換の合成プラクティス
- 06CDC、リセット、FSM、メモリのパターン
活用シーン
- 01新しいSystemVerilogモジュールの設計
- 02Verilatorのテストベンチ作成とRTLのlint
- 03Yosys合成に向けた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
書式
- 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 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 の機能を使用する場合は、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 | コンパイルを最大限並列化します |
--assert | SystemVerilog のアサーションを有効にします |
--timing | タイミング構文を有効にします |
--trace-fst | 波形を FST(圧縮形式)として出力します |
--trace-structs | 人が読みやすい構造体のダンプを出力します |
--main-top-name "-" | 余分な TOP モジュールラッパーを削除します |
--x-assign unique | X をビルドごとにランダムな定数に置き換えます |
--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);