2021-08-21

FPGA IIC 总线实现及仿真(二)


前言

FPGA IIC 总线实现及仿真(一)中完成了FPGA的IIC总线实现,本篇文章主要完成IIC总线的仿真


一、仿真测试程序

仿真测试首先让FPGA IIC总线向IIC设备写入10个数据0x010x0a,然后再由FPGA从IIC设备相同的数据地址中读取10个数据,能否正确读取0x010x0a。

仿真测试程序如下

module Test_IIC(

    );
    reg clk;
    reg rst_p;
    
    wire SCL;    
    wire SDA;
    
    reg  WR;
    reg  WR_start;
    
    reg  [7:0]  data_in;    
    wire Write_ready;    
    wire Write_done;    
 
    
    wire  [7:0]  data_out;
    wire  Read_ready;       
    wire  Read_done;     
    
    IIC_Top IIC_Top(
    .clk(clk),     
    .rst_p(rst_p),   
    
    .SCL(SCL),    
    .SDA(SDA),    
    
    .WR(WR),           
    .WR_start(WR_start), 
    
    .data_in(data_in), 
    .Write_ready(Write_ready),    
    .Write_done(Write_done),     
    //input   [7:0]  Write_num, 
    
    .data_out(data_out),
    .Read_ready(Read_ready),       
    .Read_done(Read_done)      
    //input   [7:0]  Read_num  
    );
    
  ///
  initial  begin
      clk   = 0;
      rst_p = 1;
      
      #100
      rst_p = 0;
      
      #1000
      WR = 0;
      WR_start=1;
     
      
      #1000
      WR_start=0;
      
      #2000000
      WR = 1;
      WR_start=1;
      
      #1000
      WR_start=0;
  end
  
   always  #10  clk = ~clk ; 
/写数据/
   reg    Write_ready_r = 0;   
   
   always @(posedge clk) begin
    Write_ready_r<=Write_ready;
    if(WR_start==1)
        data_in<=8'h01;
    else
    if(Write_ready_r==0 && Write_ready==1)
        data_in <=data_in+1;
    end
  // 
  IIC_Slave   IIC_Slave
  (
    .clk(clk),       
    
    .SCL(SCL),    
    .SDA(SDA)    
    
  );
      
endmodule

在测试时,我们建立了一个IIC_Slave 模块来模拟IIC从设备,代码如下

module IIC_Slave(
    input clk,     //工作时钟  
   
    input  SCL,    //IIC 时钟信号
    inout  SDA    //IIC 数据信号    
      
    );
    
    
    parameter   write_addr      =   8'b1010_000_0;  //IIC 从设备的读地址
    parameter   read_addr       =   8'b1010_000_1;  //IIC 从设备的写地址 
       
/    
    reg     IIC_SCL    = 0;
    reg     IIC_SDAin  = 0;
    reg     IIC_SDAin_r= 0;
    reg     IIC_SDAout = 0;
    reg     IIC_SDAout_r = 0;
    reg     is_out     = 0;
    
    reg     WR_bit     = 0;   //0:写数据 1:读数据
    
    reg  [7:0]    Bit_cnt   = 0;//接收bit数 计数器

    reg  [7:0]    data_addr   = 0;
    reg  [7:0]    send_data  = 0;//写入的数据
    reg  [7:0]    recv_data  = 0;//读出的数据
    
    reg  [7:0]     data_mem  [0:255];  //数据存储
    
    integer i;  //数据初始化
    initial  begin       
        for (i=0; i<255 ; i=i+1) begin
            data_mem[i]<=0;
        end
     end
    
    reg  [3:0]    IIC_state          = 0;
    reg  [3:0]    IIC_next_state     = 0;
    localparam  [3:0]  Idle_state     = 0,
                       Start_state    = 1,//start信号                       
                       WrAddr_state   = 2,//设备写地址
                       DataAddr_state = 3,//设备读地址
                       ReStart_state  = 4,//读数据时,重新发送start信号
                       RdAddr_state   = 5,//数据地址
                       WrData_state   = 6,//写数据
                       RdData_state   = 7,//读数据
                       sACK_state     = 8,//从设备响应
                       mACK_state     = 9,//主设备响应
                       //NOP_state      = 10,//主设备非应答信号
                       Stop_state     = 11;//stop信号
   
 assign   SDA = (is_out==1) ? IIC_SDAout : 1'bz;  //is_out=1 时发送,is_out=0时 时输入
