概要
windowsでiverilogやってみた。
8bit CPU、書いてみた。
8bitCPUで、何ができる。
1-100まで、数えることができる。
8bit CPUの仕様
stackマシーン (registerマシーンではない)
stackは、8bitで5個搭載
命令は、12bit
ROMは、32個
RAMは、0個
アドレスレジスタ 8bit 1個
プログラムカウンタ(pc) 5bit
命令一覧
RAMアクセス->無し
フラグ->無し
| ニーモニック | 動作 | 機械語 |
|---|---|---|
| end | 停止 | 00 |
| push x | スタックにxを積む。 | 2xx |
| adr x | アドレスレジスタにxを積む。 | 300 |
| if1 | スタックTOPが1でなければ、jmp | 400 |
| out | スタックTOPを出力。 | 500 |
| dup | スタックTOPを複写。 | 800 |
| drop | スタックTOPを廃棄。 | 900 |
| + | スタックTOPをNEXTで加算。 | F00 |
| - | スタックTOPをNEXTで減算。 | F01 |
| == | スタックTOPをNEXTで同じなら1 | F0F |
サンプルコード
module cpu8(input clk, input rst, output [7:0] outport);
integer i;
reg [7:0] q[0:5];
reg [11:0] rom[0:15];
reg [4:0] pc;
reg [7:0] adr,
out;
wire [11:0] code = rom[pc];
wire [3:0] op = code[11:8];
wire [7:0] opland = code[7:0];
assign outport = out;
task push;
begin
for (i = 1; i < 6; i = i + 1)
q[i] <= q[i - 1];
end
endtask
task pop;
begin
for (i = 1; i < 5; i = i + 1)
q[i] <= q[i + 1];
end
endtask
initial
begin
//$monitor(" pc: %d op: %h top: %d next: %d thd: %d adr: %d", pc, op, q[0], q[1], q[2], adr);
end
always @(posedge clk)
begin
if (rst)
begin
rom[0] <= 12'h2_00; // PUSH 0 (カウンター初期値)
rom[1] <= 12'h3_02; // ADR 2 (戻るadrを積む)
rom[2] <= 12'h2_01; // PUSH 1
rom[3] <= 12'hF_00; // ADD (+
rom[4] <= 12'h8_00; // DUP (カウンターを複製)
rom[5] <= 12'h8_00; // DUP (カウンターを複製)
rom[6] <= 12'h5_00; // OUT (現在のカウントを出力、1つ消費)
rom[7] <= 12'h2_64; // PUSH 100
rom[8] <= 12'hF_0F; // EQ (== 100)
rom[9] <= 12'h4_00; // JEQ (if top != 1 then adr)
rom[10] <= 12'h0_00; // END
rom[11] <= 12'h0_00; //
rom[12] <= 12'h0_00; //
rom[13] <= 12'h0_00; //
rom[14] <= 12'h0_00; //
rom[15] <= 12'h0_00; //
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 (下8bit)
begin
push;
q[0] <= opland;
pc <= pc + 1;
end
4'h3: // ADR (下8bit)
begin
adr <= opland;
pc <= pc + 1;
end
4'h4: // IF1 (topが1でなければ、adrの値をPCにする)
begin
if (q[0] != 8'd1)
pc <= adr;
else
pc <= pc + 1;
q[0] <= q[1];
pop;
end
4'h5: // OUT
begin
out <= q[0];
pop;
pc <= pc + 1;
end
4'h8: // DUP
begin
push;
pc <= pc + 1;
end
4'h9: // DROP
begin
q[0] <= q[1];
pop;
pc <= pc + 1;
end
4'hf: // 演算
begin
case (opland)
8'h00: // ADD
begin
q[0] <= q[1] + q[0];
pop;
end
8'h01: // SUB
begin
q[0] <= q[1] - q[0];
pop;
end
8'h0f: // EQ
begin
if (q[0] == q[1])
q[0] <= 8'd1;
else
q[0] <= 8'd0;
pop;
end
endcase
pc <= pc + 1;
end
default:
pc <= pc + 1;
endcase
end
end
endmodule
テストベンチ
`timescale 1ns / 1ps
module test;
reg clk,
rst;
wire [7:0] outport;
cpu8 u(.clk(clk), .rst(rst), .outport(outport));
initial
begin
$dumpfile("test.vcd");
$dumpvars(0, test);
$monitor(" %d", outport);
clk = 0;
rst = 0;
#2
rst = 1;
#2
rst = 0;
#2000
$finish;
end
always
#1
clk = ~clk;
endmodule
投入したソースコード
PUSH 0 (カウンター初期値)
ADR 2 (戻るアドレスを積む)
PUSH 1 (カウントアップ用)
ADD (+ 1)
DUP (カウンターを複製)
DUP (カウンターを複製)
OUT (現在のカウントを出力、1つ消費)
PUSH 100
EQ (== 100)
IF1 (if top != 1 then adr)
END
実行結果
>vvp a.out
VCD info: dumpfile test.vcd opened for output.
x
0
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
stack7.v:161: $finish called at 2004000 (1ps)
以上。