你的积分板(
apbuart_scoreboard
)是一个用于比较APB总线与UART接口之间的数据传输的SystemVerilog类,集成了多个监测器和驱动器以验证通信的正确性。它通过不断从APB和UART的监视器与驱动器收集数据,并对这些数据进行比较,从而确保系统的传输符合设计规范。1. 积分板的总体结构与作用
apbuart_scoreboard
是一个继承自 uvm_scoreboard
的类,主要任务包括:- 接收来自 APB 和 UART 的数据包(
pkt
)。
- 存储这些数据包在相应的队列中。
- 比较实际从硬件捕获的输出(监视器捕获的)与软件驱动的预期数据(驱动器生成的)。
- 通过
uvm_error
和uvm_info
记录是否存在不匹配。
积分板主要包括三个部分:
- 端口定义和数据存储:积分板通过
uvm_analysis_imp
接口接收来自监视器和驱动器的数据包,并将这些数据存储在相应的队列中。
- 数据比较与配置比较:积分板会根据DUT的配置进行状态更新,同时对比预期与实际的通信数据。
- 日志与错误报告:如果预期数据和实际数据不匹配,积分板会打印错误日志,帮助工程师定位设计中的问题。
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
- 流程分析:
- 等待
pkt_qu_monapb
队列中有数据。 - 从队列中取出数据包。
- 根据地址类型执行不同操作:
- 如果是配置写操作(
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 接口之间的数据传输和配置,包括配置的更新和读出、数据的发送和接收。它主要通过以下几个步骤工作:
- 监视器和驱动器接口接收数据。
- 将数据存入相应的队列。
- 在运行阶段根据地址类型执行对比操作。
- 记录匹配和不匹配的日志信息,帮助工程师发现和修复设计中的错误。
这个积分板的设计思想是在保证通信的完整性和正确性的同时,提供一个自动化的验证机制,帮助验证团队快速发现和修复潜在的问题。