【PARTHENON解説書の目次に戻る】

6. 論理合成プログラム SFLEXP

6.1 起動方法
6.2 エラーへの対処
6.3 SFLEXPが出力する初期回路の外部端子と構成要素
6.4 論理合成の実際

SFLEXP (SFL EXPander) は,SFLファイルを読み込み,SFLのモジュールごとに論理合成を行います.

SFLEXPによる合成結果のネットリストは,HSL言語(形式)のファイルとして出力されます.PARTHENONのネットリスト記述言語はNLDですが,開発の歴史的経緯からSFLEXPは,HSLによるネットリストを出力します.しかし,HSL_NLDにより,このHSLはすぐにNLDに変換されるので,利用者はHSLの形式を知る必要はありません.

NLDで表現された論理合成結果は,続いて,

の各プログラムに渡され,そこで論理圧縮と実部品割り付けが行われます.


6.1 起動方法

簡単なSFL記述を論理合成してみましょう.

として論理合成を行うには,MS-DOSのコマンド・プロンプト A> に対し

A>sflexp abc.sfl abc.hsl

と入力します.すると,ずいぶんたくさんのメッセージが出力されますが,最後から2行目に

There are 0 errors.
が現れていれば,論理合成は正しく実行されています.

ここで SFLEXPの起動の形式をまとめておきます.起動形式は以下のとおりです.

    sflexp sfl_file_name hsl_file_name [switch ...]

sfl_file_name : 合成対象のSFLソース・ファイル名

hsl_file_name : ネットリストとして出力するHSLファイル名

switch ::= {-single | -multi | -nopost} | {-nld | -nonld} | -fin param | -level_factor param | -mem param

switch (スイッチ)は順不同で,任意個指定できます.省略された場合は,選択的 switch では先頭のものが指定されたものとされます.param指定の switchでは,あるデフォルト値が使われます.

以下,各スイッチについて解説します.

(1) -single | -multi | -nopost

SFLEXPでは,SFLのモジュールごとに論理合成を行っていきますが,SFLで記述された手続きを簡単化しつつおおよそ構造へ変換したあとで,もう一度「論理の簡単化」を行います.この処理をポストオプト処理といいます.「書き込みの競合」などもこの処理の過程で見つかります.-single | -multi | -nopost の switchは,このポストオプト処理を制御するものです.

-single は,ポストオプトをアクションごとに行うことを指定します.ここでアクションとは,レジスタのライト・イネーブル論理やセレクタのセレクト論理のことです.

-multi は,ポストオプトをすべてのアクションに対して一括して行うことを指定します.

-nopost では,ポストオプトは行われません.

これら三つのスイッチを比べると,一見,-multi を指定するのがもっとも良いように思えます.しかし,-multi を指定すると,モジュール全体の論理を対象として簡単化を行うので,たしかに回路は小さくなりますが,

などの問題があり,-single のほうが一般に良い結果が得られます.そこでデフォルトは -single となっています.

(2) -nld | -nonld

-nld では,SFLEXPで合成結果を出力する際に,SFLのモジュールから合成されたネットリストだけでなく,セレクタやビット幅を持つレジスタなど(4.3節で分解可能な論理セルと述べたもの)のネットリストも合成結果のネットリストに含めることを指定します.

-nonld では,SFLのモジュールから合成されたネットリストのみが出力されます.

(3) -fin param

SFLEXPは,手続きを構造に変換した直後に論理要素をand,orからnand,norへ変換します.これはnand,norのほうが実際の論理ゲートとの対応がよいためです.この変換では,さらに回路に現れるnandゲートとnorゲートの種類をその入力数で制限することができます.論理ゲートの入力数をファン・イン数というのでfinというスイッチ名になっています.たとえば,

-fin 5

と指定した場合は,2入力から 5入力までのnandゲート,norゲートが使われます.1を指定するとand,orからnand,norへの変換そのものが行われず,ファン・イン数の制限も行われません.指定のない場合は 8が指定されたものとみなされます.

(4) -level_factor param

