概要
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
以上。