ここからは実際にCPUを作って動かしてみます。コンピュータの動きを理解するのに必ずしもそこまでする必要はないと思いますので、興味のある方のみ挑戦してみてください。引き続きコンピュータとプログラミングの関係について理解を深めたい方はこの「FPGAVerilog HDLで作るCPU」を飛ばして後のページの「自作CPUのハンドアセンブル」に進まれるのが良いでしょう。

ここからしばらくはFPGAVerilog HDLを使った回路の設計方法などについての話が続きます。CPUの中には様々なデジタル回路の要素が含まれているのでVerilog HDLを学ぶ上での入門用素材としても最適です。以降の内容はFPGAやVerilog HDLを使った設計の基礎を学びたい入門者の方にとっても参考になるように、Verilog HDLの基礎的な文法についても概説してあります。HDLを学ぶ上での足掛かりとしてもらえたら幸いです。

 

ここでは実際にCPUを動かす方法として「FPGA」というデバイスを使います。FPGAfield programmable gate array)は内部の電子回路を自由に組み替えることができる半導体デバイスです。

電子回路の記述にはVerilog HDLを使用します。Verilog HDLはハードウェア記述言語というもので、ソフトウェアを記述する一般的なプログラミング言語とは異なるものです。見た目だけを見れば両者はとてもよく似ているのですが、電子回路の構造を記述するハードウェア記述言語は、一連の処理を上から順番に記述していくソフトウェア用のプログラミング言語とは考え方や必要になる知識の点でいろいろと違いがあります。

まず、前ページで紹介した「ALU」と「M1」~「M5」までのマルチプレクサの仕様についてより具体的に定義してしまいます。

ALU内部の接続回路を切り替える4ビットのコントロール用端子について、入力信号と内部回路の対応を以下のように割り当てます。

A端子の内容を出力 :0000

ADD回路 :0001

SUB回路 :0010

AND回路 :0011

OR回路 :0100

NOT回路 :0101

SL回路 :0110

SR回路 :0111

B端子の内容を出力 :1000

以下のCPUの設計図内に書かれた「M1」~「M5」までのマルチプレクサの経路選択について、経路選択用のコントロール端子が「0」の時には上側の経路が、「1」の時には下側の経路が接続される仕様になっているものとします。

 

CPUの設計図

 

今回作ったCPUは次に示す9つのソースコードから作られています。

multiplexer_8.v :8ビットサイズのマルチプレクサ。「M1」「M4」「M5」に使われるコードです。

multiplexer_16.v :16ビットサイズのマルチプレクサ。「M2」「M3」に使われるコードです。

register_8.v :8ビットサイズのレジスタ。「WR」「IR1」「IR2」「HR」「P1」「P2」「P3」「P4」に使われるコードです。

program_counter.v :16ビットサイズのプログラムカウンタ。「PC」に使われるコードです。

alu.v :「ALU」のコードです。

スポンサーリンク

control_unit.v :「IR1」の内容に従って各部品へ制御信号を送る、コントローラのような役割を果たすコードです。

prescaler.v FPGA外部から入力されてくるクロック信号を分周する回路のコードです。クロック周波数が速すぎる場合はこの回路で調整します。

wrapper_memory.v :「RAM」はメモリ回路なのでその機能を直接Verilog HDLで記述するのではなく、FPGAが持っているメモリ機能を実現するための専用回路を使います。このメモリ専用回路はFPGAのメーカーやデバイスの価格によって使い方や容量が異なるので、汎用性を高めるために一度wrapper_memory.vで包んでから使っています。(私はAltera社のFPGAを使っています。)

cpu.v :上記の回路を配線でつないでまとめるためのコードです。CPU全体の機能がここで完成します。

 

上記のソースコードは以下のリンクからダウンロードできます。

cpu_code.zip

 

上記のソースコードについて簡単なものから順に解説していきたいと思います。

multiplexer_8.v

multiplexer_8.v はとても短いコードなのでVerilog HDLの入門に最適です。