SFLEXPの論理合成の過程には,論理の共通部分をくくり出してまとめる処理が組み込まれています.この処理により,論理回路を多段にし,回路を小さくするわけです.ところがこの処理がききすぎると論理段数が深くなりすぎることがあり,とくに演算回路などでは,その部分がクリティカル・パスとなって,LSI全体の性能を落とすようなことも起こります.そのような場合に,このスイッチによって,共通部分のくくり出しの度合いを制御します.

この-level_factorスイッチの意味は,ある論理のくくり出し候補に対し,そのくくり出しによって減る入力数が param 程度では,そのくくり出しをしないというものです.

たとえば,

-level_factor 3

では,あるくくり出す論理の候補があって,そのくくり出しによって全体の論理ゲートの入力数が 3個減るとすると,そのくくり出しは行わないということになります.paramが 0であれば,どのようなくくり出しも効果があるなら行うということになります.指定がない場合は,0が指定されたものとされます.

(5) -mem param

SFLEXPでは,リスト処理やパターンマッチを使用した高度なプログラミングが必要であったため,論理合成処理の制御をprolog言語で書いています.このため,SFLEXPにはprologの処理系が含まれており,これがスタックを作るためのスタティックなメモリ領域を必要とします.

一方,高速化を要求されるためC言語で書かれた部分は,ダイナミックなヒープ領域を必要とします.

すなわち,SFLEXPは,

の両方を必要とします.prologのメモリ領域が不足した場合には「スタック・オーバフロー」のメッセージが,Cのメモリ領域が不足した場合には「一般保護違反」のメッセージが出力されます.どちらをどれだけ用意するべきかは,SFLの記述によるので一概には決められません.そこでこのスイッチで制御する必要があります.

この-memスイッチは,prologのためのスタティックな領域をどれほど割り付けるかを決定します.残りがCのための領域となります.

paramは,sss か ss か s か m で,

をprologのスタック領域に使用します.

利用できるメモリが 3Mバイトでは sss を,5Mバイトでは ss を,10Mバイトでは s を,14Mバイト以上では m を指定するのがよいでしょう.


6.2 エラーへの対処

論理合成中に発生するかもしれない問題には,

(1) シンタックス・エラー
(2) オーバフロー
(3) 書き込み競合エラー
(4) 遷移先が存在しない状態遷移エラー

があります.

(1) シンタックス・エラー

SFLEXP はSFL記述を読み込むところから処理を始めます.この過程で見つかるSFL記述の間違いをシンタックス・エラーといいます.

シンタックス・エラーがある場合は,

??? The facility name: xx (in operation) is not defined.
などのエラー・メッセージが出力され,次に,
準備ができたらどれかキーを押して下さい...
と出力されます.ここでなにかキーを押すと,エディタが起動されてエラーの修正が可能となります.

なお,エディタが起動されるメカニズムは,%PARTHENON%\com にある sfl_edit.bat ファイルに指定されていますので,お手持ちのエディタに合わせて使いやすいように変更してください(デフォルトでは,ビレッジセンター鰍フ Vz Editor を起動するように設定.ただし,起動パスにVz Editor が存在していることが前提).エディタの起動時点でカーソルはエラーを発見した行にあります.また f・4 キーによりエラーがありそうな場所の直後の行と桁位置が示されます.

エラーを修正したら,エディタを終了させてから,r. と入力してください.修正箇所から解析を再開します.r. はSFLEXPのサブコマンドで,再試行(retry)を表します.この時点で入力可能なサブコマンドは,

r. :retry (再試行)
a. :abort (中断)
h. :help
の三つだけです.a. を入力すると,SFLEXPはその時点で終了します.

また,SFLEXPのサブコマンドを入力中に,

prolog: syntax error at LINE=2, COLUMN=0
のようなメッセージが出力されたときは,
.<CR> (ピリオドとリターン)
を何度か入力し,":" が入力プロンプトとして出力されてから,サブコマンドを入力してください.この時点で検出できるエラーは,

などです.

シンタックス・エラーがなければ,そのまま論理合成に移ります.論理合成は非常にたくさんのステップを踏むので,無限ループに入ってしまったのかと心配にならないようにたくさんのメッセージを出力します.これらは,それなりの報告をしているのですが,気にしないでください.

