`timescale 1ns/1ps
module isp #(
    parameter reg [15:0] IN_WIDTH    = 1936,
    parameter reg [15:0] IN_HEIGHT   = 1088,
    parameter reg [15:0] OUT_WIDTH   = 1920,
    parameter reg [15:0] OUT_HEIGHT  = 1080,
    parameter reg [ 4:0] COLOR_DEPTH = 8,     // Can't Change!!!
    parameter reg [ 1:0] RAW_TYPE    = 3      // 0:grbg 1:rggb 2:bggr 3:gbrg
) (
    // 基本信号
    input wire clk,
    input wire reset,

    // 数据输入信号
    input wire in_en,
    input wire [15:0] in_data[3],  // 数据输入线，0、1、2分别表示第一、二、三行
    output wire out_ready,            // 数据请求线，高电平：请求三个数据，直到读取完才拉低
    output wire out_receive,

    // output wire out_clk,
    output wire out_en,
    output wire [3 * COLOR_DEPTH - 1:0] out_data,
    input wire in_ready,
    // input wire in_receive,

    // 颜色校正，低八位为小数位，高八位为整数位
    input wire [15:0] gain_red,
    input wire [15:0] gain_green,
    input wire [15:0] gain_blue,
    input wire blender_enable,  // 是否启用颜色校正

    // Gamma矫正，低八位为小数位
    // input wire [7:0] gamma_inverse,
    input wire [7:0] gamma_table[256],
    input wire gamma_enable,

    // 白平衡
    input wire [15:0] white_gain[3],
    input wire [8:0] flame_rate,
    input wire white_enable,

    // 饱和度校正
    input wire signed [31:0] saturation_inc,
    input wire               saturation_enable  // -256~256
);
  localparam reg [15:0] BAYER_WIDTH = IN_WIDTH - 2;
  localparam reg [15:0] BAYER_HEIGHT = IN_HEIGHT - 2;

  wire [COLOR_DEPTH - 1:0] w_out_data[3];
  assign out_data = {w_out_data[2], w_out_data[1], w_out_data[0]};

  // 颜色校正，并改变色深
  wire blender_en, blender_ready, blender_receive;
  wire [15:0] blender_r, blender_g, blender_b;

  // 裁切图像
  wire crop_en, crop_ready, crop_receive;  // scaler 请求数据
  reg [COLOR_DEPTH - 1:0] crop_data[3];

  // Gamma矫正
  wire gamma_en, gamma_ready, gamma_receive;
  wire [COLOR_DEPTH - 1 : 0] gamma_data[3];

  // 白平衡
  wire white_en, white_ready, white_receive;
  wire [COLOR_DEPTH - 1 : 0] white_data[3];

  // 饱和度校正
  wire saturation_en, saturation_ready, saturation_receive;
  wire [COLOR_DEPTH - 1 : 0] saturation_data[3];

  //   reg in_receive;
  //   always @(posedge clk) in_receive <= in_en;
  wire in_receive;
  assign in_receive = out_en;

  // assign out_clk = clk;

  Demosaic2 #(
      .IM_WIDTH (IN_WIDTH),
      .IM_HEIGHT(IN_HEIGHT),
      .RAW_TYPE (RAW_TYPE)
  ) inst_demosaic (
      .clk(clk),
      .reset(reset),
      .in_en(in_en),
      .in_data(in_data),
      .out_ready(out_ready),
      .out_receive(out_receive),

      .out_en(blender_en),
      .in_ready(blender_ready),
      .in_receive(blender_receive),
      .out_r(blender_r),
      .out_g(blender_g),
      .out_b(blender_b)
  );

  ColorBlender #(
      .IN_DEPTH (12),
      .OUT_DEPTH(COLOR_DEPTH)
  ) inst_blender (
      .clk  (clk),
      .reset(reset),

      .in_en(blender_en),
      .in_data({blender_b, blender_g, blender_r}),
      .out_ready(blender_ready),
      .out_receive(blender_receive),

      .in_ready(crop_ready),
      .in_receive(crop_receive),
      .out_en(crop_en),
      .out_data(crop_data),

      .gain_red(gain_red),
      .gain_green(gain_green),
      .gain_blue(gain_blue),
      .enable(blender_enable)
  );

  Crop #(
      .IN_WIDTH(BAYER_WIDTH),
      .IN_HEIGHT(BAYER_HEIGHT),
      .OUT_WIDTH(OUT_WIDTH),
      .OUT_HEIGHT(OUT_HEIGHT),
      .COLOR_DEPTH(COLOR_DEPTH)
  ) inst_crop (
      .clk  (clk),
      .reset(reset),

      .in_en(crop_en),
      .out_ready(crop_ready),
      .out_receive(crop_receive),
      .in_data({crop_data[2], crop_data[1], crop_data[0]}),

      .out_en(white_en),
      .in_ready(white_ready),
      .in_receive(white_receive),
      .out_data({white_data[2], white_data[1], white_data[0]})
  );

  GreyWorld #(
      .COLOR_DEPTH(COLOR_DEPTH),
      .IM_SIZE({16'b0, OUT_WIDTH} * {16'b0, OUT_HEIGHT})
  ) inst_whitebalance (
      .clk  (clk),
      .reset(reset),

      .in_en(white_en),
      .in_data(white_data),
      .out_ready(white_ready),
      .out_receive(white_receive),

      .in_ready(gamma_ready),
      .in_receive(gamma_receive),
      .out_en(gamma_en),
      .out_data(gamma_data),

      .enable(white_enable),
      .flame_rate(flame_rate),
      .white_gain(white_gain)
  );

  // 查找表型Gamma校正
  GammaCorrection #(
      .COLOR_DEPTH(COLOR_DEPTH)
  ) inst_gamma (
      .clk  (clk),
      .reset(reset),

      .in_en(gamma_en),
      .in_data(gamma_data),
      .out_ready(gamma_ready),
      .out_receive(gamma_receive),

      .in_ready(saturation_ready),
      .in_receive(saturation_receive),
      .out_en(saturation_en),
      .out_data(saturation_data),

      .gamma_table(gamma_table),
      .enable(gamma_enable)
  );

  SaturationCorrection #(
      .COLOR_DEPTH(COLOR_DEPTH)
  ) inst_saturation (
      .clk  (clk),
      .reset(reset),

      .in_en(saturation_en),
      .out_ready(saturation_ready),
      .out_receive(saturation_receive),
      .in_data(saturation_data),

      .in_ready(in_ready),
      .in_receive(in_receive),
      .out_en(out_en),
      .out_data(w_out_data),

      .saturation_inc(saturation_inc),
      .enable(saturation_enable)
  );

endmodule