/IIC 状态机///    
    always @ (posedge clk)  begin
    IIC_SCL<=SCL;
    case(IIC_state)       
        Idle_state:begin
            Bit_cnt <= 0;        
            if(IIC_SDAin==0 && IIC_SDAin_r==1 && SCL==1)
               IIC_state<= WrAddr_state;
            else
               IIC_state<= Idle_state; 
        end
       
                
        WrAddr_state:begin    //写8bit设备写地址
            if(IIC_SCL==0 && SCL==1)
                Bit_cnt<= Bit_cnt+1;
                
            if(Bit_cnt==8 && SCL==0)
            begin
                WR_bit<=recv_data[0];
                IIC_state<= sACK_state;
                if(recv_data==write_addr)
                begin
                    IIC_SDAout_r<=0;                   
                    IIC_next_state<=DataAddr_state;
                end
                else
                begin
                    IIC_SDAout_r<=1;
                    IIC_next_state<=Idle_state;
                end
            end
       
        end
        
        DataAddr_state:begin    //写8bit设备读地址
            if(IIC_SCL==0 && SCL==1)
                Bit_cnt<= Bit_cnt+1;
                
            if(Bit_cnt==8 && SCL==0)
            begin
                IIC_state<= sACK_state;
                IIC_next_state<=WrData_state;
                IIC_SDAout_r<=0;
                //if(WR_bit==0)                    
                
                //else
                //    IIC_next_state<=ReStart_state;
            end
         
        end
        
         //ReStart_state:begin       
         //   if(IIC_SDAin==0 && IIC_SDAin_r==1 && IIC_SCL==1)
         //      IIC_state<= RdAddr_state;
         //   else
         //      IIC_state<= ReStart_state;           
        //end
        
        RdAddr_state:begin    //写8bit设备读地址
            if(IIC_SCL==0 && SCL==1)
                Bit_cnt<= Bit_cnt+1;
                
            if(Bit_cnt==8 && SCL==0)
            begin
                if(recv_data==read_addr)
                begin
                    IIC_SDAout_r<=0;
                    IIC_state<= sACK_state;
                    IIC_next_state<=RdData_state;
                end
                else
                begin
                    IIC_SDAout_r<=1;
                    IIC_state<= sACK_state;
                    IIC_next_state<=Idle_state;
                end
            end
        
        end
        
        WrData_state:begin    //读数据         
         if(IIC_SDAin==0 && IIC_SDAin_r==1 && IIC_SCL==1)//如果收到Restart信号,则进入RdAddr_state
               IIC_state<= RdAddr_state;
                     
         if(IIC_SDAin==1 && IIC_SDAin_r==0 && IIC_SCL==1)///如果收到Stop信号,则进入Idle_state
                IIC_state<= Idle_state;
               
            if(IIC_SCL==0 && SCL==1)
                Bit_cnt<= Bit_cnt+1;
                              
            if(Bit_cnt==8 && SCL==0)
            begin
                IIC_state<= sACK_state;                 
                IIC_next_state<=WrData_state;
                IIC_SDAout_r<=0;
            end          
        end
        
        RdData_state:begin    //读数据
            if(IIC_SCL==0 && SCL==1)
                Bit_cnt<= Bit_cnt+1;
                
            if(Bit_cnt==8 && SCL==0)
            begin
                IIC_state<= mACK_state;                                
            end
            
        end
        
        sACK_state:begin    //响应
            Bit_cnt <= 0;               
            if(IIC_SCL==0 && SCL==1) 
               IIC_state<= IIC_next_state;
               
            
  
        end
        
        mACK_state:begin    //等待响应
           Bit_cnt <= 0;
           if(IIC_SCL==0 && SCL==1)
           begin
                if(IIC_SDAin==0 )
                    IIC_state<= IIC_next_state;
                else
                    IIC_state<= Stop_state;    //如果未响应,则结束此次读写操作      
            end
        end
                   
        Stop_state:begin    
             if(IIC_SDAin==1 && IIC_SDAin_r==0 && IIC_SCL==1)
                IIC_state<= Idle_state;          
        end        
    endcase
    end
    
 SDA 时序/    
   
    always @ (posedge clk)  begin
        if(IIC_state==sACK_state || IIC_state==RdData_state)
                is_out<=1;
        else
                is_out<=0;
    end
    
    always @ (posedge clk)  begin
        if(is_out==0)
        begin
            IIC_SDAin_r<=IIC_SDAin;
            IIC_SDAin<=SDA;
        end
    end
    
    
   
    
     always @ (posedge clk)  begin
        case(IIC_state)
        sACK_state:begin
            IIC_SDAout<=IIC_SDAout_r;
        end
        RdData_state:begin
        if(IIC_SCL==1 && SCL==0)
         case(Bit_cnt)
             0:IIC_SDAout<=send_data[7];  //先发高位
             1:IIC_SDAout<=send_data[6];  
             2:IIC_SDAout<=send_data[5];  
             3:IIC_SDAout<=send_data[4];  
             4:IIC_SDAout<=send_data[3];  
             5:IIC_SDAout<=send_data[2];
             6:IIC_SDAout<=send_data[1];
             7:IIC_SDAout<=send_data[0]; 
         endcase      
        end
            
        default:
            IIC_SDAout<=1;
        endcase
     end
      读数据 时序/ 
    
     always @ (posedge clk)  begin
        //if(Bit_cnt==8 && SCL==0)
        //begin
            if( IIC_state==RdData_state )
               send_data<=data_mem[data_addr];
        //end
      end     
     
        
     写数据 时序/ 
     
    always @ (posedge clk)  begin
        if(IIC_state==WrAddr_state || IIC_state==DataAddr_state || IIC_state==RdAddr_state
        || IIC_state==WrData_state)
        begin
        
         if(IIC_SCL==0 && SCL==1)
         case(Bit_cnt)
             0:recv_data[7]<=IIC_SDAin;  //先收高位
             1:recv_data[6]<=IIC_SDAin;  
             2:recv_data[5]<=IIC_SDAin;  
             3:recv_data[4]<=IIC_SDAin;  
             4:recv_data[3]<=IIC_SDAin;
             5:recv_data[2]<=IIC_SDAin;
             6:recv_data[1]<=IIC_SDAin;
             7:recv_data[0]<=IIC_SDAin; 
         endcase      
      end
      end
      
      
      always @ (posedge clk)  begin
        if(Bit_cnt==8 && SCL==0)
        begin
            if( IIC_state==DataAddr_state )     
                data_addr<=recv_data;
            else if(IIC_state==WrData_state || IIC_state==RdData_state)
                data_addr<=data_addr+1;
        end
      end 
      
      always @ (posedge clk)  begin
        if(Bit_cnt==8 && SCL==0)
        begin
            if( IIC_state==WrData_state )
                data_mem[data_addr]<=recv_data;
        end
      end 
         
endmodule

二、仿真结果

首先,FPGA通过IIC总线向IIC设备写入数据,仿真结果如下图所示。图中上面红色圈为FPGA主设备时序,下边粉色圈为IIC从设备时序图。

IIC写时许

然后,FPGA在通过IIC总线从IIC设备的相同地址读入数据,仿真结果如下图所示。图中上面紫色圈为FPGA主设备时序,下边绿色圈为IIC从设备时序。

IIC读时序

总体读写时序如下图所示

IIC读写时序


FPGA IIC 总线实现及仿真(一)

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

mhlicq

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值