Communication: port
https://www.learnsystemc.com/basic/port
通信にはキーとなる概念が3つある。
- インタフェース
- sc_interface から派生した抽象クラス。sc_objectからは派生していない。
- 純粋仮想関数を持ち、それらはインタフェースを継承したチャネル内で実装される。
- ポート
- ポートはインタフェースメソッドを呼び出されたとき、バインドされているチャネルにメソッド呼び出しを転送する。
- ポートでは必要なサービスの集合を定義する。
- チャネル
- sc_prim_channel はすべてのプリミティブチャネルの基底クラス。
- チャネルはパブリックなメンバ関数を提供し、それらはinterface method call paradigmで使用される。
- プリミティブチャネルは1つ以上のインタフェースを実装する。
つまり、
- ポートはサービスを必要とし、インタフェースはサービスを定義(=API定義)し、チャネルはサービスを実装(=API実装)する。
- ポートはチャネルに接続(紐づけ)できる。その場合、チャネルはポートが必要とするインタフェースを実装している必要がある。
- ポートは基本的にチャネルへのポインタのように扱える。
ポートを使うケース
Communication: export
https://www.learnsystemc.com/basic/export
sc_exportとは
- モジュールが親モジュールに対してインタフェースを提供できるようにする
- exportに紐づいたチャネルにインタフェースメソッドコールを転送
- exportを持つモジュールによって提供されるサービスを定義できる
いつ使うか
- exportを通じてインタフェースを提供することは、モジュールの中でインタフェースを実装することの代替となる。
- 明示的なexportの使用により、モジュールは構造化された方法で複数のインタフェースを提供できるようになる。
- あるモジュールが子モジュール内のチャネルインタフェースに属するメンバ関数を呼び出すとき、子モジュールのexportを経由すべき。
Communication: port 2 port
https://www.learnsystemc.com/basic/port2port
今まで見てきたケース
- 同じモジュール内の2つのプロセスがチャネル経由で接続
- process1() --> channel --> process2()
- 別モジュールの2つのプロセスがポートとチャネル経由で接続
- module1::process1() --> module1::port1 --> channel --> module2::port2 --> module2::process2()
- 別モジュールの2つのプロセスがexport経由で接続
- module1::process1() --> module1::channel --> module1::export1 --> module2::port2 --> module2::process2()
これらのケースではすべてポートの接続にチャネルが必要だった。モジュールのポートからサブモジュールのポートに直接できる接続できる特別なケースがある。
-
- module::port1 --> module::submodule::port2
補注:チャネル経由はインタフェースがin/out異なってるが、サブモジュールへの直接接続時はin同士またはout同士の接続。
Communication: specialized ports
https://www.learnsystemc.com/basic/specialized_port
sc_portクラスでポートを宣言する以外に、様々な特殊化されたポートクラスがある。異なったチャネルタイプや追加の機能がある。
- sc_in: signalを使う
- sc_fifo_in: fifo読み込み
- sc_fifo_out: fifo書き込み
- sc_in&ly;bool>, sc_in<sc_dt::sc_logic>: value_changed(), pos(), neg()
- sc_inout: signalを使う。value_changed(), initialize()
- sc_inout<bool>, sc_inout<sc_dt::sc_logic>: value_changed(), initialize(), pos(), neg()
- sc_out: sc_inoutの派生クラス。
- sc_in_resolved: sc_in<sc_dt::sc_logic> の派生クラス。
- sc_inout_resolved: sc_inout<sc_dt::sc_logic> の派生クラス。
- sc_out_resolved: sc_inout_resolvedの派生クラス。
- sc_in_rv: sc_in<sc_dt::sc_lv<W>>の派生クラス。
- sc_inout_rv: sc_inout<sc_dt::sc_lv<W>>の派生クラス。
- sc_out_rv: sc_inout_rvの派生クラス。
sc_port<sc_signal_inout_if<int>> はsignalが提供するメンバ関数のみにアクセスできる。
1. read()
2. write()
3. default_event()
- sc_sensitiveの<<演算子によって静的センシティビティを定義するためにポートが使われた場合に呼び出される。
4. event()
- イベントが発生したかをチェックする。true/falseを返す。
5. value_changed_event()
- 値が変化したイベント
sc_port<sc_signal_inout_if<bool>> は追加で signal<bool>が提供するメンバ関数にアクセスできる。
6. posedge()
7. posedge_event()
8. negedge()
9. negedge_event()
sc_inout<> は追加のメンバ関数がある。
10. initialize()
- チャネルに紐づけられる前に初期値を定義
11. value_changed()
- チャネルに紐づけられる前にセンシティビティを設けるために使う
bool, sc_logic, sc_inout<bool> チャネルはさらに以下のメンバ関数がある。
12. pos()
13. neg()
1~9のメンバ関数はsignal channelによって提供される。port->method()で呼び出す。
10~13のメンバ関数は特殊化ポートによって提供される。port.method()で呼び出す。
補注:
クラスの親子関係は以下が参考になる。
https://eda-playground.readthedocs.io/en/latest/_static/systemc-2.3.1/sysc/inherits.html
Communication: port array
https://www.learnsystemc.com/basic/port_array
ポートを宣言するとき、
- 一つ目のテンプレート引数はインタフェース名を指定する。ポートの種別でもある。
- ポートはポート種別に応じたチャネル、port, export にのみ紐づけできる。
- 二つ目のテンプレート引数はOptionalでチャネルインタフェースの最大数を整数値で指定できる。
- デフォルトは1
- もし値が0なら、チャネルインタフェースと無限に紐づけできる
- 許容される数以上のチャネルと紐づけするとエラーになる
- 三つ目のテンプレート引数はOptionalでsc_port_policy型のポートポリシーを指定できる。複数のポートを紐づけしたときのルールや紐づいてないポートのルールが決定される。
- デフォルト SC_ONE_OR_MORE_BOUND: ポートは一つ以上のチャネルと紐づけされる。最大値は2つ目の引数で指定される。エラボレーションの最後で紐づけされていないままならエラー。
- SC_ZERO_OR_MORE_BOUND: ポートは0以上のチャネルと紐づけされる。最大値は2つ目の引数で指定される。エラボレーションの最後で紐づけされていないままでもよい。
- SC_ALL_BOUND: ポートは2つ目の引数で指定される1以上の値とちょうど同じだけのチャネルインスタンスと紐づけされる必要がある。
- 2つ目の引数が0の場合、SC_ONE_OR_MORE_BOUNDと同じ意味になる。
- 紐づけされたチャネル数が不足する場合はエラーになる。
ポートを同じ(?)チャネルに複数回紐づけするのはエラーになる。
C++の構文を使ってポートアレイを定義することもできる。
sc_port<IF> p[10] or vector<sc_port<IF>> p(10);