I2C 接受模块起始信号捕捉的问题

这个问题我纠结了好久,本来以为写对了的代码,结果发现再添加了延时操作以后,时序仿真中出现了延拍现象,导致后面的时序都错了。今天找到了解决方法,特此写一个博客记录一下:
首先我想到的是方法如下:

always@( posedge scl )
begin
    s0 <= sda;
end

always@( negedge scl )
begin
    s1 <= sda;
end

always@( negedge scl )  
begin
    if( (s0 == 1 &  s1 == 0 ) )
        start_state <= 1;
    else if ( s0 == 0 & s1 == 1  )
        stop_state  <= 1; 
    else begin
        start_state <= 0;
        stop_state  <= 0;
    end
end

在时序仿真的时候,上段代码在没有加上#2延时的时候居然没有问题….囧…归结原因就是start_state这个寄存器通过某种形式与s1形成了由同一个时钟沿构成了级联方式,这样的理论上start_state,以及stop_state都会延时一拍,到下个时钟下降沿才会出现正脉冲。而不加#2延时符号,仿真器还识别不出来这个设计瑕疵,加上延时,就可以看到时序全部乱了。

后来想用组合逻辑来解决,代码如下:

always@( sda or scl )
begin
   //用来捕捉SCL高电平处,SDA下降的状态机
    if( scl == 0  ) begin
        capture_start_state = start_step1;
        start_state = 0;
    end
    else if( scl == 1 && sda == 1 && capture_start_state == start_step1 )
        capture_start_state = start_step2;
    else if( scl == 1 && sda == 0 && capture_start_state == start_step2  )
    begin
        start_state = 1;
        capture_start_state = start_step1;
    end
end

以上定义了一个状态机,当scl的时候start_state 以及状态机初始化,当scl与sda同时为1的时候调到step2态,如果step2态中在一个scl = 1与sda = 0,那么就可以判断start_state为0了
I2C 接受模块起始信号捕捉的问题_第1张图片
但是这个start信号宽度太窄,想了很多办法,都没法做到使得start_state脉冲能够持续半个以上SCL周期。有同学想到办法可以跟我交流一下。所以这种方法,也只能放弃/(ㄒoㄒ)/~~

最后一种方法还是网上查到的方法

always@( posedge scl )
begin
    s0 <= sda;
end

always@( negedge scl )
begin
    s1 <= sda;
end

always@( negedge scl )
begin
    s2 <= s0;
end

assign start_state = ( s2 ) & ( !s1 ); 
assign stop_state  = ( s1 ) & ( !s2 );

首先仍然是采样上升沿信号s0与下降沿信号s1,然后在下降沿的时候采样s0得到s2信号,这样做的作用是将
s0信号延时半个周期,s2与s1也就可以进行对齐的比较了,比较的结果,就是持续一个下降沿到下降沿周期的一个结果,这个结果是该下降沿所对应的SCL高电平期的两个沿的布尔结果(s2 & ( ! s1) 或者 !(s2) & s1)或是启动信号或者是终止信号,在SCL高电平上存在电平跳变的时候,start_state 或者stop_state这两根线上,会出现持续一个下降沿到下降沿的周期跳变
I2C 接受模块起始信号捕捉的问题_第2张图片
时序图如上,如果加上延时,更能反映真实时序了。

最后是我同事写的一个代码

        always@( posedge scl )
        s0 <= sda;

        always@( negedge scl )
        s1 <= sda;

        always@( posedge scl )begin //将s0信号延时半拍形成,此时s2信号如果 
            start_state <= ( ( s0 ) && ( !s1 ) );
            stop_state <= ( ( s1 ) && ( !s0) );
        end

