



module control
(
	LMB_Clk,
	LMB_Rst,
	LMB_ABus,
	LMB_WriteDBus,
	LMB_AddrStrobe,
	LMB_WriteStrobe,
	LMB_ReadStrobe,

	LMB_AddrStrobe_1,
	lmb_we_1,
	LMB_WriteDBus_1,
	LMB_ABus_1,
	lmb_select_1,

	BRAM_Din_B,
	BRAM_EN_B_i,
	BRAM_WEN_B_i,
	BRAM_Dout_B_i,
	BRAM_Addr_B_i,
	//lmb_rdy,
	lmb_rdy_copy,
	transmission_state,
	reg_read,

	trans_valid,
	trans_data,
	rcv_ack,
	rcv_valid,
	rcv_data,
	trans_ack,

	debug_send_counter,
	debug_receive_counter,
	debug_send_state,
	debug_send_done,
	debug_BRAM_Din_B,
	debug_BRAM_Dout_B_i,

	debug_trans_valid,
	debug_trans_data,
	debug_rcv_ack,
	debug_rcv_valid,
	debug_rcv_data,
	debug_trans_ack,
	debug_rcv_size,
	debug_receive_done
);


input LMB_Clk;
input LMB_Rst;
input [0:31] LMB_ABus;
input [0:31] LMB_WriteDBus;
input LMB_AddrStrobe;
input LMB_WriteStrobe;
input LMB_ReadStrobe;
input LMB_AddrStrobe_1;
input [0:3] lmb_we_1;
input [0:31] LMB_WriteDBus_1;
input [0:31] LMB_ABus_1;
input lmb_select_1;

input [0:31] BRAM_Din_B;
output reg BRAM_EN_B_i;
output reg [0:3] BRAM_WEN_B_i;
output reg [0:31] BRAM_Dout_B_i;
output reg [0:31] BRAM_Addr_B_i;
//output reg lmb_rdy;
output lmb_rdy_copy;
output [0:31] transmission_state;
output reg reg_read;

output reg trans_valid;
output reg [0:31] trans_data;
output reg rcv_ack;
input rcv_valid;
input [0:31] rcv_data;
input trans_ack;

output [0:31] debug_send_counter;
output [0:31] debug_receive_counter;
output [0:1] debug_send_state;
output debug_send_done;
output [0:31] debug_BRAM_Din_B;
output [0:31] debug_BRAM_Dout_B_i;

output debug_trans_valid;
output [0:31] debug_trans_data;
output debug_rcv_ack;
output debug_rcv_valid;
output [0:31] debug_rcv_data;
output debug_trans_ack;

output [0:31] debug_rcv_size;
output debug_receive_done;

localparam idle = 2'b00;
localparam wait_size = 2'b01;
localparam wait_rcv_addr = 2'b10;
localparam processing = 2'b11;

wire lmb_rdy_i;
reg send_done,receive_done;
reg [0:1] send_state;
reg [0:1] receive_state;
reg [0:31] send_counter;
reg [0:31] receive_counter;
reg [0:1] token;

reg trans_ack_hold, rcv_valid_hold;

reg [0:31] trans_src_base_addr, trans_target_base_addr, trans_size;
reg [0:31] rcv_base_addr, rcv_size;

assign lmb_rdy_copy = lmb_rdy_i;
assign debug_send_counter = send_counter;
assign debug_receive_counter = receive_counter;
assign debug_send_state = send_state;
assign debug_send_done = send_done;

