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 rreloj y un generador de reset, si bien este último podría generarse en el test como un estímulo más.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 |
`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
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 |
`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 , input termina , output 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.
1 2 3 4 5 6 7 8 9 10 11 12 13 |
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.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
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 $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
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 |
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 ciclcos 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