いよいよ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: }