ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

基于UVM的AHB Lite eFlash控制器验证平台设计与实现

基于UVM的AHB Lite eFlash控制器验证平台设计与实现

1. 项目概述:为什么需要一个专门的eFlash控制器验证平台?

在当前的SoC设计中,嵌入式闪存(eFlash)控制器是一个既关键又复杂的模块。它负责管理CPU对片上非易失性存储器的所有访问,包括代码的读取、数据的写入与擦除。而AHB Lite总线作为ARM AMBA协议家族中一个轻量级、高性能的片上互连标准,因其结构简单、易于集成,常被用作CPU与这类外设控制器之间的标准接口。当我们将“AHB Lite”、“eFlash控制器”和“验证”这三个词放在一起时,一个严峻的挑战就浮现了:如何确保这个负责存储“系统大脑”指令的模块,其行为在硅前阶段就是百分之百正确的?

这就是“基于UVM的AHB Lite eFlash控制器验证平台”项目的核心价值所在。它不是一个简单的测试集合,而是一个系统化的、可复用的、自动化的验证解决方案。UVM(Universal Verification Methodology)作为当前芯片验证领域的事实标准,提供了一套完整的框架,用于构建模块化、可扩展的验证环境。对于eFlash控制器这类具有复杂状态机(如编程、擦除序列)、严格时序要求(如等待周期插入)和关键数据完整性(如ECC校验)的模块,一个基于UVM的验证平台是确保其功能完备性和可靠性的不二之选。这个项目适合所有正在或即将从事数字IC验证的工程师,特别是那些需要验证存储控制器、总线接口或复杂状态机模块的同行。通过这个案例,你不仅能理解如何搭建一个标准的UVM验证环境,更能掌握针对存储类外设的特殊验证策略和技巧。

2. 平台顶层架构与组件设计思路

一个健壮的UVM验证平台,其顶层架构的设计决定了后续验证工作的效率和可维护性。对于AHB Lite eFlash控制器验证平台,我们的核心思路是“隔离”与“复用”:将激励产生、驱动监控、功能检查、参考模型等关注点分离到不同的组件中。

2.1 验证环境整体框图与数据流

