SFLで作るPICO-16プロセッサ/第5回
をテンプレートにして作成
[
トップ
] [
新規
|
一覧
|
単語検索
|
最終更新
|
ヘルプ
|
ログイン
]
開始行:
[[SFLで作るPICO-16プロセッサ]]
*First PICO-16プロセッサのデータパスの設計 [#l2672d44]
ALUを使用した演算を行う基本的な命令セットを備えたプロセッサを設計します。~
2サイクルで動作するFirst PICO-16プロセッサの仕組みを考えてみましょう。
**コンテンツ [#n17a3f6e]
#contents
*First PICO-16プロセッサの構成と解説 [#ee770e29]
基本的なALUで行う演算の命令を備えたプロセッサをFirst PICO-16プロセッサとして解説します。
**2つのALUで実現する回路構成 [#i31b1f97]
[[第3回>SFLで作るPICO-16プロセッサ/第3回]]で設計した命令フェッチ回路と[[第4回>SFLで作るPICO-16プロセッサ/第4回]]で設計した実行ユニットを繋げれば、命令メモリに格納されている命令を実行していくプロセッサが完成します。~
プロセッサの全体の回路構成を図に表すと以下のようになります。ただし、今回は新たに命令コードを解析する命令デコーダを挟んであります。
CENTER:&ref(SFLで作るPICO-16プロセッサ/第5回/fpico16_rev1.png,80%);
ここで注目して欲しいのは、この回路では2つのALUを使用していることです。~
プログラムカウンタのインクリメントのためには、ALUの加算の機能しか使わないので、せっかく実装してあるALUの他の機能が無駄になってしまいます。~
解決方法として、左側のALUを加算の機能だけを持つ回路である加算器に置き換えてしまう考え方があります。~
ただし今回は、プロセッサの動作に必要な演算を1つのALUで全て行う回路構成を考えてみましょう。
**1つのALUで実現する回路構成 [#jde89aae]
命令メモリから読み込んだ命令コードを命令レジスタ(Instruction Register)に格納します。~
また、ALUに入力する信号を選択できるように設計を変更します。SFL記述では、ALUを使用するタイミングの制御を行う必要があります。~
本来はステートマシンを用いて行いますが、今回は回路の動作を2つのステップに分けて実行することでALUを使用するタイミングを制御します。
CENTER:&ref(SFLで作るPICO-16プロセッサ/第5回/fpico16_rev2.png,80%);
**動作 [#tb163b14]
ALUを2回に分けて使用するため、次の2つのステップに分けて命令を実行します。
***命令フェッチの動作 [#r1cec17a]
命令フェッチでは2つの動作を行います。
-プログラムカウンタの値をアドレスとして、命令メモリから命令コードを読み出し、命令レジスタに書き込む。
-ALUでプログラムカウンタをインクリメントし、プログラムカウンタの値を更新する。
CENTER:&ref(SFLで作るPICO-16プロセッサ/第5回/fpico16_if.png,80%);
***実行の動作 [#ja700189]
実行では命令レジスタの値を使用して、次の4つの動作を行います。
-命令レジスタをデコードする。
-レジスタファイルから使用する汎用レジスタの値を読み出す。
-命令に対応した演算をALUで行う。
-ALUでの演算結果をレジスタファイルの指定した汎用レジスタへ書き込む。
CENTER:&ref(SFLで作るPICO-16プロセッサ/第5回/fpico16_ex.png,80%);
***波形 [#pb400633]
命令の実行様子を波形にすると以下のようになります。
CENTER:&ref(fpico16_wave.png);
inst_fetchが1のとき、プログラムカウンタ(pc)と命令レジスタ(ir)が更新され、~
executeが1のとき、レジスタファイルの汎用レジスタ(r0-r7)が更新される様子が分かります。
ここでは省略していますが、クロック信号の立ち上がり時にデータの更新が行われます。
*仕様 [#h8068000]
今回設計する回路の仕様は3つに分けて説明します。
**命令デコーダ [#wd693381]
命令コードから、使用する汎用レジスタアドレス、書き込む汎用レジスタアドレス、即値、ALUの機能などを決定します。
CENTER:&ref(SFLで作るPICO-16プロセッサ/第5回/inst_decoder.png,75%);
ただし、PICO-16プロセッサの命令セットの特性から、命令コード16ビットのうち、<10:8>を読み出しレジスタ兼書き込みレジスタ、<7:5>を読み出しレジスタ、<7:0>を8ビットの即値として扱うので、これらの接続に関しては直接命令コードからビットを切り出して接続します。
この命令デコーダは、命令コードの<15:11>の5ビットが示す命令の種類と、<4:0>が示す機能コードから、ALUの制御信号を解析します。~
命令コードは仕様で決められているので、命令の種類と機能コードから使用するALUの演算を決定します。
***入出力 [#je5f381d]
-入力
--命令コード(16ビット)
-出力(制御出力)
--ALU制御信号(AND, OR, XOR, NOT, ADD, SUB, SHL, SHR)
***機能 [#s22b8a6c]
-デコード
--命令コードから実行する命令を解析し、使用するALU制御信号を決定する。
--ALU制御信号は[[制御出力>#w85bec99]]として宣言する。
***命令セット [#q90f1339]
First PICO-16プロセッサでは次の命令をサポートします。
| 命令 | 意味 |>|>|>| 命令コード | 記述例 | 意味 |
| | | <15:11> | <10:8> | <7:5> | <4:0> | | |
| AND | And | 0 | rd | rs | 2 | and r1, r2 | r1 <= r1 & r2 |
| OR | Or | 0 | rd | rs | 3 | or r1, r2 | r1 <= r1 | r2 |
| XOR | Exclusive or | 0 | rd | rs | 4 | xor r1, r2 | r1 <= r1 @ r2 |
| NOT | Not | 0 | rd | rs | 5 | not r1, r2 | r1 <= ^r2 |
| ADD | Add | 0 | rd | rs | 6 | add r1, r2 | r1 <= r1 + r2 |
| SUB | Subtract | 0 | rd | rs | 7 | sub r1, r2 | r1 <= r1 - r2 |
| SL | Shift left | 0 | rd | rs | 12 | sl r1, r2 | r1 <= r2 << 1 |
| SR | Shift right | 0 | rd | rs | 13 | sr r1, r2 | r1 <= r2 >> 1 |
| ANDI | And immediate | 2 | rd |>| immediate | andi r1, 10 | r1 <= r1 & 10 |
| ORI | Or immediate | 3 | rd |>| immediate | ori r1, 10 | r1 <= r1 | 10 |
| XORI | Exclusive or immediate | 4 | rd |>| immediate | xori r1, 10 | r1 <= r1 @ 10 |
| ADDI | Add immediate | 6 | rd |>| immediate | addi r1, 10 | r1 <= r1 + 10 |
| SUBI | Subtract immediate | 7 | rd |>| immediate | subi r1, 10 | r1 <= r1 - 10 |
rdはregister destinationの略で、書き込みレジスタを示しています。~
rsはregister sourceの略で、読み出しレジスタを示しています。~
PICO-16では基本的にrd = rd + rsといった演算を行います。
**データパス [#x54e503f]
[[First PICO-16プロセッサの構成と解説>#ee770e29]]の[[1つのALUで実現する回路構成>#jde89aae]]で解説した部分を設計します。
***入出力 [#l7e29a23]
制御入力のみになります。
***内部レジスタ [#v17ec8be]
-プログラムカウンタ(16ビット)
--実行する命令のアドレスを保持するレジスタ。
--リセット時に0に初期化する。
-命令レジスタ(16ビット)
--実行する命令の命令コードを保持するレジスタ。
***サブモジュール [#p7b7a736]
-ALU
--[[第1回>SFLで作るPICO-16プロセッサ/第1回]]で設計したALU。
-命令メモリ
--[[第3回>SFLで作るPICO-16プロセッサ/第3回]]で設計した命令メモリ。
--サブモジュールの名前はimemとする。
-レジスタファイル
--[[第2回>SFLで作るPICO-16プロセッサ/第2回]]で設計したレジスタファイル。
--サブモジュールの名前はrfとする。
-命令デコーダ
***機能 [#r35eeb5a]
-プログラムカウンタのインクリメント
--ALUのADD機能を使用してプログラムカウンタをインクリメント(+1)する。
-メモリ内容の読み出し
--プログラムカウンタを命令メモリのアドレスとして、命令メモリからのデータを出力する。
-デコードを実行
--命令デコーダを使用して、命令に応じたALUの機能を決定する。
-使用する汎用レジスタの読み出し
--レジスタファイルから使用する汎用レジスタの値を読み出す。
-命令の実行
--命令デコーダの結果に応じたALUの機能を用いて演算を行う。
--即値を扱う命令は、即値に対して[[符号拡張>#x666043e]]もしくは[[ゼロ拡張>#p3db468e]]を行い、16ビットのデータとして扱う。
---符号拡張 : ADDI, SUBI
---ゼロ拡張 : ANDI, ORI, XORI
-ALUでの演算結果を使用したライトバック
--ALUの結果を使用してレジスタファイルの汎用レジスタの値を更新する。
**全体 [#g9dcde8c]
命令フェッチと実行の2つの制御信号を持ち、それぞれの動作に必要なデータパスの制御信号を操作する回路を設計します。
***入出力 [#m79eca18]
制御入力のみになります。
***サブモジュール [#hf74bfbd]
-データパス
--サブモジュールの名前はdpとする。
***機能 [#p1fa7e7c]
-命令フェッチ
--プログラムカウンタのインクリメントを実行する。
--メモリ内容の読み出しを実行する。
-実行
--デコードを実行する。
--使用する汎用レジスタの読み出しを実行する。
--命令の実行を行う。
--ALUでの演算結果を使用したライトバックを実行する。
*実装 [#w40baf9e]
実装も3つに分けて説明します。
**命令デコーダ [#z2407ab2]
ファイル名はinst_decoder.sfl及び、inst_decoder.hとします。
***ヘッダ [#oe7ab1a2]
declare inst_decoder {
input opcode<16>;
instrin decode;
instrout AND, OR, XOR, NOT, ADD, SUB, SL, SR;
instrout ANDI, ORI, XORI, ADDI, SUBI;
instr_arg decode(opcode);
}
***モジュール [#z91a9140]
以下の手順に従って記述してください。
/* OPコードのDEFINE */
%d OP_RTYPE 0b00000
%d OP_ANDI 0b00010
%d OP_ORI 0b00011
%d OP_XORI 0b00100
%d OP_ADDI 0b00110
%d OP_SUBI 0b00111
/* RTYPEで使用するFUNCコードのDEFINE */
%d FUNC_AND 0b00010
%d FUNC_OR 0b00011
%d FUNC_XOR 0b00100
%d FUNC_NOT 0b00101
%d FUNC_ADD 0b00110
%d FUNC_SUB 0b00111
%d FUNC_SL 0b01100
%d FUNC_SR 0b01101
module inst_decoder {
/* 1. 入出力信号、制御入出力信号を記述 */
/* 2. 制御入力に対する動作を記述 */
}
**データパス [#b732fcc6]
ファイル名はdatapath.sfl及び、datapath.hとします。
***ヘッダ [#s7602757]
declare datapath {
/* 命令フェッチステートの制御 */
instrin inc_pc;
instrin imem_read;
/* 実行ステートの制御 */
instrin decode;
instrin reg_fetch;
instrin execute;
instrin wb_alu;
}
***モジュール [#p0ba7314]
以下の手順に従って記述してください。
/* 1. サブモジュールとして使用するモジュールのヘッダをインクルード */
module datapath {
/* 2. 入出力信号、制御入出力信号を記述 */
/* 3. 内部レジスタ宣言 */
/* 4. サブモジュール宣言 */
/* 5. 制御入力に対する動作を記述 */
}
**全体 [#t85b523c]
ファイル名はfpico16.sfl及び、fpico16.hとします。
***ヘッダ [#oe7ab1a2]
declare fpico16 {
instrin inst_fetch;
instrin execute;
}
***モジュール [#z91a9140]
以下の手順に従って記述してください。
/* 1. サブモジュールとして使用するモジュールのヘッダをインクルード */
module fpico16 {
/* 2. 入出力信号、制御入出力信号を記述 */
/* 3. サブモジュール宣言 */
/* 4. 制御入力に対する動作を記述 */
}
*論理シミュレーション [#da55b9ac]
回路が正しく動作しているかどうかを確認します。
+&ref(ex05.tar.gz);をダウンロードする。~
~
解凍方法)
% tar zxvf ex05.tar.gz
+inst_decoder.h、inst_decoder.sfl、datapath.h、datapath.sfl、fpico16.h、fpico16.sflを作成し、ex05ディレクトリに入れる。
--inst_decoder.hは、[[ここ>#oe7ab1a2]]から内容をコピーする。
--inst_decoder.sflは、[[ここ>#z91a9140]]から内容をコピーして、記述を完成させる。
--datapath.hは、[[ここ>#s7602757]]から内容をコピーする。
--datapath.sflは、[[ここ>#p0ba7314]]から内容をコピーして、記述を完成させる。
--fpico16.hは、[[ここ>#oe7ab1a2]]から内容をコピーする。
--fpico16.sflは、[[ここ>#z91a9140]]から内容をコピーして、記述を完成させる。
+[[第1回>SFLで作るPICO-16プロセッサ/第1回]]で作成したalu.h、alu.sflをex05ディレクトリにコピーする。
+[[第2回>SFLで作るPICO-16プロセッサ/第2回]]で作成したregfile.h、regfile.sflをex05ディレクトリにコピーする。
+[[第3回>SFLで作るPICO-16プロセッサ/第3回]]で作成したinst_memory.h、inst_memory.sflをex05ディレクトリにコピーする。
+シミュレーションを行う。
--シミュレーションには次のコマンドを使います。
% make
もしくは、
% make sim
--シミュレーションの結果はsim.logに出力されます。正しく動作していると&ref(sim.log.txt);のようになります。
--余裕があればテストベンチを自由に改変してみましょう。
+波形を観察する。
--波形ビューワの起動には次のコマンドを使います。
% make wave
--
+ファイルをクリーンする。
--ファイルのクリーンには次のコマンドを使います。
% make clean
--シミュレーションや波形ビューワで生成されたファイルのサイズが大きいときもあるので、全て終わったら余計なファイルを削除しておきましょう。
**シミュレーション結果 [#r4571f28]
正しく動作していれば、次のような結果が出ます。
|ステップ|命令|r0|r1|r2|r3|r4|r5|r6|r7|
|1|ori r0, 10|000a|0000|0000|0000|0000|0000|0000|0000|
|2|xori r1, 0x20|000a|0020|0000|0000|0000|0000|0000|0000|
|3|andi r2, 0x10|000a|0020|0000|0000|0000|0000|0000|0000|
|4|addi r2, 30|000a|0020|001e|0000|0000|0000|0000|0000|
|5|subi r2, 10|000a|0020|0014|0000|0000|0000|0000|0000|
|6|sr r3, r2|000a|0020|0014|000a|0000|0000|0000|0000|
|7|sl r6, r2|000a|0020|0014|000a|0000|0000|0028|0000|
|8|add r7, r3|000a|0020|0014|000a|0000|0000|0028|000a|
|9|sub r3, r2|000a|0020|0014|fff6|0000|0000|0028|000a|
|10|not r4, r3|000a|0020|0014|fff6|0009|0000|0028|000a|
|11|xor r5, r2|000a|0020|0014|fff6|0009|0014|0028|000a|
|12|and r3, r2|000a|0020|0014|0014|0009|0014|0028|000a|
|13|or r2, r1|000a|0020|0034|0014|0009|0014|0028|000a|
**テストベンチ [#p1d92796]
今回のテストベンチで使っているtaskは全部で6つあります。
-set_memory_data~
メモリに初期値をセットします。データの中身はmemory_data.memに定義されています。initialize内で呼び出されています。~
~
使用例)
set_memory_data;
-initialize~
リセット処理及びメモリの初期化を行います。テストベンチの最初に一度だけ使用します。~
~
使用例)
initialize;
-ifetch~
命令フェッチを行います。executeタスクで使用しています。~
~
使用例)
ifetch;
-execute~
命令の実行を行います。executeタスクで使用しています。~
~
使用例)
execute;
-display_regfile~
レジスタファイルの中身を表示します。executeタスクで使用しています。~
~
使用例)
display_regfile;
-execute~
1つの引数を指定します。実行するプログラムの命令数をここでは入力します。~
~
使用例)
execute(20);
**テストプログラム [#h6e06186]
テストプログラムはex05/sample/sample.sとして記述します。~
今回使用するプログラムは適当な命令を並べただけの簡単なプログラムです。
ori r0, 10
xori r1, 0x20
andi r2, 0x10
addi r2, 30
subi r2, 10
sr r3, r2
sl r6, r2
add r7, r3
sub r3, r2
not r4, r3
xor r5, r2
and r3, r2
or r2, r1
この内容は自由に変更してもらって構いません。~
プログラムを変更した後は、テストベンチのexecuteタスクに命令数を指定して、シミュレーションを行ってください。
*ヒントと豆知識 [#o2cd2c7e]
ここでは、今回の演習に関わる豆知識やヒントを紹介します。
**制御出力 [#w85bec99]
出力に対して必ず0か1の信号を与えたいとき、制御端子を利用すると簡単に記述できます。~
例えば、次のような入力と出力を持つ回路を記述するときを考えてみましょう。
|入力|>|>|>|出力|
||first|second|third|fourth|
|00|1|0|0|0|
|01|0|1|0|0|
|10|0|0|1|0|
|11|0|0|0|1|
通常の出力端子では、以下のように記述します。
instrin decode
output first, second, third, fourth;
instruct decode any {
func == 0b00 : par { first = 0b1; second = 0b0; third = 0b0; fourth = 0b0 }
func == 0b01 : par { first = 0b0; second = 0b1; third = 0b0; fourth = 0b0 }
func == 0b10 : par { first = 0b0; second = 0b0; third = 0b1; fourth = 0b0 }
func == 0b11 : par { first = 0b0; second = 0b0; third = 0b0; fourth = 0b1 }
}
この方法だと、必ず全ての信号に入力を与える必要があります。~
制御出力を仕様すると、以下のように記述できます。
instrin decode;
instrout first, second, third, fourth;
instruct decode any {
func == 0b00 : first();
func == 0b01 : second();
func == 0b10 : third();
func == 0b11 : fourth();
}
今回の設計課題では、制御出力を用いる記述を使って命令デコーダを設計してください。
**符号拡張 [#x666043e]
例えば4ビットで-3を表すと、0b1101になります。これを8ビットのデータ0b00001101として扱うと、これは13を表すビット列になってしまいます。~
正しくは0b11111101としなければなりません。これは拡張するビット分を符号ビットで並べることで正しい拡張ができます。~
符号付きの整数値を扱うとき、符号ビットを考慮して、ビット幅を拡張しなければなりません。そこで必要なのが符号拡張になります。
SFLでの例を紹介します。32ビットの信号がin_dataとして定義されているときに、下位16ビットを符号拡張して32ビットにしたいときは、以下のように記述します。
32#in_data<15:0>;
**ゼロ拡張 [#p3db468e]
ゼロ拡張は、符号拡張とは異なり、拡張する際に必ず0を並べる拡張方法です。
32ビットの信号がin_dataとして定義されているときに、下位16ビットをゼロ拡張して32ビットにしたいときは、以下のように記述します。
16#0b0 || in_data<15:0>;
または、以下のような書き方もできます。
0x0000 || in_data<15:0>;
終了行:
[[SFLで作るPICO-16プロセッサ]]
*First PICO-16プロセッサのデータパスの設計 [#l2672d44]
ALUを使用した演算を行う基本的な命令セットを備えたプロセッサを設計します。~
2サイクルで動作するFirst PICO-16プロセッサの仕組みを考えてみましょう。
**コンテンツ [#n17a3f6e]
#contents
*First PICO-16プロセッサの構成と解説 [#ee770e29]
基本的なALUで行う演算の命令を備えたプロセッサをFirst PICO-16プロセッサとして解説します。
**2つのALUで実現する回路構成 [#i31b1f97]
[[第3回>SFLで作るPICO-16プロセッサ/第3回]]で設計した命令フェッチ回路と[[第4回>SFLで作るPICO-16プロセッサ/第4回]]で設計した実行ユニットを繋げれば、命令メモリに格納されている命令を実行していくプロセッサが完成します。~
プロセッサの全体の回路構成を図に表すと以下のようになります。ただし、今回は新たに命令コードを解析する命令デコーダを挟んであります。
CENTER:&ref(SFLで作るPICO-16プロセッサ/第5回/fpico16_rev1.png,80%);
ここで注目して欲しいのは、この回路では2つのALUを使用していることです。~
プログラムカウンタのインクリメントのためには、ALUの加算の機能しか使わないので、せっかく実装してあるALUの他の機能が無駄になってしまいます。~
解決方法として、左側のALUを加算の機能だけを持つ回路である加算器に置き換えてしまう考え方があります。~
ただし今回は、プロセッサの動作に必要な演算を1つのALUで全て行う回路構成を考えてみましょう。
**1つのALUで実現する回路構成 [#jde89aae]
命令メモリから読み込んだ命令コードを命令レジスタ(Instruction Register)に格納します。~
また、ALUに入力する信号を選択できるように設計を変更します。SFL記述では、ALUを使用するタイミングの制御を行う必要があります。~
本来はステートマシンを用いて行いますが、今回は回路の動作を2つのステップに分けて実行することでALUを使用するタイミングを制御します。
CENTER:&ref(SFLで作るPICO-16プロセッサ/第5回/fpico16_rev2.png,80%);
**動作 [#tb163b14]
ALUを2回に分けて使用するため、次の2つのステップに分けて命令を実行します。
***命令フェッチの動作 [#r1cec17a]
命令フェッチでは2つの動作を行います。
-プログラムカウンタの値をアドレスとして、命令メモリから命令コードを読み出し、命令レジスタに書き込む。
-ALUでプログラムカウンタをインクリメントし、プログラムカウンタの値を更新する。
CENTER:&ref(SFLで作るPICO-16プロセッサ/第5回/fpico16_if.png,80%);
***実行の動作 [#ja700189]
実行では命令レジスタの値を使用して、次の4つの動作を行います。
-命令レジスタをデコードする。
-レジスタファイルから使用する汎用レジスタの値を読み出す。
-命令に対応した演算をALUで行う。
-ALUでの演算結果をレジスタファイルの指定した汎用レジスタへ書き込む。
CENTER:&ref(SFLで作るPICO-16プロセッサ/第5回/fpico16_ex.png,80%);
***波形 [#pb400633]
命令の実行様子を波形にすると以下のようになります。
CENTER:&ref(fpico16_wave.png);
inst_fetchが1のとき、プログラムカウンタ(pc)と命令レジスタ(ir)が更新され、~
executeが1のとき、レジスタファイルの汎用レジスタ(r0-r7)が更新される様子が分かります。
ここでは省略していますが、クロック信号の立ち上がり時にデータの更新が行われます。
*仕様 [#h8068000]
今回設計する回路の仕様は3つに分けて説明します。
**命令デコーダ [#wd693381]
命令コードから、使用する汎用レジスタアドレス、書き込む汎用レジスタアドレス、即値、ALUの機能などを決定します。
CENTER:&ref(SFLで作るPICO-16プロセッサ/第5回/inst_decoder.png,75%);
ただし、PICO-16プロセッサの命令セットの特性から、命令コード16ビットのうち、<10:8>を読み出しレジスタ兼書き込みレジスタ、<7:5>を読み出しレジスタ、<7:0>を8ビットの即値として扱うので、これらの接続に関しては直接命令コードからビットを切り出して接続します。
この命令デコーダは、命令コードの<15:11>の5ビットが示す命令の種類と、<4:0>が示す機能コードから、ALUの制御信号を解析します。~
命令コードは仕様で決められているので、命令の種類と機能コードから使用するALUの演算を決定します。
***入出力 [#je5f381d]
-入力
--命令コード(16ビット)
-出力(制御出力)
--ALU制御信号(AND, OR, XOR, NOT, ADD, SUB, SHL, SHR)
***機能 [#s22b8a6c]
-デコード
--命令コードから実行する命令を解析し、使用するALU制御信号を決定する。
--ALU制御信号は[[制御出力>#w85bec99]]として宣言する。
***命令セット [#q90f1339]
First PICO-16プロセッサでは次の命令をサポートします。
| 命令 | 意味 |>|>|>| 命令コード | 記述例 | 意味 |
| | | <15:11> | <10:8> | <7:5> | <4:0> | | |
| AND | And | 0 | rd | rs | 2 | and r1, r2 | r1 <= r1 & r2 |
| OR | Or | 0 | rd | rs | 3 | or r1, r2 | r1 <= r1 | r2 |
| XOR | Exclusive or | 0 | rd | rs | 4 | xor r1, r2 | r1 <= r1 @ r2 |
| NOT | Not | 0 | rd | rs | 5 | not r1, r2 | r1 <= ^r2 |
| ADD | Add | 0 | rd | rs | 6 | add r1, r2 | r1 <= r1 + r2 |
| SUB | Subtract | 0 | rd | rs | 7 | sub r1, r2 | r1 <= r1 - r2 |
| SL | Shift left | 0 | rd | rs | 12 | sl r1, r2 | r1 <= r2 << 1 |
| SR | Shift right | 0 | rd | rs | 13 | sr r1, r2 | r1 <= r2 >> 1 |
| ANDI | And immediate | 2 | rd |>| immediate | andi r1, 10 | r1 <= r1 & 10 |
| ORI | Or immediate | 3 | rd |>| immediate | ori r1, 10 | r1 <= r1 | 10 |
| XORI | Exclusive or immediate | 4 | rd |>| immediate | xori r1, 10 | r1 <= r1 @ 10 |
| ADDI | Add immediate | 6 | rd |>| immediate | addi r1, 10 | r1 <= r1 + 10 |
| SUBI | Subtract immediate | 7 | rd |>| immediate | subi r1, 10 | r1 <= r1 - 10 |
rdはregister destinationの略で、書き込みレジスタを示しています。~
rsはregister sourceの略で、読み出しレジスタを示しています。~
PICO-16では基本的にrd = rd + rsといった演算を行います。
**データパス [#x54e503f]
[[First PICO-16プロセッサの構成と解説>#ee770e29]]の[[1つのALUで実現する回路構成>#jde89aae]]で解説した部分を設計します。
***入出力 [#l7e29a23]
制御入力のみになります。
***内部レジスタ [#v17ec8be]
-プログラムカウンタ(16ビット)
--実行する命令のアドレスを保持するレジスタ。
--リセット時に0に初期化する。
-命令レジスタ(16ビット)
--実行する命令の命令コードを保持するレジスタ。
***サブモジュール [#p7b7a736]
-ALU
--[[第1回>SFLで作るPICO-16プロセッサ/第1回]]で設計したALU。
-命令メモリ
--[[第3回>SFLで作るPICO-16プロセッサ/第3回]]で設計した命令メモリ。
--サブモジュールの名前はimemとする。
-レジスタファイル
--[[第2回>SFLで作るPICO-16プロセッサ/第2回]]で設計したレジスタファイル。
--サブモジュールの名前はrfとする。
-命令デコーダ
***機能 [#r35eeb5a]
-プログラムカウンタのインクリメント
--ALUのADD機能を使用してプログラムカウンタをインクリメント(+1)する。
-メモリ内容の読み出し
--プログラムカウンタを命令メモリのアドレスとして、命令メモリからのデータを出力する。
-デコードを実行
--命令デコーダを使用して、命令に応じたALUの機能を決定する。
-使用する汎用レジスタの読み出し
--レジスタファイルから使用する汎用レジスタの値を読み出す。
-命令の実行
--命令デコーダの結果に応じたALUの機能を用いて演算を行う。
--即値を扱う命令は、即値に対して[[符号拡張>#x666043e]]もしくは[[ゼロ拡張>#p3db468e]]を行い、16ビットのデータとして扱う。
---符号拡張 : ADDI, SUBI
---ゼロ拡張 : ANDI, ORI, XORI
-ALUでの演算結果を使用したライトバック
--ALUの結果を使用してレジスタファイルの汎用レジスタの値を更新する。
**全体 [#g9dcde8c]
命令フェッチと実行の2つの制御信号を持ち、それぞれの動作に必要なデータパスの制御信号を操作する回路を設計します。
***入出力 [#m79eca18]
制御入力のみになります。
***サブモジュール [#hf74bfbd]
-データパス
--サブモジュールの名前はdpとする。
***機能 [#p1fa7e7c]
-命令フェッチ
--プログラムカウンタのインクリメントを実行する。
--メモリ内容の読み出しを実行する。
-実行
--デコードを実行する。
--使用する汎用レジスタの読み出しを実行する。
--命令の実行を行う。
--ALUでの演算結果を使用したライトバックを実行する。
*実装 [#w40baf9e]
実装も3つに分けて説明します。
**命令デコーダ [#z2407ab2]
ファイル名はinst_decoder.sfl及び、inst_decoder.hとします。
***ヘッダ [#oe7ab1a2]
declare inst_decoder {
input opcode<16>;
instrin decode;
instrout AND, OR, XOR, NOT, ADD, SUB, SL, SR;
instrout ANDI, ORI, XORI, ADDI, SUBI;
instr_arg decode(opcode);
}
***モジュール [#z91a9140]
以下の手順に従って記述してください。
/* OPコードのDEFINE */
%d OP_RTYPE 0b00000
%d OP_ANDI 0b00010
%d OP_ORI 0b00011
%d OP_XORI 0b00100
%d OP_ADDI 0b00110
%d OP_SUBI 0b00111
/* RTYPEで使用するFUNCコードのDEFINE */
%d FUNC_AND 0b00010
%d FUNC_OR 0b00011
%d FUNC_XOR 0b00100
%d FUNC_NOT 0b00101
%d FUNC_ADD 0b00110
%d FUNC_SUB 0b00111
%d FUNC_SL 0b01100
%d FUNC_SR 0b01101
module inst_decoder {
/* 1. 入出力信号、制御入出力信号を記述 */
/* 2. 制御入力に対する動作を記述 */
}
**データパス [#b732fcc6]
ファイル名はdatapath.sfl及び、datapath.hとします。
***ヘッダ [#s7602757]
declare datapath {
/* 命令フェッチステートの制御 */
instrin inc_pc;
instrin imem_read;
/* 実行ステートの制御 */
instrin decode;
instrin reg_fetch;
instrin execute;
instrin wb_alu;
}
***モジュール [#p0ba7314]
以下の手順に従って記述してください。
/* 1. サブモジュールとして使用するモジュールのヘッダをインクルード */
module datapath {
/* 2. 入出力信号、制御入出力信号を記述 */
/* 3. 内部レジスタ宣言 */
/* 4. サブモジュール宣言 */
/* 5. 制御入力に対する動作を記述 */
}
**全体 [#t85b523c]
ファイル名はfpico16.sfl及び、fpico16.hとします。
***ヘッダ [#oe7ab1a2]
declare fpico16 {
instrin inst_fetch;
instrin execute;
}
***モジュール [#z91a9140]
以下の手順に従って記述してください。
/* 1. サブモジュールとして使用するモジュールのヘッダをインクルード */
module fpico16 {
/* 2. 入出力信号、制御入出力信号を記述 */
/* 3. サブモジュール宣言 */
/* 4. 制御入力に対する動作を記述 */
}
*論理シミュレーション [#da55b9ac]
回路が正しく動作しているかどうかを確認します。
+&ref(ex05.tar.gz);をダウンロードする。~
~
解凍方法)
% tar zxvf ex05.tar.gz
+inst_decoder.h、inst_decoder.sfl、datapath.h、datapath.sfl、fpico16.h、fpico16.sflを作成し、ex05ディレクトリに入れる。
--inst_decoder.hは、[[ここ>#oe7ab1a2]]から内容をコピーする。
--inst_decoder.sflは、[[ここ>#z91a9140]]から内容をコピーして、記述を完成させる。
--datapath.hは、[[ここ>#s7602757]]から内容をコピーする。
--datapath.sflは、[[ここ>#p0ba7314]]から内容をコピーして、記述を完成させる。
--fpico16.hは、[[ここ>#oe7ab1a2]]から内容をコピーする。
--fpico16.sflは、[[ここ>#z91a9140]]から内容をコピーして、記述を完成させる。
+[[第1回>SFLで作るPICO-16プロセッサ/第1回]]で作成したalu.h、alu.sflをex05ディレクトリにコピーする。
+[[第2回>SFLで作るPICO-16プロセッサ/第2回]]で作成したregfile.h、regfile.sflをex05ディレクトリにコピーする。
+[[第3回>SFLで作るPICO-16プロセッサ/第3回]]で作成したinst_memory.h、inst_memory.sflをex05ディレクトリにコピーする。
+シミュレーションを行う。
--シミュレーションには次のコマンドを使います。
% make
もしくは、
% make sim
--シミュレーションの結果はsim.logに出力されます。正しく動作していると&ref(sim.log.txt);のようになります。
--余裕があればテストベンチを自由に改変してみましょう。
+波形を観察する。
--波形ビューワの起動には次のコマンドを使います。
% make wave
--
+ファイルをクリーンする。
--ファイルのクリーンには次のコマンドを使います。
% make clean
--シミュレーションや波形ビューワで生成されたファイルのサイズが大きいときもあるので、全て終わったら余計なファイルを削除しておきましょう。
**シミュレーション結果 [#r4571f28]
正しく動作していれば、次のような結果が出ます。
|ステップ|命令|r0|r1|r2|r3|r4|r5|r6|r7|
|1|ori r0, 10|000a|0000|0000|0000|0000|0000|0000|0000|
|2|xori r1, 0x20|000a|0020|0000|0000|0000|0000|0000|0000|
|3|andi r2, 0x10|000a|0020|0000|0000|0000|0000|0000|0000|
|4|addi r2, 30|000a|0020|001e|0000|0000|0000|0000|0000|
|5|subi r2, 10|000a|0020|0014|0000|0000|0000|0000|0000|
|6|sr r3, r2|000a|0020|0014|000a|0000|0000|0000|0000|
|7|sl r6, r2|000a|0020|0014|000a|0000|0000|0028|0000|
|8|add r7, r3|000a|0020|0014|000a|0000|0000|0028|000a|
|9|sub r3, r2|000a|0020|0014|fff6|0000|0000|0028|000a|
|10|not r4, r3|000a|0020|0014|fff6|0009|0000|0028|000a|
|11|xor r5, r2|000a|0020|0014|fff6|0009|0014|0028|000a|
|12|and r3, r2|000a|0020|0014|0014|0009|0014|0028|000a|
|13|or r2, r1|000a|0020|0034|0014|0009|0014|0028|000a|
**テストベンチ [#p1d92796]
今回のテストベンチで使っているtaskは全部で6つあります。
-set_memory_data~
メモリに初期値をセットします。データの中身はmemory_data.memに定義されています。initialize内で呼び出されています。~
~
使用例)
set_memory_data;
-initialize~
リセット処理及びメモリの初期化を行います。テストベンチの最初に一度だけ使用します。~
~
使用例)
initialize;
-ifetch~
命令フェッチを行います。executeタスクで使用しています。~
~
使用例)
ifetch;
-execute~
命令の実行を行います。executeタスクで使用しています。~
~
使用例)
execute;
-display_regfile~
レジスタファイルの中身を表示します。executeタスクで使用しています。~
~
使用例)
display_regfile;
-execute~
1つの引数を指定します。実行するプログラムの命令数をここでは入力します。~
~
使用例)
execute(20);
**テストプログラム [#h6e06186]
テストプログラムはex05/sample/sample.sとして記述します。~
今回使用するプログラムは適当な命令を並べただけの簡単なプログラムです。
ori r0, 10
xori r1, 0x20
andi r2, 0x10
addi r2, 30
subi r2, 10
sr r3, r2
sl r6, r2
add r7, r3
sub r3, r2
not r4, r3
xor r5, r2
and r3, r2
or r2, r1
この内容は自由に変更してもらって構いません。~
プログラムを変更した後は、テストベンチのexecuteタスクに命令数を指定して、シミュレーションを行ってください。
*ヒントと豆知識 [#o2cd2c7e]
ここでは、今回の演習に関わる豆知識やヒントを紹介します。
**制御出力 [#w85bec99]
出力に対して必ず0か1の信号を与えたいとき、制御端子を利用すると簡単に記述できます。~
例えば、次のような入力と出力を持つ回路を記述するときを考えてみましょう。
|入力|>|>|>|出力|
||first|second|third|fourth|
|00|1|0|0|0|
|01|0|1|0|0|
|10|0|0|1|0|
|11|0|0|0|1|
通常の出力端子では、以下のように記述します。
instrin decode
output first, second, third, fourth;
instruct decode any {
func == 0b00 : par { first = 0b1; second = 0b0; third = 0b0; fourth = 0b0 }
func == 0b01 : par { first = 0b0; second = 0b1; third = 0b0; fourth = 0b0 }
func == 0b10 : par { first = 0b0; second = 0b0; third = 0b1; fourth = 0b0 }
func == 0b11 : par { first = 0b0; second = 0b0; third = 0b0; fourth = 0b1 }
}
この方法だと、必ず全ての信号に入力を与える必要があります。~
制御出力を仕様すると、以下のように記述できます。
instrin decode;
instrout first, second, third, fourth;
instruct decode any {
func == 0b00 : first();
func == 0b01 : second();
func == 0b10 : third();
func == 0b11 : fourth();
}
今回の設計課題では、制御出力を用いる記述を使って命令デコーダを設計してください。
**符号拡張 [#x666043e]
例えば4ビットで-3を表すと、0b1101になります。これを8ビットのデータ0b00001101として扱うと、これは13を表すビット列になってしまいます。~
正しくは0b11111101としなければなりません。これは拡張するビット分を符号ビットで並べることで正しい拡張ができます。~
符号付きの整数値を扱うとき、符号ビットを考慮して、ビット幅を拡張しなければなりません。そこで必要なのが符号拡張になります。
SFLでの例を紹介します。32ビットの信号がin_dataとして定義されているときに、下位16ビットを符号拡張して32ビットにしたいときは、以下のように記述します。
32#in_data<15:0>;
**ゼロ拡張 [#p3db468e]
ゼロ拡張は、符号拡張とは異なり、拡張する際に必ず0を並べる拡張方法です。
32ビットの信号がin_dataとして定義されているときに、下位16ビットをゼロ拡張して32ビットにしたいときは、以下のように記述します。
16#0b0 || in_data<15:0>;
または、以下のような書き方もできます。
0x0000 || in_data<15:0>;
ページ名: