Aleatorización


La aletaorización corre a cargo de la llamada al método predefinido “randomize” a cualquier objeto del tipo definido por la clase.

Definir un objeto, requiere el uso de la declaración del objeto y de la instanciación.

Bus P; //declarar: generar un identificador a un objeto de tipo Bus – inicializado en null
P=new; //instanciar: asignar el espacio para el objeto y ejecutar automáticamente la función new() en la clase para la inicialización
  • Evidentemente para poder hacer esto la clase que se referencia debe de ser visible
  • Evidentemente ambas acciones (declarar e instanciar) se pueden hacer en una única línea: Bus P =new;
  • La función new() es el contructor – sin valor de retorno. El constructor se puede omitir en la clase: en este caso, se asigna el espacio para el nuevo objeto y se conservan los valores iniciales de las propiedades.
  • new() puede tomar argumentos:
function new(bit [40:0] a);
     addr = a;
endfunction

Llegado a este punto supongamos que quiero aleatorizar . Bastaría, si ya tenemos el objeto construido , en llamar el método «randomize» . En el siguiente ejemplo mostramos la construcción y la realización de dos randomizaciones:

nitial begin
  Bus busInst;
  busInst = new;
  // now busInt.addr and busInst.data are both zeroes
  if (!busInst.randomize()) begin
    $display(“randomization failed”);
    $finish();
  end
// now bustInst.addr and busInst.data have random values that satisfy the constraints
  assert (busInst.randomize()) else    $fatal(“randomization failed”);
// now bustInst.addr and busInst.data have other random values that satisfy the constraints
end

Cada vez que aleatorizamos una clase, se selecciona aleatoriamente una entre todas las combinaciones posibles de todas las variables aleatorias que satisfacen todas las restricciones que hemos colocado. En el caso de que no haya ninguna combinación posible, la función randomize dará un «0»

Acciones adicionales en el método randomize

Añadir más restricciones

Se realiza con la palabra reservada «with»

res = bus.randomize() with {addr[0] || addr[1];};

Se puede observar que al randomizar indicamos una restricción adicional expresada entre las llaves. Además de todas las restricciones dadas en la clase, para sólo esta aleatorización agregamos la restricción que addr[0] o addr[1] debe ser 1.

Cambiar el carácter de las variables

  • Las clases pueden tener miembros rand y no-rand
  • al aleatorizar una clase, aleatorizamos solo las variables aleatorias, los no aleatorios mantienen sus valores
  • Las restricciones pueden mezclar en sus expresiones variables rand y no rand

Veamos un ejemplo

class B;
  rand bit a, b;
  bit c, d;
  constraint c1 { d -> b;};
  constraint c2 { a &b == c;};
endclass

Cuando llamamos al método randomize podemos cambiar el carácter de dichas variables

  • inst.randomize(a) : poniendo como argumento la variable a indicamos que solo esta variable va a ser considerada como randomizable. La variable b será considerada como variable estática.
  • inst.randomize(a,c): El efecto sobre a y b es el mismo del ejemplo anterior; sin embargo la variable c que era estática, pasa a ser ahora en esta randomización como variable.

Métodos adicionales

constraint_mode

Veamos el siguiente ejemplo. Como puede observarse, podemos inhabilitar antes de cada llamada a randomize las restricciones que deseemos con tan solo llamar el método constraint_mode asociadas a dicha restricción y poniendo como argumento un cero.

Cuando deseemos podremos volver a habilitar la restricción con el mismo método asociada a la misma y con un argumento uno.

class Bus;
  rand bit[15:0] addr;
  rand bit[31:0] data;
  constraint word_align {addr[1:0] == 2’b0;}
  constraint addr_data_dep {addr==0 -> data==0;}
endclass
initial begin
  Bus bus;
  bus = new;
bus.randomize();
// turn off the word_align constraint
bus.word_align.constraint_mode(0);
bus.randomize();
bus.randomize() with {addr[0]==1;};
// turn on the word_align constraint
bus.word_align.constraint_mode(1);
bus.randomize();

rand_mode

Tomando como ejemplo el código anterior en el que tenemos dos variables aleatorizables : addr y data Después de esta declaración: bus.addr.rand_mode(0);

«addr” dentro del objeto “bus” se considera como una variable no-rand(no aleatorizado cuando realizamos el randomize())

Para volver a su situación rand, usamos: bus.addr.rand_mode(1);

pre_randomize y post_randomize

En nuestra clase podemos definir unas funciones pre_randomize y post_randomize. Veamos el siguiente ejemplo:

program ordering;
  class frame_t;
    rand bit zero;
    rand bit [15:0] data [];
  
    constraint frame_sizes {
     solve zero before data.size;     
      zero -> data.size == 0;
      data.size inside {[0:10]};
      foreach (data[i])
        data[i] == i;
    }
     function void pre_randomize();
      begin
        assert (zero==1'b0) else 
          $display("zero estaba activo");
      end
    endfunction
    function void post_randomize();
      begin
        assert (zero==1'b0) else 
          $display("zero esta activo");
        $display("length   : %0d", data.size());
        for (integer i = 0; i < data.size(); i++) begin
         $write ("%2x ",data[i]);
        end
        $write("\n");
      end
    endfunction
  endclass
  initial begin
     frame_t frame = new();
     integer i,j = 0;
    for (j=0;j < 10; j++) begin
       $write("-------------------------------\n");
       $write("Randomize Value\n");
       i = frame.randomize();
     end
     $write("-------------------------------\n");
  end
endprogram

Dichos métodos serán ejecutados automáticamente (sin necesidad de llamarlos explícitamente) justo antes y después de que tenga lugar la aleatorización al llamar randomize()

el flujo de ejecución será el siguiente:

  • frame.pre_randomize();
  • frame is randomized
  • frame.post_randomize();

Veamos el funcionamiento en el siguiente laboratorio virtual

randcase

A veces lo que deseamos aleatorizar mediante el «solver» es la ejecución de un determinada rutina u otra en función de unos pesos que indiquen la probabilidad de las mismas. Eso lo realizaremos mediante «randcase«. Veamos el siguiente ejemplo

while (driver.sb.size>0) //empiezo a vaciar
    fork begin
		fork
			 randcase
				10: driver.genPop_rand();
				5: driver.genPush_rand();
			  endcase
			 randcase
				10: driver.genPop_rand();
				6: driver.genPush_rand();
			  endcase
		join_any
                $display("la fifo tiene  %d palabras", driver.sb.size);
		$display("Instance coverage is %e", driver.COV_entradas.get_coverage());
		disable fork;
      end join

Podemos observar en el «fork join_any» central que realizamos dos operaciones concurrentes representadas cada una de ellas con un randcase. Estas operaciones concurrentes tienen una mayor probabilidad de ser operaciones de lectura (en este caso de una FIFO) que de escritura. Si ambos procesos concurrentes coinciden con la misma rutina, ésta se realiza una única vez. Si la rutina es diferente se realizarán ambas (lo cual permitiría lectura y escritura simultánea de la FIFO).

Con esta estructura podemos lograr un vaciado progresivo random de la FIFO, sin impedir que pueda haber escrituras o escrituras y lecturas simultáneas.