I2C_VIP

继APB总线协议之后再来看一下串行总线的I2C传输协议,体验一下串行总线在数据传输的复杂度

I2C总线协议

概述

只有两条同步串行总线:串行数据线(SDA),串行时钟线(SCL)

主器件用于启动总线传输数据,产生时钟以开发传送的器件,此时任何被寻址的器件都被认为是从器件

image-20210810222806466

在总线上主和从,发和收的关系不是恒定的,而是取决于此时数据传送方向

主机发数据给从机:则主机寻址从器件,然后发送数据至从器件,最后由主机终止数据传送

主机接收从器件数据:则主机寻址从器件,然后接收从器件发送的数据,最后由主机终止数据传送

主机负责产生定时时钟和终止数据传送,核心在主机

例如:控制系统就可以作为主机,传感器作为从机

特征

I2C总线是一个真正的多主机总线,如果两个或多个主机同时初始化数据传输,可以通过冲突检测和仲裁防止数据破坏

每个连接到总线上的器件都有唯一的地址,任何器件既可以作为主机也可以作为从机

数据传输和地址设定由软件设定,非常灵活。总线上的器件增加和删除不影响其他器件正常工作

1
假如希望有多个微控制器(MCU)将数据记录到单个存储卡或将文本显示到单个LCD时,这个功能就非常有用。

地址

I2C总线上的每一个设备都对应一个唯一地址,主从设备直接的数据传输时建立在地址的基础上,也就是说,主设备在传输有效数据之前要先指定从设备的地址,地址指定的过程和上面数据传输的过程一样,只不过大多数从设备的地址是7位的

不是1次传一个字节吗,为啥是7位的地址?

协议规定再给地址添加一个最低位用来表示接下来数据传输的方向,0表示主设备向从设备写数据,1表示主设备向从设备读数据。

Screenshot_20210811_000123_tv.danmaku.bili

硬件层

两条数据线需要接上拉电阻

img

I²C总线(SDASCL)内部都使用漏极开路驱动器(开漏驱动),因此SDASCL 可以被拉低为低电平,但是不能被驱动为高电平,所以每条线上都要使用一个上拉电阻,默认情况下将其保持在高电平;

img

  • 连接到总线的设备的输出级必须有一个漏极开路或集电极开路来执行“线与“功能。
  • 只有单个主设备时,如果总线上没有设备拉伸时钟,则主设备的SCL输出可以是推挽式设计

起始和截止条件

总线数据传输必须以一个起始信号为开始条件,一个结束信号为传输停止条件,这两个信号由主设备产生。

起始和结束信号产生条件:总线在空闲状态时,SCL和SDA都保持高电平

  • 当SCL为高电平而SDA由高到低跳变时,表示产生一个起始条件;
  • 当SCL为高而SDA由低到高跳变时,产生一个停止条件

在起始条件产生后,总线处于忙状态,由本次数据传输的主从设备独占,其他I2C器件无法访问总线;停止条件产生后,数据传输的主从设备释放总线,总线再次处于空闲

image-20210810234457187

应答(ACK)&非应答(NACK)

响应应答信号

主设备在SCL线上产生每个时钟脉冲的过程中将在SDA线上传输一个数据位,当一个字节数据位从高到低传输完后,从设备拉低SDA线,即应答位(ACK),表明字节已成功接收,可以发送下一个字节;

主设备产生所有时钟脉冲,包括第九个确认(ACK)时钟脉冲。

在第九个确认脉冲时钟脉冲期间,发送方将释放SDA总线,从而接收方可以把SDA拉低, 而且在脉冲高电平期间,SDA需持续拉低,定义为应答信号(ACK)

Screenshot_20210810_235506_tv.danmaku.bili

非响应应答信号

当SDA在第九个时钟脉冲期间保持为高时,定义为非应答信号(NACK)。

从端没有对该请求做出响应,主服务器可以生成终止传输的停止条件,或者生成启动新传输的重复启动条件。

Screenshot_20210810_235456_tv.danmaku.bili

传输操作

主设备往从设备中写数据

Screenshot_20210811_000842_tv.danmaku.bili

主设备在从设备中读数据

Screenshot_20210811_000911_tv.danmaku.bili

主设备往从设备中写数据,然后重启起始条件,紧接着在从设备中读取数据

Screenshot_20210811_000921_tv.danmaku.bili

主设备在从设备中读数据,然后重启起始条件,紧接着往从设备中写数据

为啥要重启起始条件呢?

因为每次起始的第一个8拍,第8位的读写位就定死了数据的传输方向,只能重启

数据有效性

  • SDA线上的数据在SCL高电平必须是稳定的。
  • 只有当SCL时钟信号为低时,SDA的数据才可以发生变化。
  • 每传输一个数据位产生一个时钟脉冲。

image-20210811001743880

时钟同步和仲裁

同步

两个主设备可以同时在空闲的总线上开始传送数据,必须有一种方法来决定哪一个来控制总线并完成它的数据传送,这是通过时钟同步和仲裁完成的。

时钟同步是连接到SCL总线的I2C接口的“线与”实现的;

当SCL总线从高电平拉低时,主设备将计数他们的低电平时钟,一旦一个主设备时钟拉低,其将把SCL总线也拉低,直到主设备时钟再次拉高;

然而如果另一个主设备的时钟仍旧是拉低的,SCL总线将保持拉低状态;因此,SCL总线被有最长低周期的主设备保持在低电平。在此期间,低周期较短的主设备将进入高电平等待状态。

当所有相关的主设备时钟拉高时,主设备时钟和SCL总线的状态就没有区别了,所有的主时钟开始计算它们的高周期;第一个结束高周期的主设备会将SCL线再次拉低。 这样就产生了一个同步的SCL时钟,它的低周期由低电平最长的主设备时钟确定,而它的高周期由高电平最短的主设备时钟确定。

image-20210811002857044

仲裁

仲裁和同步一样,只有在系统中使用多个主设备时才需要;从设备不参与仲裁程序。

  • 只有在总线空闲的情况下,主设备才可以发起传送。

  • 两个主机可以在启动条件的最小保持时间(tHD;STA)内生成一个启动条件,从而在总线上生成一个有效的启动条件。然后仲裁程序决定哪一个主设备来完成它的传输。

  • 一个主机每发送1个bit数据,在scl处于高电平时,就检查看SDA的电平是否和发送的数据一致,如果不一致,这个主机便知道自己输掉仲裁,然后停止向SDA写数据。也就是说,如果主机一直检查到总线上数据和自己发送的数据一致,则继续传输,这样在仲裁过程中就保证了赢得仲裁的master不会丢失数据。

    仲裁输掉的主机在检测到自己输了后也不再产生时钟脉冲,并且在总线空闲时才能重写传输

  • 仲裁的过程可能要经过多个bit的发送和检查,实际上如果两个主机发送的时序和数据完全一致,则两个主机都能正常完成整个数据传输

image-20210811004346655

显示了两个主设备的仲裁过程;

  • 当产生DATA1的主设备数据与SDA线上的实际数据有差异时,DATA1输出被关闭。这并不影响获胜主设备发起的数据传输。

  • 由于I2C总线的控制完全取决于竞争主设备发送的地址和数据,因此没有中央主设备,总线上也没有任何优先级顺序

总线清零

  • 在一些概率极低的情况中,时钟(SCL)会一直卡在低电平,如果I2C设备有硬件复位输入,优先考虑用硬件复位信号重置总线,如果I2C设备没有硬件复位输入,

    则激活强制的内部通电复位(POR)电路。

  • 如果数据线(SDA)一直卡在低电平,主设备应该发送9个时钟脉冲。控制总线拉低的设备应该在这9个时钟内释放它。如果没有效果,则使用硬件复位或循环电源来清除总线。

时钟拉伸

时钟拉伸通过保持SCL行较低来暂停事务;期间传输不能继续,直到SCL再次被拉高。

在字节传输级别,设备可能能够以较快的速度接收字节数据,但是需要更多的时间来存储收到的字节或准备传输另一个字节。在字节发送的(ACK)位之后,从设备

可以拉低SCL总线,迫使主设备进入等待状态,直到从设备准备好下一个字节的传输(参见图7)。

在比特级别,诸如微控制器这样的设备,无论是否为i2c总线提供有限的硬件,都可以通过延长每个时钟的低周期来降低总线时钟的速度。

在高速模式下,这个握手功能只能在字节级使用。

保留地址

两组8地址(0000 XXX和1111 XXX)是被保留的为了实现下面的功能

image-20210811120702489

广播寻址(0000 0000)

广播寻址用于同时寻址连接到i2c总线的每个设备;如果设备不需要广播提供的任何数据,它可以NACK来忽略广播地址;如果设备确实需要来自广播的数据,它就会对广播地址进行ACK,并作为一个从接收设备。

如果多个设备都发出了响应,主设备实际上并不知道有多少设备回应;每个能够处理此数据的从接收设备都会对第二个和后面的字节ACK;一般广播地址的含义总是在第二个字节中指定。

有两种情况需要考虑:当最低有效位B为零时。当最低有效位B为1时。

image-20210811121740400

LSB=0

0000 0110 (06h):通过硬件复位和写从设备可编程部分;当接收到这2字节序列时,从设备回应广播地址复位,并接收其地址可编程部分。

必须采取预防措施,以确保设备在施加电源电压后不会拉低SDA或SCL,因为这些低电平会阻塞总线。

0000 0100 (04h):通过硬件编写从地址的可编程部分。行为如上述,但设备不重置。

0000 0000 (00h):此代码不允许用作第二个字节。

LSB=1

当第B位为1时,2字节序列是一个硬件通用调用。

这意味着序列是由硬件主设备传输的,例如键盘扫描器,它可以通过编程传输所需的从地址。

由于硬件主设备事先不知道消息必须传输到哪个设备,因此它只能生成这个硬件通用调用和它自己的地址,并将其标识给系统。

第二个字节中剩下的7位包含硬件主设备地址;这个地址被一个连接到总线的智能设备(例如,一个微控制器)识别,然后总线接受来自硬件设备的信息。

start byte(0000 0001)

存在原因:

并不是每个连接到I2C总线的微控制器都有集成的I2C控制器。这些微控制器必须永久观察I2C线路,以检测I2C传输。这将消耗(主要由轮询完成)大量的CPU时间。为了减少这种CPU功率的浪费,可以用较慢的仲裁方法建立I2C传输。

start byte有何作用

为此,主机发送启动条件,然后是 start byte (’ 00000001 ‘),一个虚拟的应答脉冲和一个重复的启动条件。观察微控制器只能检测SDA上的七个零中的一个来检测I2C传输。这可以通过一个相对缓慢的轮询率来实现。一旦控制器检测到SDA是低的,它就可以切换到一个更高的轮询速率,以便等待重复的启动条件和接下来的I2C传输。

传输结束后,它可以切换回节省cpu功耗的慢轮询速率,以便检测下一次传输。

image-20210811145304888

在开始字节之后产生一个与ACK相关的时钟脉冲,这样做只是为了与总线上使用的字节处理格式相一致,不允许任何设备确认开始字节

Device ID(1111 1xx1)

概念:

设备ID字段是一个可选的3字节只读字(24位),提供以下信息:

  • 12位带有制造商名称,每个制造商都是唯一的(例如NXP); 9位带有零件标识,由制造商指定(例如PCA9698) ;3位带有模具修订,由制造商指定(例如RevX)

设备ID是只读的,在设备中硬连接,可以按如下方式访问:

  • 开始条件(S)& 主设备发送预留的设备ID I2C-bus地址,后面跟着设置为0的R/W位(写):11111000。

  • 主设备发送从设备可以识别I2C-bus从地址;LSB是一个Don ‘ t care值;只有一个设备可以识别这个字节(具有i2c总线从地址的那个)。

  • 主设备发送一个重新启动条件(Sr)。主设备发送预留的设备ID I2C-bus地址,后面跟着R/W位,设置为1(读):1111 1001。

  • 接着读设备ID,首先是12制造商比特(第一个字节+第二个字节的高四比特),紧随其后的是九个部分标识位(第二个字节的低四比特+ 第三个字节的高五比特),然后是

    三个模具修改部分(第三个字节低四比特)。主设备通过对最后一个字节NACK来结束读取序列,从而重新设置从设备状态并允许主设备发送停止条件