整个验证环境(uvm_env)以testbench顶层模块为容器,内部实例化了待测设计(DUT,即AHB Lite eFlash控制器)、虚拟接口(virtual interface)以及UVM环境本身。数据流始于test,它负责配置并启动整个环境。test中实例化的env则像一个指挥中心,协调着以下几个关键组件协同工作:

  1. agent(代理):这是与DUT物理接口直接交互的“前线部队”。我们通常为AHB Lite总线创建一个agent,因为它是最主要的数据通道。这个agent内部包含:

    • sequencer(序列发生器):负责调度测试场景(sequence)产生的具体事务(transaction),比如读操作、写操作、配置寄存器等。它是激励的“发源地”。
    • driver(驱动器):从sequencer获取transaction,通过虚拟接口(virtual interface)将其转换成符合AHB Lite协议周期的信号时序,驱动到DUT的输入端口。它负责协议的“物理层”实现。
    • monitor(监视器):是一个被动的组件,它通过虚拟接口“窥视”DUT接口上的信号变化,无论这些信号来自driver还是DUT的输出。它将捕捉到的信号转换回抽象的transaction,并发送出去供其他组件分析。一个设计良好的monitor应该能同时捕捉输入和输出,为功能覆盖和检查提供原始数据。
  2. scoreboard(记分板):这是验证平台的“裁判”。它订阅(通常通过analysis_portanalysis_exportmonitor发送出来的transaction。对于eFlash控制器,scoreboard的核心任务是进行数据一致性检查。例如,当它收到一个AHB总线写事务(来自monitor)时,它会将这个写入的数据和地址记录在自己的内存模型中;当后续收到一个读事务时,它会从自己的内存模型中取出预期数据,与monitor捕获到的DUT实际读回数据进行比对。任何不匹配都会报告错误。

  3. reference model(参考模型):对于eFlash控制器,一个纯粹基于事务的scoreboard可能不够。因为eFlash操作(如编程、擦除)不是立即生效的,它们有复杂的内部状态和时序。因此,我们通常需要一个reference model,它是一个用高级语言(如SystemVerilog或C++)编写的、模拟DUT理想行为的“黄金模型”。reference model接收与DUT相同的输入激励(如配置命令、写入数据),并计算出预期的输出行为(如状态寄存器值、读回数据、中断产生)。scoreboard则负责比较DUT的实际输出与reference model的预期输出。

  4. coverage collector(覆盖率收集器):通常内嵌在agent或作为独立组件,用于收集功能覆盖率。对于AHB Lite接口,我们需要覆盖各种总线传输类型(IDLE, BUSY, NONSEQ, SEQ)、传输大小(HSIZE)、突发长度(HBURST)、保护控制(HPROT)等交叉场景。对于eFlash控制器本身,则需要覆盖其所有的操作命令(Program, Erase Sector/Block, Read Status等)、各种状态跳转、错误注入场景(如写保护、地址越界)以及ECC纠错/检错情况。

注意:虚拟接口(virtual interface)是连接静态的testbench模块世界和动态的UVM类世界的关键桥梁。它本质上是一个指向实际接口(interface)实例的句柄。必须在run_test()之前,通过uvm_config_db机制将实际的接口指针“传递”给UVM环境中的agent等组件。

2.2 关键组件的交互与配置机制

组件间通过TLM(Transaction Level Modeling)通信端口进行交互,这实现了高度的解耦。例如,sequencerdriver之间通过seq_item_portseq_item_export连接;monitor使用analysis_port广播它捕获的事务;scoreboardcoverage collector则通过analysis_export来订阅这些事务。

平台的灵活性和可重用性很大程度上依赖于uvm_config_db配置机制。我们可以通过它在不同层次(如testenv)设置参数,并让底层组件(如agent)获取。例如:

  • test中,我们可以配置agent是处于主动模式(ACTIVE,包含driversequencer)还是被动模式(PASSIVE,仅包含monitor),这在集成到更上层环境时非常有用。
  • 可以配置virtual interface的句柄。
  • 可以配置reference model的初始化内存镜像文件路径。
  • 可以配置scoreboard的检查使能开关,在回归测试中为提升速度可以暂时关闭深度检查。

这种架构确保了平台既能进行细致的模块级验证,其组件(尤其是agentscoreboard)也能方便地复用到芯片级(SoC)验证环境中。

3. AHB Lite VIP与eFlash事务建模

验证平台的基础设施是高效验证的前提。对于AHB Lite总线,我们通常不会从零开始编写drivermonitor,而是使用或借鉴成熟的验证IP(VIP)。

3.1 AHB Lite VIP的集成与定制

市面上有商业和开源的AHB Lite VIP可供选择。集成VIP的关键步骤包括:

  1. 接口封装:将VIP提供的interface实例化到testbench顶层,并与DUT的AHB Lite信号正确连接。
  2. 代理集成:将VIP的agent(通常是一个uvm_agent派生类)实例化到我们的env中。这取代了我们自己手写的driver/monitor
  3. 序列重用:VIP通常会提供一套基础的序列库(如简单的读、写、复位序列)。我们需要继承这些基础序列,来创建符合我们eFlash控制器特定需求的序列。例如,一个eFlash编程操作可能对应一个AHB的写突发传输。
  4. 响应处理:配置VIP的drivermonitor,使其能够正确处理DUT返回的HRESP响应(OKAY, ERROR, SPLIT, RETRY)。对于eFlash控制器,在擦除或编程期间,它可能会返回HRESP=OKAY但拉长HREADY来插入等待周期,或者在某些错误条件下返回HRESP=ERROR。VIP必须能处理这些情况。

实操心得:即使使用VIP,也强烈建议深入阅读其drivermonitor的代码。理解它如何解析协议、如何处理背压(HREADY),对于调试总线相关的问题至关重要。我曾遇到一个案例,VIP的driver在遇到HRESP=ERROR时默认会终止整个突发传输,而我们的DUT设计期望是仅终止当前传输。如果不了解这一点,就会导致测试用例的行为与预期不符。

3.2 eFlash控制器事务(Transaction)定义

uvm_sequence_item是激励的基本单元。我们需要定义一个能够描述eFlash控制器所有可能操作的事务类,比如eflash_item

class eflash_item extends uvm_sequence_item; // 操作类型枚举 typedef enum {OP_READ, OP_PROGRAM, OP_SECTOR_ERASE, OP_BULK_ERASE, OP_READ_STATUS, OP_WRITE_PROTECT} op_type_e; rand op_type_e op_type; // 操作类型 rand bit [31:0] addr; // 目标地址 rand bit [31:0] data[]; // 写入数据队列(对于读操作,则为预期数据) rand int data_size; // 数据大小(字节) rand bit inject_error; // 是否注入错误(如ECC错误、写保护) bit [31:0] status; // 状态寄存器返回值(用于读状态操作) int latency_cycles; // 操作实际消耗的时钟周期数(由monitor收集) // 约束条件 constraint addr_alignment_c { // AHB Lite地址对齐约束,根据HSIZE确定 solve op_type before addr; if (op_type inside {OP_READ, OP_PROGRAM}) { addr % (2**data_size) == 0; // 简化示例,实际需根据HSIZE约束 } } constraint data_size_c { data_size inside {1, 2, 4}; // 支持字节、半字、字访问 } `uvm_object_utils_begin(eflash_item) `uvm_field_enum(op_type_e, op_type, UVM_ALL_ON) `uvm_field_int(addr, UVM_ALL_ON) `uvm_field_array_int(data, UVM_ALL_ON) `uvm_field_int(data_size, UVM_ALL_ON) `uvm_field_int(inject_error, UVM_ALL_ON) `uvm_field_int(status, UVM_ALL_ON) `uvm_field_int(latency_cycles, UVM_ALL_ON) `uvm_object_utils_end function new(string name = "eflash_item"); super.new(name); endfunction endclass

这个eflash_item是一个高层次的抽象。在sequence中,我们生成的是eflash_item。但在发送给AHB VIP之前,需要一个adapter(适配器)或一个专门的sequencer/driver层,将eflash_item“翻译”成一个或多个具体的AHB Lite总线事务(ahb_item)。例如,一个OP_PROGRAM操作可能被翻译成:1)一个配置命令寄存器的AHB写;2)一个写入目标地址的AHB写;3)一个轮询状态寄存器的AHB读序列,直到操作完成。

