22
14

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

制御レジスタ (CSR) を自動生成する

Last updated at Posted at 2019-08-29

制御レジスタ (CSR) を自動生成する

はじめに

制御レジスタの実装、手書きですか?実装自体は難しくはないですが、ビット位置など、微妙に異なる単調な記述が続くので、かなり面倒です。こういう手合のものは、自動生成するに限ります。という訳で、自動生成ツールを作りました。

RgGen

RgGen (https://github.com/rggen/rggen) は、YAML とかExcel とか仕様に近い記述から、

  • SystemVerilog/Verilog/VHDL RTL
  • シミュレーション用モデル (UVM RAL)
  • C ヘッダーファイル
  • Markdown

を自動生成するツールです。

こんな入力

から、こんな出力を生成します。

また、追加のプラグインを用いれば、Verilog/VHDL/C header を生成することもできます。

独自ビットフィールドやレジスタバスのプロトコルの追加など、自身の環境に合わせて拡張することも可能です。実際、ビットフィールドや社内仕様のプロトコルを追加して、実業務で運用しています。拡張方法については、以下を参照してください。

インストール

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 が指定されている場合に有効です。
    • { lsb: 0, width: 4, sequence_size: 4, step: 16 } の場合、実際のビット割当は以下のようになります。
      • [3:0]
      • [19:16]
      • [35:32]
      • [51:48]
  • 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 クラスライブラリを使って構成されています。

生成物を、実際のデザインや検証環境に組み込むには、上記のリポジトリをクローンする必要があります。また、クローン先を示す環境変数 (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 モデルを使ったサンプル環境を準備しています。実際に、組み込む際の参考になると思います。

質問など

質問やフィードバックがありましたら、以下の連絡先に連絡願います :pray:

22
14
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
22
14

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?