1
前回教育用の RISC POCO を導入しました 今日はその Verilog 記述を紹介します まず この復習をやっておきましょう 2
最も重要な点は メモリの読み書きで レジスタ間接指定の理解です これはポインタと一緒なので 間違えないように修得してください 3
RISC なので 基本の演算はレジスタ同士でしかできません MV はレジスタ間のデータ移動なので気をつけてください 4
イミーディエイト命令は 命令コード中の数字がそのまま演算に使われます 符号付の命令は直値が符号拡張されます LDI,ADDI がこれに当たります 符号無し命令は頭に 0 を補うのもので LDIU ADDIU がこれに当たります 5
このようなプログラムでは いちいち数字をレジスタに入れるのが面倒ですが ステップ数が多くなるのを厭わなければ問題なくできます 6
命令のフィールドを決める方法としては I 形式と R 形式があります I 形式は 8 ビットの直値を伴うもので R 形式は opcode は全て 0 で funct 形式を利用して命令を識別します 7
POCO の構造はこのような図になります これを今回は Verilog で記述していきます 8
ではまずレジスタファイルの記述から始めましょう A,B ポートは読み出し専門 C ポートは書き込み用です レジスタファイルも メモリ同様 出来合いの回路である IP(Intellectural Property) で実装する場合がありますが ここではレジスタ数が少ないので 合成の対象としたいと思います ここで使うレジスタファイルは 読み出し専用の A ポート B ポートと書き込み専用の C ポートを持つ 2R1W の 3 ポートメモリです 読み出しはアドレスに対応したデータが A,B ポートから直接表れます 書き込みについては rwe=1 の時に C ポートに置いた入力データをクロックの立ち上がりに同期してアドレス C に対して書き込みます 9
レジスタファイルの Verilog 記述を示します ここでは 8 つのレジスタを別々に宣言しています これはメモリの形で宣言しても良いのですが これをやると gtkwave で観測できなくなります ( メモリの中身は普通 vcd ファイルに保存しません これをやるとファイル量が巨大化するためです ) 別々に宣言しておけば 見ることができて便利です さて 読み出しについては条件選択文を使っています アドレスに対応したレジスタをそれぞれのポートに出力します 10
次に書き込みです ここでは レジスタへの書き込みなので always 文を使います リセットはしたいところですが レジスタは一種のメモリなので ここはしないことにします 書き込みイネーブル we=1 の時書き込みます ここで どのレジスタに書き込むのかを選ぶのに case 文を使っています case 文は C 言語の switch 文と似ていて ( ) 内の値に応じた処理を行います どれも成り立たない場合は default 以下の文が実行されます 条件選択文と違って default は省略可能ですが きちんと記述することをお勧めします 11
Case 文の文法をまとめておきます それぞれの文は begin.. end の書き方を使って複数の文を書くことができます case 文の終わりは endcase です 12
case 文は大変便利な書き方なので 色々な場所で使いたいのですが if 文と同じく always 文の中だけに使うことができます すなわち この文の左辺は レジスタのみで 文中にはレジスタへの値の書き込みだけです なぜこのような制約があるのでしょうか?if 文同様 レジスタは値が記憶できるので 全ての条件が尽くされなくても 中身が確定するためです 繰り返しますが レジスタへの値の書き込みは 通常のプログラム言語における変数への代入と大変似ているのです 13
ちなみに レジスタはメモリ構文を使ってもかけます この場合 r[0],r[1] という書き方をします 実はこの記法の方が普通なのですが gtkwave で中身が見れないと困るので ここでは敢えてレジスタとして分けて宣言しています 14
では POCO の Verilog 記述を見て行きましょう アキュムレータマシン同様 命令メモリとデータメモリは CPU の範囲に入れません 入力はおなじみの clk, rst_n 命令メモリからの入力は idatain 命令メモリのアドレスは iaddr としています データメモリからの入力は ddatain, 書き込み用の出力は ddataout アドレスは daddr です 書き込みイネーブル信号は we でこれが 1 の時の clk の立ち上がりで ddataout に出力した値がメモリに書き込まれます アキュムレータマシン同様 pc をレジスタで宣言します 次にデータパスの中間信号を定義します これは図の青字と対応させてください やはりアキュムレータマシン同様 デコード信号を定義します これは対応する命令がフェッチされたときに 1 になるようにします 15
POCO の構成を図に示します 信号線の名前を Verilog 記述と対応してください 16
まず ddataout には ST 命令で値を書き込むポートなので レジスタファイルの A ポート rf_a をそのまま繋ぎます 一方 アドレスは レジスタファイルの B ポートからの出力 rf_b をつなぎます これでレジスタ間接指定ができます 命令メモリのアドレスには pc をそのままつなぎます 読んで来た命令 idatain は opcode,rd,rs,func の並びに分解されます 一方 下位 8 ビットはイミーディエイトが入る場所なんで imm として 8 ビットを切り出します 次に st_op から addiu_op まで それぞれの命令に対応した信号線が H レベルになります ここで R 型の場合 func で命令を識別しているのが分かります ちなみに alu_op は ALU 命令全体をカバーします これは func の上位 2 ビットが 00 であることにより識別します 17
定義が並んでいる def.h の内容を示しましょう I 型命令は opcode R 型命令は func の 4 ビットと比較します R 型命令の opcode は 0000 です 18
ではデータパスの記述を確認します ここで alu_b は ALU の B 入力に入れるためのデータを選択します ADDI と LDI では符号拡張 ADDIU と LDIU ではゼロ拡張した結果を使います これらの命令以外ならば レジスタ間演算命令なので レジスタファイルの B ポートからの出力を使います 次に ALU の com は ADDI と ADDIU の時は加算命令 (110) LDI と LDIU では B 入力のスルー (001) が入るようにします それ以外の場合は func の下位 3 ビットが入り それぞれの演算を行います rf_c はレジスタファイルの入力データポートで LD 命令の時はメモリからのデータ入力 ddatain が入り その他の場合は ALU の出力が入ります rwe はレジスタへの書き込み信号なので レジスタに書き込む命令の OR をとります 19
符号拡張とゼロ拡張の書き方を復習しましょう 符号ビットを 8 個並べて元の数とくっつけます 20
では POCO の構成と それぞれの記述の対応を考えましょう 青い四角 赤い四角 緑の四角がそれぞれの条件選択文と対応しています 2 ページ前のスライドと対応を考えてください 21
ALU レジスタファイルを実体化している部分は 今まで定義した信号名を使います always 文中では PC のカウントアップをしています これは アキュムレータマシンと同じです 22
赤い四角と緑の四角を見てください これが前のページの記述と対応します 23
では どのように各部が動くか 見てみましょう LDI r1,#0 では 命令の下位 8 ビットを符号拡張します このため alu_b は 01 にします com は comsel を 01 にして THB を入れてやります この 0 を rf_csel を 0 にしてレジスタファイルの C 入力に入れます rwe=1 にして書き込みます 24
LD 命令の場合は rf_b がそのままデータメモリのアドレスに入るので ddatain から読み出されたデータが入ってくるので rf_csel を 1 にしてメモリからのデータをレジスタファイルの C 入力にいれてやります rwe=1 にして書き込みます 25
ST 命令は rf_a を ddataout に出してやり we=1 にしてデータメモリに書き込みます daddr には rf_b が入っており これは LD 命令と同じです 今度はレジスタファイルに書き込まないので rwe=0 にします 26
レジスタ同士の演算はどうなるでしょう? この場合 func コードの下 3 ビットを ALU の S に入れてやります このために comsel=00 にします alu_bsel には 00 を入れてレジスタファイルの B ポートの出力を通してやります 計算結果をレジスタファイルに書き込むため rf_csel=0 として rwe=1 とします 27
R 型の命令をまとめます opcode は 00000 で func で識別する点に注意ください func の上位 2 ビットが 00 の場合 下の 3 ビットを ALU のコマンドに入れます 28
I 型の命令の一覧です opcode で命令を識別します 29
インフォ丸が教えてくれる今日のまとめです 30
では演習をやってみましょう LDHI を実装します この命令は上位 8 ビットにイミーディエイトの数字を入れ 下位 8 ビットを 0 にします LDI 命令では下の 8 ビットしか入らないので LDHI は上位 8 ビットに値を入れる際に便利です 31
今 opcode を 01010 にします もちろんこの命令は I 型です この命令はセコイ感じもしますが 便利なので 全ての RISC が持っています 32