`timescale 1ns / 1ps
//////////////////////////////////////////////////////////////////////////////////
// Company: 
// Engineer: Jürgen Böhm
// 
// Create Date:    02:08:39 02/18/2007 
// Design Name: 
// Module Name:    cpu 
// Project Name: 
// Target Devices: 
// Tool versions: 
// Description: 
//
// Dependencies: 
//
// Revision: 
// Revision 0.01 - File Created
// Additional Comments: 
//
//////////////////////////////////////////////////////////////////////////////////


module comp_lt ( a, b, is_lt );

input[31:0] a,b;
output reg is_lt;

always @(a,b)
	begin
		if (a[31]==b[31])
			is_lt <= (a[30:0] < b[30:0]);
		else
			is_lt <= a[31];		
	end

endmodule


module alu ( opcodeIn, opAin, opBin, opCin, aluOut, overflow, carry, zero, freeze, clk );

input[31:0] opAin, opBin, opCin;
input[5:0] opcodeIn;

output[31:0] aluOut;

output overflow, carry, zero;

input freeze;

input clk;

reg[31:0] aluOut;

assign zero = (aluOut == 0);


// experiment in staging the ALU:


/*
reg[31:0] opA, opB, opC;
reg[5:0] opcode;

always @(posedge clk)
	begin
		opA <= opAin;
		opB <= opBin;
		opC <= opCin;
		opcode <= opcodeIn;
	end

*/
wire[31:0] opA = opAin;
wire[31:0] opB = opBin;
wire[31:0] opC = opCin;

wire[5:0] opcode = opcodeIn;


// the comparators

wire aLTb;
wire aGTb;

comp_lt comp_lt0 (opA, opB, aLTb );
//comp_lt comp_lt1 (opB, opA, aGTb );


wire aEQb;
wire aLTEb;
wire aGTEb;

assign aGTb = ~(aEQb | aLTb);

assign aEQb = (opA == opB);
assign aLTEb = aLTb | aEQb;
assign aGTEb = aGTb | aEQb;


// the main alu

wire[31:0] lenVect = 32'd12 + {opA[29:0], 2'b0};

always @(posedge clk)
	begin
		if (~freeze)
			begin
				case (opcode)
					0: aluOut <= {opA[31:2] + opB[31:2],2'b0};
					1: aluOut <= {opA[31:2] - opB[31:2],2'b0};

					2: aluOut <= aLTEb ? 4 : opC;
					3: aluOut <= aGTEb ? 4 : opC;
					4: aluOut <= aLTb ? 4 : opC;
					5: aluOut <= aGTb ? 4 : opC;
					6: aluOut <= aEQb ? 4: opC;

					11: aluOut <= opA | opB;
					12: aluOut <= opA & opB;
		//			13: aluOut <= ~(opA | opB);
		
					// barrel shift operations (costly in space)
		
					14: aluOut <= (opA >> (opB[6:2]));
					15: aluOut <= (opA << (opB[6:2]));
						


					16: aluOut <= {opA[31:2], opA[1:0] & opB[3:2]};
					17: aluOut <= {opA[31:2], opA[1:0] | opB[3:2]};
					18: aluOut <= {opA[31:2], opA[1:0] ^ opB[3:2]};
					19: aluOut <= (opA[1:0] == opB[3:2]) ? 4 : opC;
					20: aluOut <= {28'b0,opB[1:0],2'b0};
					21: aluOut <= {opA[31:2],opB[3:2]};

					24,25: aluOut <= (opA[31:2] == opB[31:2]) ? 4 : opC;
					26: aluOut <= {opA[31:2] & opB[31:2], opA[1:0]};
					27: aluOut <= {opA[31:2] | opB[31:2], opA[1:0]};
					28: aluOut <= {opB[31:2],2'b0};


					32: if (|(opB[2:0]))
								begin
									aluOut <= {opB[31:3],3'b0} + 32'd8;
								end
							else
								begin
									aluOut <= opB;
								end

					33: aluOut <= lenVect;
					34: aluOut <= {opB[31:2],2'b10};
					35: aluOut <= {opB[31:2],2'b00} + {opA[31:2],2'b00};
					36: aluOut <= {opA[31:2],2'b00} + {opB[31],opB[31], opB[31:2]};

					37: aluOut <= {opA[17:2],opB[17:2]};
					//38: aluOut <= {opA[31:2],2'b00} + {opB[31],opB[31],opB[31:2]};
					38: aluOut <= opA + {opB[31],opB[31],opB[31:2]};
					39: aluOut <= {opB[30:0],1'b0};
					40: aluOut <= {1'b0,opB[31:1]};
					41: aluOut <= opA + {{14{opB[15]}}, opB[15:0],2'b0};
					42: aluOut <= 32'd16;
					43: aluOut <= opA + 32'd8;  //tp = closure + 8
					44: aluOut <= opA + 32'd16; //litbase = tp + 16
					45: aluOut <= opA + 32'd4;  //tpcodestart = (tp+4)
					46: aluOut <= 32'h40;
					50: aluOut <= 32'h0;
					default : aluOut <= opB;
				endcase
			end
		else
			begin
				aluOut <= aluOut;
			end
	end

endmodule


module cpu(	x_addr,
				x_datain,
				x_dataout,
				x_readmem,
				x_writemem,
				x_ssrmem,
				x_enmem,
				x_wmem,
				waitrq,
				irq,
				type,
				rst,
				clk,
				tosout,
				debugout
				);


output[31:0] tosout;
reg[31:0] tosout;

output[31:0] debugout;
reg[31:0] debugout;

output[31:0] x_addr;

input[31:0] x_datain;

output[31:0] x_dataout;

output x_readmem;
output x_writemem;

output[3:0] x_ssrmem;
output[3:0] x_enmem;
output[3:0] x_wmem;



wire[31:0] addr;
wire[31:0] datain;
wire[31:0] dataout;

output[1:0] type;

//registered output lines


reg readmem;
reg writemem;

assign x_readmem = readmem;
assign x_writemem = writemem;

reg[1:0] type;


input waitrq;

wire waitrq1;
wire waitrqX;

assign waitrqX = 1'b0;

input[2:0] irq;



input rst;
input clk;



// the memadaptors

		
		wire[31:0] data_b_w;
		wire[31:0] data_b_r;

		wire[3:0] ssrmem;
		wire[3:0] ssrmem_w;

		wire[3:0] enmem;
		wire[3:0] enmem1;
			
		wire readmem_only = readmem & ~writemem;
		wire writemem_only = writemem & ~readmem;
					
		assign data_b_w = dataout;
		assign datain = data_b_r;
		
		wire[31:0] amem_w;
		wire[31:0] amem_r;

		wire[3:0] wmem;

		// bus section

		wire[3:0] enmem_bus;
		wire[3:0] wmem_bus;
		
		wire[31:0] data_o_bus;
		wire[31:0] data_i_bus;
		
		assign data_i_bus = x_datain;
		assign x_dataout = data_o_bus;
				
		wire[31:0] addr_bus;
		
		wire rdack_A;


		mem_adaptor_be_w maw0_A (.dinbus(data_b_w), .abus(addr), .enbus(writemem_only), .webus(writemem_only),
														.ssrbus(1'b0),
												.doutmem(data_o_bus), .amem(amem_w), .enmem(enmem1),
													.wemem(wmem), .ssrmem(ssrmem_w), .type(type) );
		
		mem_adaptor_be_r mar0_A (.doutbus(data_b_r), .abus(addr), .enbus(readmem_only),
														.ssrbus(1'b0), .read_ack (rdack_A),
												.dinmem(data_i_bus), .amem(amem_r), .enmem(enmem),
													.ssrmem(ssrmem), .type(type), .clk(clk));


		assign enmem_bus = enmem | enmem1;
		assign wmem_bus = wmem;
	
		assign addr_bus = (|wmem_bus) ? amem_w : amem_r;

		assign x_enmem = enmem_bus;
		assign x_wmem = wmem_bus;
		
		assign x_ssrmem = ssrmem_w | ssrmem;
		
		assign x_addr = addr_bus;


/// end adpators section






// special provision for data in/out

reg[31:0] datao;
reg dataeno;



// registers


wire[31:0] addro;



reg[31:0] csp, dsp;  // code stack pointer , data stack pointer

reg[31:0] ir; 

reg[31:0] pc;


// derived signal d from ir


wire[31:0] d;
wire[15:0] d1;

assign d1 = ir[15:0];

assign d = {{14{d1[15]}},d1,1'b0,1'b0};




// status word

reg[31:0] status;


// irq mask

wire[2:0] irq_akt;

assign irq_akt = status[5:3];

wire irq_enabled;

assign irq_enabled = status[2];


// the registers


reg[31:0] tp;
reg[31:0] litbase;
reg[31:0] tos;


// the ALU

wire[31:0] aluOut;

wire[5:0] aluOp;

wire overflow, carry, zero;

reg[31:0] regA;
reg[31:0] regB;
reg[31:0] regC;


reg[31:0] aluOpA;



alu alu0 (.opcodeIn(aluOp),.opAin(aluOpA),.opBin(tos),.opCin(regC),
									.aluOut(aluOut),.overflow(overflow),.carry(carry),.zero(zero),
									.freeze(waitrq1),.clk(clk));


wire[5:0] aluOp_h;

wire use_aluOp_h;

assign aluOp = use_aluOp_h ? aluOp_h : ir[29:24];

wire is_regC = (tos == regC);

// the bus structures and multiplexors

// data bus

reg[31:0] databus;

wire[3:0] datsel;

always @(datsel, dsp, csp, aluOut, datain, tos, d, tp, litbase, regB, regC, status)
	begin
		case (datsel)
			0 : databus = dsp;
			1 : databus = csp;
			2 : databus = aluOut;
			3 : databus = datain;
			4 : databus = tos;
			5 : databus = d;
			6 : databus = tp;
			7 : databus = litbase;
			8 : databus = regB;
			9 : databus = regC;
			10 : databus = status;
			11 : databus = {datain[29:0], 2'b0};
			12 : databus = {2'b0, regB[31:2]};
			13 : databus = regA;
			14 : databus = pc;
			15 : databus = 32'd8;
		endcase
	end
	
wire[2:0] alu_source;

always @(alu_source, regA, databus)
	begin
		case (alu_source[1:0])
			0 : aluOpA = regA;
			2 : aluOpA = 32'd8;
			1 : aluOpA = 32'd12;
			3 : aluOpA = databus;
		endcase
	end



wire[3:0] datinsel;

reg[12:0] regwrite1;

always @(datinsel)
	case (datinsel)
		0 : regwrite1 = 13'b1 << 0;
		1 : regwrite1 = 13'b1 << 1;
		2 : regwrite1 = 13'b1 << 2;
		3 : regwrite1 = 13'b1 << 3;
		4 : regwrite1 = 13'b1 << 4;
		5 : regwrite1 = 13'b1 << 5;
		6 : regwrite1 = 13'b1 << 6;
		7 : regwrite1 = 13'b1 << 7;
		8 : regwrite1 = 13'b1 << 8;
		10 : regwrite1 = 13'b1 << 10;
		11 : regwrite1 = 13'b1 << 11;
		12 : regwrite1 = 13'b1 << 12;
		default : regwrite1 = 13'b1 << 9;
	endcase
	
wire[12:0] regwrite;

assign regwrite = regwrite1 & {13{~waitrqX}}; 


reg[31:0] xinp1;
reg[31:0] xinp2;

wire[1:0] xinp1sel;

always @(xinp1sel, pc, csp, dsp)
	begin
		case (xinp1sel)
			0: xinp1 = pc;
			1: xinp1 = dsp;
			2, 3: xinp1 = csp;
		endcase
	end


wire[1:0] xinp2sel;

always @(xinp2sel, d)
	begin
		case (xinp2sel)
			0: xinp2 = 32'd4;
			1: xinp2 = 32'hffffffff - 32'd3;
			2,3: xinp2 = d;
		endcase
	end


wire[31:0] accuxres;
assign accuxres = xinp1 + xinp2;

wire dspalt;
wire cspalt;

wire dspalt1 = dspalt & ~waitrqX;
wire cspalt1 = cspalt & ~waitrqX;

always @(posedge clk or posedge rst)
	begin
		if (rst)
			begin
				dsp <= 32'h800;
			end
		else
			begin
			 if (dspalt1)
			 	begin
			 		dsp <= accuxres;
				end
			 else
			 if (regwrite[0])
				begin
					dsp <= databus;
				end
			end
	end
always @(posedge clk or posedge rst)
	begin
		if (rst)
			begin
				csp <= 32'h500;
			end
		else
			begin
			 if (cspalt1)
			 	begin
					csp <= accuxres;
				end
			 else
			 if (regwrite[1])
				 begin
					csp <= databus;
				end
			end
	end
always @(posedge clk or posedge rst)
	begin
		if (rst)
			begin
				regA <= 32'h00;
			end
		else
			begin
			 if (regwrite[2])
			 	begin
				 	regA <= databus;
				end
			 else if (regwrite[10])
			 	begin
					regA <= 32'd0;
				end
			 else if (regwrite[11])
			 	begin
				  regA <= 32'd4;
				end
			end
	end
always @(posedge clk or posedge rst)
	begin
		if (rst)
			begin
				regB <= 32'h00;
			end
		else
			begin
			 if (regwrite[3])
				 regB <= databus;
			end
	end
always @(posedge clk or posedge rst)
	begin
		if (rst)
			begin
				regC <= 32'h00;
			end
		else
			begin
			 if (regwrite[4])
				 regC <= databus;
			end
	end
always @(posedge clk or posedge rst)
	begin
		if (rst)
			begin
				tos <= 32'h00;
			end
		else
			begin
			 if (regwrite[5])
			 	begin
				 tos <= databus;
				end
			 else if (regwrite[12])
			  begin
				 tos <= 32'd4;
				end
			end
	end
always @(posedge clk or posedge rst)
	begin
		if (rst)
			begin
				tp <= 32'h00;
			end
		else
			begin
			 if (regwrite[6])
				 tp <= databus;
			end
	end
always @(posedge clk or posedge rst)
	begin
		if (rst)
			begin
				litbase <= 32'h00;
			end
		else
			begin
			 if (regwrite[7])
				 litbase <= databus;
			end
	end

always @(posedge clk or posedge rst)
	begin
		if (rst)
			begin
				ir <= 32'h00;
			end
		else
			begin
			 if (regwrite[8])
				 ir <= databus;
			end
	end

// pc

wire[1:0] next_pc;

always @(posedge clk or posedge rst)
	begin
		if (rst)
			begin
				pc <= 0;
			end
		else
			case (next_pc & {2{~waitrqX}})
				0 : pc <= pc;
				1 : pc <= accuxres;
				2 : pc <= databus;			
			endcase
	end


// data bus interface control

reg[31:0] databus_x;

assign dataout = dataeno ? databus_x : 32'bz;



always @(waitrqX, databus)
	begin
		if (~waitrqX)
			begin
				databus_x = databus;
			end
	end


// addr

wire addr_l;
wire[2:0] addr_sel;
wire addr_en;

reg[31:0] addro1;
reg[31:0] addrox;	

always @(addr_sel, addr_l, regA, litbase, d, aluOut, tos, pc, tp, regB, addrox)
	case ({addr_l, addr_sel})
		0 : addro1 = regA;
		1 : addro1 = litbase + d;
		2 : addro1 = aluOut;
		3 : addro1 = tos;
		4 : addro1 = pc;
		5 : addro1 = litbase;
		6 : addro1 = tp;
		7 : addro1 = regB;
		8, 9, 10, 11, 12, 13, 14, 15 : addro1 = addrox;
	endcase
	

always @(addr_sel, dsp, csp, d)
	begin
		addrox = (addr_sel[2] ? dsp : csp ) + (addr_sel[1] ? d  : 0) + (addr_sel[0] ? -32'd4 : 0);
	end

	
	
assign addro = addr_en ? addro_x : 32'bz;

reg[31:0] addro_x;

assign addr = addro_x;


always @(waitrqX, addro1, addr_en)
	begin
		if (~waitrqX)
			begin
				addro_x = addro1;
			end
	end


// auxiliary initialization counter


reg[3:0] auxcnt;

wire rst_long;

assign rst_long = |auxcnt;

always @(posedge clk or posedge rst)
	begin
		if (rst)
			begin
				auxcnt <= 4'd15;
			end
		else
			begin
				if (rst_long)
					auxcnt <= auxcnt - 1;
			end
	end

	




// miscellaneous

always @(dataeno)
	begin
		writemem <= dataeno;
	end
	

// micro program control

wire[31:0] mic_inst_raw;
wire[31:0] mic_inst_h_raw;


reg[31:0] mic_inst;
reg[31:0] mic_inst_h;

reg[9:0] micr_pc;
reg[9:0] micr_pc_new;



assign datsel = mic_inst_h[19:16];
assign datinsel = mic_inst[27:24];
assign addr_sel = mic_inst[22:20];

assign next_pc = mic_inst[19:18];

assign xinp2sel = mic_inst[17:16];
assign xinp1sel = mic_inst[15:14];

assign dspalt = mic_inst[13:13];
assign cspalt = mic_inst[12:12];



wire mic_jmp_allow;
assign mic_jmp_allow = mic_inst[11];


//assign addr_en = mic_inst[10:10];

assign addr_en = mic_inst[6:6] | mic_inst[7:7];


wire set_status;
assign set_status = mic_inst[9:9];

assign addr_l = mic_inst[23:23];

wire data_eno_mic = mic_inst[6:6];

wire read_mem = mic_inst[7:7];

reg[3:0] mic_next;


always @(is_regC, mic_inst, mic_jmp_allow)
	begin
		if (is_regC && mic_jmp_allow)
			mic_next = mic_inst[3:0] + 1;
		else
			mic_next = mic_inst[3:0];
	end
	
wire dispatch = mic_inst_h[6:6];

wire to_page_zero = mic_inst_h[7:7];

wire[1:0] mtyp = mic_inst_h[9:8];

assign aluOp_h = mic_inst_h[5:0];

assign use_aluOp_h = mic_inst_h[10:10];

assign alu_source = mic_inst_h[13:11];

wire[1:0] start_interrupt;

assign start_interrupt = mic_inst_h[15:14];


always @(mtyp)
	begin
		type <= mtyp;
	end

always @(read_mem)
	begin
		readmem <= read_mem;
	end



always @(data_eno_mic)
	begin
		dataeno <= data_eno_mic;
	end



wire irq_pending = (irq > irq_akt);

wire cycle_start = (micr_pc == 10'b0);

wire irq_cycle_start = cycle_start && irq_enabled && irq_pending && (~|auxcnt);



always @(posedge clk)
	begin
		if (rst_long)
			begin
				status <= 32'h4; // start with interrupts enabled (status[2] = 1)
			end
		else
			begin
				if (~waitrqX)
					begin
					 if (set_status)
						 begin
							 $display ( "assign status, val is = %h", tos );
							 status <= tos;
						 end
					 else if (start_interrupt == 2'b01)
						 begin
							 $display ( "setting priority, new is = %h", irq );
							 status[8:6] <= status[5:3];
							 status[5:3] <= irq;
							 status[2] <= 1'b0; // irq disabled
						 end
					 else if ((start_interrupt == 2'b10) & status[9]) 
											 // status[9] is the switch that transforms an ordinary RET into a
											 // RET from interrupt
						 begin
							 $display ( "resetting priority, new is = %h", status[8:6] );
							 status[9] <= 1'b0;
							 status[5:3] <= status[8:6];
							 status[2] <= 1'b1; // irq reenabled
						 end
					 else if ((start_interrupt == 2'b10))
						 begin
	 //						$display ( "passing RET without resetInt" );
	 //						$stop;
						 end
					end
				else
					begin
						status <= status;
					end
		  end
	end


assign waitrq1 = waitrq & cycle_start;


always @(mic_next, dispatch, databus, waitrqX, micr_pc, micr_pc_new, rst_long, ir, to_page_zero, irq_cycle_start)
	begin
//		if (rst | |auxcnt) begin
		if (rst_long) begin
			micr_pc_new = 62* 8;
			tosout = 0;
		end else begin
			if (~waitrqX)
				begin
//					$display ( "micr_pc = %h, ir = %h, pc is = %h, datain is = %h, addr = %h, csp = %h, dsp = %h readmem = %h, writemem = %h", 
//							micr_pc, ir, pc, datain, addr, csp, dsp, readmem, writemem );
					debugout = {16'b0,ir[31:16]};
					if (ir[23:16] == 8'd60)
						tosout = tos;
					if (to_page_zero)
						begin
							$display("to page zero");
							micr_pc_new = {6'b0, mic_next};
						end
					else if (irq_cycle_start)
						begin
							$display ( "irq cycle start.");
							micr_pc_new = {1'b0, 6'd14, 3'b0};
						end
					else if (dispatch)
						begin
							$display ("dispatch");
							micr_pc_new = {1'b0,databus[21:16],mic_next[2:0]};
						end
					else
						begin
							$display ("micr_pc new = %h", micr_pc_new);
							micr_pc_new = {micr_pc[9:4],mic_next};
						end
				end
			else
				begin
					$display("waitrq");
					micr_pc_new = micr_pc;
				end
		end
	end


always @(posedge clk) // or posedge rst)
	begin
//		if (rst | |auxcnt)
		if (rst_long)
			begin
//				$display ( "cpu rst = %h", rst );
				micr_pc <= 62* 8;
			end
		else
			begin
/*			
				if ((micr_pc_new[8:3] == 14) || (micr_pc_new[8:3] == 15))
					begin
						$display ("in irq : micr_pc_new = %h", micr_pc_new );
					end
				if ((micr_pc_new[8:3] == 42) || (micr_pc_new[8:3] == 41))
					begin
						$display ("get-set-status ir = %h status = %h", ir, status );
					end
*/
				micr_pc <= micr_pc_new;
			end
		
	end



`define mic_inst_wait {4'd0, 4'd9, 15'd0,  2'd0, 1'd0, 2'd0, 4'd0}
`define mic_inst_wait_h {18'd0, 3'd1, 1'd0, 2'd0, 1'd0, 1'd0, 6'd63}

/*
always @(waitrq1, rst_long, mic_inst_raw, mic_inst_h_raw)
	begin
		if (~waitrq1 & ~rst_long)
			begin
				mic_inst <= mic_inst_raw;
				mic_inst_h <= mic_inst_h_raw;
			end
		else
			begin
				mic_inst <= `mic_inst_wait;
				mic_inst_h <= `mic_inst_wait_h;
			end
	end
*/

always @(waitrqX, rst_long, mic_inst_raw, mic_inst_h_raw)
	begin
		if (~rst_long)
			begin
				mic_inst = mic_inst_raw;
				mic_inst_h = mic_inst_h_raw;
			end
		else
			begin
				mic_inst <= `mic_inst_wait;
				mic_inst_h <= `mic_inst_wait_h;
			end
	end




micro_store mcs0 ( .address({micr_pc_new[9:0]}), .data(mic_inst_raw), .read_en(1'b1), .ce(1'b1), .clk(clk)); 
micro_store_1 mcs1 ( .address({micr_pc_new[9:0]}), .data(mic_inst_h_raw), .read_en(1'b1), .ce(1'b1), .clk(clk));


endmodule

