I2C总线协议的verilog实现

    最近一直在学习各种接口,今天要讲的是I2C 总线。I2C是是一种简单的同步串行总线。 它只需要两根线即可在连接于总线上的器件之间传送信息。
主器件用于启动总线传送数据,并产生时钟以开放传送的器件,此时任何被寻址的器件均被认为是从器件.在总线上主和从、发和收的关系不是恒定的,而取决于此时数据传送方向。如果主机要发送数据给从器件,则主机首先寻址从器件,然后主动发送数据至从器件,最后由主机终止数据传送;如果主机要接收从器件的数据,首先由主器件寻址从器件.然后主机接收从器件发送的数据,最后由主机终止接收过程。在这种情况下.主机负责产生定时时钟和终止数据传送。这也就是主器件对于从器件的两种操作,即写操作和读操作。
I2C总线的写时序如下所示:
I2C总线协议的verilog实现_第1张图片
读时序如下所示:
I2C总线协议的verilog实现_第2张图片
下面是verilog实现,用状态机实现主器件对从器件的读写,由于主器件的数据是来自于并行总线,所以写期间会涉及到并串转换,用一个任务实现;读期间会涉及到串并转换也用一个任务实现。这两个任务使用一个子状态机实现,具体见下面的代码:
module i2c(clk,rst,rd,wr,w_data,w_addr,sda,scl,ack);
parameter size_addr=8;
parameter size_data=8;
//主状态机的各个状态
parameter idle=4'd0,wr_ready=4'd1,wr_ctrl=4'd2,
          wr_addr=4'd3,wr_data=4'd4,rd_ctrl=4'd5,
		  rd_data=4'd6,stop=4'd7,ackn=4'd8;
//启动状态机的各个状态
parameter head_begin=2'b00,head_bit=2'b01,head_end=2'b10;
//停止状态及的各个状态
parameter stop_begin=2'b00,stop_bit=2'b01,stop_end=2'b10;
//并串转换状态机的各个状态
parameter sh8out_begin=4'd0,bit6=4'd1,bit5=4'd2,bit4=4'd3,
          bit3=4'd4,bit2=4'd5,bit1=4'd6,bit0=4'd7,sh8out_end=4'd8;
//串并转换状态机的各个状态
parameter sh8in_begin=4'd0,bit70=4'd1,bit60=4'd2,bit50=4'd3,
          bit40=4'd4,bit30=4'd5,bit20=4'd6,bit10=4'd7,bit00=4'd8,
		  sh8in_end=4'd9;
input clk,rst;
input rd,wr;

input [size_addr-1:0] w_addr;
input [size_data-1:0] w_data;

inout sda;
output scl;
output ack;
reg ack;
reg scl;

reg link_sda;//开关信号
reg link_write;
reg link_read;
reg link_start;
reg link_stop;

reg start_buf;
reg stop_buf;
reg [size_data-1:0]sh8out_buf;
reg [size_data-1:0]sh8in_buf;
wire [size_data-1:0]r_data;//读出的数据
reg FF;//任务完成标志

reg [3:0] mstate;//主状态机寄存器
reg [1:0] start_state;//启动状态寄存器
reg [1:0] stop_state;//结束状态寄存器
reg [3:0] sh8out_state;//并串转换状态寄存器
reg [3:0] sh8in_state;//串并转换状态寄存器

wire sda1,sda2,sda3,sda4;
assign sda1=(link_start==1)?1'b1:1'b0;
assign sda2=(link_stop==1) ?1'b1:1'b0;
assign sda3=(link_write==1)?sh8out_buf[7]:1'b0;
assign sda4=sda1|sda2|sda3;
assign sda=(link_sda==1)?sda4:1'bz;
assign r_data=(link_read==1)?sh8in_buf:8'hzz;

always@(posedge clk)//scl时钟产生逻辑
begin
if(!rst) scl<=0;
else scl<=~scl;
end

