//! Measure the current of IR LEDs per eye
//! Uses the Gowin ADC built in to each FPGA 
//!
//! ======= Module descriptions: =======
//! adctrig (ADC Trigger)
//! This module sends adc requests to the Gowin ADC hardware module.
//! Waits for hardware adc ready signal to go low before releasing
//! the request. If for some reason the ready signal is NOT high
//! when the trigger is requested, simply waits a few cycles and
//! releases the request.
//!
//! adcreq (ADC request)
//! creates ADC triggers after each rising and falling edge of IR LED pulse
//! This module is responsible for starting the Gowin ADC at the proper point
//! in time for active current sense and (expected) zero current measurements.
//! Also periodically triggers the ADC if IR pulse signal isn't changing.
//!
//! irled_current_sense
//! This is the top module for the current sense function.
//! Saves the most recent value of live or zero current sense for each eye
//! Higher level controller can receive these measurements and decide whether
//! or not to disable eyetracking due to an error

// Add the following lines to the .cst file to connect the analog pads to the ADC inputs:
// USE_ADC_SRC bus0 IOT19 // L7 (p) L8 (n) left camera analog input
// USE_ADC_SRC bus1 IOB26 // F2 (p) F1 (n) right camera analog input

// ---------------------------------------------------------------------------
// | Eye    | Current Sense I/O Pin | Current Sense IOB Number | Bank  | Bus | 
// ---------------------------------------------------------------------------
// | Left   | L7+, L8-              | IOT19                    | 7     | 0   |
// | Right  | F2+, F1-              | IOB26                    | 5     | 1   |
// ---------------------------------------------------------------------------

module adctrig
( 
    input   CLK,
    input   RESET,
    input   ADC_TRIG, //! Trigger input from controller
    output  ADC_TRIG_ACK, //! Acknowledge output to controller

    input   ADC_READY, //! Ready from Gowin ADC
    output  ADC_REQ  //! Request output to Gowin ADC
);

reg ack;
reg req;
reg [1:0] ready_cdc;
wire adcready = ready_cdc[1];
reg [3:0] reqcounter;

reg [1:0] state;
localparam      IDLE = 2'b00,
                WAIT_READY_FALL = 2'b01,
                WAIT_COUNTER = 2'b10,
                WAIT_ACK = 2'b11;

//! Clock domain crossing for hardware signals
always @(posedge CLK or posedge RESET) begin
    if(RESET)
        ready_cdc <= 2'b00;
    else 
        ready_cdc <= {ready_cdc[0], ADC_READY};
end

always @(posedge CLK or posedge RESET) begin
    if(RESET) begin
        ack <= 1'b0;
        req <= 1'b0;
        reqcounter <= 4'd0;
        state <= IDLE;
    end 
    else begin
        case(state) 
        IDLE: begin
            ack <= 1'b0;
            if(ADC_TRIG) begin
                req <= 1'b1;
                if(adcready) begin
                    state <= WAIT_READY_FALL;
                end
                else begin
                    state <= WAIT_COUNTER;
                    reqcounter <= 4'd15;
                end
            end 
        end
        
        WAIT_READY_FALL: begin
            if(!adcready) begin
                state <= WAIT_ACK;
                ack <= 1'b1;
                req <= 1'b0;
            end            
        end

        WAIT_COUNTER: begin
            if(reqcounter == 4'd0) begin
                state <= WAIT_ACK;
                ack <= 1'b1;
                req <= 1'b0;
            end
            else begin
                reqcounter <= reqcounter - 4'd1;
            end
        end

        WAIT_ACK: begin
            if(!ADC_TRIG) begin
                ack <= 1'b0;
                state <= IDLE;
            end
        end

        endcase
    end
end

assign ADC_REQ = req;
assign ADC_TRIG_ACK = ack;

endmodule

module adcreq
#(
    parameter CLK_FREQ_MHZ = 60
)
( 
    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
);

