いよいよKUE-CHIP2全体を,SFLのモジュールkuechip2として記述します. リスト4.1 に一例を示します.
モジュールkuechip2では,これまでに設計したモジュールkueshiftと kuealuをサブモジュールとして用います.それらのSFL記述は,必ずしもここ にインクルードする必要はないのですが,インクルードすると論理合成の際に 便利です.%iに続けて"と"でファイル名を囲むことで,カレントディレクトリ から見たパス名のファイルをインクルードします.
図1.1 のブロック図には,INCという8ビットのインクリメンタがあります.8ビット のインクリメンタはまだ設計していないのですが,PARTHENON標準ライブラリ の中にinc8という8ビットのインクリメンタが存在しますので,これを用いる ことにします.PARTHENON標準ライブラリは,$PARTHENON/sfl_lib.dirという ディレクトリに,多くのファイルとして格納されていますが,%iに続けて< と>でファイル名を囲むことで,このディレクトリにあるファイルをインク ルードします.inc8.hというファイルの中身は, リスト4.2 のようになっています.inc8は,機能回路として定義されています.機能回路 はモジュールのようなものですが,論理合成されない,使える演算子と構成要 素が拡張されているなどという点でモジュールと異なっています.機能回路の 記述は,circuit_typeあるいはcircuit_classで始めます.inc8などの PARTHENON標準ライブラリは,すでに論理合成されてそのネットリストが用意 されていますので,機能回路で定義されているわけです.
SFLでは,%dを用いてマクロ定義を行います.たとえば,NOPという文字列 は,(ir<7:3> == 0b00000)という文字列に置き換えられます.ここでは マクロ定義を用いて, 表1.1 に従って命令の分類を行っています.
モジュールkuechip2では,これまでに設計したモジュールkueshift, kuealuを,それぞれサブモジュールshifter, aluとして用います.そのために は,サブモジュールタイプの定義が必要ですので,ここで行っています. kueshiftの制御入力端子doの仮引数と,kuealuの制御入力端子doの仮引数は, ともに,すべてのデータ入力端子とします.
ここからは,モジュールkuechip2の定義部の記述となります.まずは,外 部端子を記述します.KUE-CHIP2の外部端子はすでに規定されているので,こ こにはそれらを書き下せばよいのですが,どれを制御端子にしてどれをデータ 端子にするかということを決める必要があります.
まず入力端子に関してですが,メモリやIBUFの値を読み込むためのdbi, IBUF_FLAG, OBUF_FLAGを読み込むためのibuf_flg_in, obuf_flg_inはすべてデー タ入力端子とします.出力端子に関しては,メモリの値の読み出しを依頼する mem_re,メモリへ値の書き込みを依頼するmem_we,IBUFの値の読み出しを依頼 するibuf_re,IBUF_FLAGを0にするibuf_flg_clr,OBUFへ値の書き込みを依頼 するobuf_weは制御出力端子にします.そして,メモリやOBUFに値を書き込む ためのdbo<8>,メモリのアドレスを指定するためのab<9>はデー タ出力端子とします.
その他に,デザインコンテストでは規定されていませんが,kuechip2を起 動するための制御入力端子startを持つものとします.
次はkuechip2の構成要素を記述します.まずレジスタに関しては, 図1.1 に示したように,acc, ix, cf, vf, nf, zf, pc, ir, marを用意します. KUE-CHIP2のレジスタはリセット信号でリセットされるので,レジスタとして はreg_wr (register with reset)を用いることにします.そのほかに,図1.1 に示されているものとしては,モジュールkueshift, kuealuを,それぞれサブ モジュールshifter, aluとして用い,PARTHENON標準ライブラリのinc8を,サ ブモジュールincとして用います.また,データ内部端子として,sel_bを用意 します.
そのほかに,図1.1にはありませんが,SFL記述を簡潔にするためのいくつ かの構成要素を定義します.bcond_calc, bcond_statusは,BRANCH命令を処理 するために設けた制御内部端子です.exec_aluは,aluを用いて算術論理演算 命令を処理するための制御内部端子です.制御内部端子は,制御入力端子や制 御出力端子と同様に,起動されなければその値は0で,起動されるとその値が1 になります.また,動作を関連づけることもできます.これらの働きについて はもう少し後で説明します.
制御端子のうち,制御出力端子と制御内部端子の仮引数の定義は,モジュー ルの定義部で行います.制御出力端子と制御内部端子を起動するのは,これら を持つモジュールだからです.
mem_reはabを仮引数として持つものとします.メモリの内容を読みだすた めには,アドレスを与えなければならないからです.mem_weについては,アド レスだけではなく,メモリに書き込むべきデータの値も与えなければなりませ んので,abとdboを仮引数とします.obuf_weについては,書き込むべきデータ の値だけを与えれば良いので,dboを仮引数とします.
exec_aluはsel_bを仮引数として持つものとします. 図1.1 に示すように,sel_bはaluの入力の1つとなるからです.
SFLには,ステージという概念があります.制御端子は1クロックで終了す る動作を行うのに対し,ステージは複数クロックにまたがる一連の動作を行い ます.すなわち,ステージは状態を持ちますが,制御端子は状態を持ちません.
ステージを複数個用意すると,並列動作をうまく記述することができます. しかし,ここではステージを一つだけ用意し,その唯一のステージの名前を allとします.ステージallは, 表1.1 に示されたフェーズ表に従って,各命令の動作を制御するものとします.
ステージにはタスクというものが存在し,これが起動されることでステー ジが動作すると考えられています.ステージallにもタスクが必要です.tとい う名前のタスクを用意します.
次に,制御端子による動作を記述します.制御端子のうち,制御入力端子 と制御内部端子による動作の定義は,これらを持つモジュールの定義部で行わ れます.一方,制御出力端子による動作の定義は,その制御出力端子を持つモ ジュールの外,つまりそのモジュールをサブモジュールとして使用するモジュー ルで行われます.従って,ここでは行われません.
制御入力端子startが起動されると,ステージallのタスクtが起動され, KUE-CHIP2が動き出します.
制御内部端子bcond_calcは,命令コードとフラグの値に従って,BRANCH命 令の分岐条件を判定します.もし条件が成り立てば,制御内部端子 bcond_statusを起動して1にします.条件が成り立たなければ,bcond_status は0のままです.
制御内部端子exec_aluは,ALUを用いて算術論理演算命令を行い,フラグを 更新します.算術論理演算命令の第1オペランドは,ir<3>が1か0かで, IXかACCとなります.命令の第2オペランドは,exec_aluの仮引数sel_bに与え られた値となります.演算結果は,ir<3>が1か0かで,IXかACCに格納さ れます(比較命令CMP以外).
最後に,ステージallの動作を記述します.SFLのステージは,generateと いうキーワードで起動され,起動された次のクロックから動作を開始し, finishというキーワードで動作を終了します.したがって,allは,制御入力 端子startによって動作を開始し,HLT命令により動作を終了します.
ステージallは3つの状態を持つものとし, 表1.1 に示した3つのフェーズp1, p2, p3をそれぞれの状態に対応させます.状態p1 では,命令の種類によらず動作は一定ですが,状態p2, p3での動作は命令の種 類によって様々なものとなります.状態を持つステージでは,どれかの1つの 状態のみが有効になっています.ステージが動作中であっても,有効でない状 態の動作は行われません.初めに有効な状態(初期状態)は,キーワード first_stateで指定します.有効な状態の変更(状態遷移)は,キーワード gotoによって行います.
表1.1とこのSFL記述を見比べることで,SFL記述の意味がわかりやすくなる と思います.
制御端子による動作とステージの動作の記述には,制御端子の起動がたく さん現れています.制御端子は,その後に()を付けることで起動します.制御 端子は,制御入力端子,制御出力端子,制御内部端子に分けられますが,ここ では,それぞれの起動について説明します.
まず,制御入力端子ですが,あるモジュールから起動するのは,そこで使 用しているサブモジュールの制御入力端子であることに注意してください.例 えば140行目では,サブモジュールshifterの制御入力端子doを, ir<2:0>, ix, cfを引数として起動しています.これにより,kueshift のdoの仮引数の定義(28行目)に従って,shifterのデータ入力端子mode, in, ciに,それぞれ,ir<2:0>, ix, cfの値が転送されます.また,shifter のdoが1になり,それに対応づけられた動作が行われます.この動作は,モジュー ルkueshiftの定義部に記述されています.このように,制御入力端子を用いて, あるモジュールからそこで使用しているサブモジュールに仕事を依頼します.
次に,制御出力端子について見てみます.例えば106行目では,制御出力端 子mem_reを,0b0 || pcを引数として起動しています.これにより,61行目の 仮引数の定義に従って,データ出力端子abに0b0 || pcの値が転送されます. また,mem_reが1になり,それに対応づけられた動作が行われます.この動作 とは,メモリのab番地の内容をdbiに転送することですが,これはモジュール kuechip2の外側で規定されます.このように,制御出力端子を用いて,あるモ ジュールから外側のモジュールに仕事を依頼します.
最後に,制御内部端子について見てみます.例えば156行目では,制御内部 端子exec_aluを,ixを引数として起動しています.これにより,64行目の仮引 数の定義に従って,データ内部端子sel_bにixの値が転送されます.また, exec_aluが1になり,それに対応づけられた動作が行われます.この動作は, 89行目から99行目に書かれています.制御内部端子は,制御入力端子や制御出 力端子のように別のモジュールに仕事を依頼するものではなく,一連のまとまっ た動作を記述するためのものです.従って,必ずしもこのように記述する必要 はないのですが,制御入力端子を利用することで,SFL記述を簡潔にすること ができます.
以上で,リスト4.1の説明は終わりです.リスト4.1のSFL記述は, kuechip2.sflというファイルに格納するものとします.
[リスト 4.1] SFL記述 (kuechip2.sfl) [TOP]1: /** kuechip2 : The main module for KUE-CHIP2 **/ 2: 3: %i "kueshift.sfl" 4: %i "kuealu.sfl" 5: %i <inc8.h> 6: 7: %d NOP (ir<7:3> == 0b00000) 8: %d HLT ((ir<7:3> == 0b00001) | (ir<7:4> == 0b0101)) 9: %d OUT (ir<7:3> == 0b00010) 10: %d IN (ir<7:3> == 0b00011) 11: %d SRCF (ir<7:4> == 0b0010) 12: %d BRANCH (ir<7:4> == 0b0011) 13: %d SHIFT (ir<7:4> == 0b0100) 14: %d LD_REG ((ir<7:4> == 0b0110) & (ir<2:1> == 0b00)) 15: %d AL_REG ((ir<7> == 0b1) & (ir<2:1> == 0b00)) 16: %d LD_IMM ((ir<7:4> == 0b0110) & (ir<2:1> == 0b01)) 17: %d AL_IMM ((ir<7> == 0b1) & (ir<2:1> == 0b01)) 18: %d LD_MA ((ir<7:4> == 0b0110) & (ir<2> == 0b1)) 19: %d ST_MA ((ir<7:4> == 0b0111) & (ir<2> == 0b1)) 20: %d AL_MA ((ir<7> == 0b1) & (ir<2> == 0b1)) 21: 22: submod_type kueshift { 23: /** external pins **/ 24: instrin do; 25: input mode<3>, in<8>, ci; 26: output out<8>, co, vo, no, zo; 27: /** arguments of instrin **/ 28: instr_arg do(mode, in, ci); 29: } 30: 31: submod_type kuealu { 32: /** external pins **/ 33: instrin do; 34: input mode<3>, a<8>, b<8>, ci; 35: output out<8>, co, vo, no, zo; 36: /** arguments of instrin **/ 37: instr_arg do(mode, a, b, ci); 38: } 39: 40: module kuechip2 { 41: /** external pins **/ 42: instrin start; 43: input dbi<8>; 44: input ibuf_flg_in, obuf_flg_in; 45: instrout mem_we, mem_re; 46: instrout ibuf_re, ibuf_flg_clr, obuf_we; 47: output dbo<8>, ab<9>; 48: 49: /** elements **/ 50: reg_wr acc<8>, ix<8>; 51: reg_wr cf, vf, nf, zf; 52: reg_wr pc<8>, ir<8>, mar<8>; 53: inc8 inc; 54: kuealu alu; 55: kueshift shifter; 56: sel sel_b<8>; 57: instrself bcond_calc, bcond_status; 58: instrself exec_alu; 59: 60: /** arguments of instrout and instrself **/ 61: instr_arg mem_re(ab); 62: instr_arg mem_we(ab, dbo); 63: instr_arg obuf_we(dbo); 64: instr_arg exec_alu(sel_b); 65: 66: /** stages and tasks and their arguments **/ 67: stage_name all { task t(); } 68: 69: /** operations of instrin and instrself **/ 70: instruct start generate all.t(); 71: instruct bcond_calc if ( 72: ( ir<3:0> == 0b0000 ) | 73: ( ( ir<3:0> == 0b1000 ) & vf ) | 74: ( ( ir<3:0> == 0b0001 ) & ^zf ) | 75: ( ( ir<3:0> == 0b1001 ) & zf ) | 76: ( ( ir<3:0> == 0b0010 ) & ^nf ) | 77: ( ( ir<3:0> == 0b1010 ) & nf ) | 78: ( ( ir<3:0> == 0b0011 ) & ^(nf | zf) ) | 79: ( ( ir<3:0> == 0b1011 ) & nf | zf ) | 80: ( ( ir<3:0> == 0b0100 ) & ^ibuf_flg_in ) | 81: ( ( ir<3:0> == 0b1100 ) & obuf_flg_in ) | 82: ( ( ir<3:0> == 0b0101 ) & ^cf ) | 83: ( ( ir<3:0> == 0b1101 ) & cf ) | 84: ( ( ir<3:0> == 0b0110 ) & ^(vf @ nf) ) | 85: ( ( ir<3:0> == 0b1110 ) & vf @ nf ) | 86: ( ( ir<3:0> == 0b0111 ) & ^((vf @ nf) | zf) ) | 87: ( ( ir<3:0> == 0b1111 ) & (vf @ nf) | zf ) 88: ) bcond_status(); 89: instruct exec_alu par { 90: any { 91: ir<3> : alu.do(ir<6:4>, ix, sel_b, cf); 92: else : alu.do(ir<6:4>, acc, sel_b, cf); 93: } 94: if ( ^(ir<6:4> == 0b111) ) any { /* except CMP */ 95: ir<3> : ix := alu.out; 96: else : acc := alu.out; 97: } 98: cf := alu.co; vf := alu.vo; nf := alu.no; zf := alu.zo; 99: } 100: 101: /** operations of stages **/ 102: stage all { 103: state_name p1, p2, p3; 104: first_state p1; 105: state p1 par { 106: ir := mem_re(0b0 || pc).dbi; 107: pc := inc.do(pc).out; 108: goto p2; 109: } 110: state p2 any { 111: NOP : goto p1; 112: HLT : par { 113: goto p1; 114: finish; 115: } 116: OUT : par { 117: obuf_we(acc); 118: goto p1; 119: } 120: IN : par { 121: acc := ibuf_re().dbi; 122: goto p3; 123: } 124: SRCF : par { 125: cf := ir<3>; 126: goto p1; 127: } 128: BRANCH : par { 129: bcond_calc(); 130: mem_re(0b0 || pc); 131: inc.do(pc); 132: any { 133: bcond_status : pc := dbi; 134: else : pc := inc.out; 135: } 136: goto p1; 137: } 138: SHIFT : par { 139: any { 140: ir<3> : ix := shifter.do(ir<2:0>, ix, cf).out; 141: else : acc := shifter.do(ir<2:0>, acc, cf).out; 142: } 143: cf := shifter.co; vf := shifter.vo; 144: nf := shifter.no; zf := shifter.zo; 145: goto p1; 146: } 147: LD_REG : par { 148: any { 149: ir<0> & ^ir<3> : acc := ix; 150: ^ir<0> & ir<3> : ix := acc; 151: } 152: goto p1; 153: } 154: AL_REG : par { 155: any { 156: ir<0> : exec_alu(ix); 157: else : exec_alu(acc); 158: } 159: goto p1; 160: } 161: LD_IMM : par { 162: any { 163: ir<3> : ix := mem_re(0b0 || pc).dbi; 164: else : acc := mem_re(0b0 || pc).dbi; 165: } 166: pc := inc.do(pc).out; 167: goto p1; 168: } 169: AL_IMM : par { 170: exec_alu(mem_re(0b0 || pc).dbi); 171: pc := inc.do(pc).out; 172: goto p1; 173: } 174: LD_MA | ST_MA | AL_MA : par { 175: mem_re(0b0 || pc); 176: any { 177: ir<1> : mar := alu.do(0b011, ix, dbi, 0b0).out; 178: else : mar := dbi; 179: } 180: pc := inc.do(pc).out; 181: goto p3; 182: } 183: } 184: state p3 par { 185: any { 186: IN : ibuf_flg_clr(); 187: LD_MA : any { 188: ir<3> : ix := mem_re(ir<0> || mar).dbi; 189: else : acc := mem_re(ir<0> || mar).dbi; 190: } 191: ST_MA : any { 192: ir<3> : mem_we(ir<0> || mar, ix); 193: else : mem_we(ir<0> || mar, acc); 194: } 195: AL_MA : exec_alu(mem_re(ir<0> || mar).dbi); 196: } 197: goto p1; 198: } 199: } 200: }
1: /****************************************** 2: * (C)Copyright by N.T.T 1993(unpublished) * 3: * All rights are reserved. by K.Oguri * 4: ******************************************/ 5: circuit_class inc8 { 6: input in<8> ; 7: output out<8> ; 8: instrin do ; 9: instr_arg do(in) ; 10: instruct do out = in + 0b1 ; 11: }