//! adc request
//!
//! creates ADC triggers after rising and falling edges of IR LED pulse
//! 
//! also triggers periodically if the IR LED is non-changing

module adcreq
#(
    parameter CLK_FREQ_MHZ = 24
)
( 
    input   CLK,
    input   RESET,
    input   PULSE, //! IR LED pulse output - used for timing the ADC triggers
    
    output  ADC_REQ, //! sent to adctrig module, and then to Gowin ADC hardware
    input   ADC_READY, //! result is ready in Gowin ADC hardware
    output  ADC_CHAN, //! 0 - left LED, 1 - right LED
    output  SAMPLE_ZERO //! expecting to sample a zero current value
);

// how long to wait when not pulsing before taking idle measurements
`define ADC_TIMEOUT_DELAY       (32'd50000 * CLK_FREQ_MHZ)
// 80us setting time for edges
`define ADC_SETTLING_DELAY      (16'd80 * CLK_FREQ_MHZ)
// 40us between samples
`define ADC_INTERSAMPLE_DELAY   (16'd150 * CLK_FREQ_MHZ)

reg [31:0] timeout_count;
reg [15:0] delay_count;
reg [2:0] pulse_z; // delay / clock domain crossing for pulse input
reg [2:0] ready_z; // delay / clock domain crossing for adc ready
reg [2:0] pulse_state;
reg adc_req;
wire adc_ack;
reg adc_channel; // 0 - left, 1 - right
reg sample_type; // 0 - during pulse, 1 - during off (expecting zero current)
reg request_complete;
reg conversion_complete;

wire pulse_rising = pulse_z[2:1] == 2'b01;
wire pulse_falling = pulse_z[2:1] == 2'b10;

always @(posedge CLK or posedge RESET) begin
    if(RESET) begin
        pulse_z <= 3'b000;
    end 
    else begin
        pulse_z <= {pulse_z[1:0], PULSE};
    end
end

wire ready_rising = ready_z[2:1] == 2'b01;
wire ready_falling = ready_z[2:1] == 2'b10;

always @(posedge CLK or posedge RESET) begin
    if(RESET) begin
        ready_z <= 3'b000;
    end 
    else begin
        ready_z <= {ready_z[1:0], ADC_READY};
    end
end


localparam  STATE_IDLE          = 3'd0, //! nothing happening
            STATE_PULSE_LEFT    = 3'd1, //! measuring left led current during IR pulse
            STATE_PULSE_RIGHT   = 3'd2, //! measuring right led current during IR pulse
            STATE_OFF_LEFT      = 3'd3, //! measuring left led current while off
            STATE_OFF_RIGHT     = 3'd4, //! measuring right led current while off
            STATE_TIMEOUT_LEFT  = 3'd5, //! measuring left led current while off (after pulse timeout)
            STATE_TIMEOUT_RIGHT = 3'd6; //! measuring right led current while off (after pulse timeout)

always @(posedge CLK or posedge RESET) begin
    if(RESET) begin
        timeout_count <= 32'd0;
        delay_count <= 16'd0;
        pulse_state <= STATE_IDLE;
        sample_type <= 1'b0;
        request_complete <= 1'b0;
        adc_req <= 1'b0;
        adc_channel <= 1'b0;
        conversion_complete <= 1'b0;
        sample_type <= 1'b0;
    end else begin
        case(pulse_state)
        STATE_IDLE: begin
            if(pulse_rising) begin
                request_complete <= 1'b0;
                delay_count <= 16'd0;
                timeout_count <= 32'd0;
                pulse_state <= STATE_PULSE_LEFT;
            end
            else if(pulse_falling) begin
                request_complete <= 1'b0;
                delay_count <= 16'd0;
                timeout_count <= 32'd0;
                pulse_state <= STATE_OFF_LEFT;
            end
            else if(timeout_count >= (`ADC_TIMEOUT_DELAY)) begin
                request_complete <= 1'b0;
                timeout_count <= 32'd0;
                pulse_state <= STATE_TIMEOUT_LEFT;
            end
            else begin
                timeout_count <= timeout_count + 32'd1;
            end
        end
        STATE_PULSE_LEFT, STATE_PULSE_RIGHT, STATE_OFF_LEFT, STATE_OFF_RIGHT, STATE_TIMEOUT_LEFT, STATE_TIMEOUT_RIGHT: begin
            // cancel on pulse activity
            if(pulse_falling || pulse_rising) begin
                pulse_state <= STATE_IDLE;
            end
            else begin
                if(!request_complete) begin
                    if(!adc_req) begin
                        if(pulse_state == STATE_PULSE_LEFT || pulse_state == STATE_OFF_LEFT) begin
                            if(delay_count >= (`ADC_SETTLING_DELAY)) begin
                                conversion_complete <= 1'b0;
                                adc_req <= 1'b1;
                                adc_channel <= 1'b0;

                                if(pulse_state == STATE_PULSE_LEFT)
                                    sample_type <= 1'b0;
                                else
                                    sample_type <= 1'b1;
                            end
                            else
                                delay_count <= delay_count + 16'd1;
                        end
                        else begin // STATE_PULSE_RIGHT or STATE_OFF_RIGHT or either of the STATE_TIMEOUT_x's
                            // no delay, go straight to adc request
                            conversion_complete <= 1'b0;
                            adc_req <= 1'b1;
                            if(pulse_state == STATE_TIMEOUT_LEFT)
                                adc_channel <= 1'b0;
                            else
                                adc_channel <= 1'b1;
                            if(pulse_state == STATE_PULSE_RIGHT)
                                sample_type <= 1'b0;
                            else
                                sample_type <= 1'b1;
                        end
                    end
                    else begin // adc_req == 1
                        if(adc_ack) begin
                            request_complete <= 1'b1;
                            adc_req <= 1'b0;
                        end
                    end
                end
                else begin // request_complete == 1
                    if(!conversion_complete) begin
                        // wait for ADC_READY to go high, signaling ADC conversion is complete
                        if(ready_rising) begin
                            conversion_complete <= 1'b1;
                            delay_count <= 16'd0;
                        end
                    end 
                    else begin
                        if(pulse_state == STATE_PULSE_LEFT || pulse_state == STATE_OFF_LEFT || pulse_state == STATE_TIMEOUT_LEFT) begin
                            // wait intersample delay then trigger other channel
                            if(delay_count >= (`ADC_INTERSAMPLE_DELAY)) begin
                                if(pulse_state == STATE_PULSE_LEFT)
                                    pulse_state <= STATE_PULSE_RIGHT;
                                else if(pulse_state == STATE_OFF_LEFT)
                                    pulse_state <= STATE_OFF_RIGHT;
                                else
                                    pulse_state <= STATE_TIMEOUT_RIGHT;
                                delay_count <= 16'd0;
                                request_complete <= 1'b0;
                            end
                            else begin
                                delay_count <= delay_count + 16'd1;
                            end
                        end
                        else begin // STATE_x_RIGHT, next state is back to idle
                            pulse_state <= STATE_IDLE;
                        end
                    end
                end
            end
            
        end


        default: begin
            pulse_state <= STATE_IDLE;
            adc_req <= 1'b0;
            sample_type <= 1'b0;
        end
        endcase
    end
end

adctrig u_adctrig(
    .CLK(CLK),
    .RESET(RESET),
    .ADC_TRIG(adc_req),
    .ADC_TRIG_ACK(adc_ack),
    .ADC_READY(ADC_READY),
    .ADC_REQ(ADC_REQ)
);

assign ADC_CHAN = adc_channel;
assign SAMPLE_ZERO = sample_type;

endmodule
