002 I2C Verilog实现源码解析

源码地址:http://www.opencores.org/projects/i2c/
时序图在线绘制工具:https://wavedrom.com/
绘图工具:https://app.diagrams.net/

1 框架结构

002 I2C Verilog实现源码解析_第1张图片

i2c_master_top

在这里插入图片描述
002 I2C Verilog实现源码解析_第2张图片

  • 读的时候多写了一次设备地址S_RD_DEV_ADDR1,与第一次不同的是,地址的最低位是1,表示读
  • S_WR_ERR_NACK:写了S_WR_DEV_ADDR、S_RD_DEV_ADDR0,却没有ACK
  • start:S_WR_DEV_ADDR、S_RD_DEV_ADDR0、S_RD_DEV_ADDR1
  • stop:S_WR_STOP、S_RD_STOP
  • read:S_RD_DATA
  • write:S_WR_DEV_ADDR -> S_WR_DATA,S_RD_DEV_ADDR0 -> S_RD_REG_ADDR1
  • i2c_al:仲裁失败(arbitrament lose),The stop signal is detected but no signal is requested.The host setting SDA is high,Actual SDA is low
  • done:决定状态跳转
i2c_master_byte_ctrl

002 I2C Verilog实现源码解析_第3张图片
八位并行转串行

always @(posedge clk or negedge nReset)
begin
  if (!nReset) // asynchronous reset
    sr <= #1 8'h0; //the"#"just for modelsim,it's not used when Analysis & Synthesis
  else if (rst==1) // synchronous reset
    sr <= #1 8'h0;
  else if (ld)  // load data
    sr <= #1 din; // write data to sda
  else if (shift)                               
    sr <= #1 {sr[6:0], core_rxd}; // read data from sda
end

八位计数器

always @(posedge clk or negedge nReset)
begin
  if (!nReset)
    dcnt <= #1 3'h0;
  else if (rst)
    dcnt <= #1 3'h0;
  else if (ld)                                 
    dcnt <= #1 3'h7;
  else if (shift)
    dcnt <= #1 dcnt - 3'h1;
end

assign cnt_done = ~(|dcnt);
  • core_ack:更新大条件
  • cnt_done:更新小条件,更新8位后可进入下一状态
  • core_cmd:更新操作:I2C_CMD_NOP、I2C_CMD_START、I2C_CMD_STOP、I2C_CMD_WRITE、I2C_CMD_READ
  • cmd-ack:i2c_master_top中的done,决定状态跳转
i2c_master_bit_ctrl

对串行后的每一位进行输出,每一位的输出可分成5个阶段(a、b、c、d、i),start多了一个e,其中i是idle

002 I2C Verilog实现源码解析_第4张图片
开始,读写,停止的时序如下:
在这里插入图片描述
在这里插入图片描述
start
002 I2C Verilog实现源码解析_第5张图片
stop
002 I2C Verilog实现源码解析_第6张图片
read
002 I2C Verilog实现源码解析_第7张图片
read里的SDA是高阻态,不是1,这样slave可以控制SDA,输出数据给master
write
002 I2C Verilog实现源码解析_第8张图片

  • 每个都是5个状态,所以它更新的频率是5 * fSCL,如果fSCL=100Khz,sys_clk=50,那么clk_div=99
  • start是开始,所以多了个e,e过后是i,但这个i其实是后面那个状态(read,write)的
信号前处理

SDA、SCL的数据并没有直接用,而是经历了很多filter
002 I2C Verilog实现源码解析_第9张图片
Capture

always @(posedge clk or negedge nReset)
begin
    if (!nReset)
    begin
       cSCL <= #1 2'b00;
       cSDA <= #1 2'b00;
    end
    else if (rst)
    begin
       cSCL <= #1 2'b00;
       cSDA <= #1 2'b00;
    end
    else
    begin
       cSCL <= {cSCL[0],scl_i};
       cSDA <= {cSDA[0],sda_i};
    end
end

Filter

always @(posedge clk or negedge nReset)//fSCL is the Operation control clock
begin
    if(!nReset)
    begin
       fSCL <= 3'b111;
       fSDA <= 3'b111;
    end
    else if (rst)
    begin
       fSCL <= 3'b111;
       fSDA <= 3'b111;
    end
    else if (~|filter_cnt)             
    begin
       fSCL <= {fSCL[1:0],cSCL[1]};
       fSDA <= {fSDA[1:0],cSDA[1]};
    end
 end
  • filter_cnt频率是fSCL频率的四倍

Synchronized、Delayed

always @(posedge clk or negedge nReset)
begin
    if (~nReset)
    begin
       sSCL <= #1 1'b1;
       sSDA <= #1 1'b1;
  
       dSCL <= #1 1'b1;
       dSDA <= #1 1'b1;
    end
    else if (rst)
    begin
       sSCL <= #1 1'b1;
       sSDA <= #1 1'b1;
       dSCL <= #1 1'b1;
       dSDA <= #1 1'b1;
    end
    else
    begin
     sSCL <= #1 &fSCL[2:1] | &fSCL[1:0] | (fSCL[2] & fSCL[0]);
       sSDA <= #1 &fSDA[2:1] | &fSDA[1:0] | (fSDA[2] & fSDA[0]);
       dSCL <= #1 sSCL;
       dSDA <= #1 sSDA;
    end
end 

// generate dout signal (store SDA on rising edge of SCL)
always @(posedge clk)
    if(sSCL & ~dSCL) 
		dout <= #1 sSDA;
  • 四倍频率采样SCL,使用三个连续位决策输出,进行同步

仲裁失败(Aribitration lost)

//仲裁丢失:当检测到停止信号,没有检测到请求信号,主机设置SDA为高电平,但实际上SDA为低电平
always @(posedge clk or negedge nReset)  
begin
    if(~nReset)
       al <= #1 1'b0;
    else if(rst)
       al <= #1 1'b0;
    else
       al <= #1 (sda_chk & ~sSDA & sda_oen)|(|c_state & sto_condition & ~cmd_stop);
end

你可能感兴趣的:(FPGA)