Generando hilos en SystemVerilog con variables de entrada

0

Me encuentro con una situación bastante confusa donde estaba escribiendo un código para un monitor UVM. Este módulo realiza estas operaciones en orden:

  1. Escuche el canal (interfaz virtual) hasta que intercepte uno o varios eventos en el nivel de pin.
  2. Rellene una estructura de datos con cualquier valor que pueda leerse en los puertos de entrada de la interfaz.
  3. Genere un hilo secundario para esperar la respuesta y vuelva al paso 1.

Spawning está usando la construcción SystemVerilog Fork .. JOIN_NONE y estaba destinado a iniciar una tarea que solo espera la respuesta en la interfaz y luego continuar con la tarea principal - SIN tener que esperar a que la tarea generada finalice. Dentro de FORK JOIN, hay una tarea de SystemVerilog con argumentos de entrada que se pasan por valor. Cuando se inicia el programa, índice = 0. Cuando se ejecuta get_output (data_struct, index) dentro de FORK..JOIN, el índice sigue siendo 0. ¡Sin embargo, cambia una vez dentro de la tarea generada! Por favor, mire cuidadosamente donde coloco mis impresiones de depuración del índice variable. La primera imprime el índice = 0 y la segunda imprime el índice = 1. No es posible en mi opinión que esto pueda suceder, porque primero, las variables se pasan por valor a la tarea y el segundo índice se incrementa después de que la tarea se llama asumiendo instrucciones tener lugar en un orden secuencial dentro de la tarea run_phase. Quiero saber por qué el índice cambia dentro de la tarea generada por sí mismo. El ajuste que encontré para mantener el valor pasado es agregar una declaración de espera de 1ns después de que se haya llamado a la tarea generada.

Aquí está el código:

task run_phase(uvm_phase phase);
            int unsigned index;
            dpu_input_str data_struct;

            //monitoring

            index = 0;

            //wait until NOT reset
            @(posedge vif.rst_n);
            forever begin
                    //listen to channel: wait for any change in the IF signals
                    fork
                            @(vif.ctrl_0);
                            @(vif.ctrl_1);
                            ...
                    join_any
                    disable fork; //kill outstanding waiting processes
                    //create intermediate struct
                    data_struct.ctrl_0        = vif.ctrl_0;
                    data_struct.ctrl_1        = vif.ctrl_1;
                    ..

                    //wait for the output
                    //spawn a task to perform that and get back to channel listening
                    //again
                    fork
                            get_output(data_struct, index);
                    join_none
                    $display("Value of index after calling the task is:  %d", index);
                    //#1;
                   index++;
                end
        endtask: run_phase

    task get_output(input dpu_input_str data_struct,
                                    input int unsigned  index);
            dpu_simple_transaction dpu_tr;
            int unsigned delay_cnt;
            int i;
            //create analysis transaction
            dpu_tr = dpu_simple_transaction::type_id::create
            (.name("dpu_tr_dut_mon"),
             .contxt(get_full_name));

            $display("Value of index inside the task is:  %d", index);

            // fill in data back into analysis transaction
            // pay attention to cast when writing numeric values to enum datatypes

            dpu_tr.ctrl_0        = dpu_control_type'(data_struct.ctrl_0);
            dpu_tr.ctrl_1        = dpu_control_type'(data_struct.ctrl_1);
            ...

            // decide about the delay
            ...
    endtask
    
pregunta Theo

1 respuesta

1

Para ayudarme a explicar lo que está sucediendo, he copiado la parte relevante de su código. Hay un hilo principal que se está ejecutando y que llega a esta bifurcación ... join_none. Cuando un subproceso SV se encuentra con una bifurcación, registra los bloques adjuntos begin..end y las declaraciones secuenciales (como la llamada de la función get_output aquí) como subprocesos con el programador. Pero el programador no genera estos hilos inmediatamente. En realidad no puede hacerlo, hasta que el programador obtenga el control de ejecución. En este punto el hilo padre se está ejecutando. El programador tiene que esperar hasta que el hilo padre ceda. El subproceso principal se producirá al encontrar cualquier tipo de declaración de espera (o un bloqueo de lectura / escritura de puerto tlm, etc.).

Si hubieses usado la bifurcación emparejada con join o join_any, el hilo principal se hubiera producido de inmediato. Pero con join_none, no lo hace.

Por lo tanto, el subproceso principal continúa con la ejecución y solo cuando llega al número 1 (si no se ha comentado), da como resultado que el programador pueda generar el subproceso bifurcado. Y con el # 1 comentado, el índice se incrementa antes de que el hilo bifurcado tenga la oportunidad de ejecutarse. SystemVerilog le proporciona una solución. Implica utilizar la región declarativa de la construcción de la horquilla. Vea el segundo fragmento de código en la parte inferior.

//spawn a task to perform that and get back to channel listening again
fork
  get_output(data_struct, index);
join_none
$display("Value of index after calling the task is:  %d", index);
//#1;
index++;

Con el siguiente código, la declaración '' automatic int _index = index; '' se ejecuta inmediatamente, junto con el registro del hilo bifurcado. Para referencia consulte la sección 9.3.2 del estándar SV 1800-2012. Puede omitir el principio y el final de la envolvente a partir del siguiente código, pero creo que agregan claridad al código.

fork
  automatic int _index = index;
  begin
    get_output(data_struct, _index);
  end
join_none
    
respondido por el Puneet Goel

Lea otras preguntas en las etiquetas