module i2cSlave (
  input clk,
  input rst,
  inout sda,
  input scl,

  output[7:0] ram_dataIn,
  output[9:0] ram_wraddress,
  output ram_wren,
  output enable_osd,
  output[7:0] highlight_line,
  output[7:0] reconf_data,
  output[7:0] video_gen_data,
  output Scanline scanline,
  output [23:0] conf240p,
  output reset_dc,
  output reset_opt,
  output[7:0] reset_conf,
  output activateHDMIoutput,
  output hq2x,
  output [1:0] colorspace,
  input [23:0] pinok,
  input [23:0] timingInfo,
  input [23:0] rgbData,
  input add_line,
  input line_doubler,
  input is_pal,
  input force_generate,
  input ControllerData controller_data,
  input KeyboardData keyboard_data,
  input [31:0] pll_adv_lockloss_count,
  input [31:0] hpd_low_count,
  input [31:0] pll54_lockloss_count,
  input [31:0] pll_hdmi_lockloss_count,
  input [31:0] control_resync_out_count,
  input [31:0] monitor_sense_low_count,
  input [15:0] testdata,
  output [7:0] clock_config_data,
  output [7:0] color_config_data,
  input [11:0] nonBlackPos1,
  input [11:0] nonBlackPos2,
  output nonBlackPixelReset,
  input [23:0] color_space_explorer,
  output resetpll
);

// local wires and regs
reg sdaDeb;
reg sclDeb;
reg [`DEB_I2C_LEN-1:0] sdaPipe;
reg [`DEB_I2C_LEN-1:0] sclPipe;

reg [`SCL_DEL_LEN-1:0] sclDelayed;
reg [`SDA_DEL_LEN-1:0] sdaDelayed;
reg [1:0] startStopDetState;
wire clearStartStopDet;
wire sdaOut;
wire sdaIn;
wire [7:0] regAddr;
wire [7:0] dataToRegIF;
wire writeEn;
wire [7:0] dataFromRegIF;
reg [1:0] rstPipe;
wire rstSyncToClk;
reg startEdgeDet;

