MIPS Processor

A Single-Cycle Mips Processor Implementation as the Computer Architecture course projects.

CPU_Core_MIPS_SingleCycle.v



module CPU_Core_MIPS_SingleCycle( // Datapath of CPU  
	input clk	
);
	
/*********************************Wire Declarations**********************************/
	
	// Control Unit Wires 
	wire reg_wr_en, mux_regDst, mux_aluSrc, dataMem_rd_en, dataMem_wr_en, mux_memtoReg,
		jump, branch, mux_zeroExtend, jal;	
	wire [3:0] aluOp; 
		
	reg [31:0] PC;
	// PC Wires
	wire [31:0] pc_in, pc_add4, pc_relative_addr, jump_addr, mux_branch_out, mux_jump_out;	

	wire branch_is_taken; //For both types of branch: "BNE" and "BEQ" - If branh is TAKEN this signal is 1    
	
	// ALU Control Wires
	wire [3:0] alu_control;
	
	// Instruction Memory Wires
	wire [31:0] Instruction;	
	
	// Register File Wires
	wire [4:0]  reg_wr_addr;
	wire [31:0] reg_rd1_data, reg_rd2_data, reg_wr_data;
		
	// SignExtend Immediate
	wire [31:0] Immediate, zeroExtend, signExtend;
	
	// ALU wires	
	wire [31:0] alu_in2;
	wire [31:0] alu_out;
	wire alu_zero; 
	
	// Data Memory Wires
	wire [31:0] dataMem_out;
	
	wire [31:0] mux_memtoReg_out; // for jal mux
	wire [4:0] mux_regDst_out;   // for jal mux	 		
	
