// N-105 : A cut down version of Altera's Nios
// 
// Written by Rosemary Francis 2004
// Sanity checked by Daniel Hulme and Simon Moore 2004
//

//top level module

module cpu(clk, 
			reset,
			//master interface to avalon bus for memory access
			waitrequest_mem_access,
			address_mem_access,
			byteenable_mem_access,
			read_mem_access,
			readdata_mem_access,
			write_mem_access,
			writedata_mem_access,
			//master interface to avalon bus for instruction fetch
			waitrequest_instr_fetch,
			address_instr_fetch,
			byteenable_instr_fetch,
			read_instr_fetch,
			readdata_instr_fetch);
			


input clk;
input reset;

output [15:0]address_mem_access;
output [3:0] byteenable_mem_access;
output read_mem_access;
input [15:0] readdata_mem_access;
output write_mem_access;
output [15:0] writedata_mem_access;
input waitrequest_mem_access;

output [15:0]address_instr_fetch;
output [3:0] byteenable_instr_fetch;
output read_instr_fetch;
input [15:0] readdata_instr_fetch;
input waitrequest_instr_fetch;

wire [15:0] pc;
wire [3:0]flags;
wire [3:0] new_flags;
wire set_cond_en;
wire alu_en;
wire br_unit_en;
wire load_n_store;
wire return_en;
wire [15:0] branch_address;
wire bsr_en;
wire [3:0] index_a;
wire [3:0] index_b;
wire [15:0] contents_a;
wire [15:0] contents_b;
wire [3:0] alu_opcode;
wire [6:0] imm7;
wire [10:0] imm11;
wire [15:0] load_result;
wire [15:0] alu_result;
wire [15:0] new_return_addr;
wire [15:0] dest_contents;
wire write_en;
wire instruction_ready;
wire [15:0] next_instruction;
wire advance_pipeline;
wire mem_is_busy;
wire ld_st_coming;

assign advance_pipeline = (reset)? 1'b1 : !mem_is_busy & instruction_ready;

// advance_pipline indicates to instruction decode that it must update the instruction 
// with the value of next_instruction and to the branch unit that it must update the pc
// with the next address. 

//module instantiation...

instruction_fetch the_instruction_fetch(.pc(pc), 
				.next_instruction(next_instruction),
				.instruction_ready(instruction_ready),
				//interface to avalon bus (via top level module)
				.waitrequest(waitrequest_instr_fetch),
				.address(address_instr_fetch),
				.byteenable(byteenable_instr_fetch),
				.read(read_instr_fetch),
				.readdata(readdata_instr_fetch));
								
instruction_decode the_instruction_decode(.clk(clk),
				.reset(reset),
				.advance_pipeline(advance_pipeline),
				.next_instruction(next_instruction),
				.ld_st_coming(ld_st_coming),
  				.condition_flags(flags),
				.set_cond_en(set_cond_en),
				.alu_en(alu_en),
				.alu_opcode(alu_opcode), //Simon's Signal!
				.imm7(imm7),
				.imm11(imm11),
				.index_a(index_a),
				.index_b(index_b),
				.br_unit_en(br_unit_en),	
				.load_n_store(load_n_store), 
				.return_en(return_en),
				.bsr_en(bsr_en));
				
mem_access_unit the_mem_access_unit(.clk(clk),
				.reset(reset),
				.load_n_store(load_n_store), 
				.contents_a(contents_a), 	
				.contents_b(contents_b), 
				.load_result(load_result),
				.mem_is_busy(mem_is_busy),
				.advance_pipeline(advance_pipeline),
				.ld_st_coming(ld_st_coming),
		  		//interface to avalon bus (via top level module)
				.waitrequest(waitrequest_mem_access),
				.address(address_mem_access),
				.byteenable(byteenable_mem_access),
				.read(read_mem_access),
				.readdata(readdata_mem_access),
				.write(write_mem_access),
				.writedata(writedata_mem_access));

alu the_alu(.contents_a(contents_a), 
	.contents_b(contents_b), 
	.imm7(imm7),
	.opcode(alu_opcode),
	.result(alu_result),
	.flags(new_flags));

branch_unit the_branch_unit(.clk(clk),
			.reset(reset),
			.return_en(return_en),
			.return_addr(contents_a),
			.new_return_addr(new_return_addr),
			.imm11(imm11),
			.advance_pipeline(advance_pipeline),
			.pc(pc),
			.br_unit_en(br_unit_en));
			
condition_unit the_condition_unit(.clk(clk),
				.flags_in(new_flags),
				.set_cond_en(set_cond_en),
				.flags(flags));

