I2C虚拟项目笔记(二)-virtual sequence实战:中断与异常场景构建

I2C虚拟项目笔记(二)-virtual sequence实战:中断与异常场景构建 1. 为什么需要模拟中断与异常场景在实际的I2C总线通信中各种异常情况时有发生。比如从设备突然掉电导致无应答NACK或者主设备在发送数据时遭遇干扰导致传输中断。这些场景如果不在验证阶段充分覆盖就可能给产品埋下隐患。我记得去年参与一个智能家居项目时就遇到过类似问题。当时设备在实验室测试一切正常但量产后发现部分产品在高温环境下会偶发通信失败。后来排查发现是I2C从设备在极端温度下响应变慢导致主设备误判为NACK。如果当初验证时能模拟这种极端场景就能提前发现问题。通过virtual sequence构建这些异常场景可以系统性地验证DUT的中断处理能力。具体来说我们需要关注7-bit和10-bit地址模式下的NACK处理数据传输过程中的异常终止RESTART功能禁用时的错误恢复各种异常触发的TX_ABRT中断是否正确产生2. 搭建验证环境的关键组件2.1 I2C VIP配置要点I2C验证IPVIP是构建虚拟场景的核心。在搭建环境时需要特别注意Slave端的响应配置。比如要模拟地址NACK就需要将Slave配置为对特定地址不响应。i2c_slave_cfg.addr_mode I2C_10BIT_ADDR; // 设置10位地址模式 i2c_slave_cfg.ignore_addr_list {10h123}; // 忽略地址0x123对于数据NACK场景可以通过控制Slave的ACK行为来实现task i2c_slave_driver::send_nack_after_byte(bit[7:0] target_byte); // 当收到特定数据字节时返回NACK endtask2.2 APB总线寄存器配置中断相关的寄存器配置主要通过APB总线完成。关键寄存器包括寄存器名称地址偏移功能描述IC_CON0x00控制寄存器设置工作模式IC_TX_ABRT_SOURCE0x80记录传输终止原因IC_INTR_MASK0x2C中断屏蔽控制配置示例// 使能TX_ABRT中断 apb_write(IC_INTR_MASK, 32h0000_0004); // 设置7位地址模式 apb_write(IC_CON, 32h0000_0040);3. Virtual Sequence实战技巧3.1 构建基础传输场景在开始异常场景前先要确保基础传输功能正常。这里分享一个我常用的基础sequence模板class i2c_basic_seq extends uvm_sequence; virtual task body(); // 1. 配置寄存器 setup_registers(); // 2. 启动传输 i2c_start_seq.start(p_sequencer); // 3. 发送地址数据 send_address(); send_data(); // 4. 结束传输 i2c_stop_seq.start(p_sequencer); endtask endclass3.2 模拟地址NACK场景地址NACK是最常见的异常之一。在10-bit地址模式下模拟过程需要特别注意地址分两次发送的特性。class i2c_addr_nack_seq extends i2c_basic_seq; virtual task send_address(); // 发送地址第一部分 i2c_byte_seq.byte_data 8hF0 | (target_addr 8); i2c_byte_seq.start(p_sequencer); // 从设备返回NACK i2c_slave_seq.respond_with_nack(); // 检查中断状态 check_interrupt(TX_ABRT); endtask endclass实际项目中我发现很多工程师会忽略检查IC_TX_ABRT_SOURCE寄存器的值。这个寄存器能明确告诉我们中断原因对调试非常重要。3.3 数据NACK与传输终止数据NACK的模拟相对简单但要注意时序控制。太早或太晚发送NACK都可能导致DUT行为不同。class i2c_data_nack_seq extends i2c_basic_seq; virtual task send_data(); // 发送第一个字节正常ACK i2c_byte_seq.byte_data data_queue.pop_front(); i2c_byte_seq.start(p_sequencer); // 发送第二个字节触发NACK i2c_byte_seq.byte_data data_queue.pop_front(); fork begin i2c_byte_seq.start(p_sequencer); end begin // 在第9个时钟周期拉高SDANACK #(8*i2c_clk_period); i2c_slave_seq.force_sda_high(); end join // 检查中断和状态寄存器 check_interrupt(TX_ABRT); check_abort_source(ABRT_SLAVE_DISABLED); endtask endclass4. 波形分析与调试技巧4.1 关键信号捕获在验证中断场景时建议在波形中标记以下关键点START条件生成时刻地址/数据传输的每个字节ACK/NACK响应位置中断信号assertion时间我通常会在测试中插入一些标记事件方便后期分析// 在sequence中插入波形标记 $display(TX_ABRT中断触发时刻%t, $time); uvm_hdl_force(tb.dut.intr, 1b1); #10ns; uvm_hdl_release(tb.dut.intr);4.2 常见问题排查在实际项目中经常遇到中断未能正确触发的情况。根据我的经验可以按以下步骤排查确认IC_INTR_MASK寄存器配置正确检查IC_ENABLE寄存器是否已使能确认APB总线时钟与I2C时钟的相位关系检查TX_ABRT中断是否被其他条件屏蔽有个特别容易忽略的点是时钟域交叉问题。如果APB时钟和I2C时钟不同源可能需要额外的同步处理时间。我曾经遇到过一个案例中断信号因为跨时钟域延迟了3个周期才被APB侧捕获导致软件误判为中断丢失。