オール・トランジスタ4ビットCPUの製作とFPGA開発
[Vol.2 CPUのレジスタとI/Oの設計]

ALU,レジスタ,I/Oなどをトランジスタ・レベルで手作りし,さらにFPGAにも実装


【Index】

Vol.1 ノイマン型CPUの設計

Vol.2 CPUのレジスタとI/Oの設計

Vol.3 Lチカで学ぶFPGA開発体験

Vol.4 CPUのROM,PC,ALUの設計

Vol.5 ステート・マシンと命令デコーダの設計

Vol.6 CPUの全体統合とプログラムの実行

1.全体に共通する回路設計の考え方

CPUに必要なブロックを1つずつ設計していく

Vol.2以降では,前回考えた4ビットCPUのアーキテクチャに含まれる“A REG”,“B REG”,“I/O”,“ALU”,“PC”,“ROM”,“ID”,“STATE MACHINE”といった各ブロックの設計を行います.今後の回路設計では「ディジタル回路ブロック」を使ったオール・トランジスタの設計例と,Verilog HDLを使ってMAX10 FPGAに実装する設計例の両方を示します.

今回は,すべての回路設計で共通する考え方を整理してから,比較的回路構成が単純な“A REG”,“B REG”,“I/O”を作ります.

一般的な回路設計の流れ

入出力仕様を決める

図1に,Vol.2以降で解説する回路設計における共通の流れを示します.

図1 CPUを構成する各ブロックの回路を設計する基本的な流れ
これはトランジスタ・レベルの回路設計でも,HDLを使ったFPGA設計でも同じ

最初に,設計対象のブロック(あるいはモジュール)の入出力端子の仕様を決めます.これは,その回路を外部から見た場合のインターフェース仕様を決定する作業となります.

内部構成をブロック図で表す

続いて,回路の内部構成をおおまかに決めます.求められている機能を実現するために必要となるブロックを並べて,それぞれの接続関係を定義します.このようにして,内部構成を「ブロック図」の形で書いておくとその後の設計を進めやすくなります.

詳細設計を行う

トランジスタ・レベルあるいはゲート・レベルの回路設計

ブロック図ができたら,それを実現するための具体的な回路を設計します.この作業における設計の抽象度は,実際に使用する部品によって異なります.

本技術解説シリーズのテーマは「CPUをトランジスタだけで作ること」なので,「トランジスタ」という部品を最小単位として回路構成を考えるのが妥当だと考えられます.しかし,すべてのブロックをトランジスタ・レベルで設計すると回路図が膨大な量になって大変です.

そこで,今回は「デジタル回路ブロック」を最小単位として扱うことにします.これは,いわゆる「論理ゲート回路」のレベルでディジタル回路を設計することに相当します.

レジスタ・トランスファ・レベルの回路設計

本技術解説シリーズでは,FPGA上にCPUを実装する設計も行います.今回は,一般的に用いられる「レジスタ・トランスファ・レベル」(Register Transfer Level:RTL)の設計手法を採用します.これは,データを保持する回路である「レジスタ」を最小要素として回路構成を記述する方法です.

場面に応じて適切な抽象度で考えることが重要

図2に示すように,設計の抽象度は,「レジスタ・トランスファ・レベル > 論理ゲート・レベル >トランジスタ・レベル」の順に低くなります(具体的,物理的になる).

より抽象度が高い考え方として,回路構造には深入りせずに表面的な振る舞い(behavior)に着目して動作を考える「ビヘイビア・レベル」というものもあります.前回考えたCPUのアーキテクチャを考える作業は,ビヘイビア・レベルで各ブロックを扱っていました.このように,回路設計の段階に応じて適切な抽象度を選択すると作業の効率が上がります.

図2 回路設計における抽象度の違い
今回は「論理ゲート・レベル」と「レジスタ・トランスファ・レベル」で考える

動作検証を行う

回路設計が完了したら,動作検証を行います.トランジスタ・レベルの設計をする場合は回路シミュレータを使っても良いのですが,今回はデジタル回路ブロックの動作(論理ゲート・レベルの動作)が保証されていると仮定して,回路をそのまま組んで実験することにします.