assign sda = (sdaOut == 1'b0) ? 1'b0 : 1'bz;
assign sdaIn = sda;

// sync rst rsing edge to clk
always @(posedge clk) begin
  if (rst == 1'b1)
    rstPipe <= 2'b11;
  else
    rstPipe <= {rstPipe[0], 1'b0};
end

assign rstSyncToClk = rstPipe[1];

// debounce sda and scl
always @(posedge clk) begin
  if (rstSyncToClk == 1'b1) begin
    sdaPipe <= {`DEB_I2C_LEN{1'b1}};
    sdaDeb <= 1'b1;
    sclPipe <= {`DEB_I2C_LEN{1'b1}};
    sclDeb <= 1'b1;
  end
  else begin
    sdaPipe <= {sdaPipe[`DEB_I2C_LEN-2:0], sdaIn};
    sclPipe <= {sclPipe[`DEB_I2C_LEN-2:0], scl};
    if (&sclPipe[`DEB_I2C_LEN-1:1] == 1'b1)
      sclDeb <= 1'b1;
    else if (|sclPipe[`DEB_I2C_LEN-1:1] == 1'b0)
      sclDeb <= 1'b0;
    if (&sdaPipe[`DEB_I2C_LEN-1:1] == 1'b1)
      sdaDeb <= 1'b1;
    else if (|sdaPipe[`DEB_I2C_LEN-1:1] == 1'b0)
      sdaDeb <= 1'b0;
  end
end


// delay scl and sda
// sclDelayed is used as a delayed sampling clock
// sdaDelayed is only used for start stop detection
// Because sda hold time from scl falling is 0nS
// sda must be delayed with respect to scl to avoid incorrect
// detection of start/stop at scl falling edge. 
always @(posedge clk) begin
  if (rstSyncToClk == 1'b1) begin
    sclDelayed <= {`SCL_DEL_LEN{1'b1}};
    sdaDelayed <= {`SDA_DEL_LEN{1'b1}};
  end
  else begin
    sclDelayed <= {sclDelayed[`SCL_DEL_LEN-2:0], sclDeb};
    sdaDelayed <= {sdaDelayed[`SDA_DEL_LEN-2:0], sdaDeb};
  end
end

// start stop detection
always @(posedge clk) begin
  if (rstSyncToClk == 1'b1) begin
    startStopDetState <= `NULL_DET;
    startEdgeDet <= 1'b0;
  end
  else begin
    if (sclDeb == 1'b1 && sdaDelayed[`SDA_DEL_LEN-2] == 1'b0 && sdaDelayed[`SDA_DEL_LEN-1] == 1'b1)
      startEdgeDet <= 1'b1;
    else
      startEdgeDet <= 1'b0;
    if (clearStartStopDet == 1'b1)
      startStopDetState <= `NULL_DET;
    else if (sclDeb == 1'b1) begin
      if (sdaDelayed[`SDA_DEL_LEN-2] == 1'b1 && sdaDelayed[`SDA_DEL_LEN-1] == 1'b0) 
        startStopDetState <= `STOP_DET;
      else if (sdaDelayed[`SDA_DEL_LEN-2] == 1'b0 && sdaDelayed[`SDA_DEL_LEN-1] == 1'b1)
        startStopDetState <= `START_DET;
    end
  end
end


registerInterface u_registerInterface(
    .clk(clk),
    .addr(regAddr),
    .dataIn(dataToRegIF),
    .writeEn(writeEn),
    .dataOut(dataFromRegIF),
    .ram_dataIn(ram_dataIn),
    .ram_wraddress(ram_wraddress),
    .ram_wren(ram_wren),
    .enable_osd(enable_osd),
    //.debugData(debugData),
    .controller_data(controller_data),
    .keyboard_data(keyboard_data),
    .highlight_line(highlight_line),
    .reconf_data(reconf_data),
    .video_gen_data(video_gen_data),
    .scanline(scanline),
    .conf240p(conf240p),
    .reset_dc(reset_dc),
    .reset_opt(reset_opt),
    .reset_conf(reset_conf),
    .pinok(pinok),
    .timingInfo(timingInfo),
    .rgbData(rgbData),
    .add_line(add_line),
    .line_doubler(line_doubler),
    .is_pal(is_pal),
    .force_generate(force_generate),
    .activateHDMIoutput(activateHDMIoutput),
    .hq2x(hq2x),
    .colorspace(colorspace),
    .pll_adv_lockloss_count(pll_adv_lockloss_count),
    .hpd_low_count(hpd_low_count),
    .pll54_lockloss_count(pll54_lockloss_count),
    .pll_hdmi_lockloss_count(pll_hdmi_lockloss_count),
    .control_resync_out_count(control_resync_out_count),
    .monitor_sense_low_count(monitor_sense_low_count),
    .testdata(testdata),
    .clock_config_data(clock_config_data),
    .color_config_data(color_config_data),
    .nonBlackPos1(nonBlackPos1),
    .nonBlackPos2(nonBlackPos2),
    .nonBlackPixelReset(nonBlackPixelReset),
    .color_space_explorer(color_space_explorer),
    .resetpll(resetpll)
);

serialInterface u_serialInterface (
  .clk(clk), 
  .rst(rstSyncToClk | startEdgeDet), 
  .dataIn(dataFromRegIF), 
  .dataOut(dataToRegIF), 
  .writeEn(writeEn),
  .regAddr(regAddr), 
  .scl(sclDelayed[`SCL_DEL_LEN-1]), 
  .sdaIn(sdaDeb), 
  .sdaOut(sdaOut), 
  .startStopDetState(startStopDetState),
  .clearStartStopDet(clearStartStopDet) 
);


endmodule