always@(posedge clk)
begin
 if(!rst)
 begin
   mstate<=idle;
   link_read<=0;
   link_write<=0;
   link_sda<=0;
   link_start<=0;
   ack<=0;
   link_stop<=0;
   FF<=0;
 end
 else 
 begin
  case(mstate)
  idle: 
       begin
	   mstate<=wr_ready;
	   start_state<=head_begin;
	   link_write<=0;
	   link_start<=1;
	   start_buf<=1;
	   ack<=0;
	   FF<=0;
	   end
  wr_ready:
       begin
	   if(FF==0) headstate;
	   else
	   begin
	    mstate<=wr_ctrl;
		FF<=0;
		sh8out_state<=sh8out_begin;
		sh8out_buf<=8'b1100_1011;
	   end
	   end
  wr_ctrl:
       begin
	   if(FF==0)sh8out;
	   else
	   begin
	   mstate<=wr_addr;
	   FF<=0;
	   sh8out_state<=sh8out_begin;
	   sh8out_buf<=w_addr;
	   end
	   end
  wr_addr:
       begin
	   if (FF==0)sh8out;
	   else
	   begin
	   FF<=0;
	   if(rd)
	    begin
		link_read<=1;
		mstate<=rd_ctrl;
		sh8out_buf<=8'b1001010;
		sh8out_state<=sh8out_begin;
		end
	   if(wr)
	    begin
		mstate<=wr_data;
		sh8out_buf<=w_data;
		sh8out_state<=sh8out_begin;
		link_write<=1;
		end
	   end
	   end
   wr_data:
        begin
		if(FF==0)sh8out;
		else
		begin
		mstate<=stop;
		stop_state<=stop_begin;
		link_stop<=1;
		stop_buf<=0;
		FF<=0;
		end
		end
   rd_ctrl:
        begin
		if(FF==0)sh8out;
		else
		begin
		FF<=0;
		mstate<=rd_data;
		sh8in_state<=sh8in_begin;
		end
		end
   rd_data:
        begin
		if(FF==0)sh8in;
		else 
		begin
		FF<=0;
		mstate<=stop;
		stop_buf<=0;
		link_stop<=1;
		stop_state<=stop_begin;
		end
		end
	
   stop:
        begin
		if(FF==0)stopstate;
		else
		begin
		 ack<=1;
		 mstate<=ack;
		 FF<=0;
		end
		end
   ackn:
        begin
		 mstate<=idle;
		 ack<=0;
		end
	default:mstate<=idle;
  endcase
 end
end

task headstate;
begin
case(start_state)
 head_begin:
       begin
	   if(!scl)
	   begin
	   link_sda<=1;
	   start_state<=head_bit;
	   end
	   else 
	   begin
	   start_state<=head_begin;
	   end
		 
	   end
 head_bit:
       begin
	   if(scl)
	    begin
		start_buf<=0;
		start_state<=head_end;
		end
	   else start_state<=head_bit;
	   end
 head_end:
       if(!scl)
       begin
	   link_sda<=0;
	   link_start<=0;
	   FF<=1;
	   start_state<=head_begin;
	   end
	   else start_state<=head_end;
endcase
end
endtask

task stopstate;
begin
 case(stop_state)
 stop_begin:
         if(!scl)
         begin
		  link_sda<=1;
		  stop_state<=stop_bit;
		 end
		 else stop_state<=stop_begin;
 stop_bit:
        begin
		 if(scl)
		 begin
		  stop_state<=stop_end;
		  stop_buf<=1;
		 end
		 else stop_state<=stop_bit;
		end
 stop_end:
      if(!scl)
      begin
	   link_sda<=0;
	   link_stop<=0;
	   FF<=1;
	   stop_state<=stop_begin;
	  end
	  else stop_state<=stop_end;
 endcase
end
endtask