reg_fetch the_reg_fetch(.clk(clk),
		 .reset(reset),
		 .br_unit_en(br_unit_en),
		 .index_a(index_a), 
		 .index_b(index_b), 
		 .contents_a(contents_a), 
 		 .contents_b(contents_b),
		 .dest_contents(dest_contents),
		 .write_en(write_en),
		 .read_n_write(!instruction_ready));
		
write_back the_write_back(.alu_result(alu_result),
			.alu_en(alu_en),
			.load_result(load_result),
			.load_en(load_n_store),
			.bsr_en(bsr_en),
			.ret_addr(new_return_addr),
			.dest_contents(dest_contents),
			.write_en(write_en));
			
endmodule

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//********************************************************************************************************************//
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

//this returns the next instruction and is stage one in the two stage pipeline

module instruction_fetch(pc, 
						next_instruction, 
						instruction_ready, 
						//interface to avalon bus (via top level module)
						waitrequest,
						readdata,
						address,
						byteenable,
						read);
input [15:0] pc;
output [15:0] next_instruction;
output instruction_ready; 

//interface to avalon land
input waitrequest;
input [15:0] readdata;
output [15:0]address;
output [3:0] byteenable;
output read;


assign address = pc;
assign byteenable = 4'b0011;

//byteenable is set to write to the first two bytes only since our word size is only 16 bits

assign read = 1;
assign next_instruction = readdata;
assign instruction_ready = !waitrequest;

endmodule


//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

//below are the modules for stage two of the pipeline

module instruction_decode(clk,
						 reset,
						 advance_pipeline,
						 ld_st_coming,
						 next_instruction,
						 condition_flags,
						 set_cond_en,	
						 alu_opcode,
						 imm7,
						 imm11,
						 index_a,
						 index_b,
						 alu_en,			
						 br_unit_en,	
						 load_n_store,
						 return_en,
						 bsr_en);	
						
input clk;
input reset;
input advance_pipeline;
input [15:0] next_instruction;
input [3:0] condition_flags;

output ld_st_coming;
output [3:0] alu_opcode;
output [6:0] imm7;
output [10:0] imm11;
output [3:0] index_a;
output [3:0] index_b;
output set_cond_en;		
output alu_en;			
output br_unit_en;
output load_n_store;	
output return_en;		
output bsr_en;

//ld_st_coming tells the memory to set the mem_is_busy flag
//set_cond_en tells the condition unit to update the flags from the ALU
//alu_en tells write back to use the ALU result	
//br_unit_en tells the branch unit to update the pc with a branch value
//load_n_store tells the mem access unit to perform load (not store) and tells the write back to store the result	
//return_en tells the branch unit to update pc to an absolute address and the reg fetch unit to fetch r15
//bsr_en tells the write back unit whether to save the current pc in r15

reg [15:0] instruction;
reg skip;

wire [4:0] opcode;
wire [3:0] imm4;

always@(posedge clk)
	instruction <= (reset) ? 16'd0 : (advance_pipeline) ? next_instruction : instruction; 

