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

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)

.
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()<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()<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