また,FPGAの場合はVerilogでテスト・ベンチを記述して,論理回路シミュレータ“ModelSim”でシミュレーションを実行します.具体的なシミュレーションの手順やModelSimの使い方は,「Vol.3 Lチカ・ロジックのFPGA実装とシミュレーション(mz-cpu1738-da3)」を参照してください.

2.「レジスタ」の原型となる回路:“D-FF”

記憶回路

入力された信号を記憶する回路のことを「記憶回路」といいます.また,記憶回路を必要なビット数だけ並べた回路は「レジスタ」と呼ばれます.レジスタは,これから作るCPUにおいて中心的な役割を担う回路の1つです.

ここでは,レジスタの原型となる「D型フリップフロップ」(以下“D-FF”)の動作と回路構成について簡単に確認しておきます.

D-FFの基本動作

図3にD-FFのシンボル,表1にD-FFの動作を表した特性表を示します.この回路は“$CK$”端子に印加される電圧が立ち上がるタイミングで,“$D$”端子に印加された電圧レベルを記憶します.記憶した結果は“$Q$”端子から出力されます.“$\overline{Q}$”端子からは“$Q$”端子の論理レベルを反転させた信号が出力されます.このような動作をするD-FFは,「ポジティブ・エッジ・トリガ型D-FF」と呼ばれます.

“$CK$”信号の立ち下がりで入力信号を記憶する「ネガティブ・エッジ・トリガ型D-FF」もありますが,今回は扱いません.

図3 ポジティブ・エッジ・トリガ型D-FFのシンボル
表1 ポジティブ・エッジ・トリガ型D-FFの特性表

「ディジタル回路ブロック」を使ったD-FFの製作

ポジティブ・エッジ・トリガ型のD-FFを作る方法はいくつかありますが,ここではNANDゲートを使った図4の回路について考えることにします.この回路を「デジタル回路ブロック」で実際に組むと,写真1のようになります.2入力NANDゲートを5個,2入力NANDゲートを1個使っていて,合計トランジスタ数は26個です.

図4 「ポジティブ・エッジ・トリガ型D-FF」をゲート・レベルで構成した例
写真1 「ポジティブ・エッジ・トリガ型D-FF」を「ディジタル回路ブロック」で作ったようす

ポジティブ・エッジ・トリガ型D-FFの動作解説

“U5”と“U6”はSR-FFを構成している

NANDゲートを使ったポジティブ・エッジ・トリガ型D-FFの動作原理について考えます.

図4の回路の“U5”と“U6”は,ノード“$X$”および“$Y$”を入力端子としてもつ「セット・リセット型フリップフロップ」(SR-FF)を構成しています.“$X = 0$,$Y = 1$”のときは出力が“$Q$ = 1”となり,“$X = 1$,$Y = 0$”のときは“$Q = 0$”となります.また,“$X = Y = 1$”のときは現在の状態が保持されます.なお,“$X = Y = 0$”はその後の状態遷移が一意に定まらないので入力禁止とします.

入力が“$D = 0$”のときにクロック信号“$CK$”が立ち上がった時

図5(a)は,データ入力が“$D = 0$”の状態でクロック入力“$CK$”を“0→1”と変化させたときのようすを表しています.このとき,ノード“$X$”のレベルは“1”のままで,ノード“$Y$”のレベルが“1→0”と変化します.これにより,“U5”と“U6”で構成されたSR-FFの出力は“$Q = 0$”となって入力信号“$D$”の値が記憶されます.

クロック入力“$CK$”が“1”になった後は“U4”の出力が“1”に固定されるので,入力“$D$”を変化させても出力には影響しません.また,クロックを“1→0”と立ち下げたときはノード“$X$”および“$Y$”のレベルが共に“1”になるので,“U5”と“U6”で構成されるSR-FFは直前の状態をそのまま保持します.このようにして,「ポジティブ・エッジ・トリガ」の動作が実現されます.

図5 「ポジティブ・エッジ・トリガ型D-FF」の動作について考える

入力が“$D=1$”でクロック信号“$CK$”が立ち上がった時

