`include "xor.v"
`include "fulladder.v"

`define FA_DELAY 0
`define AND_DELAY 0
`define OR_DELAY 0
`define NOT_DELAY 0
`define XOR_DELAY 0

//---------------------------------------------------------
// A 4-bit ALU 
//----------------------------------------------------------
//	The control signals for the ALU :
//		000 - transfer a if cin == 0 else transfer a+1
//		001 - add b to a if cin == 0 else add b to a+1
//		010 - sub b from a if cin == 0 else sub b from a+1
//		011 - decrement a if cin == 0 else transfer a
//		100 - a or b
//		101 - a xor b
//		110 - a and b
//		111 - not a
//----------------------------------------------------------

`define INCREMENT 3'b000
`define SUM 3'b001
`define DIFFERENCE 3'b010
`define DECREMENT 3'b011
`define ALU_OR 3'b100
`define ALU_XOR 3'b101
`define ALU_AND 3'b110
`define ALU_NOT 3'b111

`define SHR 3'b001
`define SHL 3'b010

module ALU(cout,f,ctrl_in,cin,in1,in2,clk,reset);

input ctrl_in,cin,in1,in2,clk,reset;
output cout,f;

wire [3:0] f;
wire [2:0] ctrl_in;
wire [3:0] in1;
wire [3:0] in2;
wire cin;

wire [3:0] temp_sum;
reg [3:0] temp_and;
reg [3:0] temp_or;
wire [3:0] temp_xor;
reg [3:0] temp_not;

reg [3:0] adder_in1;
reg [3:0] adder_in2;
reg adder_cin;

reg [3:0] xor_in1;
reg [3:0] xor_in2;

reg [3:0] dff_inp;

// Description of the ALU circuit


full_adder FA_1(.sum(temp_sum),.cout(cout),
				.cin(adder_cin),.in1(adder_in1),
				.in2(adder_in2));
xor_4_bits XOR_4_BITS(.temp(temp_xor),
				.a(xor_in1),.b(xor_in2));

buffered_D_FF4  b_dff4(.out(f),.d(dff_inp),.clk(clk),
					.reset(reset));

always @(in1 or in2 or ctrl_in or cin)
begin
case (ctrl_in)
		
	`INCREMENT :
	`ifdef STRICT
			begin
				adder_in1 = in1;
				adder_in2 = 4'b0;
				adder_cin = cin;
				#1 dff_inp = temp_sum;
			end
	`else			// 1 operand instruction
			begin
				case (in1[2:0])
					`INCREMENT:
					begin
						$display("doggggg");
						adder_in1 = in2;
						adder_in2 = 4'b0001;
						adder_cin = 0;
						#1 dff_inp = temp_sum;
					end
					`DECREMENT:
					begin
						adder_in1 = in2;
						adder_in2 = 4'b1111;
						adder_cin = 0;
						#1 dff_inp = temp_sum;
					end
					`ALU_NOT:
					begin
						temp_not = ~in2;
						#1 dff_inp = temp_not;
					end
					`SHR:
					begin
						temp_not = {1'b0,in2[3:1]};
						#1 dff_inp = temp_not;
					end	
					`SHL:
					begin
						temp_not = {in2[2:0],1'b0};
						#1 dff_inp = temp_not;
					end	
				endcase
			end
	`endif
	`SUM :
			begin
				adder_in1 = in1;
				adder_in2 = in2;
				adder_cin = cin;
				#1 dff_inp = temp_sum;
			end
	`DIFFERENCE :
			begin
			#(`NOT_DELAY)
				adder_in1 = in1;
				adder_in2 = ~in2;
				adder_cin = cin;
				#1 dff_inp = temp_sum;
			end	
	`DECREMENT :
			begin
			#(`NOT_DELAY)
				adder_in1 = in1;
				adder_in2 = -4'b1;
				adder_cin = cin;
				#1 dff_inp = temp_sum;
			end	
	`ALU_OR :
			begin
				temp_or = in1 | in2;
				#1 dff_inp = temp_or;
			end
	`ALU_XOR :
			begin
				xor_in1 = in1;
				xor_in2 = in2;
				#1 dff_inp = temp_xor;
			end
	`ALU_AND :
			begin
				temp_and = in1 & in2;
				#1 dff_inp = temp_and;
			end
	`ALU_NOT :
			begin
				temp_not = ~in1;
				#1 dff_inp = temp_not;
			end

	default : ; 
endcase

//$strobe ($time," in case:ctrl:%b out:%b in1: %b in2:%b cin:%b"
//				,ctrl_in,dff_inp,in1,in2,cin);

end
endmodule

//-------------------------------------------------------
//	Xor gate which deals with a bus width of 4 bits.
//	This is separated from the main code just for
//	enhancing readability and modularity.
//---------------------------------------------------------

module xor_4_bits(temp,a,b);

input a,b;
output temp;

wire [3:0] a;
wire [3:0] b;
wire [3:0] temp;

// XOR delays need not be specified here.
//	That is done as part of the specification
//	of the my_xor module.

my_xor m1(temp[0],a[0],b[0]);
my_xor m2(temp[1],a[1],b[1]);
my_xor m3(temp[2],a[2],b[2]);
my_xor m4(temp[3],a[3],b[3]);

endmodule

module D_FF(q,d,clk,reset);

output q;
input d,clk,reset;
reg q;

always @(negedge reset or negedge clk)
if (~reset)
   q = 1'b0;
else
  q = d; //Comment: Synchronous Reset
endmodule


module buffered_D_FF4(out,d,clk,reset);

output out;
input d,clk,reset;

wire [3:0] out,temp;
wire [3:0] d;

D_FF dff0(.q(temp[0]),.d(d[0]),.clk(clk),.reset(reset));
D_FF dff1(.q(temp[1]),.d(d[1]),.clk(clk),.reset(reset));
D_FF dff2(.q(temp[2]),.d(d[2]),.clk(clk),.reset(reset));
D_FF dff3(.q(temp[3]),.d(d[3]),.clk(clk),.reset(reset));

// TODO bufifs

assign out = reset ? temp : 4'bzzzz; 

/*
bufif1 (out[0],temp[0],reset);
bufif1 (out[1],temp[1],reset);
bufif1 (out[2],temp[2],reset);
bufif1 (out[3],temp[3],reset);*/

endmodule

