0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

概要 

windowsでiverilogやってみた。
8bit CPU、書いてみた。
命令を拡張してみた。

命令一覧

基本命令一覧 (上位4ビット op で識別)

プログラムコード(12ビット)の上位4ビット(code[11:8])で判別される基本命令です。

オペコード (op) 命令名 引数 (opland) スタック変化 動作説明
4'h0 HALT なし なし プログラムの実行を停止します(PCをその場に固定)。
4'h2 PUSH 8ビット数値 [ → opland ] 指定した8ビットの即値(opland)をスタックに積みます。
4'h3 ADR 6ビットアドレス なし ジャンプ先のアドレスレジスタ(adr)に値をセットします。
4'h4 JEQ なし [ q[0] → ] 条件付きジャンプ: もし q[0] が 1以外 なら pc <= adr へジャンプします。q[0] が 1 なら次の命令に進みます。実行後、q[0] はポップされて消滅します。
4'h5 OUT なし [ q[0] → ] スタックトップの q[0] の値を外部出力ポート(outport)に書き出し、ポップします。
4'h8 DUP なし [ → q[0] ] スタックトップの値を複製して積みます(※4'hF の 8'h20 と同等)。
4'h9 DROP なし [ q[0] → ] スタックトップの値を1つ破棄します(※4'hF の 8'h21 と同等)。
4'hF EXT (下位を参照) 下位に依存 拡張命令モード。下位8ビット(opland)で具体的な演算を指定します。

op = 4'h4 の挙動は opland に応じて以下のように変化します。

オペコード (op) 拡張機能 (opland) 命令名 スタック変化 動作説明
4'h4 8'h00 IF0 [ q → ] 0ならジャンプ: q が 0 であれば pc <= adr へジャンプします。そうでなければ次の命令へ。
4'h4 8'h01 IF1 [ q → ] 1ならジャンプ: q が 1 であれば pc <= adr へジャンプします。そうでなければ次の命令へ。
4'h4 8'h0F jmp [ q → ] ジャンプ: 無条件で、 pc <= adr へジャンプします。