(2) オーバフロー

ソフトウェアのコンパイラは「手続きを手続きへ変換」すればいいのに対して,論理合成は「手続きを構造へ変換」するために,その過程で論理式の簡単化という処理が必要となります.この処理には膨大なメモリと計算量を必要とする場合があるので,SFLの記述によってはオーバフローが発生することがあります.これはSFLの記述が正しくても発生します.もしオーバフローが発生したら記述を変えてみるしかありません.オーバーフローに関しては 6.4節で詳しく説明します。

(3) 書き込み競合エラー

書き込み競合エラーというのは,SFL記述に問題があるときに報告されます.SFLEXPの終了まぎわに出力されるエラー・カウントの数が 0でないときは,このエラーか状態遷移エラーが発生しています.

<リスト6.1> 書き込み競合エラーが発生する記述例

 1:	module ng1 {
 2:	    input a<8>;
 3:	    input b<8>;
 4:	    input c;
 5:	    instrin start;
 6:	    reg r<8>;
 7:	    instrin start any {
 8:	        c: r:=a;
 9:	        c: r:=b;
10:	    }
11:	}

リスト6.1の例で見てみると,8行目,9行目で指定されたレジスタrへの代入が必ず競合します.もちろんシミュレーションをしっかりやっておけば,この問題は見つかるでしょう.しかし,競合を起こす状況を設定しないとシミュレータはエラーを報告してくれませんので,見落とすかもしれません.ところが,論理合成ではすべての論理を展開して整理しますので,レジスタrへの代入が競合することがわかるのです.エラー・メッセージは

???ERROR data collision must occur, because select condition becomes 'true' in action(2) ???
のように出力されますので,action(id)の idからどのアクションかを追跡します.アクションとはポストオプト処理のところで説明した様に「レジスタへの書き込み」などのことです.追跡の方法は詳しくは述べませんが,エラー・メッセージの付近を注意深くながめるとヒントがあると思います.最初のうちはエラーが出たらとにかくSFL記述を見直してみます.

(4) 状態遷移エラー

遷移先が存在しない状態遷移エラーは,リスト6.2のように,たった 1個の有効な状態(st2へは遷移が発生しないのでst1のみが有効)にわざわざ状態遷移する記述(goto文のこと)があるとき,合成処理が正しく行われずにエラーとなります.

自状態に対する状態遷移は,状態遷移しないのと同じですから,そのような無意味な状態遷移を記述しないようにしてください.

<リスト6.2> 無駄な状態遷移がエラーとなる記述例

 1:	module ng2 {
 2:	    stage_name s {
 3:	        task t();
 4:	    }
 5:	    stage s {
 6:	        state_name st1;
 7:	        state_name st2;
 8:	        first_state st1;
 9:	        state st1 goto st1;
10:	        state st2 goto st2;
11:	    }
12:	}


6.3 SFLEXPが出力する初期回路の外部端子と構成要素

SFLEXPはSFLのモジュール毎にSFL記述を回路(初期回路)に変換します.その構成要素として論理セルが使われること,またこの回路はOPT_MAP,RINV,ONSETのプログラムによって最適化され,最終的に実セルのみで作られた回路に変換されることはすでに述べました.本6.3節と次の6.4節では,この初期回路の様子をもう少し詳しく述べます.

本節では,SFLの構成要素と直接対応する初期回路の構成要素やSFLの動作記述とは無関係で固定的な接続関係を,6.4節では、SFLのいろいろな動作記述が構成要素間のどんな接続に変換されるのかを述べます.

● モジュールの外部端子

初期回路(nldのモジュール)の外部端子はSFLでのモジュールの外部端子に以下の4つの外部入力端子

p_reset パワー・オン・リセット
m_clock マスタ・クロック
s_clock スレーブ・クロック
b_clock バス・クロック
が加えられたものとなります.

初期回路の外部端子の端子タイプはSFLでの端子タイプから以下の対応で示されるものとなります。

