10.【学习】SPI & UART 验证环境与测试用例

SPI & UART 完整UVM验证环境与测试用例

以下验证环境,包含:配置类+顶层Env+基础Test+5个实用测试用例,与之前提供的事务类、Driver、Monitor、Agent无缝对接。


一、配置类(Config)

用于统一配置SPI和UART的参数,支持在test中动态修改。

c 复制代码
class spi_uart_config extends uvm_object;
  `uvm_object_utils(spi_uart_config)

  // SPI 配置参数
  bit spi_active = 1'b1; // 是否启用SPI agent
  spi_transaction::spi_mode_e spi_default_mode = spi_transaction::MODE_0;
  int unsigned spi_default_clk_div = 10; // 100MHz系统时钟 → 10MHz SPI时钟
  int unsigned spi_max_clk_div = 255;
  int unsigned spi_min_clk_div = 2;

  // UART 配置参数
  bit uart_active = 1'b1; // 是否启用UART agent
  uart_transaction::parity_e uart_default_parity = uart_transaction::PARITY_NONE;
  int unsigned uart_default_baud_div = 868; // 100MHz系统时钟 → 115200波特率
  int unsigned uart_default_stop_bits = 1;

  function new(string name = "spi_uart_config");
    super.new(name);
  endfunction
endclass

二、顶层环境类(Env)

集成SPI和UART agent,以及可选的scoreboard和reference model。

c 复制代码
class spi_uart_env extends uvm_env;
  `uvm_component_utils(spi_uart_env)

  // 配置对象
  spi_uart_config cfg;

  // Agent
  spi_agent spi_agt;
  uart_agent uart_agt;

  // 分析端口
  uvm_analysis_port #(spi_transaction) spi_ap;
  uvm_analysis_port #(uart_transaction) uart_ap;

  function new(string name = "spi_uart_env", uvm_component parent = null);
    super.new(name, parent);
    spi_ap = new("spi_ap", this);
    uart_ap = new("uart_ap", this);
  endfunction

  virtual function void build_phase(uvm_phase phase);
    super.build_phase(phase);

    // 获取配置对象
    if (!uvm_config_db#(spi_uart_config)::get(this, "", "cfg", cfg)) begin
      `uvm_fatal("NO_CFG", "无法获取spi_uart_config配置对象")
    end

    // 创建SPI agent
    if (cfg.spi_active) begin
      spi_agt = spi_agent::type_id::create("spi_agt", this);
      uvm_config_db#(uvm_active_passive_enum)::set(this, "spi_agt", "is_active", UVM_ACTIVE);
    end

    // 创建UART agent
    if (cfg.uart_active) begin
      uart_agt = uart_agent::type_id::create("uart_agt", this);
      uvm_config_db#(uvm_active_passive_enum)::set(this, "uart_agt", "is_active", UVM_ACTIVE);
    end

    `uvm_info("ENV", "SPI-UART验证环境构建完成", UVM_MEDIUM)
  endfunction

  virtual function void connect_phase(uvm_phase phase);
    super.connect_phase(phase);

    // 连接分析端口
    if (cfg.spi_active) begin
      spi_agt.mon.ap.connect(spi_ap);
    end

    if (cfg.uart_active) begin
      uart_agt.mon.ap.connect(uart_ap);
    end

    // 配置monitor参数
    if (cfg.spi_active) begin
      spi_agt.mon.set_config(cfg.spi_default_mode, cfg.spi_default_clk_div);
    end

    if (cfg.uart_active) begin
      uart_agt.mon.set_config(cfg.uart_default_baud_div, 
                              cfg.uart_default_parity, 
                              cfg.uart_default_stop_bits);
    end
  endfunction

  virtual function void end_of_elaboration_phase(uvm_phase phase);
    super.end_of_elaboration_phase(phase);
    uvm_top.print_topology(); // 打印UVM拓扑结构
  endfunction
endclass

三、基础测试类(Base Test)

所有测试用例的基类,负责创建环境和配置。

c 复制代码
class base_test extends uvm_test;
  `uvm_component_utils(base_test)

  spi_uart_env env;
  spi_uart_config cfg;

  function new(string name = "base_test", uvm_component parent = null);
    super.new(name, parent);
  endfunction

  virtual function void build_phase(uvm_phase phase);
    super.build_phase(phase);

    // 创建并设置配置对象
    cfg = spi_uart_config::type_id::create("cfg");
    uvm_config_db#(spi_uart_config)::set(this, "env", "cfg", cfg);

    // 创建环境
    env = spi_uart_env::type_id::create("env", this);

    // 设置仿真超时时间(10ms)
    uvm_top.set_timeout(10ms);
  endfunction

  virtual function void end_of_elaboration_phase(uvm_phase phase);
    super.end_of_elaboration_phase(phase);
    // 打印配置信息
    `uvm_info("TEST", "测试配置信息:", UVM_MEDIUM)
    `uvm_info("TEST", $sformatf("SPI默认模式:%s", cfg.spi_default_mode.name()), UVM_MEDIUM)
    `uvm_info("TEST", $sformatf("UART默认波特率分频:%0d", cfg.uart_default_baud_div), UVM_MEDIUM)
  endfunction

  virtual task run_phase(uvm_phase phase);
    super.run_phase(phase);
    phase.raise_objection(this);
    
    // 等待复位完成
    #100ns;
    
    // 运行测试用例(子类重写)
    run_test_case();
    
    // 等待所有事务完成
    #1ms;
    phase.drop_objection(this);
  endtask

  // 测试用例入口(子类重写)
  virtual task run_test_case();
    `uvm_info("TEST", "运行基础测试用例", UVM_MEDIUM)
  endtask
endclass

四、实用测试用例集合

1. 冒烟测试(Smoke Test)

验证最基本的功能是否正常工作,是所有测试的第一步。

c 复制代码
class smoke_test extends base_test;
  `uvm_component_utils(smoke_test)

  function new(string name = "smoke_test", uvm_component parent = null);
    super.new(name, parent);
  endfunction

  virtual task run_test_case();
    spi_transaction spi_tr;
    uart_transaction uart_tr;

    `uvm_info("SMOKE_TEST", "开始SPI冒烟测试", UVM_MEDIUM)
    
    // SPI 简单读写测试
    for (int i=0; i<5; i++) begin
      spi_tr = spi_transaction::type_id::create("spi_tr");
      // 固定参数,确保可重复性
      spi_tr.mode = cfg.spi_default_mode;
      spi_tr.clk_div = cfg.spi_default_clk_div;
      spi_tr.mosi_data = new[1];
      spi_tr.mosi_data[0] = 8'hA5 + i;
      spi_tr.data_delay = new[1];
      spi_tr.data_delay[0] = 0;
      spi_tr.cs_low_delay = 1;
      spi_tr.cs_high_delay = 1;
      
      spi_tr.start(env.spi_agt.seqr);
      #1us;
    end

    `uvm_info("SMOKE_TEST", "开始UART冒烟测试", UVM_MEDIUM)
    
    // UART 简单发送测试
    for (int i=0; i<5; i++) begin
      uart_tr = uart_transaction::type_id::create("uart_tr");
      uart_tr.dir = uart_transaction::TX;
      uart_tr.data = 8'h5A + i;
      uart_tr.parity_type = cfg.uart_default_parity;
      uart_tr.stop_bits = cfg.uart_default_stop_bits;
      uart_tr.baud_rate_div = cfg.uart_default_baud_div;
      
      uart_tr.start(env.uart_agt.seqr);
      #100us; // 等待UART传输完成
    end

    `uvm_info("SMOKE_TEST", "冒烟测试完成", UVM_MEDIUM)
  endtask
endclass

2. SPI 全模式测试

验证SPI所有四种工作模式是否正常。

c 复制代码
class spi_all_mode_test extends base_test;
  `uvm_component_utils(spi_all_mode_test)

  function new(string name = "spi_all_mode_test", uvm_component parent = null);
    super.new(name, parent);
  endfunction

  virtual task run_test_case();
    spi_transaction spi_tr;
    spi_transaction::spi_mode_e modes[] = {
      spi_transaction::MODE_0,
      spi_transaction::MODE_1,
      spi_transaction::MODE_2,
      spi_transaction::MODE_3
    };

    foreach (modes[i]) begin
      `uvm_info("SPI_MODE_TEST", $sformatf("测试SPI模式:%s", modes[i].name()), UVM_MEDIUM)
      
      // 配置monitor为当前模式
      env.spi_agt.mon.set_config(modes[i], cfg.spi_default_clk_div);
      
      // 发送多个事务
      for (int j=0; j<10; j++) begin
        spi_tr = spi_transaction::type_id::create("spi_tr");
        spi_tr.randomize() with {
          mode == modes[i];
          clk_div == cfg.spi_default_clk_div;
          mosi_data.size() inside {[1:8]};
        };
        spi_tr.start(env.spi_agt.seqr);
        #1us;
      end
    end

    `uvm_info("SPI_MODE_TEST", "SPI全模式测试完成", UVM_MEDIUM)
  endtask
endclass

3. UART 全配置测试

验证UART所有波特率、校验方式和停止位的组合。

c 复制代码
class uart_full_config_test extends base_test;
  `uvm_component_utils(uart_full_config_test)

  function new(string name = "uart_full_config_test", uvm_component parent = null);
    super.new(name, parent);
  endfunction

  virtual task run_test_case();
    uart_transaction uart_tr;
    uart_transaction::parity_e parities[] = {
      uart_transaction::PARITY_NONE,
      uart_transaction::PARITY_EVEN,
      uart_transaction::PARITY_ODD
    };
    int stop_bits[] = {1, 2, 3};
    int baud_divs[] = {868, 434, 217, 108}; // 115200, 230400, 460800, 921600

    foreach (baud_divs[i]) begin
      foreach (parities[j]) begin
        foreach (stop_bits[k]) begin
          `uvm_info("UART_CONFIG_TEST", 
            $sformatf("测试UART配置:波特率分频=%0d, 校验=%s, 停止位=%0d", 
                      baud_divs[i], parities[j].name(), stop_bits[k]), UVM_MEDIUM)
          
          // 配置monitor
          env.uart_agt.mon.set_config(baud_divs[i], parities[j], stop_bits[k]);
          
          // 发送多个字节
          for (int l=0; l<5; l++) begin
            uart_tr = uart_transaction::type_id::create("uart_tr");
            uart_tr.randomize() with {
              dir == uart_transaction::TX;
              parity_type == parities[j];
              stop_bits == stop_bits[k];
              baud_rate_div == baud_divs[i];
            };
            uart_tr.start(env.uart_agt.seqr);
            #200us;
          end
        end
      end
    end

    `uvm_info("UART_CONFIG_TEST", "UART全配置测试完成", UVM_MEDIUM)
  endtask
endclass

4. 随机测试(Random Test)

生成大量随机事务,快速覆盖各种场景。

c 复制代码
class random_test extends base_test;
  `uvm_component_utils(random_test)

  // 测试参数
  int num_spi_trans = 100;
  int num_uart_trans = 100;

  function new(string name = "random_test", uvm_component parent = null);
    super.new(name, parent);
  endfunction

  virtual task run_test_case();
    `uvm_info("RANDOM_TEST", "开始随机测试", UVM_MEDIUM)

    // 并行运行SPI和UART随机测试
    fork
      run_spi_random();
      run_uart_random();
    join

    `uvm_info("RANDOM_TEST", "随机测试完成", UVM_MEDIUM)
  endtask

  virtual task run_spi_random();
    spi_transaction spi_tr;
    for (int i=0; i<num_spi_trans; i++) begin
      spi_tr = spi_transaction::type_id::create("spi_tr");
      // 完全随机,覆盖所有约束
      if (!spi_tr.randomize()) begin
        `uvm_error("RANDOM_TEST", "SPI事务随机化失败")
      end
      spi_tr.start(env.spi_agt.seqr);
      #1us;
    end
  endtask

  virtual task run_uart_random();
    uart_transaction uart_tr;
    for (int i=0; i<num_uart_trans; i++) begin
      uart_tr = uart_transaction::type_id::create("uart_tr");
      // 完全随机,覆盖所有约束
      if (!uart_tr.randomize()) begin
        `uvm_error("RANDOM_TEST", "UART事务随机化失败")
      end
      uart_tr.start(env.uart_agt.seqr);
      #100us;
    end
  endtask
endclass

5. 错误注入测试(Error Injection Test)

验证DUT对各种错误情况的处理能力。

c 复制代码
class error_injection_test extends base_test;
  `uvm_component_utils(error_injection_test)

  function new(string name = "error_injection_test", uvm_component parent = null);
    super.new(name, parent);
  endfunction

  virtual task run_test_case();
    spi_transaction spi_tr;
    uart_transaction uart_tr;

    `uvm_info("ERROR_TEST", "开始SPI错误注入测试", UVM_MEDIUM)
    
    // SPI 错误注入:不同时钟分频和模式的组合
    for (int i=0; i<20; i++) begin
      spi_tr = spi_transaction::type_id::create("spi_tr");
      spi_tr.randomize() with {
        clk_div inside {[2:20]}; // 测试不同时钟频率
        mosi_data.size() inside {[1:16]};
      };
      spi_tr.start(env.spi_agt.seqr);
      #1us;
    end

    `uvm_info("ERROR_TEST", "开始UART错误注入测试", UVM_MEDIUM)
    
    // UART 错误注入:校验错误和帧错误
    for (int i=0; i<10; i++) begin
      uart_tr = uart_transaction::type_id::create("uart_tr");
      uart_tr.randomize() with {
        dir == uart_transaction::TX;
        parity_type != uart_transaction::PARITY_NONE;
        parity_error dist {0:=50, 1:=50}; // 50%概率注入校验错误
        frame_error dist {0:=70, 1:=30}; // 30%概率注入帧错误
      };
      uart_tr.start(env.uart_agt.seqr);
      #200us;
    end

    `uvm_info("ERROR_TEST", "错误注入测试完成", UVM_MEDIUM)
  endtask
endclass

五、完整文件结构

将所有文件按照以下结构组织,便于管理和编译:

复制代码
spi_uart_uvm/
├── src/
│   ├── spi/
│   │   ├── spi_transaction.sv
│   │   ├── spi_interface.sv
│   │   ├── spi_driver.sv
│   │   ├── spi_monitor.sv
│   │   ├── spi_sequencer.sv
│   │   ├── spi_agent.sv
│   │   └── spi_coverage.sv
│   ├── uart/
│   │   ├── uart_transaction.sv
│   │   ├── uart_interface.sv
│   │   ├── uart_driver.sv
│   │   ├── uart_monitor.sv
│   │   ├── uart_sequencer.sv
│   │   ├── uart_agent.sv
│   │   └── uart_coverage.sv
│   ├── env/
│   │   ├── spi_uart_config.sv
│   │   └── spi_uart_env.sv
│   └── test/
│       ├── base_test.sv
│       ├── smoke_test.sv
│       ├── spi_all_mode_test.sv
│       ├── uart_full_config_test.sv
│       ├── random_test.sv
│       └── error_injection_test.sv
└── tb/
    └── top_tb.sv

六、编译与运行命令

VCS 编译命令

bash 复制代码
vcs -sverilog -ntb_opts uvm-1.2 \
    +incdir+./src/spi+./src/uart+./src/env+./src/test \
    ./src/spi/*.sv \
    ./src/uart/*.sv \
    ./src/env/*.sv \
    ./src/test/*.sv \
    ./tb/top_tb.sv \
    -o simv

运行指定测试用例

bash 复制代码
# 运行冒烟测试
./simv +UVM_TESTNAME=smoke_test

# 运行随机测试
./simv +UVM_TESTNAME=random_test

# 运行错误注入测试
./simv +UVM_TESTNAME=error_injection_test

# 生成覆盖率报告
./simv +UVM_TESTNAME=random_test -cm line+cond+fsm+branch+tgl

七、最佳实践与扩展建议

1. 验证流程建议

  1. 先跑冒烟测试:确保基本功能正常
  2. 再跑全模式/全配置测试:覆盖所有接口配置
  3. 然后跑随机测试:快速提升覆盖率
  4. 最后跑错误注入测试:验证鲁棒性
  5. 根据覆盖率报告:编写定向测试用例覆盖剩余点

2. 扩展建议

  • 添加Scoreboard:用于比较DUT的输出和预期结果
  • 添加Reference Model:实现一个简单的SPI/UART参考模型
  • 添加Virtual Sequencer:用于协调SPI和UART的事务发送
  • 添加更多测试用例:如长包测试、背靠背传输测试、中断测试等

3. 常见问题解决

  • 覆盖率收敛慢:调整约束的权重分布,增加关键场景的概率
  • 随机化失败 :检查约束是否有冲突,使用soft约束降低优先级
  • 时序问题:调整接口的时钟块延时,确保采样和驱动的时机正确
相关推荐
yu859395813 小时前
基于MATLAB的层合板等效模量及极限强度计算实现
开发语言·matlab
咸甜适中13 小时前
rust语言学习笔记Trait(十三)Borrow、BorrowMut(借用)
笔记·学习·rust
凌云若寒13 小时前
SENTINEL软件
学习·sentinel·产品经理·制造·软件需求
星轨初途13 小时前
【C++ 进阶】list 核心机制解析及 vector 巅峰对决
开发语言·数据结构·c++·经验分享·笔记·list
wh_xia_jun13 小时前
Apifox 测试项目实操1
开发语言·lua
影寂ldy13 小时前
C#Lambda表达式
开发语言·c#
小侯不躺平.13 小时前
C++ Boost库【6】时间戳整体综合
开发语言·c++·算法
ytttr87313 小时前
基于MATLAB的三维六面体有限元网格模型
开发语言·matlab
likerhood13 小时前
Java ArrayList 详解:从动态数组到扩容机制与常见陷阱
java·开发语言·windows