windowsでiverilog その67

# 概要　

windowsでiverilogやってみた。
cpuのテストベンチ書いてみた。

# 参考にしたページ

http://japanese.sugawara-systems.com/opencores/VPI/cpu.htm

# サンプルコード


```

`define ADD_com			5'b00000
`define SET_com			5'b00001
`define SUB_com			5'b00010
`define SHIFT_L_com		5'b00011
`define SHIFT_R_com		5'b00100
`define JMP_com			5'b00101
`define SYNC_com		5'b00110
`define AND_com			5'b00111
`define OR_com			5'b01000
`define XOR_com			5'b01001
`define NOT_com			5'b01010
`define MUL_com			5'b01011
`define BSHIFT_L_com	5'b01100
`define BSHIFT_R_com	5'b01101
`define Eq				4'b0000
`define Not_Eq			4'b0001
`define Always			4'b0010
`define LT				4'b0011
`define GT				4'b0100
`define LTE				4'b0101
`define GTE				4'b0110
`define ZR				4'b0000
`define R0				4'b0001
`define R1				4'b0010
`define R2				4'b0011
`define R3				4'b0100
`define P_IN			4'b0101
`define P_OUT			4'b0110

`define NOP						romdata={Add,ZR,ZR,ZR,Eq,Reserved,16'h 0000}
`define MOV(r0,r1) 				romdata={Add,r0,ZR,r1,Eq,Reserved,16'h 0000}
`define CLR(r0)					romdata={Add,ZR,ZR,r0,Eq,Reserved,16'h 0000}
`define SET(r0,num) 			romdata={Set,P_IN,ZR,r0,Eq,Reserved,16'd num}
`define JUMP(num)				romdata={Jmp,ZR,ZR,ZR,Always,Reserved,16'd num}
`define NOT(r0,r1) 				romdata={Not,r0,ZR,r1,Always,Reserved,16'h 0000}
`define ADD(r0,r1,r2) 			romdata={Add,r0,r1,r2,Eq,Reserved,16'h 0000}
`define SUB(r0,r1,r2) 			romdata={Sub,r0,r1,r2,Eq,Reserved,16'h 0000}
`define MUL(r0,r1,r2) 			romdata={Mul,r0,r1,r2,Eq,Reserved,16'h 0000}
`define JUMP_IF_LT(r0,r1,num) 	romdata={Jmp,r0,r1,ZR,LT,Reserved,16'd num}
`define JUMP_IF_GT(r0,r1,num) 	romdata={Jmp,r0,r1,ZR,GT,Reserved,16'd num}
`define JUMP_IF_LTE(r0,r1,num) 	romdata={Jmp,r0,r1,ZR,LTE,Reserved,16'd num}
`define JUMP_IF_GTE(r0,r1,num) 	romdata={Jmp,r0,r1,ZR,GTE,Reserved,16'd num}
`define JUMP_IF_EQ(r0,r1,num) 	romdata={Jmp,r0,r1,ZR,Eq,Reserved,16'd num}
`define SHIFT_L(r0,r1)			romdata={Shift_L,ZR,r0,r1,Eq,Reserved,16'h 0000}
`define SHIFT_R(r0,r1)			romdata={Shift_R,ZR,r0,r1,Eq,Reserved,16'h 0000}
`define BSHIFT_L(r0,r1,r2) 		romdata={BShift_L,r0,r1,r2,Eq,Reserved,16'h 0000}
`define BSHIFT_R(r0,r1,r2) 		romdata={BShift_R,r0,r1,r2,Eq,Reserved,16'h 0000}
`define SYNC					romdata={Sync,ZR,ZR,ZR,Eq,Reserved,16'h 0000}
`define ADD_LOOP(r0,r1,r2,num) 	romdata={Add,r0,r1,r2,Eq,LoopEnabled,16'd num}
`define SUB_LOOP(r0,r1,r2,num) 	romdata={Sub,r0,r1,r2,Eq,LoopEnabled,16'd num}
`define MUL_LOOP(r0,r1,r2,num) 	romdata={Mul,r0,r1,r2,Eq,LoopEnabled,16'd num}
`define SHIFT_L_LOOP(r0,num) 	romdata={Shift_L,ZR,r0,r0,Eq,LoopEnabled,16'd num}
`define SHIFT_R_LOOP(r0,num) 	romdata={Shift_R,ZR,r0,r0,Eq,LoopEnabled,16'd num}

module register_file(input clock, input reset, input [3:0] A_address, input [3:0] B_address, input [3:0] write_address, input [15:0] alu_data, port_in_data, output [15:0] out_dataA, out_dataB, port_out_data);
	reg [15:0] p_out_reg,
		r0,
		r1,
		r2,
		r3;
	assign out_dataA = (A_address == `P_OUT) ? p_out_reg : (A_address == `P_IN) ? port_in_data : (A_address == `R0) ? r0 : (A_address == `R1) ? r1 : (A_address == `R2) ? r2 : (A_address == `R3) ? r3 : 16'b0;
	assign out_dataB = (B_address == `P_OUT) ? p_out_reg : (B_address == `P_IN) ? port_in_data : (B_address == `R0) ? r0 : (B_address == `R1) ? r1 : (B_address == `R2) ? r2 : (B_address == `R3) ? r3 : 16'b0;
	assign port_out_data = p_out_reg;
	always @(posedge clock, posedge reset)
	begin
		if (reset)
		begin
			p_out_reg <= 0;
		end
		else if (write_address == `P_OUT)
		begin
			p_out_reg <= alu_data;
		end
		else
		begin
			p_out_reg <= p_out_reg;
		end
	end
	always @(posedge clock, posedge reset)
	begin
		if (reset)
		begin
			r0 <= 0;
		end
		else if (write_address == `R0)
		begin
			r0 <= alu_data;
		end
		else
		begin
			r0 <= r0;
		end
	end
	always @(posedge clock, posedge reset)
	begin
		if (reset)
		begin
			r1 <= 0;
		end
		else if (write_address == `R1)
		begin
			r1 <= alu_data;
		end
		else
		begin
			r1 <= r1;
		end
	end
	always @(posedge clock, posedge reset)
	begin
		if (reset)
		begin
			r2 <= 0;
		end
		else if (write_address == `R2)
		begin
			r2 <= alu_data;
		end
		else
		begin
			r2 <= r2;
		end
	end
	always @(posedge clock, posedge reset)
	begin
		if (reset)
		begin
			r3 <= 0;
		end
		else if (write_address == `R3)
		begin
			r3 <= alu_data;
		end
		else
		begin
			r3 <= r3;
		end
	end

endmodule

module alu(input [4:0] command, input [3:0] jump_cond, input [15:0] portA, portB, output reg [15:0] alu_out);
	always @(*)
	begin
		case (command)
		`ADD_com:
			alu_out = portA + portB;
		`SUB_com:
			alu_out = portA - portB;
		`SHIFT_L_com:
			alu_out = portB << 1;
		`SHIFT_R_com:
			alu_out = portB >> 1;
		`BSHIFT_L_com:
			alu_out = portA << portB;
		`BSHIFT_R_com:
			alu_out = portA >> portB;
		`MUL_com:
			alu_out = portA * portB;
		`AND_com:
			alu_out = portA & portB;
		`OR_com:
			alu_out = portA | portB;
		`XOR_com:
			alu_out = portA ^ portB;
		`NOT_com:
			alu_out = ~portA;
		`JMP_com:
			case (jump_cond)
			`Eq:
				alu_out = (portA == portB);
			`Not_Eq:
				alu_out = (portA != portB);
			`Always:
				alu_out = 1;
			`LT:
				if (portA < portB)
					alu_out = 1;
				else
					alu_out = 0;
			`GT:
				if (portA > portB)
					alu_out = 1;
				else
					alu_out = 0;
			`LTE:
				if (portA <= portB)
					alu_out = 1;
				else
					alu_out = 0;
			`GTE:
				if (portA >= portB)
					alu_out = 1;
				else
					alu_out = 0;
			default:
				alu_out = 0;
			endcase
		default:
			alu_out = portA + portB;
		endcase
	end
endmodule

module rom(input [15:0] rom_address, output [39:0] Data);
	assign Data = romdata(rom_address);
	wire [4:0] command = Data[39:35];
	wire [3:0] A_address = Data[34:31];
	wire [3:0] B_address = Data[30:27];
	wire [3:0] write_address = Data[26:23];
	wire [3:0] jump_cond = Data[22:19];
	wire [15:0] im_val = Data[15:0];
	wire [2:0] loop_cond = Data[18:16];
	parameter [4:0] Set = `SET_com,
		Add = `ADD_com,
		Sub = `SUB_com,
		Shift_L = `SHIFT_L_com,
		Shift_R = `SHIFT_R_com,
		Jmp = `JMP_com,
		Sync = `SYNC_com,
		Mul = `MUL_com,
		And = `AND_com,
		Or = `OR_com,
		Xor = `XOR_com,
		Not = `NOT_com,
		BShift_L = `BSHIFT_L_com,
		BShift_R = `BSHIFT_R_com;
	parameter [4:0] \セット = `SET_com,
		\加算 = `ADD_com,
		\減算 = `SUB_com,
		\左シフト = `SHIFT_L_com,
		\右シフト = `SHIFT_R_com,
		\ジャンプ = `JMP_com,
		\シンク命令 = `SYNC_com,
		\乗算 = `MUL_com,
		\＆ = `AND_com,
		\OR = `OR_com,
		\XOR = `XOR_com,
		\NOT = `NOT_com,
		\左バレルシフト = `BSHIFT_L_com,
		\右バレルシフト = `BSHIFT_R_com;
	parameter [3:0] P_OUT = `P_OUT,
		P_IN = `P_IN,
		R0 = `R0,
		R1 = `R1,
		R2 = `R2,
		R3 = `R3,
		ZR = `ZR;
	parameter [3:0] Eq = `Eq,
		Not_Eq = `Not_Eq,
		Always = `Always,
		LT = `LT,
		GT = `GT,
		LTE = `LTE,
		GTE = `GTE;
	parameter [2:0] Reserved = 3'b000,
		LoopEnabled = 3'b001;
	reg [8 * 40:1] str;
	function [8 * 3:1] get_rn(input [3:0] addr);
		case (addr)
		P_OUT:
			get_rn = "OUT";
		P_IN:
			get_rn = "IN";
		R0:
			get_rn = "R0";
		R1:
			get_rn = "R1";
		R2:
			get_rn = "R2";
		R3:
			get_rn = "R3";
		ZR:
			get_rn = "ZR";
		default:
			get_rn = "???";
		endcase
	endfunction
	always @*
	begin
		case (command)
		Add:
			if (loop_cond == 0)
			begin
				if (A_address == ZR && B_address == ZR & write_address == ZR)
					$sformat(str, "NOP");
				else if (B_address == ZR && A_address == ZR )
					$sformat(str, "%sをクリア", get_rn(write_address));
				else if (B_address == ZR)
					$sformat(str, "%sから%sへコピー", get_rn(A_address), get_rn(write_address));
				else
					$sformat(str, "%s=%s + %s", get_rn(write_address), get_rn(A_address), get_rn(B_address));
			end
			else
			begin
				if (A_address == ZR && B_address == ZR & write_address == ZR)
					$sformat(str, "NOP %dループ付", im_val);
				else if (B_address == ZR && A_address == ZR )
					$sformat(str, "%sをクリア %dループ付", get_rn(write_address), im_val);
				else if (B_address == ZR)
					$sformat(str, "%sから%sへコピー %dループ付", get_rn(A_address), get_rn(write_address), im_val);
				else
					$sformat(str, "%s=%s + %s %dループ付", get_rn(write_address), get_rn(A_address), get_rn(B_address), im_val);
			end
		Set:
			$sformat(str, "%s=%h[Hex]", get_rn(write_address), im_val);
		Mul:
			if (loop_cond == 0)
			begin
				$sformat(str, "%s=%s * %s", get_rn(write_address), get_rn(A_address), get_rn(B_address));
			end
			else
				$sformat(str, "%s=%s * %s 　 %dループ付", get_rn(write_address), get_rn(A_address), get_rn(B_address), im_val);
		Sub:
			if (loop_cond == 0)
			begin
				$sformat(str, "%s=%s - %s", get_rn(write_address), get_rn(A_address), get_rn(B_address));
			end
			else
				$sformat(str, "%s=%s - %s %dループ付", get_rn(write_address), get_rn(A_address), get_rn(B_address), im_val);
		Sync:
			$sformat(str, "シンク命令");
		Not:
			$sformat(str, "%s=~%s", get_rn(write_address), get_rn(A_address));
		Jmp:
			if (jump_cond == Always)
				$sformat(str, "%d番地へジャンプ", im_val);
			else if (jump_cond == LT)
				$sformat(str, "%s < %s なら%d番地へジャンプ", get_rn(A_address), get_rn(B_address), im_val);
			else if (jump_cond == GT)
				$sformat(str, "%s > %s なら%d番地へジャンプ", get_rn(A_address), get_rn(B_address), im_val);
			else if (jump_cond == LTE)
				$sformat(str, "%s <= %s なら%d番地へジャンプ", get_rn(A_address), get_rn(B_address), im_val);
			else if (jump_cond == GTE)
				$sformat(str, "%s >= %s なら%d番地へジャンプ", get_rn(A_address), get_rn(B_address), im_val);
			else if (jump_cond == Eq)
				$sformat(str, "%s == %s なら%d番地へジャンプ", get_rn(A_address), get_rn(B_address), im_val);
		Shift_L:
			if (loop_cond == 0)
				$sformat(str, "%s=%s << 1", get_rn(write_address), get_rn(B_address));
			else
				$sformat(str, "%s=%s << %d", get_rn(write_address), get_rn(B_address), im_val);
		Shift_R:
			if (loop_cond == 0)
				$sformat(str, "%s=%s >> 1", get_rn(write_address), get_rn(B_address));
			else
				$sformat(str, "%s=%s >> %d", get_rn(write_address), get_rn(B_address), im_val);
		BShift_L:
			$sformat(str, "%s=%s <<%s", get_rn(write_address), get_rn(A_address), get_rn(B_address));
		BShift_R:
			$sformat(str, "%s=%s >> %s", get_rn(write_address), get_rn(A_address), get_rn(B_address));
		endcase
	end
	function [39:0] romdata;
		input [15:0] address;
		case(address)
		0:
			`SET(P_OUT, 100);
		1:
			`SET(R0, 5);
		2:
			`MOV(R0, R1);
		3:
			`MOV(R1, R2);
		4:
			`MOV(R2, R3);
		5:
			`SUB(R1, R2, R0);
		6:
			`SUB(R3, R1, R3);
		7:
			`ADD(P_IN, P_OUT, P_OUT);
		8:
			`ADD(P_OUT, P_OUT, P_OUT);
		9:
			`SUB(P_IN, P_IN, R1);
		10:
			`SUB(P_OUT, P_IN, R2);
		11:
			`SET(R0, 2);
		12:
			`SET(R1, 8);
		13:
			`MUL(R0, R1, R3);
		14:
			`NOP;
		15:
			`MUL(R3, R3, R3);
		16:
			`ADD(P_OUT, R3, P_OUT);
		17:
			`SYNC;
		18:
			`JUMP(1000);
		1000:
			`SET(R0, 5);
		1001:
			`SET(R1, 2);
		1002:
			`ADD_LOOP(R0, R1, R0, 10);
		1003:
			`SHIFT_L_LOOP(R1, 5);
		1004:
			`SHIFT_R_LOOP(R1, 5);
		1005:
			`SUB_LOOP(R0, R1, R0, 10);
		1006:
			`SET(P_OUT, 5);
		1007:
			`MUL_LOOP(P_OUT, R1, P_OUT, 7);
		1008:
			`JUMP(1500);
		1500:
			`SET(R0, 16);
		1501:
			`SET(R1, 2);
		1502:
			`SET(P_OUT, 0);
		1503:
			`SUB(R0, R1, R0);
		1504:
			`JUMP_IF_GT(R0, R1 ,1503);
		1505:
			`SET(R2, 16);
		1506:
			`SET(R3, 2);
		1507:
			`SET(P_OUT, 0);
		1509:
			`SUB(R2, R3, R2);
		1511:
			`JUMP_IF_GTE(R2, R3 ,1509);
		1512:
			`SET(R0, 43690);
		1513:
			`NOT(R0, R1);
		1514:
			`SET(R0, 2);
		1515:
			`SET(R1, 7);
		1516:
			`SET(R2, 0);
		1517:
			`BSHIFT_L(R0, R1, R0);
		1518:
			`BSHIFT_R(R0, R1, R0);
		1519:
			`JUMP(0);
		default:
			romdata = 0;
		endcase
	endfunction
endmodule

module cpu(input clock, reset, input [15:0] port_in_data, input sync, output [15:0] port_out_data);
	localparam integer Loop_counter_width = 16;
	localparam integer Stop_count = 2 ** Loop_counter_width - 1;
	reg [Loop_counter_width - 1:0] loop_counter;
	reg [15:0] pc;
	reg sync_ff,
		sync_ff2;
	wire [39:0] rom_data;
	wire [4:0] command = rom_data[39:35];
	wire [3:0] A_address = rom_data[34:31];
	wire [3:0] B_address = rom_data[30:27];
	wire [3:0] write_address = rom_data[26:23];
	wire [3:0] jump_cond = rom_data[22:19];
	wire [2:0] reserved = rom_data[18:16];
	wire [15:0] immediate_data = rom_data[15:0];
	wire loop_flag = reserved[0];
	wire [15:0] out_dataA,
		out_dataB;
	wire [15:0] alu_data;
	wire [15:0] Aport_data = (command == `SET_com) ? immediate_data : port_in_data;
	wire Reset_loop_counter = (loop_counter < 2) || (loop_counter == Stop_count && immediate_data == 16'b0) ? 1 : 0;
	wire sync_pos_edge_detected = sync_ff & ~sync_ff2;
	register_file register_file(.clock(clock), .reset(reset), .A_address(A_address), .B_address(B_address), .write_address(write_address), 
		.alu_data(alu_data), .out_dataA(out_dataA), .out_dataB(out_dataB), .port_in_data(Aport_data), .port_out_data(port_out_data));
	alu alu(.command(command), .jump_cond(jump_cond), .portA(out_dataA), .portB(out_dataB), .alu_out(alu_data));
	rom rom(.rom_address(pc), .Data(rom_data));
	always @(posedge clock, posedge reset)
	begin
		if (reset)
		begin
			loop_counter <= Stop_count;
		end
		else if (Reset_loop_counter)
		begin
			loop_counter <= Stop_count;
		end
		else if (loop_flag && loop_counter == Stop_count)
		begin
			loop_counter <= immediate_data;
		end
		else if (loop_flag)
		begin
			loop_counter <= loop_counter - 16'b1;
		end
		else
		begin
			loop_counter <= Stop_count;
		end
	end
	always @(posedge clock, posedge reset)
	begin
		if (reset)
			sync_ff <= 0;
		else
			sync_ff <= sync;
	end
	always @(posedge clock, posedge reset)
	begin
		if (reset)
			sync_ff2 <= 0;
		else
			sync_ff2 <= sync_ff;
	end
	always @(posedge clock, posedge reset)
	begin
		if (reset)
		begin
			pc <= 0;
		end
		else if (command == `JMP_com && alu_data[0] == 1'b1)
		begin
			pc <= immediate_data;
		end
		else if (command == `SYNC_com)
		begin
			if (sync_pos_edge_detected)
			begin
				pc <= pc + 1;
			end
			else
			begin
				pc <= pc;
			end
		end
		else if (loop_flag)
		begin
			if (Reset_loop_counter)
			begin
				pc <= pc + 1;
			end
			else
			begin
				pc <= pc;
			end
		end
		else
		begin
			pc <= pc + 1;
		end
	end
	initial
	begin
		$monitor("%d %d", pc, port_out_data);
	end
endmodule

module test;
	reg clk,
		rst,
		sync;
	cpu u(.clock(clk), .reset(rst), .port_in_data(data), .sync(sync), .port_out_data(out));
	initial
	begin
		clk = 0;
		rst = 0;
		sync = 0;
		#2
			rst = 1;
		#4
			rst = 0;
			//sync = 1;
		#200
			$finish;
	end
	always
		#1
			clk = ~clk;
	initial
	begin
		$dumpfile("test.vcd");
		$dumpvars(0, test);
	end
endmodule



```

# 実行結果


```

VCD info: dumpfile test.vcd opened for output.
    x     x
    0     0
    1   100
    2   100
    3   100
    4   100
    5   100
    6   100
    7   100
    8     x
    9     x
   10     x
   11     x
   12     x
   13     x
   14     x
   15     x
   16     x
   17     x


```


以上。