<SFLでの端子タイプ> <初期回路での端子タイプ>
データ入力端子と制御入力端子 → 外部入力端子
データ出力端子と制御出力端子 → 外部出力端子
データ双方向端子 → 外部双方向端子
● サブモジュール,サブモジュールの外部端子

SFLでのサブモジュールは初期回路のサブモジュールとなります.サブモジュールのインスタンス名はSFLでのインスタンス名と同じです.サブモジュールの外部端子はSFLでのサブモジュールの外部端子に上記の4つの端子(p_reset,m_clock,s_clock,b_clock)が加えられたものとなります.

● データ・レジスタ

SFLのキーワード reg,reg_wr,reg_ws で定義されたデータレジスタは初期回路では(論理セルの)データ・レジスタreg-N,regr-N,regs-N となります.インスタンス名はSFLでのインスタンス名と同じです.

● 状態レジスタ

SFLでのステージとセグメントは,ステージ内の状態の総数(セグメントの状態の数を含めます)をNとすると,log2N個の状態レジスタ reg--1 となります.インスタンス名は,ステージ名-bit_posi または,ステージ名-セグメント名-bit_posi です.ここで bit_posi は0,1,2,...です.

● タスク・レジスタ

SFLのステージからは,ステージ名--allというインスタンス名のタスクレジスタ reg---1 が生成され,SFLでのタスクは,ステージ名-タスク名というインスタンス名のタスクレジスタ reg---1 となります.

● リセット、クロックの接続

サブモジュールや論理セル(データ・レジスタ,状態レジスタ,タスク・レジスタ)のp_reset,m_clock,s_clock,b_clock端子はモジュールのp_reset,m_clock,s_clock,b_clock端子に直接接続されます.すなわち,これらのリセット信号やクロック信号の極性やパルスの波形の意味はPARTHENONのプログラムでは規定されません.これらの情報はセルライブラリで規定されます.

たとえば、セルライブラリを設計あるいは選択する時に,レジスタとして単一クロックのエッジ・トリガタイプを採用し,クロック端子としてm_clockを使用した場合(こういった情報はPCDで記述してOPT_MAPに伝える),OPT_MAPの機能により,s_clock端子とその接続は最終回路から削除されます.

● 双方向端子に対するバス・ドライブ回路、セレクタの挿入

モジュールの外部双方向端子やサブモジュールの外部双方向端子には,これをドライブするためのバスドライブ回路bdrv-Nが接続されます.そのインスタンス名は各々,データ双方向端子名-drive,サブモジュール名-データ双方向端子名-driveです.さらにデータ双方向端子への転送元が複数個ある場合はバスドライブ回路の前にセレクタslN-Mが挿入されます.

● 出力端子に対するセレクタの挿入

データ出力端子へのデータ転送元が1個の場合は初期回路の転送元と外部出力端子は直接接続されます.データ出力端子へのデータ転送元が複数個の場合は外部出力端子の前にセレクタが挿入されます.

● レジスタに対するセレクタの挿入

レジスタへの転送元が複数個のとき,レジスタの入力端子の前にセレクタが挿入されます.

● 挿入されるセレクタのセル名、インスタンス名

挿入されるセレクタには論理セルのslN-Mが使われ,インスタンス名はsel-1,sel-2,...となります.

● 制御内部端子

SFLでの制御内部端子は初期回路では制御の中継端子inst-dumとなります.インスタンス名はSFLでの制御内部端子名と同じです.制御内部端子の起動が複数箇所から行われる場合はinst-dumの前にorゲートが挿入されます.

● データ内部端子(データの転送元が複数個の場合)

データの転送元が複数個の場合,bus_vまたはbus,あるいはsel_vまたはselで定義されたデータ内部端子は,以下の対応でbsN-MあるいはslN-Mへ変換されます.

bus_vまたはbus → bsN-M (3ステートによるデータ・セレクタ)
sel_vまたはsel → slN-M (and,orによるデータ・セレクタ)
bsN-MあるいはslN-Mのインスタンス名はSFLでのデータ内部端子の名前と同じです.

