PSoC UDB 内 PLD/マクロセルによるアップダウン・カウンタ (2)
論理合成ソフトウェアでは、ゲート・レベルのランダム・ロジックで描いた加算/減算器回路からマクロセルの arithmetic / carry-chain モードを利用する回路に自動的に変換されるようにはなっていないので、ユーザが verilog コードで明示的に示してやる必要があります。
verilog コードは C ソースのように任意にプロジェクトに追加できるわけではなく、ユーザ定義コンポーネントのシンボルを作成し、それに付随して生成される .v ファイル内に必要な記述を追加して利用する形になります。
Cypress 社のドキュメント
PSoC® Creator™ Component Author Guide
http://japan.cypress.com/documentation/component-datasheets/psoc-creator-component-author-guide
Document # 001-42697 Rev. *S
PSoC® Creator™ コンポーネント作成者ガイド
文書番号 # 001-69351 版 *A
で詳細は説明されているので、ここではコンポーネントの作成方法については要点を述べるにとどめます。
ここでは PSoC UDB のマクロセルの arithmetic / carry-chain モードを利用する加算器/減算器のことを仮に「キャリー・チェイン加算器/減算器」と呼ぶことにします。
キャリー・チェイン加算器/減算器を verilog で記述する方法には、現在のところ次の 3 種類が判明しています。
2. と 3. については、PSoC Creator が利用している論理合成ソフトウェアの「warp」のライブラリ・フォルダ
を探っていて、「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() を使う方法を述べます。