/*********************************Datapath Connections**********************************/
	
	// PC Datapath :
	// harchizi k bekhad tush neveshte she bayad ba clock kar kone : PC Register, Instruction Memory, Data Memory
	always@ (posedge clk) PC <= pc_in;	// Always update PC register when clock goes on Positive Edge		
	
	assign pc_add4 = PC + 4;
	
	assign jump_addr = { pc_add4[31:28],{Instruction[25:0]<<2}};
		
	assign pc_relative_addr = (Immediate<<2) + pc_add4;
	
	assign branch_is_taken = Instruction[26] ? (!alu_zero) : (alu_zero) ; // For both types of branch: "BNE" and "BEQ" - If branh is TAKEN this signal is 1;  
																		 // Opcode of BEQ: 000100 - Opcode of BNE: 000101;
																		 // So if last bit of opcode is 1 (Instruction[26] = 0)
																		 // Its BEQ and if Instructino[26] = 1 its BNE
																		 // For Taken BEQ alu_zero must be 1 and 
																		 // For Taken BEQ alu_zero must be 0 
	Mux32bit Mux_Branch(
		.in1	(pc_add4),
		.in2	(pc_relative_addr),
		.sel 	(branch & branch_is_taken),
		.out 	(mux_branch_out)
	);
	
	Mux32bit Mux_Jump(
		.in1 	(mux_branch_out),
		.in2	(jump_addr),				
		.sel	(jump),
		.out 	(mux_jump_out)
	);
	
	wire jumpReg;
	assign jumpReg = (Instruction[31:26] == 6'b000000 ) && (Instruction[5:0] == 6'b001000); // For Identifying JR Instruction 
	
	Mux32bit Mux_JumpReg(			
		.in1	(mux_jump_out),			
		.in2	(reg_rd1_data),
		.sel	(jumpReg),
		.out	(pc_in)
	);
	
	// Instruction Memory Datapath or Instction Fetch (IF) :
		
	Instruction_Memory Instruction_Memory(
		.addr	(PC),
		.out	(Instruction)
	);		
	
	// Decode and Register File Datapath or Instction Decode (ID) :
	
	Mux5bit Mux_RegDst(
		.in1	(Instruction[20:16]), 
		.in2 	(Instruction[15:11]),
		.sel 	(mux_regDst),
		.out	(mux_regDst_out)
	);
	
	Mux5bit Mux_RegWrAddr(
		.in1	(mux_regDst_out),
		.in2	(5'd31), // $ra = $31
		.sel	(jal),
		.out	(reg_wr_addr)
	);
		
	Register_File Register_File(
		.clk		(clk),		
		.rd1_addr 	(Instruction[25:21]),
		.rd1_data	(reg_rd1_data),
		.rd2_addr	(Instruction[20:16]),
		.rd2_data	(reg_rd2_data),
		.wr_en 		(reg_wr_en),
		.wr_addr	(reg_wr_addr),
		.wr_data	(reg_wr_data)		
	);	
	
	assign signExtend = {{16{Instruction[15]}}, Instruction[15:0]}; // Sign Extend Immediate
	assign zeroExtend = {        16'b0		  , Instruction[15:0]}; // Zero Extend Immediate
	
	Mux32bit Mux_Immediate(
		.in1	(signExtend),
		.in2	(zeroExtend),
		.sel	(mux_zeroExtend),
		.out	(Immediate)
	);
		
	Mux32bit Mux_ALUSrc(
		.in1	(reg_rd2_data),
		.in2	(Immediate),
		.sel	(mux_aluSrc),
		.out 	(alu_in2)	
	);
	
	// Controller Datapath
	
	Controller Controller(
		.opcode			(Instruction[31:26]),	
		.aluOp			(aluOp),		
		.dataMem_rd_en	(dataMem_rd_en),
		.dataMem_wr_en	(dataMem_wr_en),
		.reg_wr_en		(reg_wr_en),
		.mux_aluSrc		(mux_aluSrc),
		.mux_regDst		(mux_regDst),
		.mux_memtoReg	(mux_memtoReg),
		.branch			(branch),
		.jump			(jump),
		.zeroExtend 	(mux_zeroExtend),
		.jal			(jal)
	);
	
	ALU_Control ALU_Control(
		.aluOp			(aluOp),
		.funct			(Instruction[5:0]),
		.alu_control	(alu_control)
	);
	
	// ALU Datapath  or Execute (EX) : 
	
	ALU ALU(
		.in1		(reg_rd1_data),
		.in2 		(alu_in2),
		.control	(alu_control),
		.out		(alu_out),
		.zero		(alu_zero)
	);
	
	// Data Memory Datapath (Mem) :
	
	Data_Memory Data_Memory( 
		.clk		(clk),
		.rd_en		(dataMem_rd_en),
		.addr		(alu_out),		
		.out 		(dataMem_out),
		.wr_en		(dataMem_wr_en),
		.wr_data 	(reg_rd2_data)
	);
	
	
	// Write Back Datapath in Register File Write Data (WR) :
			
	Mux32bit Mux_MemtoReg(
		.in1 	(alu_out),	
		.in2	(dataMem_out),
		.sel	(mux_memtoReg),
		.out 	(mux_memtoReg_out)
	);
	
	Mux32bit Mux_RegWrData(
		.in1 	(mux_memtoReg_out),
		.in2 	(pc_add4),
		.sel 	(jal),
		.out	(reg_wr_data)
	);
															
endmodule

		

Controller.v


/********************************** Controller Unit for MIPS CPU (Using Bit-Map Method) ********************************/  
//!TODO: For Adding New Instructions to CPU :
//	 		1_First you must define the Opcode Macro of New Instructions in the MIPS_DEFINES.v file,
// 	 		2_Then add a switch case with that opcode macro, 
// 	 		3_Then set appropriate string of bits to bitmap register, according to it's controlling signal

`include "MIPS_DEFINES.v"	

module Controller(		
	input [5:0] opcode,	
	output [3:0] aluOp,
	output dataMem_rd_en,
	output dataMem_wr_en,
	output reg_wr_en,
	output mux_aluSrc,
	output mux_regDst,
	output mux_memtoReg,
	output branch,
	output jump,		
	output zeroExtend,
	output jal
);	
	
	reg [13:0] bitmap;
	
	// Setting Controlling Signals Using Bit-Map Method:
	
	assign mux_regDst = bitmap[0];
	assign mux_aluSrc = bitmap[1];
	assign aluOp[0] = bitmap[2];	
	assign aluOp[1] = bitmap[3];	
	assign aluOp[2] = bitmap[4];	
	assign aluOp[3] = bitmap[5];	
	assign dataMem_rd_en = bitmap[6];
	assign dataMem_wr_en = bitmap[7];
	assign mux_memtoReg = bitmap[8];
	assign reg_wr_en = bitmap[9];	
	assign branch = bitmap[10];
	assign jump = bitmap[11];
	assign zeroExtend = bitmap[12];
	assign jal = bitmap[13];
				 
//!TODO: Define Commented opcodes macros in MIPS_DEFINES.v and Set bitmap controlling signals for them/*
	
	always @(opcode) begin 	
		case(opcode)			
/*				          		| jal |zerEx|jump|branch|regWr|memtoReg|memWr|memRead|aluOp |aluSrc|regDst|		// Controlling Signals Table
				          		|-----|-----|----|------|-----|--------|-----|-------|------|------|------| 								               */			 
			`OP_R_TYPE: bitmap=	{ 1'b0, 1'b0,1'b0, 1'b0 , 1'b1,  1'b0  , 1'b0,  1'b0 ,`RTYPE, 1'b0 , 1'b1 };		//R-Type(Register Type Addressing) Instructions: for Arithmetic Operations 					      													
			`OP_ADDI:   bitmap=	{ 1'b0, 1'b0,1'b0, 1'b0 , 1'b1,  1'b0  , 1'b0,  1'b0 ,  `ADD, 1'b1 , 1'b0 };		//I-Type(Immediate Type Addressing) Instructions:			
			`OP_SLTI:   bitmap=	{ 1'b0, 1'b0,1'b0, 1'b0 , 1'b1,  1'b0  , 1'b0,  1'b0 ,  `SLT, 1'b1 , 1'b0 };
			`OP_ANDI:   bitmap=	{ 1'b0, 1'b1,1'b0, 1'b0 , 1'b1,  1'b0  , 1'b0,  1'b0 ,  `AND, 1'b1 , 1'b0 };		//I-Type for Arithmetic Operations:	 		
			`OP_ORI:    bitmap=	{ 1'b0, 1'b1,1'b0, 1'b0 , 1'b1,  1'b0  , 1'b0,  1'b0 ,   `OR, 1'b1 , 1'b0 };
			`OP_XORI:   bitmap=	{ 1'b0, 1'b1,1'b0, 1'b0 , 1'b1,  1'b0  , 1'b0,  1'b0 ,  `XOR, 1'b1 , 1'b0 };			
			`OP_LW:     bitmap=	{ 1'b0, 1'b0,1'b0, 1'b0 , 1'b1,  1'b1  , 1'b0,  1'b1 ,  `ADD, 1'b1 , 1'b0 };		//I-Type for Data Memory Operations: Base Addressing		
			`OP_SW:     bitmap=	{ 1'b0, 1'b0,1'b0, 1'b0 , 1'b0,  1'bx  , 1'b1,  1'b0 ,  `ADD, 1'b1 , 1'bx };						   					 										
	`OP_BEQ,`OP_BNE:    bitmap=	{ 1'b0, 1'b0,1'b0, 1'b1 , 1'b0,  1'bx  , 1'b0,  1'b0 ,  `SUB, 1'b0 , 1'bx };		//I-Type for Branch Operations: PC-Relative Addressing - Controlling Signlas (bitmap) of both type of branchs(BEQ and BNQ) is same  						    					 										
			`OP_J:      bitmap=	{ 1'b0, 1'b0,1'b1, 1'b0 , 1'b0,  1'bx  , 1'b0,  1'b0 ,  `XXX, 1'bx , 1'bx };		//J-Type(Jump Type Addressing) Instructions: for Jump Operations			 	
			`OP_JAL:    bitmap=	{ 1'b1, 1'b0,1'b1, 1'b0 , 1'b1,  1'bx  , 1'b0,  1'b0 ,  `XXX, 1'bx , 1'bx };
//			`OP_JR:     JR is a R-type Instruction, controlling signals and datapath of JR are handled in Core.v			
		endcase	
		
	end	
	
endmodule	


		

MIPS_SingleCycle_tb.v


// It loads "instruction.txt" into instruction memory,
//    simulates MIPS CPU and prints out the internal states
//    of CPU to "output.txt".
// This information helps you to debug.
// You can modify instruction.txt or this testbench file to verify your hardware design 

`define CYCLE_TIME 1000

module TestBench;

	
	reg         clk;	
	integer     i, j , outfile, counter;
	integer file;
	
	always #(`CYCLE_TIME/2) clk = ~clk;    

	CPU_Core_MIPS_SingleCycle CPU(
			.clk  (clk)			
	);
    
	initial begin
		CPU.PC = 32'b0; // Initialize PC = 0; 
		// Initialize instruction memory ZERO
		for(i=0; i<256; i=i+1) begin
			CPU.Instruction_Memory.memory[i] = 32'b0;
		end
    
		// Initialize data memory ZERO
		for(i=0; i<32; i=i+1) begin
			CPU.Data_Memory.memory[i] = 8'b0;
		end    
        
		// initialize Register File ZERO
		for(i=0; i<32; i=i+1) begin
			CPU.Register_File.registers[i] = 32'b0;
		end

//////////////////////////////////////////////////////////////
		
		//// Initialize Data Memory
		// CPU.Data_Memory.memory[0] = 8'b1;       // n = 5 for example
		// CPU.Data_Memory.memory[4] = 8'b10;				

		// Initialize Register File 	
		CPU.Register_File.registers[1] = 32'b0; // li $1, 0 # first number of fibonachi
		CPU.Register_File.registers[2] = 32'b1;	//li $2, 1  # second number of fibonachi
		CPU.Register_File.registers[3] = 32'b1; // li $3, 1 # for adding n. ( n = n+1)
		CPU.Register_File.registers[4] = 32'b1; // li $4, 1 # for n or number of loop executions or number of fibonachi 
		CPU.Register_File.registers[5] = 32'b1111;//  li $5, 15 # for determining which number of fibonachi wanting to evaluate
						
		//// Instruction Memory wih Machine Codes : 			
		////loop: 		
		// CPU.Instruction_Memory.memory[1] = 32'h00221020;// add $2, $1, $2
		// CPU.Instruction_Memory.memory[2] = 32'h00410822; //sub $1, $2, $1
		// CPU.Instruction_Memory.memory[3] = 32'h00832020; //add $4, $4, $3			
		// CPU.Instruction_Memory.memory[4] = 32'h1485fffc;// bnq $4, $5, loop:

			
/////////////////////////////////////////////////////////////////////////////////////		
    	
		//Load instructions into instruction memory
		file = $fopen ("program.hex", "r+"); 

		for(j = 0; j< 40; j = j+1) begin
			$fscanf(file, "%h", CPU.Instruction_Memory.memory[j]);
		end
    
		// Open output file
		outfile = $fopen("output.txt") | 1;
    
		
    
		counter = 0;
		clk = 0;		
    
		#(`CYCLE_TIME/4);			
    
	end
  
	always@(posedge clk) begin
//		if(counter == 10)    // stop after 60 cycles
//			$stop;
        
			// print PC
		$fdisplay(outfile, "PC = %d", CPU.PC);
    
		// print Registers
		$fdisplay(outfile, "Registers");
		$fdisplay(outfile, "R0(r0) =%d, R8 (t0) =%d, R16(s0) =%d, R24(t8) =%d", CPU.Register_File.registers[0], CPU.Register_File.registers[8] , CPU.Register_File.registers[16], CPU.Register_File.registers[24]);
		$fdisplay(outfile, "R1(at) =%d, R9 (t1) =%d, R17(s1) =%d, R25(t9) =%d", CPU.Register_File.registers[1], CPU.Register_File.registers[9] , CPU.Register_File.registers[17], CPU.Register_File.registers[25]);
		$fdisplay(outfile, "R2(v0) =%d, R10(t2) =%d, R18(s2) =%d, R26(k0) =%d", CPU.Register_File.registers[2], CPU.Register_File.registers[10], CPU.Register_File.registers[18], CPU.Register_File.registers[26]);
		$fdisplay(outfile, "R3(v1) =%d, R11(t3) =%d, R19(s3) =%d, R27(k1) =%d", CPU.Register_File.registers[3], CPU.Register_File.registers[11], CPU.Register_File.registers[19], CPU.Register_File.registers[27]);
		$fdisplay(outfile, "R4(a0) =%d, R12(t4) =%d, R20(s4) =%d, R28(gp) =%d", CPU.Register_File.registers[4], CPU.Register_File.registers[12], CPU.Register_File.registers[20], CPU.Register_File.registers[28]);
		$fdisplay(outfile, "R5(a1) =%d, R13(t5) =%d, R21(s5) =%d, R29(sp) =%d", CPU.Register_File.registers[5], CPU.Register_File.registers[13], CPU.Register_File.registers[21], CPU.Register_File.registers[29]);
		$fdisplay(outfile, "R6(a2) =%d, R14(t6) =%d, R22(s6) =%d, R30(s8) =%d", CPU.Register_File.registers[6], CPU.Register_File.registers[14], CPU.Register_File.registers[22], CPU.Register_File.registers[30]);
		$fdisplay(outfile, "R7(a3) =%d, R15(t7) =%d, R23(s7) =%d, R31(ra) =%d", CPU.Register_File.registers[7], CPU.Register_File.registers[15], CPU.Register_File.registers[23], CPU.Register_File.registers[31]);

		// print Data Memory
		$fdisplay(outfile, "Data Memory: 0x00 =%d", {CPU.Data_Memory.memory[3] , CPU.Data_Memory.memory[2] , CPU.Data_Memory.memory[1] , CPU.Data_Memory.memory[0] });
		$fdisplay(outfile, "Data Memory: 0x04 =%d", {CPU.Data_Memory.memory[7] , CPU.Data_Memory.memory[6] , CPU.Data_Memory.memory[5] , CPU.Data_Memory.memory[4] });
		$fdisplay(outfile, "Data Memory: 0x08 =%d", {CPU.Data_Memory.memory[11], CPU.Data_Memory.memory[10], CPU.Data_Memory.memory[9] , CPU.Data_Memory.memory[8] });
		$fdisplay(outfile, "Data Memory: 0x0c =%d", {CPU.Data_Memory.memory[15], CPU.Data_Memory.memory[14], CPU.Data_Memory.memory[13], CPU.Data_Memory.memory[12]});
		$fdisplay(outfile, "Data Memory: 0x10 =%d", {CPU.Data_Memory.memory[19], CPU.Data_Memory.memory[18], CPU.Data_Memory.memory[17], CPU.Data_Memory.memory[16]});
		$fdisplay(outfile, "Data Memory: 0x14 =%d", {CPU.Data_Memory.memory[23], CPU.Data_Memory.memory[22], CPU.Data_Memory.memory[21], CPU.Data_Memory.memory[20]});
		$fdisplay(outfile, "Data Memory: 0x18 =%d", {CPU.Data_Memory.memory[27], CPU.Data_Memory.memory[26], CPU.Data_Memory.memory[25], CPU.Data_Memory.memory[24]});
		$fdisplay(outfile, "Data Memory: 0x1c =%d", {CPU.Data_Memory.memory[31], CPU.Data_Memory.memory[30], CPU.Data_Memory.memory[29], CPU.Data_Memory.memory[28]});
	
		$fdisplay(outfile, "\n");
    
		counter = counter + 1;
	end

  
endmodule



		

ALU_Control.v



`include "MIPS_DEFINES.v"

module ALU_Control (
	input [3:0] aluOp,
	input [5:0] funct,
	output reg [3:0] alu_control
);
	
	always @(funct or aluOp) begin
		if(aluOp == `RTYPE)	begin	
			case (funct)		  		 		 							
				`FUNCT_ADD:	alu_control <= `ADD;
				`FUNCT_SUB: 	alu_control <= `SUB;
				`FUNCT_AND: 	alu_control <= `AND;
				`FUNCT_OR: 	alu_control <= `OR; 
				`FUNCT_NOR: 	alu_control <= `NOR;
				`FUNCT_SLT: 	alu_control <= `SLT;
				`FUNCT_XOR: 	alu_control <= `XOR;
				default: 	alu_control <= `ADD;			
			endcase
		end
		
		else begin
			alu_control <= aluOp;			
		end
	end		
	
endmodule


		

ALU.v



`include "MIPS_DEFINES.v"
module ALU(
	input [31:0] 		in1,
	input [31:0] 		in2,
	input [3:0]			control,
	output reg [31:0]	out,
	output				zero		
);

	assign zero = (out == 0);
	
	always @(control, in1, in2) begin 
		case (control) 
			`AND: out <= in1 & in2;
			`OR: out <= in1 | in2;
			`ADD: out <= in1 + in2;
			`SUB: out <= in1 - in2;
			`SLT: out <= $signed(in1) < $signed(in2) ? 1 : 0; // Signed comparison in Verilog with key word  $signed(x) !
			`NOR: out <= ~(in1 | in2); // result is nor
			`XOR: out <= in1 ^ in2;
			default: out <= 0;
		endcase			
	end
	
endmodule	



		

Data_Memory.v


`include "MIPS_DEFINES.v"

module Data_Memory(
	input clk,
	input [31:0] addr,
	input rd_en,	
	output [31:0] out,	
	input  wr_en,	
	input [31:0] wr_data		
);	
	reg	    [7:0]   memory  [0:`DATA_MEM_SIZE-1];
	
	assign  out = (rd_en) ? {memory[addr+3], memory[addr+2], memory[addr+1], memory[addr]} : 32'b0;
	
	always @(posedge clk) begin
		if(wr_en) begin		
			memory[addr+3] <= wr_data[31:24];
			memory[addr+2] <= wr_data[23:16];
			memory[addr+1] <= wr_data[15:8];
			memory[addr]   <= wr_data[7:0];
		end	
	end
	
endmodule	

		

Register_File.v

		
module Register_File (
	input clk,
	
	input [4:0] rd1_addr,
	output [31:0] rd1_data,
	
	input [4:0] rd2_addr,	
	output [31:0] rd2_data,	
	
	input wr_en, 
	input [4:0] wr_addr,
	input [31:0] wr_data 	
);
	
	reg [31:0] registers [0:31];
	
	assign rd1_data = registers[rd1_addr];
	assign rd2_data = registers[rd2_addr];
	
	always @(posedge clk) begin
		if (wr_en) 
			registers[wr_addr] <= wr_data;	
	end
	
endmodule