`timescale 1ns/1ps
// 三通道图像合成一个RGB图像
module ColorBlender #(
    parameter reg [4:0] IN_DEPTH  = 12,  // 输入图像的色深
    parameter reg [4:0] OUT_DEPTH = 8    // 输出图像的色深
) (
    input wire clk,
    input wire reset,

    input wire in_en,
    input wire [15:0] in_data[3],  // 0:R 1:G 2:B
    output wire out_ready,
    output wire out_receive,

    // 输出相关
    input wire in_ready,
    input wire in_receive,
    output reg out_en,
    output reg [OUT_DEPTH - 1:0] out_data[3],

    // 颜色校正
    input wire [15:0] gain_red,
    input wire [15:0] gain_green,
    input wire [15:0] gain_blue,
    input wire enable
);
  localparam reg [2:0] READ_DATA = 0;
  localparam reg [2:0] CALC_DATA = 1;
  localparam reg [2:0] SATI_DATA = 2;
  localparam reg [2:0] SEND_DATA = 3;

  reg [2:0] state, nextState;
  reg [32 - 1:0] data_cal[3];  // 用于保存运算结果，防止溢出

  always @(posedge clk) begin
    if (reset) begin
      state <= READ_DATA;
    end else begin
      state <= nextState;
    end
  end

  always @(*) begin
    case (state)
      READ_DATA: nextState = (in_en) ? CALC_DATA : READ_DATA;
      CALC_DATA: nextState = SATI_DATA;
      SATI_DATA: nextState = SEND_DATA;
      SEND_DATA: nextState = (in_receive) ? READ_DATA : SEND_DATA;
      default:   nextState = READ_DATA;
    endcase
  end

  assign out_ready   = (!in_en && state == READ_DATA && !reset) ? 1 : 0;
  assign out_receive = (in_en && state == READ_DATA && !reset) ? 1 : 0;

  always @(posedge clk) begin
    if (reset) begin
      // 初始化
      data_cal[0] <= 0;
      data_cal[1] <= 0;
      data_cal[2] <= 0;

      out_data[0] <= 0;
      out_data[1] <= 0;
      out_data[2] <= 0;
      out_en <= 0;
    end else begin
      case (state)
        READ_DATA: begin
          if (in_en) begin
            data_cal[0] <= ({16'b0, in_data[0]}) << (8 - (IN_DEPTH - OUT_DEPTH));
            data_cal[1] <= ({16'b0, in_data[1]}) << (8 - (IN_DEPTH - OUT_DEPTH));
            data_cal[2] <= ({16'b0, in_data[2]}) << (8 - (IN_DEPTH - OUT_DEPTH));
          end
        end

        CALC_DATA: begin
          if (enable) begin
            data_cal[0] <= (data_cal[0] * {16'b0, gain_red}) >> 16;
            data_cal[1] <= (data_cal[1] * {16'b0, gain_green}) >> 16;
            data_cal[2] <= (data_cal[2] * {16'b0, gain_blue}) >> 16;
          end else begin
            data_cal[0] <= data_cal[0] >> 8;
            data_cal[1] <= data_cal[1] >> 8;
            data_cal[2] <= data_cal[2] >> 8;
          end
        end

        SATI_DATA: begin
          data_cal[0] <= |data_cal[0][31 : OUT_DEPTH] ? {32{1'b1}} : data_cal[0];
          data_cal[1] <= |data_cal[1][31 : OUT_DEPTH] ? {32{1'b1}} : data_cal[1];
          data_cal[2] <= |data_cal[2][31 : OUT_DEPTH] ? {32{1'b1}} : data_cal[2];
        end

        SEND_DATA: begin
          if (in_ready && !in_receive) begin
            out_en <= 1;
            out_data[0] <= data_cal[0][OUT_DEPTH-1:0];
            out_data[1] <= data_cal[1][OUT_DEPTH-1:0];
            out_data[2] <= data_cal[2][OUT_DEPTH-1:0];
          end else out_en <= 0;
        end

        default: ;
      endcase
    end
  end

endmodule
