I2C_transaction extends uvm_sequence_item: 用于I2C mst和slv中,以及mon中直接例化;
枚举变量(types中定义)command_enum cmd=I2C_WRITE;包括I2C_WRITE(默认)写 I2C_READ读、I2C_GEN_CALL广播、I2C_DEVICE_ID 请求ID;在mst req和rsp中、slv rsp中使用;
变量bit [`LVC_I2C_SLA_ADD_WIDTH-1:0] addr=`LVC_I2C_SLAVE0_ADDRESS;指定要操作的slv地址,在mst req和rsp中、slv rsp中使用;`I2C_SLA_ADD_WIDTH(slv地址宽度在defines中定义)=10,`I2C_SLAVE0_ADDRESS(slv默认地址)=10'b1100110011
数组bit [7:0] data [ ] 要在总线上传输的数据;在mst req和rsp中、slv req(读时)和rsp中使用;
bit addr_10bit = 0;(开关 启用10位寻址)
int unsigned ADDR_10BIT_ON_wt = 0;控制10位寻址相对频率,随机化时加约束产生;
int unsigned ADDR_10BIT_OFF_wt= 1;控制7位寻址相对频率,随机化时加约束产生;
bit start_detected;启动1、未启动0;在mst和slv rsp中;
bit stop_detected;停止1、未停止0;……
bit ack_detected [ ];ACK1、NACK0;……
bit rep_start_detected;重复启动信号;……
bit read_write;读写信号,1读0写;……
int start_detected_st、 int start_detected_et;存放发送START状态开始、结束时间;……
int stop_detected_st、 int stop_detected_et;存放发送STOP状态开始、结束时间;……
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;存储Mst发送Slv地址的开始、结束时间;……
bit enable_cmd = 1;启动/禁用Mst发出的命令;mst和slv seq中;
bit enable_pkt = 1;启动/禁用drv使用seq data;mst和slv seq中;
bit enable_cfg = 1;启动/禁用cfg;……
bit [7:0] gnrl_arp_arg;bit drct_arp_arg; SMBUS中使用;
约束valid_ranges:如果cfg不为空&&bus_type(types)为I2C_BUS状态,cmd在上述四种模式中、data.size在[1:5120]间,否则size在[1:1024];(bus_type包括:I2C_BUS、MIPI_I3C_BUS、 SMBUS_BUS)
约束reasonable_cmd:如果cfg不为空&&bus_type为I2C_BUS状态,cmd为I2C_READ、I2C_WRITE;(默认开启可reasonable_constraint_mode控制)
约束reasonable_data:如果cfg不为空&&bus_type为I2C_BUS状态,data.size在[1:10]之间,否则size在[1:1024];(默认开启可reasonable_constraint_mode控制)
约束reasonable_addr:Slv地址默认在10'b1100110011或10'b1100110000;
约束reasonable_addr_10bit:如果控制ADDR_10BIT_OFF_wt7位寻址频率变量为1,则0元素执行1次,即关闭10位寻址开关;反之1元素执行1次,开启10位寻址;
注册、域的自动化;
函数new()、reasonable_constraint_mode(控制约束块的打开关闭)、is_valid();
is_valid()中使用`LVC_DATA_UTIL_IS_VALID_BV_W_RANGE(PROP,MIN,MAX)宏(defines)如果PROP不在MIN:MAX中 is_valid置0;
I2C_if (input bit CLK):
串行时钟SCL、串行数据SDA、串行数据SMBALRT、复位RST、enable_pullup_resistor为1将SCL SDA拉高;
三个logic变量并判断是否===0,如果为0相应信号拉低,否则保持高阻态;(必须使用===)
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;
可继续添加断言、覆盖率;
I2C_bfm_common extends uvm_object:通用特性;
例化接口、cfg、uvm_component comp; 注册;定义一些记录时间的变量;
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 总线空闲时间
函数new();assign_vif(接口与当前接口连接);任务source_event 等待event 触发;reconfigure_via_task(cfg的传递);clk_low_offset_gen(判断cfg中bus_speed类型,并赋相应的时间)
如在STANDARD_MODE模式下,将i2c_clk_high = cfg.scl_high_time_ss(在define中scl_high_time_ss约束为`LVC_I2C_CLK_HIGH_SS默认4000)同理约束了上述所有时间,以及四种模式;
任务wait_data_hd_time()当上升沿循环次数等于刚赋值的dat_hd跳出等待;wait_for_reset()等待RST释放;
I2C_slave_driver_common extends I2C_bfm_common;
I2C_slave_driver extends uvm_driver #(lvc_i2c_slave_transaction);
analysis_port;例化slave_driver_common、I2C_agent_configuration;
旗语collect_xact;确定序列阻塞或非阻塞enable_slave_blocking;
event:TX_XACT_CONSUMED(当slv_drv消耗trans);EVENT_START_DETECTED(当slv检测到START);EVENT_STOP_DETECTED(当slv检测到STOP);EVENT_ACK_RECEIVED(当slv接收到ACK);EVENT_ACK_GENERATED(当slv生成ACK)
函数new()例化旗语collect_xact(1)、analysis_port;
函数build_phase:get cfg、判断I2C_configuration中set_I2C_if是否成功;创建common并给cfg;I2C_cfg中i2c_if给i2c_common中assign_vif连接;创建event_pool;
任务consume_from_seq_item_port(主要):例化trans、trans_out;两个变量success和drop为0;forever块中创建trans→get_next_item如果trans不是空的打印出来;→判断drop是否为1,如果为1发回响应,如果为0调用trans中的is_valid函数判断数据有效→调用slave_driver_common中send_xact函数①clk_low_offset_gen判断cfg中bus_speed类型,并赋相应的时间→
②调用slave_start函数 forever块中等待一个SDA下降沿,如果此时SCL为高(启动),尝试get旗语lock中的key,get到了start_flag=1,等待一个上升沿后end_flag=0;如果没get到rs_flag=1,(重复启动条件)end_flag=0;(开始data_ana)
②并行执行slave_end(trans)函数 forever中等待一个SDA上升沿,如果此时SCL为高(停止),mon.data给trans.data、j=0恢复初始 、归还lock中的key、end_flag=1;
②并行执行data_ana数据分析;等待slave_start函数的start_flag或rs_flag,如果rs_flag=1(没get到)rs_flag置0;start_flag=0;for循环将SDA传送的从地址数据放入mon_data[ j ] [i的0:7]中(开始例化的队列);casex中
检查地址如果为8'b0000_001x,8'b0000_010x,8'b0000_011x报错误(不支持的保留地址);等待SCL下降沿、调用I2C_commom中wait_data_hd_time函数根据bus_speed中dat_hd(数据等待时间)等待上升沿;等待完毕将接口i2C_if中sda_slave置1,将SDA保持高阻态(NACK);
8'b0000_0001 START byte :j=0; start_flag=0; continue
地址如果为8'b0000_0000;I2C_GEN_CALL、等待SCL为低时,将sda_slave置为0→SDA为低(返回ACK),再等待SCL为低(一个周期); j++ ;收集后8位数据放入mon_data [j+1] [i0:7]中;再次发送ACK;如果没有收到end_flag结束和rs_flag重复启动,将SDA数据放入tmp_data[m],当m从8→0后将数据放入mon_data[j]中,并等待dat_hd,传回ACK,m重新为8,j++,继续下一组数据传输;结束后判断如果rs_flag=1(重复启动条件、slave_start将其置为1),并且j!=0(代表有数据传递),将收到的数据传给 trans.data,并清空mon_data,j=0;
8'b0000_1xxx: hs-mode slave code-done
8'b1111_1xxx;DEVICE ID;
8'b1111_0xxx: 10-bit slave addressing--done
default:不是保留位的情况;检查cfg.slave_address[6:0]与case前放入的mon_data[j][7:1]七位数据是否相同,若不相同返回NACK;→判断nack_addr_count如果>0,SDA做相应操作,如果!>0,返回ACK并判断读或写操作;
mon_data[j][0]==0写:等待wait_data_hd_time,在没有收到end_flag结束和rs_flag重复启动情况,wait_data_hd_time,返回ACK,将SDA数据放入tmp_data[m],当m从8→0后将数据放入mon_data[j]中,m重新为8,j++,结束写;结束后判断如果rs_flag=1(重复启动条件、slave_start将其置为1),并且j!=0(代表有数据传递),将收到的数据传给 trans.data,并清空mon_data,j=0;
mon_data[j][0]==1读:在没有收到end_flag结束和rs_flag重复启动情况,wait_data_hd_time,将trans.data从最高位传送到SDA到master上,如果m=0后重置m=8,n++准备传递下一个字节,释放总线给mst,如果返回SDA为低,继续传送下个字节;
send_xact结束后:调用put_response_to_seq_item_port函数,例化一个trans resp,判断cfg中需要enable_put_response时,拿取钥匙,将操作后读trans克隆给resp并强制转换,加入ID,返回resp,归还钥匙;
准备就绪将trans克隆给trans_out write出去;
reconfigure_via_task