Modelsim - Problema de verificación extraño con DDR y Xilinx UNISIM

Estoy verificando el componente VHDL usando OVM y me encontré con serios problemas. Descubrí que el problema está en un componente específico y creé un entorno específicamente para él. Es un convertidor de RGMII a interfaz interna (de DDR a SDR) que utiliza componentes Xilinx UNISIM para simular el comportamiento especial de las partes del chip FPGA.

BUFG_inst : BUFG
port map (
    O => INTERNAL_CLK,
    I => RGMII_CLK
);

IDDRDATA0_inst: IDDR
generic map (
    DDR_CLK_EDGE => "SAME_EDGE_PIPELINED",
    INIT_Q1 => '0',
    INIT_Q2 => '0',
    SRTYPE  => "SYNC"
)
port map (
    Q1 => data(0),
    Q2 => data(4),
    C  => INTERNAL_CLK,
    CE => CE,
    D  => RGMII_DATA(0),
    R  => RST,
    S  => '0'
);
-- several more instantiations

Genero transacciones y las envío a DUT usando el controlador:

forever
begin
my_transaction tx;

@(posedge dut_vi.clock); 
seq_item_port.get(tx);

dut_vi.ctl   = tx.ctl;
dut_vi.reset = tx.reset;
dut_vi.data  = tx.data;

@(negedge dut_vi.clock); 
seq_item_port.get(tx);

dut_vi.ctl   = tx.ctl;
dut_vi.reset = tx.reset;
dut_vi.data  = tx.data;
end

En Modelsim wave, esta entrada (observar los puertos de entrada del DUT) está bien. Pero internamente, toma el valor anterior de los datos; por ejemplo, el valor anterior de CTL era 0, en el flanco ascendente es 1, pero el DUT muestrea con el flanco ascendente ese valor anterior. 0 ya que no se actualizó dentro de modelsim.

Intenté usar "pequeño truco":

TMP_CLK <= RGMII_CLK after 1ps;

BUFG_inst : BUFG
port map (
    O => INTERNAL_CLK,
    I => TMP_CLK
);

Resolvió el problema (parcialmente). El DUT ahora estaba muestreando los valores correctos... Pero creó un problema con la salida y el monitor. Ahora todo está correcto en la onda, pero el monitor ve valores incorrectos (pero solo a veces, generalmente al comienzo del paquete, la señal válida cambia de 0 a 1). Entonces es un problema similar al de la entrada. Mi monitor se ve así:

forever
begin
dut_out_transaction tx;

@(posedge dut_out_vi.clock);          
tx = dut_out_transaction::type_id::create("tx");

tx.dv    = dut_out_vi.dv;
tx.err   = dut_out_vi.err;
tx.sof   = dut_out_vi.sof;
tx.eof   = dut_out_vi.eof;
tx.data  = dut_out_vi.data;

$display("DUT_OUT EOF: %b", dut_out_vi.eof);

if (dut_out_vi.eof == 1)  // this never happens
    ovm_report_info("out_monitor", "EOF");

aport.write(tx);
end

Como es un error similar, probé un truco similar: agregué #1ps justo después de @(posege dut_out_vi.clock); y realmente ayudó ... Pero surgió un nuevo problema: ahora el monitor nunca ve la señal EOF en 1 a pesar de que está en 1 en la onda varias veces.

Yo uso Modelsim 10.5 con resolución de 1ps. ¿Alguna idea de qué podría estar causando o cómo solucionarlo?

Es posible que desee intentar agregar un bloque de reloj ( IEEE1800-2012 § 14 Bloques de reloj ) a su interfaz. Esto puede permitirle controlar qué tan lejos a la izquierda del reloj se considerará su valor de muestra.
@Greg Interesante... Le echaré un vistazo. Mientras tanto, lo arreglé usando esperar. Publicaré la respuesta.

Respuestas (1)

Finalmente lo resolví: el problema estaba en la biblioteca unisim de Xilinx. Por alguna razón, hay un retraso de 100ps (que no debería estar en la simulación de comportamiento) en IDDR. Entonces, la solución es esperar en el monitor (en realidad tengo dos: para entrada y salida de DUT):

forever
begin
  my_transaction tx;

  @(posedge dut_vi.clock);
  #1ps
  tx = my_transaction::type_id::create("tx");

  tx.ctl   = dut_vi.ctl;
  tx.reset = dut_vi.reset;
  tx.data  = dut_vi.data;

  aport.write(tx);

  @(negedge dut_vi.clock);
  #1ps
  tx = my_transaction::type_id::create("tx");

  tx.ctl   = dut_vi.ctl;
  tx.reset = dut_vi.reset;
  tx.data  = dut_vi.data;

  aport.write(tx);
end

y segundo:

forever
begin
  dut_out_transaction tx;

  @(posedge dut_out_vi.clock);
  #101ps   // simulation hack
  tx = dut_out_transaction::type_id::create("tx");

  tx.dv    = dut_out_vi.dv;
  tx.err   = dut_out_vi.err;
  tx.sof   = dut_out_vi.sof;
  tx.eof   = dut_out_vi.eof;
  tx.data  = dut_out_vi.data;
end

En el monitor de entrada, tengo que esperar 1 ps (no entendí por qué) y en la salida, tengo que esperar 101 ps debido a la demora dentro de IDDR (revisé el código). De esta manera, el marcador obtiene los datos correctos.