bus_v,sel_vで定義されたデータ内部端子は論理圧縮の過程でなくなってしまうことがあり,その場合には初期回路には何もあらわれません.

● データ内部端子(データの転送元が1個の場合)

データの転送元が1個の場合,selまたはbusで定義されたデータ内部端子はデータの中継端子bus-Nに変換されます.bus-Nのインスタンス名はSFLでのデータ内部端子の名前と同じです.

● 論理ゲート、定数

SFLの動作記述から生成される論理ゲートや定数のインスタンス名は以下のようになります.

	<論理セル名>	<インスタンス名>

	high-		high-
	low-		low-
	inv-		inv-1,inv-2,inv-3,...
	and--M		and-1,and-2,and-3,...
	nand--M		nand-1,nand-2,nand-3,...
	or--M		or-1,or-2,or-3,...
	nor--M		nor-1,nor-2,nor-3,...
	eor--2		eor-1,eor-2,eor-3,...




6.4 論理合成の実際

論理合成は,「手続きを構造へ変換するものであり,手続きを手続きへ変換するソフトウェアのコンパイラとは違う」と述べましたが,所詮,SFLの構文や要素ごとにテンプレート(雛形)を用意していて順々に変換しているだけです.ただ最適化を高度に行わないと,どのテンプレートを選ぶかの判断さえできなくなるという点で論理合成の要となる技術は論理簡単化であるといえます.本書ではその詳細にはふれません.

SFLEXPは,6.3節で述べたように,SFLの要素のうち実体と対応がつくもの(たとえばレジスタなど)は,そのまま論理セルへ変換し,その間を制御論理を加えながら接続していきます.このとき,必要であればデータの選択を行うセレクタを挿入していきます.

● 制御とデータが分離される例

リスト6.3を合成してみます.合成は,

    sflexp %1.sfl %1.hsl
    mkdir %1.1st
    hsl_nld %1.hsl %1.1st
    nld_ps -o %1.ps %1 %1.1st %PARTHENON%/CELLDEMO/START
    type %1.ps > prn

のようなバッチ・プログラムで行うことにします.OPT_MAP,ONSET,RINVをかけませんので回路の最適化は不十分ですが,SFLEXPが何を出力しているかは,このほうがよくわかります.なおこのバッチ・プログラムではプリンタがPostScript対応でなければなりません.


<リスト6.3> 制御とデータが分離される例

 1:	module test1 {
 2:	    input abc;
 3:	    bidirect xyz;
 4:	    instrin start;
 5:	    instruct start xyz = abc;
 6:	}

論理合成の結果は,図6.1のようになります.

<図6.1> リスト6.3の合成結果

この例では,データ双方向端子xyzに対する値の設定があるので,まず,bdrv-1がxyzの前に挿入され,その出力outがxyzに接続されます.xyzに対する入力がabcのみなので,abcは単純にbdrv-1の入力inに接続されます.また,xyzへ値を設定する契機は,startが"1"のときですので,startがbdrv-1のenbに接続されることになります.

● 制御がなくなってしまう例

次は制御がなくなってしまう例です.リスト6.4は,xyzがデータ出力端子として定義されて

いる以外は前の例と同じです.これを合成すると図6.2のようになります.

このSFL記述は,startが"1"のときabcをxyzへ設定することを主張しており,startが"0"のときは何も主張していません.すなわちstartが"1"でないときはxyzはとくになんでもよいわけですから,常時abcを出力するように合成します.この結果,たんにabcがxyzへ接続され,start端子は不要となります.


<リスト6.4> 制御が無くなってしまう例

 1:	module test2 {
 2:	    input abc;
 3:	    output xyz;
 4:	    instrin start;
 5:	    instruct start xyz = abc;
 6:	}

<図6.2> リスト6.4の合成結果



● 論理が圧縮される例

次は演算子が含まれる場合です.リスト6.5を合成すると図6.3のようになります.この例のように演算子があらわれると,SFLEXPは記述どおり単純に接続するのではなく,論理を簡単にしようとします.その結果がこの回路です.SFLEXPは全体を見た極性の最適化を行いませんので,この例の様に無駄なインバータが含まれてしまうことが起こりますが,無駄なインバータはOPT_MAP,ONSET,RINVによって削除されます.


