APB-UART(上)

目录

  • SPEC
  • congfig
    • Uart Congfiguration
    • APB Congfiguration
  • scoreboard

SPEC

  • 典型的基于AMBA总线的SOC系统结构如下图所示。
    APB-UART(上)_第1张图片

这种SOC结构是在高速总线协议和低速总线协议的互联支持下,将工作在不同频率的各个系统模块进行整合,最终协同处理器完成运算工作。其中低速总线往往采用APB协议,两种总线之间使用转接桥(bridge)完成协议转换。APB协议作为外围总线协议,将为各个低速模块提供通信接口。

  • DUT结构图如下:
    APB-UART(上)_第2张图片
    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取出的数据。

你可能感兴趣的:(验证项目,经验分享)