assign ld_st_coming = (!skip | (skip & opcode != 5'b11111)) & (next_instruction[4:3] == 2'b10);

assign opcode = instruction [4:0];
assign imm4 = instruction [11:8];   
assign imm7 = instruction [11:5];	
assign imm11 = instruction [15:5];
assign index_a = instruction [15:12];
assign index_b = instruction [11:8];
assign alu_opcode = opcode[3:0];

assign set_cond_en = !skip & ~opcode[4] 				//ALU
						& ~(opcode[3:2] == 2'b10) 		//not a shift
						& ~(opcode[3:0] == 4'b0011)		//not "not"
						& ~(opcode[2:0] == 4'b100); 	//not "mov" or "movi"
	
assign alu_en = !skip & ~opcode[4] 
				& ~(opcode[2:0] == 3'b111);	//ALU and not "cmp" or "cmpi"	
						
assign br_unit_en 	= !skip & (opcode[4:2] == 3'b110);		//"br" or "bsr" or "ret"
assign load_n_store = !skip & (opcode[4:2] == 3'b100);  	//"ld"
assign return_en 	= !skip & (opcode[4:0] == 5'b11010);	//"ret"
assign bsr_en 		= !skip & (opcode[4:0] == 5'b11001); 	//"bsr"

 //condition decode
wire condition;
parameter C = 0;
parameter Z = 1;
parameter V = 2;
parameter N = 3;
 
assign condition =	
			(imm4[3:1] == 3'd0) ?  condition_flags[C]:
			(imm4[3:1] == 3'd1) ?  condition_flags[Z]:
			(imm4[3:1] == 3'd2) ?  condition_flags[N]:
			(imm4[3:1] == 3'd3) ?  ~(condition_flags[N] ^ condition_flags[V]):
			(imm4[3:1] == 3'd4) ?  condition_flags[Z] | (condition_flags[N] ^ condition_flags[V]):
			(imm4[3:1] == 3'd5) ?  condition_flags[V]:
			(imm4[3:1] == 3'd6) ?  condition_flags[C] | condition_flags[Z]: 
			1'b0;

//ifs handling
always@(posedge clk)
	skip <= (reset) ? 1'b0 :
			(opcode == 5'b11111) ? //ifs
				(imm4[0] ? ~condition : condition):
			(advance_pipeline) ? 1'b0 :	skip;

endmodule

//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

module mem_access_unit(clk,
						reset,
						load_n_store,
						contents_a, 
						contents_b,
						ld_st_coming,
						advance_pipeline, 
						load_result,
						mem_is_busy,
						//interface to avalon bus (via top level module)
						waitrequest,
						readdata,
						address,
						byteenable,
						read,
						write,
						writedata);
		
input clk;		
input reset;
input load_n_store;
input [15:0] contents_a;
input [15:0] contents_b;
input ld_st_coming;
input advance_pipeline;

output [15:0] load_result;
output mem_is_busy;

//interface to avalon land!!
input waitrequest;
input [15:0] readdata;
output [15:0]address;
output [3:0] byteenable;
output read;
output write;
output [15:0] writedata;
	
assign address = contents_b;
assign load_result = readdata;
assign writedata = contents_a;
assign byteenable = 4'b0011;

reg read;
reg write;
reg mem_is_busy;

always@(posedge clk)
begin
			
	mem_is_busy <= (reset)? 1'b0 :
				 (advance_pipeline & ld_st_coming) ? 1'b1 :
				 ((read|write) & !waitrequest) ? 1'b0 : mem_is_busy;

	read <= load_n_store & mem_is_busy;
	write <= !load_n_store & mem_is_busy;

end


endmodule

//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

module alu(contents_a, 
			contents_b, 
			imm7,			
			opcode,			//alu opcode - bottom 4 bits of instruction opcode
			result,
			flags);
			
input [15:0] contents_a;
input [15:0] contents_b;
input [6:0] imm7;
input [3:0] opcode;

output [15:0] result;
output [3:0] flags;

wire [16:0] result_c; //result with carry

wire [15:0] simm16;  //immediate extended for signed arithmetic
assign simm16 = (imm7[6]) ? {9'b111111111, imm7} : {9'd0, imm7};
wire [15:0] uimm16;  //immediate extended for signed arithmetic
assign uimm16 = {9'b000000000, imm7};

wire [3:0] imm4;  // 4 bit immediate for shifts etc
assign imm4 = imm7[6:3];

//shift/rotate...
wire [15:0] shift_result;
shifter the_shifter(.contents_a(contents_a),
					.imm4(imm4),
					.opcode(opcode[1:0]),
					.shift_result(shift_result));

//instruction decode...
assign result_c = 
	(opcode == 4'd0) ? contents_a | contents_b: 	//or
	(opcode == 4'd1) ? contents_a & contents_b:		//and
	(opcode == 4'd2) ? contents_a ^ contents_b: 	//xor
	(opcode == 4'd3) ? ~contents_a: 				//not
	
	(opcode == 4'd4) ? contents_b:					//mov
	(opcode == 4'd5) ? contents_a + contents_b: 	//add
	(opcode == 4'd6) ? contents_a - contents_b:		//sub
	(opcode == 4'd7) ? contents_a - contents_b: 	//cmp
	
	(opcode[3:2] == 2'b10) ? shift_result:			//shift or rotate
													//see shifter module
	
	(opcode == 4'd12) ? simm16:					//movi signed
	(opcode == 4'd13) ? contents_a + uimm16 :	//addi unsigned
	(opcode == 4'd14) ? contents_a - uimm16 :	//subi unsigned	 
	(opcode == 4'd15) ? contents_a - simm16 : 	//cmpi signed 
	17'hxxxxx; //undef

assign result = result_c [15:0];

//c : carry out of add or borrow out of sub
assign flags[0] = !(opcode[3:2] == 2'b00) & result_c[16];

//z : result is 0
assign flags[1] = (result == 16'd0);

//v : arithmetic overflow i.e. wrong sign
assign flags[2] = 
	//add
	(opcode == 4'd5) ?  					
		(contents_a[15] == contents_b[15]) & (contents_a[15] == !result_c[15]): 
	//sub or cmp		
	((opcode == 4'd6) | (opcode == 4'd7)) ? 	
		(contents_a[15] == !contents_b[15]) & (contents_a[15] == !result_c[15]) & (result != 16'd0): 
	//addi		
	(opcode == 4'd13) ? 				
		(!contents_a[15] & result_c[15]) :	
	//subi 		
	(opcode == 4'd14) ? 				
		(contents_a[15] & !result_c[15] & (result != 16'd0)): 	
	//cmpi		
	(opcode == 4'd15) ? 					
		(contents_a[15] & !result_c[15] & !simm16[15] & (result != 16'd0)): 
	1'b0;
		
//n : sign of result
assign flags[3] = result[15]; 


endmodule

//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

module shifter(contents_a,
				imm4,
				opcode,
				shift_result);
				
input [15:0] contents_a;
input [3:0] imm4;
input [1:0] opcode;

output [15:0] shift_result;

wire [31:0] not_shifted;

assign not_shifted = (opcode == 2'b00) ? {contents_a, 16'd0} :   				//lsli
				(opcode == 2'b01) ? {16'd0, contents_a} :						//lsri
				(opcode == 2'b10) ? (contents_a[15] ? 
										{16'b1111111111111111, contents_a} : 
										{16'd0, contents_a}) :					//asri
				(opcode == 2'b11) ? {contents_a, contents_a} : 					//roti
				32'hxxxxxxxx; 											
			
wire [23:0] shifted_by8;
wire [19:0] shifted_by4;
wire [17:0] shifted_by2;
wire [16:0] shifted_by1;

assign shifted_by8 		= (imm4[3]) ? not_shifted[31:8] : not_shifted[23:0];
assign shifted_by4 		= (imm4[2]) ? shifted_by8[23:4] : shifted_by8[19:0];
assign shifted_by2 		= (imm4[1]) ? shifted_by4[19:2] : shifted_by4[17:0];
assign shifted_by1	 	= (imm4[0]) ? shifted_by2[17:1] : shifted_by2[16:0];		

assign shift_result = (imm4 == 4'd0) ? contents_a : shifted_by1[15:0];

endmodule

//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

module branch_unit(clk,
					reset,
					return_en,  
					return_addr,
					imm11,
					advance_pipeline,
					br_unit_en,
					new_return_addr,
					pc);
					
input clk;
input reset;
input return_en;
input [15:0] return_addr;
input [10:0] imm11;
input advance_pipeline;
input br_unit_en;

output [15:0] new_return_addr;
output [15:0] pc;
 
//note that the pipeline is not flushed so the instruction following a branch is executed before the branch
// If the instruction after the branch is an ifs, the obvious happens; if
// it is another branch, weird things happen.
//a return is a branch back to two instructions after the branch to subroutine. 

wire [15:0] relative_addr; 			//sign extend relative address
assign relative_addr = imm11[10] ? 
				{5'b11111, imm11, 1'b0}: 
				{5'b00000, imm11, 1'b0};
assign new_return_addr = pc + 2;

/*************/
reg [15:0] pc;
/*************/

always@(posedge clk or posedge reset)
begin
	if(reset)
		pc <= 16'd0;
	else if(advance_pipeline)
		if(br_unit_en)
			pc <=  (return_en) ? return_addr : (pc + relative_addr);
		else 
			pc <= pc + 2;
end

endmodule

//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

module condition_unit(clk,
						flags_in,
						set_cond_en,
						flags);
						
input clk;
input [3:0] flags_in;
input set_cond_en;

output [3:0] flags;

reg [3:0] flags; //NVZC

always@(posedge clk)
begin
	if(set_cond_en)
		flags <= flags_in;
end

endmodule

//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////


			

module reg_fetch(clk,
				 reset,
				 br_unit_en,
				 index_a, 
				 index_b,
				 dest_contents,
				 write_en,
				 read_n_write, 
				 contents_a, 
				 contents_b);

input clk;
input reset;
input br_unit_en;
input [3:0] index_a;
input [3:0] index_b;
input [15:0] dest_contents;
input write_en;
input read_n_write;

output [15:0] contents_a;
output [15:0] contents_b;

//******************************
//register file  16 * [15:0]

reg [15:0] register_file [15:0];

//******************************

	//if instruction is a return, fetch r15 instead of ra 
assign contents_a = 
	(br_unit_en & read_n_write) ? register_file[ 4'd15 ] : 
	(read_n_write) ?
				(index_a == 0) ? register_file[0] :
				(index_a == 1) ? register_file[1] :
				(index_a == 2) ? register_file[2] :
				(index_a == 3) ? register_file[3] :
				(index_a == 4) ? register_file[4] :
				(index_a == 5) ? register_file[5] :
				(index_a == 6) ? register_file[6] :
				(index_a == 7) ? register_file[7] :
				(index_a == 8) ? register_file[8] :
				(index_a == 9) ? register_file[9] :
				(index_a == 10) ? register_file[10] :
				(index_a == 11) ? register_file[11] :
				(index_a == 12) ? register_file[12] :
				(index_a == 13) ? register_file[13] :
				(index_a == 14) ? register_file[14] :
				(index_a == 15) ? register_file[15] : contents_a : contents_a;			
				
assign contents_b = (read_n_write) ?
					(index_b == 0) ? register_file[0] :
					(index_b == 1) ? register_file[1] :
					(index_b == 2) ? register_file[2] :
					(index_b == 3) ? register_file[3] :
					(index_b == 4) ? register_file[4] :
					(index_b == 5) ? register_file[5] :
					(index_b == 6) ? register_file[6] :
					(index_b == 7) ? register_file[7] :
					(index_b == 8) ? register_file[8] :
					(index_b == 9) ? register_file[9] :
					(index_b == 10) ? register_file[10] :
					(index_b == 11) ? register_file[11] :
					(index_b == 12) ? register_file[12] :
					(index_b == 13) ? register_file[13] :
					(index_b == 14) ? register_file[14] :
					(index_b == 15) ? register_file[15] : contents_b :contents_b;

always@(posedge clk)
	if(reset)
	begin
		register_file[0] <= 16'hxxxx;
		register_file[1] <= 16'hxxxx;
		register_file[2] <= 16'hxxxx;
		register_file[3] <= 16'hxxxx;
		register_file[4] <= 16'hxxxx;
		register_file[5] <= 16'hxxxx;
		register_file[6] <= 16'hxxxx;
		register_file[7] <= 16'hxxxx;
		register_file[8] <= 16'hxxxx;
		register_file[9] <= 16'hxxxx;
		register_file[10] <= 16'hxxxx;
		register_file[11] <= 16'hxxxx;
		register_file[12] <= 16'hxxxx;
		register_file[13] <= 16'hxxxx;
		register_file[14] <= 16'hxxxx;
		register_file[15] <= 16'hxxxx;
	end
	else
	if(write_en & !read_n_write)  //if bsr write to r15 not destination (ra)
		if(br_unit_en)
			register_file[ 4'd15 ] <= dest_contents;
		else begin
			register_file[0] <= (4'd0 == index_a) ? dest_contents : register_file[0];
			register_file[1] <= (4'd1 == index_a) ? dest_contents : register_file[1];
			register_file[2] <= (4'd2 == index_a) ? dest_contents : register_file[2];
			register_file[3] <= (4'd3 == index_a) ? dest_contents : register_file[3];
			register_file[4] <= (4'd4 == index_a) ? dest_contents : register_file[4];
			register_file[5] <= (4'd5 == index_a) ? dest_contents : register_file[5];
			register_file[6] <= (4'd6 == index_a) ? dest_contents : register_file[6];
			register_file[7] <= (4'd7 == index_a) ? dest_contents : register_file[7];
			register_file[8] <= (4'd8 == index_a) ? dest_contents : register_file[8];
			register_file[9] <= (4'd9 == index_a) ? dest_contents : register_file[9];
			register_file[10] <= (4'd10 == index_a) ? dest_contents : register_file[10];
			register_file[11] <= (4'd11 == index_a) ? dest_contents : register_file[11];
			register_file[12] <= (4'd12 == index_a) ? dest_contents : register_file[12];
			register_file[13] <= (4'd13 == index_a) ? dest_contents : register_file[13];
			register_file[14] <= (4'd14 == index_a) ? dest_contents : register_file[14];
			register_file[15] <= (4'd15 == index_a) ? dest_contents : register_file[15];
		end


endmodule

//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

module write_back(alu_result,
					alu_en,
					load_result,
					load_en,
					bsr_en,
					ret_addr, //r15
					dest_contents,
					write_en);
					
input [15:0] alu_result;
input [15:0] load_result;
input [15:0] ret_addr;
input alu_en;
input load_en;
input bsr_en;

output [15:0] dest_contents;
output write_en;

assign dest_contents = (alu_en) ? alu_result :
						(bsr_en) ? ret_addr :
						(load_en) ? load_result : 16'hxxxx;
						
assign write_en = (alu_en | load_en | bsr_en);

			
endmodule

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