図5(b)は,入力端子を“$D = 1$”とした状態でクロック入力“$CK$”を“0→1”と変化させたときのようすを表しています.このとき,ノード“$Y$”のレベルは“1”のままで,ノード“$X$”のレベルが“1→0”と変化します.これにより,“U5”と“U6”で構成されたSR-FFの出力は“$Q = 1$”となって入力信号“$D$”の値が記憶されます.

クロック入力“$CK$”が立ち上がった後に“$D$”入力を変化させたときの挙動や,クロック入力“$CK$”を立ち下げたときの挙動は先に考えた“$D = 0$”の場合と同じです.

非同期リセット機能付きのD-FF

図6に示すD-FFは,リセット端子“$\overline{RST}$”をもっています.このフリップフロップの特性表を表2に示します.クロック入力“$CK$”に対する挙動は先に考えたD-FFと同じですが,“$\overline{RST}$”端子を“0”にすると強制的に出力が“$Q = 0$”となります.このように,クロック信号と同期せず,任意のタイミングでリセットができる機能を「非同期リセット」といいます.よって,この回路は「非同期リセット付きのポジティブ・エッジ・トリガ型D-FF」ということになります.

後で設計するCPUの「レジスタ」回路では,この「非同期リセット付きポジティブ・エッジ・トリガ型D-FF」を基本要素として使います.なお,リセット信号は“0”で有効になるので,リセット入力端子の名前は「負論理」であることを示す「バー」を付けています.

図6 非同期リセット付きポジティブ・エッジ・トリガ型D-FFのシンボル
表2 非同期リセット付きポジティブ・エッジ・トリガ型D-FFの特性表

非同期リセット付きD-FFの製作例

NANDゲートを使って構成した「非同期リセット付きポジティブ・エッジ・トリガ型D-FF」を図7に示します.また,この回路を「デジタル回路ブロック」で実際に組んだものを写真2に示します.この回路では2入力NANDゲートを2個,3入力NANDゲートを4個使っており,合計トランジスタ数は32個です.

図7 「非同期リセット付きポジティブ・エッジ・トリガ型D-FF」をゲート・レベルで構成した例
ICはいっさい使わない!1738個のMOSFETでCPUに必要なすべての回路を作る
写真2 「非同期リセット付きポジティブ・エッジ・トリガ型D-FF」を「ディジタル回路ブロック」で作ったようす

非同期リセット付きD-FFの動作解説

「非同期リセット付きポジティブ・エッジ・トリガ型D-FF」の回路は,先に紹介した「ポジティブ・エッジ・トリガ型D-FF」とほとんど同じです.変更点はリセット信号の追加だけなので,ここではリセット入力“$\overline{RST}$”の影響だけを考えることにします.

リセット入力を“$\overline{RST} = 1$”とした場合

リセット入力を“$\overline{RST} = 1$”とした場合,“$\overline{RST}$”信号が入力される“U2”,“U4”,“U6”は「2入力NANDゲート」として振る舞います.これは図4の回路と等価なので,先に考えた(リセット機能なしの)「ポジティブ・エッジ・トリガ型D-FF」として動作します.

リセット入力を“$\overline{RST} = 0$”とした場合

リセット入力を“$\overline{RST} = 0$”とした状態を図8に示します.“U2”の出力すなわちノード“$X$”は他の信号線のレベルによらず“1”になります.また,“U6”の出力は他の信号線のレベルによらず“1”になります.これに伴い,“U5”の出力は“0”になります.以上のことから,“$\overline{RST} = 0$”を入力することによって任意のタイミングで“$Q = 0$”となることがわかります.

図8 「非同期リセット付きポジティブ・エッジ・トリガ型D-FF」に"$\overline{RST} = 0$"を入力したときのようす

非同期リセット付きD-FFをFPGA上に実装する

ソース・コード

今後のCPU回路設計でよく使う「非同期リセット付きポジティブ・エッジ・トリガ型D-FF」をFPGA上に実装する方法について確認しておきます.リスト1に,Verilog HDLの記述例を示します.

