Open Design Computer Project

オリジナルCPUから作る本格的自作コンピューター

ユーザ用ツール

サイト用ツール


hardware:processor_development_tips

プロセッサ設計のノウハウ

オリジナルプロセッサ・自作CPUを作ってみたい。そう思ってる方に自分が今までに作ってきて学んだノウハウを少しばかり公開します。

命令アーキテクチャの設計

アウトオブオーダ実行についてプロセッサの設計

アウトオブオーダ実行はプロセッサに入ってきた命令の順序を動的に変え実行を行う仕組みです。基本的な原理は、命令オペランドの依存関係を調べ、実行中の命令が参照したり、結果をライトバックするレジスタと干渉していない場合現在実行している命令を追い越して実行を行う仕組みです。

アウトオブオーダ実行の特徴としては、命令セットの変更なしに命令レベル並列度を上げることができます。そして、たとえばDIV命令やLoad/Store等のレイテンシが長い命令にのみ限定したりすればさほどハードウェア規模は大きくなりません。

レジスタリネーミング付きアウトオブオーダ実行プロセッサの設計

レジスタリネーミングしないアウトオブオーダ実行は、さほど大きなハードウェアを必要としませんが、レジスタの依存性がつくため、並列度もそれほど高くなりません。レジスタリネーミング+アウトオブオーダ実行はとても多くのハードウェアリソースを必要としますが、ノイマンアーキテクチャでできる限りのアウトオブオーダ実行が可能になります。

構成としては、論理レジスタ(プログラムで参照されるレベルのレジスタファイル)をタグにして、物理レジスタファイル(実際にプロセッサが持つレジスタファイルで論理レジスタよりも少なくとも1本以上多い)からなります。論理レジスタをキーとして、開いている物理レジスタを有り当てます。オペランドで参照する命令が後から入ってきた時には、その物理レジスタのタグを渡すことで、プログラムから触れるレジスタファイルに時間軸を付けれるような感じになり、演算に必要となるデータが確定した時に演算を先に実行することができます。

古典的な方式は上記に上げるものですが、実際には例外に対応しなくてはなりません。ここでいう例外というのは割り込み等ももちろん含みますがジャンプ命令もコアに対しての例外と考えます。アウトオブオーダ実行なので当然、追い越し実行が行われていますが、例外が発生するとその追い越し実行したものをなかったことにしなくてはなりません。具体的にはレジスタを巻き戻す必要があります。ロールバック機構というものを考える必要がありますが、それは最後にコミットした時点のレジスタファイルへのポインタを持ったリオーダーバッファを用いる方法と、どのような時点でもコミットされたレジスタファイルを持つ方法があります。

後者は機構が少し楽になりますが物理レジスタファイルとは別にコミットしたレジスタファイルを保つ必要があり、無駄なハードウェアが増えてしまいます。リオーダーバッファを用いる方法は複雑になりますが、すべての物理レジスタファイルを有効活用できるという利点があります。

このレジスタファイル付きアウトオブオーダー実行コアをFPGA上に実装する場合、パイプラインの設計を注意しなくてはなりません。レジスタリネーミングとリザベーションステーションはとても規模が大きくなります。また、規模の小さいFPGA上にフッティングするのは難しいと言えます。と言うよりやめたほうがいいです。FPGAろターゲットにするには、リネーミングなしのアウトオブオーダが限界かと思います。MIST1032SAはコアだけで5万LE以上を消費しています。

FPGAとVerilog HDLについて

ここでは私が使用しているAltera社のFPGAについて説明します。Xilinxやその他ベンダでもほぼ同様のことが言えるかと思います。

FPGAのLEリソースを全て使い切るような設計をしない

FPGA搭載されているロジックエレメント(LE)は、全て使用しようとすると論理合成結果の遅延が増えて動作周波数が下がってしまったり、もし動作周波数を気にしないのであっても論理合成ツールが残り少ないリソースをどうやりくりしてフッティングさせるかを検証するのに膨大な時間とパソコンのメモリリソースが必要です。よって、デザインを変更するかより高集積度なFPGAへの変更を検討するべきです。

論理合成するファイルでfor()の高度な使い方をしない

テストベンチファイルなら問題ないのですが、論理合成するRTLファイルでfor()を使用する際は、基本的な繰り返し以外は控えるべきです。例えばメモリ宣言した全てデータを0クリアするとか、そういう使い方は良いですが、for()の中にif文で条件分岐させたりという使い方をすると、RTLシミュレーションは通っても、論理合成ツールで通らないことがあるため、書くべきではありません。

wire reg宣言は必ず使用する前に宣言する

wire や reg宣言をするよりも前に、その信号に対してアサインしたり、参照することはVerilog構文として違反していません。具体的には以下の様な例です。

wire data = b_data;
reg b_data;

しかしこの書き方だと、シミュレータや論理合成ツールによってはエラーになります。どのようなツールでも論理合成できるよう先に代入する方を宣言するべきです。

Veritak問題なし
Veritak + LintWarning
Icalrus VerilogWarning
Model Sim
Riviera-PRO
ALINT
VCSError
Altera Quartus II問題なし
Xilinx ISEError

always@文で組合せ回路を書くときはalways @* で書く

これはVerilog 2001から追加されたものなので、Verilog-1995などではサポートされていません。

組み合わせ回路を書く場合、少し大規模になるとfunctionかalways@で記述するかと思います。どちらを選択するかは自由だと思いますが、私はどちらかと言えばalways@派です。always@で組合せ回路を書く場合、Verilog-1995などでは入力をすべてセンシティビティリストに書く必要がありますが、Verilog 2001ではalways@*というのが追加されており、組み合わせ回路として書く場合センシティビティリストは不要です。

インスタンスは名前参照にする

名前参照ではなく宣言順に並べることが可能ですが、これは後でコードを見直すとき見にくかったり、そもそも危険な書き方なので、特別な理由がなければ名前参照にするべきです。これはモジュールを呼び出すインスタンスだけではなく、parameterにおいても同様です。

ifのネストを深くしない

if文を深くするとプライオリティロジックが増えます。可能であれば1つのインデントでまとめて書き、ifのプライオリティを使用した書き方を行うべきです。これによってゲート数の削減に繋がります。また、プライオリティを全く必要としない場合はcase文を用いるべきです。

モジュールの出力はレジスタ出力にする

特に、FPGAのIOに対してアサインするようなシチュエーションでは特に気をつけることが必要です。FPGAの構造上IO端子はD-FFで、そのまま出力されますが、レジスタ出力ではなく組み合わせ回路出力になると、別のLUTを経由してアサインされるため、思わぬ遅延を招く可能性があり、タイミングがずれてしまうことがあります。直接IO出力を行わないモジュールでも、設計の一貫性を保つために出来るだけレジスタ出力にするのが好ましいです。

ステートマシンはムーア型

ムーア型は内部ステートの状態のみを出力するステートマシンです。それに対して、ミーリ型は内部ステートの他に、入力を組み合わせて出力するステートマシンです。ミーリ型は回路規模によって思わぬメタステーブルを引き起こす可能性があるため、できるだけ避けるべきです。

hardware/processor_development_tips.txt · 最終更新: 2016/03/06 17:01 by takahiro