//////////////////////////////////////////////////////////////////////
//// ////
//// i2cSlave.v ////
//// ////
//// This file is part of the i2cSlave opencores effort.
//// ////
//// ////
//// Module Description: ////
//// You will need to modify this file to implement your
//// interface.
//// ////
//// To Do: ////
////
//// ////
//// Author(s): ////
//// - Steve Fielding, sfielding@base2designs.com ////
//// ////
//////////////////////////////////////////////////////////////////////
//// ////
//// Copyright (C) 2008 Steve Fielding and OPENCORES.ORG ////
//// ////
//// This source file may be used and distributed without ////
//// restriction provided that this copyright statement is not ////
//// removed from the file and that any derivative work contains ////
//// the original copyright notice and the associated disclaimer. ////
//// ////
//// This source file is free software; you can redistribute it ////
//// and/or modify it under the terms of the GNU Lesser General ////
//// Public License as published by the Free Software Foundation; ////
//// either version 2.1 of the License, or (at your option) any ////
//// later version. ////
//// ////
//// This source is distributed in the hope that it will be ////
//// useful, but WITHOUT ANY WARRANTY; without even the implied ////
//// warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR ////
//// PURPOSE. See the GNU Lesser General Public License for more ////
//// details. ////
//// ////
//// You should have received a copy of the GNU Lesser General ////
//// Public License along with this source; if not, download it ////
//// from ////
//// ////
//////////////////////////////////////////////////////////////////////
//
`include "i2cSlave_define.v"
module i2cSlave (
clk,
rst,
sda,
scl,
tb_readEn,
tb_writeEn,
tb_addr,
tb_dataIn,
tb_dataOut
);
input clk;
input rst;
inout sda;
input scl;
// tb interface
input tb_readEn;
input tb_writeEn;
input [15:0] tb_addr;
input [15:0] tb_dataIn;
output [15:0] tb_dataOut;
// local wires and regs
reg sdaDeb;
reg sclDeb;
reg [`DEB_I2C_LEN-1:0] sdaPipe;
reg [`DEB_I2C_LEN-1:0] sclPipe;
reg [`SCL_DEL_LEN-1:0] sclDelayed;
reg [`SDA_DEL_LEN-1:0] sdaDelayed;
reg [1:0] startStopDetState;
wire clearStartStopDet;
wire sdaOut;
wire sdaIn;
wire [15:0] regAddr;
wire [7:0] dataToRegIF;
wire writeEn;
wire [7:0] dataFromRegIF;
reg [1:0] rstPipe;
wire rstSyncToClk;
reg startEdgeDet;
assign sda = (sdaOut == 1'b0) ? 1'b0 : 1'bz;
assign sdaIn = sda;
// sync rst rsing edge to clk
always @(posedge clk) begin
if (rst == 1'b1)
rstPipe <= 2'b11;
else
rstPipe <= {rstPipe[0], 1'b0};
end
assign rstSyncToClk = rstPipe[1];
// debounce sda and scl
always @(posedge clk) begin
if (rstSyncToClk == 1'b1) begin
sdaPipe <= {`DEB_I2C_LEN{1'b1}};
sdaDeb <= 1'b1;
sclPipe <= {`DEB_I2C_LEN{1'b1}};
sclDeb <= 1'b1;
end
else begin
sdaPipe <= {sdaPipe[`DEB_I2C_LEN-2:0], sdaIn};
sclPipe <= {sclPipe[`DEB_I2C_LEN-2:0], scl};
if (&sclPipe[`DEB_I2C_LEN-1:1] == 1'b1)
sclDeb <= 1'b1;
else if (|sclPipe[`DEB_I2C_LEN-1:1] == 1'b0)
sclDeb <= 1'b0;
if (&sdaPipe[`DEB_I2C_LEN-1:1] == 1'b1)
sdaDeb <= 1'b1;
else if (|sdaPipe[`DEB_I2C_LEN-1:1] == 1'b0)
sdaDeb <= 1'b0;
end
end
// delay scl and sda
// sclDelayed is used as a delayed sampling clock
// sdaDelayed is only used for start stop detection
// Because sda hold time from scl falling is 0nS
// sda must be delayed with respect to scl to avoid incorrect
// detection of start/stop at scl falling edge.
always @(posedge clk) begin
if (rstSyncToClk == 1'b1) begin
sclDelayed <= {`SCL_DEL_LEN{1'b1}};
sdaDelayed <= {`SDA_DEL_LEN{1'b1}};
end
else begin
sclDelayed <= {sclDelayed[`SCL_DEL_LEN-2:0], sclDeb};
sdaDelayed <= {sdaDelayed[`SDA_DEL_LEN-2:0], sdaDeb};
end
end
// start stop detection
always @(posedge clk) begin
if (rstSyncToClk == 1'b1) begin
startStopDetState <= `NULL_DET;
startEdgeDet <= 1'b0;
end
else begin
if (sclDeb == 1'b1 && sdaDelayed[`SDA_DEL_LEN-2] == 1'b0 && sdaDelayed[`SDA_DEL_LEN-1] == 1'b1)
startEdgeDet <= 1'b1;
else
startEdgeDet <= 1'b0;
if (clearStartStopDet == 1'b1)
startStopDetState <= `NULL_DET;
else if (sclDeb == 1'b1) begin
if (sdaDelayed[`SDA_DEL_LEN-2] == 1'b1 && sdaDelayed[`SDA_DEL_LEN-1] == 1'b0)
startStopDetState <= `STOP_DET;
else if (sdaDelayed[`SDA_DEL_LEN-2] == 1'b0 && sdaDelayed[`SDA_DEL_LEN-1] == 1'b1)
startStopDetState <= `START_DET;
end
end
end
registerInterface u_registerInterface(
.clk(clk),
.addr(regAddr),
.dataIn(dataToRegIF),
.writeEn(writeEn),
.dataOut(dataFromRegIF),
.tb_readEn(tb_readEn),
.tb_writeEn(tb_writeEn),
.tb_addr(tb_addr),
.tb_dataIn(tb_dataIn),
.tb_dataOut(tb_dataOut)
);
serialInterface u_serialInterface (
.clk(clk),
.rst(rstSyncToClk | startEdgeDet),
.dataIn(dataFromRegIF),
.dataOut(dataToRegIF),
.writeEn(writeEn),
.regAddr(regAddr),
.scl(sclDelayed[`SCL_DEL_LEN-1]),
.sdaIn(sdaDeb),
.sdaOut(sdaOut),
.startStopDetState(startStopDetState),
.clearStartStopDet(clearStartStopDet)
);
endmodule