module D_FF
(
	input wire clk,
	input wire n_rst,
	input wire d,
	
	output reg q
);

always @ (posedge clk, negedge n_rst)
begin
	if(~n_rst)
		q <= 1'b0;
	else
		q <= d;
end

endmodule

リスト1 「非同期リセット付きポジティブ・エッジ・トリガ型D-FF」のVerilog記述例
ファイル名は"D_FF.v"とする

なお,リスト1のVerilogコードによってFPGA内に生成される回路は,先に「デジタル回路ブロック」で作った回路とは厳密には異なります.あくまで「外側から見た挙動が同じ」というだけです.ご注意ください.

ソース・コードの解説

リスト1のVerilogコードの内容を簡単に解説します.

モジュール名は“D_FF”としています.クロック入力端子は“clk”,リセット入力端子は“n_rst”,入力“$D$”の端子は“d”,出力“$Q$”の端子は“q”です.なお,反転出力“$\overline{Q}$”は今後あまり使わないので定義しませんでした.

“always@( )”ブロックを使って,レジスタ“q”の動作を定義しています.リセット入力が“n_rst = 0”のときは値をリセットします.それ以外の場合はレジスタ“q”に入力“d”の値を代入します.

「デジタル回路ブロック」でこの回路を作るためには32個のトランジスタをはんだ付けする必要がありましたが,FPGA上に実装する場合は数行のVerilogコードを書くだけで済んでしまいました(FPGAって便利ですね).

D-FFの動作をシミュレーションで確認する

リスト1で定義したD-FFの動作を論理シミュレーションで確認してみましょう.リスト2に,今回使うテスト・ベンチを示します.なお,シミュレーションの手順やテスト・ベンチの作り方については「Vol.3 Lチカ・ロジックのFPGA実装とシミュレーション(mz-1738-da3)」を参照してください.

`timescale 1us/10ns

module tb_d_ff();

//wire, register declaration
reg clk;
reg n_rst;
reg d;
wire q;
reg [7:0] cycle_cnt;

//clock signal
initial clk = 1'b0;
always #0.5
	clk = ~clk;

//reset signal
initial begin
	n_rst = 1'b0;
	#1;
	n_rst = 1'b1;
	#10;
	n_rst = 1'b0;
	#1;
	n_rst = 1'b1;
end

//data signal
initial begin
	d = 1'b0;
	#2;
	d = 1'b1;
	#2;
	d = 1'b0;
	#2;
	d = 1'b1;
end

//cycle count
initial cycle_cnt = 0;
always @ (posedge clk)
	cycle_cnt = cycle_cnt + 8'd1;

//stop
always @ (*)
if(cycle_cnt == 8'd20)
	$stop;

//D_FF instance
D_FF d_ff
(
	.clk(clk),
	.n_rst(n_rst),
	.d(d),
	.q(q)
);

endmodule

リスト2 D-FFのテスト・ベンチのVerilogファイル"tb_d_ff.v"

リスト2の内容を簡単に説明します.テスト・ベンチのモジュール名は“tb_d_ff”としています.クロック信号を生成するためにレジスタ“clk”を用意して,0.5ステップ(今回は`timescaleで1μsを指定しているので0.5μsに相当する)ごとに値が反転するようにしています.また,リセット信号を生成するためにレジスタ“n_rst”を用意して,最初の1ステップだけ“0”にしてその後は10ステップの間“1”にしています.さらにその後,非同期リセットの効果を試すために1ステップだけ“n_rst”の値を“0”にしています.

シミュレーション全体のステップ数を管理するために,“cycle_cnt”というレジスタを用意してクロックの回数をカウントしています.今回は適当なシミュレーションの長さとして,“cycle_cnt = 20”となった時点で終了するようにしました.

シミュレーション対象のモジュール“D_FF”は,“d_ff”という名前でインスタンス化しています.後で波形表示を行うときは,この“d_ff”という名前で内部信号にアクセスすることになります.

ModelSim用のスクリプト・ファイル

