Verilog的I2C实现

I2C的Verilog实现有几个需要注意的地方:

  1. SDA是双向口,在Verilog中应声明为inout sda;这里就涉及到了inout口的使用方式
    assign sda = (link_write)? sh8out_buf[7] : 1'bz;

    一般都是使用三态门控制双向口,需要先声明一个link_write寄存器,用于判断sda是输入还是输出,若link_write为高则sda为输出,输出sh8out_buf[7],否则sda为z,即是高阻态,作为输入口,接受外来数据。
  2. 根据I2C协议的规定,sda数据线若不是在表示开始或停止位时,就只能在scl的低电平发生数据变化(详情看I2C详解 )。这里有一个非常巧妙的办法。
    always @(negedge CLK)         
        if(!RESET)
              SCL <= 0;
            else             
              SCL <= ~SCL;
    CLK的下降沿来触发SCL时钟,CLK的上升沿来触发SDA,这样SDASCL就相差了半个 SCL 周期,这样就满足了I2C总线上的传输协议。
  3. Task语句块的使用,任务就是一段封装在“task-endtask”之间的程序。任务是通过调用来执行的,而且只有在调用时才执行,如果定义了任务,但是在整个过程中都没有调用它,那么这个任务是不会执行的。调用某个任务时可能需要它处理某些数据并返回操作结果,所以任务应当有接收数据的输入端和返回数据的输出端。另外,任务可以彼此调用,而任务内还可以调用函数。
    task task_id;
        [declaration]
        procedural_statement
    endtask 

(1)在第一行“task”语句中不能列出端口名称;

(2)任务的输入、输出端口和双向端口数量不受限制,甚至可以没有输入、输出以及双向端口。

(3)在任务定义的描述语句中,可以使用出现不可综合操作符合语句(使用最为频繁的就是延迟控制语句) ,但这样会造成该任务不可综合。

(4)在任务中可以调用其他的任务或函数,也可以调用自身。

(5)在任务定义结构内不能出现 initial和 always过程块。

(6)在任务定义中可以出现“disable 中止语句” ,将中断正在执行的任务,但其是不可综合的。当任务被中断后,程序流程将返回到调用任务的地方继续向下执行。

如例所示:

task shift8_out;
begin
casex(sh8out_state)
         sh8out_bit7:      //将 sh8out_buf[7]给 sda
                if(!SCL) //scl 为低电平时给 sda 赋值,这是 i2c 总线时序要求的
                begin   
                  link_sda <= YES;
                  link_write <= YES;
                  sh8out_state <= sh8out_bit6;
                end       
                  else       
                  sh8out_state  <= sh8out_bit7;                         
                  sh8out_bit6:    //将 sh8out_buf[6]给 sda
                     if(!SCL)   
                        begin   
                          link_sda <= YES;
                          link_write <= YES;
                          sh8out_state <= sh8out_bit5;   
                          sh8out_buf <= sh8out_buf<<1;
                        end      
                     else       
                          sh8out_state <= sh8out_bit6;                     
                          sh8out_bit5:    //将 sh8out_buf[5]给 sda
                            if(!SCL)   
                                begin   
                                  sh8out_state   <= sh8out_bit4;   
                                  sh8out_buf    <= sh8out_buf<<1;
                                end       
                            else     
                                sh8out_state   <= sh8out_bit5;       
                                sh8out_bit4:    //将 sh8out_buf[4]给 sda
                            if(!SCL)   
                                begin   
                                  sh8out_state  <= sh8out_bit3;
                                  sh8out_buf    <= sh8out_buf<<1;
                                end         
                            else       
                                sh8out_state < = sh8out_bit4;   
                          sh8out_bit3:    //将 sh8out_buf[3]给 sda
                            if(!SCL)   
                                begin   
                                   sh8out_state   <= sh8out_bit2;   
                                   sh8out_buf    <= sh8out_buf<<1;   
                                end         
                            else       
                                sh8out_state   <= sh8out_bit3;
                          sh8out_bit2:    //将 sh8out_buf[2]给 sda
                            if(!SCL)   
                                begin   
                                    sh8out_state  <= sh8out_bit1;   
                                    sh8out_buf  <= sh8out_buf<<1;     
                                end         
                            else       
                               sh8out_state   <= sh8out_bit2;   
                          sh8out_bit1:    //将 sh8out_buf[1]给 sda
                            if(!SCL)   
                                begin   
                                    sh8out_state   <= sh8out_bit0;   
                                    sh8out_buf    <= sh8out_buf<<1;   
                                end         
                            else       
                                    sh8out_state   <= sh8out_bit1;     
                          sh8out_bit0:    //将 sh8out_buf[0]给 sda
                             if(!SCL)   
                                begin   
                                    sh8out_state   <= sh8out_end;   
                                    sh8out_buf    <= sh8out_buf<<1;   
                                end         
                            else     
                                    sh8out_state   <= sh8out_bit0;
                          sh8out_end:    //将 sda 上的数据保持一个 scl 周期,保持数据的完整性
                             if(!SCL) //到下一次 scl 为低电平时,正好为一个 scl 周期   
                                begin  
                                     link_sda <= NO;
                                     link_write <= NO;   
                                     FF <= 1;
                                end         
                            else           
                                     sh8out_state <= sh8out_end;           
          endcase           
    end   
endtask


  • 4
    点赞
  • 64
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值