CPUの創り方をもとにverilog-hdlでシミュレーションをしてみた
- CPUの創り方をもとにverilog-hdlでシミュレーションをしてみた
- 4bitレジスタの作成
- 演算装置(ALU)の作成
- レジスタと演算装置を組み合わせてみる
- ROMから読み込んだ命令を実行する
- 命令のデコーダを作成する
- クロック周波数を調整してキッチンタイマーとして使えるようにする
CPUの創り方をもとにverilog-hdlでシミュレーションをしてみた
CPUの創り方 にのっているキッチンタイマー(時間固定)のCPUをverilog-hdlでシミュレーションしてみました。実装についてはこちらに作られていたのがありましたので、これをもとに確認しました。
環境構築(QuartusⅡ)
環境を準備するのが大変だと思うのですが、自分はQuartusⅡ Web Editionを入れて確認しました。QuartusⅡ Web Editionはこちらのページの"ソフトウエアを選択"からダウンロードできるかと思います。
新規プロジェクト作成
QuartusⅡインストールまで完了して新規プロジェクトの作成では以下を選択します。 "File" -> "New Project Wizard..." 途中でインストール先やプロジェクトタイプや使用するデバイス(FPGA)について聞かれますが、verilog-hdlのシミュレーションをしたいだけであればインストール先を指定後"Finish"を選択して大丈夫のはずです。
新規verilogファイル作成
新規プロジェクト作成後ファイルを追加する場合は以下を選択します。 "File" -> "New..." -> "Design Files" -> "Verilog HDL File" -> "OK"
4bitレジスタの作成
今回は4bitのcpuを作るのですが、この4bitはcpuが保持できる状態のサイズになっており保持するためにはレジスタを使います。4bitのcpuであればレジスタが保持できるデータサイズが4bitになりまして、cpuではレジスタを複数保持しています。
- RegisterFile.v
module RegisterFile ( input clk_cpu, input reset, input load, input [3:0] dat_in, output [3:0] dat_out ); reg [3:0] register; always @(posedge clk_cpu or posedge reset) begin if (reset) begin register <= 4'd0; end else if (load) begin register <= dat_in; end end assign dat_out = register; endmodule
4bitレジスタの実装は↑のようになるのですが、やっている内容としてはクロックのタイミングでresetが立っていたら0を出力、loadが立っていたら入力値をそのまま返す、それ以外は依然の出力値をそのまま返すといった内容になっています。verilogだと簡単にかけてよいのですが、データ保持のためには実際の回路としてはフリップフロップを使っているかと思います。
これだけだと何に使えるのかイメージがわかないかもしれませんが、例えばアセンブラ言語のmovは値のコピーを行うのですがloadを立ててコピーする値をdat_inに渡すことでmovを実現することができます。またcpuが実行する命令のアドレスを保持するためのプログラムカウンタはレジスタ上に保存しておきクロックのタイミングでdat_outの内容を+1してdat_inに渡す、それからjnc命令で実行する命令のアドレスを変更する場合はdat_inに渡す内容をジャンプ先に帰ることで実現できます。
では実際にレジスタの動作を確認したいと思うのですが、動作確認用に以下のファイルを追加します。
- RegisterFileTest.v
`include "Defines.v" module RegisterFileTest(); reg clk_cpu; reg reset; reg load; reg [3:0] dat_in; wire [3:0] dat_out; integer i; RegisterFile Register0( .clk_cpu(clk_cpu), .reset(reset), .load(load), .dat_in(dat_in), .dat_out(dat_out) ); // clock initial begin clk_cpu = 0; forever #`HCYCL clk_cpu = !clk_cpu; end // test initial begin // reset repeat(5) @(posedge clk_cpu); test_reset(); // load sequential values repeat(5) @(posedge clk_cpu); dat_in = 0; repeat(20) begin test_load(dat_in + 1); end // reset repeat(5) @(posedge clk_cpu); test_reset(); end // tasks task test_reset; begin reset = 1; load = 0; repeat(5) @(posedge clk_cpu) if (dat_out != 4'd0) $display("test_reset_register_file: dat_out is not zero"); reset = 0; end endtask task test_load; input [3:0] dat_next; begin @(negedge clk_cpu) begin load = 1; dat_in = dat_next; end @(negedge clk_cpu) begin load = 0; #`HSTRB; if (dat_out != dat_in) $display("test_load: dat_out is not equal to dat_in"); end end endtask endmodule
- Defines.v
`timescale 1 ns/ 1 ns `define HCYCL 10 `define HSTRB 8
動作検証としてレジスタへの入力や出力のための変数を定義して渡しています。以下の部分では0から20までカウントしてtest_loadに渡しています。test_load内ではloadを1にしてdat_inにカウントした結果を渡しているのでレジスタで保持している値もカウントアップした結果になっているはずです。
dat_in = 0; repeat(20) begin test_load(dat_in + 1); end
動作を確認する場合はQuartusⅡのメニューからStart Compilationをクリックします。シミュレーション対象のメインのモジュールを選択するにはHierarchyの一覧の一番上の項目を右クリックし"Settings..." -> "EDA Tool Settings" -> "Simulation" -> "Test Benchs..." -> "New" と進み、、テスト対象のファイルを追加しTestbench nameを入力しOKをクリックします。
あとはコンパイルに成功した後RTL Simulationをクリックしたら別ウィンドウでシミュレーション用のウィンドウが立ち上がります。
ウィンドウを立ち上げた直後は波形が出力されないのですが"Run -All" をくりっくしてから "Stop" をクリックすると表示されるようになります。
シミュレーション結果よりdat_outがdat_inを追従していることがわかります。
演算装置(ALU)の作成
演算装置といっても今回は桁上がりがある加算器(全加算器)の機能のみで減算や乗算には対応していません。
実装は以下になります。
module Alu ( input clk, input reset, input [3:0] data_0_in, input [3:0] data_1_in, output [3:0] data_out, output carry_out ); // alu wire [4:0] result; assign result = data_0_in + data_1_in; assign data_out = result[3:0]; // carry flag reg carry; always @(posedge clk or posedge reset) begin if (reset) carry <= 0; else carry <= result[4]; end assign carry_out = carry; endmodule
data_0_inとdata_1_inが加算対象で結果がdata_outになり、桁上がりが発生したかどうかはcarry_outで分かるようになっています。 このaluを使うことでレジスタの値を加算することができるようになります。
以下のモジュールでaluのシミュレーションをしてみます。 - AluTest.v
`include "Defines.v" module AluTest(); reg clk_cpu; reg reset; reg load; reg [3:0] data_0_in; reg [3:0] data_1_in; wire [3:0] dat_out; wire carry_out; Alu Alu0( .clk(clk), .reset(reset), .data_0_in(data_0_in), .data_1_in(data_1_in), .data_out(dat_out), .carry_out(carry_out) ); // clock initial begin clk_cpu = 0; forever #`HCYCL clk_cpu = !clk_cpu; end // test initial begin // reset repeat(5) @(posedge clk_cpu); test_reset(); // load sequential values repeat(5) @(posedge clk_cpu); data_0_in = 1; data_1_in = 2; repeat(5) @(posedge clk_cpu) if (dat_out != 3 || carry_out != 0) $display("test_load: dat_out is not equal to dat_in"); // reset repeat(5) @(posedge clk_cpu); test_reset(); end // tasks task test_reset; begin reset = 1; load = 0; data_0_in = 0; data_1_in = 0; repeat(5) @(posedge clk_cpu) if (dat_out != 4'd0) $display("test_reset_register_file: dat_out is not zero"); reset = 0; end endtask endmodule
1+2をしているのでaluの出力結果が3になっているはずです。実際に確認すると以下のようになり、確かに3になっていることが確認できます。
レジスタと演算装置を組み合わせてみる
レジスタと演算装置を組み合わせてmov命令やadd命令に対応したCPUを作ってみます。できることがこれだけなのでCPUに分類できるかどうか微妙ですが
- CPU_Proto.v
`include "Defines.v" module CPU_Proto ( input clk_cpu, input reset, input select_a, input select_b, input load_a, input load_b, input load_pc, input [3:0] im, output [3:0] pc ); // program counter reg [3:0] next_pc; wire [3:0] alu_data_out; RegisterFile RegisterPC( .clk_cpu(clk_cpu), .reset(reset), .load(clk_cpu), .dat_in(next_pc), .dat_out(pc) ); always @(*) begin if (load_pc) next_pc <= alu_data_out; else next_pc <= pc + 4'd1; end // register A wire reg_a_load; wire [3:0] reg_a_out; RegisterFile RegisterFileA( .clk_cpu(clk_cpu), .reset(reset), .load(load_a), .dat_in(alu_data_out), .dat_out(reg_a_out) ); // register B wire [3:0] reg_b_out; RegisterFile RegisterFileB( .clk_cpu(clk_cpu), .reset(reset), .load(load_b), .dat_in(alu_data_out), .dat_out(reg_b_out) ); // ALU wire [3:0] alu_data_0_in; assign alu_data_0_in = select_a == 1 ? reg_a_out : select_b == 1 ? reg_b_out : 4'd0; wire [3:0] alu_data_1_in; wire alu_carry_out; Alu Alu0( .clk(clk_cpu), .reset(reset), .data_0_in(alu_data_0_in), .data_1_in(im), .data_out(alu_data_out), .carry_out(alu_carry_out) ); endmodule
RegisterPCはプログラムカウンタになります。本来であればプログラムカウンタが命令のアドレスを保持しておりそれをもとに命令を取り出すとかなのですが、今回はカウンターくらいの役割しか果たしていないです。レジスタとしてはRegisterFileAとRegisterFileBの2つがあり、aluはAlu0が一つあります。 レジスタとaluの接続として、レジスタA,レジスタBの出力のどちらかとcpuへの入力imをaluに渡しています、それからaluの出力alu_data_outを2つのレジスタの入力としています。 この時レジスタA,レジスタBの出力のどちらかをaluの入力とするかについてはselect_a, select_bの値で判定しています。
cpuへの入力 select_a, select_b, load_a, load_b, load_pc, imによりmovとaddの命令を実行できるようにしています。例えばレジスタaの値を3に変更したい場合はload_a=1, im = 3で他は0とすることで実現でき、レジスタbの値をレジスタaの値に2加算した結果にする場合はselect_a=1, load_b=1, im=2,他は0とすることで実現できます。
これの動作検証として以下でシミュレーションしてみます。
`include "Defines.v" module CPU_ProtoTest(); reg clk_cpu; reg reset; reg cpu_proto_select_a; reg cpu_proto_select_b; reg cpu_proto_load_a; reg cpu_proto_load_b; reg cpu_proto_load_pc; reg [3:0] cpu_proto_im; wire [3:0] cpu_proto_pc; CPU_Proto CPU_Proto0 ( .clk_cpu(clk_cpu), .reset(reset), .select_a(cpu_proto_select_a), .select_b(cpu_proto_select_b), .load_a(cpu_proto_load_a), .load_b(cpu_proto_load_b), .load_pc(cpu_proto_load_pc), .im(cpu_proto_im), .pc(cpu_proto_pc) ); // clock initial begin clk_cpu = 0; forever #`HCYCL clk_cpu = !clk_cpu; end // test initial begin // reset repeat(5) @(posedge clk_cpu); test_reset(); // check program counter repeat(5) @(posedge clk_cpu); // mov rega 5 @(negedge clk_cpu) begin cpu_proto_select_a <= 0; cpu_proto_select_b <= 0; cpu_proto_load_a <= 1; cpu_proto_load_b <= 0; cpu_proto_load_pc <= 0; cpu_proto_im <= 4'd5; end // mov regb rega @(negedge clk_cpu) begin cpu_proto_select_a <= 1; cpu_proto_select_b <= 0; cpu_proto_load_a <= 0; cpu_proto_load_b <= 1; cpu_proto_load_pc <= 0; cpu_proto_im <= 4'd0; end // add regb 3 @(negedge clk_cpu) begin cpu_proto_select_a <= 0; cpu_proto_select_b <= 1; cpu_proto_load_a <= 0; cpu_proto_load_b <= 1; cpu_proto_load_pc <= 0; cpu_proto_im <= 4'd3; end // jump 2 @(negedge clk_cpu) begin cpu_proto_select_a <= 0; cpu_proto_select_b <= 0; cpu_proto_load_a <= 0; cpu_proto_load_b <= 0; cpu_proto_load_pc <= 1; cpu_proto_im <= 4'd2; end // reset repeat(5) @(posedge clk_cpu); test_reset(); end // tasks task test_reset; begin reset = 1; repeat(5) @(posedge clk_cpu) if (cpu_proto_pc != 4'd0) $display("test_reset_register_file: dat_out is not zero"); reset = 0; end endtask endmodule
ここではmov rega 5, mov regb rega, add regb 3, jump 2と実行しています。
実際にシミュレーションしみるとreg_a_out, reg_b_out, cpu_proto_pcの値からその通りになっていることが確認できます。
ROMから読み込んだ命令を実行する
直前のシミュレーションでは命令を実行するためにloadやselectの値を直接渡していましたが、実際はメモリから命令を読み込んで実行するはずなのでそうできるようにしたいと思います。読み込みのみ可能なROMを以下のように実装します。 Rom_Proto.v
module Rom_Proto ( input clk_cpu, input reset, input [3:0] adrs, output [8:0] dat_out ); // ROM instruction data function [7:0] rom_data ( input [3:0] rom_adrs ); begin case (rom_adrs) 4'h0: rom_data = {4'd5, 5'b00100}; // mov rega 5 4'h1: rom_data = {4'd0, 5'b01001}; // mov regb rega 4'h2: rom_data = {4'd3, 5'b01010}; // add regb 3 4'h3: rom_data = {4'd0, 5'b10000}; // jump 0 4'h4: rom_data = {4'd0, 5'b00000}; 4'h5: rom_data = {4'd0, 5'b00000}; 4'h6: rom_data = {4'd0, 5'b00000}; 4'h7: rom_data = {4'd0, 5'b00000}; 4'h8: rom_data = {4'd0, 5'b00000}; 4'h9: rom_data = {4'd0, 5'b00000}; 4'ha: rom_data = {4'd0, 5'b00000}; 4'hb: rom_data = {4'd0, 5'b00000}; 4'hc: rom_data = {4'd0, 5'b00000}; 4'hd: rom_data = {4'd0, 5'b00000}; 4'he: rom_data = {4'd0, 5'b00000}; 4'hf: rom_data = {4'd0, 5'b00000}; default: rom_data = 9'h0; endcase end endfunction assign dat_out = rom_data(adrs); endmodule
データのフォーマットを決めて置きプログラムカウンタの値によってまとめて取り出すようにしています。今回のデータのフォーマットは {im, load_pc, load_b, load_a, select_b, select_a}のようになっています。
データのフォーマットに注意してシミュレーションの実装は以下のようになりました。assignで正しいbitを渡すようにしています。
`include "Defines.v" module CPU_ROM_ProtoTest(); reg clk; reg reset; wire select_a; wire select_b; wire load_a; wire load_b; wire load_pc; wire [3:0] im; wire [3:0] pc; wire [8:0] inst; Rom_Proto Rom_Proto0( .clk_cpu(clk), .reset(reset), .adrs(pc), .dat_out(inst) ); assign select_a = inst[0]; assign select_b = inst[1]; assign load_a = inst[2]; assign load_b = inst[3]; assign load_pc = inst[4]; assign im = inst[8:5]; CPU_Proto CPU_Proto0 ( .clk_cpu(clk), .reset(reset), .select_a(select_a), .select_b(select_b), .load_a(load_a), .load_b(load_b), .load_pc(load_pc), .im(im), .pc(pc) ); // clock initial begin clk = 0; forever #`HCYCL clk = !clk; end initial begin @(negedge clk) begin reset = 1; repeat(5) @(posedge clk) reset = 0; end end endmodule
これのシミュレーションを実行しようとするとターミナルにエラーが出るのですがシミュレーションのウィンドウからLibrary -> work -> CPU_ROM_ProtoTestを右クリックしてsimulateを選択するとシミュレーションが実行できます。結果は以下のようになり、想定通りになりました。
命令のデコーダを作成する
上記のROMだとselect, loadの値をそのまま保持しているのですが、selectやloadの値をどうするかcpu内部で判断できるようにデコーダを作成してみます。
- Decoder.v
`include "Defines.v" module Decoder ( input [3:0] op_in, input alu_carry, output [1:0] alu_data_sel, output reg_a_load, output reg_b_load, output reg_pc_load ); // decode operation and generate control path signals // format = {data selector for ALU, pc_load, b_load, a_load} function [4:0] decode_op; input [3:0] op; input alu_carry_in; case (op) `OP_NOP: decode_op = {`SEL_Z, 1'b0, 1'b0, 1'b0}; `OP_ADD_A_IM: decode_op = {`SEL_A, 1'b0, 1'b0, 1'b1}; `OP_ADD_B_IM: decode_op = {`SEL_B, 1'b0, 1'b1, 1'b0}; `OP_MOV_A_IM: decode_op = {`SEL_Z, 1'b0, 1'b0, 1'b1}; `OP_MOV_B_IM: decode_op = {`SEL_Z, 1'b0, 1'b1, 1'b0}; `OP_MOV_A_B: decode_op = {`SEL_A, 1'b0, 1'b0, 1'b1}; `OP_MOV_B_A: decode_op = {`SEL_B, 1'b0, 1'b1, 1'b0}; `OP_JMP_IM: decode_op = {`SEL_Z, 1'b1, 1'b0, 1'b0}; `OP_JNC_IM: if (~alu_carry_in) decode_op = {`SEL_Z, 1'b1, 1'b0, 1'b0}; else decode_op = {`SEL_Z, 1'b0, 1'b0, 1'b0}; default: decode_op = {`SEL_Z, 1'b0, 1'b0, 1'b0}; endcase endfunction // decompose decoded bits into control paths reg [5:0] decoded; always @ (op_in or alu_carry) begin decoded = decode_op(op_in, alu_carry); end assign reg_a_load = decoded[0]; assign reg_b_load = decoded[1]; assign reg_pc_load = decoded[2]; assign alu_data_sel = decoded[4:3]; endmodule
- Defines.v
`timescale 1 ns/ 1 ns `define HCYCL 10 `define HSTRB 8 // data select for ALU `define SEL_A 2'b00 // data select register A `define SEL_B 2'b01 // data select register B `define SEL_Z 2'b11 // data select ZERO // operations `define OP_NOP 4'h0 `define OP_ADD_A_IM 4'h1 `define OP_ADD_B_IM 4'h2 `define OP_MOV_A_IM 4'h3 `define OP_MOV_B_IM 4'h4 `define OP_MOV_A_B 4'h5 `define OP_MOV_B_A 4'h6 `define OP_JMP_IM 4'h7 `define OP_JNC_IM 4'h8
デコーダへの入力op_inが命令になっており、この値によりload, selectの値が変わるようになっています。またOP_JNC_IMは条件付きのジャンプ命令になっていてalu_carry_inが立っていない場合のみジャンプするというものになるのですが、こういった条件付きの命令を作ることを考えてもデコーダはcpuの内部にあったほうが良いのかと思います。
今回のデコーダに合わせてcpu, romの実装を以下のように修正します。
- Rom.v
`include "Defines.v" module Rom ( input clk_cpu, input reset, input [3:0] adrs, output [7:0] dat_out ); // ROM instruction data function [7:0] rom_data ( input [3:0] rom_adrs ); begin case (rom_adrs) 4'h0: rom_data = {`OP_ADD_A_IM, 4'h1}; 4'h1: rom_data = {`OP_JNC_IM, 4'h0}; 4'h2: rom_data = {`OP_ADD_A_IM, 4'h1}; 4'h3: rom_data = {`OP_JNC_IM, 4'h2}; 4'h4: rom_data = {`OP_ADD_A_IM, 4'h1}; 4'h5: rom_data = {`OP_JNC_IM, 4'h4}; 4'h6: rom_data = {`OP_ADD_A_IM, 4'h1}; 4'h7: rom_data = {`OP_JNC_IM, 4'h6}; 4'h8: rom_data = {`OP_ADD_A_IM, 4'h1}; 4'h9: rom_data = {`OP_JNC_IM, 4'h8}; 4'ha: rom_data = {`OP_JMP_IM, 4'ha}; // 自分自身にジャンプで処理終了 4'hb: rom_data = {`OP_NOP, 4'h0}; 4'hc: rom_data = {`OP_NOP, 4'h0}; 4'hd: rom_data = {`OP_NOP, 4'h0}; 4'he: rom_data = {`OP_NOP, 4'h0}; 4'hf: rom_data = {`OP_NOP, 4'h0}; default: rom_data = 8'h0; endcase end endfunction assign dat_out = rom_data(adrs); endmodule
- CPU_Decoder.v
`include "Defines.v" module CPU_Decoder ( input clk_cpu, input reset, input [7:0] inst, output [3:0] pc ); reg [3:0] next_pc; wire load_a; wire load_b; wire load_pc; wire [3:0] reg_a_out; wire [3:0] reg_b_out; wire [3:0] alu_data_0_in; wire [3:0] alu_data_1_in; wire [3:0] alu_data_out; wire [1:0] alu_data_sel; wire alu_carry_out; // program counter RegisterFile RegisterPC( .clk_cpu(clk_cpu), .reset(reset), .load(clk_cpu), .dat_in(next_pc), .dat_out(pc) ); always @(*) begin if (load_pc) next_pc <= alu_data_out; else next_pc <= pc + 4'd1; end // Decoder Decoder Decoder0 ( .op_in(inst[7:4]), .alu_carry(alu_carry_out), .alu_data_sel(alu_data_sel), .reg_a_load(load_a), .reg_b_load(load_b), .reg_pc_load(load_pc) ); // register A RegisterFile RegisterFileA( .clk_cpu(clk_cpu), .reset(reset), .load(load_a), .dat_in(alu_data_out), .dat_out(reg_a_out) ); // register B RegisterFile RegisterFileB( .clk_cpu(clk_cpu), .reset(reset), .load(load_b), .dat_in(alu_data_out), .dat_out(reg_b_out) ); // ALU assign alu_data_0_in = alu_data_sel == `SEL_A ? reg_a_out : alu_data_sel == `SEL_B ? reg_b_out : alu_data_sel == `SEL_Z ? 4'd0 : 4'd0; assign alu_data_1_in = inst[3:0]; Alu Alu0( .clk(clk_cpu), .reset(reset), .data_0_in(alu_data_0_in), .data_1_in(alu_data_1_in), .data_out(alu_data_out), .carry_out(alu_carry_out) ); endmodule
それから動作検証用のモジュールを追加して想定通り動作していることを確認します。
- CPU_DecoderTest.v
`include "Defines.v" module CPU_DecoderTest(); reg clk; reg reset; wire [7:0] inst; wire [3:0] pc; Rom Rom0( .clk_cpu(clk), .reset(reset), .adrs(pc), .dat_out(inst) ); CPU_Decoder CPU_Decoder0( .clk_cpu(clk), .reset(reset), .inst(inst), .pc(pc) ); // clock initial begin clk = 0; forever #`HCYCL clk = !clk; end initial begin @(negedge clk) begin reset = 1; repeat(5) @(posedge clk) reset = 0; end end endmodule
今回は "レジスタAの値を1追加" -> "alu_carry_outが0なら前の命令に戻る" というのを5回繰り返しています。
クロック周波数を調整してキッチンタイマーとして使えるようにする
"レジスタAの値を1追加" -> "alu_carry_outが0なら前の命令に戻る"を5回繰り返すとしてalu_carry_outが1になるタイミングは16回実行語になるので命令を実行する回数は全部で2 * 16 * 5 = 160回になります。ただ現在10ns毎でクロックが反転するようになっており命令を実行するのはクロックの立ち上がりのタイミングになっているので一命令の実行に20nsかかるので処理の完了までに160 * 20 = 3200nsとなり一瞬で終了するのでキッチンタイマーとして使えません。 そこで以下のモジュールでクロックの周波数を変更できるようにします。
- ClockPrescaler.v
`include "Defines.v" module ClockPrescaler ( input clk, input reset, output reg clk_cpu ); reg [31:0] cnt = 32'd0; always @(posedge clk or posedge reset) begin if (reset) begin cnt <= 32'd0; end else if (cnt == `CPU_CLK_PRESCALE) begin cnt <= 32'd0; end else begin cnt <= cnt + 32'd1; end end // clk_cpu register (clock for cpu) always @(posedge clk or posedge reset) begin if (reset) clk_cpu <= 0; else clk_cpu <= cnt < (`CPU_CLK_PRESCALE / 2); end endmodule
- Defines.v
`timescale 1 ns/ 1 ns `define HCYCL 10 `define HSTRB 8 // CPU prescaling (1 cycle per sec) //`define CPU_CLK_PRESCALE 32'd49999999 `define CPU_CLK_PRESCALE 32'd3 // data select for ALU `define SEL_A 2'b00 // data select register A `define SEL_B 2'b01 // data select register B `define SEL_Z 2'b11 // data select ZERO // operations `define OP_NOP 4'h0 `define OP_ADD_A_IM 4'h1 `define OP_ADD_B_IM 4'h2 `define OP_MOV_A_IM 4'h3 `define OP_MOV_B_IM 4'h4 `define OP_MOV_A_B 4'h5 `define OP_MOV_B_A 4'h6 `define OP_JMP_IM 4'h7 `define OP_JNC_IM 4'h8
動作検証用のモジュールで変換したクロックを渡すようにします。 - CPU_ClockPrescalerTest
`include "Defines.v" module CPU_ClockPrescalerTest(); reg clk; reg reset; wire clk_cpu; wire [7:0] inst; wire [3:0] pc; // cpu clock prescaling ClockPrescaler ClockPrescaler0( .clk(clk), .reset(reset), .clk_cpu(clk_cpu) ); Rom Rom0( .clk_cpu(clk_cpu), .reset(reset), .adrs(pc), .dat_out(inst) ); CPU_Decoder CPU_Decoder0( .clk_cpu(clk_cpu), .reset(reset), .inst(inst), .pc(pc) ); // clock initial begin clk = 0; forever #`HCYCL clk = !clk; end initial begin @(negedge clk) begin reset = 1; repeat(5) @(posedge clk) reset = 0; end end endmodule