CPUはクロックに従って ROM/RAM を読み込んで内部状態を変更し、外部出力を行うステートマシンと思うことができます。このとき、次の状態への変更は論理モデルでは case 文で記述することが可能です。実際に Verilog HDL で 8bit CPU を記述してみます。
まず、8bit RAM を定義します。
module ram8(q, ad, dt, we, clk)
output[7:0] q;
input [7:0] ad;
input [7:0] dt;
input we;
input clk;
reg [7:0] mem[127:0];
always @posedge(clk) begin
if (we)
mem[ad] <= dt;
q <= mem[ad];
end
endmodule
8bit CPU の仕様は以下とします。
- 内部レジスタは16個(レジスタ0はプログラムカウンタ PC 専用)
- リセットは同期式
- ポートは入力16個、出力16個
- 1ワードは8bit長
- 命令長は2ワード
- 1命令は3クロックで実行
命令ワード(bit表現)(MSB:LSB) | 説明 |
0000rrrr aaaaaaaa | RAMからアドレスaの内容をレジスタrにロードする。 |
0001rrrr aaaaaaaa | レジスタrをRAMのアドレスaにストアする。 |
0010rrrr 0000qqqq | レジスタqをアドレスとして、RAMの内容をレジスタrにロードする。 |
0011rrrr 0000qqqq | レジスタqをアドレスとして、レジスタrの内容をRAMにストアする。 |
0100qqqq rrrrtttt | レジスタrとレジスタtの和をレジスタqに代入する。 |
0110qqqq rrrrtttt | レジスタrからレジスタtを減算してレジスタqに代入する。 |
1000rrrr cccccccc | 定数cをレジスタrにロードする。 |
11101100 pppprrrr | 入力ポートpからレジスタrに値をロードする。 |
11101101 pppprrrr | レジスタrの値を出力ポートpにストアする。 |
上記以外の組合わせ | エラーとしてアドレス0をPCにロード。 |
Verilog HDL で論理レベルで書き下すと以下のようになります。
module cpu8(clk, rst, pin, pout)
input wire clk;
input wire rst;
input wire [7:0] pin[0:15];
output reg [7:0] pout[0:15];
// 内部変数
wire [7:0] q;
reg [7:0] ad;
reg [7:0] dt;
reg we;
reg [1:0] phase;
reg [7:0] phase0;
wire [3:0] dec0_upper = phase0[7:4];
wire [3:0] dec0_lower = phase0[3:0];
// ROM/RAM
ram8 ram8(.q(q), .ad(ad), .dt(dt), .we(we), .clk(clk));
// レジスタ
reg [7:0] ireg[0:15];
wire pc = ireg[0];
wire sp = ireg[15];
always @posedge(clk) begin
if (rst) begin
// リセット信号
genvar i;
for (i = 0; i < 16; i++) begin
pout[i] <= 0;
ireg[i] <= 0;
end
ad <= 0;
we <= 0;
dt <= 0;
phase <= 0;
phase0 <= 0;
end else begin
if (phase == 2'b00) begin
// 命令1ワード目フェッチ
phase0 <= q;
we <= 0;
dt <= 0;
phase <= 2'b01;
ad <= pc + 1;
end else if (phase == 2'b01') begin
// 命令2ワード目フェッチ
we <= dec0_upper[0];
case (dec0_upper[3:1])
"000": begin ad <= q; dt <= ireg[dec0_lower]; ireg[0] <= pc + 1; end
"001": begin ad <= ireg[dec0_lower]; dt <= ireg[q[3:0]]; ireg[0] <= pc + 1; end
"010": begin
ireg[dec0_lower] <= ireg[q[7:4]] + ireg[q[3:0]];
if (dec0_lower != 0)
ireg[0] <= pc + 1;
end
"011": begin
ireg[dec0_lower] <= ireg[q[7:4]] - ireg[q[3:0]];
if (dec0_lower != 0)
ireg[0] <= pc + 1;
end
"100": begin
ireg[dec0_lower] <= q;
if (dec0_lower != 0)
ireg[0] <= pc + 1;
end
"111": case (dec0_lower)
"1100": begin
ireg[q[7:4]] <= pin[3:0];
if (q[7:4] != 0)
ireg[0] <= pc + 1;
end
"1101": begin pout[q[3:0]] <= ireg[q[7:4]]; ireg[0] <= pc + 1; end
default: ireg[0] <= 0;
endcase
default: ireg[0] <= 0;
endcase
phase <= 2'10;
end else begin
// 実行サイクル
if (!dec0_upper[0]) begin
case (dec0_upper[3:1])
"000": begin
ireg[dec0_lower] <= q;
ad <= (dec0_lower) ? pc : q;
end
"001": begin
ireg[dec0_lower] <= q;
ad <= (dec0_lower) ? pc : q;
end
default:
ad <= pc;
endcase
else
ad <= pc;
end
phase <= 0;
end
end
end
endmodule
コメントを残す