`timescale 1ns / 1ps
//
// Company:
// Engineer:
//
// Create Date: 09:42:12 09/06/2012
// Design Name:
// Module Name: I2C_Start
// Project Name:
// Target Devices:
// Tool versions:
// Description:
//
// Dependencies:
//
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
//
//
module I2C_Start(
SCL,
SDA,
led,
ostart,odcnt
);
input SCL;
inout SDA;
output led, ostart, odcnt;
// The 7-bits address that we want for our I2C slave
parameter I2C_ADR = 7'h73;
//---------------------------------------------
//test pin defination
//---------------------------------------------
wire led;
wire ostart, odcnt;
//---------------------------------------------
//start,stop condition judgement
//---------------------------------------------
//wire start, stop;
reg start, stop;
reg start1,start2;
reg stop1, stop2;
/*
reg sda1, sda2;
reg sda11;
always @ ( posedge SCL )
sda1 <= SDA;
always @ ( negedge SCL )
sda2 <= SDA;
always @ ( negedge SCL )
sda11 <= sda1;
assign start = sda11 & (!sda2);
assign stop = sda2 & ( !sda11 );
*/
always @ ( negedge SDA )
if ( SCL )
start1 <= 1;
else
start1 <= 0;
always @ ( posedge SCL )
start2 <= start1;
always @ ( negedge SCL )
if ( start1 == start2 )
start <= 0;
else
start <= 1;
always @ ( posedge SDA )
if ( SCL )
stop1 <= 1;
else
stop1 <= 0;
always @ ( posedge SCL )
stop2 <= stop1;
always @ ( negedge SCL )
if ( stop1 == stop2 )
stop <= 0;
else
stop <= 1;
//----------------------------------------------
//count setting
//----------------------------------------------
//
wire scldiv;
reg [10:0] divcnt;
always @( posedge SCL or posedge start)
if ( start )
divcnt <= #1 0;
else
divcnt <= #1 divcnt + 1;
assign scldiv = divcnt[10];
assign led = scldiv;
reg [3:0] adrcont;
wire adrcont_flag = adrcont[3];
always @ ( posedge SCL or posedge start)
begin
if ( start )
adrcont <= 4'h6;
else
begin
if (adrcont_flag)
adrcont <= 4'h6;
else
adrcont <= adrcont -4'h1;
end
end
//-------------------------------------------------
//data count
//-------------------------------------------------
reg ack_assert;
reg [3:0] data_cnt;
wire dcnt_flag = data_cnt[3];
reg data_phase;
wire addr_phase = ~data_phase;
always @ ( posedge SCL or posedge start ) //used to counter data which will be sent to master
if ( start ) //make sure dcnt_flag accurate by start signal
begin
data_cnt <= 4'h7;
//data_phase <= 0; //make the flag to distinguish data or address
end
else
begin
if( dcnt_flag )
begin
data_cnt <= 4'h7;
// data_phase <= 1;
end
else
data_cnt <= data_cnt - 4'h1;
end
always @ ( posedge start or posedge ack_assert )
if ( start )
data_phase <= 0;
else
data_phase <= 1;
//--------------------------------------
//get ack count
//--------------------------------------
reg [3:0] gacnt;
wire ga_flag = gacnt[3];
always @ ( negedge SCL ) //the count is used to generate read_ack
if(dcnt_flag) //which is used to detect master read OK ack
gacnt <= 4'h7;
else
gacnt <= gacnt - 1;
//-------------------------------------
//get sda using posedge scl
//-------------------------------------
reg sdar;
always @ ( posedge SCL ) sdar <= SDA;
//----------------------------------------
//address match
//----------------------------------------
reg addr_match, write_read;
always @ ( negedge SCL or posedge start )
begin
if ( start )
begin
addr_match <= 1'h1;
write_read <= 1'h0;
end
else
begin
if( (adrcont == 6) & (sdar != I2C_ADR[6])) addr_match <= 1'h0;
if( (adrcont == 5) & (sdar != I2C_ADR[5])) addr_match <= 1'h0;
if( (adrcont == 4) & (sdar != I2C_ADR[4])) addr_match <= 1'h0;
if( (adrcont == 3) & (sdar != I2C_ADR[3])) addr_match <= 1'h0;
if( (adrcont == 2) & (sdar != I2C_ADR[2])) addr_match <= 1'h0;
if( (adrcont == 1) & (sdar != I2C_ADR[1])) addr_match <= 1'h0;
if( (adrcont == 0) & (sdar != I2C_ADR[0])) addr_match <= 1'h0;
if( adrcont == 0 ) write_read <= sdar;
end
end
//-----------------------------------------------------------------------
//send ack
//-----------------------------------------------------------------------
//reg ack_assert;
always @ ( negedge SCL )
begin
if ( adrcont_flag & addr_match & write_read )
ack_assert <= 1'h1;
else
ack_assert <= 1'h0;
end
//---------------------------------------------------
//cycle data
//---------------------------------------------------
reg [7:0] cyc_data;
parameter i_data = 8'h36; //prepare send data
always @ ( posedge dcnt_flag or posedge ack_assert ) //make the data change by dcnt
if ( ack_assert )
cyc_data <= i_data;
else
begin
if(cyc_data < 8'hfe )
cyc_data <= cyc_data + 8'h06;
else
cyc_data <= i_data;
end
//----------------------------------------------------------------------
//parallel to serial
//----------------------------------------------------------------------
reg [7:0] read_mem;
reg send_data;
always @ ( posedge dcnt_flag or posedge SCL )
if ( dcnt_flag )
read_mem <= cyc_data; //set read_mem data periodically
else
read_mem <= read_mem << 1; //parallel to serial
always @ ( negedge SCL ) //parallel to serial
send_data <= read_mem[7];
//------------------------------------------
//send data
//------------------------------------------
reg data_low;
always @ ( negedge SCL )
if(data_phase)
data_low <= send_data;
else
data_low <= 1;
//---------------------------------
//read mode get ack
//---------------------------------
reg read_ack;
reg detect_ack;
always @ ( posedge SCL ) //read_ack is used to generate detect_ack signal accruately
if ( gacnt == 0)
read_ack <= 1;
else
read_ack <= 0;
always @ ( negedge SCL ) //detect_ack is a flag
if ( read_ack == 1 ) //use to free SDA line when master send read OK ack
detect_ack <= 1;
else
detect_ack <= 0;
//---------------------------------------
//stop detect
//---------------------------------------
reg flag_stop;
always @ ( posedge SCL )
if ( start == 1 )
flag_stop <= 1; //1 means no stop
else
begin
if ( stop == 1 )
flag_stop <= 0; //0 means there is stop
end
//-------------------------------------------------------------------------
//control SDA line
//-------------------------------------------------------------------------
//control SDA line by follow 4 conditions
assign SDA = ( flag_stop &( ack_assert | (!(data_low | detect_ack ) ) ) ) ? 1'h0 : 1'hz;
assign ostart = 1;
assign odcnt = 1;
endmodule
TestBench如下:
`timescale 1ns / 1ps
module test(
sda
);
reg scl;
inout sda;
reg sda_out;
wire sda_in;
reg [7:0] data;
reg start_flag, stop_flag;
assign sda = sda_out ? 1'bz : 1'b0;
assign sda_in = sda;
pullup( sda );
I2C_Start testmine(.SDA(sda), .SCL(scl));
initial
begin
scl = 0;
sda_out = 0;
data = 8'hE6;
start_flag = 0;
#160000;
start ( );
end
always
begin
#50000 scl = ~scl;
end
always @ (posedge start_flag)
begin
repeat (8)
begin
wait ( scl == 0 );
#20000;
sda_out = data[7];
#40000;
data = data << 1;
end
wait (~ scl);
#20000;
sda_out = 1;
#5000000;
stop ( );
end
always @ ( posedge stop_flag)
begin
// sda_out = 0;
// #50000;
sda_out = 1;
end
task start;
begin
wait (scl == 0);
#20000;
sda_out = 1;
wait ( scl == 1 );
#20000;
sda_out = 0;
start_flag = 1;
end
endtask
task stop;
begin
wait ( scl == 0 );
#20000;
sda_out = 0;
wait ( scl ==1 );
#20000;
sda_out = 1;
stop_flag = 1;
end
endtask
endmodule