基于SUMBus或I2C通信协议,使用vivado2017 modsim,循环执行写操作

基于SUMBus及I2C通信协议,使用vivado2017 simulation,循环执行写操作,使用Verilog HDL代码编写,代码注释非常全面,故不再使用文字描述。SUMBus是在I2C的基础上发展起来的通信协议,其读写操作根据相关specification文献可知相同。
@[TOC] sumbus/I2C module

`timescale 1ns / 1ps
//
// Company: 
// Engineer: 
// 
// Create Date: 2021/05/17 21:44:44
// Design Name: 
// Module Name: smbus_2
// Project Name: 
// Target Devices: 
// Tool Versions: 
// Description: 
// 
// Dependencies: 
// 
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
// 
//

/*本模块主always块的结构是:先写器件地址,然后写寄存器地址。寄存器地址写完后,判断读写EEPROM,
如果写就直接进行写数据,
如果读,则送读器件地址,然后送所读寄存器地址,然后再进行读数据/
*/

module smbus_2(
        input sys_clk,      //系统时钟500kHZ(2us)
        input sys_rst_n,
        output reg SMBCLK,         //SMBus工作时钟50KHZ   
        inout IO_SDA,                //SMBus数据
 		input	SMBus_en,					//SMBus使能		
        input	rd_or_wr,					//读写标志:读1,写0	
        		
        input [6:0] device_addr,			//SMBus器件地址
        input [15:0] SMBus_reg_addr,	   //寄存器地址
        input [7:0]    SMBus_reg_data,    //寄存器数据
        
        output reg [7:0] read_data,         //接收数据
        output reg     SMBus_done = 0           //SMBus完成标志   
        );
        
 //---------------------------------------------
                //分频部分
        reg    [2:0] cnt;    // cnt=0:SMBCLK上升沿,cnt=1:SMBCLK高电平中间,cnt=2:SMBCLK下降沿,cnt=3:SMBCLK低电平中间
        reg    [8:0] cnt_delay;    //100循环计数,产生iic所需要的时钟
//        reg     SMBCLK_POS = 0;
//        reg     SMBCLK_HIG = 0;
//        reg     SMBCLK_NEG = 0;
//        reg     SMBCLK_LOW = 0;
        
        always @ (posedge sys_clk or negedge sys_rst_n)
            if( !sys_rst_n ) cnt_delay <= 9'd0;
            else if( cnt_delay == 9'd99 ) cnt_delay <= 9'd0;    //计数到10us为SMBCLK的周期,即100KHz
            else cnt_delay <= cnt_delay+1'b1;    //时钟计数
        
        always @ (posedge sys_clk or negedge sys_rst_n) begin
                if( !sys_rst_n ) cnt <= 3'd5;
                else begin
                    case ( cnt_delay )
                        9'd24:    cnt <= 3'd1;    //cnt=1:SMBCLK高电平中间,用于数据采样
                        9'd49:    cnt <= 3'd2;    //cnt=2:SMBCLK下降沿
                        9'd74:    cnt <= 3'd3;    //cnt=3:SMBCLK低电平中间,用于数据变化
                        9'd99:    cnt <= 3'd0;    //cnt=0:SMBCLK上升沿
                        default: cnt <= 3'd5;
                        endcase
                    end                                       
              end

                    
        `define SMBCLK_POS        (cnt==3'd0)        //cnt=0:SMBCLK上升沿
        `define SMBCLK_HIG_MID        (cnt==3'd1)        //cnt=1:SMBCLK高电平中间,用于数据采样
        `define SMBCLK_NEG        (cnt==3'd2)        //cnt=2:SMBCLK下降沿
        `define SMBCLK_LOW_MID        (cnt==3'd3)        //cnt=3:SMBCLK低电平中间,用于数据变化

        
        //产生SMBus所需要的时钟
        always @ (posedge sys_clk or negedge sys_rst_n)
            if(!sys_rst_n) SMBCLK <= 1'b0;
            else if(cnt_delay==9'd1) SMBCLK <= 1'b1;
            else if(cnt==3'd0) SMBCLK <= 1'b1;    //SMBCLK信号上升沿
            else if(cnt==3'd2) SMBCLK <= 1'b0;    //SMBCLK信号下降沿
            else SMBCLK <= SMBCLK;
            
        //---------------------------------------------
                //读、写时序
        parameter     IDLE     = 4'd0;
        parameter     START1     = 4'd1;
        parameter     DEV_ADDR     = 4'd2;
        parameter     ACK1     = 4'd3;
        parameter     MEM_ADDR_1     = 4'd4;
        parameter     ACK2_1     = 4'd5;
        parameter     MEM_ADDR_2     = 4'd6;
        parameter     ACK2_2     = 4'd7;
        parameter     START2     = 4'd8;
        parameter     ADD3     = 4'd9;
        parameter     ACK3    = 4'd10;
        parameter     WRITE_PROC     = 4'd11;
        parameter     ACK4    = 4'd12;
        parameter     STOP1     = 4'd13;
        parameter     STOP2     = 4'd14;
  
        reg[7:0] BUFF_REG = 8'b0000_0000;		//SMBus上传输的数据寄存器,缓存作用
        reg[3:0] SMBus_State;    //状态寄存器
        reg IO_SDA_REG;        //输出数据寄存器
        reg IO_SDA_DIR;    //输出数据IO_SDA信号inout方向控制位,1'b0为input.1'b1为output	   
        reg[3:0] num;    // 计数作用   
    
assign IO_SDA = IO_SDA_DIR ? IO_SDA_REG:1'bz;	
    
always @ (posedge sys_clk or negedge sys_rst_n) begin
	if(!sys_rst_n) 
	    begin
			SMBus_State <= IDLE;//状态寄存器
			num <= 4'd0;
			
			IO_SDA_DIR <= 1'b0;//输出数据IO_SDA信号inout方向控制位  1'b0为input
			IO_SDA_REG <= 1'b1;//输出数据寄存器,IO_SDA线保持高电平		
			read_data <= 8'b0000_0000;//接收数据
			SMBus_done <= 1'b0;//IIC完成标志
		end
	else
		case (SMBus_State)
			IDLE:	begin                              //空闲状态
					IO_SDA_DIR <= 1'b0;					//数据线IO_SDA为input
					IO_SDA_REG <= 1'b1;         //IO_SDA拉高电平
					SMBus_done <= 1'b0;    //smbus clear完成标志
					
					if(SMBus_en) 
					   begin					//SMBus_en==1,开始操作			
                            BUFF_REG <= {device_addr,1'b0};	//从设备地址(1'b1读操作)
                            SMBus_State <= START1;		
						end
					else SMBus_State <= IDLE;				//没有任何键被按下
				end
			START1: begin                //Start状态
					if(`SMBCLK_HIG_MID) 
					   begin		//`SMBCLK_HIG_MID == 1, SMBCLK为高电平期间
                            IO_SDA_DIR <= 1'b1;	//数据线IO_SDA为output
                            IO_SDA_REG <= 1'b0;		//IO_SDA_REG输出数据寄存器,拉低数据线IO_SDA,产生起始位信号
                            SMBus_State <= DEV_ADDR;
                            num <= 4'd0;		//num计数清零
						end
					else SMBus_State <= START1; //等待SMBCLK高电平中间位置到来
				end
            DEV_ADDR:	
                begin
                    if(`SMBCLK_LOW_MID) 
                        begin
                            if(num == 4'd7)
                                 begin 
                                    IO_SDA_REG <= BUFF_REG[0];   
                                    num <= 4'd0;            //num计数清零                                   
                                    SMBus_State <= ACK1;
                                end
                            else 
                                begin
                                    SMBus_State <= DEV_ADDR;                                                                
                                    case (num)
                                        4'd0: IO_SDA_REG <= BUFF_REG[7];//BUFF_REG此时存的从设备地址,SMBus先传MSB
                                        4'd1: IO_SDA_REG <= BUFF_REG[6];
                                        4'd2: IO_SDA_REG <= BUFF_REG[5];
                                        4'd3: IO_SDA_REG <= BUFF_REG[4];
                                        4'd4: IO_SDA_REG <= BUFF_REG[3];
                                        4'd5: IO_SDA_REG <= BUFF_REG[2];
                                        4'd6: IO_SDA_REG <= BUFF_REG[1];                                   
                                        default: IO_SDA_REG <= 8'b0000_0000 ;
                                    endcase
                                    num <= num+1'b1;                                    
                                end
                                
                        end
                    else SMBus_State <= DEV_ADDR;
                end					
			ACK1:	begin
                        if(`SMBCLK_LOW_MID) begin        //SMBCLK低电平
                                IO_SDA_DIR <= 1'b0;        //IO_SDA置为高阻态(input)
                                SMBus_State <= MEM_ADDR_1;    //响应信号
                                IO_SDA_REG <= 1'b0;       //从机返回ACK
                                BUFF_REG <= SMBus_reg_addr[15:8];    // 高八位寄存器地址        
                            end
                        else SMBus_State <= ACK1;        //等待从机响应
                    end
            MEM_ADDR_1:    begin
                    if(`SMBCLK_LOW_MID) 
                        begin
                            if(num == 4'd7) 
                                begin  
                                    IO_SDA_REG <= BUFF_REG[0];     
                                    num <= 4'd0;            //num计数清零                                    
                                    SMBus_State <= ACK2_1;
                                end
                            else begin
                                    IO_SDA_DIR <= 1'b1;        //IO_SDA作为output
                                    case (num)
                                            4'd0: IO_SDA_REG <= BUFF_REG[7];
                                            4'd1: IO_SDA_REG <= BUFF_REG[6];
                                            4'd2: IO_SDA_REG <= BUFF_REG[5];
                                            4'd3: IO_SDA_REG <= BUFF_REG[4];
                                            4'd4: IO_SDA_REG <= BUFF_REG[3];
                                            4'd5: IO_SDA_REG <= BUFF_REG[2];
                                            4'd6: IO_SDA_REG <= BUFF_REG[1];                                           
                                            default: IO_SDA_REG <= 8'b0000_0000 ;
                                        endcase  
                                    num <= num+1'b1;      
                                    SMBus_State <= MEM_ADDR_1;                    
                                end
                        end
                    else SMBus_State <= MEM_ADDR_1;                
                end    
				ACK2_1:	begin
                        if(`SMBCLK_LOW_MID) begin        //
                                SMBus_State <= MEM_ADDR_2;    //从机响应信号
                                IO_SDA_DIR <= 1'b0;        //IO_SDA置为高阻态(input)
                                IO_SDA_REG <= 1'b0;       //返回ACK
                                BUFF_REG <= SMBus_reg_addr[7:0];    // 1地址        
                            end
                        else SMBus_State <= ACK2_1;        //等待从机响应
                    end    
                MEM_ADDR_2:    begin
                        if(`SMBCLK_LOW_MID) begin
                                if(num==4'd7) begin    
                                        IO_SDA_REG <= BUFF_REG[0];
                                        num <= 4'd0;            //num计数清零                                        
//                                        IO_SDA_DIR <= 1'b0;        //IO_SDA置为高阻态(input)
                                        SMBus_State <= ACK2_2;
                                    end
                                else begin
                                        IO_SDA_DIR <= 1'b1;        //IO_SDA作为output
                                        num <= num+1'b1;
                                        case (num)
                                            4'd0: IO_SDA_REG <= BUFF_REG[7];
                                            4'd1: IO_SDA_REG <= BUFF_REG[6];
                                            4'd2: IO_SDA_REG <= BUFF_REG[5];
                                            4'd3: IO_SDA_REG <= BUFF_REG[4];
                                            4'd4: IO_SDA_REG <= BUFF_REG[3];
                                            4'd5: IO_SDA_REG <= BUFF_REG[2];
                                            4'd6: IO_SDA_REG <= BUFF_REG[1];
                                            default: IO_SDA_REG <= 8'b0000_0000;
                                            endcase        
                                        SMBus_State <= MEM_ADDR_2;                    
                                    end
                            end
                        else SMBus_State <= MEM_ADDR_2;                
                    end                              
                    
                ACK2_2:    
                    begin
                        if(`SMBCLK_LOW_MID)
                          begin        //从机响应信号
                                IO_SDA_DIR <= 1'b0;        //IO_SDA置为高阻态(input)
                                IO_SDA_REG <= 1'b0;       //返回ACK                            
                                if(rd_or_wr)  //读写标志:读1,写0    
                                    begin   
                                        SMBus_State <= START2;            //读操作        
                                        BUFF_REG <= {device_addr,1'b1};    //送器件地址(读操作),特定地址读需要执行该步骤以下操作                                                
                                    end    
                                else 
                                    begin
                                        SMBus_State <= WRITE_PROC;             //写操作
                                        BUFF_REG <= SMBus_reg_data;    //写入的数据    
                                    end
                         end
                            else SMBus_State <= ACK2_2;    //等待从机响应
                     end
                WRITE_PROC:   
                 begin
                    if(rd_or_wr)
                         begin     //读操作
                            if(num<=4'd7) 
                                begin                        
                                    if(`SMBCLK_LOW_MID) begin    
                                        num <= num+1'b1;
                                        SMBus_State <= WRITE_PROC;   
                                        IO_SDA_DIR <= 1'b1; 
                                        case (num)
                                            4'd0: read_data[7] <= IO_SDA;//assign IO_SDA = IO_SDA_DIR ? IO_SDA_REG:1'bz;
                                            4'd1: read_data[6] <= IO_SDA;  //读操作还没设置好,不能实现同步接收
                                            4'd2: read_data[5] <= IO_SDA; 
                                            4'd3: read_data[4] <= IO_SDA; 
                                            4'd4: read_data[3] <= IO_SDA; 
                                            4'd5: read_data[2] <= IO_SDA; 
                                            4'd6: read_data[1] <= IO_SDA; 
                                            4'd7: read_data[0] <= IO_SDA; 
                                            default: IO_SDA_REG <= 8'b0000_0000;
                                           endcase                                                                        
                                        end
                                    else SMBus_State <= WRITE_PROC;
                                end
                            else if((`SMBCLK_LOW_MID) && (num==4'd8)) begin
                                num <= 4'd0;            //num计数清零
                                SMBus_State <= ACK4;
                                end
                            else SMBus_State <= WRITE_PROC;
                        end
                    else  begin    //写操作                            
                            if(num < 4'd7) 
                                begin
                                    SMBus_State <= WRITE_PROC;
                                    if(`SMBCLK_LOW_MID)
                                     begin  
                                        IO_SDA_DIR <= 1'b1;     //数据线IO_SDA作为output                                                                        
                                        case (num)
                                            4'd0: IO_SDA_REG <= BUFF_REG[7];
                                            4'd1: IO_SDA_REG <= BUFF_REG[6];
                                            4'd2: IO_SDA_REG <= BUFF_REG[5];
                                            4'd3: IO_SDA_REG <= BUFF_REG[4];
                                            4'd4: IO_SDA_REG <= BUFF_REG[3];
                                            4'd5: IO_SDA_REG <= BUFF_REG[2];
                                            4'd6: IO_SDA_REG <= BUFF_REG[1];                                                
                                            default: IO_SDA_REG <= 8'b0000_0000;
                                            endcase    
                                        num <= num+1'b1;                                
                                      end
                                end
                            else if((`SMBCLK_LOW_MID) && (num==4'd7)) 
                                begin
                                    IO_SDA_REG <= BUFF_REG[0];
                                    num <= 4'd0;                                    
//                                    IO_SDA_DIR <= 1'b0;        //IO_SDA置为高阻态
                                    SMBus_State <= ACK4;
                                end
                            else SMBus_State <= WRITE_PROC;
                        end
                end
                            
                ACK4: begin
                        if(`SMBCLK_LOW_MID) begin
                            SMBus_State <= STOP1;
                            IO_SDA_DIR <= 1'b0;        //IO_SDA置为高阻态(input)  
                            IO_SDA_REG <= 1'b0;                      
                            end
                        else SMBus_State <= ACK4;
                    end                                             
                            
                STOP1:    begin
                        if(`SMBCLK_LOW_MID)
                             begin
                                IO_SDA_DIR <= 1'b1;
                                SMBus_State <= STOP2;
                                #10_0000 IO_SDA_REG <= 1'b1;//SMBCLK为高时,IO_SDA产生上升沿(结束信号)
                                                               
                            end
                        else SMBus_State <= STOP1;
                    end
                    
                STOP2:    begin    //
                        if(`SMBCLK_LOW_MID)
                         begin
                            IO_SDA_DIR <= 1'b0;                                         
                            SMBus_done <= 1'b1;
                             #20_0000 SMBus_State <= IDLE;
//                            SMBus_State <= SMBus_State+ 1'b1;
                        end
                        else SMBus_State <= STOP2;
                        
                    end
                default: SMBus_State <= IDLE;
                endcase
    end		

endmodule


@[TOC] sumbus/I2C Testbench

`timescale 1ns / 1ps
//
// Company: 
// Engineer: 
// 
// Create Date: 2021/05/05 09:57:24
// Design Name: 
// Module Name: testbench_2
// Project Name: 
// Target Devices: 
// Tool Versions: 
// Description: 
// 
// Dependencies: 
// 
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
// 
//


module testbench_2(

    );
    
    reg sys_clk ;
    reg sys_rst_n = 1'b0;
    reg [6:0] device_addr = 7'b0010_110;
    reg SMBus_en = 1'b0;
    reg rd_or_wr = 0;//1读0写
    
    reg [15:0] SMBus_reg_addr = 16'b1011_1000_1001_0010;           //SMBus完成标志   
    reg [7:0]  SMBus_reg_data = 8'b1011_0001;
    wire IO_SDA;
    wire SMBCLK;
    smbus_2 smbus_2_init(
        . sys_clk(sys_clk),          // Signal of Clock Input 
        . sys_rst_n(sys_rst_n),         // Hardware Reset Pin, Active Low
        . IO_SDA(IO_SDA),
        . SMBCLK(SMBCLK),  
        . device_addr(device_addr), //slave device address
        . SMBus_en(SMBus_en),
        . rd_or_wr(rd_or_wr),
        . SMBus_reg_addr(SMBus_reg_addr),
        . SMBus_reg_data(SMBus_reg_data)
        );
    
    initial begin
        sys_clk = 1'b0;
 
        sys_rst_n = 1'b0;
        # 10
        SMBus_en = 1'b1;
        sys_rst_n = 1'b1;
    end
    
    always # 1000 sys_clk = ~sys_clk;  //500kHz(2us)   
     
     /****Test_Vector****/
    reg  [6:0]  R_State;    
    reg  [7:0]  WR_Data; 
    reg  [7:0]  RD_Data;
     //建立新的三态门
    reg         SDA_R_REG;
    reg         SDA_SET;
    wire        IO_SDA_S;
    
    assign      IO_SDA_S = (SDA_SET) ? SDA_R_REG : 1'bz;  
      
    //两个三态门相连
    assign      IO_SDA=IO_SDA_S;
     
   always @ (posedge SMBCLK or negedge sys_rst_n)
    begin
      if (~sys_rst_n)  
           begin
            R_State<=7'd0;
            WR_Data<=8'b0;
            RD_Data<=8'b0101_0110;
            SDA_SET<=1'b0; //默认为输入
            SDA_R_REG <= 1'b1;           
           end
      else
       begin
         case(R_State)
           7'b0,7'd1,7'd2,7'd3,7'd4,7'd5,7'd6,7'd7: //RST状态,接收从机地址,和读写标志
              R_State <= R_State + 7'd1;  
           7'd8: //第9个脉冲周期,延迟150000到下一个脉冲的低电平中间,返回ACK信号
              begin
                #150000  SDA_SET<=1'b1;//延迟一个周期,三态转输出// IO_SDA_S = (SDA_SET) ? SDA_R_REG : 1'bz; 
                SDA_R_REG<=1'b0;
                R_State<=R_State+7'd1;
              end
           7'd9://恢复
              begin
               #150000 SDA_SET<=1'b0; //三态门转输入
                SDA_R_REG<=1'b1;
                R_State<=R_State+7'd1;
              end
           7'd10,7'd11,7'd12,7'd13,7'd14,7'd15,7'd16://接收寄存器高8位地址
                  R_State<=R_State+7'd1;
          7'd17:
             begin
               #150000 SDA_SET<=1'b1;返回ACK信号
               SDA_R_REG<=1'b0;
               R_State<=R_State+7'd1;
              end                
          7'd18: //恢复
             begin
                #150000 SDA_SET<=1'b0; //三态门转输入
                SDA_R_REG<=1'b1;
                R_State<=R_State+7'd1;                  
             end
          7'd19,7'd20,7'd21,7'd22,7'd23,7'd24,7'd25://接收寄存器低8位地址
                  R_State<=R_State+7'd1;
          7'd26:
             begin
               #150000 SDA_SET<=1'b1;返回ACK信号
               SDA_R_REG<=1'b0;
               R_State<=R_State+7'd1;
              end                
          7'd27: //恢复
             begin
                #150000 SDA_SET<=1'b0; //三态门转输入
                SDA_R_REG<=1'b1;
                    R_State<=R_State+7'd1;                  
             end
           7'd28,7'd29,7'd30,7'd31,7'd32,7'd33,7'd34:     //接收数据                 
               begin
                  WR_Data<={WR_Data[6:0],IO_SDA};
                  R_State<=R_State+7'd1; 
                  end
           7'd35:
              begin
                   WR_Data<={WR_Data[6:0],IO_SDA};
                   #150000 SDA_SET<=1'b1;//返回ACK信号
                   SDA_R_REG<=1'b0;
                   R_State<=R_State+7'd1;
              end
            7'd36: //恢复
                 begin
                    #150000 SDA_SET<=1'b0; //三态门转输入
                    SDA_R_REG<=1'b1;
                        R_State<=R_State+7'd1;                  
                 end 
             7'd37: //终止位
                begin
                   #50000 SMBus_en=1'b0;    //关闭模块

                     #20_0000   R_State<=7'd0; 
                     #20_0000 SMBus_en=1'b1; //Start开启  进行一次完整读过程                
                end                              
			default:
                   R_State<=6'b0;
              endcase
            
            end
  end
    
    
endmodule

下图为仿真结果
基于SUMBus或I2C通信协议,使用vivado2017 modsim,循环执行写操作_第1张图片图中IO_SDA蓝色线段为停止传送时刻的高阻态

你可能感兴趣的:(I2C,verilog,fpga)