`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 + {2'b0, 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]};
//					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;
					default : aluOut <= opB;
				endcase
			end
		else
			begin
				aluOut <= aluOut;
			end
	end

endmodule


module cpu(	addr,
				datain,
				dataout,
				readmem,
				writemem,
				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] addr;

input[31:0] datain;
output[31:0] dataout;

output readmem;
output writemem;

input waitrq;

wire waitrq1;

input[2:0] irq;

output[1:0] type;


input rst;
input clk;

//registered output lines


reg readmem;
reg writemem;

reg[1:0] type;

// special provision for data in/out

reg[31:0] datao;
reg dataeno;



// registers


wire[31:0] addro;

assign addr = 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[4: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 = 4;
			7 : databus = tp;
			8 : databus = litbase;
			9 : databus = regB;
			10 : databus = regC;
			11 : databus = 0;
			12 : databus = status;
			13 : databus = {datain[29:0], 2'b0};
			14 : databus = {2'b0, regB[31:2]};
			15 : databus = regA;
			16 : databus = pc;
			17 : databus = 32'd8;
		endcase
	end
	
wire[2:0] alu_source;

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



wire[3:0] datinsel;

reg[9:0] regwrite;

always @(datinsel)
	case (datinsel)
		0 : regwrite = 10'b0000000001 << 0;
		1 : regwrite = 10'b0000000001 << 1;
		2 : regwrite = 10'b0000000001 << 2;
		3 : regwrite = 10'b0000000001 << 3;
		4 : regwrite = 10'b0000000001 << 4;
		5 : regwrite = 10'b0000000001 << 5;
		6 : regwrite = 10'b0000000001 << 6;
		7 : regwrite = 10'b0000000001 << 7;
		8 : regwrite = 10'b0000000001 << 8;
		default : regwrite = 10'b0000000001 << 9;
	endcase


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;

always @(posedge clk or posedge rst)
	begin
		if (rst)
			begin
				dsp <= 32'h800;
			end
		else
			begin
			 if (dspalt)
			 	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 (cspalt)
			 	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])
				 regA <= databus;
			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])
				 tos <= databus;
			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)
				0 : pc <= pc;
				1 : pc <= accuxres;
				2 : pc <= databus;			
			endcase
	end


// data bus interface control

wire[31:0] datamem;

assign datamem = ~dataeno ? datain : 32'bz;
assign dataout = dataeno ? databus : 32'bz;


// 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 ? addro1 : 32'bz;


// 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[20: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 (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
	end


assign waitrq1 = waitrq & cycle_start;


always @(mic_next, datamem, dispatch, waitrq1, 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 (~waitrq1)
				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
	


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

