`timescale 1ns / 1ps

module del_meas #(
	parameter WIDTH = 10,
	
	// AUTOCALCULATED
	parameter DEL_MAX_TICK = (1 << WIDTH) - 1 // maximum delay that can be handled
)(
	input clk,						// clock
	input rst,						// reset
	input clear,					// clear
	
	input ref,						// reference signal
	input sig,						// signal being measured
	
	input arm,						// arm the next measurement cycle
	output listening,				// indicates that measurement is armed
	output done,					// signals when measurement has concluded
	input ack,						// acknowledge latest measurement, turn off done
	
	output reg negative,					// indicates if delay is negative
	output reg [WIDTH-1:0] delay,		// delay in ticks
	
	output [2:0] state_dbg
);

wire rstclr = rst || clear;

// named states
localparam S_IDLE = 3'b001;
localparam S_LISTEN = 3'b010;
localparam S_MEASURE = 3'b100;

// first edge associations
localparam FE_REF = 1'b0;
localparam FE_SIG = 1'b1;

reg [2:0] state; // FSM state

reg arm_prev, ref_prev, sig_prev, ack_prev; // various input values in the previous cycle
wire arm_edge = arm && !arm_prev; // there was a transition on the arm input
wire ref_edge = !ref && ref_prev; // there was a transition in the reference
wire sig_edge = !sig && sig_prev; // there was a transition in the signal

reg first_edge; // stores the signal transitioned first
wire end_meas = (first_edge == FE_REF) ? sig_edge : ref_edge; // end meas pulse
wire ack_pulse = ack && !ack_prev; // ack line has been pulsed

reg acked; // acknowledge in the previous cycle, used for done masking

// main state machine
always@(posedge clk)
begin
	if (rstclr)
	begin
		negative <= 0;
		delay <= 0;
		
		arm_prev <= 0;
		ref_prev <= 0;
		sig_prev <= 0;
		
		state <= S_IDLE;
		first_edge <= FE_REF;
		acked <= 1;
		ack_prev <= 0;
	end
	else
	begin
		// retain specific inputs
		arm_prev <= arm;
		ref_prev <= ref;
		sig_prev <= sig;
		ack_prev <= ack;
		
		// save acknowledgement
		if (ack_pulse)
		begin
			acked <= 1;
		end
		
		// delay counter with saturation
		if ((state == S_MEASURE) && (delay < DEL_MAX_TICK))
		begin
			delay <= delay + 1'b1;
		end
		
		// state machine
		case (state)
			S_IDLE: if (arm_edge) state <= S_LISTEN;
			S_LISTEN: 
				if (sig_edge || ref_edge) 
				begin
					delay <= 0; // reset the delay
				
					if (sig_edge && ref_edge) // both signals transitioned in the same time slot
					begin
						negative <= 0;
						state <= S_IDLE;
						acked <= 0;
					end
					else // only a single transision was detected
					begin
						state <= S_MEASURE; 
						first_edge <= sig_edge ? FE_SIG : FE_REF; // store which signal produced the first edge
						negative <= sig_edge; // delay is negative, if signal edge comes first
					end
				end
			S_MEASURE: 
				if (end_meas)
				begin
					state <= S_IDLE;
					acked <= 0;
				end
			default: 
				begin
					state <= S_IDLE;
					acked <= 0;
				end
		endcase 
	end
end

// output logic
assign listening = (state == S_LISTEN);
assign done = (state == S_IDLE) && (!acked);

assign state_dbg = state;

endmodule