<リスト6.5> 演算子が含まれる例

 1:	module test3 {
 2:	    input abc;
 3:	    input def;
 4:	    output xyz;
 5:	    instrin start;
 6:	    instruct start xyz = abc & def;
 7:	}


<図6.3> リスト6.5の合成結果


さて,SFLEXPによってどのように論理が簡単化されるかを見るために,もう少し複雑な論理をもつリスト6.6を合成させてみましょう.結果は図6.4のようになります.もとの論理式に比べ簡単になっていることがわかります.また,f1とf2は同じ論理なのですが,論理簡単化の過程できちんと論理のくくり出しが行われています.


<リスト6.6> 論理圧縮をさせる例

 1:	module test4 {
 2:	    input     a, b, c, d;
 3:	    output    f1, f2;
 4:	    instrin   start ;
 5:	    instruct start par {
 6:	        f1 = (^a & ^b & ^c & ^d)
 7:	           | (^a & ^b & ^c &  d)
 8:	           | (^a &  b & ^c & ^d)
 9:	           | ( a & ^b & ^c & ^d)
10:	           | ( a &  b & ^c & ^d)
11:	           | ( a &  b &  c &  d) ;
12:	        f2 = (^a & ^b & ^c & ^d)
13:	           | (^a & ^b & ^c &  d)
14:	           | (^a &  b & ^c & ^d)
15:	           | ( a & ^b & ^c & ^d)
16:	           | ( a &  b & ^c & ^d)
17:	           | ( a &  b &  c &  d) ;
18:	    }
19:	}

<図6.4> リスト6.6の合成結果


● 条件構文のある例

次の例はSFLの条件構文がどう合成されるかを見るものです.リスト6.7の記述の意味はデータ入力端子cndが"0"ならば入力abcを,"1"ならば入力defを,データ出力端子xyzに出力するというものです.合成結果は図6.5のようになります.xyzへの値の転送元が二つあるのでセレクタが挿入され,転送条件はセレクタのセレクト入力に簡単化されて接続されます.


<リスト6.7> セレクタが挿入される例

 1:	module test5 {
 2:	    input   cnd<2>;
 3:	    input   abc<4>;
 4:	    input   def<4>;
 5:	    output  xyz<4>;
 6:	    any {
 7:	        cnd == 0b00 : xyz = abc;
 8:	        cnd == 0b01 : xyz = def;
 9:	        cnd == 0b10 : xyz = def;
10:	        cnd == 0b11 : xyz = abc;
11:	    }
12:	}


<図6.5> リスト6.7の合成結果

データ転送元のabcやdefが定数の場合はどうなるでしょうか.リスト6.8は前の例のabcをob1110にdefをob0101に変えたものですが,この合成結果は,図6.6(a)のようになります.SFLEXPは,データ出力端子に対する転送では,転送元と転送条件を別々に簡単化するため,セレクタのまわりの簡単化が不十分ですが、さらにOPT_MAP,ONSET,RINVをかけると図6.6(b)のように簡単化されます.この回路図は,次のバッチプログラム

auto test6 ps celldemo
type test6.ps > prn

により出力したものです.

<リスト6.8> 定数をセレクトする例

 1:	module test6 {
 2:	    input   cnd<2>;
 3:	    output  xyz<4>;
 4:	    any {
 5:	        cnd == 0b00 : xyz = 0b1110;
 6:	        cnd == 0b01 : xyz = 0b0101;
 7:	        cnd == 0b10 : xyz = 0b0101;
 8:	        cnd == 0b11 : xyz = 0b1110;
 9:	    }
10:	}


<図6.6(a)> リスト6.8の合成結果

<図6.6(b)> リスト6.8の合成結果 (OPT_MAP,ONSET,RINV 適用後)



● 条件と定数を合わせて圧縮させる例

sel_vまたはbus_vで定義したデータ内部端子へ排他的条件で定数を転送する場合には,SFLEXPは転送元と転送条件をまとめて簡単化します.