module multiplexer_8(mux_s, mux_in_a, mux_in_b, mux_out);
	input 	 	mux_s;
	input	[7:0] mux_in_a;
	input	[7:0] mux_in_b;
	output	[7:0] mux_out;
	
	assign mux_out = (mux_s == 1'b0) ? mux_in_a : mux_in_b;
	
endmodule

 

上記のコードがmultiplexer_8.v の全コードです。Verilog HDLでは1つの機能を持った回路のかたまりを「モジュール」という単位で記述します。1行目の”module”から始まる文がモジュール文です。モジュール文では”module”と記述した後にモジュールにつける名前を記述します。ここではmultiplexer_8がそれに当たります。(module文とモジュール名の間にはスペースを入れます。)モジュール名の後にはそのモジュールで使用する入力端子と出力端子の名前を括弧で囲って定義します。(端子名の間は「,」で区切ります。)そして文の最後には「;」をつけるようにしてください。

module文の後にはそのモジュールが持つ機能を記述し、最後にそのモジュールの終わりを示す”endmodule”を記述します。”module”から”endmodule”までが1つのモジュールという単位の回路になります。(”endmodule”の最後に「;」は必要ありません。)

module文の中ではまず最初に入力端子と出力端子の定義を行います。入力端子を定義するときはinput文を、出力端子を定義するときはoutput文を使います。端子名はmodule文で既に定義しているのですが、input文とoutput文ではmodule文で定義した端子名が入力端子になるのか出力端子になるのかを定義します。その際、入出力端子が複数のビットを持つ場合は[最上位ビット:最下位ビット]の形で端子名の前にビットサイズを定義します。ちなみに最下位ビットは0番から割り当てられますので、最上位ビットは(ビットサイズ1)となります。例えば8ビットサイズの端子を定義する場合は [7:0] となります。input文、output文ともに最後は「;」で終了してください。

7行目に出てくるassign文は特定の端子の間にAND回路、OR回路、NOT回路などを配置した状態を記述するための文です。例えばaという出力端子にbcという入力端子を持つAND回路の出力を結線する場合、assign文の記述はassign a = b & c;となります。同様にbcORaに結線する場合はassign a = b | c;となります。さらにbNOTaに結線する場合はassign a = ~b;です。(assign文の最後は「;」で終了してください。)

assign文は「=」を用いた数学の式のような形で回路の構造を定義することができる文です。もちろん複数の回路が結線された状態を記述することもできます。例えばbcANDの入力とし、そのANDの出力と別の端子dORaに結線する場合、assign文の1行で表すとassign a = (b & c) | d;となります。

また同じassign a = (b & c) | d;という回路を複数の文で表すこともできます。まずwire文という文を使って結線に使うための線を定義します。wire文はinput文やoutput文と同じような書き方で導線を名前をつけて定義します。(inputoutput文と同じ書き方で複数ビットの定義も可能です。)w1という名前の線を定義する場合はwire w1;という記述になります。まずw1AND回路の出力につなげるために次のようなassign文を書きます。assign w1 = b & c;

続けてw1dORaに結線するとしてassign a = w1 | d;と記述すると、全体として最初に記述したassign a = (b & c) | d;と同じ回路ができあがります。

このようにassign文とwire文でAND回路、OR回路、NOT回路を結線していくだけでもいろいろな回路を作ることができます。しかし、すべてをAND回路、OR回路、NOT回路の組み合わせ回路として記述していくのはとても効率が悪いので、Verilog HDLには効率よく回路を記述するための文法が他にもたくさん用意されています。そのうちの1つがmultiplexer_8.vの7行目にあるマルチプレクサ回路の記述です。

7行目の文はassign文の中に括弧で囲まれた条件式と「?」があり、直後に「:」で区切られた二つの端子名が記述されています。これは「?」の前に置かれた括弧の中の条件式が成り立つ時は「:」で区切られた前の端子が選択され、逆に括弧の中の条件式が成り立たない時には「:」で区切られた後ろの端子が選択されるという、マルチプレクサ回路のような機能を実現するための記述法です。また1’b0というのは「1ビットのサイズを持った2進数表現での0」という数値を表しています。例えば同じ記述法で「3ビットのサイズを持った2進数表現での5」という数値を表現する場合は3’b101となります。

ですので7行目の意味は”mux_s”という入力端子が0である場合には”mux_in_a”の入力信号が”mux_out”へ接続され、逆にmux_s”という入力端子が1である場合には”mux_in_b”の入力信号が”mux_out”へ接続されるという意味になります。

なお、今回作るCPUではマルチプレクサ回路の経路選択について、経路選択用のコントロール端子(mux_s)が「0」の時には上側の経路が、「1」の時には下側の経路が接続される仕様になっているものとしますので、設計図上のマルチプレクサ回路の上側の経路とは”mux_in_a”を表し、下側の経路とは”mux_in_b”を表すものとします。

 

<戻る       次へ>

 

スポンサーリンク