I2C Slave Mode

自己写的,stop侦测还有问题待更新
`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



  • 1
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值