無償で使用できる論理シミュレータの“ModelSim”(Starter Edition)を使ってシミュレーションを実行します.ModelSimでシミュレーションを行う場合は,一連の設定をまとめたスクリプト・ファイル(拡張子は“.do”)を用意しておくと便利です.今回のシミュレーションのために作成したスクリプト・ファイルをリスト3に示します.なお,ModelSimを使ったシミュレーションの手順やスクリプト・ファイルの書き方については,「Vol.3 Lチカ・ロジックのFPGA実装とシミュレーション(mz-1738-da3)」を参照してください.

#transcript window setting
transcript on

#delete "rtl_work" directory
if {[file exists rtl_work]} {
	vdel -lib rtl_work -all
}

#create the design library
vlib rtl_work

#define a mapping between logical and physical library name
vmap work rtl_work

#compile the Verilog files
vlog  -vlog01compat -work work \
	+incdir+../../ \
	../../D_FF.v \
	./tb_d_ff.v

#invoke the simulator
vsim -L altera_mf_ver -c work.tb_d_ff

#wave window setting
add wave -divider TEST_BENCH
add wave -bin sim:/tb_d_ff/clk
add wave -unsigned sim:/tb_d_ff/cycle_cnt
add wave -bin sim:/tb_d_ff/n_rst
add wave -bin sim:/tb_d_ff/d

add wave -divider D_FF
add wave -bin sim:/tb_d_ff/d_ff/clk
add wave -bin sim:/tb_d_ff/d_ff/n_rst
add wave -bin sim:/tb_d_ff/d_ff/d
add wave -bin sim:/tb_d_ff/d_ff/q

#save all signal
log -r *

#run simulation
run -all

#wave sindow zoom setting
wave zoom full

リスト3 D-FFのシミュレーションに使うModelSimのスクリプト・ファイル"tb_d_ff.do"

リスト3のスクリプト・ファイルは,他のシミュレーションにも流用することができます.このスクリプト・ファイルを使いまわす場合は,テスト・ベンチ名の“tb_d_ff”の部分(“vlog”コマンドおよび“vsim”コマンドのパラメータ)と,読み込むVerilogファイルのパス(“vlog”コマンドのパラメータ),そして“tb_d_ff/d_ff/clk”などの信号線の名前(“add wave”コマンドのパラメータ)を適宜書き換えます.

ModelSimによるシミュレーション結果

リスト2のテスト・ベンチおよびリスト3のスクリプトを利用してModelSim上でシミュレーションを実行すると,図9の信号波形が得られます.クロック信号“clk”が立ち上がるタイミングで入力“d”の値が出力“q”に反映されていることがわかります.また,入力“n_rst”が“0”になるとクロックの状態とは無関係に出力“q”が“0”にリセットされるようすも確認できます.

図9 ModelSimでD-FFのシミュレーションを実行したようす

3.Aレジスタ(A REG)の設計

Aレジスタの入出力仕様

具体的なCPUの回路設計を始めます.まずは最も構成が単純な「Aレジスタ」(A REG)から考えます.

Aレジスタに求められる機能は,4ビット幅のデータを記憶することです.記憶するデータを更新するタイミングは,CPU全体に供給されるクロック信号の立ち上がりエッジとします.また,クロックとは非同期でリセットする機能も付けましょう.

データの読み込みを制御する“$LD$”端子を付ける

後でCPUの回路全体を組み立てるときに,このAレジスタのデータ入力端子は4ビットの「データ・バス」に接続されます.このバスに流れているデータはAレジスタに対するものだけではなく,他の回路ブロックに向けたデータも含まれます.そのため,毎回クロックの立ち上がり・エッジでデータを読み込んでいると,不要なデータまで読み込んでしまう可能性があります.そこで,データの読み込みを制御する「ロード端子」“$LD$”を付けることにします.“$LD = 1$”のときは入力されたデータを読み込んで出力に反映させますが,“$LD = 0$”のときは新しいデータをロードせず現在の出力状態を保持します.

入出力端子の決定と特性表

Aレジスタの入出力端子を図10のように定めます.また,レジスタの動作を表す特性表を表3に示します.外部から見てこのとおりの動作を行う回路を設計することが,今後の目標となります.

図10 Aレジスタ(A REG)の入出力端子の仕様
表3 Aレジスタの特性表

