非常感谢大家的收藏,最近项目略忙,之前记录在confluence上的内容也比较零散,因此,这篇文章一定会出来的,有时间自己就开始写。写这样一篇文章进行总结也是非常有必要的
前言
本篇主要记录自己在使用Synopsys的IP DW_apb_i2c时的体会。顾名思义,这个IP是基于I2C总线协议开发的,自己作为verifer的经验不足一年,所以一开始最困扰我的问题是I2C协议和这个IP有什么关系?(听起来能提出这样的问题是挺蠢的)
对此,我的理解是,I2C协议是信息传递的一种规则,而DW_apb_i2c是基于verilog开发的满足I2C协议要求的硬件实体。网上关于I2C协议的解读已经很多了,这篇主要讲这个IP怎么使用。能够使用这个IP的前提是理解协议。
I2C协议简要回顾
这里给出一些自己看到的还不错的参考文章
对于I2C,需要掌握的基本的点:
- I2C支持的speed mode,基本的协议规则,I2C的状态机图
- start,restart条件,7/10bit address 格式要求
- I2C多master仲裁,时钟同步和时钟扩展
- Device ID
- SMBus/PMBus
这些都可以在I2C spec上找到,需反复研读,协议并不复杂
I2C的速率模式需要明确(当时大老板提问时问了这个问题)
speed mode | rate | SCL cycle |
Standard-mode | <=100kb/s | >=10^4 ns |
Fast-mode | <=400kb/s | >=2500 ns |
Fast-mode plus | <=1000kb/s | >=1000 ns |
High-speed mode | <=3.4Mb/s | >=295 ns |
Ultra Fast-mode | <=5Mb/s | >=200 ns |
DW_apb_i2c介绍
DW_apb_i2c可以说是S家的一个非常小的IP了,但即便如此,其中很多点也需要去琢磨。个人感觉能写出这样一个IP的代码,也是非常牛逼的。
关于这个IP的介绍,CSDN上有一些数量不太多的博客,希望自己踩在前人的肩膀上对其使用做一个补充。
- DesignWare DW_apb_i2c Databook
- 原海青木大哥的几篇文章 第二章 DW_apb_i2c 介绍_i2c designware-CSDN博客
- DW_apb_i2c学习笔记之IP微架构-CSDN博客
- Synopsys DW_apb_i2c_databook 笔记_synopsys i2c-CSDN博客
DW_apb_i2c,具有slave APB接口,上层通过APB接口去配置DW_apb_i2c的寄存器 ,从而基于I2C协议传递数据。对于这个IP的使用,主要是掌握四种模式的program方法,
- master transmitter
- master receiver
- slave transmitter
- slave receiver
本文将Dw_apb_i2c和svt_i2c_vip进行连接,从而实现四种模式下的验证,VIP的使用又说来话长,仅记录一下相关的config。
svt_i2c_vip介绍
VIP使用的Synopsys的svt_i2c_vip,基于uvm的验证VIP,具体怎么使用主要还是要看VIP的user guide。VIP的验证环境大致如下图:
svt_i2c_vip提供的最上层的是system_env,在这个system_env中可以例化多对master_agent和slave_agent。由system_configuration进行配置。具体的代码可以参考VIP提供的example,对VIP的使用还是比较简单明了的。下面给出一个config的例子
function new (string name="Cm2tb_i2c_system_cfg");
super.new(name);
// Assign the necessary configuration parameters.
this.num_masters = 2;
this.num_slaves = 2;
/** Create port configurations */
this.create_sub_cfgs(this.num_masters, this.num_slaves);
/** Set mode DUT is master and VIP is slave, so master agent is passive*/
this.master_cfg[0].is_active = 1;
this.slave_cfg[0].is_active = 1;
this.master_cfg[1].is_active = 1;
this.slave_cfg[1].is_active = 1;
set_bus_speed(FAST_MODE);
this.master_cfg[0].master_code = 3'b101; // only High speed use
this.master_cfg[1].master_code = 3'b001; // only High speed use
this.slave_cfg[0].slave_address = 10'b0001001011; // 10'b000 1001011 0x4b
this.slave_cfg[0].enable_10bit_addr = 0; // 10bit_addr disable
this.slave_cfg[0].slave_type = `SVT_I2C_GENERIC;
this.slave_cfg[0].device_id = 24'h44_55_66
this.slave_cfg[1].slave_address = 10'b0001101010; // 10'b000 1001011 0x6a
this.slave_cfg[1].enable_10bit_addr = 0; // 10bit_addr disable
this.slave_cfg[1].slave_type = `SVT_I2C_GENERIC;
this.slave_cfg[1].device_id = 24'h77_88_99;
this.enable_chk_for_xz_sda_at_time0 = 1'b0;
this.enable_chk_for_xz_scl_at_time0 = 1'b0;
endfunction : new
关于IP和VIP连接的问题
提到I2C,都知道涉及到两根线SDA和SCL,这两根线都是被上拉电阻拉着的inout类型的线。但是DW_apb_i2c与SCL和SDA有关的端口不是简简单单的两根线,而是分为ic_clk_in_a,ic_data_in_a,ic_clk_oe,ic_data_oe。其中ic_clk_oe,ic_data_oe是开漏输出(open_drain)。
而svt_i2c_vip提供的interface是SCL和SDA两根inout类型的线,IP和VIP如何连接是第一个要解决的问题。这里需要非常清楚的明白开漏输出的含义和意义。学习开漏输出和inout类型的信号的关系。
下面是集电极开路(Open-Collector)OC门的例子,漏极开路(Open-Drain)OD门的道理和这个类似,只不过用的器件不同而已。OC门用的是双极性晶体管(三极管的一种),OD门用的是MOSFET(场效应晶体管)(之前在协议和代码注释里总是看到open drain却不明白啥意思)
开漏输出的设计在I2C器件的端口是必须的,一是为了防止多个I2C器件挂在I2C总线上时发生短路,二是开漏输出可以实现多个I2C器件的“线与”(wire-and)模式。同时开漏输出外面的总线必须加上拉电阻以输出高电平。这点在I2C协议里有提到过。
连接代码如下:
assign Mtop.u_DW_apb_i2c.ic_clk_in_a = i2c_if.SCL;
assign Mtop.u_DW_apb_i2c.ic_data_in_a = i2c_if.SDA;
assign i2c_if.SCL = Mtop.u_DW_apb_i2c.ic_clk_oe? 1'b0 : 1'bz;
assign i2c_if.SDA = Mtop.u_DW_apb_i2c.ic_data_oe? 1'b0 : 1'bz;
当ic_clk_oe为1时,SCL为低电平。但ic_clk_oe为高时,输出为高阻态,而SCL又加了上拉电阻,所以表现为高电平。SDA同理。通过上述连接方式将dw_apb_i2c挂到svt_i2c_vip提供的I2C总线上。
验证环境:
通过前述内容,我们解决了如下问题:
- 如何把IP连接到I2C总线上(开漏输出的连接代码逻辑)
- 如何把多个VIP的master agent和slave agent挂到I2C总线上(通过配置VIP的system cfg)
最终我们搭建的环境其实是模拟了多个master或者slave的I2C器件挂在I2C总线上进行通讯。其中蓝色的是IP,黄色的是VIP。它们都是支持I2C协议的器件。而我们的验证就是模拟这些器件之间的通讯,比如通过配置IP的寄存器使其作为master发送数据给某个slave VIP。 当然这种验证行为本质是没啥意义的,因为IP是人家公司的产品,可以认为是golden的,验肯定是验不出来什么问题。但是在实际的soc系统中,i2c是被集成到某个子系统,比如外设子系统,再向上集成经过总线fabric,最终到芯片端口的是SCL和SDA的inout类型的pin,我们再走一遍这样的验证,就可以确认内部的I2C发送数据是否能发送出去。 验证,是在没有芯片实体的代码阶段去模拟数据的发送和接受。
搭建完验证环境之后,我们会遇到如下的问题:
- 如何配置dw_apb_i2c,使其发送或接收数据?
- 如何配置svt_i2c_vip,使其发送或接收数据?
根据master和slave关系,可以分为四种情况:
- dw_apb_i2c为master transmit mode,发送数据给VIP
- dw_apb_i2c为master receive mode,从VIP读数据
- dw_apb_i2c为slave transmit mode,被VIP读数据
- dw_apb_i2c为slave receive mode,被VIP写入数据
dw_apb_i2c作为master,svt_i2c_vip作为slave
master transmit mode
待续
master receive mode
待续
dw_apb_i2c作为master,svt_i2c_vip作为slave
slave transmit mode
待续