リスト6.9は,この条件を満たしているのでこれを合成すると図6.7(a)のように,SFLEXPの段階である程度簡単化されます.ただし,この例のように条件がすべてて尽くされていない場合は,OPT_MAP,ONSET,RINVのみで簡単化を行う(すなわち,リスト6.8のようにsel_vを使わないで記述する)方がよいでしょう.この例の場合は,cndのドント・ケアを考慮すれば,本来図6.6(b)まで簡単化できるはずですが,図6.7(b)までしか簡単化されません.

<リスト6.9> セレクタ部分の圧縮を指定する例

 1:	module test7 {
 2:	    input   cnd<4>;
 3:	    sel_v   tmp<4>;
 4:	    output  xyz<4>;
 5:	    par {
 6:	        any {
 7:	            cnd == 0x0 : tmp = 0b1110;
 8:	            cnd == 0x1 : tmp = 0b0101;
 9:	            cnd == 0x2 : tmp = 0b0101;
10:	            cnd == 0x3 : tmp = 0b1110;
11:	        }
12:	        xyz = tmp;
13:	    }
14:	}

<図6.7(a)> リスト6.9の合成結果



<図6.7(b)> リスト6.9の合成結果 (OPT_MAP,ONSET,RINV 適用後)



● オーバフローする例

次は,合成がうまくいかない例です.リスト6.10は簡単な記述なのに合成しようとするとオーバフローしてしまいます.


<リスト6.10> メモリ・オーバフローを起こす例

 1:	module test8 {
 2:	    input   abc<8>;
 3:	    input   def<8>;
 4:	    output  xyz;
 5:	    xyz = /& ^ (abc & def);
 6:	}

<図6.8> 積和形への展開

SFLEXPは論理を簡単化するために論理式をまず積和形に展開します.この論理式の場合は図6.8のように積項が28個,すなわち256個となります.人間は論理の構造を知っていますので,それがどうしたと思いますが,SFLEXPはこの論理式がまったくランダムな場合と同じ方法で簡単化を行いますのでオーバフローしてしまいます.

人間が論理の構造を知っている場合は,論理の構造に即した端子を挿入して論理を分離するとオーバフローが防げます.selかbusで定義されたデータ内部端子やサブモジュールの端子がこの目的に使えます.リスト6.11にリスト6.10の論理をデータ内部端子で分離した例を示しました.


<リスト6.11> 内部端子を使用する例

 1:	module test9 {
 2:	    input   abc<8>;
 3:	    input   def<8>;
 4:	    sel     tmp<8>;
 5:	    output  xyz;
 6:	    par {
 7:	        tmp = abc & def ;
 8:	        xyz = /& ^ tmp ;
 9:	    }
10:	}

● ステージと状態遷移を含む例

リスト6.12は初めての順序回路の例です.この例では st1 と st2 の2つの状態を持つステージ stg がデータの転送・加工を制御しています.状態は st1 から st2,st2 から st1 と交互に変化します.このステージが動作を開始するのは,制御入力端子 start が起動されて,このステージにジョブを生成したときからです.状態 st1 では,in1 の値が reg1 に書き込まれ,reg1 の内容が out へ出力されます.状態 st2 では,in2 の値が reg2 に書き込まれ,reg2 の内容が out へ出力されます.auto コマンドによる合成結果は図6.9のようになります.


<リスト6.12> ステージと状態遷移を含む例

 1:	module test10 {
 2:	    input in1,in2;
 3:	    output out;
 4:	    reg reg1,reg2;
 5:	    instrin start;
 6:	    stage_name stg {
 7:	        task tsk();
 8:	    }
 9:	    instruct start generate stg.tsk();
10:	    stage stg {
11:	        state_name st1,st2;
12:	        first_state st1;
13:	        state st1 par {
14:	            reg1 := in1;
15:	            out = reg1;
16:	            goto st2;
17:	        }
18:	        state st2 par {
19:	            reg2 := in2;
20:	            out = reg2;
21:	            goto st1;
22:	        }
23:	    }
24:	}