assign lmb_rdy_i = LMB_AddrStrobe_1;
assign transmission_state = {28'b0,receive_state,send_state};
assign debug_BRAM_Din_B = BRAM_Din_B;
assign debug_BRAM_Dout_B_i = BRAM_Dout_B_i;

assign debug_trans_valid = trans_valid;
assign debug_trans_data = trans_data;
assign debug_rcv_ack = rcv_ack;
assign debug_rcv_valid = rcv_valid;
assign debug_rcv_data = rcv_data;
assign debug_trans_ack = trans_ack;

assign debug_rcv_size = rcv_size;
assign debug_receive_done = receive_done;

always @ (posedge LMB_Clk)
begin
	if(LMB_Rst)
		reg_read <= 0;
	else if(LMB_AddrStrobe && LMB_ReadStrobe && (LMB_ABus == 32'h0000FFF8))
		reg_read <= 1;
	else
		reg_read <= 0;
end


always @ (posedge LMB_Clk)
begin
	if(LMB_Rst) begin
		send_state <= idle;
		trans_src_base_addr <= 0;
		trans_target_base_addr <= 0;
		trans_size <= 0;
	end
	else
		case (send_state)
			idle:
				if(LMB_AddrStrobe && LMB_WriteStrobe && (LMB_ABus == 32'h0000FFF8)) begin
					send_state <= wait_rcv_addr;
					trans_src_base_addr <= LMB_WriteDBus;
				end
			wait_rcv_addr:
				if(LMB_AddrStrobe && LMB_WriteStrobe && (LMB_ABus == 32'h0000FFF8)) begin
					send_state <= wait_size;
					trans_target_base_addr <= LMB_WriteDBus;
				end
			wait_size:
				if(LMB_AddrStrobe && LMB_WriteStrobe && (LMB_ABus == 32'h0000FFF8)) begin
					send_state <= processing;
					trans_size <= LMB_WriteDBus;
				end
			processing:
				if(send_done == 1) begin
					send_state <= idle;
				end
		endcase
end


always @ (posedge LMB_Clk)
begin
	if(LMB_Rst) begin
		receive_state <= idle;
		rcv_base_addr <= 0;
		rcv_size <= 0;
	end
	else 
		case (receive_state)
			idle:
				if(rcv_valid == 1) begin
					receive_state <= wait_size;
					rcv_base_addr <= rcv_data;
				end
			wait_size:
				if(rcv_valid == 1) begin
					receive_state <= processing;
					rcv_size <= rcv_data;
				end
			processing:
				if(receive_done == 1) begin
					receive_state <= idle;
					rcv_base_addr <= 0;
					rcv_size <= 0;
				end
		endcase
end


always @ (*)
begin
	if(LMB_Rst) begin
		token = 0;
		BRAM_EN_B_i = 0;
		BRAM_WEN_B_i = 0;
		BRAM_Dout_B_i = 0;
		BRAM_Addr_B_i = 0;
		rcv_ack = 0;
	end
	else if((LMB_AddrStrobe_1 == 1) && (lmb_select_1 == 1)) begin
		token = 0;
		BRAM_EN_B_i = LMB_AddrStrobe_1;
		BRAM_WEN_B_i = lmb_we_1;
		BRAM_Dout_B_i = LMB_WriteDBus_1;
		BRAM_Addr_B_i = LMB_ABus_1;
		rcv_ack = 0;
	end
	else if((receive_state == processing) && ((rcv_valid == 1) || (rcv_valid_hold == 1))) begin
		//receiving
		token = 1;
		BRAM_EN_B_i = 1;
		BRAM_WEN_B_i = 4'b1111;
		BRAM_Dout_B_i = rcv_data;
		BRAM_Addr_B_i = receive_counter + rcv_base_addr;
		rcv_ack = 1;
	end
	else if((send_done == 0) && (send_state == processing) && ((send_counter <= 8) || (trans_ack == 1) || (trans_ack_hold == 1))) begin
		//read data from bram for sending
		token = 2;
		rcv_ack = 0;
		BRAM_EN_B_i = 0;
		BRAM_WEN_B_i = 0;
		BRAM_Dout_B_i = 0;
		BRAM_Addr_B_i = 0;
		if(send_counter >= 8) begin
			BRAM_EN_B_i = 1;
			BRAM_Addr_B_i = send_counter + trans_src_base_addr - 8;
		end
	end
	else begin
		token = 0;
		BRAM_EN_B_i = 0;
		BRAM_WEN_B_i = 0;
		BRAM_Dout_B_i = 0;
		BRAM_Addr_B_i = 0;
		rcv_ack = 0;
	end
end





always @ (posedge LMB_Clk)
begin
	if(LMB_Rst) begin
		trans_valid <= 0;
		send_counter <= 0;
		receive_counter <= 0;
		send_done <= 0;
		receive_done <= 0;
	end
	else if(token == 1) begin
		trans_valid <= 0;
		send_done <= 0;
		if(receive_counter == rcv_size - 4) begin
			receive_done <= 1;
			receive_counter <= 0;
		end
		else
			receive_counter <= receive_counter + 4;
	end
	else if(token == 2) begin
		receive_done <= 0;
		//to count in base address and total data size
		if(send_counter == trans_size + 8) begin
			send_done <= 1;
			send_counter <= 0;
			trans_valid <= 0;
		end
		else begin
			send_counter <= send_counter + 4;
			trans_valid <= 1;
		end
	end
	else begin
		trans_valid <= 0;
		send_done <= 0;
		receive_done <= 0;
	end
end



always @ (posedge LMB_Clk)
begin
	if(LMB_Rst)
		trans_ack_hold <= 0;
	else if(token == 2)
		trans_ack_hold <= 0;
	else if(trans_ack == 1)
		trans_ack_hold <= 1;
	else
		trans_ack_hold <= trans_ack_hold;
end


always @ (posedge LMB_Clk)
begin
	if(LMB_Rst)
		rcv_valid_hold <= 0;
	else if(rcv_ack == 1)
		rcv_valid_hold <= 0;
	else if((rcv_valid == 1) && (receive_state == processing))
		rcv_valid_hold <= 1;
	else
		rcv_valid_hold <= rcv_valid_hold;
end


/*always @ (posedge LMB_Clk)
begin
	if(LMB_Rst)
		lmb_rdy <= 0;
	else
		lmb_rdy <= lmb_rdy_i;
end*/


reg [0:31] cc;

//two latches will be inferred deliberately
always @ (*)
begin
	if(LMB_Rst) begin
		trans_data = 0;
		cc = 0;
	end                            //to avoid repetition
	else if((trans_valid == 1) && (cc != send_counter))
		if(send_counter == 4) begin //the first data to transmit is the base address
			trans_data = trans_target_base_addr;
			cc = 4;
		end
		else if(send_counter == 8) begin //the second data to transmit is the total data size
			trans_data = trans_size;
			cc = 8;
		end
		else begin
			trans_data = BRAM_Din_B;
			cc = send_counter;
		end
end

endmodule