Manejo básico de clases 2ª parte

Shallow Copy vs. Deep Copy

Shallow Copy (Copia Superficial): Una copia superficial crea un nuevo objeto, pero comparte los sub-objetos. Si se modifica un sub-objeto en la copia, el original también cambia.

class Header;
rand int len; // Longitud de la cabecera
function new();
this.len = $urandom_range(10, 20);
endfunction
endclass

class Packet;
Header hdr;   // Handle a otro objeto
int    id;
function new();
this.id = $urandom_range(1, 5);
this.hdr = new(); // Creamos el objeto cabecera
endfunction
endclass

module test_shallow;
initial begin
Packet p1 = new();
$display("[Inicial] p1.id=%0d, p1.hdr.len=%0d", p1.id, p1.hdr.len);
// Creamos una copia superficial de p1
Packet p2 = new p1;
$display("[Copia]   p2.id=%0d, p2.hdr.len=%0d", p2.id, p2.hdr.len);
// Modificamos la cabecera e ID a través de p2
p2.hdr.len = 99;
p2.id=25;
$display("[Resultado] p2.id=%0d, p2.hdr.len=%0d", p2.id, p2.hdr.len);
// Verificamos p1... ¡Ha cambiado también la cabecera, no así el ID!
$display("[Resultado] p1.id=%0d, p1.hdr.len=%0d", p1.id, p1.hdr.len);
end
endmodule

Deep Copy (Copia Profunda): Una copia profunda crea un nuevo objeto y nuevas instancias de todos sus sub-objetos. Requiere un método de copia personalizado.

class Header;
rand int len; // Longitud de la cabecera
function new();
this.len = $urandom_range(10, 20);
endfunction
function void copy(Header other);
this.len = other.len;
endfunction
endclass

class Packet;
Header hdr; // Handle a otro objeto
int    id;
function new();
this.id = $urandom_range(1, 5);
this.hdr = new(); // Creamos el objeto cabecera
endfunction
// Constructor de copia profunda
function void copy(Packet other);
this.id = other.id;
this.hdr.copy(other.hdr); // Se copia el objeto
endfunction
endclass

Clonado: Es una buena práctica implementar un método clone() que devuelva una copia del objeto.

class Packet;
Header hdr; // Handle a otro objeto
int    id;
function new();
this.id = $urandom_range(1, 5);
this.hdr = new(); // Creamos el objeto cabecera
endfunction
function void copy(Packet other);
    this.id = other.id;
    this.hdr.copy(other.hdr); // Se copia el objeto
endfunction

function Packet clone();
    clone=new();
    clone.copy(this); // Se copia el objeto
endfunction
endclass

module test_clone;
initial begin
Packet p1 = new();
Packet p2;
$display("[Inicial] p1.id=%0d, p1.hdr.len=%0d", p1.id, p1.hdr.len);
// Creamos una copia profunda usando clone
p2 = p1.clone();
$display("[Copia]   p2.id=%0d, p2.hdr.len=%0d", p2.id, p2.hdr.len);
// Modificamos la cabecera a través de p2
p2.hdr.len = 99;
$display("n[Modificado] p2.hdr.len ahora es 99");
// Verificamos p1... ¡Permanece intacto!
$display("[Resultado] p1.id=%0d, p1.hdr.len=%0d", p1.id, p1.hdr.len);
end
endmodule

Uso de packages

  • ¿Qué es un package?
    • Un package es un contenedor o espacio de nombres que agrupa definiciones comunes para que puedan ser compartidas fácilmente a través de múltiples archivos en un entorno de diseño o verificación.
  • ¿Qué puede contener?
    • parameter y localparam: Constantes globales.
    • typedef: Definiciones de tipos de datos personalizados (structs, enums, etc.).
    • function y task: Rutinas de utilidad reutilizables.
    • class: Definiciones de clases para la verificación orientada a objetos (muy usado en UVM).
    • import: Puede importar definiciones de otros paquetes.

Vamos a ver el siguiente ejemplo utilizando packages. Antes de ello supongamos que

  • Vamos escribiendo las diferentes clases en ficheros systemverilog independientes
  • Habrá relaciones «is a» y «has a» en muchos de ellos. Esto condiciona clarísimamente el orden en que dichos ficheros deben de ser compilados; pero vamos a realizar una salvaguarda que es la utilización de la definición con typedef de las diferentes clases que vamos utilizando. Eso nos va a permitir incluir esos ficheros systemverilog en cualquier orden.
package definitions_pkg;
typedef class Packet;
typedef class Header;
`include “Packet.sv”
`include “Header.sv" 
endpackage: definitions_pkg

function Packet clone();
    clone=new();
    clone.copy(this); // Se copia el objeto
endfunction
endclass

module test_clone;
initial begin
definitions_pkg::Packet p1 = new();
definitions_pkg::Packet p2;
$display("[Inicial] p1.id=%0d, p1.hdr.len=%0d", p1.id, p1.hdr.len);
// Creamos una copia profunda usando clone
p2 = p1.clone();
$display("[Copia]   p2.id=%0d, p2.hdr.len=%0d", p2.id, p2.hdr.len);
// Modificamos la cabecera a través de p2
p2.hdr.len = 99;
$display("n[Modificado] p2.hdr.len ahora es 99");
// Verificamos p1... ¡Permanece intacto!
$display("[Resultado] p1.id=%0d, p1.hdr.len=%0d", p1.id, p1.hdr.len);
end
endmodule

Convenciones del Curso

  • Deberiamos escribir las clases en ficheros systemverilog independientes.
  • Incluir como hemos visto dichos ficheros en un package que los agrupe.
  • Los handles a clases deben denominarse con el sufijo _h.
  • Al pasar un handle como argumento en funciones de clase, usar el nombre genérico rhs_h.
  • Usar el prefijo p_ o i_ para los argumentos de funciones.
  • Usar el prefijo m_ para los miembros de una clase.
  • Usar el sufijo _t para los typedef.
  • Usar el sufijo e_ para los enumerados.