stg-0 はこのステージの状態を表すレジスタです.このレジスタの"0","1"で 2つの状態が区別されています.stg--all はこのステージの on,off をあらわすタスク・レジスタです.

状態レジスタ stg-0 の反転出力(nout)は入力(in)に接続され,マシン・サイクル毎に値が反転することがわかります.また,この反転はタスク・レジスタが"1"の時のみ起こることもわかります.

reg1 や reg2 への書き込みは状態レジスタとタスク・レジスタの両方に制御されています.一方,データ出力端子 out に対する値の出力は状態レジスタだけで制御され,タスク・レジスタのon,offは不要な論理として省かれています.

この例で、ステージやタスクそして状態の実体が何であるかが理解できると思います。


<図6.9> リスト6.12の合成結果



● 転送路を考慮したSFL記述

リスト6.13とリスト6.14を比べてみて下さい.どちらもデータ入力端子 in とレジスタ a,b,c,d を転送元,レジスタ a,b,c,d を転送先とするSFL記述ですが,リスト6.13では,すべての転送が直接記述してあるのに対し,リスト6.14ではいったん tmp という名前の内部データ端子に集め,そこからレジスタ a,b,c,d へ転送しています.

どの転送が同時に起こるのかを判定できれば,どのような記述に対しても転送路を最小化することができるのですが,並列動作をしている場合にはこの同時性の判定が難しいため,SFLEXPは記述通りの転送路を生成します.従ってリスト6.13では 5つの転送元から 4つの転送先への20本の転送路が生成されてしまうことになります.ぜひリスト6.14のように記述して下さい.

このようにPARTHENONでは,どのような転送路となれば良いかまで考えて記述する必要があります.

<リスト6.13> 転送路を考慮しない記述

 1:	module test11 {
 2:	    input case<4>;
 3:	    input in<1>;
 4:	    output out<1>;
 5:	    reg a<1>,b<1>,c<1>,d<1>;
 6:	    par {
 7:	        out = d;
 8:	        any {
 9:	            case == 0x0: a := in;
10:	            case == 0x1: a := b;
11:	            case == 0x2: a := c;
12:	            case == 0x3: a := d;
13:	            case == 0x4: b := a;
14:	            case == 0x5: b := in;
15:	            case == 0x6: b := c;
16:	            case == 0x7: b := d;
17:	            case == 0x8: c := a;
18:	            case == 0x9: c := b;
19:	            case == 0xa: c := in;
20:	            case == 0xb: c := d;
21:	            case == 0xc: d := a;
22:	            case == 0xd: d := b;
23:	            case == 0xe: d := c;
24:	            case == 0xf: d := in;
25:	        }
26:	    }
27:	}


<リスト6.14> 転送路を考慮した記述

 1:	module test12 {
 2:	    input case<4>;
 3:	    input in<1>;
 4:	    output out<1>;
 5:	    reg a<1>,b<1>,c<1>,d<1>;
 6:	    sel tmp<1>;
 7:	    par {
 8:	        out = d;
 9:	        any {
10:	            case == 0x0: par { tmp = in; a := tmp; }
11:	            case == 0x1: par { tmp = b;  a := tmp; }
12:	            case == 0x2: par { tmp = c;  a := tmp; }
13:	            case == 0x3: par { tmp = d;  a := tmp; }
14:	            case == 0x4: par { tmp = a;  b := tmp; }
15:	            case == 0x5: par { tmp = in; b := tmp; }
16:	            case == 0x6: par { tmp = c;  b := tmp; }
17:	            case == 0x7: par { tmp = d;  b := tmp; }
18:	            case == 0x8: par { tmp = a;  c := tmp; }
19:	            case == 0x9: par { tmp = b;  c := tmp; }
20:	            case == 0xa: par { tmp = in; c := tmp; }
21:	            case == 0xb: par { tmp = d;  c := tmp; }
22:	            case == 0xc: par { tmp = a;  d := tmp; }
23:	            case == 0xd: par { tmp = b;  d := tmp; }
24:	            case == 0xe: par { tmp = c;  d := tmp; }
25:	            case == 0xf: par { tmp = in; d := tmp; }
26:	        }
27: }
28: }