//! current monitor
//!
//! receives ADC measurements of the current monitor
//! on each IR LED
//!
//! higher level controller can receive these measurements
//! and decide to disable eyetracking due to overcurrent

module current_monitor
(
    input           CLK
,   input           RESET

//,    input           ADC_REQ //! request to Gowin ADC hardware
,   input           ADC_READY //! reply back from Gowin ADC hardware
,   input [13:0]    ADC_DATA //! data from Gowin ADC
,   input           ADC_CHAN //! 0 - left IR LED, 1 - right IR LED
,   input           SAMPLE_ZERO //! 0 - measurent run current, 1 - measure idle current

,   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

,   input [3:0]     CONSECUTIVE_ERROR_LIMIT

,   input [13:0]    RUN_LIMIT
,   input [13:0]    IDLE_LIMIT

,   output          IR_LEFT_RUN_ERROR_LATCH
,   output          IR_RIGHT_RUN_ERROR_LATCH
,   output          IR_LEFT_IDLE_ERROR_LATCH
,   output          IR_RIGHT_IDLE_ERROR_LATCH
);

reg [13:0]  irl_run;
reg [13:0]  irr_run;
reg [13:0]  irl_idle;
reg [13:0]  irr_idle;

reg [2:0]   adcrdy_cdc; //! cross clock domain / rising edge detector
wire        adcrdy_rising = (adcrdy_cdc[2:1] == 2'b01);

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], ADC_READY};
    end
end

reg [3:0] ir_left_run_error_count;
reg [3:0] ir_right_run_error_count;
reg [3:0] ir_left_idle_error_count;
reg [3:0] ir_right_idle_error_count;
reg ir_left_run_error_latch;
reg ir_right_run_error_latch;
reg ir_left_idle_error_latch;
reg ir_right_idle_error_latch;

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;
        ir_left_run_error_count <= 4'd0;
        ir_right_run_error_count <= 4'd0;
        ir_left_idle_error_count <= 4'd0;
        ir_right_idle_error_count <= 4'd0;
        ir_left_run_error_latch <= 1'b0;
        ir_right_run_error_latch <= 1'b0;
        ir_left_idle_error_latch <= 1'b0;
        ir_right_idle_error_latch <= 1'b0;
    end 
    else begin
        if(adcrdy_rising) begin
            case({ADC_CHAN,SAMPLE_ZERO})
            2'b00: begin// channel = left, measurement = run
                irl_run <= ADC_DATA;
                if(ADC_DATA > RUN_LIMIT)
                    if(ir_left_run_error_count != 4'b1111)
                        ir_left_run_error_count <= ir_left_run_error_count + 4'd1;
                else
                    ir_left_run_error_count <= 4'd0;

            end
            2'b01: begin // channel = left, measurement = idle
                irl_idle <= ADC_DATA;
                if(ADC_DATA > IDLE_LIMIT)
                    if(ir_left_idle_error_count != 4'b1111)
                        ir_left_idle_error_count <= ir_left_idle_error_count + 4'd1;
                else
                    ir_left_idle_error_count <= 4'd0;
            end
            2'b10: begin // channel = right, measurement = run
                irr_run <= ADC_DATA;
                if(ADC_DATA > RUN_LIMIT)
                    if(ir_right_run_error_count != 4'b1111)
                        ir_right_run_error_count <= ir_right_run_error_count + 4'd1;
                else
                    ir_right_run_error_count <= 4'd0;
            end
            2'b11: begin // channel = right, measurement = idle
                irr_idle <= ADC_DATA;
                if(ADC_DATA > IDLE_LIMIT)
                    if(ir_right_idle_error_count != 4'b1111)
                        ir_right_idle_error_count <= ir_right_idle_error_count + 4'd1;
                else
                    ir_right_idle_error_count <= 4'd0;
            end
            endcase
        end   

        // permanently latch errors if over the consecutive limit
        if(ir_left_run_error_count >= CONSECUTIVE_ERROR_LIMIT)
            ir_left_run_error_latch <= 1'b1;
        if(ir_left_idle_error_count >= CONSECUTIVE_ERROR_LIMIT)
            ir_left_idle_error_latch <= 1'b1;
        if(ir_right_run_error_count >= CONSECUTIVE_ERROR_LIMIT)
            ir_right_run_error_latch <= 1'b1;
        if(ir_right_idle_error_count >= CONSECUTIVE_ERROR_LIMIT)
            ir_right_idle_error_latch <= 1'b1;
    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;

assign IR_LEFT_RUN_ERROR_LATCH = ir_left_run_error_latch;
assign IR_RIGHT_RUN_ERROR_LATCH = ir_right_run_error_latch;
assign IR_LEFT_IDLE_ERROR_LATCH = ir_left_idle_error_latch;
assign IR_RIGHT_IDLE_ERROR_LATCH = ir_right_idle_error_latch;

endmodule
