记分板的实现
🎐

记分板的实现

你的积分板(apbuart_scoreboard)是一个用于比较APB总线与UART接口之间的数据传输的SystemVerilog类,集成了多个监测器和驱动器以验证通信的正确性。它通过不断从APB和UART的监视器与驱动器收集数据,并对这些数据进行比较,从而确保系统的传输符合设计规范。

1. 积分板的总体结构与作用

apbuart_scoreboard 是一个继承自 uvm_scoreboard 的类,主要任务包括:
  • 接收来自 APB 和 UART 的数据包(pkt)。
  • 存储这些数据包在相应的队列中。
  • 比较实际从硬件捕获的输出(监视器捕获的)与软件驱动的预期数据(驱动器生成的)。
  • 通过 uvm_erroruvm_info 记录是否存在不匹配。
积分板主要包括三个部分:
  1. 端口定义和数据存储:积分板通过 uvm_analysis_imp 接口接收来自监视器和驱动器的数据包,并将这些数据存储在相应的队列中。
  1. 数据比较与配置比较:积分板会根据DUT的配置进行状态更新,同时对比预期与实际的通信数据。
  1. 日志与错误报告:如果预期数据和实际数据不匹配,积分板会打印错误日志,帮助工程师定位设计中的问题。

2. 主要模块说明

(1) 分析接口 (uvm_analysis_imp_decl)

