1. はじめに
FPGA設計において、レジスタマップの定義や、構造が似たモジュールの記述といった「単純だけどミスの許されない作業」は、意外と設計者のリソースを奪います。
アドレスやビット幅を1つ間違えるだけで、作ったはずのレジスタが叩けなかったり、接続ミスでデバッグに数時間を溶かしたり……。その確認のためにテストベンチを書き、シミュレーションを回す時間だって馬鹿になりません。
2. なぜ自動化が必要だったのか
FPGA設計者はExcelに書かれた仕様書を読み解きながら、それを正確にVerilogコードに落とし込んでいくわけですが、そもそもこんな単純作業はエンジニアの本質的な仕事ではありません。
「Excelに仕様があるなら、そこから自動でVerilogコードを吐き出せばいい」
**「エンジニアは本質的な設計(ロジック)に集中すべき」**なのです。
3. ツールの構成と仕組み
この悩みを解決するために、Pythonを用いた自動生成ツールを構築しました。
Python 3.10
pandas: Excelの仕様書を解析するために使用
Jinja2: テキストベースのテンプレートエンジン。HDLのコード生成に最適
処理フロー
Excel定義: レジスタマップ(アドレス、ビット範囲、属性、初期値など)を作成
テンプレート作成: Jinja2でVerilogの「型」となるテンプレートを作成
自動生成: PythonでExcelをパースし、Jinja2にデータを流し込んでコードを出力
4. 実装のポイント
① 仕様となるExcelファイル
以下のような形式でレジスタを定義します。

② Jinja2テンプレート(抜粋)
単なる文字列結合ではなく、テンプレートエンジンを使うことで「rw/ro属性に応じた分岐」などをスマートに記述できます。
Verilog
always @(posedge i_cpu_clk or posedge i_areset) begin
if (i_areset) begin
{# rwまたはwo属性のレジスタのみ初期化 #}
{% for reg in reg_list if reg.Type in ['rw', 'wo'] %}
r_{{ reg.Name }} <= {{ reg.Reset|hex_verilog(reg.Width) }};
{% endfor %}
o_cpu_rddata <= 32'd0;
o_cpu_rdvalid <= 1'b0;
end else begin
// 書き込み制御
if (i_cpu_cs && i_cpu_wren) begin
case (i_cpu_addr)
{% for addr, group in reg_list|groupby('Offset') %}
{% set write_fields = group|selectattr('Type', 'in', ['rw', 'wo'])|list %}
{% if write_fields %}
{{ addr_bits }}'h{{ "%X"|format(addr) }}: begin
{% for field in write_fields %}
r_{{ field.Name }} <= i_cpu_wrdata[{{ field.MSB }}:{{ field.LSB }}];
{% endfor %}
end
{% endif %}
{% endfor %}
default: ;
endcase
end
// 読み出し制御
if (i_cpu_cs && i_cpu_rden) begin
o_cpu_rdvalid <= 1'b1;
case (i_cpu_addr)
{% for addr, group in reg_list|groupby('Offset') %}
{% set read_ops = [] %}
{% for r in group if r.Type != 'pulse' %}
{% set src = "i_" + r.Name if r.Type == 'ro' else "r_" + r.Name %}
{% do read_ops.append("(" + src + " << " + r.LSB|string + ")") %}
{% endfor %}
{% if read_ops %}
{{ addr_bits }}'h{{ "%X"|format(addr) }}: begin
o_cpu_rddata <= {{ read_ops|join(' | ') }};
end
{% endif %}
{% endfor %}
default: o_cpu_rddata <= 32'd0;
endcase
end else begin
o_cpu_rdvalid <= 1'b0;
end
end
end
※ hex_verilog はVerilogの基数表現に変換するカスタムフィルタです。
③ Pythonスクリプトでのパース(抜粋)
pandasを使ってExcelの各シート(モジュール単位)を読み込み、Jinja2に渡す辞書形式に整理します。
Python
import pandas as pd
from pathlib import Path
# ... (中略) ...
with pd.ExcelFile(xlsx_path) as xl:
df_mod = pd.read_excel(xl, sheet_name=mod_name)
reg_list_for_jinja = []
# アドレス順にソートしてデータを整理
df_mod['AddrInt'] = df_mod['AddrOffset'].apply(to_int_auto)
df_sorted = df_mod.sort_values(['AddrInt'])
for _, r in df_sorted.iterrows():
msb, lsb, width = parse_bit_range(r['BitRange'])
reg_data = {
"Name": str(r['Name']),
"Offset": int(r['AddrInt']),
"MSB": msb, "LSB": lsb, "Width": width,
"Type": r['Type'],
"Reset": to_int_auto(r.get('Reset', 0)),
# ... その他必要なパラメータ ...
}
reg_list_for_jinja.append(reg_data)
5. 自動化によって得られた効果
このツールの導入により、これまで数時間かかっていた「レジスタの実装からタイポによるバグ取り」までの作業が、わずか数秒で完了するようになりました。
何より大きいのは、**「仕様書(Excel)と実装(コード)が常に100%一致する」**という安心感です。仕様変更があってもExcelを直してスクリプトを叩くだけ。人為的ミスを恐れる必要はもうありません。
6. まとめ
ExcelのレジスタマップをVerilogに落とし込むという「非創造的な単純作業」を自動化したことで、より本質的なロジック設計やアーキテクチャの検討に時間を割けるようになりました。
7. 今後の展望
レジスタコードが自動生成できるなら、その情報を活用しない手はありません。 次回は、このExcelデータから**「評価用GUI(Python/Tkinter)を自動生成する」**取り組みについて書きたいと思います。