Aレジスタの内部ブロック図

表3のAレジスタの特性表を満たすものとして,図11のようなブロック図を考えます.記憶回路の本体は,先ほど考えた「非同期リセット付きポジティブ・エッジ・トリガD-FF」をそのまま使います.今回は4ビット・データを記憶する回路なので,D-FFを4個並べます.

新しいデータを読み込むか直前のデータを保持するかを選択するために,信号選択回路である「マルチプレクサ」(multiplexer,MUX)を使います.MUXの信号選択端子を“$LD$”端子として外部に引き出します.“$LD = 0$”のときはD-FFの入力“D”に出力“$Q$”が接続されるようにします.また,“$LD = 1$”のときは外部からの入力データがD-FFの入力“$D$”に接続されるようにします.

図11 Aレジスタ(A REG)の内部ブロック図

Aレジスタを論理ゲート・レベルで作る

図11のブロック図をもとにして論理ゲート・レベルでAレジスタの回路を作ると,図12のようになります.1ビット分の記憶回路を単純に4つ並べた構成になっています.

写真3は,図12に示したAレジスタの回路を「デジタル回路ブロック」で作ったようすです.これがCPUキット“CPU1738”を構成する基板の1つである「A REG基板」となります.回路図と基板上の論理ゲートの配置がほとんど対応しているので,回路図上の信号の流れを実物で確認することができます.

この回路には37個の論理ゲートを使っています.トランジスタ数は合計178個です.

図12 Aレジスタ(A REG)の論理ゲート・レベルの回路図
写真3 CPUキット"CPU1738"の「A Register基板」

レジスタをFPGA上に実装する

続いて,FPGA上にCPUを作り込む作業について考えます.今回のCPUでは「Aレジスタ」(“A REG”),「Bレジスタ」(“B REG”),「出力ポート」(“OUT PORT”)がほとんど同じ機能をもちます.そこで,これら3つのブロックに共通して使える回路として“REGISTER”という1つのモジュールを作ることにします.

Verilogソース・コードの解説

モジュール“REGISTER”のVerilog記述例をリスト4に示します.

`default_nettype none

module REGISTER(
	input wire clk,
	input wire n_rst,
	input wire ld,
	input wire [3:0] d,
	
	output reg [3:0] q
);

always @ (posedge clk or negedge n_rst)
begin
	if (~n_rst)
		q <= 4'd0;
	else if (ld)
		q <= d;
	else
		q <= q;
end

endmodule 

`default_nettype wire

リスト4 CPUの各部で使うレジスタのVerilogソース・コード"REGISTER.v"

これは先に紹介した「非同期リセット付きポジティブ・エッジ・トリガ型D-FF」のVerilogコードを少しだけ変更したもので,“ld = 1”のときは入力“d”の値をレジスタに保持し,“ld = 0”のときは直前の値をそのまま保持するようにしています.

冒頭の“`default_nettype none”は,自分のミスで定義していないwireを使ってしまった場合にエラーを出すために書いています.例えば,定義していない“hoge”というwireを使って“assign hoge = clk;”などと書いても,通常はエラーになりません.このようなVerilogの言語仕様を積極的に活用する場合もありますが,今回は意図しない動作を防ぐために明示的に定義したwireだけを使うようにします.

なお,外部のライブラリなどを利用する場合は,この設定を残しておくと正常に論理合成できない可能性があります(そのライブラリを作った人が暗黙のwireを使っている場合).そこで,ファイルの末尾に“`default_nettype wire”と書くことで,これ以降に読み込まれるファイルに関しては暗黙のwireを許可するようにしておきます.

レジスタの論理シミュレーションをする

リスト4で記述したモジュール“REGISTER”の動作を確認するために,ModelSimを使って論理シミュレーションを実行してみます.シミュレーションに使うテスト・ベンチをリスト5に,ModelSim用のスクリプト・ファイルをリスト6に示します.今回はレジスタに出入りする信号をすべて表示するようにしました.