4. 测试场景(Sequence)与功能覆盖率模型构建

验证的本质是施加有意义的场景并评估是否达到目标。对于eFlash控制器,我们需要设计系统的测试场景并定义清晰的功能覆盖率模型。

4.1 核心测试场景设计与实现

测试场景通过uvm_sequence来实现。我们应该从简单到复杂,从正常到异常,分层构建测试库。

  1. 基础功能测试

    • register_access_seq:验证所有控制、状态、地址、数据寄存器的读写功能,包括复位默认值、保留位行为、读写权限(RO/WO/RW)。
    • single_read_write_seq:验证对eFlash存储阵列的随机单次读、写(编程)操作。重点是数据正确性和地址对齐。
    • sequential_burst_seq:验证通过AHB Lite的INCR突发传输进行连续地址的读和写,测试控制器的地址递增逻辑和缓冲区管理。
  2. eFlash专用操作测试

    • sector_erase_seq:随机选择扇区进行擦除操作,验证擦除后该扇区所有位是否为‘1’,并验证擦除期间其他扇区的数据不被影响。
    • program_erase_suspend_resume_seq:测试编程或擦除操作的挂起与恢复功能(如果DUT支持)。这是一个典型的状态机复杂交互测试。
    • protection_seq:测试写保护、读保护功能。配置保护区域,然后尝试进行违规的写或读操作,验证DUT是否正确产生错误响应(HRESP=ERROR或特定的状态位)。
  3. 错误注入与异常测试

    • ecc_error_seq:模拟Flash物理单元出错。这通常需要与后端模型或force信号配合,在读取时注入ECC可纠正单比特错误(SEC)和不可纠正双比特错误(DED),验证控制器能否正确纠正/检测并报告状态。
    • illegal_cmd_seq:发送非法的命令序列(如擦除未解锁的扇区、在编程过程中发起新的编程),验证控制器的鲁棒性。
    • concurrent_access_seq:模拟在eFlash控制器忙于内部编程/擦除时,主机尝试访问其寄存器或其它存储区域,验证总线互斥和响应机制。

一个典型的program_seq可能长这样:

class flash_program_seq extends uvm_sequence #(ahb_item); rand int num_trans; rand bit [31:0] start_addr; eflash_item eflash_op; task body(); for(int i = 0; i < num_trans; i++) begin // 1. 创建高级eflash_item并随机化 `uvm_create_on(eflash_op, p_sequencer) assert(eflash_op.randomize() with { op_type == OP_PROGRAM; addr == start_addr + i * 4; // 字对齐编程 data_size == 4; data.size() == 1; }); // 2. 通过adapter或子序列,将eflash_item转换为一系列ahb_item并发送 // 这里假设有一个将eflash_op分解为ahb事务的子任务 send_program_ahb_ops(eflash_op); end endtask // ... send_program_ahb_ops任务实现 ... endclass

4.2 功能覆盖率模型与断言

