Vengineerの妄想

人生を妄想しています。

Bluespec SystemVerilog : COUNTERを学ぶ(その3)

Verification Engineerの戯言


その2のコードに対して、decrementメソッドを追加します。
まず、interface/endinterfacedecrementメソッドを追加します。

    interface Counter;
      method Bit#(8) read();
      method Action load(Bit#(8) newval);
      method Action increment();
      method Action decrement();
    endinterface

次にmkCounterモジュールdecrementメソッドを追加します。

    method Action decrement();
      value <= value - 1;
    endmethod

テストベンチ側も変更します。

    rule step1(state == 1);
      counter.increment();
      counter.decrement();
      state <= 2;
    endrule


ここでテストベンチ(tbCounter.bsv)をコンパイルします。すると、次のようなエラーが表示されます。

    % bsc tbCounter.sv
    "TbCounter.bsv", line XX, column YY: (G0004) Error:
      Rule RL_step1 uses write methods that conflict in parallel:
        counter.increment
      and
        counter.decrement

エラーメッセージからcounter.incrementcounter.decrementがコンフリクトしています。

そこでstep1ルールを次のように3つのルール(step1a/step1b/step1c)に分けます。

    rule step1a(state == 1);
      counter.increment();
    endrule
    rule step1b(state == 1);
      counter.decrement();
    endrule
    rule step1c(state == 1);
      state <= 2;
    endrule

テストベンチ(tbCounter.bsv)をコンパイルしてみます。すると、今度はワーニングが表示されます。

    % bsc tbCounter.sv
    "TbCounter1.bsv", line XXX, column YYY: (G0010) Warning:
      Rule "step1b" was treated as more urgent than "step1a". Conflicts:
        "step1b" vs. "step1a":
          calls to
     counter.decrement vs. counter.increment
        "step1a" vs. "step1b":
          calls to
     counter.increment vs. counter.decrement
    "TbCounter1.bsv", line 13, column 14: (G0021) Warning:
      According to the generated schedule, rule "step1a" can never fire.

step1bstep1aがまだ、コンフリクトします。そして、step1aは呼ばれないようです。
そうですよね! counter.incrementcounter.decrementが同じクロックで呼ばれることは無いので!

そこで、PulseWireの登場です。

    PulseWire increment_called <- mkPulseWire();
    PulseWire decrement_called <- mkPulseWire();

    method Action increment();
      increment_called.send();
    endmethod
    method Action decrement();
      decrement_called.send();
    endmethod

    rule do_increment(increment_called && !decrement_called);
      value <= value + 1;
    endrule
    rule do_decrement(!increment_called && decrement_called);
      value <= value - 1;
    endrule

PulseWireインターフェースのインスタンス(increment_called, decrement_called)が追加になりました。
初期値は、mkPulseWireモジュールになります。

リファレンスガイドによると、PulseWireインターフェースは、次のようなメソッドを持っています。

    interface PulseWire;
      method Action send();
      method Bool _read();
    endinterface

sendメソッドは、ワイヤにシグナルを送ります。
_readメソッドは、PulseWireインターフェースインスタンスの値をリードしたときに呼ばれ、
シグナルがあるかどうか、つまり、sendメソッドが呼ばれたかどうかを示します。

このPulseWireインターフェースsendメソッドincrement/decrementメソッド内で使われています。

mkPulseWireモジュールは、リファレンスガイドによると、

    The writing to this type of wire is used in rules and action methods 
    to send a single bit to signal other methods or rules in the same clock cycle.

にようです。in the same clock cycleというところがミソのようです。

incrementメソッドdecrementメソッドが同じクロックで呼ばれると、
increment_calleddecrement_calledが共にTRUEになるので、
結果として、do_incrementルールdo_decrementルールは同時には実行されません。

検証、Verification、Bluespec SystemVerilog

P.S
夏休みが終わった。