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

PSoC Creator がインストールされているフォルダの

. . .\PSoC Creator\. . .\warp\lib\lcpsoc3\cpsoc3.vhd

の中に arithmetuc / carry-chain モードのマクロセルひとつに「ほぼ」1 対 1 に対応する VHDL コンポーネントが以下のように宣言されています。

component cy_psoc3_carry
    port (Cin  : in  std_logic;
          Cpt0 : in  std_logic;
          Cpt1 : in  std_logic;
          Sum  : out std_logic;
          Cout : out std_logic);
end component;

「ほぼ」と書いたのは、cin / cout を使う場合に cin をキャリー・チェインに取り込むマクロセル、および、キャリー・チェインから cout を取り出すためのマクロセルが「余分」に生成されるためです。
また、VHDL では記述がケース・センシティブではないのですが、Verilog から呼び出す場合にはポート名は全部小文字で .cin() のように記述しないとエラーになるようです。
Verilog のモジュール宣言に書き直すと次のようになります。

module cy_psoc3_carry( input  cin,
                       input  cpt0,
                       input  cpt1,
                       output sum,
                       output cout );

複数ビットの加算/減算回路を実現するには、cy_psoc3_carry() を複数個並べて、下位ビットマクロセルの cout 出力を隣の上位ビットのマクロセルの cin 入力につなぎます。
最下位 (LSB) の cin 入力をオープンのままにしておくか、定数の「0」を入力しておくと下位側の「余分」なマクロセルは生成されません。 (加算ではキャリーなし、減算ではボローありの状態)
それ以外では、cin 信号をキャリー・チェインに導入するためのマクロセル (「cin マクロセル」と呼ぶことにします) が LSB の下に自動的に生成されます。
この cin マクロセルを図示すると下のようになります。

LSB (b0) のマクロセルのビット位置を「0」とすると、その下に位置する cin マクロセルのビット位置は便宜的に「-1」となります。
マクロセルのキャリー・チェイン入力「selin[-1]」には固定値「0」が入力され、データ・セレクタからは常時 cpt0 側に入力された値が出力されます。
PLD 入力に接続された CIN 信号が cpt0 側に 現れるように AND アレイのプロダクト・タームを設定しておけば、CIN 信号がキャリー・チェイン出力「selout[-1]」に現れることになります。
このとき、cpt1 側は決して選択されないので、値は何でもかまいません。 また、out[-1] 出力には「~CIN」が現れますが、この値はどこにも利用されません。
ここで、減算回路の場合、CIN は負論理のボロー、つまりボローを反転したものになりますが、ボローを 外部の NOT 回路で反転してから入力すると、その論理反転の効果は PLD の AND アレイに取り込まれ、最適化により外部の NOT 回路は除去されます。
減算回路でボロー入力不使用の場合、cin ポートに対しては「常時ボローなし」の意味の定数「1」を接続する、つまり、.cin(1'b1) のような記述をする必要がありますが、その場合、上の図で CIN = 1 とした形のマクロセルが生成されます。
cout 出力ポートをオープンでなく、信号を取り出した場合に追加で生成されるマクロセル (cout マクロセルと呼ぶことにします) を図示すると下のようになります。

「バグ」か「仕様」か分かりませんが、残念なことに out[n] 出力に現れるのは、希望の selin[n] ではなく、論理反転した ~selin[n] になっています。
単に値が論理反転しているだけだからと、外部にインバータを設けると、そのインバータの効果を cout マクロセルの内部に取り込むことはできず、インバータ実現のためにもうひとつ PLD/マクロセルを消費してしまう場合があります。
演算のビット幅 (width) をひとつ拡張する形で、(width + 1) ビット幅の演算とすれば、追加されるマクロセルの cpt0, cpt1 の値の選び方で sum 出力から取り出される cout の極性も自由に選ぶことができます。 この場合と、cout 出力使用で自動的にマクロセルが追加される場合とは、リソース使用量において同等です。
演算ビット幅が width の場合の具体的な Verilog 記述を示すと、まず、cin / cout ともに使用する場合には次のようになります。

parameter width = 7;

wire             CIN;
wire [width-1:0] A;
wire [width-1:0] B;
wire [width-1:0] SUM;
wire             COUT;

wire [width-1:0] cpt0; // carry product term 0
wire [width-1:0] cpt1; // carry product term 1
wire [width:0]   cc;   // carry chain, (width + 1) bit

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

// bitwise operation
assign cpt0  = (A & B);
assign cpt1  = (~A & ~B);

// carry in / out
assign cc[0] = CIN;
assign COUT  = cc[width];

// instantiate bit slice 0 to (width - 1)
generate
  genvar i;
  for (i = 0; i < width; i = i + 1)
    begin : bitslice
      cy_psoc3_carry mc( .cin(cc[i]),
                         .cpt0(cpt0[i]),
                         .cpt1(cpt1[i]),
                         .sum(SUM[i]),
                         .cout(cc[i+1]) );
    end // for
endgenerate                                    

演算ビット幅を 1 ビット拡張して COUT を取り出す場合には次のようになります。

parameter width = 7;

wire             CIN;
wire [width-1:0] A;
wire [width-1:0] B;
wire [width-1:0] SUM;
wire             COUT;

wire [width-1:0] cpt0; // carry product term 0
wire [width-1:0] cpt1; // carry product term 1
wire [width:0]   cc;   // carry chain, (width + 1) bit

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

// bitwise operation
assign cpt0  = (A & B);
assign cpt1  = (~A & ~B);

// carry in
assign cc[0] = CIN;

// instantiate bit slice 0 to (width - 1)
generate
  genvar i;
  for (i = 0; i < width; i = i + 1)
    begin : bitslice
      cy_psoc3_carry 
        mc( .cin(cc[i]),
            .cpt0(cpt0[i]), // (A[i] & B[i])
            .cpt1(cpt1[i]), // (~A[i] & ~ B[i])
            .sum(SUM[i]),
            .cout(cc[i+1]) );
    end // for
endgenerate

// extra bit slice for MSB carry extraction
// sum = (A + B + C) where A = 0, B = 0
cy_psoc3_carry 
  mcout( .cin(c[width]),
         .cpt0(1'b0),  // (A & B) = (0 & 0) = 0
         .cpt1(1'b1),  // (~A & ~B) = (1 & 1) = 1
         .sum(COUT) ); // carry from SUM output