I2c Bit Controller in Verilog
I2c Bit Controller in Verilog
////
////
////
//// WISHBONE rev.B2 compliant I2C Master bitcontroller
////
////
////
////
////
//// Author: Richard
Herveille
////
////
[email protected]
////
////
www.asics.ws
////
////
////
//// Downloaded from:
http://www.opencores.org/projects/i2c/
////
////
////
/////////////////////////////////////////////////////////////////
////
////
////
//// Copyright (C) 2001 Richard
Herveille
////
////
[email protected]
////
////
////
//// 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 SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT
ANY
////
//// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED
////
//// TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
FITNESS
////
//// FOR A PARTICULAR PURPOSE. IN NO EVENT SHALL THE
AUTHOR
////
//// OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
INDIRECT,
////
arbitration lost (AL bit set), when another master controls the
bus and the other master generates a STOP bit.
//
//
Revision 1.10 2003/08/09 07:01:33 rherveille
//
Fixed a bug in the Arbitration Lost generation
caused by delay on the (external) sda line.
//
Fixed a potential bug in the byte controller's
host-acknowledge generation.
//
//
Revision 1.9 2003/03/10 14:26:37 rherveille
//
Fixed cmd_ack generation item (no bug).
//
//
Revision 1.8 2003/02/05 00:06:10 rherveille
//
Fixed a bug where the core would trigger an
erroneous 'arbitration lost' interrupt after being reset, when
the reset pulse width < 3 clk cycles.
//
//
Revision 1.7 2002/12/26 16:05:12 rherveille
//
Small code simplifications
//
//
Revision 1.6 2002/12/26 15:02:32 rherveille
//
Core is now a Multimaster I2C controller
//
//
Revision 1.5 2002/11/30 22:24:40 rherveille
//
Cleaned up code
//
//
Revision 1.4 2002/10/30 18:10:07 rherveille
//
Fixed some reported minor start/stop generation
timing issuess.
//
//
Revision 1.3 2002/06/15 07:37:03 rherveille
//
Fixed a small timing bug in the bit
controller.\nAdded verilog simulation environment.
//
//
Revision 1.2 2001/11/05 11:59:25 rherveille
//
Fixed wb_ack_o generation bug.
//
Fixed bug in the byte_controller statemachine.
//
Added headers.
//
//
/////////////////////////////////////
// Bit controller section
/////////////////////////////////////
//
// Translate simple commands into SCL/SDA transitions
// Each command has 5 states, A/B/C/D/idle
//
// start: SCL ~~~~~~~~~~\____
//
SDA ~~~~~~~~\______
//
x | A | B | C | D | i
//
// repstart
SCL ____/~~~~\___
//
SDA __/~~~\______
//
x | A | B | C | D | i
//
// stop
SCL ____/~~~~~~~~
//
SDA ==\____/~~~~~
//
x | A | B | C | D | i
//
//- write SCL ____/~~~~\____
//
SDA ==X=========X=
//
x | A | B | C | D | i
//
//- read
SCL ____/~~~~\____
//
SDA XXXX=====XXXX
//
x | A | B | C | D | i
//
// Timing:
Normal mode
Fast mode
/////////////////////////////////////////////////////////////////
//////
// Fscl
100KHz
400KHz
// Th_scl
4.0us
0.6us
High period of SCL
// Tl_scl
4.7us
1.3us
Low period of SCL
// Tsu:sta
4.7us
0.6us
setup time for a repeated
start condition
// Tsu:sto
4.0us
0.6us
setup time for a stop
conditon
// Tbuf
4.7us
1.3us
Bus free time between a
stop and start condition
//
// synopsys translate_off
//`include "timescale.v"
// synopsys translate_on
//`include "i2c_master_defines.v"
`define I2C_CMD_NOP
4'b0000
`define I2C_CMD_START 4'b0001
`define I2C_CMD_STOP 4'b0010
`define I2C_CMD_WRITE 4'b0100
`define I2C_CMD_READ 4'b1000
`define idle 18'b0_0000_0000_0000_0000
`define start_a 18'b0_0000_0000_0000_0001
`define start_b 18'b0_0000_0000_0000_0010
`define start_c 18'b0_0000_0000_0000_0100
`define start_d 18'b0_0000_0000_0000_1000
`define start_e 18'b0_0000_0000_0001_0000
`define stop_a 18'b0_0000_0000_0010_0000
`define stop_b 18'b0_0000_0000_0100_0000
`define stop_c 18'b0_0000_0000_1000_0000
`define
`define
`define
`define
`define
`define
`define
`define
`define
stop_d 18'b0_0000_0001_0000_0000
rd_a 18'b0_0000_0010_0000_0000
rd_b 18'b0_0000_0100_0000_0000
rd_c 18'b0_0000_1000_0000_0000
rd_d 18'b0_0001_0000_0000_0000
wr_a 18'b0_0010_0000_0000_0000
wr_b 18'b0_0100_0000_0000_0000
wr_c 18'b0_1000_0000_0000_0000
wr_d 18'b1_0000_0000_0000_0000
module i2c_master_bit_ctrl
(clk,rst,nReset,ena,clk_cnt,cmd,cmd_ack,busy,al,din,dout,scl_i,sc
l_o,scl_oen,sda_i,sda_o,sda_oen);
input clk,
// system clock
input rst,
// synchronous active high reset
input nReset,
// asynchronous active low reset
input ena,
// core enable signal
input [15:0] clk_cnt,
input [ 3:0] cmd,
output reg cmd_ack,
output reg busy,
output reg al,
input din,
output reg dout,
input scl_i,
// i2c clock line input
output scl_o,
// i2c clock line output
output reg scl_oen, // i2c clock line output enable (active
low)
input sda_i,
// i2c data line input
output sda_o,
// i2c data line output
output reg sda_oen
// i2c data line output enable (active
low)
//
// variable declarations
//
reg [ 1:0] cSCL, cSDA;
reg [ 2:0] fSCL, fSDA;
reg
and SDA inputs
reg
sSDA
reg
reg
arbitration)
reg
reg
reg [15:0]
(synthesis)
reg [13:0]
sSCL, sSDA;
dSCL, dSDA;
dscl_oen;
sda_chk;
// delayed scl_oen
// check SDA output (Multi-master
clk_en;
slave_wait;
cnt;
filter_cnt;
clk_en = 1'b1;
end
else if (slave_wait)
begin
cnt
= cnt;
clk_en = 1'b0;
end
else
begin
cnt
= cnt - 16'h1;
clk_en = 1'b0;
end
else if (rst)
begin
fSCL = 3'b111;
fSDA = 3'b111;
end
else if (~|filter_cnt)
begin
fSCL = {fSCL[1:0],cSCL[1]};
fSDA = {fSDA[1:0],cSDA[1]};
end
1'b1;
1'b1;
end
else if (rst)
begin
sSCL = 1'b1;
sSDA = 1'b1;
dSCL =
dSDA =
end
else
begin
sSCL =
sSDA =
dSCL =
dSDA =
1'b1;
1'b1;
end
// detect start condition => detect falling edge on SDA while
SCL is high
// detect stop condition => detect rising edge on SDA while
SCL is high
reg sta_condition;
reg sto_condition;
always @(posedge clk or negedge nReset)
if (~nReset)
begin
sta_condition = 1'b0;
sto_condition = 1'b0;
end
else if (rst)
begin
sta_condition
sto_condition
end
else
begin
sta_condition
sto_condition
end
=
=
1'b0;
1'b0;
=
=
// generate statemachine
// nxt_state decoder
always @(posedge clk or negedge nReset)
if (!nReset)
begin
c_state = idle;
cmd_ack = 1'b0;
scl_oen = 1'b1;
sda_oen = 1'b1;
sda_chk = 1'b0;
end
else if (rst | al)
begin
c_state = idle;
cmd_ack = 1'b0;
scl_oen = 1'b1;
sda_oen = 1'b1;
sda_chk = 1'b0;
end
else
begin
cmd_ack
= 1'b0; // default no command acknowledge +
assert cmd_ack only 1clk cycle
if (clk_en)
case (c_state) // synopsys full_case parallel_case
// idle state
idle:
begin
case (cmd) // synopsys full_case
parallel_case
`I2C_CMD_START: c_state = `start_a;
`I2C_CMD_STOP: c_state = `stop_a;
`I2C_CMD_WRITE: c_state = `wr_a;
1
`I2C_CMD_READ: c_state = `rd_a;
default:
c_state = `idle;
endcase
state
state
output
scl_oen =
sda_oen =
sda_chk =
1'b0;
end
// start
`start_a:
begin
c_state =
scl_oen =
`start_b;
scl_oen; // keep SCL in same
state
sda_oen =
sda_chk =
output
1'b1;
1'b0;
end
output
`start_b:
begin
c_state
scl_oen
sda_oen
sda_chk
=
=
=
=
`start_c;
1'b1; // set SCL high
1'b1; // keep SDA high
1'b0; // don't check SDA
=
=
=
=
`start_d;
1'b1; // keep SCL high
1'b0; // set SDA low
1'b0; // don't check SDA
=
=
=
=
`start_e;
1'b1; // keep SCL high
1'b0; // keep SDA low
1'b0; // don't check SDA
=
=
=
=
=
`idle;
1'b1;
1'b0; // set SCL low
1'b0; // keep SDA low
1'b0; // don't check SDA
=
=
=
=
`stop_b;
1'b0; // keep SCL low
1'b0; // set SDA low
1'b0; // don't check SDA
end
output
`start_c:
begin
c_state
scl_oen
sda_oen
sda_chk
end
output
`start_d:
begin
c_state
scl_oen
sda_oen
sda_chk
end
`start_e:
begin
c_state
cmd_ack
scl_oen
sda_oen
sda_chk
output
output
end
// stop
`stop_a:
begin
c_state
scl_oen
sda_oen
sda_chk
end
output
`stop_b:
begin
c_state
scl_oen
sda_oen
sda_chk
=
=
=
=
`stop_c;
1'b1; // set SCL high
1'b0; // keep SDA low
1'b0; // don't check SDA
=
=
=
=
`stop_d;
1'b1; // keep SCL high
1'b0; // keep SDA low
1'b0; // don't check SDA
=
=
=
=
=
`idle;
1'b1;
1'b1; // keep SCL high
1'b1; // set SDA high
1'b0; // don't check SDA
=
=
=
=
`rd_b;
1'b0; // keep SCL low
1'b1; // tri-state SDA
1'b0; // don't check SDA
=
=
=
=
`rd_c;
1'b1; // set SCL high
1'b1; // keep SDA tri-stated
1'b0; // don't check SDA
end
output
`stop_c:
begin
c_state
scl_oen
sda_oen
sda_chk
end
`stop_d:
begin
c_state
cmd_ack
scl_oen
sda_oen
sda_chk
output
output
end
// read
`rd_a:
begin
c_state
scl_oen
sda_oen
sda_chk
end
output
`rd_b:
begin
c_state
scl_oen
sda_oen
sda_chk
end
`rd_c:
begin
c_state
scl_oen
sda_oen
sda_chk
output
output
= `rd_d;
= 1'b1; // keep SCL high
= 1'b1; // keep SDA tri-stated
= 1'b0; // don't check SDA
end
`rd_d:
begin
c_state
cmd_ack
scl_oen
sda_oen
sda_chk
=
=
=
=
=
`idle;
1'b1;
1'b0; // set SCL low
1'b1; // keep SDA tri-stated
1'b0; // don't check SDA
=
=
=
=
`wr_b;
1'b0; // keep SCL low
din; // set SDA
1'b0; // don't check SDA
=
=
=
=
`wr_c;
1'b1; // set SCL high
din; // keep SDA
1'b0; // don't check SDA
end
// write
`wr_a:
begin
c_state
scl_oen
sda_oen
sda_chk
output (SCL low)
end
`wr_b:
begin
c_state
scl_oen
sda_oen
sda_chk
output yet
=
=
=
=
`wr_d:
begin
c_state =
cmd_ack =
scl_oen =
`wr_d;
1'b1; // keep SCL high
din;
1'b1; // check SDA output
`idle;
1'b1;
1'b0; // set SCL low
sda_oen =
sda_chk =
output (SCL low)
end
din;
1'b0; // don't check SDA
end
endcase