start_state 与 stop_state 在上升沿处进行s0与s1的布尔逻辑判断,由于采集s0的时候在在上升沿后延时一定时间,s0才会发生跳变,即假设采集到s0的时钟沿是第一个上升沿,在第二上升沿的时候,start_state与stop_state进行布尔运算的时候对应的s0仍然是第一个上升沿的采样值,依旧是说,在此处进行的布尔运算的s0和s1正是我们对应SCL高电平两个沿的SDA值,所以如果SCL高电平期间SDA变动,将会得到从正沿到正沿的一个周期的start_state 或者 stop_state的脉冲信号。 时序如下:
I2C 接受模块起始信号捕捉的问题_第3张图片
这种情况下,我们如果接受主always块中,还是以上升沿作为敏感信号,那么不能准确的捕捉到start_state信号,因为start_state会相对正沿会有一定的延时,那么此时捕捉到的应该是上个正沿的start_state的值,如果我们以负沿作为敏感信号,那么可以稳定的捕捉到start_state信号,那么需要我的主程序有一定的修改。

——————————–2016.5.5—————————————————
今天针对这个问题又发现了一点新的东西如下,针对start_state出现的不定态的情况,这是由于start_state <= ( ( s0 ) && ( !s1 ) );这句话,由于s0 ,s1在某一时刻确实可能同时为不定态,那么按照真值表不定态相与,为不定态,所以,在SDA上出现一个周期的高阻的时候,start_state信号会变成不定态,这种情况当然最好不要出现的。所以我想利用三态逻辑?:来实现一个判断,代码如下:

    always@( posedge scl )
        //s0 <=  sda?1:0;
        if( sda == 1 )
            s0 <= 1 ;
        else 
            s0 <= 0 ;

        always@( negedge scl )
        s1 <=  sda?1:0;

        assign w_start =  ( s0 == 1 && s1 ==0 )?1:0,
                 w_stop  =   ( s0 == 0 && s1 == 1 )?1:0;
        always@( posedge scl )begin //将s0信号延时半拍形成,此时s2信号如果 
            start_state <= (w_start)?1:0;
            stop_state  <= ( w_stop )?1:0; 
        end 

我的想法当然是s0 == 1 && s1 == 0 这个布尔表达式如果不等于1,则为1,否则就为0,
但这样情况s0为不定态,以及s1为不定态的时候,w_start线还是会出现不定态。

而下面这段代码,却没有问题

always@(posedge scl )
        s1 <= sda ? 1 : 0;

    always@(negedge scl )
        s2 <= sda ? 1 : 0;

    always@(posedge scl )
        start_state <= start_flag;

    always@(*)
    begin
        if(s1 == 1 && s2 == 0)
            start_flag = 1;
        else
            start_flag = 0;
    end

这段代码可以得到没有不定态的结果。

原因我想了想,按照某个视频教程上说的,s2 <= sda ? 1 : 0,应该被综合成一个与门,即sda与1进行与。
那么问题就来了,如果sda为高阻的时候,sda与1的结果是不定态,这就造成了最后有s1上有不定态产生,同理s2,w_start上也是一样。 而下面的if else语句十有八九是综合出了比较器,那么只要上面的布尔表达式的值不等于1,那么start_flag就置为0.

那么从根本上杜绝这种出现不定态的情况,就从s0和s1信号着手解决

        always@( posedge scl )
        if( sda == 1 )
            s0 <= 1 ;
        else  
            s0 <= 0 ;

        always@( negedge scl )
        if( sda == 1 )
            s1 <= 1;
        else
            s1 <= 0;

        assign w_start =  ( s0 == 1 && s1 ==0 )?1:0,
                 w_stop  =   ( s0 == 0 && s1 == 1 )?1:0;
        always@( posedge scl )begin //将s0信号延时半拍形成,此时s2信号如果 
            start_state <= (w_start)?1:0;
            stop_state  <= ( w_stop )?1:0; 
        end 

I2C 接受模块起始信号捕捉的问题_第4张图片

这里可以看到,不定态问题彻底消除啦。。

你可能感兴趣的:(verilog)