task sh8out;
begin
 case(sh8out_state)
 sh8out_begin:
      begin
	  link_sda<=1;
	  link_write<=1;
	  sh8out_state<=bit6;
	  end
 bit6:
      begin
	  if(!scl)
	  begin
	  sh8out_buf<=sh8out_buf<<1;
	  sh8out_state<=bit5;
	  end
	  else sh8out_state<=bit6;
	  end
 bit5:
      begin
	  if(!scl)
	  begin
	  sh8out_buf<=sh8out_buf<<1;
	  sh8out_state<=bit4;
	  end
	  else sh8out_state<=bit5;
	  end
 bit4:
      begin
	  if(!scl)
	  begin
	  sh8out_buf<=sh8out_buf<<1;
	  sh8out_state<=bit3;
	  end
	  else sh8out_state<=bit4;
	  end
 bit3:
      begin
	  if(!scl)
	  begin
	  sh8out_buf<=sh8out_buf<<1;
	  sh8out_state<=bit2;
	  end
	  else sh8out_state<=bit3;
	  end
 bit2:
      begin
	  if(!scl)
	  begin
	  sh8out_buf<=sh8out_buf<<1;
	  sh8out_state<=bit1;
	  end
	  else sh8out_state<=bit2;
	  end
 bit1:
      begin
	  if(!scl)
	  begin
	  sh8out_buf<=sh8out_buf<<1;
	  sh8out_state<=bit0;
	  end
	  else sh8out_state<=bit1;
	  end
 bit0:
      begin
	  if(!scl)
	  begin
	  sh8out_buf<=sh8out_buf<<1;
	  sh8out_state<=sh8out_end;
	  end
	  else sh8out_state<=bit0;
	  end
 sh8out_end:
      begin
	  sh8out_state<=sh8out_begin;
	  FF<=1;
	  link_sda<=0;
	  link_write<=0;
	  end
 endcase
end
endtask


task sh8in;
begin
case (sh8in_state)
sh8in_begin:
     begin
	 link_sda<=1;
	 link_read<=0;
	 sh8in_state<=bit70;
	 end
bit70:
    begin
	if(scl)
	begin
	 sh8in_buf[7]<=sda;
	 sh8in_state<=bit60;
	end
	else sh8in_state<=bit70;
	end
bit60:
    begin
	if(scl)
	begin
	 sh8in_buf[6]<=sda;
	 sh8in_state<=bit50;
	end
	else sh8in_state<=bit60;
	end
bit50:
    begin
	if(scl)
	begin
	 sh8in_buf[5]<=sda;
	 sh8in_state<=bit40;
	end
	else sh8in_state<=bit50;
	end
bit40:
    begin
	if(scl)
	begin
	 sh8in_buf[4]<=sda;
	 sh8in_state<=bit30;
	end
	else sh8in_state<=bit40;
	end
bit30:
    begin
	if(scl)
	begin
	 sh8in_buf[3]<=sda;
	 sh8in_state<=bit20;
	end
	else sh8in_state<=bit30;
	end
bit20:
    begin
	if(scl)
	begin
	 sh8in_buf[2]<=sda;
	 sh8in_state<=bit10;
	end
	else sh8in_state<=bit20;
	end
bit10:
    begin
	if(scl)
	begin
	 sh8in_buf[1]<=sda;
	 sh8in_state<=bit00;
	end
	else sh8in_state<=bit10;
	end
bit00:
    begin
	if(scl)
	begin
	 sh8in_buf[0]<=sda;
	 sh8in_state<=sh8in_end;
	end
	else sh8in_state<=bit00;
	end
sh8in_end:
    begin
	link_sda<=0;
	 link_read<=1;
	FF<=1;
	sh8in_state<=sh8in_begin;
	end
default:begin
        link_read<=0;
		sh8in_state<=sh8in_begin;
        end
endcase
end
endtask



endmodule



你可能感兴趣的:(IC模块设计)