Lectura de un flujo de datos en serie con Verilog

Estoy usando un FPGA para muestrear un flujo de datos en serie (en este caso, resulta ser audio PCM). Básicamente, hay dos señales:

  • Reloj de bits: una señal de reloj básica (onda cuadrada)
  • Datos: el bit a leer está presente en el flanco ascendente del reloj

Actualmente, tengo una detección de borde asíncrona en el reloj de bits. Entonces, básicamente, una bandera subirá durante un ciclo de reloj cuando el reloj de bits esté en el borde ascendente. Mi enfoque fue esperar esta bandera. Cuando la bandera sube, tomo el valor de la línea de datos y lo pongo en un índice de mi búfer. Sin embargo, no creo que esto esté funcionando correctamente. ¿No puedo simplemente probar el valor del hardware de la línea de datos? ¿Necesito amortiguarlo? Aquí está mi código de referencia:

always @(posedge clk) begin // posedge of system clock

    if(bclk_rise == 1) begin // Rising edge on BCLK detected, sample the bit and place it in the correct buffer index

        audio_data[data_count] <= data; // 'data' is the wire type that's connected to the ADC data line

        data_count <= data_count + 1; // 5-bit value, gets reset to zero every 32 loops

    end

end

Respuestas (1)

Tienes dos requisitos aquí:

  1. Debe asegurarse de muestrear esos datos en el momento correcto de acuerdo con las necesidades del autobús.
  2. Debe sincronizar estos datos en el dominio del reloj de su sistema.

Hay un par de maneras de satisfacer estas necesidades.

Primero, si el reloj del bus es lo suficientemente lento por debajo del reloj de su sistema, puede sincronizar el reloj del bus con su dominio de reloj con un doble flop. Luego use un detector de borde simple para determinar cuándo es el borde ascendente. Esto se utiliza para muestrear de forma segura la línea de datos del bus. Tenga en cuenta que esto logra el # 2 automáticamente. Además, como se indica aquí , esta es la forma preferida de hacer esto en un FPGA.

Este método tiene el inconveniente de que los datos se muestrean en algún lugar entre dos y tres relojes del sistema más tarde que el borde del reloj del bus 'en el pin' real. Si esto es demasiado largo (debido a que el reloj de su sistema no es lo suficientemente rápido en comparación), debe buscar un camino alternativo.

En este método, muestrea antes de sincronizar con el dominio del reloj del sistema. El motivo es asegurarse de que está tomando muestras en el momento adecuado según el autobús.

always @(posedge bclk) begin  // positive edge of bus clock
    sampled_data <= data;
end

En este punto, tiene sampled_data, que es una señal en el dominio bclk. Debe sincronizarlo con el dominio del reloj de su sistema. Para hacer esto, debe usar un apretón de manos o un FIFO.

Una forma que funciona es hacer el registro de desplazamiento en el dominio del reloj del bus para obtener datos paralelos. Luego páselo a través de un FIFO de doble reloj al otro dominio. Los FPGA tienen primitivas solo para este uso.

// Latch the data in the bclk domain
always @(posedge bclk) begin
    fifo_d <= {fifo_d[30:0], data};

    data_count <= data_count + 1;
    if (data_count == 31)
        fifo_wr <= 1'b1;  // This will latch fifo_d to the dual clock fifo input
    else
        fifo_wr <= 1'b0;
end

// Read the data out of the FIFO in the system clock domain.
always @(posedge clk) begin
    if (fifo_ready)
        synchronized_data <= fifo_q;   // Now the data is in your domain.
end

Otras notas:

Al igual que con cualquier E/S en el borde de la FPGA, así como entre dominios de reloj, deberá definir correctamente las restricciones de tiempo. Describir los detalles es demasiado general para este foro, pero como se recomienda en los comentarios de Greg, este documento es una buena fuente para comprender las necesidades del cruce del dominio del reloj. Los proveedores de FPGA tienden a tener escritos decentes para las definiciones de retardo de entrada y retardo de salida también.

Este artículo me dijo que no debería usar posedge bclkdebido a algunas cosas asincrónicas (no lo entiendo completamente): doulos.com/knowhow/fpga/synchronisation
Todo lo que dice es cierto, excepto que no se da cuenta de que no funcionará si el reloj de su bus está cerca de la misma velocidad que el reloj de su sistema. Sus líneas de subida/bajada se retrasan al menos 2 relojes del sistema desde el cable real. Si eso está más allá de su ventana para cerrar, entonces no funcionará, por lo que debe hacer más de lo que dije. Lo insinué en la respuesta, pero permítanme actualizar para ser más claro.
El principal problema que plantea, por cierto, tiene más que ver con no querer utilizar los recursos limitados de sincronización de FPGA para este propósito. Pero si simplemente no puede hacerlo de otra manera (o si tiene buffers de reloj de sobra), obtiene una mejor sincronización usando un posedge bclk
Si quisiera usar posedge bclk, ¿no tendría que hacer algo especial en mi archivo de código/restricciones para convertirlo en una entrada sincronizada?
Añadiré una pista. Debe analizar el paso de datos entre dominios de reloj. Esto se llama Clock Domain Crossing (CDC). Recomiendo este papel .