SPEC
- 典型的基于AMBA总线的SOC系统结构如下图所示。
这种SOC结构是在高速总线协议和低速总线协议的互联支持下,将工作在不同频率的各个系统模块进行整合,最终协同处理器完成运算工作。其中低速总线往往采用APB协议,两种总线之间使用转接桥(bridge)完成协议转换。APB协议作为外围总线协议,将为各个低速模块提供通信接口。
- DUT结构图如下:
APB-UART模块可以实现串行数据和并行数据的转换,发送和接收逻辑分别用FIFO来存储数据,CPU可以通过APB总线访问该模块,进而间接的实现对UART串行数据的访问。
在进行数据发送操作时,需要发送的数据通过APB总线被写入到该模块后,会暂时存储在Transmit FIFO中,发送逻辑模块会在特定的时刻从Transmit FIFO中取走数据,并添上起始位、奇偶校验位以及停止位,形成一个完整的数据帧。最后发送逻辑会将该数据帧放入到发送端口另一端的移位寄存器中,按照特定的波特率将数据串行移位,实现数据发送的功能。
在进行数据接收操作时,串行数据通过接收端口后会进入到接收逻辑模块,该模块对数据进行格式检测后,会将其中的起始位、校验位以及停止位移除,并把剩下的数据暂时存储在Receive FIFO中,等待CPU访问。
APB协议可参考:AMBA-APB协议
UART协议可参考:UART概述
congfig
Uart Congfiguration
uart的相关配置主要包括波特率、帧长、奇偶校验以及停止位。在apbuart_base_test中,有一个set_config_params的方法用来实现这些配置,同时,此方法中还有一个flag来决定是使用定向配置还是随机配置设置。
代码如下:
function void apbuart_base_test::set_config_params(input [31:0] bd_rate , input [3:0] frm_len , input [1:0] parity , input sb , input flag);
if(flag)//flag是用来决定使用定向配置还是随机配置(1 for random , 0 for directed)
begin
if (!cfg.randomize())
`uvm_error("RNDFAIL", " Config Randomization")
end
else
begin
cfg.frame_len = frm_len;
cfg.n_sb = sb;
cfg.parity = parity;
cfg.bRate = bd_rate;
end
cfg.baudRateFunc();
endfunction
function void uart_config::baudRateFunc();
case (bRate)
32'd4800: baud_rate = 32'd10416;
32'd9600: baud_rate = 32'd5208;
32'd14400: baud_rate = 32'd3472;
32'd19200: baud_rate = 32'd2604;
32'd38400: baud_rate = 32'd1302;
32'd57600: baud_rate = 32'd868;
32'd115200: baud_rate = 32'd434;
32'd128000: baud_rate = 32'd392;
default: baud_rate = 32'd5208;
endcase
endfunction
APB Congfiguration
APB相关的主要配置包括address以及pselect(APB中的slave引脚)。使用apbuart_base_test中的set_apbconfig_params方法来实现相关配置。
代码如下:
function void apbuart_base_test::set_apbconfig_params(input [31:0] addr, input flag);
if(flag)
begin
if (!apb_cfg.randomize())
`uvm_error("RNDFAIL", " APB Config Randomization")
end
else
begin
apb_cfg.slave_Addr = addr;
end
apb_cfg.AddrCalcFunc();
endfunction
function void apb_config::AddrCalcFunc();
if ((slave_Addr >= ` UART_START_ADDR) && (slave_Addr <= ` UART_END_ADDR))
psel_Index = 1;
else if ((slave_Addr >= ` I2C_START_ADDR) && (slave_Addr <= ` I2C_END_ADDR))
psel_Index = 2;
else if ((slave_Addr >= ` SPI_START_ADDR) && (slave_Addr <= ` SPI_END_ADDR))
psel_Index = 4;
else
psel_Index = 0;
endfunction
scoreboard
在scoreboard的run_phase()中,调用了三个不同的函数来进行比较:
- compare_config()
此函数在configuration register上有读取操作时调用,将来自apb_monitor的值与配置了configuration后的scoreboard进行比较,这里的配置主要指uart_config中的波特率、帧长、奇偶校验、停止位这几个参数。
代码如下:
function void apbuart_scoreboard::compare_config (apb_transaction apb_pkt);
if(apb_pkt.PADDR == cfg.baud_config_addr)
begin
if(apb_pkt.PRDATA == baud_rate_reg)
`uvm_info(get_type_name(),$sformatf("------ :: Baud Rate Match :: ------"),UVM_LOW)
else
`uvm_error(get_type_name(),$sformatf("------ :: Baud Rate MisMatch :: ------"))
`uvm_info(get_type_name(),$sformatf("Expected Baud Rate: %0d Actual Baud Rate: %0d",baud_rate_reg,apb_pkt.PRDATA),UVM_LOW)
`uvm_info(get_type_name(),"------------------------------------\n",UVM_LOW)
end
if(apb_pkt.PADDR == cfg.frame_config_addr)
begin
if(apb_pkt.PRDATA == frame_len_reg)
`uvm_info(get_type_name(),$sformatf("------ :: Frame Rate Match :: ------"),UVM_LOW)
else
`uvm_error(get_type_name(),$sformatf("------ :: Frame Rate MisMatch :: ------"))
`uvm_info(get_type_name(),$sformatf("Expected Frame Rate: %0h Actual Frame Rate: %0h",frame_len_reg,apb_pkt.PRDATA),UVM_LOW)
`uvm_info(get_type_name(),"------------------------------------\n",UVM_LOW)
end
if(apb_pkt.PADDR == cfg.parity_config_addr)
begin
if(apb_pkt.PRDATA == parity_reg)
`uvm_info(get_type_name(),$sformatf("------ :: Parity Match :: ------"),UVM_LOW)
else
`uvm_error(get_type_name(),$sformatf("------ :: Parity MisMatch :: ------"))
`uvm_info(get_type_name(),$sformatf("Expected Parity Value : %0h Actual Parity Value: %0h",parity_reg,apb_pkt.PRDATA),UVM_LOW)
`uvm_info(get_type_name(),"------------------------------------\n",UVM_LOW)
end
if(apb_pkt.PADDR == cfg.stop_bits_config_addr)
begin
if(apb_pkt.PRDATA == stopbit_reg)
`uvm_info(get_type_name(),$sformatf("------ :: Stop Bit Match :: ------"),UVM_LOW)
else
`uvm_error(get_type_name(),$sformatf("------ :: Stop Bit MisMatch :: ------"))
`uvm_info(get_type_name(),$sformatf("Expected Stop Bit Value : %0h Actual Stop Value: %0h",stopbit_reg,apb_pkt.PRDATA),UVM_LOW)
`uvm_info(get_type_name(),"------------------------------------\n",UVM_LOW)
end
endfunction
- compare_transmission()
此函数在DUT上有传输操作时调用,将apb_driver写入到DUT上的PWDATA与uart_monitor在DUT的TX—pin上监控的数据进行比较。
代码如下:
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)
`uvm_info(get_type_name(),"------------------------------------\n",UVM_LOW)
endfunction
- compare_receive()
在有接收操作时,此函数被调用,将uart_driver提供给RX—pin的数据与apb—monitor从DUT接收到的PRDATA进行比较。此外该函数还比较了错误信号,因为我们有向DUT提供错误数据的机制。
代码如下:
function void apbuart_scoreboard::compare_receive (apb_transaction apb_pkt , uart_transaction uart_pkt);
if(apb_pkt.PRDATA == uart_pkt.payload)
`uvm_info(get_type_name(),$sformatf("------ :: Reciever Data Packet Match :: ------"),UVM_LOW)
else
`uvm_error(get_type_name(),$sformatf("------ :: Reciever Data Packet MisMatch :: ------"))
`uvm_info(get_type_name(),$sformatf("Expected Reciever Data Value : %0h Actual Reciever Data Value: %0h",uart_pkt.payload,apb_pkt.PRDATA),UVM_LOW)
`uvm_info(get_type_name(),"------------------------------------\n",UVM_LOW)
//$display("uart_pkt.sb_corr::%0b\tuart_pkt.sb_corr_bit[0]::%0b\tcfg.n_sb::%d",uart_pkt.sb_corr,uart_pkt.sb_corr_bit,cfg.parity[1]);
if((uart_pkt.bad_parity && cfg.parity[1]) || (uart_pkt.sb_corr && (cfg.n_sb || uart_pkt.sb_corr_bit[0])))
begin
//$display("uart_pkt.sb_corr::%0b\tuart_pkt.sb_corr_bit[0]::%0b\tcfg.n_sb::%d",uart_pkt.sb_corr,uart_pkt.sb_corr_bit[0],cfg.n_sb[0]);
if(apb_pkt.PSLVERR == 1'b1)
`uvm_info(get_type_name(),$sformatf("------ :: Error Match :: ------"),UVM_LOW)
else
`uvm_error(get_type_name(),$sformatf("------ :: Error MisMatch :: ------"))
`uvm_info(get_type_name(),$sformatf("Expected Error Value : %0h Actual Error Value: %0h",1'b1,apb_pkt.PSLVERR),UVM_LOW)
`uvm_info(get_type_name(),"------------------------------------\n",UVM_LOW)
end
else
begin
if(apb_pkt.PSLVERR == 1'b0)
`uvm_info(get_type_name(),$sformatf("------ :: Error Match :: ------"),UVM_LOW)
else
`uvm_error(get_type_name(),$sformatf("------ :: Error MisMatch :: ------"))
`uvm_info(get_type_name(),$sformatf("Expected Error Value : %0h Actual Error Value: %0h",1'b0,apb_pkt.PSLVERR),UVM_LOW)
`uvm_info(get_type_name(),"------------------------------------\n",UVM_LOW)
end
endfunction
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); // checking the fifo that it contains any valid entry from monitor apb
apb_pkt_mon = pkt_qu_monapb.pop_front(); // getting the entry from the start of fifo
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))
begin
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))
begin
compare_config (apb_pkt_mon) ;
end
else if (apb_pkt_mon.PADDR == cfg.trans_data_addr)
begin
wait(pkt_qu_monuart.size() > 0);
uart_pkt_mon = pkt_qu_monuart.pop_front();
compare_transmission (apb_pkt_mon,uart_pkt_mon);
end
else if (apb_pkt_mon.PADDR == cfg.receive_data_addr)
begin
wait(pkt_qu_drvuart.size() > 0);
uart_pkt_drv = pkt_qu_drvuart.pop_front();
compare_receive (apb_pkt_mon,uart_pkt_drv);
end
end
endtask : run_phase
这里的pkt_qu_monapb、pkt_qu_monuart、pkt_qu_uartdrv分别用来存储scoreboard通过analysis_port从apb_monitor、uart_monitor、uart_driver取出的数据。