覆盖率是衡量验证完备性的标尺。我们需要在coverage collector中定义覆盖组(covergroup)。

class eflash_cov_collector extends uvm_subscriber #(eflash_item); eflash_item cov_item; `uvm_component_utils(eflash_cov_collector) covergroup cg_ahb_op; // AHB总线特性覆盖 op_type: coverpoint cov_item.op_type { bins read = {OP_READ}; bins program = {OP_PROGRAM}; bins sector_erase = {OP_SECTOR_ERASE}; bins bulk_erase = {OP_BULK_ERASE}; // ... 其他操作 } addr_alignment: coverpoint cov_item.addr[1:0] { // 检查低2位地址对齐 bins word_aligned = {0}; bins halfword_aligned = {0, 2}; bins byte_aligned = {0, 1, 2, 3}; } data_size_cp: coverpoint cov_item.data_size { bins byte = {1}; bins halfword = {2}; bins word = {4}; } // 交叉覆盖:不同操作类型下的地址对齐和数据大小 op_type_x_addr_alignment: cross op_type, addr_alignment; op_type_x_data_size: cross op_type, data_size_cp; endgroup covergroup cg_flash_state; // eFlash内部状态覆盖(通过监控状态寄存器或内部信号) // 例如:覆盖从IDLE到BUSY,从BUSY到ERROR,从BUSY到DONE的状态跳转 // 这可能需要通过后门或断言来收集信号 endgroup function new(string name, uvm_component parent); super.new(name, parent); cg_ahb_op = new(); cg_flash_state = new(); endfunction function void write(eflash_item t); this.cov_item = t; cg_ahb_op.sample(); // 采样状态覆盖组需要额外的信号信息 endfunction endclass

除了代码覆盖率,我们还需要在interfaceassertion module中使用SVA(SystemVerilog Assertions)添加关键协议断言和功能断言。例如:

  • 协议断言HREADY为低时,HTRANS不能从NONSEQ变为SEQ(除非是IDLE或BUSY)。
  • 功能断言:当状态寄存器显示BUSY时,对Flash阵列的读操作应返回无效数据或特定值。
  • 安全断言:对写保护区域的编程操作,必须导致操作失败并置位错误状态位。

断言能在违规发生时立即报错,提供最直接的错误定位,是验证平台不可或缺的“哨兵”。

5. 记分板与参考模型的设计策略

记分板和参考模型是验证平台的“大脑”,负责判断DUT行为是否正确。对于eFlash控制器,其设计需要特别考虑非易失性和操作延迟。

5.1 基于内存镜像的记分板实现

scoreboard的核心是一个能模拟eFlash阵列行为的存储器模型。我们通常用一个关联数组(associative array)或uvm_mem来模拟。

