概要
windowsでiverilogやってみた。
4bitCPUで、何ができる。
シリアルで送信できる。
4bit CPUの仕様
stackマシーン
stackは、4bitで5個搭載
命令は、8bit
ROMは、16個
RAMは、0個
退避レジスタ 4bit 1個
プログラムカウンタ(pc) 4bit
命令一覧
ジャンプ命令->無し
RAMアクセス->無し
フラグ->無し
| ニーモニック | 動作 | 機械語 |
|---|---|---|
| end | 停止 | 00 |
| sort5 | スタックに5個積んで、昇順にソートする。 | 10 |
| push x | スタックにxを積む。 | 2x |
| send | スタックTOPとNEXTを送信する。 | 30 |
| out | スタックTOPを出力。 | 50 |
| dup | スタックTOPを複写。 | 80 |
| drop | スタックTOPを廃棄。 | 90 |
| + | スタックTOPをNEXTで加算。 | F0 |
| - | スタックTOPをNEXTで減算。 | F1 |
| * | スタックTOPをNEXTで乗算。 | F2 |
| / | スタックTOPをNEXTで除算。 | F3 |
サンプルコード
module cpu4(input clk, input rst, output [3:0] outport);
parameter CLK_FREQ_HZ = 50_000_000;
parameter BAUD_RATE = 115_200;
localparam CLK_PER = 20;
localparam BIT_PERIOD = 1_000_000_000 / BAUD_RATE;
integer i,
j;
reg [3:0] q[0:5];
reg [7:0] rom[0:15];
reg [3:0] temp;
reg jpflg;
reg [3:0] pc;
reg [3:0] out;
wire [7:0] code;
wire [3:0] op;
wire [3:0] opland;
wire [3:0] qtop;
wire [3:0] qnext;
assign code = rom[pc];
assign op = code[7:4];
assign opland = code[3:0];
assign outport = out;
assign qtop = q[0];
assign qnext = q[1];
task send_serial_byte(input [7:0] data_byte);
begin
$display("send: %h '%c'", data_byte, data_byte);
out[0] = 1'b0;
#(BIT_PERIOD);
for (i = 0; i < 8; i = i + 1)
begin
out[0] = data_byte[i];
#(BIT_PERIOD);
end
out[0] = 1'b1;
#(BIT_PERIOD);
#(BIT_PERIOD * 2);
end
endtask
task push;
begin
for (i = 1; i < 6; i = i + 1)
q[i] <= q[i - 1];
end
endtask
task pop;
begin
for (i = 0; i < 5; i = i + 1)
q[i] <= q[i + 1];
end
endtask
initial
begin
//$monitor(" pc: %d top: %d next: %d out: %d", pc, qtop, qnext, out);
end
always @(posedge clk)
begin
if (rst)
begin
rom[0] <= 8'h2_5;
rom[1] <= 8'h2_5;
rom[2] <= 8'h3_0;
rom[3] <= 8'h0_0;
for (i = 0; i < 6; i = i + 1)
q[i] <= 0;
jpflg <= 0;
out <= 0;
pc <= 0;
end
else
begin
case (op)
4'h0://end
begin
jpflg = 1;
end
4'h1://sort5
begin
for (i = 0; i < 4; i = i + 1)
begin
for (j = 0; j < 4 - i; j = j + 1)
begin
if (q[j] > q[j + 1])
begin
temp = q[j];
q[j] = q[j + 1];
q[j + 1] = temp;
end
end
end
end
4'h2://push
begin
push;
q[0] <= opland;
end
4'h3://send
begin
send_serial_byte({q[1], q[0]});
pop;
pop;
end
4'h5://out
begin
out <= q[0];
pop;
end
4'h8://dup
begin
push;
end
4'h9://drop
begin
pop;
end
4'hf:
begin
case (opland)
4'h0://+
begin
q[0] <= q[1] + q[0];
pop;
end
4'h1://-
begin
q[0] <= q[1] - q[0];
pop;
end
4'h2://*
begin
q[0] <= q[1] * q[0];
pop;
end
4'h3:///
begin
q[0] <= q[1] / q[0];
pop;
end
4'h4://%
begin
q[0] <= q[1] % q[0];
pop;
end
endcase
end
endcase
if (jpflg == 0)
pc <= pc + 1;
else
jpflg <= 0;
end
end
endmodule
テストベンチ
`timescale 1ns / 1ps
module test;
parameter CLK_FREQ_HZ = 50_000_000;
parameter BAUD_RATE = 115_200;
localparam CLK_PER = 20;
localparam BIT_PERIOD = 1_000_000_000 / BAUD_RATE;
integer j;
reg clk,
rst;
reg [7:0] rx_read_val;
wire [3:0] outport;
wire tx;
assign tx = outport[0];
cpu4 u(.clk(clk), .rst(rst), .outport(outport));
initial
begin
$dumpfile("test.vcd");
$dumpvars(0, test);
$monitor(" %d %h", tx, outport);
clk = 0;
rst = 0;
#2
rst = 1;
#2
rst = 0;
#94800
$finish;
end
always
#1
clk = ~clk;
always
begin
wait (tx == 0);
#(BIT_PERIOD + (BIT_PERIOD / 2));
for (j = 0; j < 8; j = j + 1)
begin
rx_read_val[j] = tx;
#(BIT_PERIOD);
end
$display("recv: %h '%c'", rx_read_val, rx_read_val);
#(BIT_PERIOD);
end
endmodule
投入したソースコード
push 5 //5を積む
push 5 //5を積む
send //送信
実行結果
>vvp a.out
VCD info: dumpfile test.vcd opened for output.
x x
0 0
send: 55 'U'
1 1
0 0
1 1
0 0
1 1
0 0
1 1
0 0
1 1
recv: 55 'U'
stack6.v:203: $finish called at 94804000 (1ps)
以上。
