Paso 5: ordenandolo todo un poco

Siguiendo los pasos de la creación de un banco de pruebas para el radicador que responda a una estructura más actual y basada en systemVerilog, hemos visto conveniente hacer unas últimas incorporaciones que os puedan ayudar a mejorar el estilo de vuestros bancos de pruebas.

Empecemos recordando la estructura que deseamos

Estructura del banco de pruebas SystemVerilog

This image has an empty alt attribute; its file name is sv_simple_tb_with_scb.png

En esta estructura las grandes construcciones que forman parte del banco de pruebas (module sin puertos del top) son:

  • DUT o DUV : diseño bajo verificación que será un “module” instanciado
  • test: Elementos constituyentes del sistema de verificación que será un “program” instanciado
  • Elemento de comunicación entre ambas construcciones que permita hacer llegar los estímulos generados por el test al DUV y permita observar tanto dichos estímulos como los resultados obtenidos por el DUV para hacérselos llegar los elementos constitutivos del test. Esta construcción que permite esa comunicación será un “interface” instanciado.
  • Y deberíamos completar con un generador de reloj y un generador de reset, si bien este último podría generarse en el test como un estímulo más.
`timescale 1ns/1ps
module prueba_radicador();
// constants                                           
// general purpose registers
reg CLK;
reg RESET;

//instanciacion del interfaz
test_if interfaz(.reloj(CLK),.rst(RESET));

//instanciación del disenyo                  
 top_duv duv (.bus(interfaz));
            
//instanciacion del program  
estimulos estim1 (.testar(interfaz),.monitorizar(interfaz));  

// CLK
always
begin
	CLK = 1'b0;
	CLK = #50 1'b1;
	#50;
end 

// RESET
initial
begin
  RESET=1'b1;
  # 1  RESET=1'b0;
  #99 RESET = 1'b1;
end 
 
endmodule

Vamos a incluir dichos elementos empezando con el elemento de intercomunicación, que desarrollaremos a través de un «interface»

INTERFAZ

`timescale 1ns/1ps
interface test_if (  input  bit  reloj,   input  bit  rst) ;
  logic       empieza  ;
  logic       termina  ;
  logic [7:0] data_in  ;
  logic  [4:0] data_out;
  clocking md @(posedge reloj);
	input #1ns data_out;
	input #1ns data_in;
	input #1ns empieza;
    input #1ns termina;
   endclocking:md;
  clocking sd @(posedge reloj);
    input #2ns  data_out;
    output #2ns data_in;
    input #2ns  termina;
    output #2ns empieza; 
  endclocking:sd;
  modport monitor (clocking md);
  modport test (clocking sd);
  modport duv (
  		input          	reloj      ,
  		input        	rst      ,
  		output          termina  ,
  		input         	empieza   ,
  		input  	        data_in  ,
  		output          data_out
		);
endinterface

Este ejemplo de interfaz utiliza tres «mod ports» : dos de ellos para proporcionar dos puntos de vista que debe usar el test: control (color violeta) y observabilidad (color granate); y un tercer punto de vista que utilizará el DUT (color azul en la figura)

This image has an empty alt attribute; its file name is image.png

.

Los dos modos que usará el test tienen asociados sus correspondientes «clocking blocks» para sincronizar esas comunicaciones

DUT

Como hemos basado la interconexión en el banco de pruebas mediante «interface» el top del diseño suele ser un fichero de adaptación de interfaz a puertos y viceversa.




module top_duv (test_if.duv bus) ; 


sed  radicador_duv(
  .CLK  (bus.reloj),     // Clock input
.RESET  (bus.rst),     // Active LOW ASINCRONOUS reset
.X      (bus.data_in), // Data input
.COUNT  (bus.data_out),// Data Output
.START  (bus.empieza),   // duv empieza
 .FIN   (bus.termina)     //duv termina
);

endmodule

TEST

Este elemento es el que más hemos cambiado.




program estimulos
  (test_if.test testar,
   test_if.monitor monitorizar  
  );
  
utilidades_verificacion::enviroment casos = new(testar, monitorizar);     //declaración e instanciación objeto                 

initial
begin
  casos.muestrear;
  $display("hacemos un test directo");
  casos.prueba_directa;
  $display("functional coverage after prueba_directa  is %e", casos.valores_X_trigger.get_coverage());
 // $stop;           
         
  $display("hacemos un test random de pares");
  casos.prueba_random_pares;
  $display("functional coverage after prueba_random_pares  is %e", casos.valores_X_trigger.get_coverage());
//  $stop;  
           
  $display("hacemos un test random de impares");
  casos.prueba_random_impares;
  $display("functional coverage afterprueba_random_impares  is %e", casos.valores_X_trigger.get_coverage());
  $stop;             

end
endprogram

Destacan en dicho código lo siguiente:

  • Realización con la construcción «program». Como ya he dicho reiterada veces muchos ingenieros de verificación prefieren seguir utilizando un «module»
  • Dicho «program» consiste de
    • un «initial» principal donde se suceden los diferentes casos contemplados en la verificación y
    • de un objeto de la clase «enviroment» donde se encuentran los siguientes elementos:
      • Los covergroups
      • La clase de RCSG
      • La clase del Scoreboard
  • Para poder declarar e instanciar un objeto de la clase enviroment hemos utilizado un «package» denominado utilizades_verificacion que es importado utilizando el operador ::. Esto es una alternativa más elegante que nos permite que la compilación no tenga problemas aunque estos elementos sean guardados en ficheros diferentes, en cuyo caso la visibilidad de las clases solo es conseguida si son incluidos los ficheros (solución poco elegante) o si son importados los objetos.
  • La inclusión de los covergroups en una clase (enviroment) en lugar de en el interior directamente del program nos sirve de ejemplo para observar los «embedded covergroups» en los cuales
    • Definir : con la construción covergroup
    • Declaración es implícita
    • Instanciación: con el constructor new

