10.【学习】SPI UART 验证环境与测试用例
SPI & UART 完整UVM验证环境与测试用例
以下验证环境,包含:配置类+顶层Env+基础Test+5个实用测试用例,与之前提供的事务类、Driver、Monitor、Agent无缝对接。
一、配置类(Config)
用于统一配置SPI和UART的参数,支持在test中动态修改。
class spi_uart_config extends uvm_object;`uvm_object_utils(spi_uart_config)// SPI 配置参数bit spi_active=1'b1;// 是否启用SPI agentspi_transaction::spi_mode_e spi_default_mode=spi_transaction::MODE_0;intunsignedspi_default_clk_div=10;// 100MHz系统时钟 → 10MHz SPI时钟intunsignedspi_max_clk_div=255;intunsignedspi_min_clk_div=2;// UART 配置参数bit uart_active=1'b1;// 是否启用UART agentuart_transaction::parity_e uart_default_parity=uart_transaction::PARITY_NONE;intunsigneduart_default_baud_div=868;// 100MHz系统时钟 → 115200波特率intunsigneduart_default_stop_bits=1;functionnew(string name="spi_uart_config");super.new(name);endfunction endclass二、顶层环境类(Env)
集成SPI和UART agent,以及可选的scoreboard和reference model。
class spi_uart_env extends uvm_env;`uvm_component_utils(spi_uart_env)// 配置对象spi_uart_config cfg;// Agentspi_agent spi_agt;uart_agent uart_agt;// 分析端口uvm_analysis_port #(spi_transaction)spi_ap;uvm_analysis_port #(uart_transaction)uart_ap;functionnew(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 functionvoidbuild_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 agentif(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 agentif(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 functionvoidconnect_phase(uvm_phase phase);super.connect_phase(phase);// 连接分析端口if(cfg.spi_active)begin spi_agt.mon.ap.connect(spi_ap);endif(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);endif(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 functionvoidend_of_elaboration_phase(uvm_phase phase);super.end_of_elaboration_phase(phase);uvm_top.print_topology();// 打印UVM拓扑结构endfunction endclass三、基础测试类(Base Test)
所有测试用例的基类,负责创建环境和配置。
class base_test extends uvm_test;`uvm_component_utils(base_test)spi_uart_env env;spi_uart_config cfg;functionnew(string name="base_test",uvm_component parent=null);super.new(name,parent);endfunction virtual functionvoidbuild_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 functionvoidend_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 taskrun_phase(uvm_phase phase);super.run_phase(phase);phase.raise_objection(this);// 等待复位完成#100ns;// 运行测试用例(子类重写)run_test_case();// 等待所有事务完成#1ms;phase.drop_objection(this);endtask// 测试用例入口(子类重写)virtual taskrun_test_case();`uvm_info("TEST","运行基础测试用例",UVM_MEDIUM)endtask endclass四、实用测试用例集合
1. 冒烟测试(Smoke Test)
验证最基本的功能是否正常工作,是所有测试的第一步。
class smoke_test extends base_test;`uvm_component_utils(smoke_test)functionnew(string name="smoke_test",uvm_component parent=null);super.new(name,parent);endfunction virtual taskrun_test_case();spi_transaction spi_tr;uart_transaction uart_tr;`uvm_info("SMOKE_TEST","开始SPI冒烟测试",UVM_MEDIUM)// SPI 简单读写测试for(inti=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(inti=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 endclass2. SPI 全模式测试
验证SPI所有四种工作模式是否正常。
class spi_all_mode_test extends base_test;`uvm_component_utils(spi_all_mode_test)functionnew(string name="spi_all_mode_test",uvm_component parent=null);super.new(name,parent);endfunction virtual taskrun_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(intj=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 endclass3. UART 全配置测试
验证UART所有波特率、校验方式和停止位的组合。
class uart_full_config_test extends base_test;`uvm_component_utils(uart_full_config_test)functionnew(string name="uart_full_config_test",uvm_component parent=null);super.new(name,parent);endfunction virtual taskrun_test_case();uart_transaction uart_tr;uart_transaction::parity_e parities[]={uart_transaction::PARITY_NONE,uart_transaction::PARITY_EVEN,uart_transaction::PARITY_ODD};intstop_bits[]={1,2,3};intbaud_divs[]={868,434,217,108};// 115200, 230400, 460800, 921600foreach(baud_divs[i])beginforeach(parities[j])beginforeach(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)// 配置monitorenv.uart_agt.mon.set_config(baud_divs[i],parities[j],stop_bits[k]);// 发送多个字节for(intl=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 endclass4. 随机测试(Random Test)
生成大量随机事务,快速覆盖各种场景。
class random_test extends base_test;`uvm_component_utils(random_test)// 测试参数intnum_spi_trans=100;intnum_uart_trans=100;functionnew(string name="random_test",uvm_component parent=null);super.new(name,parent);endfunction virtual taskrun_test_case();`uvm_info("RANDOM_TEST","开始随机测试",UVM_MEDIUM)// 并行运行SPI和UART随机测试forkrun_spi_random();run_uart_random();join `uvm_info("RANDOM_TEST","随机测试完成",UVM_MEDIUM)endtask virtual taskrun_spi_random();spi_transaction spi_tr;for(inti=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 taskrun_uart_random();uart_transaction uart_tr;for(inti=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 endclass5. 错误注入测试(Error Injection Test)
验证DUT对各种错误情况的处理能力。
class error_injection_test extends base_test;`uvm_component_utils(error_injection_test)functionnew(string name="error_injection_test",uvm_component parent=null);super.new(name,parent);endfunction virtual taskrun_test_case();spi_transaction spi_tr;uart_transaction uart_tr;`uvm_info("ERROR_TEST","开始SPI错误注入测试",UVM_MEDIUM)// SPI 错误注入:不同时钟分频和模式的组合for(inti=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(inti=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 编译命令
vcs-sverilog-ntb_optsuvm-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\-osimv运行指定测试用例
# 运行冒烟测试./simv +UVM_TESTNAME=smoke_test# 运行随机测试./simv +UVM_TESTNAME=random_test# 运行错误注入测试./simv +UVM_TESTNAME=error_injection_test# 生成覆盖率报告./simv +UVM_TESTNAME=random_test-cmline+cond+fsm+branch+tgl七、最佳实践与扩展建议
1. 验证流程建议
- 先跑冒烟测试:确保基本功能正常
- 再跑全模式/全配置测试:覆盖所有接口配置
- 然后跑随机测试:快速提升覆盖率
- 最后跑错误注入测试:验证鲁棒性
- 根据覆盖率报告:编写定向测试用例覆盖剩余点
2. 扩展建议
- 添加Scoreboard:用于比较DUT的输出和预期结果
- 添加Reference Model:实现一个简单的SPI/UART参考模型
- 添加Virtual Sequencer:用于协调SPI和UART的事务发送
- 添加更多测试用例:如长包测试、背靠背传输测试、中断测试等
3. 常见问题解决
- 覆盖率收敛慢:调整约束的权重分布,增加关键场景的概率
- 随机化失败:检查约束是否有冲突,使用
soft约束降低优先级 - 时序问题:调整接口的时钟块延时,确保采样和驱动的时机正确
