PSoC UDB 内 PLD/マクロセルによるアップダウン・カウンタ (2)

論理合成ソフトウェアでは、ゲート・レベルのランダム・ロジックで描いた加算/減算器回路からマクロセルの arithmetic / carry-chain モードを利用する回路に自動的に変換されるようにはなっていないので、ユーザが verilog コードで明示的に示してやる必要があります。
verilog コードは C ソースのように任意にプロジェクトに追加できるわけではなく、ユーザ定義コンポーネントのシンボルを作成し、それに付随して生成される .v ファイル内に必要な記述を追加して利用する形になります。
Cypress 社のドキュメント

PSoC® Creator™ Component Author Guide
Document # 001-42697 Rev. *S
 
PSoC® Creatorコンポーネント作成者ガイド
文書番号 # 001-69351 版 *A

http://japan.cypress.com/documentation/component-datasheets/psoc-creator-component-author-guide

で詳細は説明されているので、ここではコンポーネントの作成方法については要点を述べるにとどめます。

  1. メインのプロジェクトが属するワークスペース内に「ライブラリ・プロジェクト」を追加する。
  2. ライブラリ・プロジェクト内で「Add Component Item...」を実行。
  3. 「Symbol Wizard」実行。
  4. コンポーネントのシンボルの入出力ピンを定義。
  5. 「Generate Verilog」で .v ファイル作成。
  6. .v ファイル内に必要な記述を追加。

ここでは PSoC UDB のマクロセルの arithmetic / carry-chain モードを利用する加算器/減算器のことを仮に「キャリー・チェイン加算器/減算器」と呼ぶことにします。
キャリー・チェイン加算器/減算器を verilog で記述する方法には、現在のところ次の 3 種類が判明しています。

  1. Verilog 組み込みの 2 項演算子 '+' と '-' を使用する。
  2. LPM (Library of Parameterized Modules) である madd_sub() モジュールを利用する。
  3. マクロセル単位のモジュールである cy_psoc3_carry() モジュールを (複数) 呼び出す。

2. と 3. については、PSoC Creator が利用している論理合成ソフトウェアの「warp」のライブラリ・フォルダ

PSoC Creator/warp/lib/lcpsoc3/

を探っていて、「madd_sub.vhd」と「cpsoc3.vhd」の中に記述があるのを見つけたものです。
公式なドキュメントはないようですが、madd_sub() モジュールは Altera の LPM の「lpm_add_sub」メガファンクションと、機能/パラメタ名/入出力ポート名について互換なようです。
まず、1. については、下のように単に '+' で 2 項の加算を表現すれば、自動的にキャリー・チェイン加算器に変換してくれます。

parameter width = 7;
wire [width-1:0] A;
wire [width-1:0] B;
wire [width-1:0] S;

. . . . . <中略> . . . . .

assign S = (A + B);

この例では 7 ビット幅の加算になっていますが、そのビット幅と同じマクロセル 7 個でインプリメントされます。
.rpt ファイルの中からマクロセル部の記述を抜き出したものを下に示します。

----------------------------------------------------------
Macrocell listing
----------------------------------------------------------

MacroCell: Name=S_0, Mode=(Combinatorial, Carry-Chain-Start)
    Total # of inputs        : 2
    Total # of product terms : 2
    List of special equations: 
        Cpt0 Equation   = (B_0 * A_0)
        Cpt1 Equation   = (!B_0 * !A_0)
        Clock Enable: True
    Main Equation            : 2 pterms
    (
          !B_0 * !A_0
        + B_0 * A_0
    );
    Cout = \adder7_1:MODULE_1:g1:a0:g0:u0:gof:g_arith(0):ga:cadd_0\
    Output = S_0 (fanout=1)

MacroCell: Name=S_1, Mode=(Combinatorial, Carry-Chain-Continue)
    Total # of inputs        : 2
    Total # of product terms : 2
    List of special equations: 
        Cpt0 Equation   = (B_1 * A_1)
        Cpt1 Equation   = (!B_1 * !A_1)
        Clock Enable: True
    Main Equation            : 2 pterms
    (
          !B_1 * !A_1
        + B_1 * A_1
    );
    Cin = \adder7_1:MODULE_1:g1:a0:g0:u0:gof:g_arith(0):ga:cadd_0\
    Cout = \adder7_1:MODULE_1:g1:a0:g0:u0:gof:g_arith(0):ga:cadd_1\
    Output = S_1 (fanout=1)

    . . . . . <中略> . . . . .