Veamos esa declaración de package con esas clases, objetos y covergroups

package utilidades_verificacion;
class RCSG;
  rand logic [7:0] valor;
  constraint impares    {valor[0] == 1'b1;}
  constraint pares {valor[0] == 1'b0;}
endclass
class Scoreboard;
  reg [3:0] cola_targets   [$];
  reg [3:0] target,pretarget,salida_obtenida;
  reg FINAL;
  virtual test_if.monitor mports;
  
  function new (virtual test_if.monitor mpuertos);
  begin
    this.mports = mpuertos;
  end
  endfunction
   
 task monitor_input;
   begin
     while (1)
       begin       
         @(mports.md);
         if (mports.md.empieza==1'b1)
           begin
             pretarget=$floor($sqrt(mports.md.data_in));//funcion ideal de obtencion e raiz cuadrada
             cola_targets={pretarget,cola_targets};//meto el valor deseado en la cola
           end
		end
   end
 endtask
  task monitor_output;
   begin
     while (1)
       begin       
         @(mports.md);
         if (mports.md.termina==1'b1)
           begin
         	FINAL=mports.md.termina;
         	target= cola_targets.pop_back();
         	salida_obtenida=mports.md.data_out;
             assert (salida_obtenida==target) else $error("operacion mal realizada: la raiz cuadrada de %d es %d y tu diste %d",mports.md.data_in,target,salida_obtenida);
           end
		end
   end
 endtask 
  
    
    
endclass 
    
class enviroment;
  virtual test_if.test testar_ports;
  virtual test_if.monitor monitorizar_ports;
 
//esto nos permitirá utilziar el operador ## para los ciclos de reloj
  
covergroup valores_X;    
  idea1:coverpoint  monitorizar_ports.md.data_in;
  idea2:coverpoint  monitorizar_ports.md.data_in
    {
    bins cero ={8'h00};
    bins extremo = {8'h80};
  }
endgroup; 
covergroup valores_X_trigger @(monitorizar_ports.md);
  mon_entradas_pares:coverpoint  monitorizar_ports.md.data_in 
   {
     wildcard bins impares[] ={8'bxxxxxxx0};
  }
  mon_entradas_impares:coverpoint  monitorizar_ports.md.data_in 
   {
     wildcard bins pares[] ={8'bxxxxxxx1};
  }
endgroup; 
//declaraciones de objetos
Scoreboard sb;
RCSG busInst;
  
function new (virtual test_if.test ports, virtual test_if.monitor mports);
  begin
    testar_ports = ports;
    monitorizar_ports = mports;
    //instanciación objetos
    busInst = new;//construimos la case de valores random
    valores_X=new;//construimos el covergroup
    valores_X_trigger=new;//construimos el covergroup
    sb=new(monitorizar_ports); //construimos el scoreboard
    //inicializacion   
     testar_ports.sd.empieza  <= 1'b0;
     testar_ports.sd.data_in <= 8'd0;
  end
  endfunction  
//declaraciones de 4 metodos
  
task muestrear;
begin
    //lanzamiento de procedimientos de monitorizacion
     fork
      sb.monitor_input; //lanzo el procedimiento de monitorizacion cambio entrada y calculo del valor target
      sb.monitor_output;//lanzo el procedimiento de monitorizacion cambio salida y comparacion ideal
     join_none
end
endtask  
task prueba_directa;
	testar_ports.sd.empieza <= 1'b0;
	testar_ports.sd.data_in <= 8'd25;
  repeat(3) @(testar_ports.sd);
	testar_ports.sd.empieza <= 1'b1;
  @(testar_ports.sd);
	testar_ports.sd.empieza <= 1'b0;
  @(negedge testar_ports.sd.termina);
endtask
task prueba_random_impares;
  while ( valores_X_trigger.mon_entradas_impares.get_coverage()&lt;80)
	begin
	   busInst.pares.constraint_mode(0);
	   $display("pruebo con impares");
	   assert (busInst.randomize()) else    $fatal("randomization failed");
    	testar_ports.sd.data_in<= busInst.valor;	
    	valores_X.sample();
      @(testar_ports.sd);
	   testar_ports.sd.empieza <= 1'b1;
      @(testar_ports.sd);
	   testar_ports.sd.empieza <= 1'b0;
      @(negedge testar_ports.sd.termina);
   end
endtask
task prueba_random_pares;
  while ( valores_X_trigger.mon_entradas_pares.get_coverage()&lt;80)
	begin
 	   busInst.impares.constraint_mode(0);
	   busInst.pares.constraint_mode(1);
	   $display("pruebo con pares");
	   assert (busInst.randomize()) else    $fatal("randomization failed");
    	testar_ports.sd.data_in <= busInst.valor;	
    	valores_X.sample();  
      @(testar_ports.sd);
        testar_ports.sd.empieza <= 1'b1;
      @(testar_ports.sd);
	   testar_ports.sd.empieza <= 1'b0;
      @(negedge testar_ports.sd.termina);
   end
endtask
  
endclass
endpackage

Vamos a ver todo el proyecto en el siguiente laboratorio virtual. Esta solución de banco de pruebas funciona perfectamente en questasim aun cuando el «testbench» sea distribuido en cuatro ficheros:

  • interfaz.sv
  • test.sv
  • enviroment.sv
  • banco_pruebas.sv

Laboratorio virtual

https://www.edaplayground.com/x/78af