// 50ms delay at 60MHz = 60*50,000 = 3,000,000
localparam ADC_TIMEOUT_DELAY  =     (32'd50000 * CLK_FREQ_MHZ);
// 10us setting time for edges = 60*10 = 600
localparam ADC_SETTLING_DELAY =     (16'd10 * CLK_FREQ_MHZ);
// 10us between samples
localparam ADC_INTERSAMPLE_DELAY =  (16'd10 * 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



module irled_current_sense
#(
    // Setting limit for running LED current: 
    // BCR421U set for ~13mA
    // 1 ohm current sense resistor (V = I)
    // Current sense gain = 316/15 = 21.07
    // ADC is 12-bit (max 2047) fractional part
    // Values over 1V will start to read over 2048
    //      Note that the ADC counts are inflated by 1.024x
    //      So input 0.5V will become 1048 (0.5*2048*1.024)
    // 
    // Current-to-counts calculation:
    //      ADC_DATA = I (mA) * 21.07 * 2048 * 1.024
    //
    // If we want a limit of 30mA for current (over 2x the BCR421U setting)
    // We should impose ADC counts limit of 1325
    // And a small 3mA limit for idle ==> 132
    
    parameter RUN_LIMIT = 14'd1325,
    parameter IDLE_LIMIT = 14'd132
)
(
    input           CLK
,   input           RESET

,   input           PULSE //! IR LED pulse output - used for timing the ADC triggers

,   output [13:0]   IR_LEFT_RUN //! current measurement of left IR LED while running
,   output [13:0]   IR_LEFT_IDLE //! current measurement of left IR LED while idle
,   output [13:0]   IR_RIGHT_RUN //! current measurement of right IR LED while running
,   output [13:0]   IR_RIGHT_IDLE //! current measurement of right IR LED while idle

,   output          IR_LEFT_RUN_ERROR //! overcurrent flag, left LED, run mode
,   output          IR_RIGHT_RUN_ERROR //! overcurrent flag, right LED, idle mode
,   output          IR_LEFT_IDLE_ERROR //! overcurrent flag, left LED, run mode
,   output          IR_RIGHT_IDLE_ERROR //! overcurrent flag, right LED, idle mode
    
);


reg [13:0]  irl_run;
reg [13:0]  irr_run;
reg [13:0]  irl_idle;
reg [13:0]  irr_idle;

wire        gowin_adc_ready;
wire        gowin_adc_request;
wire [13:0] gowin_adc_data;
wire        gowin_adc_chan_select;
wire        sample_zero;


reg [2:0]   adcrdy_cdc; //! cross clock domain / rising edge detector
wire        adcrdy_rising = (adcrdy_cdc[2:1] == 2'b01);

Gowin_ADC u_adc(
    .adcrdy(gowin_adc_ready), //output adcrdy
    .adcvalue(gowin_adc_data), //output [13:0] adcvalue
    .mdrp_rdata(), //output [7:0] mdrp_rdata
    .vsenctl(gowin_adc_chan_select ? 3'b000 : 3'b001), //input [2:0] vsenctl (3'b000 = glo_left (BANK 0/6/7), 3'b001 = glo_right (BANK 2/3/4/5)
    .adcen(1'b1), //input adcen
    .clk(CLK), //input clk
    .drstn(~RESET), //input drstn
    .adcreqi(gowin_adc_request), //input adcreqi
    .adcmode(1'b1), //input adcmode 1 = voltage mode, 0 = temperature mode
    .mdrp_clk(1'b0), //input mdrp_clk
    .mdrp_wdata(8'h0), //input [7:0] mdrp_wdata
    .mdrp_a_inc(1'b0), //input mdrp_a_inc
    .mdrp_opcode(2'b00) //input [1:0] mdrp_opcode
);

adcreq u_adcreq(
    .CLK(CLK),
    .RESET(RESET),
    .PULSE(PULSE),
    .ADC_REQ(gowin_adc_request),
    .ADC_READY(gowin_adc_ready),
    .ADC_CHAN(gowin_adc_chan_select),
    .SAMPLE_ZERO(sample_zero)
);

always @(posedge CLK or posedge RESET) begin
    if(RESET) begin
        adcrdy_cdc <= 3'b111; 
        // reset to all ones, not zeros
        // this prevents detecting a rising edge as soon as we leave reset
        // The Gowin ADC comes out of reset with ready high anyway
        // So the first change we actually should see is a falling edge
    end
    else begin
        adcrdy_cdc <= {adcrdy_cdc[1:0], gowin_adc_ready};
    end
end

always @(posedge CLK or posedge RESET) begin
    if(RESET) begin
        irl_run <= 14'd0;
        irr_run <= 14'd0;
        irl_idle <= 14'd0;
        irr_idle <= 14'd0;
    end 
    else begin
        if(adcrdy_rising) begin
            case({gowin_adc_chan_select, sample_zero})
            2'b00: // channel = left, measurement = run
                irl_run <= gowin_adc_data;
            2'b01: // channel = left, measurement = idle
                irl_idle <= gowin_adc_data;
            2'b10: // channel = right, measurement = run
                irr_run <= gowin_adc_data;
            2'b11: // channel = right, measurement = idle
                irr_idle <= gowin_adc_data;
            endcase
        end     
    end
end

assign IR_LEFT_RUN = irl_run;
assign IR_LEFT_IDLE = irl_idle;
assign IR_RIGHT_RUN = irr_run;
assign IR_RIGHT_IDLE = irr_idle;

assign IR_LEFT_RUN_ERROR = irl_run > RUN_LIMIT;
assign IR_LEFT_IDLE_ERROR = irl_idle > IDLE_LIMIT;
assign IR_RIGHT_RUN_ERROR = irr_run > RUN_LIMIT;
assign IR_RIGHT_IDLE_ERROR = irr_idle > IDLE_LIMIT;

endmodule