MacroCell: Name=S_5, Mode=(Combinatorial, Carry-Chain-Continue)
    Total # of inputs        : 2
    Total # of product terms : 2
    List of special equations: 
        Cpt0 Equation   = (B_5 * A_5)
        Cpt1 Equation   = (!B_5 * !A_5)
        Clock Enable: True
    Main Equation            : 2 pterms
    (
          !B_5 * !A_5
        + B_5 * A_5
        );
    Cin = \adder7_1:MODULE_1:g1:a0:g0:u0:gof:g_arith(0):ga:cadd_4\
    Cout = \adder7_1:MODULE_1:g1:a0:g0:u0:gof:g_arith(0):ga:cadd_5\
    Output = S_5 (fanout=1)

MacroCell: Name=S_6, Mode=(Combinatorial, Carry-Chain-Last)
    Total # of inputs        : 2
    Total # of product terms : 2
    List of special equations: 
        Cpt0 Equation   = (B_6 * A_6)
        Cpt1 Equation   = (!B_6 * !A_6)
        Clock Enable: True
    Main Equation            : 2 pterms
    (
          !B_6 * !A_6
        + B_6 * A_6
    );
    Cin = \adder7_1:MODULE_1:g1:a0:g0:u0:gof:g_arith(0):ga:cadd_5\
    Cout = S_6_cout
    Output = S_6 (fanout=1)

ただし、キャリー入力は実現できません。

wire CIN;

. . . . . <中略> . . . . .

assign S = (A + B + CIN);

のように書いても、実質、

parameter width = 7;
wire [width-1:0] T;

. . . . . <中略> . . . . .

assign T = (A + B);
assign S = (T + {{width-1{1'b0}}, CIN});

を最適化せずに実現した形の、まず (A + B) を計算し、それに CIN を加算するという 2 段重ねの 14 マクロセルを消費する形にコンパイルされます。
キャリー出力については、下のような常套手段で得ることができます。

wire COUT;

. . . . . <中略> . . . . .

assign {COUT, S} = (A + B);

これは実質 (width + 1) ビット幅の加算回路になるので、マクロセルが 1 個追加され、(width + 1) 個のマクロセルが消費されます。
減算回路については、

parameter width = 7;
wire [width-1:0] A;
wire [width-1:0] B;
wire [width-1:0] DIF;

. . . . . <中略> . . . . .

assign DIF = (A - B);

と書くと、実質では、2 の補数として

assign DIF = (A + ~B + 1'b1);

を実現することになりますが、マクロセルのキャリー・チェインに外部から信号を入れたり、外部の信号として引き出したりはできないようで、キャリー・チェイン内に「ボロー = 0」、つまり「キャリー = 1」の値を導入するためにひとつ余分なマクロセルを割り当てるようになっています。
したがって、width 幅の減算回路では (width + 1) 個のマクロセルを消費することになります。
ちなみに、Delta39K のマクロセル回路ではキャリー/ボローの導入に余分なマクロセルを使う必要はありません。
キャリーやボローの入力が必要なら、madd_sub() あるいは cy_psoc3_carry() を使う必要があります。
アキュムレータなどで演算結果を保持しておくレジスタが必要な場合、以下の条件を満たせば、マクロセル内の D-FF が使われて余分なマクロセルの消費がなくなります。

  • 演算結果の「組み合わせ出力」は外部に出力せず、マクロセル内部の D-FF のみ接続される。(ファンアウト = 1)
  • マクロセル自体の機能である「非同期リセット/プリセット」のみを使用。

verilog での記述は、たとえば次のようになります。

parameter width = 7;
wire [width-1:0] A;
wire [width-1:0] B;
wire [width-1:0] S;
reg  [width-1:0] Q;
wire             CLK;
wire             RES;
. . . . . <中略> . . . . .

assign S = (A + B);

always @(posedge CLK or posedge RES)
  begin
    if (RES) 
      begin // async reset
        Q <= {width{1'b0}};
      end
    else
      begin
        Q <= S;
      end // if
  end // always

PSoC UDB のマクロセル自体には「非同期リセット/プリセット」の機能しかなく、「同期リセット/プリセット」の実現には PLD 部分も使う必要があります。
そのため、同期リセット機能を入れると、

(「組み合わせ出力モート」で演算するマクロセル) + (同期リセット機能付きレジスタのマクロセル)

という 2 段重ねの構成になってしまいます。
次回は LPM の madd_sub() を使う方法を述べます。