/******************************************************************************
* Copyright                                                                   *
* Scintillating Fibre Beam Profile Monitor Software by Michal Dziewiecki,     *
* Blake Leverington and Liqing Qin is licensed under CC BY 4.0                *
* https://creativecommons.org/licenses/by/4.0/                                *
* funded by the Deutsche Forschungsgemeinschaft                               *
* (DFG, German Research Foundation) Projektnummer 419255448                   *
* Project Leader: B.Leverington                                               *
*******************************************************************************
* Create Date - Oct 15th. 2024                                                *
* Author: L.Qin                                                               *
* Module - calibration.v                                                      *
******************************************************************************/
/*Created by Lq.Qin on Oct 15th. 2024*/

//module calibration has two functions: calibration and merge 16-bit stream to 32-bit stream and then output
//calibration output 16-bit data_caled
//a better design is a design seperate these two functions
//tested by calibration_tb 25.07.2024

module calibration
(

	input wire clk,
	input wire rst, // connect to the data_in_startofpacket; to reset for everyframe
	
	//avalon ST(Streaming) sink: 0 readlatency and 0 readallowence  320 channel
	input  wire signed[15:0] data_in_data,           //  st.data
	output wire        data_in_ready,          //         .ready
	input  wire        data_in_valid,          //         .valid
	input  wire 		 data_in_empty,          //         .empty
	input  wire        data_in_endofpacket,    //         .endofpacket
	input  wire        data_in_startofpacket,  //         .startofpacket
	
	//the interface with cali_ram (storing cali factor) Avalon-MM: read califac from this ram
	output wire [8:0] 	address,
	output wire 			clken, //read
	input  wire [15:0] 	cali_fac,
	input  wire				waitrequest,
	
	//the interface for provide the calibrated data in a ram // one clock latency
	input  wire [8:0]			data_caled_address,
	output wire [15:0]		data_caled,
	input  wire					data_caled_rd_enable, // to enable read
	
	output wire					data_in_last, //....
	
	//avalon ST(Streaming) source: 0 readlatency and 0 readallowence //160 * 32 bits data  
	// channel 0 [31:16] channel 1 [15:0]
	output  wire [31:0] to_udp_data,           //  st.data
	input   wire        to_udp_ready,          //         .ready
	output  wire        to_udp_valid,          //         .valid
	output  wire [1:0]  to_udp_empty,          //         .empty
	output  wire        to_udp_endofpacket,    //         .endofpacket
	output  wire        to_udp_startofpacket  //         .startofpacket	
);
	
	assign data_in_last = data_in_endofpacket;
	
	localparam SHIFT_BIT = 13; //2^13 = 8192, the calibration factor 1 is represented by 8192 
	
	//for to_udp_data
	reg [15:0] first_data_buffer;
	
	//for read calibration ram
	reg [8:0]  	read_cali_address;
	reg signed[16:0]   cali_fac_buffer;
	wire read;
	assign clken = read;
	assign address = read_cali_address;
	
	//for write and read the calibrated data
	reg signed[31:0]	data_caled_data_32bit;
	wire 			data_caled_wren_a;
	reg [8:0]	data_caled_address_a;
	wire [8:0]	address_b;
	assign		address_b = (data_caled_rd_enable)? data_caled_address: 9'd0;
	wire[15:0]	data_caled_data_a;
	assign data_caled_data_a = {data_caled_data_32bit[31],data_caled_data_32bit[14+SHIFT_BIT:SHIFT_BIT]};
	
	
	data_caled_ram	data_caled_ram0 (
		
		.clock ( clk ),
		
		.address_a (data_caled_address_a),
		.data_a (data_caled_data_a),
		.wren_a (data_caled_wren_a),
		.q_a ( ),

		.address_b ( address_b ),
		.data_b ( ),
		.wren_b (1'b0),
		.q_b ( data_caled)
		);
		
	//the state machine for read calibration factor from ram
	reg[3:0] state;
	localparam STATE_IDLE = 4'd0; //waiting for startofpacket
	localparam STATE_CAL0 = 4'd1; //get califactor for channel0
	localparam STATE_INPUT0 = 4'd2; // stream in data for channel0;
	localparam STATE_CAL1 = 4'd3; // get califactor for channel1
	localparam STATE_INPUT1 = 4'd4; // stream in data for channel1;
	localparam STATE_CAL2 = 4'd5; //get califactor for channel1; output the first 32-bit udp data [channel0, channel1]
	localparam STATE_INPUT_EVEN = 4'd6; // stream in data for channel 2,4,6.....
	localparam STATE_CAL_ODD = 4'd7; 
	localparam STATE_INPUT_ODD = 4'd8;
	localparam STATE_CAL_EVEN = 4'd9;
	localparam STATE_OUTPUT_LAST = 4'd10; //outout the last 32-bit udp data 
	

	assign data_in_ready = (state == STATE_INPUT0 || state == STATE_INPUT1 || state == STATE_INPUT_EVEN || state == STATE_INPUT_ODD)?1'b1:1'b0;
	assign data_caled_wren_a = (state == STATE_CAL1 || state == STATE_CAL2 || state == STATE_CAL_EVEN || state == STATE_CAL_ODD || state == STATE_OUTPUT_LAST)?1'b1:1'b0;
	assign read = (state == STATE_CAL0 ||state == STATE_CAL1 || state == STATE_CAL2 || state == STATE_CAL_EVEN || state == STATE_CAL_ODD)?1'b1:1'b0;
	
	
	
	always @ (posedge clk or posedge rst)
	begin
		if (rst)
		begin
			state <= STATE_IDLE;
			data_caled_address_a <= 0;
			read_cali_address <= 0; 
		end
		else case(state)
		STATE_IDLE:
		begin
			data_caled_address_a <= 0;
			read_cali_address <= 0; 
			if (data_in_startofpacket)
			begin
				state <= STATE_CAL0;
			end
		end
		STATE_CAL0:
		begin
			if (~waitrequest) begin
				cali_fac_buffer <= {1'b0,cali_fac};
				state <= STATE_INPUT0;
			end
		end
		STATE_INPUT0:
		begin
			if (data_in_valid) 
			begin
				first_data_buffer <= 16'd0;
				data_caled_data_32bit <= data_in_data*cali_fac_buffer;
				//data_caled_data_32bit <= cali_fac_buffer;// test! cali 
				read_cali_address <= read_cali_address + 1'b1;
				state <= STATE_CAL1;
			end
		end
		STATE_CAL1:
		begin
			if (~waitrequest) begin
				cali_fac_buffer <= {1'b0,cali_fac};
				state <= STATE_INPUT1;
			end
		end
		STATE_INPUT1:
		begin
			if (data_in_valid) 
			begin
				first_data_buffer <= data_caled_data_a;
				data_caled_data_32bit <= data_in_data*cali_fac_buffer;
				//data_caled_data_32bit <= cali_fac_buffer;// test! cali 
				read_cali_address <= read_cali_address + 1'b1;
				data_caled_address_a <= data_caled_address_a + 1'b1;
				state <= STATE_CAL2;
			end
		end
		STATE_CAL2:
		begin
			if (~waitrequest & to_udp_ready) begin
				cali_fac_buffer <= {1'b0,cali_fac};
				state <= STATE_INPUT_EVEN;
			end
		end
		STATE_INPUT_EVEN:
		begin
			if (data_in_valid) 
			begin
				data_caled_data_32bit <= data_in_data*cali_fac_buffer;
				//data_caled_data_32bit <= cali_fac_buffer;// test! cali 
				first_data_buffer <= 16'd0;
				read_cali_address <= read_cali_address + 1'b1;
				data_caled_address_a <= data_caled_address_a + 1'b1;
				if (data_in_endofpacket)
					state <= STATE_OUTPUT_LAST;
				else
					state <= STATE_CAL_ODD;
			end
		end
		STATE_CAL_ODD:
		begin
			if (~waitrequest) begin
				cali_fac_buffer <= {1'b0,cali_fac};
				state <= STATE_INPUT_ODD;
			end
		end
		STATE_INPUT_ODD:
		begin
			if (data_in_valid) 
			begin
				first_data_buffer <= data_caled_data_a;
				data_caled_data_32bit <= data_in_data*cali_fac_buffer;
				//data_caled_data_32bit <= cali_fac_buffer;// test! cali 
				read_cali_address <= read_cali_address + 1'b1;
				data_caled_address_a <= data_caled_address_a + 1'b1;
				if (data_in_endofpacket)
					state <= STATE_OUTPUT_LAST;
				else
					state <= STATE_CAL_EVEN;
			end
		end
		STATE_CAL_EVEN:
		begin
			if (~waitrequest & to_udp_ready) begin
				cali_fac_buffer <= {1'b0,cali_fac};
				state <= STATE_INPUT_EVEN;
			end
		end
		STATE_OUTPUT_LAST: 
		begin
			if (to_udp_ready)
				state <= STATE_IDLE;
		end
		default:
			state <= STATE_IDLE;
		endcase
	end
	
	
	//output the 160 32bit words to upd, first channel [31:16] , second channel [15:16]
	//output  wire [31:0] to_udp_data,           //  st.data
	assign to_udp_valid = (((state == STATE_CAL2 || state == STATE_CAL_EVEN) && (~waitrequest))|| state == STATE_OUTPUT_LAST)? 1'b1: 1'b0;
	assign to_udp_empty = 2'b0;
	assign to_udp_startofpacket = (state == STATE_CAL2 && (~waitrequest))? 1'b1:1'b0;
	assign to_udp_endofpacket = (state == STATE_OUTPUT_LAST)? 1'b1: 1'b0;
	//reverse the polarity for output => no for simulation
	assign to_udp_data = (((state == STATE_CAL2 || state == STATE_CAL_EVEN) && (~waitrequest))|| state == STATE_OUTPUT_LAST) ? {~first_data_buffer,~data_caled_data_a}: 32'b0; 
	
	
	
	

endmodule