`timescale 1us/10ns

module tb_register();

//wire, register declaration
reg clk;
reg n_rst;
reg ld;
reg [3:0] d;
wire [3:0] q;
reg [7:0] cycle_cnt;

//clock signal
initial clk = 1'b0;
always #0.5
	clk = ~clk;

//reset signal
initial begin
	n_rst = 1'b0;
	#1;
	n_rst = 1'b1;
	#10;
	n_rst = 1'b0;
	#1;
	n_rst = 1'b1;
end

//load signal
initial begin
	ld = 1'b0;
	#2;
	ld = 1'b1;
	#6;
	ld = 1'b0;
end

//data signal
initial begin
	d = 4'd0;
	#4;
	d = 4'd1;
	#2;
	d = 4'd2;
	#2;
	d = 4'd3;
	#2;
	d = 4'd4;
end

//cycle count
initial cycle_cnt = 0;
always @ (posedge clk)
	cycle_cnt = cycle_cnt + 8'd1;

//stop
always @ (*)
if(cycle_cnt == 8'd20)
	$stop;

//REGISTER instance
REGISTER register
(
	.clk(clk),
	.n_rst(n_rst),
	.ld(ld),
	.d(d),
	.q(q)
);

endmodule

リスト5 レジスタのシミュレーションのためのテスト・ベンチ"tb_register.v"
#transcript window setting
transcript on

#delete "rtl_work" directory
if {[file exists rtl_work]} {
	vdel -lib rtl_work -all
}

#create the design library
vlib rtl_work

#define a mapping between logical and physical library name
vmap work rtl_work

#compile the Verilog files
vlog  -vlog01compat -work work \
	+incdir+../../ \
	../../REGISTER.v \
	./tb_register.v

#invoke the simulator
vsim -L altera_mf_ver -c work.tb_register

#wave window setting
add wave -divider TEST_BENCH
add wave -bin sim:/tb_register/clk
add wave -unsigned sim:/tb_register/cycle_cnt
add wave -bin sim:/tb_register/n_rst
add wave -unsigned sim:/tb_register/d

add wave -divider D_FF
add wave -bin sim:/tb_register/register/clk
add wave -bin sim:/tb_register/register/n_rst
add wave -bin sim:/tb_register/register/ld
add wave -unsigned sim:/tb_register/register/d
add wave -unsigned sim:/tb_register/register/q

#save all signal
log -r *

#run simulation
run -all

#wave sindow zoom setting
wave zoom full

リスト6 レジスタのシミュレーションのためのスクリプト・ファイル"tb_register.do"

シミュレーションを実行すると,図13のような波形が得られます.“ld = 1”のときは入力されたデータを読み込み,“ld = 0”のときは出力を保持し続けることが確認できます.また,非同期リセットも問題なく動作していることがわかります.

図13 レジスタのモジュール"REGSITER"の論理シミュレーション結果

4. Bレジスタ(B REG)の設計

Bレジスタの入出力仕様

「Bレジスタ」(“B REG”)は4ビットのデータを保持する回路で,基本的な動きはAレジスタと同じです.ただし,Bレジスタの出力は“ROM”の出力と同じバスを共有するので,出力が競合しないようにする必要があります.今回は部品の数を少なくするために,「トライ・ステート・バッファ」を使って出力部分を電気的に切り離すことにしました.電気的に切り離された状態というのは「高抵抗」な状態ということになりますが,ここでは意味を広げて(交流的な意味で)「ハイ・インピーダンス状態」(High-Z状態)と呼ぶことにします.

Bレジスタの入出力仕様を図14に示します.“$OE$”端子は出力を許可する「アウト・イネーブル」(Out Enable)信号を入力する端子です.また,Bレジスタの特性表を表4に示します.

図14 Bレジスタ(B REG)の入出力仕様
表4 Bレジスタの特性表

Bレジスタの内部ブロック図

図15にBレジスタの内部ブロック図を示します.基本的な構成はAレジスタと同じですが,出力部分にトライ・ステート・バッファを追加している点が異なります.

図15 Bレジスタ(B REG)の内部ブロック図

Bレジスタを論理ゲート・レベルで作る

図16に,論理ゲート・レベルで設計したBレジスタの回路図を示します.出力部分にトライ・ステート・バッファが付いている以外はAレジスタの回路図と同じです.

写真4は,図16の回路を「デジタル回路ブロック」で作ったようすです.これがCPUキット“CPU1738”の「B REG基板」となります.論理ゲートの数は41個,使用トランジスタ数は210個です.

図16 Bレジスタ(B REG)の論理ゲート・レベルの回路図
写真4 CPUキット"CPU1738"の「B Register基板」

BレジスタのFPGA実装

FPGA上にBレジスタを実装するときは,先ほど用意した“REGISTER”モジュールを使います.FPGA内部ではトライ・ステート・バッファは使わず,マルチプレクサ(MUX)を使って信号選択を行うようにします.このマルチプレクサは,レジスタどうしを接続する上位の階層(トップ・モジュール)で定義します.

5.I/O(IN PORT,OUT PORT)の設計

出力ポートの入出力仕様

出力ポート(OUT PORT)は,CPU内部のバスを流れるデータを外部回路に対して出力する回路です.バスに流れるデータを適切なタイミングで保持することで,外部回路に対して安定したデータを提供します.

この回路に求められる機能は4ビットのレジスタなので,その実体はAレジスタとまったく同じものになります.図17に出力ポートの入出力仕様,表5に出力ポートの動作を表す特性表を示します.

図17 出力ポート(OUT PORT)の入出力仕様
表5 出力ポートの特性表

出力ポートの内部ブロック図

図18に出力ポートの内部ブロック図を示します.これもAレジスタと同じです.

図18 出力ポート(OUT PORT)の内部ブロック図

出力ポートを論理ゲート・レベルで作る

図19に出力ポートを論理ゲート・レベルで構成した回路図を示します.基本的にはAレジスタと同じ回路ですが,外部回路の負荷を模擬したものとして出力ラインにLEDを付けています.

入力ポートの入出力仕様

図19 出力ポート(OUT PORT)の論理ゲート・レベルの回路図

入力ポートは,外部回路から入力された信号を適切なタイミングで内部バスに通す回路です.図20に入力ポートの入出力仕様,表6に入力ポートの動作を表す真理値表を示します.入力ポートの出力回路は“$OE = 0$”の場合に“High-Z”となり,CPUのバス配線から電気的に切り離されます.“$OE = 1$”とした場合は入力ポートの出力がバス配線に接続され,外部回路から入力されたデータをCPU内部のレジスタに格納できるようになります.

図20 入力ポート(IN PORT)の入出力仕様
表6 入力ポートの真理値表

入力ポートの内部ブロック図

図21に入力ポートの内部ブロック図を示します.最低限の機能を実装するならばトライ・ステート・バッファだけで十分ですが,今回はシュミット・トリガを入れて外部から入力された信号を波形整形するようにしています.

図21 入力ポート(IN PORT)の内部ブロック図

入力ポートを論理ゲート・レベルで作る

図22に入力ポートを論理ゲート・レベルで作った回路図を示します.シュミット・トリガの入力部分には抵抗とコンデンサで作ったローパス・フィルタを入れてあります.また,入力信号を確認するためにシュミット・トリガの出力部分にLEDを付けました.

図22 入力ポート(IN PORT)の論理ゲート・レベルの回路図

CPUキットのI/O基板

CPUキット“CPU1738”では,出力ポートと入力ポートの回路をまとめて「I/O基板」に実装しています.これを写真5に示します.この回路には49個の論理ゲートが使われており,トランジスタ数は合計258個です.

写真5 CPUキット"CPU1738"の「I/O基板」

I/OのFPGA実装

CPU内部に出力ポートを実装するときは,先に用意した“REGISTER”モジュールをそのまま使います.また,入力ポートは入力信号を内部バスに接続する際の信号選択の役割をもちますが,FPGAの内部ではトライ・ステート・バッファではなくマルチプレクサを使って実装します.

なお,FPGAの入力ピンはシュミット・トリガ・モードに設定することができます.設定方法はCPU全体をFPGAに実装する時に説明します.

関連資料


(c)2020 Nobuyasu Beppu