Thứ Năm, 18 tháng 4, 2019

No tit


module noisefter
(
  clk  ,
  rst_n,
  en   ,
  nfs  ,
  sgi  ,
  sgo 
);

input         clk  ;
input         rst_n;
input         en   ;
input  [ 2:0] nfs  ;
input         sgi  ;  // input signal
output        sgo  ;  // output signal

reg           rsgo ;

reg           sgi_1;  // 1st state
reg           sgi_2;  // 2nd state
reg           sgi_3;  // 3rd state
reg           sgi_4;  // 4th state
reg           sgi_5;  // 5th state
reg           sgi_6;  // 6th state
reg           sgi_7;  // 7th state

always @(posedge clk or negedge rst_n) begin
  if (rst_n == 1'b0) begin
    sgi_1 <= 1'b1;
  end
  else if (en == 1'b1) begin
    sgi_1 <= sgi;
  end
  else begin
    sgi_1 <= sgi_1;
  end
end

always @(posedge clk or negedge rst_n) begin
  if (rst_n == 1'b0) begin
    sgi_2 <= 1'b1;
  end
  else if (en == 1'b1) begin
    sgi_2 <= sgi_1;
  end
  else begin
    sgi_2 <= sgi_2;
  end
end

always @(posedge clk or negedge rst_n) begin
  if (rst_n == 1'b0) begin
    sgi_3 <= 1'b1;
  end
  else if (en == 1'b1) begin
    sgi_3 <= sgi_2;
  end
  else begin
    sgi_3 <= sgi_3;
  end
end

always @(posedge clk or negedge rst_n) begin
  if (rst_n == 1'b0) begin
    sgi_4 <= 1'b1;
  end
  else if (en == 1'b1) begin
    sgi_4 <= sgi_3;
  end
  else begin
    sgi_4 <= sgi_4;
  end
end

always @(posedge clk or negedge rst_n) begin
  if (rst_n == 1'b0) begin
    sgi_5 <= 1'b1;
  end
  else if (en == 1'b1) begin
    sgi_5 <= sgi_4;
  end
  else begin
    sgi_5 <= sgi_5;
  end
end

always @(posedge clk or negedge rst_n) begin
  if (rst_n == 1'b0) begin
    sgi_6 <= 1'b1;
  end
  else if (en == 1'b1) begin
    sgi_6 <= sgi_5;
  end
  else begin
    sgi_6 <= sgi_6;
  end
end

always @(posedge clk or negedge rst_n) begin
  if (rst_n == 1'b0) begin
    sgi_7 <= 1'b1;
  end
  else if (en == 1'b1) begin
    sgi_7 <= sgi_6;
  end
  else begin
    sgi_7 <= sgi_7;
  end
end

always @(sgi_1 or sgi_2 or sgi_3 or sgi_4 or sgi_5 or sgi_6 or sgi_7
         or nfs or sgi) begin
  case (nfs)
    3'b000: begin
      rsgo = sgi;
    end
    3'b001: begin
      rsgo = (sgi == sgi_1) ? sgi : rsgo ;
    end
    3'b010: begin
      rsgo = ((sgi & sgi_1 & sgi_2) || ~(sgi | sgi_1 | sgi_2) ) ? sgi : rsgo;
    end
    3'b011: begin
      rsgo = (   (sgi & sgi_1 & sgi_2 & sgi_3)
             || ~(sgi | sgi_1 | sgi_2 | sgi_3) ) ? sgi : rsgo;
    end
    3'b100: begin
      rsgo = (   (sgi & sgi_1 & sgi_2 & sgi_3 & sgi_4)
             || ~(sgi | sgi_1 | sgi_2 | sgi_3 | sgi_4) ) ? sgi : rsgo;
    end
    3'b101: begin
      rsgo = (   (sgi & sgi_1 & sgi_2 & sgi_3 & sgi_4 & sgi_5)
             || ~(sgi | sgi_1 | sgi_2 | sgi_3 | sgi_4 | sgi_5) ) ? sgi : rsgo;
    end
    3'b110: begin
      rsgo = (   (sgi&sgi_1&sgi_2&sgi_3&sgi_4&sgi_5&sgi_6)
             || ~(sgi|sgi_1|sgi_2|sgi_3|sgi_4|sgi_5|sgi_6) ) ? sgi : rsgo;
    end
    3'b111: begin
      rsgo = (   (sgi & sgi_1 & sgi_2 & sgi_3 & sgi_4 & sgi_5 & sgi_6 & sgi_7)
             || ~(sgi|sgi_1|sgi_2|sgi_3|sgi_4|sgi_5|sgi_6|sgi_7)) ? sgi : rsgo;
    end
  endcase
end

assign sgo = rsgo;

endmodule


module bbsy
(
  clk  ,
  rst_n,
  en   ,
  scli ,
  sdai ,
  sclo ,
  sdao ,
  ms   ,
  bbsy ,
  gbsy
);

parameter STRT = 2'h0;  // change when start signal
parameter STOP = 2'h1;  // change when stop signal
parameter SCLN = 2'h2;  // change when negative of scli
parameter SCLP = 2'h3;  // change when positive of scli

input  clk  ;
input  rst_n;
input  en   ;
input  scli ;
input  sdai ;
input  sclo ;
input  sdao ;
input  ms   ;
output bbsy ;  // indicate that bus is busy
output gbsy ;  // indicate that this master (GIIC) is busy

reg   rstop  ;
reg   rstart ;
reg   rstopo ;
reg   rstarto;
reg   rbbsy  ;
reg   rgbsy  ;

////////////////////////////////////////////////////////////////////////////////
// ===== detect sda posedge and negedge ===== //
wire sdao_pos;
wire sdao_neg;
reg  sdao_dl ;
always @(posedge clk or negedge rst_n) begin
  if (rst_n == 1'b0) begin
    sdao_dl <= 1'b0;
  end
  else if (en == 1'b1) begin
    sdao_dl <= sdao;
  end
  else begin
    sdao_dl <= sdao_dl;
  end
end
assign sdao_pos = (~sdao_dl) && sdao;
assign sdao_neg = sdao_dl && (~sdao);
////////////////////////////////////////////////////////////////////////////////

////////////////////////////////////////////////////////////////////////////////
// ===== detect start, stop signal ===== //
always @(posedge clk or negedge rst_n) begin
  if (rst_n == 1'b0) begin
    rstopo  <= 1'b0;
    rstarto <= 1'b0;
  end
  else if ( (en == 1'b1) && (sclo == 1'b1) ) begin
    if (sdao_pos == 1'b1) begin
      rstopo  <= 1'b1;
      rstarto <= 1'b0;
    end
    else begin
      rstopo <= 1'b0;
      if (sdao_neg == 1'b1) begin
        rstarto <= 1'b1;
      end
      else begin
        rstarto <= 1'b0;
      end
    end
  end
  else begin
    rstopo  <= rstopo ;
    rstarto <= rstarto;
  end
end
////////////////////////////////////////////////////////////////////////////////

////////////////////////////////////////////////////////////////////////////////
// ===== detect bus busy ===== //
always @(posedge clk or negedge rst_n) begin
  if (rst_n == 1'b0) begin
    rgbsy <= 1'b0;
  end
  else if (en == 1'b1) begin
    if (rstarto == 1'b1) begin
      rgbsy <= 1'b1;
    end
    else if (rstopo == 1'b1) begin
      rgbsy <= 1'b0;
    end
    else begin
      rgbsy <= rgbsy;
    end
  end
  else begin
    rgbsy <= rgbsy;
  end
end
////////////////////////////////////////////////////////////////////////////////

////////////////////////////////////////////////////////////////////////////////
// ===== detect scl posedge and negedge ===== //
wire scli_pos;
wire scli_neg;
reg  scli_dl ;
always @(posedge clk or negedge rst_n) begin
  if (rst_n == 1'b0) begin
    scli_dl <= 1'b0;
  end
  else if (en == 1'b1) begin
    scli_dl <= scli;
  end
  else begin
    scli_dl <= scli_dl;
  end
end
assign scli_pos = (~scli_dl) && scli;
assign scli_neg = scli_dl && (~scli);
////////////////////////////////////////////////////////////////////////////////

////////////////////////////////////////////////////////////////////////////////
// ===== detect sda posedge and negedge ===== //
wire sdai_pos;
wire sdai_neg;
reg  sdai_dl ;
always @(posedge clk or negedge rst_n) begin
  if (rst_n == 1'b0) begin
    sdai_dl <= 1'b0;
  end
  else if (en == 1'b1) begin
    sdai_dl <= sdai;
  end
  else begin
    sdai_dl <= sdai_dl;
  end
end
assign sdai_pos = (~sdai_dl) && sdai;
assign sdai_neg = sdai_dl && (~sdai);
////////////////////////////////////////////////////////////////////////////////

////////////////////////////////////////////////////////////////////////////////
// ===== detect start, stop signal ===== //
always @(posedge clk or negedge rst_n) begin
  if (rst_n == 1'b0) begin
    rstop  <= 1'b0;
    rstart <= 1'b0;
  end
  else if ( (en == 1'b1) && (scli == 1'b1) ) begin
    if (sdai_pos == 1'b1) begin
      rstop  <= 1'b1;
      rstart <= 1'b0;
    end
    else begin
      rstop <= 1'b0;
      if (sdai_neg == 1'b1) begin
        rstart <= 1'b1;
      end
      else begin
        rstart <= 1'b0;
      end
    end
  end
  else begin
    rstop  <= rstop ;
    rstart <= rstart;
  end
end
////////////////////////////////////////////////////////////////////////////////

////////////////////////////////////////////////////////////////////////////////
// ===== detect bus busy ===== //
always @(posedge clk or negedge rst_n) begin
  if (rst_n == 1'b0) begin
    rbbsy <= 1'b0;
  end
  else if (en == 1'b1) begin
    if (rstart == 1'b1) begin
      rbbsy <= 1'b1;
    end
    else if (rstop == 1'b1) begin
      rbbsy <= 1'b0;
    end
    else begin
      rbbsy <= rbbsy;
    end
  end
  else begin
    rbbsy <= rbbsy;
  end
end
////////////////////////////////////////////////////////////////////////////////

assign gbsy = rgbsy;
assign bbsy = rbbsy;

endmodule


module arbitration
(
  clk  ,
  rst_n,
  en   ,
  bbsy ,
  gbsy ,
  sda  ,
  msdao,
  ssdao,
  ms   ,
  st   ,
  ino  ,
  malc ,
  salc ,
  male ,
  sale
);

input  clk  ;  // clock
input  rst_n;  // reset
input  en   ;  // enable arbitration
input  bbsy ;  // bus busy
input  gbsy ;  // master (this GIIC) busy
input  sda  ;  // bus data wire
input  msdao;  // GIIC master data output
input  ssdao;  // GIIC slave data output
input  ms   ;  // bit indicate master/slave
input  st   ;  // start signal
input  ino  ;  // bit indicate input/output
input  malc ;  // clear error MALE
input  salc ;  // clear error SALE
output male ;  // Master Arbitration Lost Error
output sale ;  // Slave Arbitration Lost Error

// 1. Master Arbitration Lost Error
// 1.1. start condition double issue error (start when bus is busy)
reg rmale_wisrr;
always @(posedge clk or negedge rst_n) begin
  if (rst_n == 1'b0) begin
    rmale_wisrr <= 1'b0;
  end
  else if (malc == 1'b1) begin
    rmale_wisrr <= 1'b0;
  end
  else if (en == 1'b1) begin
    rmale_wisrr <= (bbsy && (~gbsy) && (~rmale_wisrr)) ? st : rmale_wisrr;
  end
  else begin
    rmale_wisrr <= rmale_wisrr;
  end
end

// 1.2. sdao does not match the level on the sda line (start and transmit)
reg rmale_mmatchg;
always @(posedge clk or negedge rst_n) begin
  if (rst_n == 1'b0) begin
    rmale_mmatchg <= 1'b0;
  end
  else if (malc == 1'b1) begin
    rmale_mmatchg <= 1'b0;
  end
  else if (en == 1'b1) begin
    rmale_mmatchg <= (ms && gbsy && (~ino) && (~rmale_mmatchg)) ?
                                                (msdao != sda) : rmale_mmatchg;
  end
  else begin
    rmale_mmatchg <= rmale_mmatchg;
  end
end

// 2. Slave Arbitration Lost Error
reg rsale_smatchg;
always @(posedge clk or negedge rst_n) begin
  if (rst_n == 1'b0) begin
    rsale_smatchg <= 1'b0;
  end
  else if (salc == 1'b1) begin
    rsale_smatchg <= 1'b0;
  end
  else if (en == 1'b1) begin
    rsale_smatchg <= ((~ms) && (~ino) && (~rsale_smatchg)) ?
                                                (ssdao != sda) : rsale_smatchg;
  end
  else begin
    rsale_smatchg <= rsale_smatchg;
  end
end

assign male = rmale_wisrr || rmale_mmatchg;
assign sale = rsale_smatchg;

endmodule


module prescaler
(
  clk  ,
  rst_n,
  en   ,
  psc  ,
  dlk 
);

input        clk  ;
input        rst_n;
input        en   ;
input  [3:0] psc  ;
output       dlk  ;  // div clk

reg [15:0] rpsc; 
reg        rdlk;  // dlk = clk/(2^(psc+1)

wire [15:0] pow2;  // pow2 = 2^psc
assign pow2 = 16'h01 << (psc - 4'h1);

always @(posedge clk or negedge rst_n) begin
  if (rst_n == 0) begin
    rpsc <= 16'h0001;
    rdlk <= 1'b0  ;
  end
  else if ((en == 1'b1) && (psc != 4'h0) ) begin
    if (rpsc >= pow2) begin
      rpsc <= 16'h0001;
      rdlk <= ~rdlk;
    end
    else begin
      rpsc <= rpsc + 16'h0001;
      rdlk <= rdlk;
    end
  end
  else begin
    rpsc <= 16'h0001;
    rdlk <= 1'b0 ;
  end
end

assign dlk = rdlk;

endmodule


module bitrate
(
  clk  ,
  rst_n,
  en   ,
  dlk  ,
  cnth ,
  cntl ,
  brh  ,
  brl  ,
  limh ,
  liml
);

input        clk  ;
input        rst_n;
input        en   ;
input        dlk  ;
input        cnth ;
input        cntl ;
input  [7:0] brh  ;
input  [7:0] brl  ;
output       limh ;
output       liml ;

reg    [7:0] rbrh ;
reg    [7:0] rbrl ;
reg          rlimh;
reg          rliml;

reg  rdlk_dl;  // rdlk_dl = DFF(rdlk)
wire rdlk_pos;  // detect posedge rdlk
always @(posedge clk or negedge rst_n) begin
  if (rst_n == 1'b0) begin
    rdlk_dl <= 1'b0;
  end
  else if (en == 1'b1) begin
    rdlk_dl <= dlk;
  end
  else begin
    rdlk_dl <= 1'b0;
  end
end
assign rdlk_pos = (~rdlk_dl) && dlk;

always @(posedge clk or negedge rst_n) begin
  if (rst_n == 1'b0) begin
    rbrh  <= 8'h01;
    rlimh <= 1'b0;
  end
  else if ( (en == 1'b1) && (cnth == 1'b1) && (rdlk_pos == 1'b1) ) begin
    if (rbrh >= brh) begin
      rbrh <= 8'h01;
      rlimh <= 1'b1;
    end
    else begin
      rbrh <= rbrh + 8'h01;
      rlimh <= 1'b0;
    end
  end
  else begin
    rbrh  <= rbrh;
    rlimh <= 1'b0;
  end
end

always @(posedge clk or negedge rst_n) begin
  if (rst_n == 1'b0) begin
    rbrl  <= 8'h01;
    rliml <= 1'b0;
  end
  else if ( (en == 1'b1) && (cntl == 1'b1) && (rdlk_pos == 1'b1) ) begin
    if (rbrl >= brl) begin
      rbrl <= 8'h01;
      rliml <= 1'b1;
    end
    else begin
      rbrl <= rbrl + 8'h01;
      rliml <= 1'b0;
    end
  end
  else begin
    rbrl  <= rbrl;
    rliml <= 1'b0;
  end
end

assign limh = rlimh;
assign liml = rliml;

endmodule


module timeout
(
  clk  ,
  rst_n,
  en   ,
  scli ,
  dlk  ,
  tmo  ,
  tmoc ,
  limt ,
  cnt
);

parameter IDLE = 2'h0;  // change when start signal
parameter SCLN = 2'h1;  // change when negative of scli
parameter SCLP = 2'h2;  // change when positive of scli
parameter OTHR = 2'h3;  // other state

input         clk  ;
input         rst_n;
input         en   ;
input         scli ;
input         dlk  ;
input  [15:0] tmo  ;
input         tmoc ;
output        limt ;
output [15:0] cnt  ;

reg           rlimt;
reg    [15:0] rcnt ;

reg    [ 1:0] state;
reg    [ 1:0] nxt_state;
reg           rcount;

////////////////////////////////////////////////////////////////////////////////
// ===== detect scl posedge and negedge ===== //
wire scli_pos;
wire scli_neg;
reg  scli_dl ;
always @(posedge clk or negedge rst_n) begin
  if (rst_n == 1'b0) begin
    scli_dl <= 1'b0;
  end
  else if (en == 1'b1) begin
    scli_dl <= scli;
  end
  else begin
    scli_dl <= scli_dl;
  end
end
assign scli_pos = (~scli_dl) && scli;
assign scli_neg = scli_dl && (~scli);
////////////////////////////////////////////////////////////////////////////////

////////////////////////////////////////////////////////////////////////////////
// ===== detect posedge rdlk ===== //
reg  rdlk_dl ;  // rdlk_dl = DFF(rdlk)
wire dlk_pos;  // detect posedge rdlk
always @(posedge clk or negedge rst_n) begin
  if (rst_n == 1'b0) begin
    rdlk_dl <= 1'b0;
  end
  else if (en == 1'b1) begin
    rdlk_dl <= dlk;
  end
  else begin
    rdlk_dl <= rdlk_dl;
  end
end
assign dlk_pos = (~rdlk_dl) && dlk;
////////////////////////////////////////////////////////////////////////////////

////////////////////////////////////////////////////////////////////////////////
// ===== output limit of counter -> time out ===== //
always @(posedge clk or negedge rst_n) begin
  if (rst_n == 1'b0) begin
    rlimt <= 1'b0;
  end
  else if (en == 1'b1) begin
    if (tmoc == 1'b1) begin
      rlimt <= 1'b0;
    end
    else begin
      rlimt <= (rcnt == tmo) ? 1'b1 : rlimt;
    end
  end
  else begin
    rlimt <= 1'b0;
  end
end
////////////////////////////////////////////////////////////////////////////////

////////////////////////////////////////////////////////////////////////////////
// ===== counter with time input ===== //
always @(posedge clk or negedge rst_n) begin
  if (rst_n == 1'b0) begin
    rcnt <= 16'h0000;
  end
  else if ( (en == 1'b1) && (rcount == 1'b1) && (rlimt == 1'b0) ) begin
    rcnt <= (dlk_pos == 1'b1) ? rcnt + 16'h0001 : rcnt;
  end
  else begin
    rcnt <= 16'h0000;
  end
end
////////////////////////////////////////////////////////////////////////////////

////////////////////////////////////////////////////////////////////////////////
// ===== FSM time out ===== //
always @(posedge clk or negedge rst_n) begin
  if (rst_n == 1'b0) begin
    state <= IDLE;
  end
  else if (en == 1'b1) begin
    state <= nxt_state;
  end
end


always @(state or scli_pos or scli_neg) begin
  case (state)
    IDLE: begin
      if (scli_pos == 1'b1) begin
        nxt_state = SCLP;
        rcount = 1'b0;
      end
      else if (scli_neg == 1'b1) begin
        nxt_state = SCLN;
        rcount = 1'b0;
      end
      else begin
        nxt_state = IDLE;
        rcount = 1'b0;
      end
    end
    SCLP: begin
      if (scli_neg == 1'b1) begin
        nxt_state = SCLN;
        rcount = 1'b0;
      end
      else begin
        nxt_state = SCLP;
        rcount = 1'b1;
      end
    end
    SCLN: begin
      if (scli_pos == 1'b1) begin
        nxt_state = SCLP;
        rcount = 1'b0;
      end
      else begin
        nxt_state = SCLN;
        rcount = 1'b1;
      end
    end
    OTHR: begin
      nxt_state = IDLE;
      rcount = 1'b0;
    end
  endcase
end
////////////////////////////////////////////////////////////////////////////////

assign limt = rlimt;
assign cnt  = rcnt ;

endmodule


module mstart
(
  clk    ,
  rst_n  ,
  en     ,
  st     ,
  dlk    ,
  brh    ,
  cur_scl,  // current scl signal
  cur_sda,  // current sda signal
  sclo   ,
  sdao   ,
  bsy    ,
  dne
);

parameter IDLE  = 3'd0;
parameter STRT1 = 3'd1;
parameter STRT2 = 3'd2;
parameter STRT3 = 3'd3;
parameter STRT4 = 3'd4;
parameter STRT5 = 3'd5;

input         clk    ;
input         rst_n  ;
input         en     ;
input         st     ;
input         dlk    ;
input  [ 7:0] brh    ;
input         cur_scl;
input         cur_sda;
output        sclo   ;
output        sdao   ;
output        bsy    ;
output        dne    ;

reg           rsclo  ;
reg           rsdao  ;
reg           rbsy   ;
reg           rdne   ;

////////////////////////////////////////////////////////////////////////////////
// ===== detect dck posedge ===== //
wire dlk_pos;
reg  dlk_dl ;
always @(posedge clk or negedge rst_n) begin
  if (rst_n == 1'b0) begin
    dlk_dl <= 1'b0;
  end
  else if (en == 1'b1) begin
    dlk_dl <= dlk;
  end
  else begin
    dlk_dl <= dlk_dl;
  end
end
assign dlk_pos = (~dlk_dl) && dlk;
////////////////////////////////////////////////////////////////////////////////

////////////////////////////////////////////////////////////////////////////////
// ===== start time ===== //
reg [ 7:0] stime;
reg        stlim;  // indicate stime = brh
always @(posedge clk or negedge rst_n) begin
  if (rst_n == 1'b0) begin
    stime <= 8'h00;
    stlim <= 1'b0;
  end
  else if ((en == 1'b1) && (dlk_pos == 1'b1) ) begin
    if (stime >= brh) begin
      stime <= 8'h00;
      stlim <= 1'b1;
    end
    else begin
      stime <= stime + 8'h01;
      stlim <= 1'b0;
    end
  end
  else begin
    stime <= stime;
    stlim <= 1'b0;
  end
end
////////////////////////////////////////////////////////////////////////////////

// scl: #00110
// sda: ##1100
reg [2:0] state;
always @(posedge clk or negedge rst_n) begin
  if (rst_n == 1'b0) begin
    state <= IDLE;
    rsclo <= cur_scl;
    rsdao <= cur_sda;
    rbsy  <= 1'b0;
    rdne  <= 1'b0;
  end
  else if (en == 1'b1) begin
    case (state)
      IDLE : begin
        if (st == 1'b1) begin
          state <= STRT1;
        end
        else begin
          state <= IDLE ;
        end
        rsclo <= cur_scl;
        rsdao <= cur_sda;
        rbsy  <= 1'b0;
        rdne  <= 1'b0;
      end
      STRT1: begin
        if (stlim == 1'b1) begin
          state <= STRT2;
        end
        else begin
          state <= STRT1;
        end
        rsclo <= 1'b0;
        rsdao <= rsdao;
        rbsy  <= 1'b1;
        rdne  <= 1'b0;
      end
      STRT2: begin
        if (stlim == 1'b1) begin
          state <= STRT3;
        end
        else begin
          state <= STRT2;
        end
        rsclo <= 1'b0;
        rsdao <= 1'b1;
        rbsy  <= 1'b1;
        rdne  <= 1'b0;
      end
      STRT3: begin
        if (stlim == 1'b1) begin
          state <= STRT4;
        end
        else begin
          state <= STRT3;
        end
        rsclo <= 1'b1;
        rsdao <= 1'b1;
        rbsy  <= 1'b1;
        rdne  <= 1'b0;
      end
      STRT4: begin
        if (stlim == 1'b1) begin
          state <= STRT5;
        end
        else begin
          state <= STRT4;
        end
        rsclo <= 1'b1;
        rsdao <= 1'b0;
        rbsy  <= 1'b1;
        rdne  <= 1'b0;
      end
      STRT5: begin
        if (stlim == 1'b1) begin
          state <= IDLE;
          rdne      <= 1'b1;
        end
        else begin
          state <= STRT5;
          rdne      <= 1'b0;
        end
        rsclo <= 1'b0;
        rsdao <= 1'b0;
        rbsy  <= 1'b1;
      end
    endcase
  end
  else begin
    state <= IDLE;
    rsclo <= cur_scl;
    rsdao <= cur_sda;
    rbsy  <= 1'b0;
    rdne  <= 1'b0;
  end
end

assign sclo = rsclo;
assign sdao = rsdao;
assign bsy  = rbsy ;
assign dne  = rdne ;

endmodule


module mstop
(
  clk    ,
  rst_n  ,
  en     ,
  sp     ,
  dlk    ,
  brh    ,
  cur_scl,
  cur_sda,
  sclo   ,
  sdao   ,
  bsy    ,
  dne
);

parameter IDLE  = 3'd0;
parameter STOP1 = 3'd1;
parameter STOP2 = 3'd2;
parameter STOP3 = 3'd3;
parameter STOP4 = 3'd4;
parameter STOP5 = 3'd5;
parameter STOP6 = 3'd6;

input         clk    ;
input         rst_n  ;
input         en     ;
input         sp     ;
input         dlk    ;
input  [ 7:0] brh    ;
input         cur_scl;
input         cur_sda;
output        sclo   ;
output        sdao   ;
output        bsy    ;
output        dne    ;

reg           rsclo  ;
reg           rsdao  ;
reg           rbsy   ;
reg           rdne   ;

////////////////////////////////////////////////////////////////////////////////
// ===== detect dck posedge ===== //
wire dlk_pos;
reg  dlk_dl ;
always @(posedge clk or negedge rst_n) begin
  if (rst_n == 1'b0) begin
    dlk_dl <= 1'b0;
  end
  else if (en == 1'b1) begin
    dlk_dl <= dlk;
  end
  else begin
    dlk_dl <= dlk_dl;
  end
end
assign dlk_pos = (~dlk_dl) && dlk;
////////////////////////////////////////////////////////////////////////////////

////////////////////////////////////////////////////////////////////////////////
// ===== stop time ===== //
reg [ 7:0] stome;  // counter stop time
reg        stlom;  // indicate stome = brh
always @(posedge clk or negedge rst_n) begin
  if (rst_n == 1'b0) begin
    stome <= 8'h00;
    stlom <= 1'b0;
  end
  else if ((en == 1'b1) && (dlk_pos == 1'b1) ) begin
    if (stome >= brh) begin
      stome <= 8'h00;
      stlom <= 1'b1;
    end
    else begin
      stome <= stome + 8'h01;
      stlom <= 1'b0;
    end
  end
  else begin
    stome <= stome;
    stlom <= 1'b0;
  end
end
////////////////////////////////////////////////////////////////////////////////

// scl: #00111
// sda: ##0011
reg [2:0] state    ;
always @(posedge clk or negedge rst_n) begin
  if (rst_n == 1'b0) begin
    state <= IDLE;
    rsclo <= rsclo;
    rsdao <= rsdao;
    rbsy  <= 1'b0 ;
    rdne  <= 1'b0 ;
  end
  else if (en == 1'b1) begin
    case (state)
      IDLE : begin
        if (sp == 1'b1) begin
          state <= STOP1;
        end
        else begin
          state <= IDLE ;
        end
        rsclo <= cur_scl;
        rsdao <= cur_sda;
        rbsy  <= 1'b0;
        rdne  <= 1'b0;
      end
      STOP1: begin
        if (stlom == 1'b1) begin
          state <= STOP2;
        end
        else begin
          state <= STOP1;
        end
        rsclo <= 1'b0;
        rsdao <= rsdao;
        rbsy  <= 1'b1;
        rdne  <= 1'b0;
      end
      STOP2: begin
        if (stlom == 1'b1) begin
          state <= STOP3;
        end
        else begin
          state <= STOP2;
        end
        rsclo <= 1'b0;
        rsdao <= 1'b0;
        rbsy  <= 1'b1;
        rdne  <= 1'b0;
      end
      STOP3: begin
        if (stlom == 1'b1) begin
          state <= STOP4;
        end
        else begin
          state <= STOP3;
        end
        rsclo <= 1'b1;
        rsdao <= 1'b0;
        rbsy  <= 1'b1;
        rdne  <= 1'b0;
      end
      STOP4: begin
        if (stlom == 1'b1) begin
          state <= STOP5;
        end
        else begin
          state <= STOP4;
        end
        rsclo <= 1'b1;
        rsdao <= 1'b1;
        rbsy  <= 1'b1;
        rdne  <= 1'b0;
      end
      STOP5: begin
        if (stlom == 1'b1) begin
          state <= STOP6;
        end
        else begin
          state <= STOP5;
        end
        rsclo <= 1'b1;
        rsdao <= 1'b1;
        rbsy  <= 1'b1;
        rdne  <= 1'b0;
      end
      STOP6: begin
        if (stlom == 1'b1) begin
          state <= IDLE;
        end
        else begin
          state <= STOP6;
        end
        rsclo <= 1'b1;
        rsdao <= 1'b1;
        rbsy  <= 1'b1;
        rdne  <= 1'b1;
      end
      default: begin
        state <= IDLE;
        rsclo <= rsclo;
        rsdao <= rsdao;
        rbsy  <= rbsy ;
        rdne  <= rdne ;
      end
    endcase
  end
  else begin
    state <= IDLE;
    rsclo <= rsclo;
    rsdao <= rsdao;
    rbsy  <= 1'b0 ;
    rdne  <= 1'b0 ;
  end
end


assign sclo = rsclo;
assign sdao = rsdao;
assign bsy  = rbsy ;
assign dne  = rdne ;

endmodule


module fifo
(
  clk  ,
  rst_n,
  en   ,
  rd   ,
  clrq ,
  clre ,
  tkey ,
  data ,
  tdat ,
  err  ,
  ffcr
);

parameter QUEUE_WIDTH  = 15;

input         clk  ;
input         rst_n;
input         en   ;
input         rd   ;
input         clrq ;  // clear queue
input  [ 2:0] clre ;  // clear error after master in IDLE state to avoid rick
input  [23:0] tkey ;
input  [ 7:0] data ;
output [ 7:0] tdat ;
output [ 2:0] err  ;
output [ 3:0] ffcr ;

reg    [ 7:0] rtdat ;
reg           rfull ;
reg           rempty;

reg    [ 7:0] i     ;
reg    [ 7:0] pnter ;

reg    [ 7:0] fifo[QUEUE_WIDTH:0];

wire          wrte;
assign wrte = (tkey == 24'hC0FFEE) ? 1'b1 : 1'b0;

////////////////////////////////////////////////////////////////////////////////
wire read ;
wire rd_neg;
reg  rd_dl;
always @(posedge clk or negedge rst_n) begin
  if (rst_n == 1'b0) begin
    rd_dl <= 1'b0;
  end
  else if (en == 1'b1) begin
    rd_dl <= rd;
  end
  else begin
    rd_dl <= rd_dl;
  end
end
assign read   = (~rd_dl) && rd;
assign rd_neg = rd_dl && (~rd);
////////////////////////////////////////////////////////////////////////////////

always @(posedge clk or negedge rst_n) begin
  if (rst_n == 1'b0) begin
    rfull  <= 1'b0 ;
    rempty <= 1'b0 ;
    pnter  <= 8'h00;
    fifo[QUEUE_WIDTH] <= 8'h00;
    for (i = 0; i < QUEUE_WIDTH; i = i + 1) begin
      fifo[i] <= 8'h00;
    end
  end
  else if (clrq == 1'b1) begin
    rfull  <= 1'b0 ;
    rempty <= 1'b0 ;
    pnter  <= 8'h00;
    fifo[QUEUE_WIDTH] <= 8'h00;
    for (i = 0; i < QUEUE_WIDTH; i = i + 1) begin
      fifo[i] <= 8'h00;
    end
  end
  else if (clre[2] != 1'b0) begin
    rfull  <= 1'b0  ;
    rempty <= rempty;
    pnter  <= pnter ;
    fifo[QUEUE_WIDTH] <= 8'h00;
    for (i = 0; i < QUEUE_WIDTH; i = i + 1) begin
      fifo[i] <= fifo[i];
    end
  end
  else if (en == 1'b1) begin
    rfull <= ((pnter == QUEUE_WIDTH) && ((tkey == 24'hC0FFEE))) ? 1'b1 : rfull;
    rempty <= ((pnter == 8'h00) && ((read == 1'b1))) ?
                                           1'b1 : (rempty && (pnter == 8'h00));
    fifo[QUEUE_WIDTH] <= 8'h00;
    case ({rd_neg,wrte})
      2'b00: begin
        pnter  <= pnter;
        for (i = 0; i < QUEUE_WIDTH; i = i + 1) begin
          fifo[i] <= fifo[i];
        end
      end
      2'b01: begin
        pnter  <= (pnter >= QUEUE_WIDTH) ? QUEUE_WIDTH : (pnter + 8'h01);
        for (i = 0; i < QUEUE_WIDTH; i = i + 1) begin
          if (i == pnter) begin
            fifo[i] <= (pnter >= QUEUE_WIDTH) ? fifo[i] : data;
          end
          else begin
            fifo[i] <= fifo[i];
          end
        end     
      end
      2'b10: begin
        pnter <= (pnter == 8'h00) ? 8'h00 : (pnter - 8'h01);
        for (i = 0; i < QUEUE_WIDTH; i = i + 1) begin
          fifo[i] <= fifo[i+1];
        end   
      end
      2'b11: begin
        pnter <= pnter;
        for (i = 0; i < QUEUE_WIDTH; i = i + 1) begin
          if (i == (pnter - 8'h01)) begin
            fifo[i] <= data;
          end
          else begin
            fifo[i] <= fifo[i+1];
          end
        end       
      end
    endcase
  end
  else begin
    rfull  <= rfull ;
    rempty <= rempty;
    pnter  <= pnter ;
    fifo[QUEUE_WIDTH] <= 8'h00;
    for (i = 0; i < QUEUE_WIDTH; i = i + 1) begin
      fifo[i] <= fifo[i];
    end
  end
end

always @(posedge clk or negedge rst_n) begin
  if (rst_n == 1'b0) begin
    rtdat  <= 8'h00;
  end
  else if (en == 1'b1) begin
    rtdat  <= (read == 1'b1) ? fifo[0] : rtdat;
  end
  else begin
    rtdat  <= rtdat ;
  end
end

assign tdat = rtdat;
assign err  = {rfull,rempty,1'b0};
assign ffcr = pnter;

endmodule


module lifo
(
  clk  ,
  rst_n,
  en   ,
  wr   ,
  addr ,
  clrq ,
  data ,
  rdat ,
  lfcr
);

parameter QUEUE_WIDTH = 15;

input         clk  ;
input         rst_n;
input         en   ;
input         wr   ;
input  [ 3:0] addr ;
input         clrq ;  // clear queue
input  [ 7:0] data ;
output [ 7:0] rdat ;
output [ 3:0] lfcr ;

reg    [ 7:0] rrdat;

reg    [ 7:0] i    ;
reg    [ 7:0] pnter;

reg    [ 7:0] lifo[QUEUE_WIDTH:0];

////////////////////////////////////////////////////////////////////////////////
wire write ;
reg  wr_dl ;
always @(posedge clk or negedge rst_n) begin
  if (rst_n == 1'b0) begin
    wr_dl <= 1'b0;
  end
  else if (en == 1'b1) begin
    wr_dl <= wr;
  end
  else begin
    wr_dl <= wr_dl;
  end
end
assign write  = (~wr_dl) && wr;
////////////////////////////////////////////////////////////////////////////////

// write data to lifo
always @(posedge clk or negedge rst_n) begin
  if (rst_n == 1'b0) begin
    pnter <= 8'h00;
    for (i = 0; i < QUEUE_WIDTH; i = i + 1) begin
      lifo[i] <= 8'h00;
    end
  end
  else if (clrq == 1'b1) begin
    pnter <= 8'h00;
    for (i = 0; i < QUEUE_WIDTH; i = i + 1) begin
      lifo[i] <= 8'h00;
    end
  end
  else if ((en == 1'b1) && (write == 1'b1) ) begin
    pnter   <= (pnter == QUEUE_WIDTH) ? pnter : (pnter + 8'h01);
    lifo[0] <= data;
    for (i = 1; i < QUEUE_WIDTH; i = i + 1) begin
      lifo[i] <= lifo[i - 1];
    end
  end
  else begin
    pnter <= pnter;
    for (i = 0; i < QUEUE_WIDTH; i = i + 1) begin
      lifo[i] <= lifo[i];
    end
  end
end

assign rdat = lifo[addr];
assign lfcr = pnter;

endmodule


module mtransmit
(
  clk  ,
  rst_n,
  en   ,
  tr   ,
  sdai ,
  tdat ,
  rmit ,
  ackc ,
  limh ,
  liml ,
  bsy  ,
  sclo ,
  sdao ,
  bc   ,
  acko ,
  shf  ,
  ino  ,  // input and output
  rmiti,
  acke ,
  cnth ,
  cntl ,
  dne
);

parameter IDLE  = 3'd0;
parameter TRAN1 = 3'd1;
parameter TRAN2 = 3'd2;
parameter TRAN3 = 3'd3;
parameter WACK1 = 3'd4;
parameter WACK2 = 3'd5;
parameter WACK3 = 3'd6;

input         clk  ;
input         rst_n;
input         en   ;
input         tr   ;
input         sdai ;
input  [ 7:0] tdat ;
input  [ 3:0] rmit ;
input         ackc ;
input         limh ;
input         liml ;

output        bsy  ;  // 0: free, 1: transfering
output        sclo ;
output        sdao ;
output [ 3:0] bc   ;
output        acko ;
output [ 7:0] shf  ;
output        ino  ;
output        rmiti;
output        acke ;  // 0: no error ack, 1: error ack
output        cnth ;
output        cntl ;
output        dne  ;

reg        rbsy  ;  // 0: free, 1: transfering
reg        rsclo ;
reg        rsdao ;
reg [ 3:0] rbc   ;
reg        racko ;
reg        rrmiti;
reg        racke ;
reg        rcnth ;
reg        rcntl ;
reg        rino  ;
reg        rdne  ;

reg [2:0] state;

reg [3:0] rrmit;
reg [7:0] rshf ;
reg [7:0] resf ;  // back  up rshf
always @(posedge clk or negedge rst_n) begin
  if (rst_n == 1'b0) begin
    state  <= IDLE ;
    rbsy   <= 1'b0 ;
    rsclo  <= 1'b0 ;
    rsdao  <= 1'b0 ;
    rrmit  <= rmit ;
    rshf   <= 8'h00;
    resf   <= 8'h00;
    rbc    <= 4'h0 ;
    racko  <= 1'b1 ;
    rrmiti <= 1'b0 ;
    racke  <= 1'b0 ;
    rcnth  <= 1'b0 ;
    rcntl  <= 1'b0 ;
    rdne   <= 1'b0 ;
    rino   <= 1'b0 ;
  end
  else if (en == 1'b1) begin
    case (state)
      IDLE : begin
        if ((tr == 1'b1) && (en == 1'b1)) begin
          state <= TRAN1;
        end
        else begin
          state <= IDLE ;
        end
        rbsy   <= 1'b0;
        rsclo  <= 1'b0;
        rsdao  <= 1'b0;
        rrmit  <= rmit;
        rshf   <= tdat;
        resf   <= tdat;
        rbc    <= 4'h0;
        racko  <= 1'b1;
        rrmiti <= 1'b0;
        racke  <= (ackc == 1'b1) ? 1'b0 : racke;
        rcnth  <= 1'b0;
        rcntl  <= 1'b0;
        rino   <= 1'b0;
        rdne   <= 1'b0;
      end
      TRAN1: begin
        if (liml == 1'b1) begin
          state <= TRAN2;
          rcntl <= 1'b0;
        end
        else begin
          state <= TRAN1;
          rcntl <= 1'b1;
        end
        rcnth  <= 1'b0   ;
        rbsy   <= 1'b1   ;
        rsclo  <= 1'b0   ;
        rsdao  <= rshf[7];
        rrmit  <= rrmit  ;
        rshf   <= rshf   ;
        resf   <= resf   ;
        rbc    <= rbc    ;
        racko  <= 1'b1   ;
        rrmiti <= 1'b0   ;
        racke  <= (ackc == 1'b1) ? 1'b0 : racke;
        rino   <= 1'b0   ;
        rdne   <= 1'b0   ;
      end
      TRAN2: begin
        if (limh == 1'b1) begin
          state <= TRAN3;
          rcnth <= 1'b0;
        end
        else begin
          state <= TRAN2;
          rcnth <= 1'b1;
        end
        rcntl  <= 1'b0 ;
        rbsy   <= 1'b1 ;
        rsclo  <= 1'b1 ;
        rsdao  <= rsdao;
        rrmit  <= rrmit;
        rshf   <= rshf ;
        resf   <= resf ;
        rbc    <= rbc  ;
        racko  <= 1'b1 ;
        rrmiti <= 1'b0 ;
        racke  <= (ackc == 1'b1) ? 1'b0 : racke;
        rino   <= 1'b0 ;
        rdne   <= 1'b0 ;
      end
      TRAN3: begin
        if (liml == 1'b1) begin
          if (rbc == 4'h7) begin
            state <= WACK1;
          end
          else begin
            state <= TRAN1;
          end
          rcntl <= 1'b0;
          rbc   <= rbc + 4'h1;
          rshf  <= rshf << 1'b1;
        end
        else begin
          state <= TRAN3;
          rcntl <= 1'b1;
          rbc   <= rbc;
          rshf  <= rshf;
        end
        rcnth  <= 1'b0 ;
        rbsy   <= 1'b1 ;
        rsclo  <= 1'b0 ;
        rsdao  <= rsdao;
        rrmit  <= rrmit;
        resf   <= resf ;
        racko  <= 1'b1 ;
        rrmiti <= 1'b0 ;
        racke  <= (ackc == 1'b1) ? 1'b0 : racke;
        rino   <= (rbc == 4'h7) ? 1'b1 : 1'b0;
        rdne   <= 1'b0 ;
      end
      WACK1: begin
        if (liml == 1'b1) begin
          state <= WACK2;
          rcntl     <= 1'b0 ;
        end
        else begin
          state <= WACK1;
          rcntl <= 1'b1;
        end
        rcnth  <= 1'b0 ;
        rbsy   <= 1'b1 ;
        rsclo  <= 1'b0 ;
        rsdao  <= rsdao;
        rrmit  <= rrmit;
        rshf   <= rshf ;
        resf   <= resf ;
        rbc    <= rbc  ;
        racko  <= 1'b1 ;
        rrmiti <= 1'b0 ;
        racke  <= (ackc == 1'b1) ? 1'b0 : racke;
        rino   <= 1'b1 ;
        rdne   <= 1'b0 ;
      end
      WACK2: begin
        if (limh == 1'b1) begin
          state <= WACK3;
          rcnth <= 1'b0;
          rdne  <= 1'b1;
        end
        else begin
          state <= WACK2;
          rcnth <= 1'b1;
          rdne  <= 1'b0;
        end
        rcntl  <= 1'b0 ;
        rbsy   <= 1'b1 ;
        rsclo  <= 1'b1 ;
        rsdao  <= rsdao;
        rrmit  <= rrmit;
        rshf   <= rshf ;
        resf   <= resf ;
        rbc    <= rbc  ;
        racko  <= sdai ;
        rrmiti <= 1'b0 ;
        rino   <= 1'b1 ;
        racke  <= (ackc == 1'b1) ? 1'b0 : racke;
      end
      WACK3: begin
        if (racko == 1'b0) begin  // transfer completed
          if (liml == 1'b1) begin
            state <= TRAN1;
            rcntl <= 1'b0;
            rbc   <= 4'h0;
            rbsy  <= 1'b1;
          end
          else begin
            state <= WACK3;
            rcntl <= 1'b1;
            rbc   <= rbc ;
            rbsy  <= 1'b1;
          end
          rdne   <= rdne;
          rrmit  <= rmit;
          rshf   <= tdat;
          resf   <= tdat;
          rrmiti <= 1'b0;
          racke  <= (ackc == 1'b1) ? 1'b0 : racke;
        end
        else begin  // nack
          if (rrmit == 4'h0) begin  // not re-transfer
            if (liml == 1'b1) begin
              state <= IDLE;
              rcntl <= 1'b0;
              rdne  <= 1'b1;
              rbc   <= 4'h0;
              rbsy  <= 1'b1;
              racke <= (ackc == 1'b1) ? 1'b0 : 1'b1;
            end
            else begin
              state <= WACK3;
              rcntl <= 1'b1;
              rdne  <= rdne;
              rbc   <= rbc ;
              rbsy  <= 1'b1;
              racke <= (ackc == 1'b1) ? 1'b0 : racke;
            end
            rrmit  <= rrmit;
            rshf   <= rshf;
            resf   <= resf;
            rrmiti <= 1'b0;
          end
          else begin  // re-transfer
            if (liml == 1'b1) begin
              state <= TRAN1;
              rcntl <= 1'b0;
              rdne  <= 1'b1;
              rbc   <= 4'h0;
              rbsy  <= 1'b1;
              rrmit <= rrmit - 4'h1;
            end
            else begin
              state <= WACK3;
              rcntl <= 1'b1;
              rdne  <= rdne;
              rbc   <= rbc ;
              rbsy  <= 1'b1;
              rrmit <= rrmit;
            end
            rshf   <= resf;
            resf   <= resf;
            rrmiti <= 1'b1;
            racke  <= (ackc == 1'b1) ? 1'b0 : racke;
          end
        end
        rcnth <= 1'b0 ;
        rsclo <= 1'b0 ;
        rsdao <= rsdao;
        racko <= racko;
        rino  <= 1'b1 ;
      end
    endcase 
  end
  else begin
    state  <= IDLE ;
    rbsy   <= 1'b0 ;
    rsclo  <= 1'b0 ;
    rsdao  <= 1'b0 ;
    rrmit  <= rmit ;
    rshf   <= 8'h00;
    resf   <= 8'h00;
    rbc    <= 4'h0 ;
    racko  <= 1'b1 ;
    rrmiti <= 1'b0 ;
    racke  <= (ackc == 1'b1) ? 1'b0 : racke;
    rcnth  <= 1'b0 ;
    rcntl  <= 1'b0 ;
    rdne   <= 1'b0 ;
    rino   <= 1'b0 ;
  end
end

assign bsy   = rbsy  ;
assign sclo  = rsclo ;
assign sdao  = rsdao ;
assign bc    = rbc   ;
assign acko  = racko ;
assign shf   = rshf  ;
assign ino   = rino  ;
assign rmiti = rrmiti;
assign acke  = racke ;
assign cnth  = rcnth ;
assign cntl  = rcntl ;
assign dne   = rdne  ;

endmodule


module mreceive
(
  clk  ,
  rst_n,
  en   ,
  re   ,
  acki ,
  sdai ,
  limh ,
  liml ,
  bsy  ,
  sclo ,
  sdao ,
  bc   ,
  rdat ,
  shf  ,
  ino  ,
  cnth ,
  cntl ,
  lfwr ,
  dne
);

parameter IDLE  = 3'd0;
parameter RECV1 = 3'd1;
parameter RECV2 = 3'd2;
parameter RECV3 = 3'd3;
parameter XACK1 = 3'd4;
parameter XACK2 = 3'd5;
parameter XACK3 = 3'd6;

input         clk  ;
input         rst_n;
input         en   ;
input         re   ;
input         acki ;
input         sdai ;
input         limh ;
input         liml ;

output        bsy  ;  // 0: free, 1: transfering
output        sclo ;
output        sdao ;
output [ 3:0] bc   ;
output [ 7:0] rdat ;
output [ 7:0] shf  ;
output        ino  ;
output        cnth ;
output        cntl ;
output        lfwr ;
output        dne  ;

reg        rbsy ;  // 0: free, 1: transfering
reg        rsclo;
reg        rsdao;
reg [ 3:0] rbc  ;
reg [ 7:0] rrdat;
reg        rcnth;
reg        rcntl;
reg        rino ;
reg        rlfwr;
reg        rdne ;

reg [2:0] state;
reg [7:0] rshf;
always @(posedge clk or negedge rst_n) begin
  if (rst_n == 1'b0) begin
    state <= IDLE ;
    rbsy  <= 1'b0;
    rsclo <= 1'b0;
    rsdao <= 1'b1;
    rshf  <= 8'h00;
    rbc   <= 4'h0;
    rrdat <= 8'h00;
    rcnth <= 1'b0;
    rcntl <= 1'b0;
    rino  <= 1'b1;
    rlfwr <= 1'b0;
    rdne  <= 1'b0;
  end
  else if (en == 1'b1) begin
    case (state)
      IDLE : begin
        if (re == 1'b1) begin
          state <= RECV1;
        end
        else begin
          state <= IDLE ;
        end
        rbsy  <= 1'b0;
        rsclo <= 1'b0;
        rsdao <= 1'b1;
        rshf  <= rshf;
        rbc   <= 4'h0;
        rrdat <= rrdat;
        rcnth <= 1'b0;
        rcntl <= 1'b0;
        rino  <= 1'b1;
        rlfwr <= 1'b0;
        rdne  <= 1'b0;
      end
      RECV1: begin
        if (liml == 1'b1) begin
          state <= RECV2;
          rcntl <= 1'b0;
        end
        else begin
          state <= RECV1;
          rcntl <= 1'b1;
        end
        rcnth <= 1'b0;
        rbsy  <= 1'b1;
        rsclo <= 1'b0;
        rsdao <= 1'b1;
        rshf  <= rshf;
        rbc   <= rbc;
        rrdat <= rrdat;
        rino  <= 1'b1;
        rlfwr <= 1'b0;
        rdne  <= 1'b0;
      end
      RECV2: begin
        if (limh == 1'b1) begin
          state <= RECV3;
          rcnth <= 1'b0;
        end
        else begin
          state <= RECV2;
          rcnth <= 1'b1;
        end
        rshf  <= (rsclo == 1'b0) ? {rshf[6:0],sdai} : rshf;
        rbc   <= (rsclo == 1'b0) ? rbc + 4'h1 : rbc;
        rcntl <= 1'b0;
        rbsy  <= 1'b1;
        rsclo <= 1'b1;
        rsdao <= 1'b1;
        rrdat <= rrdat;
        rino  <= 1'b1;
        rlfwr <= 1'b0;
        rdne  <= 1'b0;
      end
      RECV3: begin
        if (liml == 1'b1) begin
          rcntl <= 1'b0;
          if (rbc == 4'h8) begin
            state <= XACK1;
          end
          else begin
            state <= RECV1;
          end
        end
        else begin
          state <= RECV3;
          rcntl <= 1'b1;
        end
        rino  <= 1'b1;
        rbc   <= rbc;
        rcnth <= 1'b0;
        rbsy  <= 1'b1;
        rsclo <= 1'b0;
        rsdao <= 1'b1;
        rshf  <= rshf;
        rrdat <= rrdat;
        rlfwr <= 1'b0;
        rdne  <= 1'b0 ;
      end
      XACK1: begin
        if (liml == 1'b1) begin
          state <= XACK2;
          rcntl <= 1'b0;
        end
        else begin
          state <= XACK1;
          rcntl <= 1'b1;
        end
        rcnth <= 1'b0;
        rbsy  <= 1'b1;
        rsclo <= 1'b0;
        rsdao <= acki;
        rshf  <= rshf;
        rbc   <= rbc;
        rrdat <= rrdat;
        rino  <= 1'b0;
        rlfwr <= 1'b0;
        rdne  <= 1'b0;
      end
      XACK2: begin
        if (limh == 1'b1) begin
          state <= XACK3;
          rcnth <= 1'b0;
          rlfwr <= 1'b1;
        end
        else begin
          state <= XACK2;
          rcnth <= 1'b1;
          rlfwr <= 1'b0;
        end
        rcntl <= 1'b0;
        rbsy  <= 1'b1;
        rsclo <= 1'b1;
        rsdao <= rsdao;
        rshf  <= rshf;
        rbc   <= rbc;
        rrdat <= rshf;
        rino  <= 1'b0;
        rdne  <= 1'b0;
      end
      XACK3: begin
        if (liml == 1'b1) begin
          rcntl <= 1'b0;
          if (acki == 1'b0) begin
            state <= RECV1;
          end
          else if (acki == 1'b1) begin
            state <= IDLE;
          end
          else begin
            state <= XACK3;
          end
        end
        else begin
          state <= XACK3;
          rcntl <= 1'b1;
        end
        rcnth <= 1'b0;
        rrdat <= rrdat;
        rbsy  <= 1'b1;
        rsclo <= 1'b0;
        rsdao <= rsdao;
        rshf  <= rshf;
        rbc   <= 4'h0;
        rino  <= 1'b0;
        rlfwr <= rlfwr;
        rdne  <= 1'b1;
      end
    endcase   
  end
  else begin
    state <= IDLE ;
    rbsy  <= 1'b0;
    rsclo <= 1'b0;
    rsdao <= 1'b1;
    rshf  <= 8'h00;
    rbc   <= 4'h0;
    rrdat <= rrdat;
    rcnth <= 1'b0;
    rcntl <= 1'b0;
    rino  <= 1'b1;
    rlfwr <= rlfwr;
    rdne  <= 1'b0;
  end
end

assign bsy  = rbsy ;
assign sclo = rsclo;
assign sdao = rsdao;
assign bc   = rbc  ;
assign rdat = rrdat;
assign shf  = rshf ;
assign ino  = rino ;
assign cnth = rcnth;
assign cntl = rcntl;
assign lfwr = rlfwr;
assign dne  = rdne ;

endmodule


module sdetect
(
  clk  ,
  rst_n,
  en   ,
  scli ,
  sdai ,
  stop ,
  start,
  reset
);

parameter STRT = 2'h0;  // change when start signal
parameter STOP = 2'h1;  // change when stop signal
parameter SCLN = 2'h2;  // change when negative of scli
parameter SCLP = 2'h3;  // change when positive of scli

input  clk  ;
input  rst_n;
input  en   ;
input  scli ;
input  sdai ;
output stop ;
output start;
output reset;

reg   rstop ;
reg   rstart;
reg   rreset;

////////////////////////////////////////////////////////////////////////////////
// ===== detect scl posedge and negedge ===== //
reg scli_01;
reg scli_02;
reg scli_03;
reg scli_04;
reg scli_05;
reg scli_06;
reg scli_07;
reg scli_08;
reg scli_09;
reg scli_10;

reg sdai_01;
reg sdai_02;
reg sdai_03;
reg sdai_04;
reg sdai_05;
reg sdai_06;
reg sdai_07;
reg sdai_08;
reg sdai_09;
reg sdai_10;

// D-FF to avoid the noise
always @(posedge clk or negedge rst_n) begin
  if (rst_n == 1'b0) begin
    scli_01 <= 1'b0;
    scli_02 <= 1'b0;
    scli_03 <= 1'b0;
    scli_04 <= 1'b0;
    scli_05 <= 1'b0;
    scli_06 <= 1'b0;
    scli_07 <= 1'b0;
    scli_08 <= 1'b0;
    scli_09 <= 1'b0;
    scli_10 <= 1'b0;   
  end
  else begin
    scli_01 <= scli;
    scli_02 <= scli_01;
    scli_03 <= scli_02;
    scli_04 <= scli_03;
    scli_05 <= scli_04;
    scli_06 <= scli_05;
    scli_07 <= scli_06;
    scli_08 <= scli_07;
    scli_09 <= scli_08;
    scli_10 <= scli_09;
  end
end

// D-FF to avoid the noise
always @(posedge clk or negedge rst_n) begin
  if (rst_n == 1'b0) begin
    sdai_01 <= 1'b0;
    sdai_02 <= 1'b0;
    sdai_03 <= 1'b0;
    sdai_04 <= 1'b0;
    sdai_05 <= 1'b0;
    sdai_06 <= 1'b0;
    sdai_07 <= 1'b0;
    sdai_08 <= 1'b0;
    sdai_09 <= 1'b0;
    sdai_10 <= 1'b0;   
  end
  else begin
    sdai_01 <= sdai;
    sdai_02 <= sdai_01;
    sdai_03 <= sdai_02;
    sdai_04 <= sdai_03;
    sdai_05 <= sdai_04;
    sdai_06 <= sdai_05;
    sdai_07 <= sdai_06;
    sdai_08 <= sdai_07;
    sdai_09 <= sdai_08;
    sdai_10 <= sdai_09;
  end
end

reg  scli_dl ;
wire scli_pos;
wire scli_neg;
always @(posedge clk or negedge rst_n) begin
  if (rst_n == 1'b0) begin
    scli_dl <= 1'b0;
  end
  else if (en == 1'b1) begin
    scli_dl <= scli_10; 
  end
  else begin
    scli_dl <= scli_dl;
   end
end
assign scli_pos = (~scli_dl) && scli_10;
assign scli_neg = scli_dl && (~scli_10);
////////////////////////////////////////////////////////////////////////////////

////////////////////////////////////////////////////////////////////////////////
// ===== detect sda posedge and negedge ===== //
wire sdai_pos;
wire sdai_neg;
reg  sdai_dl ;
always @(posedge clk or negedge rst_n) begin
  if (rst_n == 1'b0) begin
    sdai_dl <= 1'b0;
  end
  else if (en == 1'b1) begin
    sdai_dl <= sdai_10;
  end
  else begin
    sdai_dl <= sdai_dl;
  end
end
assign sdai_pos = (~sdai_dl) && sdai_10;
assign sdai_neg = sdai_dl && (~sdai_10);
////////////////////////////////////////////////////////////////////////////////

////////////////////////////////////////////////////////////////////////////////
// ===== detect reset signal ===== //
reg [1:0] state;
reg [1:0] nxt_state;
always @(posedge clk or negedge rst_n) begin
  if (rst_n == 1'b0) begin
    state <= 2'b00;
  end
  else if (en == 1'b1) begin
    state <= nxt_state;
  end
end

always @(state or scli or sdai or rstart or rstop) begin
  case (state)
    STRT: begin
      if (rstart == 1'b1) begin
        nxt_state = STOP;
        rreset = 1'b0;
      end
      else begin
        nxt_state = STRT;
        rreset = 1'b0;
      end
    end
    STOP: begin
      if (scli == 1'b1) begin
        if (rstop == 1'b1) begin
          nxt_state = STRT;
          rreset = 1'b1;
        end
        else begin
          nxt_state = STOP;
          rreset = 1'b0;
        end
      end
      else if (scli == 1'b0) begin
        nxt_state = SCLN;
        rreset = 1'b0;
      end
      else begin
        nxt_state = STRT;
        rreset = 1'b0;       
      end
    end
    SCLN: begin  // scli = 0
      if (scli == 1'b1) begin
        nxt_state = SCLP;
        rreset = 1'b0;         
      end
      else begin
        nxt_state = SCLN;
        rreset = 1'b0;       
      end
    end
    SCLP: begin  // scli = 1
      if (scli == 1'b1) begin
        if (rstop == 1'b1) begin
          nxt_state = STRT;
          rreset = 1'b1;         
        end
        else begin
          nxt_state = SCLP;
          rreset = 1'b0;             
        end         
      end
      else begin
        nxt_state = STRT;
        rreset = 1'b0;       
      end
    end
  endcase
end
////////////////////////////////////////////////////////////////////////////////


////////////////////////////////////////////////////////////////////////////////
// ===== detect start, stop signal ===== //
always @(posedge clk or negedge rst_n) begin
  if (rst_n == 1'b0) begin
    rstop  <= 1'b0;
    rstart <= 1'b0;
  end
  else if ( (en == 1'b1) && (scli == 1'b1) ) begin
    if (sdai_pos == 1'b1) begin
      rstop  <= 1'b1;
      rstart <= 1'b0;
    end
    else begin
      rstop <= 1'b0;
      if (sdai_neg == 1'b1) begin
        rstart <= 1'b1;
      end
      else begin
        rstart <= 1'b0;
      end
    end
  end
  else begin
    rstop  <= rstop ;
    rstart <= rstart;
  end
end
////////////////////////////////////////////////////////////////////////////////

assign stop  = rstop ;
assign start = rstart;
assign reset = rreset;

endmodule


module stransmit
(
  clk  ,
  rst_n,
  en   ,
  tr   ,
  scli ,
  sdai ,
  tdat ,
  bsy  ,
  bc   ,
  sdao ,
  shf  ,
  ino  ,
  dne
);

parameter IDLE  = 2'd0;
parameter TRAVP = 2'd1;
parameter TRAVN = 2'd2;

input        clk  ;
input        rst_n;
input        en   ;
input        tr   ;
input        scli ;
input        sdai ;
input  [7:0] tdat ;
output       bsy  ;  // [0]: free, [1]: transfering
output [3:0] bc   ;
output       sdao ;
output [7:0] shf  ;
output       ino  ;
output       dne  ;

reg          rbsy ;  // 0: free, 1: transfering
reg    [3:0] rbc  ;
reg          rsdao;
reg          rdne ;
reg          ack  ;

reg scli_01;
reg scli_02;
reg scli_03;
reg scli_04;
reg scli_05;
reg scli_06;
reg scli_07;
reg scli_08;
reg scli_09;
reg scli_10;

// D-FF to avoid the noise
always @(posedge clk or negedge rst_n) begin
  if (rst_n == 1'b0) begin
    scli_01 <= 1'b0;
    scli_02 <= 1'b0;
    scli_03 <= 1'b0;
    scli_04 <= 1'b0;
    scli_05 <= 1'b0;
    scli_06 <= 1'b0;
    scli_07 <= 1'b0;
    scli_08 <= 1'b0;
    scli_09 <= 1'b0;
    scli_10 <= 1'b0;   
  end
  else begin
    scli_01 <= scli;
    scli_02 <= scli_01;
    scli_03 <= scli_02;
    scli_04 <= scli_03;
    scli_05 <= scli_04;
    scli_06 <= scli_05;
    scli_07 <= scli_06;
    scli_08 <= scli_07;
    scli_09 <= scli_08;
    scli_10 <= scli_09;
  end
end

reg  scli_dl ;
wire scli_pos;
wire scli_neg;
always @(posedge clk or negedge rst_n) begin
  if (rst_n == 1'b0) begin
    scli_dl <= 1'b0;
  end
  else if (en == 1'b1) begin
    scli_dl <= scli_10; 
  end
  else begin
    scli_dl <= scli_dl;
   end
end
assign scli_pos = (~scli_dl) && scli_10;
assign scli_neg = scli_dl && (~scli_10);

reg [1:0] state;
reg [7:0] rshf;
always @(posedge clk or negedge rst_n) begin 
  if (rst_n == 1'b0) begin
    state <= IDLE ;
    rsdao <= 1'b0;
    rbc   <= 4'h0;
    rshf  <= 8'h00;
    rbsy  <= 1'b0;   
    rdne  <= 1'b0;
    ack   <= ack;
  end
  else if (en == 1'b1) begin
    case (state)
      IDLE : begin
        if (tr == 1'b1) begin
          state <= TRAVP;
        end
        else begin
          state <= IDLE ;
        end
        rsdao <= tdat[7];
        rbc   <= 4'h0;
        rshf  <= tdat;
        rbsy  <= 1'b0;   
        rdne  <= 1'b0;
        ack   <= ack;
      end
      TRAVP: begin
        if (scli_pos == 1'b1) begin
          state <= TRAVN;
          rshf <= rshf << 4'h1;
          if (rbc == 4'h8) begin
            ack  <= sdai;
            rdne <= 1'b1;
          end
          else begin
            ack  <= 1'b1;
            rdne <= 1'b0;
          end
        end
        else begin
          state <= state;
          rshf  <= rshf;
          ack   <= 1'b1;
          rdne  <= 1'b0;
        end
        rsdao <= rsdao;
        rbc   <= rbc;
        rbsy  <= 1'b1;
      end
      TRAVN: begin
        if (scli_neg == 1'b1) begin
          if (rbc == 4'h8) begin
            rbc  <= 4'h0;
            rshf <= tdat;
            rdne <= 1'b1;
            if (ack == 1'b0) begin
              state <= TRAVP;
              rsdao <= tdat[7];
            end
            else begin
              state <= IDLE;
              rsdao <= rsdao;
            end
          end
          else begin
            rsdao <= rshf[7];
            rshf  <= rshf;
            state <= TRAVP;
            rbc   <= rbc + 4'h1;
            rdne  <= 1'b0;
          end
        end
        else begin
          state <= TRAVN;
          rsdao <= rsdao;
          rbc   <= rbc;
          rshf  <= rshf;
          rdne  <= rdne;
        end
        rbsy <= 1'b1;
        ack  <= ack;
      end
    endcase   
  end
  else begin
    state <= IDLE ;
    rsdao <= 1'b0;
    rbc   <= 4'h0;
    rshf  <= 8'h00;
    rbsy  <= 1'b0;   
    rdne  <= 1'b0;
    ack   <= ack;
  end
end

assign bsy  = rbsy ;
assign bc   = rbc  ;
assign sdao = rsdao;
assign shf  = rshf ;
assign ino  = (rbc == 4'h8) ? 1'b1 : 1'b0;
assign dne  = rdne ;

endmodule


module sreceive
(
  clk  ,
  rst_n,
  en   ,
  re   ,
  scli ,
  sdai ,
  hol  ,
  adrck,
  saddr,
  bsy  ,
  bc   ,
  rdat ,
  sdao ,
  shf  ,
  ino  ,
  dne
);

parameter IDLE  = 2'd0;
parameter RECVP = 2'd1;
parameter RECVN = 2'd2;

input        clk  ;
input        rst_n;
input        en   ;
input        re   ;
input        scli ;
input        sdai ;
input        hol  ;
input        adrck;  // 0: no check, just receive data, 1: address checking
input  [7:0] saddr;
output       bsy  ;  // 0: free, 1: transfering
output [3:0] bc   ;
output [7:0] rdat ;
output       sdao ;
output [7:0] shf  ;
output       ino  ;
output       dne  ;

reg          rbsy ;  // 0: free, 1: transfering
reg    [3:0] rbc  ;
reg    [7:0] rrdat;
reg          rsdao;
reg          rino ;
reg          rdne ;

reg scli_01;
reg scli_02;
reg scli_03;
reg scli_04;
reg scli_05;
reg scli_06;
reg scli_07;
reg scli_08;
reg scli_09;
reg scli_10;

reg sdai_01;
reg sdai_02;
reg sdai_03;
reg sdai_04;
reg sdai_05;
reg sdai_06;
reg sdai_07;
reg sdai_08;
reg sdai_09;
reg sdai_10;

// D-FF to avoid the noise
always @(posedge clk or negedge rst_n) begin
  if (rst_n == 1'b0) begin
    scli_01 <= 1'b0;
    scli_02 <= 1'b0;
    scli_03 <= 1'b0;
    scli_04 <= 1'b0;
    scli_05 <= 1'b0;
    scli_06 <= 1'b0;
    scli_07 <= 1'b0;
    scli_08 <= 1'b0;
    scli_09 <= 1'b0;
    scli_10 <= 1'b0;   
  end
  else begin
    scli_01 <= scli;
    scli_02 <= scli_01;
    scli_03 <= scli_02;
    scli_04 <= scli_03;
    scli_05 <= scli_04;
    scli_06 <= scli_05;
    scli_07 <= scli_06;
    scli_08 <= scli_07;
    scli_09 <= scli_08;
    scli_10 <= scli_09;
  end
end

// D-FF to avoid the noise
always @(posedge clk or negedge rst_n) begin
  if (rst_n == 1'b0) begin
    sdai_01 <= 1'b0;
    sdai_02 <= 1'b0;
    sdai_03 <= 1'b0;
    sdai_04 <= 1'b0;
    sdai_05 <= 1'b0;
    sdai_06 <= 1'b0;
    sdai_07 <= 1'b0;
    sdai_08 <= 1'b0;
    sdai_09 <= 1'b0;
    sdai_10 <= 1'b0;   
  end
  else begin
    sdai_01 <= sdai;
    sdai_02 <= sdai_01;
    sdai_03 <= sdai_02;
    sdai_04 <= sdai_03;
    sdai_05 <= sdai_04;
    sdai_06 <= sdai_05;
    sdai_07 <= sdai_06;
    sdai_08 <= sdai_07;
    sdai_09 <= sdai_08;
    sdai_10 <= sdai_09;
  end
end

reg  scli_dl ;
wire scli_pos;
wire scli_neg;
always @(posedge clk or negedge rst_n) begin
  if (rst_n == 1'b0) begin
    scli_dl <= 1'b0;
  end
  else if (en == 1'b1) begin
    scli_dl <= scli_10; 
  end
  else begin
    scli_dl <= scli_dl;
   end
end
assign scli_pos = (~scli_dl) && scli_10;
assign scli_neg = scli_dl && (~scli_10);

reg [ 1:0] state;
reg [ 7:0] rshf;
always @(posedge clk or negedge rst_n) begin
  if (rst_n == 1'b0) begin
    state <= IDLE;
    rbsy  <= 1'b0 ;
    rshf  <= 8'h00;
    rbc   <= 4'h0 ;
    rrdat <= 8'h00;
    rsdao <= 1'b1 ;
    rino  <= 1'b1 ;
    rdne  <= 1'b0 ;
  end
  else if (en == 1'b1) begin
    case (state)
      IDLE : begin
        if (re == 1'b1) begin
          state <= RECVP;
        end
        else begin
          state <= IDLE;
        end
        rbsy  <= 1'b0 ;
        rshf  <= rshf ;
        rbc   <= 4'h0 ;
        rrdat <= rrdat;
        rsdao <= 1'b1 ;
        rino  <= 1'b1 ;
        rdne  <= 1'b0 ;
      end
      RECVP: begin
        if (scli_pos == 1'b1) begin
          if (rbc == 4'h8) begin
            rshf <= rshf;
          end
          else begin
            rshf <= {rshf[6:0],sdai_10};
          end
          rbc <= rbc + 4'h1;
          state <= RECVN;
        end
        else begin
          rshf <= rshf;
          rbc <= rbc;
          state <= RECVP;
        end
        rbsy  <= 1'b1;
        rrdat <= rrdat;
        rsdao <= rsdao;
        rino  <= rino ;
        rdne  <= 1'b0;
      end
      RECVN: begin
        if (scli_neg == 1'b1) begin
          if (rbc == 4'h8) begin
            state <= RECVP;
            rrdat <= rshf;
            if (adrck == 1'b0) begin
              rsdao <= 1'b0;
            end
            else begin
              if ({rshf[7:1],(hol && rshf[0])} == saddr) begin
                rsdao <= 1'b0;
              end
              else begin
                rsdao <= 1'b1;
              end
            end
            rino <= 1'b0 ;
            rdne <= 1'b0;
          end
          else if (rbc == 4'h9) begin
            state <= IDLE ;
            rrdat <= rrdat;
            rsdao <= rsdao;
            rino  <= 1'b0 ;
            rdne  <= 1'b1 ;
          end
          else begin
            state <= RECVP;
            rrdat <= rrdat;
            rsdao <= 1'b1 ;
            rino  <= rino ;
            rdne  <= 1'b0 ;
          end
        end
        else begin
          state <= state;
          rrdat <= rrdat;
          rsdao <= rsdao;
          rino  <= rino ;
          rdne  <= rdne ;
        end
        rshf  <= rshf;
        rbsy  <= 1'b1;
        rbc   <= rbc ;
      end
    endcase   
  end
  else begin
    state <= IDLE;
    rbsy  <= 1'b0 ;
    rshf  <= 8'h00;
    rbc   <= 4'h0 ;
    rrdat <= rrdat;
    rsdao <= 1'b1 ;
    rino  <= 1'b1 ;
    rdne  <= 1'b0 ;
  end
end

assign bsy  = rbsy ;
assign bc   = rbc  ;
assign rdat = rrdat;
assign sdao = rsdao;
assign shf  = rshf ;
assign ino  = rino ;
assign dne  = rdne ;

endmodule


module master
(
  // input
  clk  ,
  rst_n,
  en   ,
  scli ,
  sdai ,
  tr   ,
  ack  ,
  st   ,
  rs   ,
  sp   ,
  dlk  ,
  tdat ,
  empt ,
  spme ,
  rmit ,
  ackc ,
  brh  ,
  brl  ,

  // output
  bsy  ,
  sclo ,
  sdao ,
  rdat ,
  shf  ,
  ino  ,
  bc   ,
  acke ,
  rmiti,
  ffrd ,
  lfwr ,
  tran_dne,
  recv_dne
);

parameter IDLE = 4'd0;
parameter STRT = 4'd1;
parameter STOP = 4'd2;
parameter RSET = 4'd3;
parameter TRAN = 4'd4;
parameter RECV = 4'd5;
parameter WACK = 4'd6;
parameter XACK = 4'd7;
parameter WAIT = 4'd8;

input        clk  ;
input        rst_n;
input        en   ;
input        scli ;
input        sdai ;
input        tr   ;
input        ack  ;
input        st   ;
input        rs   ;
input        sp   ;
input        dlk  ;
input [ 7:0] tdat ;
input        empt ;
input        spme ;
input [ 3:0] rmit ;
input        ackc ;
input [ 7:0] brh  ;
input [ 7:0] brl  ;

  // output
output        bsy ;
output        sclo;
output        sdao;
output [ 7:0] rdat;
output [ 7:0] shf ;
output        ino ;
output [ 3:0] bc  ;
output        acke ;
output        rmiti;
output        ffrd ;
output        lfwr ;
output        tran_dne;
output        recv_dne;

reg           rbsy ;
reg           rffrd;
reg           rsclo;
reg           rsdao;

////////////////////
// FSM            //
////////////////////
reg [ 3:0] state;
reg        rstart;
reg        rstop;
reg        rtransmit;
reg        rreceive;
reg        rcounter;

////////////////////
// bitrate        //
////////////////////
wire       bitrate_en  ;
wire       bitrate_dlk ;
wire       bitrate_cnth;
wire       bitrate_cntl;
wire [7:0] bitrate_brh ;
wire [7:0] bitrate_brl ;
wire       bitrate_limh;
wire       bitrate_liml;

////////////////////
// mstart         //
////////////////////
wire        mstart_en     ;
wire        mstart_st     ;
wire        mstart_dlk    ;
wire [ 7:0] mstart_brh    ;
wire        mstart_cur_scl;
wire        mstart_cur_sda;
wire        mstart_sclo   ;
wire        mstart_sdao   ;
wire        mstart_bsy    ;
wire        mstart_dne    ;

////////////////////
// mstop          //
////////////////////
wire        mstop_en     ;
wire        mstop_sp     ;
wire        mstop_dlk    ;
wire [ 7:0] mstop_brh    ;
wire        mstop_cur_scl;
wire        mstop_cur_sda;
wire        mstop_sclo   ;
wire        mstop_sdao   ;
wire        mstop_bsy    ;
wire        mstop_dne    ;

////////////////////
// mtransmit      //
////////////////////
wire        mtransmit_en   ;
wire        mtransmit_tr   ;
wire        mtransmit_sdai ;
wire [ 7:0] mtransmit_tdat ;
wire [ 3:0] mtransmit_rmit ;
wire        mtransmit_ackc ;
wire        mtransmit_limh ;
wire        mtransmit_liml ;
wire        mtransmit_bsy  ;
wire        mtransmit_sclo ;
wire        mtransmit_sdao ;
wire [ 3:0] mtransmit_bc   ;
wire        mtransmit_acko ;
wire [ 7:0] mtransmit_shf  ;
wire        mtransmit_ino  ;
wire        mtransmit_rmiti;
wire        mtransmit_acke ;
wire        mtransmit_cnth ;
wire        mtransmit_cntl ;
wire        mtransmit_dne  ;

////////////////////
// mreceive       //
////////////////////
wire        mreceive_en  ;
wire        mreceive_re  ;
wire        mreceive_acki;
wire        mreceive_sdai;
wire        mreceive_limh;
wire        mreceive_liml;
wire        mreceive_bsy ;
wire        mreceive_sclo;
wire        mreceive_sdao;
wire [ 3:0] mreceive_bc  ;
wire [ 7:0] mreceive_rdat;
wire [ 7:0] mreceive_shf ;
wire        mreceive_ino ;
wire        mreceive_cnth;
wire        mreceive_cntl;
wire        mreceive_lfwr;
wire        mreceive_dne ;

////////////////////
// bitrate        //
////////////////////
assign bitrate_en   = en;
assign bitrate_dlk  = dlk;
assign bitrate_cnth = mtransmit_cnth || mreceive_cnth;
assign bitrate_cntl = mtransmit_cntl || mreceive_cntl;
assign bitrate_brh  = brh;
assign bitrate_brl  = brl;
bitrate bitrate_01
(
  .clk  (clk         ),
  .rst_n(rst_n       ),
  .en   (bitrate_en  ),
  .dlk  (bitrate_dlk ),
  .cnth (bitrate_cnth),
  .cntl (bitrate_cntl),
  .brh  (bitrate_brh ),
  .brl  (bitrate_brl ),
  .limh (bitrate_limh),
  .liml (bitrate_liml)
);

////////////////////
// mstart         //
////////////////////
assign mstart_en      = en    ;
assign mstart_st      = rstart;
assign mstart_dlk     = dlk   ;
assign mstart_brh     = brh   ;
assign mstart_cur_scl = sclo  ;
assign mstart_cur_sda = sdao  ;

mstart mstart_01
(
  .clk    (clk           ),
  .rst_n  (rst_n         ),
  .en     (mstart_en     ),
  .st     (mstart_st     ),
  .dlk    (mstart_dlk    ),
  .brh    (mstart_brh    ),
  .cur_scl(mstart_cur_scl),
  .cur_sda(mstart_cur_sda),
  .sclo   (mstart_sclo   ),
  .sdao   (mstart_sdao   ),
  .bsy    (mstart_bsy    ),
  .dne    (mstart_dne    )
);

////////////////////
// mstop          //
////////////////////
assign mstop_en      = en   ;
assign mstop_sp      = rstop;
assign mstop_dlk     = dlk  ;
assign mstop_brh     = brh  ;
assign mstop_cur_scl = sclo ;
assign mstop_cur_sda = sdao ;

mstop mstop_01
(
  .clk    (clk          ),
  .rst_n  (rst_n        ),
  .en     (mstop_en     ),
  .sp     (mstop_sp     ),
  .dlk    (mstop_dlk    ),
  .brh    (mstop_brh    ),
  .cur_scl(mstop_cur_scl),
  .cur_sda(mstop_cur_sda),
  .sclo   (mstop_sclo   ),
  .sdao   (mstop_sdao   ),
  .bsy    (mstop_bsy    ),
  .dne    (mstop_dne    )
);

////////////////////
// mtransmit      //
////////////////////
assign mtransmit_en   = en && rtransmit;
assign mtransmit_tr   = tr;
assign mtransmit_sdai = sdai;
assign mtransmit_tdat = tdat;
assign mtransmit_rmit = rmit;
assign mtransmit_ackc = ackc;
assign mtransmit_limh = bitrate_limh;
assign mtransmit_liml = bitrate_liml;

mtransmit mtransmit_01
(
  .clk  (clk            ),
  .rst_n(rst_n          ),
  .en   (mtransmit_en   ),
  .tr   (mtransmit_tr   ),
  .sdai (mtransmit_sdai ),
  .tdat (mtransmit_tdat ),
  .rmit (mtransmit_rmit ),
  .ackc (mtransmit_ackc ),
  .limh (mtransmit_limh ),
  .liml (mtransmit_liml ),
  .bsy  (mtransmit_bsy  ),
  .sclo (mtransmit_sclo ),
  .sdao (mtransmit_sdao ),
  .bc   (mtransmit_bc   ),
  .acko (mtransmit_acko ),
  .shf  (mtransmit_shf  ),
  .ino  (mtransmit_ino  ),
  .rmiti(mtransmit_rmiti),
  .acke (mtransmit_acke ),
  .cnth (mtransmit_cnth ),
  .cntl (mtransmit_cntl ),
  .dne  (mtransmit_dne  )
);

////////////////////
// mreceive       //
////////////////////
assign mreceive_en   = en && rreceive;
assign mreceive_re   = ~tr;
assign mreceive_acki = ack;
assign mreceive_sdai = sdai;
assign mreceive_limh = bitrate_limh;
assign mreceive_liml = bitrate_liml;

mreceive mreceive_01
(
  .clk  (clk          ),
  .rst_n(rst_n        ),
  .en   (mreceive_en  ),
  .re   (mreceive_re  ),
  .acki (mreceive_acki),
  .sdai (mreceive_sdai),
  .limh (mreceive_limh),
  .liml (mreceive_liml),
  .bsy  (mreceive_bsy ),
  .sclo (mreceive_sclo),
  .sdao (mreceive_sdao),
  .bc   (mreceive_bc  ),
  .rdat (mreceive_rdat),
  .shf  (mreceive_shf ),
  .ino  (mreceive_ino ),
  .cnth (mreceive_cnth),
  .cntl (mreceive_cntl),
  .lfwr (mreceive_lfwr),
  .dne  (mreceive_dne )
);

////////////////////////////////////////////////////////////////////////////////
// ===== FSM master ===== //
reg lst;  //deLay for st signal
reg lrs;  //deLay for rs signal
reg lsp;  //deLay for sp signal
reg rino;
always @(posedge clk or negedge rst_n) begin
  if (rst_n == 1'b0) begin
    state     <= IDLE;
    lst       <= 1'b0;
    lrs       <= 1'b0;
    lsp       <= 1'b0;
    rffrd     <= 1'b0;
    rstart    <= 1'b0;
    rstop     <= 1'b0;
    rtransmit <= 1'b0;
    rreceive  <= 1'b0;
    rbsy      <= 1'b0;
    rsclo     <= 1'b1;
    rsdao     <= 1'b1;
    rino      <= 1'b0;
  end
  else if (en == 1'b1) begin
    case (state)
      IDLE: begin
        if ((sp || lsp) == 1'b1) begin
          state <= IDLE;
          lst   <= (st == 1'b1) ? 1'b1 : lst;
          lrs   <= (rs == 1'b1) ? 1'b1 : lrs;
          lsp   <= 1'b0;
          rbsy  <= 1'b0;
        end
        else if ( (st || lst || rs || lrs) == 1'b1 ) begin
          state <= STRT;
          lst   <= 1'b0;
          lrs   <= 1'b0;
          lsp   <= (sp == 1'b1) ? 1'b1 : lsp;
          rbsy  <= 1'b1;
        end
        else begin
          state <= IDLE;
          lst   <= 1'b0;
          lrs   <= 1'b0;
          lsp   <= 1'b0;
          rbsy  <= 1'b0;
        end
        rffrd     <= 1'b0;
        rstart    <= 1'b0;
        rstop     <= 1'b0;
        rtransmit <= 1'b0;
        rreceive  <= 1'b0;
        rsclo     <= 1'b1;
        rsdao     <= 1'b1;
        rino      <= 1'b0;
      end
      STRT: begin
        if (mstart_dne == 1'b1) begin
          if ((sp || lsp) == 1'b1) begin
            state <= STOP;
            rffrd <= 1'b0;
          end
          else if (tr == 1'b1) begin
            if (empt == 1'b0) begin
              state <= TRAN;
              rffrd <= 1'b1;
            end
            else begin
              state <= WAIT;
              rffrd <= 1'b0;
            end
          end
          else begin
            state <= RECV;
            rffrd <= 1'b0;
          end
        end
        else begin
          state <= STRT;
        end
        rstart    <= 1'b1;
        rstop     <= 1'b0;
        rtransmit <= 1'b0;
        rreceive  <= 1'b0;
        rbsy      <= 1'b1;
        lst       <= (st == 1'b1) ? 1'b1 : lst;
        lrs       <= (rs == 1'b1) ? 1'b1 : lrs;
        lsp       <= (sp == 1'b1) ? 1'b1 : lsp;
        rsclo     <= mstart_sclo;
        rsdao     <= mstart_sdao;
        rino      <= 1'b0;
      end
      STOP: begin
        if (mstop_dne == 1'b1) begin
          state <= IDLE;
        end
        else begin
          state <= STOP;
        end
        rstart    <= 1'b0;
        rstop     <= 1'b1;
        rtransmit <= 1'b0;
        rreceive  <= 1'b0;
        rffrd     <= 1'b0;
        rbsy      <= 1'b1;
        lst       <= (st == 1'b1) ? 1'b1 : lst;
        lrs       <= (rs == 1'b1) ? 1'b1 : lrs;
        lsp       <= (sp == 1'b1) ? 1'b1 : lsp;
        rsclo     <= mstop_sclo;
        rsdao     <= mstop_sdao;
        rino      <= 1'b0;
      end
      TRAN: begin
        if (mtransmit_dne == 1'b1) begin
          state <= WACK;
        end
        else begin
          state <= (empt == 1'b0) ? TRAN : WAIT;
        end
        rffrd     <= 1'b0;
        rstart    <= 1'b0;
        rstop     <= 1'b0;
        rreceive  <= 1'b0;
        rtransmit <= 1'b1;
        rbsy      <= 1'b1;
        lst       <= (st == 1'b1) ? 1'b1 : lst;
        lrs       <= (rs == 1'b1) ? 1'b1 : lrs;
        lsp       <= (sp == 1'b1) ? 1'b1 : lsp;
        rsclo     <= mtransmit_sclo;
        rsdao     <= mtransmit_sdao;
        rino      <= mtransmit_ino;
      end
      RECV: begin
        if (mreceive_dne == 1'b1) begin
          state <= XACK;
        end
        else begin
          state <= RECV;
        end
        rstart    <= 1'b0;
        rstop     <= 1'b0;
        rtransmit <= 1'b0;
        rreceive  <= 1'b1;
        rffrd     <= 1'b0;
        rbsy      <= 1'b1;
        lst       <= (st == 1'b1) ? 1'b1 : lst;
        lrs       <= (rs == 1'b1) ? 1'b1 : lrs;
        lsp       <= (sp == 1'b1) ? 1'b1 : lsp;
        rsclo     <= mreceive_sclo;
        rsdao     <= mreceive_sdao;
        rino      <= mreceive_ino;
      end
      WACK: begin
        if (mtransmit_dne == 1'b1) begin
          state <= WACK;
          rffrd <= (empt == 1'b0) ? ~mtransmit_rmiti : 1'b0;
          lst   <= (st == 1'b1) ? 1'b1 : lst;
          lrs   <= (rs == 1'b1) ? 1'b1 : lrs;
          lsp   <= (sp == 1'b1) ? 1'b1 : lsp;
        end
        else begin
          if ((sp || lsp) == 1'b1) begin
            state <= STOP;
            rffrd <= rffrd;
            lst   <= (st == 1'b1) ? 1'b1 : lst;
            lrs   <= (rs == 1'b1) ? 1'b1 : lrs;
            lsp   <= 1'b0;
          end
          else if ((st || lst || rs || lrs) == 1'b1) begin
            state <= STRT;
            rffrd <= rffrd;
            lst   <= 1'b0;
            lrs   <= 1'b0;
            lsp   <= (sp == 1'b1) ? 1'b1 : lsp;
          end
          else if (tr == 1'b0) begin
            state <= RECV;
            rffrd <= rffrd;
            lst   <= (st == 1'b1) ? 1'b1 : lst;
            lrs   <= (rs == 1'b1) ? 1'b1 : lrs;
            lsp   <= (sp == 1'b1) ? 1'b1 : lsp;
          end
          else if (((~mtransmit_bsy && spme) || (empt && spme)) == 1'b1) begin
            state <= STOP ;
            rffrd <= rffrd;
            lst   <= (st == 1'b1) ? 1'b1 : lst;
            lrs   <= (rs == 1'b1) ? 1'b1 : lrs;
            lsp   <= 1'b0;
          end
          else begin
            state <= (empt == 1'b0) ? TRAN : WAIT;
            rffrd <= (empt == 1'b0) ? rffrd : 1'b0;
            lst   <= (st == 1'b1) ? 1'b1 : lst;
            lrs   <= (rs == 1'b1) ? 1'b1 : lrs;
            lsp   <= (sp == 1'b1) ? 1'b1 : lsp;
          end
        end
        rstart    <= rstart   ;
        rstop     <= rstop    ;
        rtransmit <= ~empt    ;
        rreceive  <= rreceive ;
        rbsy      <= 1'b1     ;
        rsclo     <= mtransmit_sclo;
        rsdao     <= mtransmit_sdao;
        rino      <= mtransmit_ino;
      end
      XACK: begin
        if (mreceive_dne == 1'b1) begin
          state <= XACK;
          rffrd <= 1'b0;
          lst   <= (st == 1'b1) ? 1'b1 : lst;
          lrs   <= (rs == 1'b1) ? 1'b1 : lrs;
          lsp   <= (sp == 1'b1) ? 1'b1 : lsp;
        end
        else begin
          if ((sp || lsp) == 1'b1) begin
            state <= STOP;
            rffrd <= 1'b0;
            lst   <= (st == 1'b1) ? 1'b1 : lst;
            lrs   <= (rs == 1'b1) ? 1'b1 : lrs;
            lsp   <= 1'b0;
          end
          else if ((st || lst || rs || lrs) == 1'b1) begin
            state <= STRT;
            rffrd <= 1'b0;
            lst   <= 1'b0;
            lrs   <= 1'b0;
            lsp   <= (sp == 1'b1) ? 1'b1 : lsp;
          end
          else if (tr == 1'b1) begin
            state <= TRAN;
            rffrd <= 1'b1;
            lst   <= (st == 1'b1) ? 1'b1 : lst;
            lrs   <= (rs == 1'b1) ? 1'b1 : lrs;
            lsp   <= (sp == 1'b1) ? 1'b1 : lsp;
          end
          else if (mreceive_bsy == 1'b0) begin
            state <= STOP;
            rffrd <= 1'b0;
            lst   <= (st == 1'b1) ? 1'b1 : lst;
            lrs   <= (rs == 1'b1) ? 1'b1 : lrs;
            lsp   <= 1'b0;
          end
          else begin
            state <= RECV;
            rffrd <= 1'b0;
            lst   <= (st == 1'b1) ? 1'b1 : lst;
            lrs   <= (rs == 1'b1) ? 1'b1 : lrs;
            lsp   <= (sp == 1'b1) ? 1'b1 : lsp;
          end
        end
        rstart    <= rstart   ;
        rstop     <= rstop    ;
        rtransmit <= rtransmit;
        rreceive  <= rreceive ;
        rbsy      <= 1'b1     ;
        rsclo     <= mreceive_sclo;
        rsdao     <= mreceive_sdao;
        rino      <= mreceive_ino;
      end
      WAIT: begin
        if ((sp || lsp) == 1'b1) begin
          state <= STOP ;
          rffrd <= rffrd;
          lst   <= (st == 1'b1) ? 1'b1 : lst;
          lrs   <= (rs == 1'b1) ? 1'b1 : lrs;
          lsp   <= 1'b0;
        end
        else if ((st || lst || rs || lrs) == 1'b1) begin
          state <= STRT;
          rffrd <= rffrd;
          lst   <= 1'b0;
          lrs   <= 1'b0;
          lsp   <= (sp == 1'b1) ? 1'b1 : lsp;
        end
        else if (tr == 1'b0) begin
          state <= RECV;
          rffrd <= rffrd;
          lst   <= (st == 1'b1) ? 1'b1 : lst;
          lrs   <= (rs == 1'b1) ? 1'b1 : lrs;
          lsp   <= (sp == 1'b1) ? 1'b1 : lsp;
        end
        else if ((empt && spme) == 1'b1) begin
          state <= STOP ;
          rffrd <= rffrd;
          lst   <= (st == 1'b1) ? 1'b1 : lst;
          lrs   <= (rs == 1'b1) ? 1'b1 : lrs;
          lsp   <= 1'b0;
        end
        else if (empt == 1'b0) begin
          state <= TRAN;
          rffrd <= 1'b1;
          lst   <= (st == 1'b1) ? 1'b1 : lst;
          lrs   <= (rs == 1'b1) ? 1'b1 : lrs;
          lsp   <= (sp == 1'b1) ? 1'b1 : lsp;
        end
        else begin
          state <= WAIT;
          rffrd <= 1'b0;
          lst   <= (st == 1'b1) ? 1'b1 : lst;
          lrs   <= (rs == 1'b1) ? 1'b1 : lrs;
          lsp   <= (sp == 1'b1) ? 1'b1 : lsp;
        end
        rstart    <= 1'b0;
        rstop     <= 1'b0;
        rtransmit <= 1'b0;
        rreceive  <= 1'b0; 
        rbsy      <= 1'b1; 
        rsclo     <= rsclo;
        rsdao     <= rsdao;
        rino      <= rino ;
      end
      default: begin
        state     <= STOP;
        rstart    <= 1'b0;
        rstop     <= 1'b0;
        rtransmit <= 1'b0;
        rreceive  <= 1'b0; 
        rffrd     <= 1'b0;
        rbsy      <= 1'b1; 
        lst       <= (st == 1'b1) ? 1'b1 : lst;
        lrs       <= (rs == 1'b1) ? 1'b1 : lrs;
        lsp       <= (sp == 1'b1) ? 1'b1 : lsp;
        rsclo     <= rsclo;
        rsdao     <= rsdao;
        rino      <= rino ;
      end
    endcase
  end
  else begin
    state     <= IDLE;
    lst       <= 1'b0;
    lrs       <= 1'b0;
    lsp       <= 1'b0;
    rffrd     <= 1'b0;
    rstart    <= 1'b0;
    rstop     <= 1'b0;
    rtransmit <= 1'b0;
    rreceive  <= 1'b0;
    rbsy      <= 1'b0;
    rsclo     <= rsclo;
    rsdao     <= rsdao;
    rino      <= rino ;
  end
end
////////////////////////////////////////////////////////////////////////////////

assign bsy  = rbsy;
assign sclo = rsclo;
assign sdao = rsdao;
assign rdat = mreceive_rdat;
assign shf = (tr == 1'b1) ? mtransmit_shf : mreceive_shf;
assign ino = rino;
assign bc  = (tr == 1'b1) ? mtransmit_bc  : mreceive_bc ;
assign acke  = mtransmit_acke;
assign rmiti = mtransmit_rmiti;
assign ffrd  = rffrd;
assign lfwr  = mreceive_lfwr;
assign tran_dne = mtransmit_dne;
assign recv_dne = mreceive_dne;

endmodule


module slave
(
  // input
  clk  ,
  rst_n,
  en   ,
  timeo,
  arhol,
  addr ,
  tdat ,
  scli ,
  sdai ,

  // output
  bsy  ,
  tr   ,
  rdat ,
  sclo ,
  sdao ,
  reset,
  shf  ,
  bc   ,
  ino  ,
  ffrd ,
  lfwr ,
  tran_dne,
  recv_dne
);
parameter IDLE  = 4'd0;
parameter ADDR1 = 4'd1;
parameter ADDR2 = 4'd2;
parameter TRAN  = 4'd4;
parameter RECV  = 4'd5;
parameter WACK  = 4'd6;
parameter XACK  = 4'd7;

input         clk  ;
input         rst_n;
input         en   ;
input         timeo;
input         arhol;  // [1]: 10-bit address, [0]: 7-bit address
input  [ 9:0] addr ;
input  [ 7:0] tdat ;
input         scli ;
input         sdai ;

output        bsy ;
output        tr   ;  // [1]: transfer, [0]: receive
output [ 7:0] rdat ;
output        sclo ;
output        sdao ;
output        reset;
output [ 7:0] shf  ;
output [ 3:0] bc   ;
output        ino  ;
output        ffrd ;
output        lfwr ;
output        tran_dne;
output        recv_dne;

reg rw   ;
reg rbsy ;
reg rsclo;
reg rsdao;
reg rtr  ;
reg rffrd;
reg rrecvint;
reg rrecvi_01;
reg rrecvi_02;
reg rrecvi_03;
reg rrecvi_04;
reg rrecvi_05;
reg rrecvi_06;
reg rrecvi_07;
reg rrecvi_08;
reg rrecvi_09;
reg rrecvi_10;
reg rreseti_01;
reg rreseti_02;
reg rreseti_03;
reg rreseti_04;
reg rreseti_05;
reg rreseti_06;
reg rreseti_07;
reg rreseti_08;
reg rreseti_09;
reg rreseti_10;

////////////////////
// FSM            //
////////////////////
reg [ 3:0] state;
reg        rtransmit;
reg        rreceive;
reg        radrck;
reg [ 7:0] rsaddr;
reg        rhol;

////////////////////
// sdetect        //
////////////////////
wire sdetect_en   ;
wire sdetect_scli ;
wire sdetect_sdai ;
wire sdetect_stop ;
wire sdetect_start;
wire sdetect_reset;

////////////////////
// stransmit      //
////////////////////
wire        stransmit_en  ;
wire        stransmit_tr  ;
wire        stransmit_scli;
wire        stransmit_sdai;
wire [ 7:0] stransmit_tdat;
wire        stransmit_bsy ;  // [0]: free, [1]: transfering
wire [ 3:0] stransmit_bc  ;
wire        stransmit_sdao;
wire [ 7:0] stransmit_shf ;
wire        stransmit_ino ;
wire        stransmit_dne ;

////////////////////
// sreceive       //
////////////////////
wire        sreceive_en   ;
wire        sreceive_re   ;
wire        sreceive_scli ;
wire        sreceive_sdai ;
wire        sreceive_hol  ;
wire        sreceive_adrck;
wire [ 7:0] sreceive_saddr;
wire        sreceive_bsy  ;  // 0: free, 1: transfering
wire [ 3:0] sreceive_bc   ;
wire [ 7:0] sreceive_rdat ;
wire        sreceive_sdao ;
wire [ 7:0] sreceive_shf  ;
wire        sreceive_ino  ;
wire        sreceive_dne  ;

////////////////////
// sdetect        //
////////////////////
assign sdetect_en   = en  ;
assign sdetect_scli = scli;
assign sdetect_sdai = sdai;

sdetect sdetect_01
(
  .clk  (clk          ),
  .rst_n(rst_n        ),
  .en   (sdetect_en   ),
  .scli (sdetect_scli ),
  .sdai (sdetect_sdai ),
  .stop (sdetect_stop ),
  .start(sdetect_start),
  .reset(sdetect_reset)
);

////////////////////
// stransmit      //
////////////////////
assign stransmit_en   = en && rtransmit;
assign stransmit_tr   = rtransmit;
assign stransmit_scli = scli;
assign stransmit_sdai = sdai;
assign stransmit_tdat = tdat;

stransmit stransmit_01
(
  .clk  (clk           ),
  .rst_n(rst_n         ),
  .en   (stransmit_en  ),
  .tr   (stransmit_tr  ),
  .scli (stransmit_scli),
  .sdai (stransmit_sdai),
  .tdat (stransmit_tdat),
  .bsy  (stransmit_bsy ),
  .bc   (stransmit_bc  ),
  .sdao (stransmit_sdao),
  .shf  (stransmit_shf ),
  .ino  (stransmit_ino ),
  .dne  (stransmit_dne )
);

////////////////////
// sreceive       //
////////////////////
assign sreceive_en   = en && rreceive;
assign sreceive_re   = rreceive;
assign sreceive_scli = scli;
assign sreceive_sdai = sdai;
assign sreceive_hol  = rhol;
assign sreceive_adrck = radrck;
assign sreceive_saddr = rsaddr;

sreceive sreceive_01
(
  .clk  (clk           ),
  .rst_n(rst_n         ),
  .en   (sreceive_en   ),
  .re   (sreceive_re   ),
  .scli (sreceive_scli ),
  .sdai (sreceive_sdai ),
  .hol  (sreceive_hol  ),
  .adrck(sreceive_adrck),
  .saddr(sreceive_saddr),
  .bsy  (sreceive_bsy  ),
  .bc   (sreceive_bc   ),
  .rdat (sreceive_rdat ),
  .sdao (sreceive_sdao ),
  .shf  (sreceive_shf  ),
  .ino  (sreceive_ino  ),
  .dne  (sreceive_dne  )
);

////////////////////////////////////////////////////////////////////////////////
// ===== FSM slave ===== //
always @(posedge clk or negedge rst_n) begin
  if (rst_n == 1'b0) begin
    state     <= IDLE;
    rtransmit <= 1'b0;
    rreceive  <= 1'b0;
    radrck    <= 1'b0;
    rffrd     <= 1'b0;
    rhol      <= 1'b0;
    rtr       <= 1'b0;
    rbsy      <= 1'b0;
    rw        <= 1'b0;
    rsaddr    <= 8'h00;
    rsclo     <= 1'b0;
    rsdao     <= 1'b0;
  end
  else if ( (en == 1'b1) && (timeo == 1'b0) ) begin
    case (state)
      IDLE: begin
        if (sdetect_start == 1'b1) begin
          state <= ADDR1;
          rbsy  <= 1'b1;
        end
        else begin
          state <= IDLE;
          rbsy  <= 1'b0;
        end
        rtransmit <= 1'b0;
        rreceive  <= 1'b0;
        radrck    <= 1'b0;
        rffrd     <= 1'b0;
        rhol      <= 1'b0;
        rtr       <= 1'b0;
        rw        <= 1'b0;
        rsaddr    <= 8'h00;
        rsclo     <= rsclo;
        rsdao     <= rsdao;
      end
      ADDR1: begin
        if (sdetect_stop == 1'b1) begin
          state    <= IDLE;
          rreceive <= 1'b0;
          radrck   <= radrck;
          rffrd    <= 1'b0;
          rw       <= 1'b0;
          rsaddr   <= rsaddr;
        end
        else if (sreceive_dne == 1'b1) begin
          rreceive <= 1'b0;
          radrck   <= radrck;
          if (arhol == 1'b1) begin
            if (sreceive_sdao == 1'b0) begin
              state <= ADDR2;
              rffrd <= 1'b0;
            end
            else begin
              state <= IDLE;
              rffrd <= 1'b0;
            end
          end
          else begin
            if (sreceive_sdao == 1'b0) begin 
              if (rdat[0] == 1'b0) begin  // receive ~ write
                state <= RECV;
                rffrd <= 1'b0;
              end
              else begin  // transfer ~ read
                state <= TRAN;
                rffrd <= 1'b1;
              end
            end
            else begin
              state <= IDLE;
              rffrd <= rffrd;
            end
          end
          rw <= rdat[0];
          rsaddr <= rsaddr;
        end
        else begin
          rreceive <= 1'b1;
          radrck   <= 1'b1;
          if (arhol == 1'b0) begin
            rsaddr <= {addr[7:1],1'b0};
          end
          else begin
            rsaddr <= {5'b1111_0,addr[9:8],1'b0};
          end
          state <= ADDR1;
          rffrd <= 1'b0;
          rw    <= rw;
        end
        rtransmit <= 1'b0;
        rtr       <= 1'b0;
        rhol      <= 1'b0;
        rbsy      <= 1'b1;
        rsclo     <= rsclo;
        rsdao     <= sreceive_sdao;
      end
      ADDR2: begin
        if (sdetect_stop == 1'b1) begin
          state    <= IDLE;
          rreceive <= 1'b0;
          radrck   <= 1'b0;
          rsaddr   <= rsaddr;
          rffrd    <= 1'b0;
        end
        else if (sdetect_start == 1'b1) begin
          state    <= ADDR1;
          rreceive <= 1'b1;
          radrck   <= 1'b1;
          rsaddr   <= rsaddr;
          rffrd    <= 1'b0;
        end
        else if (sreceive_dne == 1'b1) begin
          rreceive <= rreceive;
          radrck   <= 1'b0;
          rsaddr   <= rsaddr;
          if (sreceive_sdao == 1'b0) begin 
            if (rw == 1'b0) begin  // receive ~ write
              state <= RECV;
              rffrd <= 1'b0;
            end
            else begin  // transfer ~ read
              state <= TRAN;
              rffrd <= 1'b1;
            end
          end
          else begin
            state <= IDLE;
            rffrd <= 1'b0;
          end
        end
        else begin
          state    <= state;
          rreceive <= 1'b1;
          radrck   <= 1'b1;
          rsaddr   <= addr[7:0];
          state    <= ADDR2;
          rffrd    <= 1'b0;
        end
        rtransmit <= 1'b0;
        rhol      <= 1'b1;
        rtr       <= 1'b0;
        rw        <= rw;
        rbsy      <= 1'b1;
        rsclo     <= rsclo;
        rsdao     <= sreceive_sdao;
      end
      TRAN: begin  // master read
        if (sdetect_stop == 1'b1) begin
          state <= IDLE;
          rffrd <= 1'b0;
        end
        else if (sdetect_start == 1'b1) begin
          state <= ADDR1;
          rffrd <= 1'b0;
        end
        else if (stransmit_dne == 1'b1) begin
          state <= WACK;
          rffrd <= 1'b1;
        end
        else begin
          state <= TRAN;
          rffrd <= 1'b0;
        end
        rtransmit <= 1'b1;
        rreceive  <= 1'b0;
        radrck    <= 1'b0;
        rsaddr    <= rsaddr;
        rhol      <= rhol;
        rtr       <= 1'b1;
        rbsy      <= 1'b1;
        rw        <= rw;
        rsclo     <= rsclo;
        rsdao     <= stransmit_sdao;
      end
      RECV: begin  // master transmit
        if (sdetect_stop == 1'b1) begin
          state <= IDLE;
        end
        else if (sdetect_start == 1'b1) begin
          state <= ADDR1;
        end
        else if (sreceive_dne == 1'b1) begin
          state <= XACK;
        end
        else begin
          state <= RECV;
        end
        rtransmit <= 1'b0  ;
        rreceive  <= 1'b1  ;
        rffrd     <= 1'b0  ;
        radrck    <= 1'b0  ;
        rsaddr    <= rsaddr;
        rhol      <= rhol  ;
        rtr       <= 1'b0  ;
        rbsy      <= 1'b1  ;
        rw        <= rw    ;
        rsclo     <= rsclo;
        rsdao     <= sreceive_sdao;
      end
      WACK: begin
        if (stransmit_dne == 1'b1) begin
          state     <= WACK;
          rtransmit <= 1'b1;
        end
        else begin
          if ((sdetect_stop == 1'b1) || (stransmit_bsy == 1'b0)) begin
            state     <= IDLE;
            rtransmit <= 1'b0;
          end
          else if (sdetect_start == 1'b1) begin
            state     <= ADDR1;
            rtransmit <= 1'b0;
          end
          else begin
            state     <= TRAN;
            rtransmit <= 1'b1;
          end
        end
        rreceive <= rreceive;
        rffrd    <= 1'b0  ;
        radrck   <= 1'b0  ;
        rsaddr   <= rsaddr;
        rhol     <= rhol  ;
        rtr      <= rtr   ;
        rbsy     <= 1'b1  ;
        rw       <= rw    ;
        rsclo    <= rsclo;
        rsdao    <= rsdao;
      end
      XACK: begin
        if (sreceive_dne == 1'b1) begin
          state    <= XACK;
          rreceive <= 1'b1;
        end
        else begin
          if (sdetect_stop == 1'b1) begin
            state    <= IDLE;
            rreceive <= 1'b0;
          end
          else if (sdetect_start == 1'b1) begin
            state    <= ADDR1;
            rreceive <= 1'b0;
          end
          else begin
            state    <= RECV;
            rreceive <= 1'b1;
          end
        end
        rtransmit <= rtransmit;
        rffrd     <= 1'b0  ;
        radrck    <= 1'b0  ;
        rsaddr    <= rsaddr;
        rhol      <= rhol  ;
        rtr       <= rtr   ;
        rbsy      <= 1'b1  ;
        rw        <= rw    ;
        rsclo     <= rsclo;
        rsdao     <= rsdao;
      end
    endcase
  end
  else begin
    state     <= IDLE;
    rtransmit <= 1'b0;
    rreceive  <= 1'b0;
    radrck    <= 1'b0;
    rffrd     <= 1'b0;
    rhol      <= 1'b0;
    rtr       <= 1'b0;
    rbsy      <= 1'b0;
    rw        <= 1'b0;
    rsclo     <= 1'b0;
    rsdao     <= 1'b0;
  end
end
////////////////////////////////////////////////////////////////////////////////

////////////////////////////////////////////////////////////////////////////////
// FF to synchronize slave receive interrupt with data in queue occur same time
always @(posedge clk or negedge rst_n) begin
  if (rst_n == 1'b0) begin
    rrecvint <= 1'b0;
  end
  else if (en == 1'b1) begin
    rrecvint <= sreceive_dne;
  end
  else begin
    rrecvint <= 1'b0;
  end
end
////////////////////////////////////////////////////////////////////////////////

////////////////////////////////////////////////////////////////////////////////
// FF to extend receive interrupt keep in 10 clock
always @(posedge clk or negedge rst_n) begin
  if (rst_n == 1'b0) begin
    rrecvi_01 <= 1'b0;
    rrecvi_02 <= 1'b0;
    rrecvi_03 <= 1'b0;
    rrecvi_04 <= 1'b0;
    rrecvi_05 <= 1'b0;
    rrecvi_06 <= 1'b0;
    rrecvi_07 <= 1'b0;
    rrecvi_08 <= 1'b0;
    rrecvi_09 <= 1'b0;
    rrecvi_10 <= 1'b0;
  end
  else if (en == 1'b1) begin
    rrecvi_01 <= rrecvint;
    rrecvi_02 <= rrecvi_01;
    rrecvi_03 <= rrecvi_02;
    rrecvi_04 <= rrecvi_03;
    rrecvi_05 <= rrecvi_04;
    rrecvi_06 <= rrecvi_05;
    rrecvi_07 <= rrecvi_06;
    rrecvi_08 <= rrecvi_07;
    rrecvi_09 <= rrecvi_08;
    rrecvi_10 <= rrecvi_09;
  end
  else begin
    rrecvi_01 <= 1'b0;
    rrecvi_02 <= 1'b0;
    rrecvi_03 <= 1'b0;
    rrecvi_04 <= 1'b0;
    rrecvi_05 <= 1'b0;
    rrecvi_06 <= 1'b0;
    rrecvi_07 <= 1'b0;
    rrecvi_08 <= 1'b0;
    rrecvi_09 <= 1'b0;
    rrecvi_10 <= 1'b0;
  end
end
////////////////////////////////////////////////////////////////////////////////

////////////////////////////////////////////////////////////////////////////////
// FF to extend reset interrupt keep in 10 clock
always @(posedge clk or negedge rst_n) begin
  if (rst_n == 1'b0) begin
    rreseti_01 <= 1'b0;
    rreseti_02 <= 1'b0;
    rreseti_03 <= 1'b0;
    rreseti_04 <= 1'b0;
    rreseti_05 <= 1'b0;
    rreseti_06 <= 1'b0;
    rreseti_07 <= 1'b0;
    rreseti_08 <= 1'b0;
    rreseti_09 <= 1'b0;
    rreseti_10 <= 1'b0;
  end
  else if (en == 1'b1) begin
    rreseti_01 <= sdetect_reset;
    rreseti_02 <= rreseti_01;
    rreseti_03 <= rreseti_02;
    rreseti_04 <= rreseti_03;
    rreseti_05 <= rreseti_04;
    rreseti_06 <= rreseti_05;
    rreseti_07 <= rreseti_06;
    rreseti_08 <= rreseti_07;
    rreseti_09 <= rreseti_08;
    rreseti_10 <= rreseti_09;
  end
  else begin
    rreseti_01 <= 1'b0;
    rreseti_02 <= 1'b0;
    rreseti_03 <= 1'b0;
    rreseti_04 <= 1'b0;
    rreseti_05 <= 1'b0;
    rreseti_06 <= 1'b0;
    rreseti_07 <= 1'b0;
    rreseti_08 <= 1'b0;
    rreseti_09 <= 1'b0;
    rreseti_10 <= 1'b0;
  end
end
////////////////////////////////////////////////////////////////////////////////

assign bsy   = rbsy;
assign rdat  = sreceive_rdat;
assign sclo  = rsclo;
assign sdao  = rsdao;
assign reset = sdetect_reset || rreseti_01 || rreseti_02 || rreseti_03
               || rreseti_04 || rreseti_05 || rreseti_06 || rreseti_07
               || rreseti_08 || rreseti_09 || rreseti_10;
assign shf   = (rtr == 1'b1) ? stransmit_shf : sreceive_shf;
assign bc    = (rtr == 1'b1) ? stransmit_bc  : sreceive_bc ;
assign tr    = rtr;
assign ino   = (rtr == 1'b1) ? stransmit_ino : sreceive_ino;
assign ffrd  = rffrd;
assign lfwr  = sreceive_dne;
assign tran_dne = stransmit_dne;
assign recv_dne =    rrecvi_01 || rrecvi_02 || rrecvi_03 || rrecvi_04
                  || rrecvi_05 || rrecvi_06 || rrecvi_07 || rrecvi_08
                  || rrecvi_09 || rrecvi_10;

endmodule


module apbif
(
// AMBA APB interface
  PCLK    ,  // in
  PRESETn ,  // in
  PADDR   ,  // in
  PWRITE  ,  // in
  PSEL    ,  // in
  PENABLE ,  // in
  PSTRB   ,  // in
  PWDATA  ,  // in
  PRDATA  ,  // out
  PREADY  ,  // out
  PSLVERR ,  // out
// GIIC core
  rdatr   ,  // in
  sttr    ,  // in
  shfr    ,  // in
  bcr     ,  // in
  isr     ,  // in
  err     ,  // in
  ffcr    ,  // in
  lfcr    ,  // in
  enar    ,  // out
  modr    ,  // out
  ackr    ,  // out
  ctlr    ,  // out
  pscr    ,  // out
  brhr    ,  // out
  brlr    ,  // out
  funcr   ,  // out
  tmor    ,  // out
  rmitr   ,  // out
  addrr   ,  // out
  tdatr   ,  // out
  ier     ,  // out
  clrr    ,  // out
  rlar       // out
);

// APB
input         PCLK   ;
input         PRESETn;
input  [ 7:0] PADDR  ;
input         PWRITE ;
input         PSEL   ;
input         PENABLE;
input  [ 3:0] PSTRB  ;
input  [31:0] PWDATA ;

output [31:0] PRDATA ;
output        PREADY ;
output        PSLVERR;

// IIC
input  [ 7:0] rdatr  ;
input  [ 7:0] sttr   ;
input  [ 7:0] shfr   ;
input  [ 7:0] bcr    ;
input  [ 7:0] isr    ;
input  [ 7:0] err    ;
input  [ 7:0] ffcr   ;
input  [ 7:0] lfcr   ;

output [31:0] enar   ;
output [ 7:0] modr   ;
output [ 7:0] ackr   ;
output [ 7:0] ctlr   ;
output [ 7:0] pscr   ;
output [ 7:0] brhr   ;
output [ 7:0] brlr   ;
output [ 7:0] funcr  ;
output [15:0] tmor   ;
output [ 7:0] rmitr  ;
output [15:0] addrr  ;
output [31:0] tdatr  ;
output [ 7:0] ier    ;
output [ 7:0] clrr   ;
output [ 7:0] rlar   ;

// APB control signal
wire   [31:0] ctrl_rdat    ;
wire   [ 3:0] ctrl_wstrb   ;
wire          ctrl_we      ;
wire          ctrl_re      ;
wire   [ 7:0] ctrl_reg_addr;
wire   [31:0] ctrl_wdat    ;

// APB reg signal
wire   [ 3:0] reg_wstrb   ;
wire          reg_we      ;
wire          reg_re      ;
wire   [ 7:0] reg_reg_addr;
wire   [31:0] reg_wdat    ;
wire   [31:0] reg_rdat_apb;

wire   [ 7:0] reg_rdatr ;
wire   [ 7:0] reg_sttr  ;
wire   [ 7:0] reg_shfr  ;
wire   [ 7:0] reg_bcr   ;
wire   [ 7:0] reg_isr   ;
wire   [ 7:0] reg_err   ;
wire   [ 7:0] reg_ffcr  ;
wire   [ 7:0] reg_lfcr  ;

wire   [31:0] reg_enar  ;
wire   [ 7:0] reg_modr  ;
wire   [ 7:0] reg_ackr  ;
wire   [ 7:0] reg_ctlr  ;
wire   [ 7:0] reg_pscr  ;
wire   [ 7:0] reg_brhr  ;
wire   [ 7:0] reg_brlr  ;
wire   [ 7:0] reg_funcr ;
wire   [15:0] reg_tmor  ;
wire   [ 7:0] reg_rmitr ;
wire   [15:0] reg_addrr ;
wire   [31:0] reg_tdatr ;
wire   [ 7:0] reg_ier   ;
wire   [ 7:0] reg_clrr  ;
wire   [ 7:0] reg_rlar  ;

assign reg_wstrb    = ctrl_wstrb   ;
assign reg_we       = ctrl_we      ;
assign reg_re       = ctrl_re      ;
assign reg_reg_addr = ctrl_reg_addr;
assign reg_wdat     = ctrl_wdat    ;
assign ctrl_rdat    = reg_rdat_apb ;

assign reg_rdatr    = rdatr        ;
assign reg_sttr     = sttr         ;
assign reg_shfr     = shfr         ;
assign reg_bcr      = bcr          ;
assign reg_isr      = isr          ;
assign reg_err      = err          ;
assign reg_ffcr     = ffcr         ;
assign reg_lfcr     = lfcr         ;

apbif_ctrl apbif_ctrl_01
(
  .PCLK    (PCLK         ),
  .PRESETn (PRESETn      ),
  .PADDR   (PADDR        ),
  .PWRITE  (PWRITE       ),
  .PSEL    (PSEL         ),
  .PENABLE (PENABLE      ),
  .PSTRB   (PSTRB        ),
  .PWDATA  (PWDATA       ),
  .PRDATA  (PRDATA       ),
  .PREADY  (PREADY       ),
  .PSLVERR (PSLVERR      ),
  .rdat    (ctrl_rdat    ),
  .wstrb   (ctrl_wstrb   ),
  .we      (ctrl_we      ),
  .re      (ctrl_re      ),
  .reg_addr(ctrl_reg_addr),
  .wdat    (ctrl_wdat    )
);

apbif_reg apbif_reg_01
(
  .PCLK    (PCLK        ),
  .PRESETn (PRESETn     ),
  .wstrb   (reg_wstrb   ),
  .we      (reg_we      ),
  .re      (reg_re      ),
  .reg_addr(reg_reg_addr),
  .wdat    (reg_wdat    ),
  .rdat_apb(reg_rdat_apb),
  .rdatr   (reg_rdatr   ),
  .sttr    (reg_sttr    ),
  .shfr    (reg_shfr    ),
  .bcr     (reg_bcr     ),
  .isr     (reg_isr     ),
  .err     (reg_err     ),
  .ffcr    (reg_ffcr    ),
  .lfcr    (reg_lfcr    ),
  .enar    (reg_enar    ),
  .modr    (reg_modr    ),
  .ackr    (reg_ackr    ),
  .ctlr    (reg_ctlr    ),
  .pscr    (reg_pscr    ),
  .brhr    (reg_brhr    ),
  .brlr    (reg_brlr    ),
  .funcr   (reg_funcr   ),
  .tmor    (reg_tmor    ),
  .rmitr   (reg_rmitr   ),
  .addrr   (reg_addrr   ),
  .tdatr   (reg_tdatr   ),
  .ier     (reg_ier     ),
  .clrr    (reg_clrr    ),
  .rlar    (reg_rlar    )
);

assign enar  = reg_enar ;
assign modr  = reg_modr ;
assign ackr  = reg_ackr ;
assign ctlr  = reg_ctlr ;
assign pscr  = reg_pscr ;
assign brhr  = reg_brhr ;
assign brlr  = reg_brlr ;
assign funcr = reg_funcr;
assign tmor  = reg_tmor ;
assign rmitr = reg_rmitr;
assign addrr = reg_addrr;
assign tdatr = reg_tdatr;
assign ier   = reg_ier  ;
assign clrr  = reg_clrr ;
assign rlar  = reg_rlar ;

endmodule


////////////////////////////////////////////////////////////////////////////////
// Project name: GIIC (I2C-bus)                                               //
// File name   : apbif_ctrl                                                   //
// Description : APB control                                                  //
// Time: 20:05 02/20/2019                                                     //
// Copyright © 2019 by Unillog                                                //
////////////////////////////////////////////////////////////////////////////////

module apbif_ctrl
(
  PCLK    ,
  PRESETn ,
  PADDR   ,
  PWRITE  ,
  PSEL    ,
  PENABLE ,
  PSTRB   ,
  PWDATA  ,
  PRDATA  ,
  PREADY  ,
  PSLVERR ,
  rdat    ,
  wstrb   ,
  we      ,
  re      ,
  reg_addr,
  wdat 
);

  // APB
input         PCLK    ;
input         PRESETn ;
input  [ 7:0] PADDR   ;
input         PWRITE  ;
input         PSEL    ;
input         PENABLE ;
input  [ 3:0] PSTRB   ;
input  [31:0] PWDATA  ;
output [31:0] PRDATA  ;
output        PREADY  ;
output        PSLVERR ;
  // apbif_reg
input  [31:0] rdat    ;
output [ 3:0] wstrb   ;
output        we      ;
output        re      ;
output [ 7:0] reg_addr;
output [31:0] wdat    ;

reg [31:0] prdata_reg;
always @(posedge PCLK or negedge PRESETn) begin
  if (PRESETn == 1'b0) begin
    prdata_reg <= 32'h0000_0000; 
  end
  else if (re == 1'b1) begin
    prdata_reg <= rdat;
  end
  else begin
    prdata_reg <= prdata_reg;
  end
end
assign PRDATA = prdata_reg;

assign wstrb    = PSTRB;
assign we       = PSEL &  PENABLE &  PWRITE;
assign re       = PSEL & ~PENABLE & ~PWRITE & (PSTRB == 4'b0000);
assign reg_addr = PADDR;
assign wdat     = PWDATA;

assign PREADY  = 1'b1;  // bus always ready
assign PSLVERR = (PADDR[1:0] == 2'b00) ? 1'b0 : we;  // error if valid addr

endmodule

////////////////////////////////////////////////////////////////////////////////
// Project name: GIIC (I2C-bus)                                               //
// File name   : apbif_reg                                                    //
// Description : APB register                                                 //
// Time: 20:05 02/20/2019                                                     //
// Copyright © 2019 by Unillog                                                //
////////////////////////////////////////////////////////////////////////////////

module apbif_reg
(
  // APB control
  PCLK    ,
  PRESETn ,
  wstrb   ,
  we      ,
  re      ,
  reg_addr,
  wdat    ,
  rdat_apb,
  // IIC core
  rdatr   ,
  sttr    ,
  shfr    ,
  bcr     ,
  isr     ,
  err     ,
  ffcr    ,
  lfcr    ,
  enar    ,
  modr    ,
  ackr    ,
  ctlr    ,
  pscr    ,
  brhr    ,
  brlr    ,
  funcr   ,
  tmor    ,
  rmitr   ,
  addrr   ,
  tdatr   ,
  ier     ,
  clrr    ,
  rlar
);

parameter OPBT_ENAR  = 32'h0000_0000;
parameter OPBT_WPR   = 16'h0000;
parameter OPBT_MODR  = 8'h00;
parameter OPBT_ACKR  = 8'h00;
parameter OPBT_CTLR  = 8'h00;
parameter OPBT_PSCR  = 8'h00;
parameter OPBT_BRHR  = 8'h00;
parameter OPBT_BRLR  = 8'h00;
parameter OPBT_FUNCR = 8'h00;
parameter OPBT_TMOR  = 16'h0000;
parameter OPBT_RMITR = 8'h00;
parameter OPBT_ADDRR = 16'h0000;
parameter OPBT_TDATR = 8'h00;
parameter OPBT_IER   = 8'h00;
parameter OPBT_CLRR  = 8'h00;
parameter OPBT_RLAR  = 8'h00;

parameter ENAR  = 8'h00;  // Enable Register
parameter WPR   = 8'h04;  // Protect Register
parameter MODR  = 8'h08;  // Mod Register
parameter ACKR  = 8'h0C;  // ACK Register
parameter CTLR  = 8'h10;  // Control Register
parameter PSCR  = 8'h14;  // Prescaler Register
parameter BRHR  = 8'h18;  // Bit Rate High Register
parameter BRLR  = 8'h1C;  // Bit Rate Low Register
parameter FUNCR = 8'h20;  // Function Register
parameter TMOR  = 8'h24;  // Time Out Register
parameter RMITR = 8'h28;  // Retransmit Register
parameter ADDRR = 8'h2C;  // Address Register
parameter TDATR = 8'h30;  // Transmit Data Register
parameter IER   = 8'h34;  // Interrupt Enable Register
parameter CLRR  = 8'h38;  // Clear Register
parameter RLAR  = 8'h3C;  // Receive LIFO Address Register

parameter RDATR = 8'h50;  // Receive Data Register
parameter STTR  = 8'h54;  // Status Register
parameter SHFR  = 8'h58;  // Shift Register
parameter BCR   = 8'h5C;  // Bit Count Register
parameter ISR   = 8'h60;  // Interrupt Status Register
parameter ERR   = 8'h64;  // Error Register
parameter FFCR  = 8'h68;  // FIFO Counter Register
parameter LFCR  = 8'h6C;  // LIFO Counter Register

input         PCLK    ;
input         PRESETn ;
// APB control
input  [ 3:0] wstrb   ;
input         we      ;
input         re      ;
input  [ 7:0] reg_addr;
input  [31:0] wdat    ;
output [31:0] rdat_apb;
// GIIC core
input  [ 7:0] rdatr;
input  [ 7:0] sttr ;
input  [ 7:0] shfr ;
input  [ 7:0] bcr  ;
input  [ 7:0] isr  ;
input  [ 7:0] err  ;
input  [ 7:0] ffcr ;
input  [ 7:0] lfcr ;

output [31:0] enar ;
output [ 7:0] modr ;
output [ 7:0] ackr ;
output [ 7:0] ctlr ;
output [ 7:0] pscr ;
output [ 7:0] brhr ;
output [ 7:0] brlr ;
output [ 7:0] funcr;
output [15:0] tmor ;
output [ 7:0] rmitr;
output [15:0] addrr;
output [31:0] tdatr;
output [ 7:0] ier  ;
output [ 7:0] clrr ;
output [ 7:0] rlar ;

reg    [31:0] r_rdat_apb;  // Data read

reg [31:0] r_enar ;  // Enable Register
reg [15:0] r_wpr  ;  // Protect Register
reg [ 7:0] r_modr ;  // Mod Register
reg [ 7:0] r_ackr ;  // ACK Register
reg [ 7:0] r_ctlr ;  // Control Register
reg [ 7:0] r_pscr ;  // Prescaler Register
reg [ 7:0] r_brhr ;  // Bit Rate High Register
reg [ 7:0] r_brlr ;  // Bit Rate Low Register
reg [ 7:0] r_funcr;  // Function Register
reg [15:0] r_tmor ;  // Time Out Register
reg [ 7:0] r_rmitr;  // Retransmit Register
reg [15:0] r_addrr;  // Address Register
reg [31:0] r_tdatr;  // Transmit Data Register
reg [ 7:0] r_ier  ;  // Interrupt Enable Register
reg [ 7:0] r_clrr ;  // Clear Register
reg [ 7:0] r_rlar ;  // Receive LIFO Address Register
 
// Enable Register  00  ENAR
// KCE[31:1] ICE
always @(posedge PCLK or negedge PRESETn) begin
  if (PRESETn == 1'b0) begin
    r_enar <= OPBT_ENAR;
  end
  else if ( (we == 1'b1) && (reg_addr == ENAR) &&
          ({wdat[31:1],1'b0} == 32'hCAFE_CAFE) ) begin
    r_enar[31:8] <= 24'h00_0000;
    r_enar[ 7:0] <= (wstrb[0] == 1'b1) ? {7'b0000_000,wdat[0]} : r_enar;
  end
  else begin
    r_enar <= r_enar;
  end
end

// Protect Register 04  WPR
// RLAWP CLWP IEWP TDATWP ADDRWP RMITWP 
//                               TMOWP FUNCWP BRATWP PSCWP  CTLWP  ACKWP  MODWP
always @(posedge PCLK or negedge PRESETn) begin
  if (PRESETn == 1'b0) begin
    r_wpr <= OPBT_WPR;
  end
  else if ( (we == 1'b1) && (reg_addr == WPR) ) begin
    r_wpr[15:13] <= 3'b000;
    r_wpr[12: 8] <= (wstrb[1] == 1'b1) ? wdat[12:8] : r_wpr[15:8];
    r_wpr[ 7: 0] <= (wstrb[0] == 1'b1) ? wdat[ 7:0] : r_wpr[ 7:0];
  end
  else begin
    r_wpr <= r_wpr;
  end
end

// Mod Register 08  MODR
// MS  TR
always @(posedge PCLK or negedge PRESETn) begin
  if (PRESETn == 1'b0) begin
    r_modr <= OPBT_MODR;
  end
  else if ( (we == 1'b1) && (reg_addr == MODR) && (r_wpr[0] == 1'b1) ) begin
    r_modr <= (wstrb[0] == 1'b1) ? {6'b0000_00,wdat[1:0]} : r_modr;
  end
  else begin
    r_modr[7:2] <=  r_modr[7:2];
    r_modr[  1] <= (err[1]) ? 1'b0 : r_modr[1];
    r_modr[  0] <= (err[1]) ? 1'b0 : r_modr[0];
  end
end

// ACK Register  0C  ACKR 
// ACK
always @(posedge PCLK or negedge PRESETn) begin
  if (PRESETn == 1'b0) begin
    r_ackr <= OPBT_ACKR;
  end
  else if ( (we == 1'b1) && (reg_addr == ACKR) && (r_wpr[1] == 1'b1) ) begin
    r_ackr <= (wstrb[0] == 1'b1) ? {7'b0000_000,wdat[0]} : r_ackr;
  end
  else begin
    r_ackr <= r_ackr;
  end
end

// Control Register  10  CTLR 
// ST  RS  SP
always @(posedge PCLK or negedge PRESETn) begin
  if (PRESETn == 1'b0) begin
    r_ctlr <= OPBT_CTLR;
  end
  else if ( (we == 1'b1) && (reg_addr == CTLR) && (r_wpr[2] == 1'b1) ) begin
    r_ctlr <= (wstrb[0] == 1'b1) ? {5'b0000_0,wdat[2:0]} : r_ctlr;
  end
  else begin
    r_ctlr <= OPBT_CTLR;
  end
end

// Prescaler Register 14  PSCR
// PSC[3:0]
always @(posedge PCLK or negedge PRESETn) begin
  if (PRESETn == 1'b0) begin
    r_pscr <= OPBT_PSCR;
  end
  else if ( (we == 1'b1) && (reg_addr == PSCR) && (r_wpr[3] == 1'b1) ) begin
    r_pscr <= (wstrb[0] == 1'b1) ? {4'h0,wdat[3:0]} : r_pscr ;
  end
  else begin
    r_pscr <= r_pscr;
  end
end

// Bit Rate High Register 18  BRHR
// BRH[7:0]
always @(posedge PCLK or negedge PRESETn) begin
  if (PRESETn == 1'b0) begin
    r_brhr <= OPBT_BRHR;
  end
  else if ( (we == 1'b1) && (reg_addr == BRHR) && (r_wpr[4] == 1'b1) ) begin
    r_brhr <= (wstrb[0] == 1'b1) ? wdat[7:0] : r_brhr;
  end
  else begin
    r_brhr <= r_brhr;
  end
end

// Bit Rate Low Register  1C  BRLR
// BRL[7:0]
always @(posedge PCLK or negedge PRESETn) begin
  if (PRESETn == 1'b0) begin
    r_brlr <= OPBT_BRLR;
  end
  else if ( (we == 1'b1) && (reg_addr == BRLR) && (r_wpr[4] == 1'b1) ) begin
    r_brlr <= (wstrb[0] == 1'b1) ? wdat[7:0] : r_brlr;
  end
  else begin
    r_brlr <= r_brlr;
  end
end

// Function Register  20  FUNCR
// ALE  RMITE TMOE
always @(posedge PCLK or negedge PRESETn) begin
  if (PRESETn == 1'b0) begin
    r_funcr <= OPBT_FUNCR;
  end
  else if ( (we == 1'b1) && (reg_addr == FUNCR) && (r_wpr[5] == 1'b1) ) begin
    r_funcr <= (wstrb[0] == 1'b1) ? {1'b0,wdat[6:0]} : r_funcr;
  end
  else begin
    r_funcr <= r_funcr;
  end
end

// Time Out Register  24  TMOR
// TMO[15:0]   
always @(posedge PCLK or negedge PRESETn) begin
  if (PRESETn == 1'b0) begin
    r_tmor <= OPBT_TMOR;
  end
  else if ( (we == 1'b1) && (reg_addr == TMOR) && (r_wpr[6] == 1'b1)) begin
    r_tmor[15:8] <= (wstrb[1] == 1'b1) ? wdat[15:8] : r_tmor[15:8];
    r_tmor[ 7:0] <= (wstrb[0] == 1'b1) ? wdat[ 7:0] : r_tmor[ 7:0];
  end
  else begin
    r_tmor <= r_tmor;
  end
end
         
// Retransmit Register 28  RMITR
// - - - - RMIT[3:0]   
always @(posedge PCLK or negedge PRESETn) begin
  if (PRESETn == 1'b0) begin
    r_rmitr <= OPBT_RMITR;
  end
  else if ( (we == 1'b1) && (reg_addr == RMITR) && (r_wpr[7] == 1'b1)) begin
    r_rmitr <= (wstrb[0] == 1'b1) ? {4'h0,wdat[3:0]} : r_rmitr;
  end
  else begin
    r_rmitr <= r_rmitr;
  end
end
 
// Address Register 2C  ADDRR
// HOL  - - - - - ADDR10 ADDR7
always @(posedge PCLK or negedge PRESETn) begin
  if (PRESETn == 1'b0) begin
    r_addrr <= OPBT_ADDRR;
  end
  else if ( (we == 1'b1) && (reg_addr == ADDRR) && (r_wpr[8] == 1'b1) ) begin
    r_addrr[15:8] <= (wstrb[1] == 1'b1) ? {wdat[15],5'b00000,wdat[9:8]}
                                                             : r_addrr[15:8];
    r_addrr[ 7:0] <= (wstrb[0] == 1'b1) ? wdat[7:0] : r_addrr[ 7:0];
  end
  else begin
    r_addrr <= r_addrr;
  end
end
   
// Transmit Data Register 30  TDATR
// TDAT[31:0]             
always @(posedge PCLK or negedge PRESETn) begin
  if (PRESETn == 1'b0) begin
    r_tdatr <= {24'hC0FFEE,OPBT_TDATR};
  end
  else if ( (we == 1'b1) && (reg_addr == TDATR) && (r_wpr[9] == 1'b1) ) begin
    r_tdatr[31:0] <= (wstrb[0] == 1'b1) ? {24'hC0FFEE,wdat[7:0]} : r_tdatr[31:0];
  end
  else begin
    r_tdatr[31:0] <= {24'h0000_00,r_tdatr[7:0]};
  end
end

// Interrupt Enable Register  34  IER
// QEMIE RESETIE  RMITIE  RECVIE  TRANIE
always @(posedge PCLK or negedge PRESETn) begin
  if (PRESETn == 1'b0) begin
    r_ier <= OPBT_IER;
  end
  else if ( (we == 1'b1) && (reg_addr == IER) && (r_wpr[10] == 1'b1)) begin
    r_ier <= (wstrb[0] == 1'b1) ? {3'b000,wdat[4:0]} : r_ier;
  end
  else begin
    r_ier <= r_ier;
  end
end

// Clear Register 38  CLRR
// FQUEC QFLC - LQUEC TMOC ACKC MALC SALC
always @(posedge PCLK or negedge PRESETn) begin
  if (PRESETn == 1'b0) begin
    r_clrr <= OPBT_CLRR;
  end
  else if ( (we == 1'b1) && (reg_addr == CLRR) && (r_wpr[11] == 1'b1) ) begin
    r_clrr <= (wstrb[0] == 1'b1) ? {wdat[7:6],1'b0,wdat[4:0]}: r_clrr;
  end
  else begin
    r_clrr <= OPBT_CLRR;
  end
end

// Clear Register 3C  RLAR
// RLA[7:0]
always @(posedge PCLK or negedge PRESETn) begin
  if (PRESETn == 1'b0) begin
    r_rlar <= OPBT_RLAR;
  end
  else if ( (we == 1'b1) && (reg_addr == RLAR) && (r_wpr[12] == 1'b1) ) begin
    r_rlar <= (wstrb[0] == 1'b1) ? {4'h0,wdat[3:0]} : r_rlar;
  end
  else begin
    r_rlar <= r_rlar;
  end
end

////////////////////////////////////////////////////////////////////////////////
// FOR APB READ //
/////////////////
always @(reg_addr or
         r_enar or r_wpr or r_modr or r_ackr or r_ctlr or r_pscr or r_brhr or
         r_brlr or r_funcr or r_tmor or r_rmitr or r_addrr or r_tdatr or r_ier
         or r_clrr or r_rlar
         or rdatr or sttr or shfr or bcr or isr or err or ffcr or lfcr) begin
  case (reg_addr)
    ENAR : begin
      r_rdat_apb <= r_enar;
    end
    WPR  : begin
      r_rdat_apb <= {16'h0000,r_wpr};
    end
    MODR : begin
      r_rdat_apb <= {24'h00_0000,r_modr};
    end
    ACKR : begin
      r_rdat_apb <= {24'h00_0000,r_ackr};
    end
    CTLR : begin
      r_rdat_apb <= {24'h00_0000,r_ctlr};
    end
    PSCR : begin
      r_rdat_apb <= {24'h00_0000,r_pscr};
    end
    BRHR: begin
      r_rdat_apb <= {24'h00_0000,r_brhr};
    end
    BRLR : begin
      r_rdat_apb <= {24'h00_0000,r_brlr};
    end
    FUNCR: begin
      r_rdat_apb <= {24'h00_0000,r_funcr};
    end
    TMOR: begin
      r_rdat_apb <= {16'h0000,r_tmor};
    end
    RMITR: begin
      r_rdat_apb <= {24'h00_0000,r_rmitr};
    end
    ADDRR: begin
      r_rdat_apb <= {16'h0000,r_addrr};
    end
    TDATR: begin
      r_rdat_apb <= {24'h00_0000,r_tdatr[7:0]};
    end
    IER: begin
      r_rdat_apb <= {24'h00_0000,r_ier};
    end
    CLRR: begin
      r_rdat_apb <= {24'h00_0000,r_clrr};
    end
    RLAR: begin
      r_rdat_apb <= {24'h00_0000,r_rlar};
    end
    RDATR: begin
      r_rdat_apb <= {24'h00_0000,rdatr};
    end
    STTR : begin
      r_rdat_apb <= {24'h00_0000,sttr};
    end
    SHFR : begin
      r_rdat_apb <= {24'h00_0000,shfr};
    end
    BCR  : begin
      r_rdat_apb <= {24'h00_0000,bcr};
    end
    ISR  : begin
      r_rdat_apb <= {24'h00_0000,isr};
    end
    ERR  : begin
      r_rdat_apb <= {24'h00_0000,err};
    end
    FFCR : begin
      r_rdat_apb <= {24'h00_0000,ffcr};
    end
    LFCR : begin
      r_rdat_apb <= {24'h00_0000,lfcr};
    end
  endcase
end

assign rdat_apb = r_rdat_apb;
assign enar     = r_enar    ;
assign modr     = r_modr    ;
assign ackr     = r_ackr    ;
assign ctlr     = r_ctlr    ;
assign pscr     = r_pscr    ;
assign brhr     = r_brhr    ;
assign brlr     = r_brlr    ;
assign funcr    = r_funcr   ;
assign tmor     = r_tmor    ;
assign rmitr    = r_rmitr   ;
assign addrr    = r_addrr   ;
assign tdatr    = r_tdatr   ;
assign ier      = r_ier     ;
assign clrr     = r_clrr    ;
assign rlar     = r_rlar    ;

endmodule


module iic
(
  clk   ,  // clock
  rst_n ,  // reset

  enar  ,  // Enable Register
  modr  ,  // Mod Register
  ackr  ,  // ACK Register
  ctlr  ,  // Control Register
  pscr  ,  // Prescaler Register
  brhr  ,  // Bit Rate High Register
  brlr  ,  // Bit Rate Low Register
  funcr ,  // Function Register
  tmor  ,  // Time Out Register
  rmitr ,  // Retransmit Register
  addrr ,  // Address Register
  tdatr ,  // Transmit Data Register
  ier   ,  // Interrupt Enable Register
  clrr  ,  // Clear Register
  rlar  ,  // Receive LIFO Address Register

  rdatr ,  // Receive Data Register
  sttr  ,  // Status Register
  shfr  ,  // Shift Register
  bcr   ,  // Bit Count Register
  isr   ,  // Interrupt Status Register
  err   ,  // Error Register
  ffcr  ,  // FIFO Counter Register
  lfcr  ,  // LIFO Counter Register

  scl   ,  // scl pin
  sda      // sda pin
);

input         clk  ;
input         rst_n;

input  [31:0] enar ;
input  [ 7:0] modr ;
input  [ 7:0] ackr ;
input  [ 7:0] ctlr ;
input  [ 7:0] pscr ;
input  [ 7:0] brhr ;
input  [ 7:0] brlr ;
input  [ 7:0] funcr;
input  [15:0] tmor ;
input  [ 7:0] rmitr;
input  [15:0] addrr;
input  [31:0] tdatr;
input  [ 7:0] ier  ;
input  [ 7:0] clrr ;
input  [ 7:0] rlar ;

output [ 7:0] rdatr;
output [ 7:0] sttr ;
output [ 7:0] shfr ;
output [ 7:0] bcr  ;
output [ 7:0] isr  ;
output [ 7:0] err  ;
output [ 7:0] ffcr ;
output [ 7:0] lfcr ;

inout         scl  ;
inout         sda  ;

wire        en     ;
wire        ms     ;
wire        tr     ;
wire        ack    ;
wire        st     ;
wire        rs     ;
wire        sp     ;
wire [ 3:0] psc    ;
wire [ 7:0] brh    ;
wire [ 7:0] brl    ;
wire [ 2:0] nfs    ;
wire        spme   ;
wire        ale    ;
wire        rmite  ;
wire        tmoe   ;
wire [15:0] tmo    ;
wire [ 3:0] rmit   ;
wire [ 9:0] addr   ;
wire        arhol  ;
wire [23:0] tkey   ;
wire [ 7:0] tdat   ;
wire        qemie  ;
wire        resetie;
wire        rmitie ;
wire        recvie ;
wire        tranie ;
wire        fquec  ;
wire        qflc   ;
wire        qemc   ;
wire        lquec  ;
wire        tmoc   ;
wire        ackc   ;
wire        malc   ;
wire        salc   ;
wire [ 3:0] rla    ;

wire [ 7:0] rdat  ;
wire        ino   ;
wire        sclo  ;
wire        sdao  ;
wire        scli  ;
wire        sdai  ;
wire        bbsy  ;
wire        gbsy  ;
wire        bsy   ;
wire [ 7:0] shf   ;
wire [ 3:0] bc    ;
wire        qemi  ;
wire        reseti;
wire        rmiti ;
wire        recvi ;
wire        trani ;
wire        qfle  ;
wire        qeme  ;
wire        timoe ;
wire        acke  ;
wire        male  ;
wire        sale  ;
wire        oe    ;  // output enable to avoid artribute
////////////////////

wire tran_dne;
wire recv_dne;

////////////////////
// noisefter      //
////////////////////
wire        noisefter_en     ;
wire [ 2:0] noisefter_nfs    ;
wire        noisefter_scl_sgi; 
wire        noisefter_scl_sgo; 
wire        noisefter_sda_sgi; 
wire        noisefter_sda_sgo; 

////////////////////
// bbsy           //
////////////////////
wire bbsy_en  ;
wire bbsy_scli;
wire bbsy_sdai;
wire bbsy_sclo;
wire bbsy_sdao;
wire bbsy_ms  ;
wire bbsy_bbsy; 
wire bbsy_gbsy; 

////////////////////
// arbitration    //
////////////////////
wire arbitration_en   ;
wire arbitration_bbsy ;
wire arbitration_gbsy ;
wire arbitration_sda  ;
wire arbitration_msdao;
wire arbitration_ssdao;
wire arbitration_st   ;
wire arbitration_ms   ;
wire arbitration_ino  ;
wire arbitration_malc ;
wire arbitration_salc ;
wire arbitration_male ;
wire arbitration_sale ;

////////////////////
// prescaler      //
////////////////////
wire       prescaler_en ;
wire [3:0] prescaler_psc;
wire       prescaler_dlk; 

////////////////////
// timeout        //
////////////////////
wire        timeout_en  ;
wire        timeout_scli;
wire        timeout_dlk ;
wire [15:0] timeout_tmo ;
wire        timeout_tmoc;
wire        timeout_limt;
wire [15:0] timeout_cnt ;

////////////////////
// fifo           //
////////////////////
wire        fifo_en  ;
wire        fifo_rd  ;
wire        fifo_clrq;
wire [ 2:0] fifo_clre;
wire [23:0] fifo_tkey;
wire [ 7:0] fifo_data;
wire [ 7:0] fifo_tdat;
wire [ 2:0] fifo_err ;
wire [ 3:0] fifo_ffcr;

////////////////////
// lifo           //
////////////////////
wire        lifo_en  ;
wire        lifo_wr  ;
wire [ 7:0] lifo_addr;
wire        lifo_clrq;
wire [ 7:0] lifo_data;
wire [ 7:0] lifo_rdat;
wire [ 3:0] lifo_lfcr;

////////////////////
// master         //
////////////////////
wire        master_en  ;
wire        master_dlki;
wire        master_sdai;
wire        master_tr  ;
wire        master_ack ;
wire        master_st  ;
wire        master_rs  ;
wire        master_sp  ;
wire        master_dlk ;
wire [ 7:0] master_tdat;
wire        master_empt;
wire        master_spme;
wire [ 3:0] master_rmit;
wire        master_ackc;
wire [ 7:0] master_brh ;
wire [ 7:0] master_brl ;
wire        master_bsy ;
wire        master_dlko;
wire        master_sdao;
wire [ 7:0] master_rdat;
wire [ 7:0] master_shf ;
wire        master_ino ;
wire [ 3:0] master_bc  ;
wire        master_acke;
wire        master_rmiti;
wire        master_ffrd;
wire        master_lfwr;
wire        master_tran_dne;
wire        master_recv_dne;

////////////////////
// slave          //
////////////////////
wire        slave_en   ;
wire        slave_timeo;
wire        slave_arhol;
wire [ 9:0] slave_addr ;
wire [ 7:0] slave_tdat ;
wire        slave_scli ;
wire        slave_sdai ;
wire        slave_tr   ;
wire        slave_bsy  ;
wire [ 7:0] slave_rdat ;
wire        slave_sclo ;
wire        slave_sdao ;
wire        slave_reset;
wire [ 7:0] slave_shf  ;
wire        slave_ino  ;
wire [ 3:0] slave_bc   ;
wire        slave_ffrd ;
wire        slave_lfwr ;
wire        slave_tran_dne;
wire        slave_recv_dne;

assign en = enar[0];
assign {ms,tr} = modr[1:0];
assign ack = ackr[0];
assign {st,rs,sp} = ctlr[2:0];
assign psc = pscr[3:0];
assign brh = brhr;
assign brl = brlr;
assign {nfs,spme,ale,rmite,tmoe} = funcr[6:0];
assign tmo = tmor;
assign rmit = rmitr[3:0];
assign addr = addrr[9:0];
assign arhol = addrr[15];
assign tkey = tdatr[31:8];
assign tdat = tdatr[7:0];
assign {qemie,resetie,rmitie,recvie,tranie} = ier[4:0];
assign {fquec,qflc} = clrr[7:6];
assign {qemc,lquec,tmoc,ackc,malc,salc} = clrr[4:0];
assign rla = rlar[3:0];

////////////////////
// noisefter      //
////////////////////
assign noisefter_en      = en ;
assign noisefter_nfs     = nfs;     
assign noisefter_scl_sgi = scl;
assign noisefter_sda_sgi = sda;
noisefter noisefter_scl
(
  .clk  (clk              ),
  .rst_n(rst_n            ),
  .en   (noisefter_en     ),
  .nfs  (noisefter_nfs    ),
  .sgi  (noisefter_scl_sgi),
  .sgo  (noisefter_scl_sgo)
);

noisefter noisefter_sda
(
  .clk  (clk              ),
  .rst_n(rst_n            ),
  .en   (noisefter_en     ),
  .nfs  (noisefter_nfs    ),
  .sgi  (noisefter_sda_sgi),
  .sgo  (noisefter_sda_sgo)
);

////////////////////
// bbsy           //
////////////////////
assign bbsy_en   = en ;
assign bbsy_scli = scl;
assign bbsy_sdai = sda;
assign bbsy_sclo = master_dlko;
assign bbsy_sdao = master_sdao;
assign bbsy_ms   = ms ;
bbsy bbsy_01
(
  .clk  (clk      ),
  .rst_n(rst_n    ),
  .en   (bbsy_en  ),
  .scli (bbsy_scli),
  .sdai (bbsy_sdai),
  .sclo (bbsy_sclo),
  .sdao (bbsy_sdao),
  .ms   (bbsy_ms  ),
  .bbsy (bbsy_bbsy),
  .gbsy (bbsy_gbsy)
);

////////////////////
// arbitration    //
////////////////////
assign arbitration_en    = en && ale  ;
assign arbitration_bbsy  = bbsy_bbsy  ;
assign arbitration_gbsy  = bbsy_gbsy  ;
assign arbitration_sda   = sda        ;
assign arbitration_msdao = master_sdao;
assign arbitration_ssdao = slave_sdao ;
assign arbitration_ms    = ms         ;
assign arbitration_st    = st || rs   ;
assign arbitration_ino   = ino        ;
assign arbitration_malc  = malc       ;
assign arbitration_salc  = salc       ;

arbitration arbitration_01
(
  .clk  (clk              ),
  .rst_n(rst_n            ),
  .en   (arbitration_en   ),
  .bbsy (arbitration_bbsy ),
  .gbsy (arbitration_gbsy ), 
  .sda  (arbitration_sda  ),
  .msdao(arbitration_msdao),
  .ssdao(arbitration_ssdao),
  .ms   (arbitration_ms   ),
  .st   (arbitration_st   ),
  .ino  (arbitration_ino  ),
  .malc (arbitration_malc ),
  .salc (arbitration_salc ),
  .male (arbitration_male ),
  .sale (arbitration_sale )
);

////////////////////
// prescaler      //
////////////////////
assign prescaler_en  = en ;
assign prescaler_psc = psc;
prescaler prescaler_01
(
  .clk  (clk           ),
  .rst_n(rst_n         ),
  .en   (prescaler_en  ),
  .psc  (prescaler_psc ),
  .dlk  (prescaler_dlk )
);

////////////////////
// timeout        //
////////////////////
assign timeout_en   = en && tmoe && (~ms);
assign timeout_scli = scli               ;
assign timeout_dlk  = prescaler_dlk      ;
assign timeout_tmo  = tmo                ;
assign timeout_tmoc = tmoc               ;

timeout timeout_01
(
  .clk  (clk         ),
  .rst_n(rst_n       ),
  .en   (timeout_en  ),
  .scli (timeout_scli),
  .dlk  (timeout_dlk ),
  .tmo  (timeout_tmo ),
  .tmoc (timeout_tmoc),
  .limt (timeout_limt),
  .cnt  (timeout_cnt )
);

////////////////////
// fifo           //
////////////////////
assign fifo_en   = en;
assign fifo_rd   = (ms == 1'b1) ? master_ffrd : slave_ffrd;
assign fifo_clrq = fquec;
assign fifo_clre = {qflc,2'b00};
assign fifo_tkey = tkey;
assign fifo_data = tdat;
fifo fifo_01
(
  .clk  (clk      ),
  .rst_n(rst_n    ),
  .en   (fifo_en  ),
  .rd   (fifo_rd  ),
  .clrq (fifo_clrq),
  .clre (fifo_clre),
  .tkey (fifo_tkey),
  .data (fifo_data),
  .tdat (fifo_tdat),
  .err  (fifo_err ),
  .ffcr (fifo_ffcr)
);

////////////////////
// lifo           //
////////////////////
assign lifo_en   = en;
assign lifo_wr   = (ms == 1'b1) ? master_lfwr : slave_lfwr;
assign lifo_addr = rla;
assign lifo_clrq = lquec;
assign lifo_data = (ms == 1'b1) ? master_rdat : slave_rdat;
lifo lifo_01
(
  .clk  (clk      ),
  .rst_n(rst_n    ),
  .en   (lifo_en  ),
  .wr   (lifo_wr  ),
  .addr (lifo_addr),
  .clrq (lifo_clrq),
  .data (lifo_data),
  .rdat (lifo_rdat),
  .lfcr (lifo_lfcr)
);

////////////////////
// master         //
////////////////////
assign master_en   = (arbitration_male || arbitration_sale) ? 1'b0 : en && ms;
assign master_dlki = scli;
assign master_sdai = sdai;
assign master_tr   = tr;
assign master_ack  = ack;
assign master_st   = (ale == 1'b0) ? ctlr[2]:(ctlr[2] & (~bbsy_bbsy));
assign master_rs   = (ale == 1'b0) ? ctlr[1]:(ctlr[1] & bbsy_bbsy & bbsy_gbsy);
assign master_sp   = sp;
assign master_dlk  = prescaler_dlk;
assign master_tdat = fifo_tdat;
assign master_empt = fifo_err[1];
assign master_spme = spme;
assign master_rmit = (rmite == 1'b1) ? rmit : 4'h0 ;
assign master_ackc = ackc;
assign master_brh  = brh;
assign master_brl  = brl;

master master_01
(
  .clk     (clk            ),
  .rst_n   (rst_n          ),
  .en      (master_en      ),
  .scli    (master_dlki    ),
  .sdai    (master_sdai    ),
  .tr      (master_tr      ),
  .ack     (master_ack     ),
  .st      (master_st      ),
  .rs      (master_rs      ),
  .sp      (master_sp      ),
  .dlk     (master_dlk     ),
  .tdat    (master_tdat    ),
  .empt    (master_empt    ),
  .spme    (master_spme    ),
  .rmit    (master_rmit    ),
  .ackc    (master_ackc    ),
  .brh     (master_brh     ),
  .brl     (master_brl     ),
  .bsy     (master_bsy     ),
  .sclo    (master_dlko    ),
  .sdao    (master_sdao    ),
  .rdat    (master_rdat    ),
  .shf     (master_shf     ),
  .ino     (master_ino     ),
  .bc      (master_bc      ),
  .acke    (master_acke    ),
  .rmiti   (master_rmiti   ),
  .ffrd    (master_ffrd    ),
  .lfwr    (master_lfwr    ),
  .tran_dne(master_tran_dne),
  .recv_dne(master_recv_dne)
);

////////////////////
// slave          //
////////////////////
assign slave_en    = (arbitration_male || arbitration_sale) ? 1'b1:en && (~ms);
assign slave_timeo = timeout_limt;
assign slave_arhol = arhol;
assign slave_addr  = addr;
assign slave_tdat  = fifo_tdat;
assign slave_scli  = scli;
assign slave_sdai  = sdai;

slave slave_01
(
  .clk     (clk           ),
  .rst_n   (rst_n         ),
  .en      (slave_en      ),
  .timeo   (slave_timeo   ),
  .tr      (slave_tr      ),
  .arhol   (slave_arhol   ),
  .addr    (slave_addr    ),
  .tdat    (slave_tdat    ),
  .scli    (slave_scli    ),
  .sdai    (slave_sdai    ),
  .bsy     (slave_bsy     ),
  .rdat    (slave_rdat    ),
  .sclo    (slave_sclo    ),
  .sdao    (slave_sdao    ),
  .reset   (slave_reset   ),
  .shf     (slave_shf     ),
  .ino     (slave_ino     ),
  .bc      (slave_bc      ),
  .ffrd    (slave_ffrd    ),
  .lfwr    (slave_lfwr    ),
  .tran_dne(slave_tran_dne),
  .recv_dne(slave_recv_dne)
);

////////////////////
// inout port     //
////////////////////
assign sclo = (ms  == 1'b1) ? master_dlko : slave_sclo;
assign sdao = (ms  == 1'b1) ? master_sdao : slave_sdao;
assign scli = (ms  == 1'b0) ? noisefter_scl_sgo : 1'b1;
assign sdai = (ino == 1'b1) ? noisefter_sda_sgo : 1'b1;
assign scl  = ((ms  == 1'b1) && (sclo == 1'b0) && (oe == 1'b1)) ? 1'b0 : 1'bz;
assign sda  = ((ino == 1'b0) && (sdao == 1'b0) && (oe == 1'b1)) ? 1'b0 : 1'bz;
////////////////////

////////////////////
// pre output     //
////////////////////
assign rdat   = lifo_rdat;
assign ino    = (ms == 1'b1) ? master_ino : slave_ino;
assign bbsy   = bbsy_bbsy;
assign gbsy   = bbsy_gbsy;
assign bsy    = (ms == 1'b1) ? master_bsy : slave_bsy;
assign shf    = (ms == 1'b1) ? master_shf : slave_shf;
assign bc     = (ms == 1'b1) ? master_bc  : slave_bc;
assign reseti = ((resetie == 1'b1) && (ms == 1'b0)) ? slave_reset : 1'b0;
assign qemi   = (qemie == 1'b1) ? fifo_err[1] : 1'b0;
assign rmiti  = (rmitie) ? master_rmiti : 1'b0 ; 
assign recvi  = (recvie) ? ((ms) ? master_recv_dne : slave_recv_dne) : 1'b0;
assign trani  = (tranie) ? ((ms) ? master_tran_dne : slave_tran_dne) : 1'b0;
assign qfle   = fifo_err[2];
assign timoe  = timeout_limt;
assign acke   = master_acke;
assign male   = arbitration_male;
assign sale   = arbitration_sale;
assign oe     = ((ale == 1'b0) || (ms == 1'b0)) ?
                                1'b1 : (~bbsy_bbsy || (bbsy_bbsy & bbsy_gbsy));
////////////////////

////////////////////
// output signal  //
////////////////////
assign rdatr = rdat;
assign sttr  = {ino,sclo,sdao,scli,sdai,bbsy,gbsy,bsy};
assign shfr  = shf[7:0];           
assign bcr   = {4'h0,bc[3:0]};
assign isr   = {3'b000,qemi,reseti,rmiti,recvi,trani};
assign err   = {1'b0,qfle,2'b00,timoe,acke,male,sale};
assign ffcr  = fifo_ffcr;
assign lfcr  = lifo_lfcr;

endmodule


module giic
(
  // AMBA APB
  PCLK   ,
  PRESETn,
  PADDR  ,
  PWRITE ,
  PSEL   ,
  PENABLE,
  PSTRB  ,
  PWDATA ,
  PRDATA ,
  PREADY ,
  PSLVERR,
  // INTERRUPT
  qemi   ,
  reseti ,
  rmiti  ,
  recvi  ,
  trani  ,
  // IO
  scl,
  sda
);

// AMBA APB
input         PCLK   ;
input         PRESETn;
input  [ 7:0] PADDR  ;
input         PWRITE ;
input         PSEL   ;
input         PENABLE;
input  [ 3:0] PSTRB  ;
input  [31:0] PWDATA ;
output [31:0] PRDATA ;
output        PREADY ;
output        PSLVERR;
// INTERRUPT
output        qemi   ;
output        reseti ;
output        rmiti  ;
output        recvi  ;
output        trani  ;
// IO
inout         scl    ;
inout         sda    ;

////////////////////
// APB signal     //
////////////////////
// in
wire        apbif_PCLK   ;
wire        apbif_PRESETn;
wire [ 7:0] apbif_PADDR  ;
wire        apbif_PWRITE ;
wire        apbif_PSEL   ;
wire        apbif_PENABLE;
wire [ 3:0] apbif_PSTRB  ;
wire [31:0] apbif_PWDATA ;
// out
wire [31:0] apbif_PRDATA ;
wire        apbif_PREADY ;
wire        apbif_PSLVERR;
// in
wire [ 7:0] apbif_rdatr  ;
wire [ 7:0] apbif_sttr   ;
wire [ 7:0] apbif_shfr   ;
wire [ 7:0] apbif_bcr    ;
wire [ 7:0] apbif_isr    ;
wire [ 7:0] apbif_err    ;
wire [ 7:0] apbif_ffcr   ;
wire [ 7:0] apbif_lfcr   ;
// out
wire [31:0] apbif_enar   ;
wire [ 7:0] apbif_modr   ;
wire [ 7:0] apbif_ackr   ;
wire [ 7:0] apbif_ctlr   ;
wire [ 7:0] apbif_pscr   ;
wire [ 7:0] apbif_brhr   ;
wire [ 7:0] apbif_brlr   ;
wire [ 7:0] apbif_funcr  ;
wire [15:0] apbif_tmor   ;
wire [ 7:0] apbif_rmitr  ;
wire [15:0] apbif_addrr  ;
wire [31:0] apbif_tdatr  ;
wire [ 7:0] apbif_ier    ;
wire [ 7:0] apbif_clrr   ;
wire [ 7:0] apbif_rlar   ;

////////////////////
// iic signal     //
////////////////////
// in
wire        iic_clk  ;
wire        iic_rst_n;
wire [31:0] iic_enar ;
wire [ 7:0] iic_modr ;
wire [ 7:0] iic_ackr ;
wire [ 7:0] iic_ctlr ;
wire [ 7:0] iic_pscr ;
wire [ 7:0] iic_brhr ;
wire [ 7:0] iic_brlr ;
wire [ 7:0] iic_funcr;
wire [15:0] iic_tmor ;
wire [ 7:0] iic_rmitr;
wire [15:0] iic_addrr;
wire [31:0] iic_tdatr;
wire [ 7:0] iic_ier  ;
wire [ 7:0] iic_clrr ;
wire [ 7:0] iic_rlar ;
// out
wire [ 7:0] iic_rdatr;
wire [ 7:0] iic_sttr ;
wire [ 7:0] iic_shfr ;
wire [ 7:0] iic_bcr  ;
wire [ 7:0] iic_isr  ;
wire [ 7:0] iic_err  ;
wire [ 7:0] iic_ffcr ;
wire [ 7:0] iic_lfcr ;

// connect signal APBIF <-> iic
// input -> APB in
assign apbif_PCLK    = PCLK   ;
assign apbif_PRESETn = PRESETn;
assign apbif_PADDR   = PADDR  ;
assign apbif_PWRITE  = PWRITE ;
assign apbif_PSEL    = PSEL   ;
assign apbif_PENABLE = PENABLE;
assign apbif_PSTRB   = PSTRB  ;
assign apbif_PWDATA  = PWDATA ;
// APB out -> output
assign PRDATA  = apbif_PRDATA ;
assign PREADY  = apbif_PREADY ;
assign PSLVERR = apbif_PSLVERR;
// APB out -> iic in
assign iic_clk   = PCLK       ;
assign iic_rst_n = PRESETn    ;
assign iic_enar  = apbif_enar ; 
assign iic_modr  = apbif_modr ; 
assign iic_ackr  = apbif_ackr ; 
assign iic_ctlr  = apbif_ctlr ; 
assign iic_pscr  = apbif_pscr ; 
assign iic_brhr  = apbif_brhr ; 
assign iic_brlr  = apbif_brlr ; 
assign iic_funcr = apbif_funcr; 
assign iic_tmor  = apbif_tmor ; 
assign iic_rmitr = apbif_rmitr; 
assign iic_addrr = apbif_addrr; 
assign iic_tdatr = apbif_tdatr; 
assign iic_ier   = apbif_ier  ; 
assign iic_clrr  = apbif_clrr ;
assign iic_rlar  = apbif_rlar ; 
// iic out -> APB in
assign apbif_rdatr = iic_rdatr;
assign apbif_sttr  = iic_sttr ;
assign apbif_shfr  = iic_shfr ;
assign apbif_bcr   = iic_bcr  ;
assign apbif_isr   = iic_isr  ;
assign apbif_err   = iic_err  ;
assign apbif_ffcr  = iic_ffcr ;
assign apbif_lfcr  = iic_lfcr ;
////////////////////
// APB IF         //
////////////////////
apbif apbif_01
(
  .PCLK    (apbif_PCLK   ),
  .PRESETn (apbif_PRESETn),
  .PADDR   (apbif_PADDR  ),
  .PWRITE  (apbif_PWRITE ),
  .PSEL    (apbif_PSEL   ),
  .PENABLE (apbif_PENABLE),
  .PSTRB   (apbif_PSTRB  ),
  .PWDATA  (apbif_PWDATA ),
  .PRDATA  (apbif_PRDATA ),
  .PREADY  (apbif_PREADY ),
  .PSLVERR (apbif_PSLVERR),
  .rdatr   (apbif_rdatr  ),
  .sttr    (apbif_sttr   ),
  .shfr    (apbif_shfr   ),
  .bcr     (apbif_bcr    ),
  .isr     (apbif_isr    ),
  .err     (apbif_err    ),
  .ffcr    (apbif_ffcr   ),
  .lfcr    (apbif_lfcr   ),
  .enar    (apbif_enar   ),
  .modr    (apbif_modr   ),
  .ackr    (apbif_ackr   ),
  .ctlr    (apbif_ctlr   ),
  .pscr    (apbif_pscr   ),
  .brhr    (apbif_brhr   ),
  .brlr    (apbif_brlr   ),
  .funcr   (apbif_funcr  ),
  .tmor    (apbif_tmor   ),
  .rmitr   (apbif_rmitr  ),
  .addrr   (apbif_addrr  ),
  .tdatr   (apbif_tdatr  ),
  .ier     (apbif_ier    ),
  .clrr    (apbif_clrr   ),
  .rlar    (apbif_rlar   )
);

////////////////////
// IIC CORE       //
////////////////////
iic iic_01
(
  .clk  (iic_clk  ), 
  .rst_n(iic_rst_n), 
  .enar (iic_enar ), 
  .modr (iic_modr ), 
  .ackr (iic_ackr ), 
  .ctlr (iic_ctlr ), 
  .pscr (iic_pscr ), 
  .brhr (iic_brhr ), 
  .brlr (iic_brlr ), 
  .funcr(iic_funcr), 
  .tmor (iic_tmor ), 
  .rmitr(iic_rmitr), 
  .addrr(iic_addrr), 
  .tdatr(iic_tdatr), 
  .ier  (iic_ier  ), 
  .clrr (iic_clrr ), 
  .rlar (iic_rlar ),
  .rdatr(iic_rdatr), 
  .sttr (iic_sttr ), 
  .shfr (iic_shfr ), 
  .bcr  (iic_bcr  ), 
  .isr  (iic_isr  ), 
  .err  (iic_err  ),
  .ffcr (iic_ffcr ),
  .lfcr (iic_lfcr ), 
  .scl  (scl      ), 
  .sda  (sda      )
);

////////////////////
// ISR CORE       //
////////////////////
assign qemi   = iic_isr[4];
assign reseti = iic_isr[3];
assign rmiti  = iic_isr[2];
assign recvi  = iic_isr[1];
assign trani  = iic_isr[0];

endmodule