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