class eflash_scoreboard extends uvm_scoreboard; `uvm_component_utils(eflash_scoreboard) uvm_tlm_analysis_fifo #(ahb_item) ahb_fifo; // 接收来自AHB monitor的事务 uvm_tlm_analysis_fifo #(eflash_item) eflash_fifo; // 接收来自eFlash功能monitor的事务(可选) // eFlash内存镜像:地址 -> 数据 bit [7:0] flash_mem [bit [31:0]]; // 保护区域配置镜像 bit [31:0] protect_start_addr, protect_end_addr; // 参考模型句柄 eflash_ref_model ref_model; function new(string name, uvm_component parent); super.new(name, parent); ahb_fifo = new("ahb_fifo", this); eflash_fifo = new("eflash_fifo", this); endfunction task run_phase(uvm_phase phase); fork process_ahb_transactions(); // process_eflash_transactions(); join endtask task process_ahb_transactions(); ahb_item ahb_tr; forever begin ahb_fifo.get(ahb_tr); // 根据ahb_tr的类型(读/写)、地址、数据,更新或检查内存镜像 case (ahb_tr.HWRITE) 1'b1: process_ahb_write(ahb_tr); // 处理写操作 1'b0: process_ahb_read(ahb_tr); // 处理读操作 endcase end endtask task process_ahb_write(ahb_item tr); // 1. 检查地址是否在保护区域内 if (is_protected(tr.HADDR)) begin // 预期DUT应返回HRESP=ERROR或忽略写入 // 记分板记录此预期,等待monitor报告的实际响应进行比对 expected_resp_queue.push_back(EXPECT_ERROR); end else if (is_control_register(tr.HADDR)) { // 2. 如果是控制寄存器写入(如命令寄存器),则调用参考模型更新状态 ref_model.write_register(tr.HADDR, tr.HWDATA); // 参考模型可能会触发一个后台任务来模拟编程/擦除延迟 } else if (is_data_register(tr.HADDR)) { // 3. 如果是数据寄存器写入,缓存数据,等待命令生效 ref_model.write_data_buffer(tr.HADDR, tr.HWDATA); } else if (is_memory_array(tr.HADDR)) { // 4. 如果是直接内存写入(编程),需要结合当前控制器状态判断 // 如果控制器处于可编程状态,则更新内存镜像,否则预期错误 if (ref_model.current_state == IDLE) { update_memory_mirror(tr.HADDR, tr.HWDATA, tr.HSIZE); } end endtask task process_ahb_read(ahb_item tr); bit [31:0] expected_data; // 1. 根据地址从内存镜像或参考模型获取预期数据 if (is_status_register(tr.HADDR)) { expected_data = ref_model.get_status(); } else if (is_memory_array(tr.HADDR)) { expected_data = read_memory_mirror(tr.HADDR, tr.HSIZE); } // 2. 将预期数据存入队列,等待与monitor捕获的实际读数据(HRDATA)比对 expected_rd_data_queue.push_back(expected_data); endtask // 还需要一个任务来比对预期和实际响应/数据 task check_response(); // 从队列中取出预期和实际值进行比对,使用`uvm_error`报告差异 endtask endclass

5.2 参考模型的行为建模要点

reference model需要精确模拟eFlash控制器的关键行为:

  • 命令解析与状态机:模型内部需要有一个与DUT RTL描述一致的状态机(IDLE, CMD_LATCH, ADDR_LATCH, DATA_LATCH, BUSY, ERROR等)。当收到特定的命令序列(如解锁->擦除命令->扇区地址)时,状态机跳转到BUSY
  • 操作延迟:编程和擦除是“慢”操作。参考模型在进入BUSY状态后,需要经过一个可配置的延迟(可以通过rand变量模拟工艺偏差),再跳转到DONEERROR状态。这个延迟期间,对存储阵列的读操作应返回旧数据或特定值。
  • 数据存储:模型内部维护自己的存储阵列镜像。当模拟编程操作成功完成后,将缓存的数据写入到镜像的对应地址。擦除操作则将对应地址范围的数据全部置为全‘1’。
  • 错误模拟:参考模型可以根据配置,随机或定向地模拟操作失败(如编程验证失败),从而置位错误状态位。这可以用来验证scoreboard和测试用例对错误路径的检查是否完备。

参考模型与scoreboard的交互通常是单向的:scoreboard将输入(总线事务)传递给参考模型,参考模型更新内部状态并产生预期输出,scoreboard再获取这些预期值用于比对。它们共同构成了预测DUT行为的“黄金标准”。

6. 平台集成、调试与回归测试实践

搭建好各个组件后,将它们集成并顺畅运行是另一个挑战。调试和建立回归测试流程则是保证验证质量持续性的关键。

6.1 环境集成与连接

集成的主要工作是在envconnect_phase中完成所有TLM端口的连接和config_db的配置。

class ahb_eflash_env extends uvm_env; ahb_agent ahb_agt; eflash_scoreboard scb; eflash_cov_collector cov; eflash_ref_model ref_model; virtual ahb_if vif; function void build_phase(uvm_phase phase); super.build_phase(phase); // 1. 创建组件 ahb_agt = ahb_agent::type_id::create("ahb_agt", this); scb = eflash_scoreboard::type_id::create("scb", this); cov = eflash_cov_collector::type_id::create("cov", this); ref_model = eflash_ref_model::type_id::create("ref_model", this); // 2. 从config_db获取虚拟接口 if (!uvm_config_db#(virtual ahb_if)::get(this, "", "ahb_vif", vif)) begin `uvm_fatal("CFG", "Cannot get ahb_vif from config_db!") end // 将vif传递给agent uvm_config_db#(virtual ahb_if)::set(this, "ahb_agt*", "ahb_vif", vif); endfunction function void connect_phase(uvm_phase phase); super.connect_phase(phase); // 3. 连接TLM端口 // 将agent的monitor的分析端口连接到scoreboard和coverage collector ahb_agt.monitor.item_collected_port.connect(scb.ahb_fifo.analysis_export); ahb_agt.monitor.item_collected_port.connect(cov.analysis_export); // 将scoreboard与ref_model连接(假设通过函数调用或端口) scb.ref_model = ref_model; endfunction endclass

6.2 典型调试问题与排查技巧