2. 拡張演算・システム命令一覧 (op = 4'hF 時の下位8ビット)

op が 4'hF のとき、下位8ビット(code[7:0])の値によって以下の演算やI/O処理を行います。
算術・論理演算命令 (実行後に必ず1回ポップされるため、2引数を消費して1結果を残す)

opland 命令名 算術式 / 動作 スタック変化 (実行前 → 実行後)
8'h00 + 加算 [ q[1], q[0] ] → [ (q[1] + q[0]) ]
8'h01 - 減算 [ q[1], q[0] ] → [ (q[1] - q[0]) ]
8'h02 * 乗算 [ q[1], q[0] ] → [ (q[1] * q[0]) ]
8'h03 / 除算(0除算時は0) [ q[1], q[0] ] → [ (q[1] / q[0]) ]
8'h04 mod 剰余(0除算時は0) [ q[1], q[0] ] → [ (q[1] % q[0]) ]
8'h0F = 比較(等しい) [ q[1], q[0] ] → [ 等しければ1, 違えば0 ]
8'h10 < 比較(より小さい) [ q[1], q[0] ] → [ q[1] < q[0] なら1, 違えば0 ]
8'h11 > 比較(より大きい) [ q[1], q[0] ] → [ q[1] > q[0] なら1, 違えば0 ]
8'h12 max 最大値選択 [ q[1], q[0] ] → [ 大きい方の値 ]
8'h13 min 最小値選択 [ q[1], q[0] ] → [ 小さい方の値 ]
8'h14 and ビット論理積 [ q[1], q[0] ] → [ (q[1] & q[0]) ]
8'h15 or ビット論理和 [ q[1], q[0] ] → [ (q[1]
8'h16 invert ビット反転 (※ポップなし) [ q[0] ] → [ ~q[0] ]

スタック操作命令 (Forth互換ワード)

opland 命令名 動作説明 スタック変化 (実行前 → 実行後)
8'h20 dup スタックトップを複製してプッシュ。 [ q[0] ] → [ q[0], q[0] ]
8'h21 drop スタックトップを1つ削除。 [ q[0] ] → [ ]
8'h22 swap 上位2つの値を入れ替える。 [ q[1], q[0] ] → [ q[0], q[1] ]
8'h23 over 上から2番目の値を複製して一番上に積む。 [ q[1], q[0] ] → [ q[1], q[0], q[1] ]
8'h24 rot 上位3つの値を回転させる (q[0]がq[1]へ、q[1]がq[2]へ、q[2]がq[0]へ)。 [ q[2], q[1], q[0] ] → [ q[1], q[0], q[2] ]

コンソール出力・デバッグ命令 (シミュレータ表示用)

opland 命令名 動作説明 スタック変化
8'h30 . q[0] の値を「数値(10進数)」としてコンソールに表示し、ポップ。 [ q[0] → ]
8'h31 emit q[0] の値を「ASCII文字」としてコンソールに表示し、ポップ。 [ q[0] → ]
8'h32 cr 改行(システムに \n)を出力。 なし
8'h33 space 半角スペース(" ")を出力。 なし
8'h34 .s デバッグ用。現在のスタックの中身(q[0]?q[5])を一覧表示。 なし

特殊命令

opland 命令名 動作説明 スタック変化
8'h40 random Verilogの $random 関数を用いて、0?255のランダムな数値をスタックに積む。 [ → 乱数 ]

サンプルコード


module cpu8(input clk, input rst, output [7:0] outport);
	integer i;
	reg [7:0] q [0:5];
	reg [11:0] rom [0:127];
	reg [6:0] pc;
	reg [6:0] adr;
	reg [7:0] out;
	wire [11:0] code = rom[pc];
	wire [3:0] op = code[11:8];
	wire [7:0] opland = code[7:0];
	assign outport = out;
	initial
	begin
		//$monitor(" pc: %d top: %d next: %d adr: %d", pc, q[0], q[1], adr);
	end
	always @(posedge clk)
	begin
		if (rst)
		begin
			rom[0] <= 12'h0_00; // HALT

			for (i = 0; i < 6; i = i + 1)
				q[i] <= 0;
			adr <= 0;
			out <= 0;
			pc <= 0;
		end
		else
		begin
			case (op)
			4'h0: // HALT
				pc <= pc;
			4'h2: // PUSH
			begin
				q[5] <= q[4]; q[4] <= q[3]; q[3] <= q[2]; q[2] <= q[1]; q[1] <= q[0];
				q[0] <= opland;
				pc <= pc + 1;
			end
			4'h3: // ADR
			begin
				adr <= opland[5:0];
				pc <= pc + 1;
			end
			4'h4: // IF (ジャンプ命令)
			begin
				case (opland)
				8'h00: // JEQ (topが0ならジャンプ)
				begin
					if (q[0] == 8'd0)
						pc <= adr;
					else
						pc <= pc + 1;
					q[0] <= q[1]; 
					q[1] <= q[2]; q[2] <= q[3]; q[3] <= q[4]; q[4] <= q[5]; q[5] <= 8'd0;
				end
				8'h01: // JNE (topが1ならジャンプ)
				begin
					if (q[0] == 8'd1)
						pc <= adr;
					else
						pc <= pc + 1;
					q[0] <= q[1]; 
					q[1] <= q[2]; q[2] <= q[3]; q[3] <= q[4]; q[4] <= q[5]; q[5] <= 8'd0;
				end
				8'hFF: // JMP (無条件ジャンプ)
				begin
					pc <= adr;
				end
				default:
					pc <= pc + 1;
				endcase
			end
			4'h5: // OUT
			begin
				out <= q[0];
				q[0] <= q[1]; 
				q[1] <= q[2]; q[2] <= q[3]; q[3] <= q[4]; q[4] <= q[5]; q[5] <= 8'd0;
				pc <= pc + 1;
			end
			4'h8: // DUP
			begin
				q[5] <= q[4]; q[4] <= q[3]; q[3] <= q[2]; q[2] <= q[1]; q[1] <= q[0];
				q[0] <= q[0];
				pc <= pc + 1;
			end
			4'h9: // DROP
			begin
				q[0] <= q[1]; 
				q[1] <= q[2]; q[2] <= q[3]; q[3] <= q[4]; q[4] <= q[5]; q[5] <= 8'd0;
				pc <= pc + 1;
			end
			4'hf: // EXTENDED
			begin
				case (opland)
				// --- 2つの引数を消費して、計算結果を新q[0]に格納する正しい演算処理 ---
				8'h00: // +
				begin
					q[0] <= q[1] + q[0];
					q[1] <= q[2]; q[2] <= q[3]; q[3] <= q[4]; q[4] <= q[5]; q[5] <= 8'd0;
				end
				8'h01: // -
				begin
					q[0] <= q[1] - q[0];
					q[1] <= q[2]; q[2] <= q[3]; q[3] <= q[4]; q[4] <= q[5]; q[5] <= 8'd0;
				end
				8'h02: // *
				begin
					q[0] <= q[1] * q[0];
					q[1] <= q[2]; q[2] <= q[3]; q[3] <= q[4]; q[4] <= q[5]; q[5] <= 8'd0;
				end
				8'h03: // /
				begin
					q[0] <= (q[0] != 0) ? q[1] / q[0] : 8'd0;
					q[1] <= q[2]; q[2] <= q[3]; q[3] <= q[4]; q[4] <= q[5]; q[5] <= 8'd0;
				end
				8'h04: // mod
				begin
					q[0] <= (q[0] != 0) ? q[1] % q[0] : 8'd0;
					q[1] <= q[2]; q[2] <= q[3]; q[3] <= q[4]; q[4] <= q[5]; q[5] <= 8'd0;
				end
				8'h0F: // =
				begin
					q[0] <= (q[1] == q[0]) ? 8'd1 : 8'd0;
					q[1] <= q[2]; q[2] <= q[3]; q[3] <= q[4]; q[4] <= q[5]; q[5] <= 8'd0;
				end
				8'h10: // <
				begin
					q[0] <= (q[1] < q[0])  ? 8'd1 : 8'd0;
					q[1] <= q[2]; q[2] <= q[3]; q[3] <= q[4]; q[4] <= q[5]; q[5] <= 8'd0;
				end
				8'h11: // >
				begin
					q[0] <= (q[1] > q[0])  ? 8'd1 : 8'd0;
					q[1] <= q[2]; q[2] <= q[3]; q[3] <= q[4]; q[4] <= q[5]; q[5] <= 8'd0;
				end
				8'h12: // max
				begin
					q[0] <= (q[0] > q[1]) ? q[0] : q[1];
					q[1] <= q[2]; q[2] <= q[3]; q[3] <= q[4]; q[4] <= q[5]; q[5] <= 8'd0;
				end
				8'h13: // min
				begin
					q[0] <= (q[0] < q[1]) ? q[0] : q[1];
					q[1] <= q[2]; q[2] <= q[3]; q[3] <= q[4]; q[4] <= q[5]; q[5] <= 8'd0;
				end
				8'h14: // and
				begin
					q[0] <= q[1] & q[0];
					q[1] <= q[2]; q[2] <= q[3]; q[3] <= q[4]; q[4] <= q[5]; q[5] <= 8'd0;
				end
				8'h15: // or
				begin
					q[0] <= q[1] | q[0];
					q[1] <= q[2]; q[2] <= q[3]; q[3] <= q[4]; q[4] <= q[5]; q[5] <= 8'd0;
				end
				8'h16: // invert
				begin
					q[0] <= ~q[0];
				end
				8'h20: // dup
				begin
					q[5] <= q[4]; q[4] <= q[3]; q[3] <= q[2]; q[2] <= q[1]; q[1] <= q[0]; 
					q[0] <= q[0];
				end
				8'h21: // drop
				begin
					q[0] <= q[1];
					q[1] <= q[2]; q[2] <= q[3]; q[3] <= q[4]; q[4] <= q[5]; q[5] <= 8'd0;
				end
				8'h22: // swap
				begin
					q[0] <= q[1];
					q[1] <= q[0];
				end
				8'h23: // over
				begin
					q[5] <= q[4]; q[4] <= q[3]; q[3] <= q[2]; q[2] <= q[1]; q[1] <= q[0]; 
					q[0] <= q[1];
				end
				8'h24: // rot
				begin
					q[0] <= q[2];
					q[1] <= q[0];
					q[2] <= q[1];
				end
				8'h30: // .
				begin
					$write("%d ", q[0]);
					q[0] <= q[1]; 
					q[1] <= q[2]; q[2] <= q[3]; q[3] <= q[4]; q[4] <= q[5]; q[5] <= 8'd0;
				end
				8'h31: // emit
				begin
					$write("%c", q[0]);
					q[0] <= q[1]; 
					q[1] <= q[2]; q[2] <= q[3]; q[3] <= q[4]; q[4] <= q[5]; q[5] <= 8'd0;
				end
				8'h32: // cr
				begin
					$display("");
				end
				8'h33: // space
				begin
					$write(" ");
				end
				default: ;
				endcase
				pc <= pc + 1;
			end
			default:
				pc <= pc + 1;
			endcase
		end
	end
endmodule




以上。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?