// Pulse generator for Infrared LED

// Operation:
// On rising edge of strobe input, starts a 
// counter (pre-pulse count).
// When counter expires, turns on output. Starts
// a second counter (pulse length count).
// When secound counter expires, output is turned off.

// If input strobe goes low before either counter expires,
// pulse is disabled instantly. There's no purpose having
// the LEDs on outside of the camera exposure window.

module irled_pulse
#(
    parameter CLK_DIV = 8
)
(
    input           CLK
,   input           RESET

,   input           STROBE
,   input [15:0]    PRE_PULSE_COUNT
,   input [15:0]    PULSE_LENGTH_COUNT
,   output          PULSE
);

reg [15:0] count;
reg [15:0] pre_pulse_count;
reg [15:0] pulse_length_count;

reg [2:0] strobe_cdc; // double ff cross domain clock recovery
                      // third register is for rising edge detection

wire strobe_rising;
assign strobe_rising = (strobe_cdc[2:1] == 2'b01);

reg [1:0] strobe_state;

localparam  STROBE_STATE_IDLE       = 2'b00,
            STROBE_STATE_PRE_PULSE  = 2'b01,
            STROBE_STATE_PULSE      = 2'b10;


// **** Rising edge detector ****
always @(posedge CLK or posedge RESET) begin
    if(RESET) begin
        strobe_cdc <= 3'b000;
    end
    else begin
        strobe_cdc <= {strobe_cdc[1:0], STROBE};
    end
end

reg [7:0] clk_div_count;
localparam [7:0] bCLK_DIV = CLK_DIV - 1;

// **** State machine ****
always @(posedge CLK or posedge RESET) begin
    if(RESET) begin
        count <= 16'd0;
        strobe_state <= STROBE_STATE_IDLE;
        pre_pulse_count <= 16'd0;
        pulse_length_count <= 16'd0;
        clk_div_count <= 8'd0;
    end 
    else begin

        if(clk_div_count == bCLK_DIV)
            clk_div_count <= 8'd0;
        else
            clk_div_count <= clk_div_count + 8'd1;

        case(strobe_state)
        STROBE_STATE_IDLE: begin
            // Only update (pre) pulse counts when in idle state
            // prevents spurious events
            pre_pulse_count <= PRE_PULSE_COUNT;
            pulse_length_count <= PULSE_LENGTH_COUNT;


            if(strobe_rising) begin
                strobe_state <= STROBE_STATE_PRE_PULSE;
                count <= 16'd0;
            end
        end
        STROBE_STATE_PRE_PULSE: begin
            if(STROBE) begin
                if(count == pre_pulse_count) begin
                    count <= 16'd0;
                    strobe_state <= STROBE_STATE_PULSE;
                end
                else begin
                    if(clk_div_count == 8'd0)
                        count <= count + 16'd1;
                end
            end
            else begin
                // if STROBE input goes low:
                // shortcut back to idle and wait for rising edge again
                strobe_state <= STROBE_STATE_IDLE;
            end
        end
        STROBE_STATE_PULSE: begin
            if(STROBE) begin
                if(count == pulse_length_count) begin
                    strobe_state <= STROBE_STATE_IDLE;
                end 
                else begin
                    if(clk_div_count == 8'd0)
                        count <= count + 16'd1;
                end
            end 
            else begin
                strobe_state <= STROBE_STATE_IDLE;
            end
        end
        endcase
    end
end

assign PULSE = (strobe_state == STROBE_STATE_PULSE);


endmodule