10bit地址

10位地址扩展了地址位数;7位和10位地址的设备可以连接到相同的I2C总线,并且7位和10位地址可以在所有总线速度模式中使用。

10位从地址由开始条件或重复开始条件后的前两个字节组成。第一个字节的前7位是组合1111 0XX,其中最后2位(XX)是10位地址的两个最高有效位(MSB);

第一个字节的第8位是决定传输方向的R/W位。前面描述的7位寻址的所有读/写格式组合都可以用10位寻址实现。

10-bit 地址写

开始条件后跟着10位从地址,每个从设备将从地址(1111 0XX)第一个字节的前7位与它自己的地址进行比较,并检测第八位(R/W方向位)是否为0。

有可能有多个从设备匹配并生成应答信号(A1)。

把以上所有匹配从设备的地址与从地址(XXXX XXXX)第二个字节的8位进行比较,只有一个从设备地址匹配并生成应答信号(A2)。

匹配的从设备地址一直由主地址寻址,直到它接收到一个停止条件(P)或一个重复启动条件(Sr),并且其后面跟着一个不同的从属地址

image-20210811120305309

10-bit 地址读

传输方向在第二个R/W位之后改变;在确认位A2之前,其过程和读相同;重复启动条件(Sr)之后,匹配的从设备会记住它之前被寻址过。

然后这个从设备检查Sr之后的第一个字节的前7位是否与开始条件之后的第一个字节相同,并检测第8位(R/W)是否为1;如果匹配,从设备认为它已经作为一个发送器被寻址并生成确认A3。

从发送设备保持寻址,直到它收到一个停止条件(P)或直到它收到另一个重复启动条件(Sr),且后面跟着一个不同的从设备地址。

image-20210811120522640

VIP开发

接口及功能分析

interface

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
`ifndef LVC_I2C_IF_SV
`define LVC_I2C_IF_SV

interface lvc_i2c_if (input bit CLK);

/**
线与类型的SCL
*/
wand SCL;

/**
线与类型的SDA
*/
wand SDA;

/**
* This signal is connected to the SMBALRT (serial data) of
* all connected masters/slaves. All masters/slaves drive their
* SMBALRT on this signal of the corresponding instance of this port interface.
* The output SMBALRT from all masters/slaves is wired AND by this signal.
*/
wand SMBALRT;

/**
* Reset signal
* Reset is generated from the testbench. This reset is used to
* reset master/slave bfm, monitor and checker.
*/
bit RST ;

/**
* Determines whether to pullup the SCL & SDA from Master & Slave.
* When the value is 1 then SCL & SDA will be pulled up to '1'.
*/
bit enable_pullup_resistor = 1'b1;

logic scl_master;
logic smbalrt_master;
logic sda_master;

// 空闲状态下,将总线驱动为高阻态
assign SCL = scl_master === 0 ? 1'b0 : 1'bz;
assign SDA = sda_master === 0 ? 1'b0 : 1'bz;
assign SMBALRT = smbalrt_master === 0 ? 1'b0 : 1'bz;

logic scl_slave;
logic smbalrt_slave;
logic sda_slave;

assign SCL = scl_slave === 0 ? 1'b0 : 1'bz;
assign SDA = sda_slave === 0 ? 1'b0 : 1'bz;
assign SMBALRT = smbalrt_slave === 0 ? 1'b0 : 1'bz;
//类似于时钟块
modport lvc_i2c_master_modport (
input RST,
input CLK,
inout SCL,
inout SMBALRT,
inout SDA
);

modport lvc_i2c_slave_modport (
input RST,
input CLK,
inout SCL,
inout SMBALRT,
inout SDA
);

modport lvc_i2c_monitor_modport (
input RST,
input CLK,
inout SCL,
inout SMBALRT,
inout SDA
);
//定义各类型时间,启动条件,截止条件,应答和非应答位生成,应答和非应答位接收,重复启动条件,扩展地址(开始位,广播的两次发送)
event event_master_start_generated ;
event event_master_stop_generated ;
event event_master_ack_generated ;
event event_master_nack_generated ;
event event_master_ack_received ;
event event_master_nack_received ;
event event_master_repeated_start_generated ;
event event_master_start_byte_transmited ;
event event_master_general_call_addr_sent ;
event event_master_general_call_sec_byte_sent ;
event event_master_arbitration_loss_detected;

assign (weak0, weak1) SCL = enable_pullup_resistor ? 1 : 0;
assign (weak0, weak1) SDA = enable_pullup_resistor ? 1 : 0;
assign (weak0, weak1) SMBALRT = enable_pullup_resistor ? 1 : 0;

endinterface : lvc_i2c_if

`endif // LVC_I2C_IF_SV

transcation

考虑总线的特性状态,进行功能特性提取

核心就4个变量:命令cmd,地址adress,data数组,选择是否使用10bit地址

当然还有很多其他的状态:开始和截止位状态,重启状态,响应位状态

base_transcation

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
`ifndef LVC_I2C_TRANSACTION_SV
`define LVC_I2C_TRANSACTION_SV
class lvc_i2c_transaction extends uvm_sequence_item;

lvc_i2c_agent_configuration cfg = null;

/**
* - I2C_WRITE : 写命令
* - I2C_READ : 读命令
* - I2C_GEN_CALL : 广播
* - I2C_DEVICE_ID : 请求设备id
*/
rand command_enum cmd = I2C_WRITE;

// 在defines.svh中定义,LVC_I2C_SLA_ADD_WIDTH为10bit地址;LVC_I2C_SLAVE0_ADDRESS为10'b1100110011
rand bit [`LVC_I2C_SLA_ADD_WIDTH-1:0] addr = `LVC_I2C_SLAVE0_ADDRESS;

//1BYTE数据构成的数组
rand bit [7:0] data [];

// 主端是否使能10bit地址
rand bit addr_10bit = 0;

// 使用10bit地址的权重,通过约束对addr_10bit进行控制
int unsigned ADDR_10BIT_ON_wt = 0;
int unsigned ADDR_10BIT_OFF_wt = 1;

/**
* - 1 : Start detected
* - 0 : Start not detected
*/
bit start_detected;

/**
* - 1 : Stop detected
* - 0 : Stop not detected
*/
bit stop_detected;

/**
* - 1 : ACK detected
* - 0 : NACK detected
*/
bit ack_detected [];

/**
* - 1 : Repeated Start detected
* - 0 : Repeated Start not detected
*/
bit rep_start_detected;

/**
* 双向数据线,读和写均要进行监测
* - 1 : Read command detected
* - 0 : Write command detected
*/
bit read_write;

/**
* Stores start time of START condition sent by Master.<br/>
* Time is represented in timescale used (100ps by default).<br/>
*/
int start_detected_st;

//存储启动条件结束时间
int start_detected_et;

//存储截止条件开始时间
int stop_detected_st;

// 存储截止条件结束时间
int stop_detected_et;

//确认位
int ack_detected_st [];
int ack_detected_et [];

//复启位
int rep_start_detected_st;
int rep_start_detected_et;

//读写命令位
int read_write_st;
int read_write_et;

//地址位
int addr_st;
int addr_et;

// 每个bit位
int data_st [];
int data_et [];

/**
* Enable/Disable the command fired from Master. Possible values are :
* 1 : Command is sent to BFM. <br/>
* 0 : Command is not sent to BFM. <br/>
*/
bit enable_cmd = 1;

/**
* sequence是否使用
* 1 : Agent Driver uses sequence data <br/>
* 0 : Agent Driver ignores the sequence data <br/>
*/
bit enable_pkt = 1;

/**
* Enable/disable configuration driving. Possible values are :
* 1 : Configuration is sent to BFM. <br/>
* 0 : Configuration is not sent to BFM. <br/>
*/
bit enable_cfg = 1;

constraint valid_ranges {
if((cfg!=null)&&(cfg.bus_type == I2C_BUS )) cmd inside {I2C_READ, I2C_WRITE , I2C_GEN_CALL, I2C_DEVICE_ID};
if((cfg!=null)&&(cfg.bus_type == I2C_BUS )) data.size() inside {[1:5120]};
else data.size() inside {[1:1024]};
}
constraint reasonable_cmd {
if((cfg!=null)&&(cfg.bus_type == I2C_BUS )) cmd inside {I2C_READ, I2C_WRITE };
}
constraint reasonable_data {
if((cfg!=null)&&(cfg.bus_type == I2C_BUS )) data.size() inside {[1:10]};
else data.size() inside {[1:1024]};
}
constraint reasonable_addr {
addr inside {10'b1100110011, 10'b1100110000};
}
constraint reasonable_addr_10bit {
addr_10bit dist {
0 :/ADDR_10BIT_OFF_wt,
1 :/ADDR_10BIT_ON_wt
};
}

/**
* UVM field automation macros
*/
`uvm_object_utils_begin(lvc_i2c_transaction)
`uvm_field_enum(command_enum, cmd , UVM_ALL_ON|UVM_ENUM)
`uvm_field_object(cfg , UVM_ALL_ON|UVM_NOPRINT)
`uvm_field_int(addr , UVM_ALL_ON|UVM_HEX)
`uvm_field_array_int(data , UVM_ALL_ON|UVM_HEX)
`uvm_field_int(addr_10bit , UVM_ALL_ON)
`uvm_field_int(start_detected , UVM_ALL_ON|UVM_NOPRINT)
`uvm_field_int(stop_detected , UVM_ALL_ON|UVM_NOPRINT)
`uvm_field_int(rep_start_detected , UVM_ALL_ON|UVM_NOPRINT)
`uvm_field_int(read_write , UVM_ALL_ON)
`uvm_field_int(start_detected_st , UVM_ALL_ON|UVM_NOPRINT)
`uvm_field_int(start_detected_et , UVM_ALL_ON|UVM_NOPRINT)
`uvm_field_int(stop_detected_st , UVM_ALL_ON|UVM_NOPRINT)
`uvm_field_int(stop_detected_et , UVM_ALL_ON|UVM_NOPRINT)
`uvm_field_array_int(ack_detected , UVM_ALL_ON|UVM_NOPRINT)
`uvm_field_array_int(ack_detected_st , UVM_ALL_ON|UVM_NOPRINT)
`uvm_field_array_int(ack_detected_et , UVM_ALL_ON|UVM_NOPRINT)
`uvm_field_int(rep_start_detected_st , UVM_ALL_ON|UVM_NOPRINT)
`uvm_field_int(rep_start_detected_et , UVM_ALL_ON|UVM_NOPRINT)
`uvm_field_int(read_write_st , UVM_ALL_ON|UVM_NOPRINT)
`uvm_field_int(read_write_et , UVM_ALL_ON|UVM_NOPRINT)
`uvm_field_int(addr_st , UVM_ALL_ON|UVM_NOPRINT)
`uvm_field_int(addr_et , UVM_ALL_ON|UVM_NOPRINT)
`uvm_field_array_int(data_st , UVM_ALL_ON|UVM_NOPRINT)
`uvm_field_array_int(data_et , UVM_ALL_ON|UVM_NOPRINT)
`uvm_field_int(enable_cmd , UVM_ALL_ON|UVM_NOPRINT)
`uvm_field_int(enable_pkt , UVM_ALL_ON|UVM_NOPRINT)
`uvm_field_int(enable_cfg , UVM_ALL_ON|UVM_NOPRINT)
`uvm_field_int(ADDR_10BIT_ON_wt , UVM_ALL_ON|UVM_NOPRINT)
`uvm_field_int(ADDR_10BIT_OFF_wt , UVM_ALL_ON|UVM_NOPRINT)
`uvm_object_utils_end

