PULPの開発する、ハードウェア開発用の依存関係解決ツールBenderについて調査している。
まず概要部分を日本語に翻訳してみよう。
Benderはハードウェア設計プロジェクトのための依存関係管理ツールです。IP間の依存関係を定義し、単体テストを実行し、ソースファイルが各種シミュレーションツールや合成ツールで正しく動作することを検証する方法を提供します。
これは基本的にはバージョン管理ツールだが、Gitにもsubmodulesという類似機能があるのに、なぜBenderが必要なのだろうか。
Gitのsubmodulesは、依存する他のリポジトリを含めたり、特定のバージョンに固定したりすることができる。
一方、Benderは複数のGitリポジトリを管理できるだけでなく、依存関係のチェックも行える。
この依存関係のチェックとは具体的に何だろうか。サブモジュールとして管理しているモジュールの仕様が変更されると、メインのリポジトリ側でそれが使用できなくなる可能性がある。単純にサブモジュールをアップデートすると、すぐに互換性が失われる恐れがある。
あるリポジトリrepo_Aに、モジュールAが存在するとする:
module A; B inst_B ( .inA (in_a), .inB (in_b), .outC (out_c) ); endmodule
別のリポジトリrepo_BにモジュールBが存在し、AとBは依存関係にあるとする。
現在、repo_Bのcommit IDは0x100とする。
module B ( input inA, input inB, output outC ); assign outC = inA + inB; endmodule
repo_Bに機能が追加されたとする。
機能の追加には以下の種類がある:
- 後方互換性のない変更があった場合
- 後方互換性のある新規機能の追加があった場合
- 後方互換性のあるバグ修正が行われた場合
まず2.と3.の場合を考えよう。バグ修正ではないが、以下のような機能修正が行われたとする。これは下位互換性がある。この変更が行われたrepo_Bのコミットを0x101とする。
module B ( input inA, input inB, output outC ); always_comb begin outC = inA + inB; end endmodule
では、1.のような後方互換性のない変更が加わった場合はどうか。上記のモジュールBにおいて、入力inBが不要だったとする。すると、変更後のモジュールBは以下のようになる。この変更が行われたrepo_Bのコミットを0x102とする。
module B ( input inA, output outC ); always_comb begin outC = !inA; end endmodule
この場合、repo_Aにとってrepo_Bの0x100、0x101は使用可能だが、0x102にsubmoduleのリビジョンをアップデートすると動作しなくなる。git submoduleはこのようなサブモジュールの互換性チェックを行わず、単純にリビジョンを更新するため、コンパイル時になって初めて互換性の問題が発覚する。
これをBenderはどのように解決するのか。
Benderは、リポジトリのリビジョン番号に加えて、セマンティック・バージョンをタグとして適用することで互換性を表現する。
MAJOR.MINOR.PATCH
- MAJOR(メジャー):後方互換性のない変更が加えられた場合に増加
- MINOR(マイナー):後方互換性のある新機能が追加された場合に増加
- PATCH(パッチ):後方互換性のあるバグ修正が行われた場合に増加
これにより、上記のrepo_Bでは:
- 0x100→0x101のときはMINORまたはPATCHの番号を更新する(タグを付ける)
- 0x101→0x102のときはMAJORの番号を更新する
repo_Aはrepo_Bの互換性のあるバージョンをYAMLファイルで記述し、repo_Bのタグに含まれるセマンティック・バージョンをチェックすることで、どこまで更新可能かを管理できる。