`uvm_analysis_imp_decl(_monapb) `uvm_analysis_imp_decl(_monuart) `uvm_analysis_imp_decl(_drvuart)
  • 这几行代码是使用 uvm_analysis_imp_decl() 宏来创建分析端口。这些端口会用来接收不同类型的数据包。
  • _monapb_monuart_drvuart 分别用于区分 APB 和 UART 的监视器数据包,以及 UART 驱动器的数据包。

(2) 数据存储与端口声明

apb_transaction pkt_qu_monapb[$]; uart_transaction pkt_qu_monuart[$]; uart_transaction pkt_qu_drvuart[$];
  • 这些队列用于存储从不同监视器和驱动器收到的数据包。
  • 每种类型的数据包被推入不同的队列,以便积分板可以在比较时正确区分数据。
这些队列是动态数组($表示动态大小),可以根据需要动态地增长。每次接收到新的数据包时,数据会被存储到对应的队列末尾,等待后续的处理和比较。
uvm_analysis_imp_monapb #(apb_transaction, apbuart_scoreboard) item_collected_export_monapb; uvm_analysis_imp_monuart #(uart_transaction, apbuart_scoreboard) item_collected_export_monuart; uvm_analysis_imp_drvuart #(uart_transaction, apbuart_scoreboard) item_collected_export_drvuart;
  • 这几行代码是定义用于接收数据的分析接口端口。每个端口都连接了相应的监视器或驱动器,以接收特定类型的数据包。

(3) write 函数的实现

积分板为每个分析端口实现了一个 write 函数,用于接收数据包并将其存储在对应的队列中。
function void apbuart_scoreboard::write_monapb(apb_transaction pkt); pkt_qu_monapb.push_back(pkt); // 将来自APB监视器的数据包放入队列末尾 endfunction
  • write_monapb()write_monuart()write_drvuart() 分别将来自 APB 监视器、UART 监视器和 UART 驱动器的数据包存入相应的队列。
  • 这些数据包将稍后用于比较过程。

(4) run_phase() 任务

task apbuart_scoreboard::run_phase(uvm_phase phase); apb_transaction apb_pkt_mon; uart_transaction uart_pkt_mon; apb_transaction apb_pkt_drv; uart_transaction uart_pkt_drv; forever begin wait(pkt_qu_monapb.size() > 0); apb_pkt_mon = pkt_qu_monapb.pop_front(); // 根据不同地址对数据包进行操作 if (apb_pkt_mon.PWRITE==1 && (apb_pkt_mon.PADDR == cfg.baud_config_addr || apb_pkt_mon.PADDR == cfg.frame_config_addr || apb_pkt_mon.PADDR == cfg.parity_config_addr || apb_pkt_mon.PADDR == cfg.stop_bits_config_addr)) { // 更新配置寄存器 case(apb_pkt_mon.PADDR) cfg.baud_config_addr: baud_rate_reg = apb_pkt_mon.PWDATA; cfg.frame_config_addr: frame_len_reg = apb_pkt_mon.PWDATA; cfg.parity_config_addr: parity_reg = apb_pkt_mon.PWDATA; cfg.stop_bits_config_addr: stopbit_reg = apb_pkt_mon.PWDATA; default: `uvm_error(get_type_name(), $sformatf("------ :: Incorrect Config Address :: ------")) endcase end else if (apb_pkt_mon.PWRITE == 0 && (apb_pkt_mon.PADDR == cfg.baud_config_addr || apb_pkt_mon.PADDR == cfg.frame_config_addr || apb_pkt_mon.PADDR == cfg.parity_config_addr || apb_pkt_mon.PADDR == cfg.stop_bits_config_addr)) { // 比较配置 compare_config(apb_pkt_mon); } else if (apb_pkt_mon.PADDR == cfg.trans_data_addr) { // 比较发送数据 wait(pkt_qu_monuart.size() > 0); uart_pkt_mon = pkt_qu_monuart.pop_front(); compare_transmission(apb_pkt_mon, uart_pkt_mon); } else if (apb_pkt_mon.PADDR == cfg.receive_data_addr) { // 比较接收数据 wait(pkt_qu_drvuart.size() > 0); uart_pkt_drv = pkt_qu_drvuart.pop_front(); compare_receive(apb_pkt_mon, uart_pkt_drv); } end endtask
  • 流程分析
      1. 等待 pkt_qu_monapb 队列中有数据。
      1. 从队列中取出数据包。
      1. 根据地址类型执行不同操作:
          • 如果是配置写操作(PWRITE == 1),则更新内部寄存器。
          • 如果是配置读操作(PWRITE == 0),则调用 compare_config() 函数,检查配置数据的正确性。
          • 如果是传输数据地址,则从 pkt_qu_monuart 中取出UART数据,并调用 compare_transmission() 比较。
          • 如果是接收数据地址,则从 pkt_qu_drvuart 中取出UART数据,并调用 compare_receive() 比较。

(5) 比较函数

  • compare_config():比较配置寄存器的数据是否正确。例如,检查波特率、帧长度等是否匹配。
  • compare_transmission():将 APB 传输的数据包与 UART 的传输数据包进行对比。如果不匹配,则报告错误。
  • compare_receive():比较 APB 接收的数据与 UART 驱动器发送的数据是否一致。
function void apbuart_scoreboard::compare_transmission(apb_transaction apb_pkt, uart_transaction uart_pkt); if (apb_pkt.PWDATA == uart_pkt.transmitter_reg) `uvm_info(get_type_name(), $sformatf("------ :: Transmission Data Packet Match :: ------"), UVM_LOW) else `uvm_error(get_type_name(), $sformatf("------ :: Transmission Data Packet MisMatch :: ------")) `uvm_info(get_type_name(), $sformatf("Expected Transmission Data Value : %0h Actual Transmission Data Value: %0h", apb_pkt.PWDATA, uart_pkt.transmitter_reg), UVM_LOW) endfunction
  • 在这些比较函数中,如果数据不匹配,会通过 uvm_error() 打印错误信息,这对于验证流程中的问题定位非常重要。

3. 积分板的工作机制总结

  • 数据接收:积分板通过分析接口端口接收来自 APB 和 UART 的数据包,并将这些数据包存储在相应的队列中。每个队列都是通过动态数组实现的,使用 $ 表示动态大小,可以根据需要动态扩展。每当接收到新的数据包时,数据会通过 push_back() 方法被添加到队列末尾,确保数据按照接收的顺序存储。稍后在 run_phase() 中,这些数据会通过 pop_front() 方法从队列的开头被取出,以保证数据的顺序处理。
  • 数据比较:在 run_phase 中,积分板会根据数据包的地址信息确定如何处理这些数据,是进行配置更新、读取比较,还是进行传输数据或接收数据的比较。
  • 错误报告:通过调用 compare_config()compare_transmission()compare_receive(),将实际输出与预期值进行比较,发现不一致时会记录错误,帮助验证工程师发现设计中的问题。

4. 总结

这个积分板是为了验证 APB 总线和 UART 接口之间的数据传输和配置,包括配置的更新和读出、数据的发送和接收。它主要通过以下几个步骤工作:
  • 监视器和驱动器接口接收数据
  • 将数据存入相应的队列
  • 在运行阶段根据地址类型执行对比操作
  • 记录匹配和不匹配的日志信息,帮助工程师发现和修复设计中的错误。
这个积分板的设计思想是在保证通信的完整性和正确性的同时,提供一个自动化的验证机制,帮助验证团队快速发现和修复潜在的问题。