制御レジスタ (CSR) を自動生成する
はじめに
制御レジスタの実装、手書きですか?実装自体は難しくはないですが、ビット位置など、微妙に異なる単調な記述が続くので、かなり面倒です。こういう手合のものは、自動生成するに限ります。という訳で、自動生成ツールを作りました。
RgGen
RgGen (https://github.com/rggen/rggen) は、YAML とかExcel とか仕様に近い記述から、
- SystemVerilog/Verilog/VHDL RTL
- シミュレーション用モデル (UVM RAL)
- C ヘッダーファイル
- Markdown
を自動生成するツールです。
こんな入力
- https://github.com/rggen/rggen-sample/blob/master/block_0.yml
- https://github.com/rggen/rggen-sample/blob/master/block_0.xlsx
- https://github.com/rggen/rggen-sample/blob/master/block_0.rb
から、こんな出力を生成します。
- https://github.com/rggen/rggen-sample/blob/master/block_0.sv
- https://github.com/rggen/rggen-sample/blob/master/block_0_ral_pkg.sv
- https://github.com/rggen/rggen-sample/blob/master/block_0.h
また、追加のプラグインを用いれば、Verilog/VHDL/C header を生成することもできます。
- Verilog
- VHDL
独自ビットフィールドやレジスタバスのプロトコルの追加など、自身の環境に合わせて拡張することも可能です。実際、ビットフィールドや社内仕様のプロトコルを追加して、実業務で運用しています。拡張方法については、以下を参照してください。
インストール
RgGen は Ruby を使って実装されています。サポートするバージョンは 2.6 以上です。まずは、Ruby を使えるようにしましょう。Ruby のバージョン管理ソフトである rbenv
を使うのが手っ取り早いです。この辺の記事が参考になると思います。
Ruby が使えるようになったら、RgGen のインストールです。次のコマンドを実行すれば、インストール可能です。
$ gem install rggen
rggen -v
を実行してみて、バージョンが表示されれば、インストール成功です。
$ rggen -v
RgGen 0.19
入力ファイル
以下の 2 種類の入力ファイルが必要です。
コンフィグレーションファイル
データバス幅や、アドレス幅など設定を纏めたファイルです。YAML の連想配列形式を使って記述します。主に、以下の項目を指定します。
- bus_width
- データバスのビット幅を指定します。
- address_width
- アドレスのビット幅を指定します。
- protocol
- レジスタバスのプロトコルをしています。
-
apb
/axi4lite
/wishbone
が指定可能です。
- enable_wide_register
-
true
を指定した場合、8 bytes 超のレジスタの定義を許可します- この場合、C ヘッダーファイルは生成できません
-
- array_port_format
- 配列状になっている入出力ポートの形式を指定します。
-
packed
/unpakced
/serialized
が指定可能です。-
packed
- 配列の形式にパックド配列を用いいます。
- 未指定の場合、この形式が適用されます。
-
unpacked
- 配列の形式にアンパックド配列を用いいます。
-
serialized
- 配列を 1 つのビット列に纏めます。
-
- 幅が
8
、配列の大きさが2 * 3
の場合、各形式でのポート宣言は以下のようになります。-
packed
input [1:0][2:0][7:0] i_foo
-
unpacked
input [7:0] i_foo[2][3]
-
serialized
input [47:0] i_foo
-
bus_width: 32
address_width: 16
protocol: apb
レジスタマップ
アドレスや、ビットフィールドの幅やアクセス属性など、制御レジスタの仕様を纏めたファイルです。YAML/Ruby/Excel で記述可能ですが、本稿では、YAML に絞って説明します。他のフォーマットについては、サンプルを参照してください。
レジスタマップは、
- レジスタブロック
- レジスタ
- ビットフィールド
の階層から成っています。以下の用に、連想配列の配列が階層状に連なった形に成っています。
register_blocks:
- name: ...
registers:
- name: ...
bit_fields:
- { name: ... }
- { name: ... }
全景は、サンプルを参照してください。
レジスタブロック
機能ブロック単位で制御レジスタをまとめる階層です。以下の項目を指定します。また、registers
に、配下のレジスタの記述を配列で羅列します。
- name
- レジスタブロックの名前です。
- byte_size
- レジスタブロックの大きさです。バイト単位で指定します。
register_blocks:
- name: cfg_foo
byte_size: 256
registers:
- ...
- ...
- name: cfg_bar
byte_size: 1024
registers:
- ...
- ...
レジスタ
ビットフィールドを機能単位で纏める階層です。以下の項目を指定します。また、bit_fields
には以下のビットフィールドの記述を羅列します。
byte_size
を超えない範囲であれば、ビットフィールドの幅に制限はありません。なので、データバス幅を超える幅を持つレジスタを定義することが出来ます。
- name
- レジスタの名前です。
- レジスタブロック単位で単一の名前をつけてください。
- offset_address
- レジスタブロック内のオフセットアドレスです。
- 単位はバイトです。
- 未指定の場合、1 つ前のレジスタから計算されます。
- 以下の場合でアドレスを重複させることが出来ます。
- アクセス属性が、読み込みのみと書き込みのみの場合
- レジスタ型が
indirect
の場合
- type
- レジスタの型を指定します。
- 無指定
- external
- SRAM や他のレジスタブロックを接続するためのレジスタ型です。
-
size
で指定した範囲を外部に開放します。
- indirect
- 指定したビットフィールドを、追加のインデックスとして使用するレジスタ型です。
- 単一のアドレス範囲に複数のレジスタを配置したい場合に使用します。
- レジスタ幅分しか、アドレスを専有しません。
- 以下のように配列で、追加のインデックスの指定を行います。
[indirect, foo.foo_0, foo.foo_1, [bar.bar_0, 0]]
- ビットフィールド名のみが指定された場合は、配列の添字として使用されます。
- ビットフィールド名と値が指定された場合は、ビットフィールドが指定した値になった場合に、有効になります。
- reserved
- 指定されたアドレス領域を予約済みにするためのレジスタ型です。
- size
- 配列形式を使用して指定します。
- レジスタ型が、無指定/
indirect
の場合- 配列の大きさを指定します。
- レジスタ型が、
external
の場合- アドレス方向の大きさを、ワード単位で指定します。
- バス幅が 32 ビットの場合、4 バイトが 1 ワードになります。
registers:
- name: foo
offset_address: 0x00
bit_fields:
- ...
- ...
- name: bar
offset_address: 0x10
type: external
size: [4]
- name: baz
offset_address: 0x20
type: [indirect, foo.foo_0, foo.foo_1, [foo.foo_2, 1]]
size: [2, 3]
bit_fields:
- ...
- ...
ビットフィールド
制御レジスタの実体です。以下の項目を指定します。
- name
- ビットフィールド名です。
- レジスタ単位で、単一の名前をつけてください。
- bit_assignment
- 開始ビット位置 (LSB) や幅などを、以下の項目を、連想配列形式で指定します。
- width
- ビットフィールドの幅を指定します。
- 未指定の場合は 1 ビット幅になります。
- lsb
- 開始ビット位置を指定します。
- 未指定の場合は、1 つ前のビットフィールドの値から計算されます。
- sequence_size
- いくつ繰り返すかを指定します。
- 未指定の場合は、繰り返しなしです。
- step
- 現在の開始ビット位置と次の開始ビット位置の差分を指定します。
- 未指定の場合は、
width
になります。 -
sequense_size
が指定されている場合に有効です。
- width
-
{ lsb: 0, width: 4, sequence_size: 4, step: 16 }
の場合、実際のビット割当は以下のようになります。- [3:0]
- [19:16]
- [35:32]
- [51:48]
- 開始ビット位置 (LSB) や幅などを、以下の項目を、連想配列形式で指定します。
- type
- ビットフィールドの型を指定します。
- 指定可能なビットフィールド型は以下を参照してください。
- initial_value
- ビットフィールドの初期値(リセット値)を指定します。
-
initial_value: VALUE
- 指定した値がそのまま反映されます。
-
initial_value: { default: VALUE }
- 初期値はパラメータ化されます。
-
VALUE
で指定された値が、パラメータの既定値になります。
-
initial_value: [VALUE_1, VALUE_2,...]
- ビットフィールドが繰り返しになっている場合の、各ビットフィールドの初期値です。
- 繰り返しが
4
でリセット値が[0, 1, 2, 3]
の場合、各ビットフィールドの初期値は以下のようになります。-
bit_field[0]
: 0 -
bit_field[1]
: 1 -
bit_field[2]
: 2 -
bit_field[3]
: 3
-
- reference
- 参照するビットフィールドを
register_name.bit_field_name
で指定します。
- 参照するビットフィールドを
bit_fields:
- { name: foo, bit_assignment: { lsb: 0 , width: 8 }, type: rw, initial_value 0x00 }
- { name: bar, bit_assignment: { lsb: 32, width: 8 }, type: ro }
指定可能なビットフィールド型
- rc
- 読み出しのみ可能なビットフィールドです。
- 読み出し時に 0 クリアします。
-
reference
が指定された場合、指定されたビットフィールドはマスクとして使用されます。-
value & mask
が読み出し値になります。 - マスクが掛かっていない、素の
value
も外部に出力されます。
-
- 割り込みステータスへの使用を想定しています。
- reserved
- 予約済み領域です。
- ro
- 読み出しのみ可能なビットフィールドです。
-
reference
が指定された場合、指定されたビットフィールドの値が読み出し値になります。-
rc
型等でマクスが指定されている場合、マスクが掛かっていない素の値が参照されます。
-
- rof
- 読み出しのみ可能なビットフィールドです。
-
initial_value
で指定した値が読み出されます。
- rs
- 読み出しのみ可能なビットフィールドです。
- 読み出し時に 1 にセットされます。
- rotrg
- 読み出しを示すトリガー出力付きの
ro
ビットフィールド型です
- 読み出しを示すトリガー出力付きの
- row0trg/row1trg
-
ro
/row[0/1]trg
を合わせたビットフィールド型です
-
- rowo
-
ro
/wo
を合わせたビットフィールド型です
-
- rowotrg
- 読み出し/書き込みを示すトリガー出力付きの
rowo
ビットフィールド型です
- 読み出し/書き込みを示すトリガー出力付きの
- rw
- 読み書き可能なビットフィールドです。
- rwc
- 読み書き可能なビットフィールドで、クリア入力が付随します。
-
reference
が指定された場合、指定されたビットフィールドがクリア入力となります。
- rwe
- 読み書きの可能なビットフィールドで、イネーブル入力が付随します。
- イネーブル入力が 1 の時に、書き込みができます。
-
reference
が指定された場合、指定されたビットフィールドがイネーブル入力となります。
- rwl
- 読み書き可能なビットフィールで、ロック入力が付随します。
- ロック入力が 1 の場合、書き込みはできません。
-
reference
が指定された場合、指定されたビットフィールドがロック入力となります。
- rws
- 読み書き可能なビットフィールドで、セット入力が付随します。
- セット入力が 1 の場合、外部からの入力データを取り込みます。
-
reference
が指定された場合、指定されたビットフィールドがセット入力となります。
- rwtrg
- 読み出し/書き込みを示すトリガー出力付きの
rw
ビットフィールド型です
- 読み出し/書き込みを示すトリガー出力付きの
- w0c/w1c
-
w0c
の場合は 0 を、w1c
の場合は 1 を書き込んだ場合に 0 クリアされます。 -
reference
が指定された場合、指定されたビットフィールドはマスクとして使用されます。-
value & mask
が読み出し値になります。 - マスクが掛かっていない、素の
value
も外部に出力されます。
-
- 割り込みステータスへの使用を想定しています。
-
- w0crs/w1crs
-
w0crs
の場合は 0 を、w1crs
の場合は 1 を書き込んだ場合に、0 クリアされます。 - 読み出し時は、全ビットに 1 が設定されます。
-
- w0s/w1s
-
w0s
の場合は 0 を、w1s
の場合は 1 を書き込んだ場合に 1 にセットされます。
-
- w0src/w1src
-
w0crc
の場合は 0 を、w1src
の場合は 1 を書き込んだ場合に、1 が設定されます。 - 読み出し時は、全ビットが 0 クリアされます。
-
- w0trg/w1trg
-
w0trg
の場合は 0 を、w1trg
の場合は 1 を書き込んだ場合に、one-shot のトリガー信号が出力されます。 - 処理開始の通知や、クリア入力への使用を想定しています。
-
- w1
- リセット後、1 回だけ書き込めるビットフィールドです。
- wo
- 書き込みのみ可能なビットフィールドです。
- wo1
-
w1
型の write only 版です。
-
- wotrg
- 書き込みを示すトリガー出力付きの
wo
ビットフィールド型です
- 書き込みを示すトリガー出力付きの
生成!
rggen
コマンドにコンフィグレーションファイル、レジスタマップを与えると、SystemVerilog RTL などを生成します。コンフィグレーションファイルは -c
オプションの引数として与えます。複数個のレジスタマップを一度に与えることも可能です。
$ rggen -c config.yml block_0.yml block_1.yml
実行ディレクトリに、SystemVerilog RTL/UVM RALモデル/C ヘッダーファイル/Markdown が出力されます。出力されるフィアルの命名規則は以下のとおりです。
File Type | Naming Rule |
---|---|
SystemVerilog RTL |
block_name .sv |
UVM RAL model |
block_name _ral_pkg.sv |
C header file |
block_name .h |
Markdown |
block_name .md |
出力先を代えたい場合は -o
オプションで出力先を指定します。
$ rggen -c config.yml -o out block_0.yml block_1.yml
この場合、out
ディレクトリにファイルが書き出されます。
組み込み
生成された SystemVerilog RTL/UVM RAL model は、共通 RTL モジュール、UVM RAL クラスライブラリを使って構成されています。
- RTL モジュール
- UVM RAL クラスライブラリ
生成物を、実際のデザインや検証環境に組み込むには、上記のリポジトリをクローンする必要があります。また、クローン先を示す環境変数 (RGGEN_SV_RTL_ROOT
/RGGEN_SV_RAL_ROOT
) も設定します。
$ git clone https://github.com/rggen/rggen-sv-rtl.git
$ export RGGEN_SV_RTL_ROOT=`pwd`/rggen-sv-rtl
$ git clone https://github.com/rggen/rggen-sv-ral.git
$ export RGGEN_SV_RAL_ROOT=`pwd`/rggen-sv-ral
シミュレーションの場合、シミュレーションコマンドは次のようになります。
$ vcs \
-full64 -sverilog -ntb_opts uvm \
-f $RGGEN_SV_RTL_ROOT/compile.f block_0.sv your_design.sv \
-f $RGGEN_SV_RAL_ROOT/compile.f block_0_ral_pkg.sv your_tb.sv
生成た SystemVerilog RTL と UVM RAL モデルを使ったサンプル環境を準備しています。実際に、組み込む際の参考になると思います。
質問など
質問やフィードバックがありましたら、以下の連絡先に連絡願います