/**
* CONSTRUCTOR : Create a new transaction instance, passing the appropriate
* argument values to the parent class.
*
* @param name Instance name of the transaction
*/
extern function new (string name = "lvc_i2c_transaction");

// 决定reasonable_cmd,reasonable_data等是否有效
extern virtual function int reasonable_constraint_mode (bit on_off);

/**
* slient决定是否异常要输出warning
*/
extern virtual function bit is_valid (bit silent = 1);
endclass

function lvc_i2c_transaction::new(string name = "lvc_i2c_transaction");
super.new(name);
endfunction

//一旦启动, reasonable_constraint就可以对valid_ranges_constraint进行覆盖
function int lvc_i2c_transaction::reasonable_constraint_mode (bit on_off);
reasonable_cmd.constraint_mode(on_off);
reasonable_addr.constraint_mode(on_off);
return on_off;
endfunction


function bit lvc_i2c_transaction::is_valid (bit silent = 1);
is_valid = 1;
`LVC_DATA_UTIL_IS_VALID_BV_W_RANGE(addr,0,10'b11_1111_1111)
`LVC_DATA_UTIL_IS_VALID_BV_W_RANGE(addr_10bit,0,1)
`LVC_DATA_UTIL_IS_VALID_BV_W_RANGE(ADDR_10BIT_ON_wt,0,32'hFFFF_FFFF)
`LVC_DATA_UTIL_IS_VALID_BV_W_RANGE(ADDR_10BIT_OFF_wt,0,32'hFFFF_FFFF)
`LVC_DATA_UTIL_IS_VALID_BV_W_RANGE(start_detected,0,1)
`LVC_DATA_UTIL_IS_VALID_BV_W_RANGE(stop_detected,0,1)
`LVC_DATA_UTIL_IS_VALID_BV_W_RANGE(rep_start_detected,0,1)
`LVC_DATA_UTIL_IS_VALID_BV_W_RANGE(read_write,0,1)
`LVC_DATA_UTIL_IS_VALID_BV_W_RANGE(start_detected_st,32'h0,32'hFFFF_FFFF)
`LVC_DATA_UTIL_IS_VALID_BV_W_RANGE(start_detected_et,32'h0,32'hFFFF_FFFF)
`LVC_DATA_UTIL_IS_VALID_BV_W_RANGE(stop_detected_st,32'h0,32'hFFFF_FFFF)
`LVC_DATA_UTIL_IS_VALID_BV_W_RANGE(stop_detected_et,32'h0,32'hFFFF_FFFF)
`LVC_DATA_UTIL_IS_VALID_BV_W_RANGE(rep_start_detected_st,32'h0,32'hFFFF_FFFF)
`LVC_DATA_UTIL_IS_VALID_BV_W_RANGE(rep_start_detected_et,32'h0,32'hFFFF_FFFF)
`LVC_DATA_UTIL_IS_VALID_BV_W_RANGE(read_write_st,32'h0,32'hFFFF_FFFF)
`LVC_DATA_UTIL_IS_VALID_BV_W_RANGE(read_write_et,32'h0,32'hFFFF_FFFF)
`LVC_DATA_UTIL_IS_VALID_BV_W_RANGE(addr_st,32'h0,32'hFFFF_FFFF)
`LVC_DATA_UTIL_IS_VALID_BV_W_RANGE(addr_et,32'h0,32'hFFFF_FFFF)
`LVC_DATA_UTIL_IS_VALID_BV_W_RANGE(enable_cmd,0,1)
`LVC_DATA_UTIL_IS_VALID_BV_W_RANGE(enable_pkt,0,1)
`LVC_DATA_UTIL_IS_VALID_BV_W_RANGE(enable_cfg,0,1)
`LVC_DATA_UTIL_IS_VALID_BV_W_RANGE(gnrl_arp_arg,0,8'hFF)
`LVC_DATA_UTIL_IS_VALID_BV_W_RANGE(drct_arp_arg,0,1)
endfunction

master_transaction

总线按bit传输且存在开始、结束、重启、失去仲裁、非响应、时钟拉伸等情况

主端多出的对于事件的可能处理:

  • 主端在发送完一个transcation后是重启还是结束:sr_or_p_gen

  • 主端仲裁:

    • 失去仲裁后,是否完成当前transcation发送:abort_if_arb_lost
    • 失去仲裁后,要重新尝试几次来完成当前的transcation传输:num_of_retry
    • 是否等待别的主端开始,以进入仲裁状态:arbitrate
  • 主端是否发送start byte:send_start_byte

  • 对于非应答做何处理:

    • 是否放弃当前传输:retry_if_nack

    • 尝试几次来完成当前的transcation传输:num_of_retry

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
`ifndef LVC_I2C_MASTER_TRANSACTION_SV
`define LVC_I2C_MASTER_TRANSACTION_SV

class lvc_i2c_master_transaction extends lvc_i2c_transaction;
/** 当前的transcation发送完后是重启还是截止
* - 1 : Generates Repeated START (Sr) condition.<br/>
* - 0 : Generates STOP (P) condition.<br/>
*/
rand bit sr_or_p_gen = 0;

//如果没有收到ACK,或者失去仲裁,主端要尝试几次来完成当前的transcation传输
rand bit [2:0] num_of_retry = 3'b001;

//gen_call模式的第二个byte值,参考响应内容
rand bit [7:0] sec_byte_gen_call = 8'h04;

//主端是否发送开始位
rand bit send_start_byte = 0;

/**
* 一次传输结束到下一次传输开始的空闲时间
* Time is represented in timescale used (100ps by default).<br/>
*/
rand bit [31:0] idle_time = 32'h0001;

/**
* 失去仲裁的时候,主端应该如何做
* - 1 : Dump the current transaction.<br/>
* - 0 : Retry to complete the current transaction
*/
rand bit abort_if_arb_lost = 0;

/**
* 设置为1时,等待别的master的开始条件,然后开始传输任务,并进行仲裁
* - 1 : Wait for START condition before Starting the next transaction on the bus.<br/>
* - 0 : Does not wait for START from another Master to start the next transaction.<br/>
*/
rand bit arbitrate = 0;
/**
* 非应答时是继续传完还是直接放弃当前任务
* - 1 : Retry to complete the current transaction<br/>
* - 0 : Abort (dump) the current transaction.<br/>
*/
rand bit retry_if_nack = 0;

int unsigned SR_OR_P_GEN_REPEATED_wt = 0;
int unsigned SR_OR_P_GEN_STOP_wt = 1;


int unsigned START_BYTE_ON_wt = 0;
int unsigned START_BYTE_OFF_wt = 1;


int unsigned ABORT_IF_ARB_LOST_ON_wt = 0;
int unsigned ABORT_IF_ARB_LOST_OFF_wt = 1;

int unsigned ARBITRATE_ON_wt = 0;
int unsigned ARBITRATE_OFF_wt = 1;

int unsigned RETRY_IF_NACK_ON_wt = 0;
int unsigned RETRY_IF_NACK_OFF_wt = 1;

// 是否在传输一个字节后进行时钟拉伸
bit enable_clk_stretch_after_byte = 1'b0;

// 字节等级进行时钟拉伸的位置
rand bit [31:0] clk_stretch_byte_level_pos = 32'h0000;

// device_id是先写,重启,再读id,通过将该参数设为1,可以中断重启,改为停止
rand bit m_device_id_gen_stop = 0;

//
rand int nack_at_device_id_byte = 0;

rand int device_id_rollback_iteration = 0;

constraint master_valid_ranges {
idle_time inside {[0:1000]};
num_of_retry inside { [0:7]};
sec_byte_gen_call inside { 8'h04, 8'h06 };
clk_stretch_byte_level_pos <= data.size();
nack_at_device_id_byte inside {[0:`LVC_I2C_NACK_AT_DEVICE_ID_BYTE_MAX_VALID]};
device_id_rollback_iteration inside {[0:`LVC_I2C_DEVICE_ID_ROLLBACK_ITER_MAX_VALID]};
}
constraint reasonable_sr_or_p_gen {
sr_or_p_gen dist {
0 :/SR_OR_P_GEN_STOP_wt,
1 :/SR_OR_P_GEN_REPEATED_wt
};
}
constraint reasonable_idle_time {
idle_time dist {
0 := 1,
[1:10] := 100000,
[11:1000] := 1
};
}
constraint reasonable_sec_byte_gen_call {
sec_byte_gen_call inside {8'h04, 8'h06};
}
constraint reasonable_send_start_byte {
send_start_byte dist {
0 :/START_BYTE_OFF_wt,
1 :/START_BYTE_ON_wt
};
}
constraint reasonable_abort_if_arb_lost {
abort_if_arb_lost dist {
0 :/ABORT_IF_ARB_LOST_OFF_wt,
1 :/ABORT_IF_ARB_LOST_ON_wt
};
}
constraint reasonable_arbitrate {
arbitrate dist {
0 :/ARBITRATE_OFF_wt,
1 :/ARBITRATE_ON_wt
};
}
constraint reasonable_retry_if_nack {
retry_if_nack dist {
0 :/RETRY_IF_NACK_OFF_wt,
1 :/RETRY_IF_NACK_ON_wt
};
}
constraint reasonable_m_device_id_gen_stop {
m_device_id_gen_stop dist {
0 := 1,
1 := 0
};
}
constraint reasonable_nack_at_device_id_byte {
nack_at_device_id_byte inside {[0:20]};
}
constraint reasonable_device_id_rollback_iteration {
device_id_rollback_iteration inside {[0:5]};
}

`uvm_object_utils_begin(lvc_i2c_master_transaction)
`uvm_object_utils_end

extern function new (string name = "lvc_i2c_master_transaction");
extern function bit is_valid (bit silent =1);

endclass

function lvc_i2c_master_transaction::new(string name = "lvc_i2c_master_transaction");
super.new(name);
endfunction
function bit lvc_i2c_master_transaction::is_valid(bit silent =1);
is_valid = super.is_valid(silent);
`LVC_DATA_UTIL_IS_VALID_BV_W_RANGE(sr_or_p_gen,0,1)
`LVC_DATA_UTIL_IS_VALID_BV_W_RANGE(num_of_retry,0,3'b111)
`LVC_DATA_UTIL_IS_VALID_BV_W_RANGE(sec_byte_gen_call,0,8'hFF)
`LVC_DATA_UTIL_IS_VALID_BV_W_RANGE(send_start_byte,0,1)
`LVC_DATA_UTIL_IS_VALID_BV_W_RANGE(idle_time,0,32'hFFFF_FFFF)
`LVC_DATA_UTIL_IS_VALID_BV_W_RANGE(abort_if_arb_lost,0,1)
`LVC_DATA_UTIL_IS_VALID_BV_W_RANGE(arbitrate,0,1)
`LVC_DATA_UTIL_IS_VALID_BV_W_RANGE(retry_if_nack,0,1)
`LVC_DATA_UTIL_IS_VALID_BV_W_RANGE(SR_OR_P_GEN_REPEATED_wt,0,32'hFFFF_FFFF)
`LVC_DATA_UTIL_IS_VALID_BV_W_RANGE(SR_OR_P_GEN_STOP_wt,0,32'hFFFF_FFFF)
`LVC_DATA_UTIL_IS_VALID_BV_W_RANGE(START_BYTE_ON_wt,0,32'hFFFF_FFFF)
`LVC_DATA_UTIL_IS_VALID_BV_W_RANGE(START_BYTE_OFF_wt,0,32'hFFFF_FFFF)
`LVC_DATA_UTIL_IS_VALID_BV_W_RANGE(ABORT_IF_ARB_LOST_ON_wt,0,32'hFFFF_FFFF)
`LVC_DATA_UTIL_IS_VALID_BV_W_RANGE(ABORT_IF_ARB_LOST_OFF_wt,0,32'hFFFF_FFFF)
`LVC_DATA_UTIL_IS_VALID_BV_W_RANGE(ARBITRATE_ON_wt,0,32'hFFFF_FFFF)
`LVC_DATA_UTIL_IS_VALID_BV_W_RANGE(ARBITRATE_OFF_wt,0,32'hFFFF_FFFF)
`LVC_DATA_UTIL_IS_VALID_BV_W_RANGE(RETRY_IF_NACK_ON_wt,0,32'hFFFF_FFFF)
`LVC_DATA_UTIL_IS_VALID_BV_W_RANGE(RETRY_IF_NACK_OFF_wt,0,32'hFFFF_FFFF)
`LVC_DATA_UTIL_IS_VALID_BV_W_RANGE(enable_clk_stretch_after_byte,0,1)
`LVC_DATA_UTIL_IS_VALID_BV_W_RANGE(clk_stretch_byte_level_pos,0,32'hFFFF_FFFF)
`LVC_DATA_UTIL_IS_VALID_BV_W_RANGE(m_device_id_gen_stop,0,1)
`LVC_DATA_UTIL_IS_VALID_BV_W_RANGE(nack_at_device_id_byte,32'h0,32'hFFFF_FFFF)
`LVC_DATA_UTIL_IS_VALID_BV_W_RANGE(device_id_rollback_iteration,32'h0,32'hFFFF_FFFF)
endfunction
`endif // LVC_I2C_MASTER_TRANSACTION_SV

slave_transcation

从端决定回复的应答信号以及时钟拉伸

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
`ifndef LVC_I2C_SLAVE_TRANSACTION_SV
`define LVC_I2C_SLAVE_TRANSACTION_SV

class lvc_i2c_slave_transaction extends lvc_i2c_transaction;

/** Object used to hold exceptions for a transaction */
//lvc_i2c_slave_transaction_exception_list exception_list = null;

// 从端选择响应还是非响应
rand bit nack_addr = 0;

// 从端来几次非响应
rand bit [31:0] nack_addr_count = 32'h0000;

//每个byte传输后的拉伸周期
int unsigned clk_stretch_time_after_byte = 0;

//在传输地址时进行bit等级的拉伸
int unsigned clk_stretch_time_addr_byte = 0;

//在传输数据时进行bit等级的拉伸
int unsigned clk_stretch_time_data_byte = 0;

// 判断是否对上述变量进行随机
bit enable_random_clk_stretch_time_after_byte = 1'b0;
bit enable_random_clk_stretch_time_addr_byte = 1'b0;
bit enable_random_clk_stretch_time_data_byte = 1'b0;

// 选择bit等级的时钟拉伸在时钟的哪一个bit位做
rand bit [3:0] clk_stretch_bit_level_addr_pos = 4'b0000;

//data
rand bit [3:0] clk_stretch_bit_level_data_pos = 4'b0000;

//byte
rand bit [31:0] clk_stretch_byte_level_pos = 32'h0000;

/**
* Dumps the EEPROM memory in Slave BFM from the file specified by files
* handle passed< as second argument.
* <u>Note</u>: <i>This configuration takes effect if Slave is configured
* as an EEPROM Slave.</i>
*/
bit [31:0] eeprom_mem_dump = 32'h0000;

/**
* Loads the EEPROM memory in Slave BFM with the data specified in files
* handle passed as second argument. Note: This configuration occurs
* only if Slave is configured as an EEPROM Slave.
*/
bit [31:0] eeprom_mem_load = 32'h0000;

// 每隔几个数据byte发送1个非应答信号
rand bit [31:0] nack_data = 32'h0000;


int unsigned NACK_ADDRESS_ON_wt = 0;
int unsigned NACK_ADDRESS_OFF_wt = 1;

constraint slave_valid_ranges {
nack_addr_count inside {[0:31]};
clk_stretch_bit_level_addr_pos inside {[0:8]};
clk_stretch_bit_level_data_pos inside {[0:8]};
clk_stretch_byte_level_pos <= data.size();
}
constraint reasonable_nack_addr {
nack_addr dist {
0 :/NACK_ADDRESS_OFF_wt,
1 :/NACK_ADDRESS_ON_wt
};
}

constraint reasonable_nack_data {
soft nack_data == 0;
}
constraint reasonable_clk_stretch_bit_level_addr_pos {
clk_stretch_bit_level_addr_pos inside {[1:8]};
}
constraint reasonable_clk_stretch_bit_level_data_pos {
clk_stretch_bit_level_data_pos inside {[1:8]};
}
constraint reasonable_clk_stretch_byte_level_pos {
clk_stretch_byte_level_pos inside {[1:data.size()]};
}

`uvm_object_utils_begin(lvc_i2c_slave_transaction)
`uvm_object_utils_end

extern function new (string name = "lvc_i2c_slave_transaction");
extern function bit is_valid (bit silent =1);

endclass

function lvc_i2c_slave_transaction::new(string name = "lvc_i2c_slave_transaction");
super.new(name);
endfunction
function bit lvc_i2c_slave_transaction::is_valid(bit silent =1);
is_valid = super.is_valid(silent);
`LVC_DATA_UTIL_IS_VALID_BV_W_RANGE(nack_addr,0,1);
`LVC_DATA_UTIL_IS_VALID_BV_W_RANGE(nack_addr_count,0,32'hFFFF_FFFF);
`LVC_DATA_UTIL_IS_VALID_BV_W_RANGE(clk_stretch_time_after_byte,0,32'hFFFF_FFFF);
`LVC_DATA_UTIL_IS_VALID_BV_W_RANGE(clk_stretch_time_addr_byte,0,32'hFFFF_FFFF);
`LVC_DATA_UTIL_IS_VALID_BV_W_RANGE(clk_stretch_time_data_byte,0,32'hFFFF_FFFF);
`LVC_DATA_UTIL_IS_VALID_BV_W_RANGE(enable_random_clk_stretch_time_after_byte,0,1);
`LVC_DATA_UTIL_IS_VALID_BV_W_RANGE(enable_random_clk_stretch_time_addr_byte,0,1);
`LVC_DATA_UTIL_IS_VALID_BV_W_RANGE(enable_random_clk_stretch_time_data_byte,0,1);
`LVC_DATA_UTIL_IS_VALID_BV_W_RANGE(clk_stretch_bit_level_addr_pos,0,4'b1111);
`LVC_DATA_UTIL_IS_VALID_BV_W_RANGE(clk_stretch_bit_level_data_pos,0,4'b1111);
`LVC_DATA_UTIL_IS_VALID_BV_W_RANGE(clk_stretch_byte_level_pos,0,32'hFFFF_FFFF);
`LVC_DATA_UTIL_IS_VALID_BV_W_RANGE(eeprom_mem_dump,0,32'hFFFF_FFFF);
`LVC_DATA_UTIL_IS_VALID_BV_W_RANGE(eeprom_mem_load,0,32'hFFFF_FFFF);
`LVC_DATA_UTIL_IS_VALID_BV_W_RANGE(nack_data,0,32'hFFFF_FFFF);
`LVC_DATA_UTIL_IS_VALID_BV_W_RANGE(NACK_ADDRESS_ON_wt,0,32'hFFFF_FFFF);
`LVC_DATA_UTIL_IS_VALID_BV_W_RANGE(NACK_ADDRESS_OFF_wt,0,32'hFFFF_FFFF);
endfunction

`endif // LVC_I2C_SLAVE_TRANSACTION_SV

环境搭建

driver

和apb相比,驱动的思想应该用串行的方式来理解,按bit位进行分析

比如apb的一次传输需要用两拍或者更多拍来完成,setup和trans这两个状态就要依次驱动

i2c明显更加复杂,需要考虑地址位,读写位,响应位,结束位等等,但归根结底还是串行的,一样用串行的驱动方式

bfm_common

核心方法:clk_low_offset_gen()

对不同模式驱动的参数进行配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
`ifndef LVC_I2C_BFM_COMMON_SV
`define LVC_I2C_BFM_COMMON_SV

class lvc_i2c_bfm_common extends uvm_object;

lvc_i2c_vif i2c_if;
uvm_component comp;
lvc_i2c_configuration cfg;

`uvm_object_utils_begin(lvc_i2c_bfm_common)
`uvm_object_utils_end
int unsigned i2c_clk_high=0; //clk high level time
int unsigned i2c_clk_low=0; //clk low level time
int unsigned re_sta_su=0; //repeat start setup time
int unsigned sto_su=0; //stop setup time
int unsigned sta_hd=0; //start or repeat start hold time
int unsigned dat_hd=0; //data hold time,
int unsigned tbuf_time=0; //bus free time between a stop and

extern function new(string name = "lvc_i2c_bfm_common");

extern virtual function void assign_vif(lvc_i2c_vif i2c_if);

extern virtual task source_event(event sv_e, uvm_event uvm_e);

extern virtual task reconfigure_via_task(lvc_i2c_configuration cfg);

extern task wait_data_hd_time();
extern task clk_low_offset_gen();
extern virtual task wait_for_reset();
endclass

function lvc_i2c_bfm_common::new(string name = "lvc_i2c_bfm_common");
super.new(name);
endfunction

function void lvc_i2c_bfm_common::assign_vif(lvc_i2c_vif i2c_if);
this.i2c_if = i2c_if;
endfunction

task lvc_i2c_bfm_common::source_event(event sv_e, uvm_event uvm_e);
forever begin
@(sv_e);
uvm_e.trigger();
end
endtask

task lvc_i2c_bfm_common::reconfigure_via_task(lvc_i2c_configuration cfg);
this.cfg = cfg;
endtask

task lvc_i2c_bfm_common::clk_low_offset_gen();
case (cfg.bus_speed)
STANDARD_MODE:
begin
i2c_clk_high = cfg.scl_high_time_ss;
i2c_clk_low = cfg.scl_low_time_ss;
re_sta_su = cfg.min_su_sta_time_ss;
sto_su = cfg.min_su_sto_time_ss;
sta_hd = cfg.min_hd_sta_time_ss;
dat_hd = cfg.min_hd_dat_time_ss;
tbuf_time = cfg.tbuf_time_ss;
end
FAST_MODE:
begin
i2c_clk_high = cfg.scl_high_time_fs;
i2c_clk_low = cfg.scl_low_time_fs;
re_sta_su = cfg.min_su_sta_time_fs;
sto_su = cfg.min_su_sto_time_fs;
sta_hd = cfg.min_hd_sta_time_fs;
dat_hd = cfg.min_hd_dat_time_fs;
tbuf_time = cfg.tbuf_time_fs;
end
HIGHSPEED_MODE:
begin
i2c_clk_high = cfg.scl_high_time_hs;
i2c_clk_low = cfg.scl_low_time_hs;
re_sta_su = cfg.min_su_sta_time_hs;
sto_su = cfg.min_su_sto_time_hs;
sta_hd = cfg.min_hd_sta_time_hs;
dat_hd = cfg.min_hd_dat_time_hs;
tbuf_time = cfg.tbuf_time_hs;
end
FAST_MODE_PLUS:
begin
i2c_clk_high = cfg.scl_high_time_fm_plus;
i2c_clk_low = cfg.scl_low_time_fm_plus;
re_sta_su = cfg.min_su_sta_time_fm_plus;
sto_su = cfg.min_su_sto_time_fm_plus;
sta_hd = cfg.min_hd_sta_time_fm_plus;
dat_hd = cfg.min_hd_dat_time_fm_plus;
tbuf_time = cfg.tbuf_time_fm_plus;
end
default:
begin
i2c_clk_high = cfg.scl_high_time_ss;
i2c_clk_low = cfg.scl_low_time_ss;
re_sta_su = cfg.min_su_sta_time_ss;
sto_su = cfg.min_su_sto_time_ss;
sta_hd = cfg.min_hd_sta_time_ss;
dat_hd = cfg.min_hd_dat_time_ss;
tbuf_time = cfg.tbuf_time_ss;
end
endcase
endtask : clk_low_offset_gen

task lvc_i2c_bfm_common::wait_data_hd_time();
for(int i=0; i<=dat_hd;i++)
@(posedge i2c_if.CLK);
endtask : wait_data_hd_time

task lvc_i2c_bfm_common::wait_for_reset();
// wait for reset release
@(negedge i2c_if.RST);
//wait(i2c_if.`I2C_MASTER_MODPORT.RST === 'b0);
endtask
`endif // LVC_I2C_BFM_COMMON_SV

master_driver_common

核心方法:send_xact(lvc_i2c_master_transaction trans)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
`ifndef LVC_I2C_MASTER_DRIVER_COMMON_SV
`define LVC_I2C_MASTER_DRIVER_COMMON_SV

class lvc_i2c_master_driver_common extends lvc_i2c_bfm_common;

`uvm_object_utils_begin(lvc_i2c_master_driver_common)
`uvm_object_utils_end
semaphore lock; //if current send the restart cmd, next transaction need not to send start cmd
bit nack_flag=0; // flag about nack generated by slave
bit is10bits_again=0;
bit same_slv_addr_in10bit=0;
bit [9:0] pre_addr=0;
extern function new(string name = "lvc_i2c_master_driver_common");

extern virtual task send_xact(lvc_i2c_master_transaction trans);
extern virtual task collect_response_from_vif(lvc_i2c_master_transaction trans);
extern task start_gen();
extern task stop_gen();
extern task rw_slave_7bit_addr(bit[6:0] addr, bit rw);
extern task rw_slave_10bit_addr(bit [9:0] addr, bit rw);
extern task send_byte(bit[7:0] send_byte);
extern task recv_byte(output bit[7:0] recv_data);
extern task recv_byte_noack(output bit[7:0] recv_data);
extern task send_start_byte();
extern task pre_hs_in_fm_send_byte(bit[7:0] send_byte);
extern task pre_hs_in_fm_plus_send_byte(bit[7:0] send_byte);
extern task pre_hs_in_fm_start_gen();
extern task pre_hs_in_fm_plus_start_gen();
extern task re_start_gen();
extern task drive_data(lvc_i2c_master_transaction trans);
extern task rw_same_slave_10bit_addr(bit [9:0] addr, bit rw);

endclass

function lvc_i2c_master_driver_common::new(string name = "lvc_i2c_master_driver_common");
super.new(name);
lock = new(1);
endfunction

task lvc_i2c_master_driver_common::send_xact(lvc_i2c_master_transaction trans);
lvc_i2c_master_transaction trans_temp;
clk_low_offset_gen();
drive_data(trans);
endtask

task lvc_i2c_master_driver_common::drive_data(lvc_i2c_master_transaction trans);
bit rw; //0--write, 1--read
bit[7:0] recv_data;
bit[7:0] gen_call_first_byte = 8'b0000_0000;
bit[7:0] gen_call_second_byte = trans.sec_byte_gen_call;
bit[7:0] device_id_w = 8'b1111_1000;
bit[7:0] device_id_r = 8'b1111_1001;
bit[7:0] hs_code = {5'b00001,cfg.master_code};
bit[2:0] num_of_retry=0;

if(trans.retry_if_nack)
num_of_retry = trans.num_of_retry;
else
num_of_retry=0;

case(trans.cmd)
I2C_WRITE:

I2C_READ:

I2C_GEN_CALL:

I2C_DEVICE_ID:

endcase
endtask : drive_data
`endif // LVC_I2C_MASTER_DRIVER_COMMON_SV

start_gen()

  • mos管导通,将scl和sda两根线拉高
  • 等待1000个外部时钟,将sda置低
  • 已经完成启动,但是需要等待sta_hd个外部时钟,将scl置低,因为scl为高时,数据不能变,为低时才真正把数据放到总线上
1
2
3
4
5
6
7
8
9
10
11
12
13
14
task lvc_i2c_master_driver_common::start_gen();
if(lock.try_get()) begin
//repeat(1000) @(posedge i2c_if.CLK);
i2c_if.scl_master = 1;
i2c_if.sda_master = 1;
repeat(1000) @(posedge i2c_if.CLK);
i2c_if.sda_master = 0;
repeat(sta_hd) @(posedge i2c_if.CLK);
i2c_if.scl_master = 0;
//->i2c_if.event_master_start_generated;
end
else
`uvm_info("start", "Because The Last Transaction Have Generate Restart CMD, This Transation Need Not Generate Start CMD", UVM_DEBUG);
endtask : start_gen

stop_gen()

等待4个时间

  • wait_data_hd_time():数据的hold time,在子类中定义;将sda置为0
  • i2c_clk_low个外部时钟,将scl置为1
  • sto_su个外部时钟,将sda拉高
  • 再等待tbuf_time个外部时钟,释放锁,此时其他master可以通过start_gen来获取锁
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
task lvc_i2c_master_driver_common::stop_gen();
fork
begin
wait_data_hd_time();
i2c_if.sda_master = 0;
end
begin
repeat(i2c_clk_low) @(posedge i2c_if.CLK);
i2c_if.scl_master = 1;
wait(i2c_if.SCL == 1);
end
join
repeat(sto_su) @(posedge i2c_if.CLK);
i2c_if.sda_master = 1;
repeat(tbuf_time) @(posedge i2c_if.CLK);
// i2c_if.scl_master = 1'bz;
// i2c_if.sda_master = 1'bz;
//->i2c_if.event_master_stop_generated;
lock.put();
endtask : stop_gen

re_start_gen()

和start非常类似,但是需要考虑当前两根线的状态,不能直接置为1

scl在每次传输结束时都置为0,等待i2c_clk_low个外部时钟后,再将其拉高

在低电平期间将sda置1,比scl置1早re_sta_su个外部时钟

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
task lvc_i2c_master_driver_common::re_start_gen();
//bus free time, hs mode no this parameter.
fork
begin
repeat(i2c_clk_low) @(posedge i2c_if.CLK);
i2c_if.scl_master = 1;
end
begin
repeat(i2c_clk_low-re_sta_su) @(posedge i2c_if.CLK);
i2c_if.sda_master = 1;
end
join

repeat(re_sta_su) @(posedge i2c_if.CLK);
i2c_if.sda_master = 0;
repeat(sta_hd) @(posedge i2c_if.CLK);
i2c_if.scl_master = 0;
//->i2c_if.event_master_repeated_start_generated;
endtask : re_start_gen

send_byte

8位bit的发送+判断是否响应

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
task lvc_i2c_master_driver_common::send_byte(bit[7:0] send_byte);
// i2c_if.scl_master = 0;
// wait(i2c_if.SCL&i2c_if.SDA != 1'bz); //stretch release
for(int i=7;i>=0;i--) begin
fork
begin
wait_data_hd_time();
i2c_if.sda_master=send_byte[i];
end
begin
repeat(i2c_clk_low) @(posedge i2c_if.CLK);
i2c_if.scl_master = 1;
wait(i2c_if.SCL == 1);
end
join
repeat(i2c_clk_high) @(posedge i2c_if.CLK);
i2c_if.scl_master = 0;
end
fork
begin
// wait_data_hd_time();
// i2c_if.sda_master = 1'bz;
end
begin
repeat(i2c_clk_low) @(posedge i2c_if.CLK);
i2c_if.scl_master = 1;
if(i2c_if.sda_master != 0)
nack_flag=1;
else
nack_flag=0;
//`uvm_error("master driver sent byte to slave","slave send no ack to master.")
end
join
repeat(i2c_clk_high) @(posedge i2c_if.CLK);
i2c_if.scl_master = 0;
endtask : send_byte

接收byte发送应答

和写一样,每次scl为1时采集数据;

8个数据读完后要发送1个应答位

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
task lvc_i2c_master_driver_common::recv_byte(output bit[7:0] recv_data);
for(int i=7;i>=0;i--) begin
repeat(i2c_clk_low) @(posedge i2c_if.CLK);
i2c_if.scl_master = 1;
recv_data[i] = i2c_if.SDA;
repeat(i2c_clk_high) @(posedge i2c_if.CLK);
i2c_if.scl_master = 0;
end
fork
begin
wait_data_hd_time();
i2c_if.sda_master = 0; //send ack to slave
end
begin
repeat(i2c_clk_low) @(posedge i2c_if.CLK);
i2c_if.scl_master = 1;
end
join
repeat(i2c_clk_high) @(posedge i2c_if.CLK);
i2c_if.scl_master = 0;
// wait_data_hd_time();
// i2c_if.sda_master = 1'bz;
endtask : recv_byte

接收byte发送非应答

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
task lvc_i2c_master_driver_common::recv_byte_noack(output bit[7:0] recv_data);  //the last data received, master will sent out a n-ack to slave
for(int i=7;i>=0;i--) begin
repeat(i2c_clk_low) @(posedge i2c_if.CLK);
i2c_if.scl_master = 1;
recv_data[i] = i2c_if.SDA;
repeat(i2c_clk_high) @(posedge i2c_if.CLK);
i2c_if.scl_master = 0;
end
fork
begin
wait_data_hd_time();
i2c_if.sda_master = 1; //send n-ack to slave
end
begin
repeat(i2c_clk_low) @(posedge i2c_if.CLK);
i2c_if.scl_master = 1;
end
join
repeat(i2c_clk_high) @(posedge i2c_if.CLK);
i2c_if.scl_master = 0;
// wait_data_hd_time();
// i2c_if.sda_master = 1'bz;
endtask : recv_byte_noack

send_start_byte

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
task lvc_i2c_master_driver_common::send_start_byte();
bit[8:0] start_byte = 9'b000000011;
for(int i=8;i>=0;i--) begin
fork
begin
wait_data_hd_time();
i2c_if.sda_master=start_byte[i];
end
begin
repeat(i2c_clk_low) @(posedge i2c_if.CLK);
i2c_if.scl_master = 1;
end
join
repeat(i2c_clk_high) @(posedge i2c_if.CLK);
i2c_if.scl_master = 0;
end
//->i2c_if.event_master_start_byte_transmited;
re_start_gen();
endtask : send_start_byte

7bit地址传输

  • 一个bit位的传输逻辑

    前面start开启的时候scl已经置0了,数据可以直接放上来;

    因为wait_data_hd_time()等待dat_hd个外部时钟,远小于i2c_clk_low。所以在scl低电平期间,数据已经建立;

    然后将scl置1,高电平维持i2c_clk_high个外部时钟;

    最后将scl置0,一次bit传输结束

    可以看到:每次开始,或者1bit传输结束后,scl都置为0,以等待下一个bit的传输

  • 一个byte的传输逻辑

    传完7bit地址和1bit的rw,在下一个高电平期间采样,采样sda判断是否为1

    为1则有应答,为0则无应答

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
task lvc_i2c_master_driver_common::rw_slave_7bit_addr(bit[6:0] addr, bit rw);
for(int i=6;i>=0;i--) begin
fork
begin
wait_data_hd_time();
i2c_if.sda_master = addr[i];
end
begin
repeat(i2c_clk_low) @(posedge i2c_if.CLK);
i2c_if.scl_master = 1;
end
join
repeat(i2c_clk_high) @(posedge i2c_if.CLK);
i2c_if.scl_master = 0;
end
fork
begin
wait_data_hd_time();
i2c_if.sda_master = rw;
end
begin
repeat(i2c_clk_low) @(posedge i2c_if.CLK);
i2c_if.scl_master = 1;
end
join
repeat(i2c_clk_high) @(posedge i2c_if.CLK);
i2c_if.scl_master = 0;
fork
begin
// wait_data_hd_time();
// i2c_if.sda_master = 1'bz;
end
begin
repeat(i2c_clk_low) @(posedge i2c_if.CLK);
i2c_if.scl_master = 1;
if(i2c_if.sda_master !=0)
nack_flag = 1;
else
nack_flag = 0;
//`uvm_error("master driver sent 7 bit slave addr","slave send no ack to master.")
end
join

repeat(i2c_clk_high) @(posedge i2c_if.CLK);
i2c_if.scl_master = 0;
endtask : rw_slave_7bit_addr

10bit地址传输

两个字节,明显要复杂一些,但是串行驱动的本质不变

和7位地址的区别在于:10位地址的读需要重启

写的话类似:

  • 先组装第一个字节数组,传第一个字节,然后再传第二个low_address字节

读的话有点意思:

  • 第一个字节的读写位同样是0(写),然后再传第二个low_address字节
  • 想要修改读写位只能重启re_start_gen(),只需要重传高8位,将读写位改为1
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
task lvc_i2c_master_driver_common::rw_slave_10bit_addr(bit [9:0] addr, bit rw);
bit[7:0] wr_fst_byte = {5'b11110,addr[9:8],1'b0};
bit[7:0] rd_fst_byte = {5'b11110,addr[9:8],1'b1};

if(rw == 0) begin //write data to 10bits address slave
for(int i=7;i>=0;i--) begin //write 10bits address high 2bit
fork
begin
wait_data_hd_time();
i2c_if.sda_master = wr_fst_byte[i];
end
begin
repeat(i2c_clk_low) @(posedge i2c_if.CLK);
i2c_if.scl_master = 1;
end
join
repeat(i2c_clk_high) @(posedge i2c_if.CLK);
i2c_if.scl_master = 0;
end
fork
begin
// wait_data_hd_time();
// i2c_if.sda_master = 1'bz;
end
begin
repeat(i2c_clk_low) @(posedge i2c_if.CLK);
i2c_if.scl_master = 1;
if(i2c_if.sda_master !=0)
nack_flag=1;
else
nack_flag=0;
//`uvm_error("master driver sent 10 bit slave addr","slave send no ack to master.")
end
join
repeat(i2c_clk_high) @(posedge i2c_if.CLK);
i2c_if.scl_master = 0;
////////////
for(int i=7;i>=0;i--) begin //write 10bits address low 8bit
fork
begin
wait_data_hd_time();
i2c_if.sda_master = addr[i];
end
begin
repeat(i2c_clk_low) @(posedge i2c_if.CLK);
i2c_if.scl_master = 1;
end
join
repeat(i2c_clk_high) @(posedge i2c_if.CLK);
i2c_if.scl_master = 0;
end
fork
begin
// wait_data_hd_time();
// i2c_if.sda_master = 1'bz;
end
begin
repeat(i2c_clk_low) @(posedge i2c_if.CLK);
i2c_if.scl_master = 1;
if(i2c_if.sda_master !=0)
nack_flag=1;
else
nack_flag=0;
//`uvm_error("master driver sent 10 bit slave addr","slave send no ack to master.")
end
join
repeat(i2c_clk_high) @(posedge i2c_if.CLK);
i2c_if.scl_master = 0;
end
else begin //read data from 10bits address slave
for(int i=7;i>=0;i--) begin //write 10bits address high 2bit
fork
begin
wait_data_hd_time();
i2c_if.sda_master = wr_fst_byte[i];
end
begin
repeat(i2c_clk_low) @(posedge i2c_if.CLK);
i2c_if.scl_master = 1;
end
join
repeat(i2c_clk_high) @(posedge i2c_if.CLK);
i2c_if.scl_master = 0;
end
fork
begin
// wait_data_hd_time();
// i2c_if.sda_master = 1'bz;
end
begin
repeat(i2c_clk_low) @(posedge i2c_if.CLK);
i2c_if.scl_master = 1;
if(i2c_if.sda_master !=0)
nack_flag=1;
else
nack_flag=0;
//`uvm_error("master driver sent 10 bit slave addr","slave send no ack to master.")
end
join
repeat(i2c_clk_high) @(posedge i2c_if.CLK);
i2c_if.scl_master = 0;

for(int i=7;i>=0;i--) begin //write 10bits address low 8bit
fork
begin
wait_data_hd_time();
i2c_if.sda_master = addr[i];
end
begin
repeat(i2c_clk_low) @(posedge i2c_if.CLK);
i2c_if.scl_master = 1;
end
join
repeat(i2c_clk_high) @(posedge i2c_if.CLK);
i2c_if.scl_master = 0;
end
fork
begin
// wait_data_hd_time();
// i2c_if.sda_master = 1'bz;
end
begin
repeat(i2c_clk_low) @(posedge i2c_if.CLK);
i2c_if.scl_master = 1;
if(i2c_if.sda_master !=0)
nack_flag=1;
else
nack_flag=0;
//`uvm_error("master driver sent 10 bit slave addr","slave send no ack to master.")
end
join
repeat(i2c_clk_high) @(posedge i2c_if.CLK);
i2c_if.scl_master = 0;

re_start_gen();
for(int i=7;i>=0;i--) begin //write 10bits address high 2bit
fork
begin
wait_data_hd_time();
i2c_if.sda_master = rd_fst_byte[i];
end
begin
repeat(i2c_clk_low) @(posedge i2c_if.CLK);
i2c_if.scl_master = 1;
end
join
repeat(i2c_clk_high) @(posedge i2c_if.CLK);
i2c_if.scl_master = 0;
end
fork
begin
// wait_data_hd_time();
// i2c_if.sda_master = 1'bz;
end
begin
repeat(i2c_clk_low) @(posedge i2c_if.CLK);
i2c_if.scl_master = 1;
if(i2c_if.sda_master !=0)
nack_flag=1;
else
nack_flag=0;
//`uvm_error("master driver sent 10 bit slave addr","slave send no ack to master.")
end
join
repeat(i2c_clk_high) @(posedge i2c_if.CLK);
i2c_if.scl_master = 0;
end
endtask : rw_slave_10bit_addr

I2C_WRITE:

  • 判断模式是标准还是快速,标准模式直接执行start_gen(),判断是否发送start_byte。快速模式需要先来一套二连,然后执行re_start_gen()
  • 选择地址模式并发送地址,处理非应答情况
  • 发送transcation中的数据,每个字节都要处理非应答情况
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
I2C_WRITE:
for(int retry_num=0; retry_num<=num_of_retry; ) begin
nack_flag = 0;
if(cfg.bus_speed == HIGHSPEED_MODE) begin
if(cfg.start_hs_in_fm_plus==0) begin
pre_hs_in_fm_start_gen();
pre_hs_in_fm_send_byte(hs_code);
end else begin
pre_hs_in_fm_plus_start_gen();
pre_hs_in_fm_plus_send_byte(hs _code);
end

re_start_gen();

if(trans.addr_10bit ==1)
rw_slave_10bit_addr(trans.addr, 0);
else
rw_slave_7bit_addr(trans.addr[6:0], 0);

// check and response when addr/send_start_byte nack generated by slave
if(nack_flag==1) begin
if(num_of_retry==0 | !trans.retry_if_nack) begin
stop_gen();
return;
end
else if(retry_num==num_of_retry) begin
retry_num++;
continue;
end
else begin
retry_num++;
if(trans.sr_or_p_gen==0) //0--gen sotp, 1--gen re-start
stop_gen();
else
re_start_gen();
continue;
end
end //if(nack_flag==1)

for(int i=0; i<trans.data.size();i++)
begin
send_byte(trans.data[i]);
// check and response when data nack generated by slave
if(nack_flag==1) begin
if(num_of_retry==0 | !trans.retry_if_nack) begin
stop_gen();
return;
end
else if(retry_num==num_of_retry) begin
retry_num++;
continue;
end
else begin
retry_num++;
if(trans.sr_or_p_gen==0) //0--gen sotp, 1--gen re-start
stop_gen();
else
re_start_gen();
break;
end
// if(trans.retry_if_nack) begin
// re_start_gen();
// break;
// end
// else begin
// stop_gen();
// return;
// end
end //if(nack_flag==1)
end//for(int i=0; i<trans.data.size();i++)

if(nack_flag==0 | retry_num==num_of_retry+1) begin
if(trans.sr_or_p_gen==0) //0--gen sotp, 1--gen re-start
stop_gen();
else
re_start_gen();
break;
end //if(nack_flag==0)
end //if(cfg.bus_speed == HIGHSPEED_MODE)
else begin
start_gen();

if(trans.send_start_byte == 1)
send_start_byte();

if(trans.addr_10bit ==1) begin
is10bits_again=1;
pre_addr=trans.addr;

rw_slave_10bit_addr(trans.addr, 0);
end else
rw_slave_7bit_addr(trans.addr[6:0], 0);

// check and response when addr/send_byte nack generated by slave
if(nack_flag==1) begin
if(num_of_retry==0 | !trans.retry_if_nack) begin
stop_gen();
return;
end
else if(retry_num==num_of_retry) begin
retry_num++;
continue;
end
else begin
retry_num++;
if(trans.sr_or_p_gen==0) //0--gen sotp, 1--gen re-start
stop_gen();
else
re_start_gen();
continue;
end
// if(retry_num==trans.num_of_retry | !trans.retry_if_nack) begin
// stop_gen();
// return;
// end
// else begin
// re_start_gen();
// continue;
// end
end //if(nack_flag==1)

for(int i=0; i<trans.data.size();i++)
begin
send_byte(trans.data[i]);
// check and response when data nack generated by slave
if(nack_flag==1) begin
if(num_of_retry==0 | !trans.retry_if_nack) begin
stop_gen();
return;
end
else if(retry_num==num_of_retry) begin
retry_num++;
continue;
end
else begin
retry_num++;
if(trans.sr_or_p_gen==0) //0--gen sotp, 1--gen re-start
stop_gen();
else
re_start_gen();
break;
end
end
end //for(int i=0; i<trans.data.size();i++)

if(nack_flag==0 | retry_num==num_of_retry+1) begin
if(trans.sr_or_p_gen==0) //0--gen sotp, 1--gen re-start
stop_gen();
else
re_start_gen();
break;
end //if(nack_flag==0)
end //else high_speed
end //for(int i=0; i<=trans.num_of_retry; i++)

I2C_READ:

和I2C_WRITE完全一致

I2C_GEN_CALL:

只需要考虑标准模式:

  • 执行start_gen();
  • 判断是否发送start_byte
  • 发送第一个字节+非应答处理+发送第二个信号+非应答处理
  • 循环发送data字节+非应答处理
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
I2C_GEN_CALL: 
for(int retry_num=0; retry_num<=num_of_retry;) begin
start_gen();

if(trans.send_start_byte==1)
send_start_byte();

send_byte(gen_call_first_byte);
// check and response when addr/send_start_byte nack generated by slave
if(nack_flag==1) begin
if(num_of_retry==0 | !trans.retry_if_nack) begin
stop_gen();
return;
end
else if(retry_num==num_of_retry) begin
retry_num++;
continue;
end
else begin
retry_num++;
if(trans.sr_or_p_gen==0) //0--gen sotp, 1--gen re-start
stop_gen();
else
re_start_gen();
continue;
end
end //if(nack_flag==1)
//->i2c_if.event_master_general_call_addr_sent;
send_byte(gen_call_second_byte);
// check and response when addr/send_start_byte nack generated by slave
if(nack_flag==1) begin
if(num_of_retry==0 | !trans.retry_if_nack) begin
stop_gen();
return;
end
else if(retry_num==num_of_retry) begin
retry_num++;
continue;
end
else begin
retry_num++;
if(trans.sr_or_p_gen==0) //0--gen sotp, 1--gen re-start
stop_gen();
else
re_start_gen();
continue;
end
end
//->i2c_if.event_master_general_call_sec_byte_sent;
//if(gen_call_second_byte[0]==1'b1) begin
//if send_start_byte==1,send start byte.

for(int i=0; i<trans.data.size();i++)
begin
send_byte(trans.data[i]);
// check and response when data nack generated by slave
if(nack_flag==1) begin
if(num_of_retry==0 | !trans.retry_if_nack) begin
stop_gen();
return;
end
else if(retry_num==num_of_retry) begin
retry_num++;
continue;
end
else begin
retry_num++;
if(trans.sr_or_p_gen==0) //0--gen sotp, 1--gen re-start
stop_gen();
else
re_start_gen();
break;
end
end // if(nack_flag==1)
end

if(nack_flag==0 | retry_num==num_of_retry+1) begin
if(trans.sr_or_p_gen==0) //0--gen sotp, 1--gen re-start
stop_gen();
else
re_start_gen();
break;
end //if(nack_flag==0)
end

I2C_DEVICE_ID:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
I2C_DEVICE_ID: 
for(int retry_num=0; retry_num<=num_of_retry; ) begin
start_gen();
send_byte(device_id_w);
// check and response when addr/send_start_byte nack generated by slave
if(nack_flag==1) begin
if(num_of_retry==0 | !trans.retry_if_nack) begin
stop_gen();
return;
end
else if(retry_num==num_of_retry) begin
retry_num++;
continue;
end
else begin
retry_num++;
if(trans.sr_or_p_gen==0) //0--gen sotp, 1--gen re-start
stop_gen();
else
re_start_gen();
continue;
end
end

if(trans.addr_10bit ==1)
rw_slave_10bit_addr(trans.addr, 0);
else
rw_slave_7bit_addr(trans.addr[6:0], 0);
//ack/noack don't care

if(trans.m_device_id_gen_stop==0) begin
re_start_gen();
send_byte(device_id_r);
// check and response when addr/send_start_byte nack generated by slave
if(nack_flag==1) begin
if(num_of_retry==0 | !trans.retry_if_nack) begin
stop_gen();
return;
end
else if(retry_num==num_of_retry) begin
retry_num++;
continue;
end
else begin
retry_num++;
if(trans.sr_or_p_gen==0) //0--gen sotp, 1--gen re-start
stop_gen();
else
re_start_gen();
continue;
end
end //if(nack_flag==1)

if(trans.device_id_rollback_iteration ==0) begin //no rollback
case(trans.nack_at_device_id_byte)
1:
begin
recv_byte_noack(recv_data);
trans.data[0] = recv_data;

if(trans.sr_or_p_gen==0 | retry_num==num_of_retry+1 | retry_num==0) begin //0--gen sotp, 1--gen re-start
retry_num++;
stop_gen();
end
else
re_start_gen();
break;
end
2:
begin
recv_byte(recv_data);
trans.data[0] = recv_data;
recv_byte_noack(recv_data);
trans.data[1] = recv_data;

if(trans.sr_or_p_gen==0 | retry_num==num_of_retry+1 | retry_num==0) begin //0--gen sotp, 1--gen re-start
retry_num++;
stop_gen();
end
else
re_start_gen();

break;
end
default:
begin
recv_byte(recv_data);
trans.data[0] = recv_data;
recv_byte(recv_data);
trans.data[1] = recv_data;
recv_byte_noack(recv_data);
trans.data[2] = recv_data;

if(trans.sr_or_p_gen==0 | retry_num==num_of_retry+1 | retry_num==0) begin //0--gen sotp, 1--gen re-start
retry_num++;
stop_gen();
end
else
re_start_gen();

break;
end
endcase
end //if(trans.device_id_rollback_iteration ==0)
else begin //rollback
for(int i=0;i<trans.nack_at_device_id_byte-1;i++) begin
recv_byte(recv_data);
trans.data[i] = recv_data;
end
recv_byte_noack(recv_data);
trans.data[trans.nack_at_device_id_byte-1] = recv_data;

if(trans.sr_or_p_gen==0 | retry_num==num_of_retry+1 | retry_num==0) begin //0--gen sotp, 1--gen re-start
retry_num++;
stop_gen();
end
else
re_start_gen();

break;
end//else begin //rollback
end//if(trans.m_device_id_gen_stop==0)
else begin
if(trans.sr_or_p_gen==0 | retry_num==num_of_retry+1 | retry_num==0) begin //0--gen sotp, 1--gen re-start
retry_num++;
stop_gen();
end
else
re_start_gen();
break;
end
end

master_driver

核心方法是 : run_phase中的consume_from_seq_item_port()

调用的核心驱动方式是子类的:common.send_xact(trans);

没有通过seq_item_port.item_done(rsp)的方式返回rsp,而是调用方法put_response_to_seq_item_port(lvc_i2c_master_transaction xact, int drop = 0);

该方法内部声明了一个带锁线程,当多个master同时驱动时

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
`ifndef LVC_I2C_MASTER_DRIVER_SVH
`define LVC_I2C_MASTER_DRIVER_SVH

class lvc_i2c_master_driver extends uvm_driver #(lvc_i2c_master_transaction);

uvm_analysis_port #(lvc_i2c_master_transaction) trans_port;

lvc_i2c_master_driver_common common;

lvc_i2c_agent_configuration cfg_snapshot;

local semaphore collect_xact;

local lvc_i2c_agent_configuration cfg;

uvm_event_pool event_pool;

`uvm_register_cb(lvc_i2c_master_driver, lvc_i2c_master_driver_callback)
// This macro performs UVM object creation, type control manipulation, and
// factory registration
`uvm_component_utils_begin(lvc_i2c_master_driver)
// USER: Register fields here
`uvm_field_object(cfg, UVM_ALL_ON)
`uvm_component_utils_end

extern function new (string name, uvm_component parent);

extern function void build_phase(uvm_phase phase);

extern task reconfigure_via_task(lvc_i2c_configuration cfg);
extern virtual protected task source_events();

extern task consume_from_seq_item_port();

extern virtual task post_seq_item_get_cb_exec(lvc_i2c_master_transaction xact, ref bit drop);

extern task put_response_to_seq_item_port(lvc_i2c_master_transaction xact, int drop = 0);

/**
* This task assign the virtual interfaces to #common
*/
extern function void assign_vif();
/** @endcond */

/** Run phase of UVM which calls consume_from_seq_item_port task */
extern task run_phase(uvm_phase phase);


endclass : lvc_i2c_master_driver

function lvc_i2c_master_driver::new (string name, uvm_component parent);
super.new(name, parent);
collect_xact = new(1);
trans_port = new("trans_port",this);
endfunction

function void lvc_i2c_master_driver::build_phase(uvm_phase phase);
`uvm_info("build_phase", $sformatf("%s: starting...",get_type_name()), UVM_LOW)
if(!uvm_config_db#(lvc_i2c_agent_configuration)::get(this, "", "cfg", cfg)) begin
`uvm_fatal("build_phase", "Unable to get the master agent configuration and failed to extract config info from the object")
end

if(cfg.i2c_if == null) begin
`uvm_fatal("build_phase", "A virtual interface was not received through the config object")
end

common = lvc_i2c_master_driver_common::type_id::create("common");
common.cfg = this.cfg;
this.assign_vif();

event_pool = new("event_pool");
if(cfg.enable_driver_events == 1) begin
TX_XACT_CONSUMED = event_pool.get("TX_XACT_CONSUMED");
EVENT_START_GENERATED = event_pool.get("EVENT_START_GENERATED");
EVENT_STOP_GENERATED = event_pool.get("EVENT_STOP_GENERATED");
EVENT_ACK_GENERATED = event_pool.get("EVENT_ACK_GENERATED");
EVENT_NACK_GENERATED = event_pool.get("EVENT_NACK_GENERATED");
EVENT_ACK_RECEIVED = event_pool.get("EVENT_ACK_RECEIVED");
EVENT_NACK_RECEIVED = event_pool.get("EVENT_NACK_RECEIVED");
EVENT_REPEATED_START_GENERATED = event_pool.get("EVENT_REPEATED_START_GENERATED");
EVENT_START_BYTE_TRANSMITED = event_pool.get("EVENT_START_BYTE_TRANSMITED");
EVENT_GENERAL_CALL_ADDR_SENT = event_pool.get("EVENT_GENERAL_CALL_ADDR_SENT");
EVENT_GENERAL_CALL_SEC_BYTE_SENT = event_pool.get("EVENT_GENERAL_CALL_SEC_BYTE_SENT");
EVENT_ARBITRATION_LOSS_DETECTED = event_pool.get("EVENT_ARBITRATION_LOSS_DETECTED");
end
`uvm_info("build_phase", $sformatf("%s: finishing...",get_type_name()), UVM_LOW)
endfunction

task lvc_i2c_master_driver::reconfigure_via_task(lvc_i2c_configuration cfg);
if(!$cast(this.cfg, cfg))
`uvm_fatal("CASTFAIL", "I2C configuration handle type inconsistence")
common.reconfigure_via_task(cfg);
endtask

function void lvc_i2c_master_driver::post_seq_item_get(lvc_i2c_master_transaction xact, ref bit drop);
// EMPTY
endfunction

task lvc_i2c_master_driver::source_events();
if(cfg.enable_driver_events == 1) begin
end
endtask

task lvc_i2c_master_driver::consume_from_seq_item_port();
lvc_i2c_master_transaction trans;
lvc_i2c_master_transaction trans_out;
bit success = 0;
bit drop = 0;
forever begin
trans = new();
`uvm_info("consume_from_seq_item_port", "Get request item from sequencer...", UVM_DEBUG)
seq_item_port.get_next_item(trans);

`uvm_info("consume_from_seq_item_port", "Got request item from sequencer...", UVM_DEBUG)
// TODO:: if trans.exception_list or randomized_transaction_exception_list ??
// branches open here
drop = 0;
// callback called for loading exceptions from callbacks
post_seq_item_get_cb_exec(trans, drop);

if(drop) begin
`uvm_info("consume_from_seq_item_port", "Drop bit set through post_seq_item_get callback. Transaction dropped", UVM_DEBUG)
// put response to seq_item_port
seq_item_port.put_response(trans);
end
else begin
// Fatal if transaction received from sequencer is not valid
if(!trans.is_valid()) begin
`uvm_fatal("consume_from_seq_item_port", "Transaction received from the sequencer failed the is_valid() check")
end
// Drive transaction to bus
//common.send_xact(trans, trans.get_type_name());
common.send_xact(trans);
// Put response object to seq_item_port
put_response_to_seq_item_port(trans);
`uvm_info("consume_from_seq_item_port", $sformatf("lvc_i2c_master_driver: Driving transaction with cmd %s", trans.cmd), UVM_LOW)
$cast(trans_out,trans.clone());
trans_port.write(trans_out);
if(trans_out != null) begin
$display("mater driver get trans from seq, print_driver_trans");
trans_out.print();
end
drop = 0;
end
// Acknowledge item_done() to sequencer
seq_item_port.item_done();
`uvm_info("consume_from_seq_item_port", "Transaction Process in Driver Complete...", UVM_DEBUG)
end
endtask

task lvc_i2c_master_driver::post_seq_item_get_cb_exec(lvc_i2c_master_transaction xact, ref bit drop);
post_seq_item_get(xact, drop);
`uvm_do_callbacks(lvc_i2c_master_driver, lvc_i2c_master_driver_callback, post_seq_item_get(this, xact, drop))
endtask

task lvc_i2c_master_driver::put_response_to_seq_item_port(lvc_i2c_master_transaction xact, int drop = 0);
lvc_i2c_master_transaction resp;
if(cfg.enable_put_response) begin
fork
begin : collect_and_put_response_thread
// Grap semaphore
collect_xact.get();
// Cast received xact to local resp
if(!$cast(resp, xact.clone()))
`uvm_error("put_response_to_seq_item_port", "Failed attempting to cast response object in Master Driver")
// Collect response attributes values from virtual interface
//
common.collect_response_from_vif(resp);
resp.set_id_info(xact);
// Put response to seq_item_port
seq_item_port.put_response(resp);
// Release semphore
collect_xact.put();
end // collect_and_put_response_thread
join_none
end // if(cfg.enable_put_response)
endtask

function void lvc_i2c_master_driver::assign_vif();
common.assign_vif(cfg.i2c_if);
endfunction

task lvc_i2c_master_driver::run_phase(uvm_phase phase);
`uvm_info("run_phase", "lvc_i2c_master_driver::Starting...", UVM_LOW)
`uvm_info("run_phase", "Wait for Reset...", UVM_DEBUG)
common.wait_for_reset();
`uvm_info("run_phase", "Wait for Observed...", UVM_DEBUG)
fork
source_events();
//join_none
consume_from_seq_item_port();
`uvm_info("run_phase", "lvc_i2c_master_driver::Finishing...", UVM_LOW)
join_none
endtask

`endif // LVC_I2C_MASTER_DRIVER_SVH

slave_driver_commom

相对清晰很多

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
class lvc_i2c_slave_driver_common extends lvc_i2c_bfm_common;

`uvm_object_utils_begin(lvc_i2c_slave_driver_common)
`uvm_object_utils_end
logic start_flag = 0;
logic end_flag = 0;
logic rs_flag = 0;
int m=0;
int nack_addr_count;
bit[9:0] tmp_data;
bit[7:0] mon_data[$];
int j=0;
semaphore lock; //if current send the restart cmd, next transaction need not to send start cmd

extern function new(string name = "lvc_i2c_slave_driver_common");

extern task slave_start(); //SLAVE start generation
extern task slave_end(lvc_i2c_slave_transaction trans); //SLAVE end generation
extern task data_ana(lvc_i2c_slave_transaction trans); //analysis collected data from sda line.

extern virtual task send_xact(lvc_i2c_slave_transaction trans);
extern virtual task collect_response_from_vif(lvc_i2c_slave_transaction trans);
endclass

slave_start()

对总线进行采样,满足开始条件就尝试上锁,由当前slave执行;

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
task lvc_i2c_slave_driver_common::slave_start();

forever begin
@(negedge i2c_if.SDA);
begin
if(i2c_if.SCL==1)
begin
if(lock.try_get()) begin
start_flag = 1;
@(posedge i2c_if.SCL);

end_flag = 0;
//rs_flag=0;
end
else begin
// @(negedge i2c_if.SDA);
rs_flag = 1;
end_flag = 0;
//start_flag = 0;
end
end
end
end
endtask : slave_start

slave_end()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
task lvc_i2c_slave_driver_common::slave_end(lvc_i2c_slave_transaction trans);

forever begin
@(posedge i2c_if.SDA);
begin
if(i2c_if.SCL==1) begin
trans.data= mon_data;
mon_data= {};
j=0;
// @(posedge i2c_if.SDA);
lock.put();
end_flag = 1;
return;
end
end
end
endtask : slave_end

data_ana(trans)

从端驱动的核心方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
task lvc_i2c_slave_driver_common::data_ana(lvc_i2c_slave_transaction trans);  //check all protocol and collect write/read data to transaction
bit ack_nak;
int n=0;
int deviceid=0;
bit flag_1=0;
nack_addr_count=trans.nack_addr_count;
forever
begin
wait(start_flag|rs_flag);
begin
if(rs_flag)
rs_flag=0;
end
start_flag =0;
for(int i=7;i>=0;i--) begin
@(posedge i2c_if.SCL);
mon_data[j][i] = i2c_if.SDA;
end

casex(mon_data[j]) //check first byte
8'b0000_001x,8'b0000_010x,8'b0000_011x:
begin
`uvm_error("slave driver common","slave driver receive reserved address")
//send nack to master
@(negedge i2c_if.SCL);
wait_data_hd_time();
i2c_if.sda_slave = 1'b1;
@(negedge i2c_if.SCL);
// i2c_if.sda_slave = 1'bz;
end
8'b0000_0000: //general call address --done

8'b0000_0001: //start byte-done
begin
j=0;
// flag_1=0;
start_flag=0;
continue;
end
8'b0000_1xxx: //hs-mode slave code-done
begin
//send nack to master
@(negedge i2c_if.SCL);
wait_data_hd_time();
i2c_if.sda_slave = 1'b1;
@(negedge i2c_if.SCL);
// i2c_if.sda_slave = 1'bz;
j=0; continue;
end
8'b1111_1xxx: //device ID, --done

8'b1111_0xxx: //10-bit slave addressing--done
begin
if(mon_data[j][2:1] == cfg.slave_address[9:8]) begin //10bit address high two bits match
//send ack and continue receive or transmit data
@(negedge i2c_if.SCL);
wait_data_hd_time();
i2c_if.sda_slave = trans.nack_addr;
if(mon_data[j][0]==0) begin //10bit address first byte,write cmd, coutinue receive the second byte, low 8 bits address
@(negedge i2c_if.SCL);
wait_data_hd_time();
i2c_if.sda_slave = 1'bz;

// j++;
//receive the second byte and check if it's available
for(int i=7;i>=0;i--) begin
@(posedge i2c_if.SCL);
mon_data[j][i] = i2c_if.SDA;
end

if(mon_data[j] == cfg.slave_address[7:0]) begin //slave low 8 bits address match,continue receive data
@(negedge i2c_if.SCL);
wait_data_hd_time();
i2c_if.sda_slave = trans.nack_addr;
@(negedge i2c_if.SCL);
wait_data_hd_time();
i2c_if.sda_slave = 1'bz;
m=8;
//j++;
while((!end_flag) & (!rs_flag)) begin
@(posedge i2c_if.SCL);
tmp_data[m] = i2c_if.SDA;
@(negedge i2c_if.SCL);
m--;
if(m==0) begin
wait_data_hd_time();
i2c_if.sda_slave = 1'b0;
mon_data[j] = tmp_data[8:1];
m=8;
j++;
@(negedge i2c_if.SCL);
wait_data_hd_time();
i2c_if.sda_slave = 1'bz;
end
end
if(rs_flag & j!=0) begin
trans.data=mon_data;
mon_data={};
j=0;
$display("slv_driver trans data is %p",trans.data);
end

end //second byte match end
else begin
//send nack and break
@(negedge i2c_if.SCL);
wait_data_hd_time();
i2c_if.sda_slave = 1'b1;
@(negedge i2c_if.SCL);
wait_data_hd_time();
i2c_if.sda_slave = 1'bz;
break;
end //second byte not match end
end //10 bits address write end
else if(mon_data[j][0]==1) begin //10bit address read
//trans.data=new[3];
m=8;n=0;
while((!end_flag) & (!rs_flag)) begin
//send data to master
@(negedge i2c_if.SCL);
wait_data_hd_time();
i2c_if.sda_slave=trans.data[n][m-1];
@(posedge i2c_if.SCL);
m--;
if(m==0) begin
m=8;
n++;
@(negedge i2c_if.SCL);
wait_data_hd_time();
i2c_if.sda_slave = 1'bz; // release bus to mst,
end
end //while end
end //10 bits address read end
end //match end
else begin //higt two bits not match
//send nack and break;
@(negedge i2c_if.SCL);
wait_data_hd_time();
i2c_if.sda_slave = 1'b1;
@(negedge i2c_if.SCL);
wait_data_hd_time();
i2c_if.sda_slave = 1'bz;
continue;
end
end
default: //7bit slave address read or write, should seperate read and write, --done
begin
if(mon_data[j][7:1] == cfg.slave_address[6:0]) begin
if(nack_addr_count>0) begin
//send ack to master
@(negedge i2c_if.SCL);
// wait_data_hd_time();
i2c_if.sda_slave = trans.nack_addr;

// release bus for sr from mst
@(negedge i2c_if.SCL);
// i2c_if.sda_slave = 1'bz;
//till end of sr, then sample addr again
// @(negedge i2c_if.SCL);
// end_flag=0;
nack_addr_count--;
end else begin //nack_addr_count!>0
//send ack to master
@(negedge i2c_if.SCL);
wait_data_hd_time();
i2c_if.sda_slave = 1'b0;
if(mon_data[j][0]==0) begin //write
@(negedge i2c_if.SCL);
wait_data_hd_time();
i2c_if.sda_slave = 1'bz;
m=8; j=0;mon_data = {};
while((!end_flag) & (!rs_flag)) begin
@(posedge i2c_if.SCL);
tmp_data[m] = i2c_if.SDA;
@(negedge i2c_if.SCL);
m--;
if(m==0) begin
wait_data_hd_time();
i2c_if.sda_slave = (trans.nack_data==j+1) ? 1'b1 : 1'b0; //send ack to master driver
mon_data[j] = tmp_data[8:1];
m=8;
j++;
@(negedge i2c_if.SCL);
wait_data_hd_time();
i2c_if.sda_slave = 1'bz;
end
end //end while
if(rs_flag & j!=0) begin
trans.data=mon_data;
mon_data={};
j=0;
$display("slv_driver trans data is %p",trans.data);
end
end //end write
else if(mon_data[j][0]==1) begin //read
if(deviceid==1) begin
//send ack and continue receive or transmit data
//@(negedge i2c_if.SCL);
//i2c_if.sda_slave = trans.nack_addr;
@(negedge i2c_if.SCL);
wait_data_hd_time();
i2c_if.sda_slave = 1'bz; // release bus to mst,
@(negedge i2c_if.SCL);
end else begin

m=8;n=0;
while((!end_flag) & (!rs_flag)) begin
//send data to master
@(negedge i2c_if.SCL);
wait_data_hd_time();
i2c_if.sda_slave=trans.data[n][m-1];
@(posedge i2c_if.SCL);
m--;
if(m==0) begin
m=8;
n++;
@(negedge i2c_if.SCL);
wait_data_hd_time();
i2c_if.sda_slave = 1'bz; // release bus to mst,
@(posedge i2c_if.SCL);
if(i2c_if.SDA==0)
continue;
else
break;
end
end //while end
end // end
end //read end
end // nack_addr_count end

end //address is valid end
else begin
//send nack to master
@(negedge i2c_if.SCL);
wait_data_hd_time();
i2c_if.sda_slave = 1'b1;
@(negedge i2c_if.SCL);
// i2c_if.sda_slave = 1'bz;
break;
end
end //default end
endcase
end
endtask : data_ana

8’b 0000_0000(广播)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
begin
trans.cmd = I2C_GEN_CALL;
//send ack to master
@(negedge i2c_if.SCL);
wait_data_hd_time();
i2c_if.sda_slave = 1'b0;
@(negedge i2c_if.SCL);
// i2c_if.sda_slave = 1'bz;
//collect second byte
j++;
for(int i=7;i>=0;i--) begin
@(posedge i2c_if.SCL);
mon_data[j][i] = i2c_if.SDA;
end
//send ack to master
@(negedge i2c_if.SCL);
wait_data_hd_time();
i2c_if.sda_slave = 1'b0;
@(negedge i2c_if.SCL);
// i2c_if.sda_slave = 1'bz;
m=8; j=0;
while((!end_flag) & (!rs_flag)) begin
@(posedge i2c_if.SCL);
tmp_data[m] = i2c_if.SDA;
@(negedge i2c_if.SCL);
m--;
if(m==0) begin
mon_data[j] = tmp_data[8:1];
wait_data_hd_time();
i2c_if.sda_slave = 1'b0;
m=8;
j++;
@(negedge i2c_if.SCL);
// i2c_if.sda_slave = 1'bz;
end
end
if(rs_flag & j!=0) begin
trans.data=mon_data; //if rs_flag==1 in this step, should sent data to trans
mon_data = {};
j=0;
$display("slv_driver trans data is %p",trans.data);
end
end

8’b1111_1xxx(device_id)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
begin
deviceid=1;
//send ack and continue receive or transmit data
@(negedge i2c_if.SCL);
wait_data_hd_time();
i2c_if.sda_slave = 1'b0;
@(negedge i2c_if.SCL);
// i2c_if.sda_slave = 1'bz;
trans.cmd = I2C_DEVICE_ID;
if(mon_data[j][0]==0) begin //devide ID first byte, write slave address followed
//continue receive slave address, the address is 7 bits or 10 bits
j=0;
continue;
end
else if(mon_data[j][0]==1) begin //device ID byte followed re-start, read 3 bytes ID
j=8;
while((!end_flag) & (!rs_flag)) begin
//for(int i=7; i>=0; i--) begin
for(int i=23; i>=0; i--) begin
wait_data_hd_time();
i2c_if.sda_slave = cfg.device_id[i];
@(negedge i2c_if.SCL);
j--;
if(j==0) begin
j=8;
// i2c_if.sda_slave = 1'bz; // release bus to mst,
@(negedge i2c_if.SCL);
end
end
end
end
end