在平台运行初期,你会遇到各种问题。以下是一些常见问题及排查思路:

问题现象可能原因排查步骤
仿真卡死,无进度1. Sequence未产生激励。
2. Driver/Sequencer握手死锁。
3. DUT等待某个永远不发生的条件。
1. 检查sequencebody()任务是否启动,是否调用了start_item()/finish_item()
2. 在driver中打印发送的transaction,在sequencer打印接收的transaction。
3. 检查DUT的接口信号,特别是HREADYHRESP,看是否陷入等待。
Scoreboard报错,数据不匹配1. 参考模型行为与DUT不一致。
2. Monitor抓取的数据有误。
3. 时序问题,比对时机不对。
1. 首先隔离问题:在scoreboard中打印预期值和实际值。
2. 检查monitor的协议解析逻辑,特别是复杂时序(如HREADY为低时的信号保持)。
3. 检查scoreboard的比对逻辑,是否考虑了eFlash操作的延迟。可能需要引入“预期结果队列”和延迟比对机制。
覆盖率收敛缓慢1. 测试场景过于单一。
2. 约束条件太强,限制了随机化。
3. 覆盖点定义有误或未采样。
1. 分析覆盖率报告,看哪些bin没覆盖到。针对性地编写定向测试或增强随机约束。
2. 放松sequence中的约束,让随机化更充分。
3. 在coverage collectorwrite函数中打印采样信息,确认覆盖组被正确触发。
断言频繁触发1. 断言本身条件太严或写错。
2. DUT行为确实不符合协议/规范。
3. 测试激励不合法。
1. 仔细审查断言条件,尤其是涉及多个时钟周期的时序断言。
2. 结合波形和断言失败信息,分析DUT在失败时刻的行为。
3. 检查sequence产生的激励是否在所有情况下都符合协议要求。

实操心得:调试UVM平台,波形图日志是你的两大法宝。务必在关键组件(driver,monitor,scoreboard)中使用uvm_info打印详细的调试信息,并设置不同的verbosity级别。在初期调试时,可以将UVM_VERBOSITY设为UVM_HIGH甚至UVM_DEBUG。另外,学会使用仿真器的forcedeposit命令来绕过一些初始的集成问题(比如强制一个复位信号),可以快速推进到核心功能的调试。

6.3 回归测试与自动化流程

当平台稳定后,需要建立回归测试集(regression suite)。这通常由一个顶层base_test和众多派生test组成。

class base_test extends uvm_test; ahb_eflash_env env; // 公共的配置和phase设置 virtual function void build_phase(uvm_phase phase); super.build_phase(phase); // 设置共同的env配置,如关闭某些检查以加速 uvm_config_db#(int)::set(this, "env.scb", "checks_enable", 1); env = ahb_eflash_env::type_id::create("env", this); endfunction endclass class test_register_access extends base_test; // 专门测试寄存器访问 virtual task run_phase(uvm_phase phase); register_access_seq reg_seq = register_access_seq::type_id::create("reg_seq"); phase.raise_objection(this); reg_seq.start(env.ahb_agt.sequencer); phase.drop_objection(this); endtask endclass class test_random_ops extends base_test; // 随机混合操作测试 virtual task run_phase(uvm_phase phase); random_eflash_seq rand_seq = random_eflash_seq::type_id::create("rand_seq"); phase.raise_objection(this); assert(rand_seq.randomize() with {num_trans inside {[100:500]};}); rand_seq.start(env.ahb_agt.sequencer); phase.drop_objection(this); endtask endclass

使用Makefile或Python脚本自动化回归流程:

  1. 编译:编译所有RTL设计文件、UVM库、测试平台文件。
  2. 仿真:针对testlist中的每一个测试用例,运行仿真,并指定不同的随机种子(+uvm_set_seed)。
  3. 收集:收集每个测试的日志文件、覆盖率数据库(.ucd文件)、断言报告和波形文件(可选)。
  4. 分析:合并覆盖率数据库,生成覆盖率报告。检查所有仿真的日志中是否有UVM_ERRORUVM_FATAL。分析断言失败报告。
  5. 报告:生成一个回归测试总结报告,包含通过率、功能覆盖率、代码覆盖率等关键指标。

这个自动化流程可以集成到持续集成(CI)系统中,每晚自动运行,确保代码的任何修改都不会引入回归缺陷。对于eFlash控制器验证,回归测试中应特别关注那些长时间运行、混合了编程、擦除、读取的随机测试,它们最容易暴露深层次的时序和状态机交互问题。

返回列表