¿Qué tan rápido debo registrar mi CPLD en comparación con la velocidad de mi bus SPI?

5

Como estoy seguro de que todos aquí saben, en el diseño de FPGA / CPLD, a menudo se necesita sincronizar una señal asíncrona más lenta (por ejemplo, la línea SCK de SPI) con una señal de reloj mucho más rápida que se envía directamente al FPGA / CPLD. Mi pregunta es, ¿cuánto más rápido debe ser el reloj FPGA / CPLD en relación con mi señal asíncrona? ¿Diez veces? ¿Veinte veces?

En mi caso, menos de 10x no funciona bien. Específicamente: configuré mi velocidad de SCK a 4 MHz mientras que mi reloj fue de 20 MHz. Esto no funcionó en absoluto. 2 Mhz funciona, pero ocasionalmente me surgen algunos problemas. A 1 MHz, funciona muy bien, sin problemas hasta ahora.

Código VHDL para el CPLD:

library ieee;
use ieee.std_logic_1164.all;

entity PISO is
  port(CLK, nCS, SCK, nRESET : in std_logic;
                PI  : in  std_logic_vector(71 downto 0);
                SO  : out std_logic);
end PISO;


architecture archi of PISO is
   signal tmp: std_logic_vector(PI'high downto PI'low);
   signal bitOut: std_logic;
   signal rise, fall : std_logic;
   signal oscena:   std_logic;
   signal iCLK  :   std_logic;

   signal SCK_rising, SCK_falling, SCK_sync, SCK_delay : std_logic;
   signal CS_rising, CS_falling, CS_sync, CS_delay     : std_logic;

   component sync
    generic(
        RESET_STATE : std_logic := '0' -- '0' for active low sync
    );
    port(
        clk  : in  std_logic;
        rstN : in  std_logic;
        d    : in  std_logic;
        q    : out std_logic
    );
end component;

   begin
    sync1 : sync
    generic map(
        RESET_STATE => '0'
    )
    port map(
        clk  => clk,
        rstN => nRESET,
        d    => sck,
        q    => SCK_sync
    );

sync2 : sync
    generic map(
        RESET_STATE => '1'
    )
    port map(
        clk  => clk,
        rstN => nRESET,
        d    => nCS,
        q    => CS_sync
    );

    process(clk, nRESET)
begin
    if (nRESET = '0') then
        sck_rising  <= '0';
        sck_falling <= '0';
        sck_delay   <= '0';
    elsif rising_edge(clk) then
        if cs_sync = '1' then
            sck_delay   <= '0';
            sck_rising  <= '0';
            sck_falling <= '0';
        else
            sck_delay   <= sck_sync;
            sck_rising  <= sck_sync and (not sck_delay);
            sck_falling <= (not sck_sync) and sck_delay;
        end if;
    end if;
end process;

process(clk, nRESET)
begin
    if (nRESET = '0') then
        cs_rising  <= '0';
        cs_falling <= '0';
        cs_delay   <= '0';
    elsif rising_edge(clk) then
        cs_delay   <= cs_sync;
        cs_rising  <= cs_sync and (not cs_delay);
        cs_falling <= (not cs_sync) and cs_delay;
    end if;
end process;


process(CLK, nRESET)
begin
    if (nRESET = '0') then
        tmp <= (others => '0');
    elsif rising_edge(CLK) then
        if CS_sync = '0' then
            if SCK_falling = '1' then
                tmp <= tmp(PI'high -1 downto PI'low) & '0';
            end if;
        elsif CS_sync = '1' then
            tmp <= PI;
        end if;
    end if;
end process;

SO <= tmp(PI'high) when nCS = '0' else 'Z';


end archi;

Y aquí está el código para el componente de sincronización:

library ieee;
use ieee.std_logic_1164.all;

entity sync is
generic (
    RESET_STATE : std_logic := '0' -- '0' for active low sync
);
port (
    clk   : in  std_logic;
    rstN  : in  std_logic;
    d     : in  std_logic;
    q     : out std_logic
);
end entity;

architecture behavioral of sync is
    signal d_meta : std_logic;
    begin
process(clk, rstN)
begin
    if (rstN = '0') then
        d_meta <= RESET_STATE;
        q      <= RESET_STATE;
    elsif (clk'event and clk = '1') then
        d_meta <= d;
        q      <= d_meta;
    end if;
end process;
end architecture;

En cuanto a la simplicidad, sé que SPI es super simple pero soy un novato, por lo que todo esto es bastante difícil para mí. Solo después de semanas tenía sentido para mí que necesitaba sincronizar. las señales en el CPLD / FPGA (inicialmente solo estaba usando el SCK como mi reloj y ni siquiera tenía un reloj separado en mi tablero. Funcionó bien para velocidades más lentas, pero aumentar la velocidad incluso a 1 MHz hizo que la ingenuidad de mi enfoque obvio). Estoy seguro (de hecho, lo sé por sus excelentes publicaciones por aquí) su enfoque es mucho más simple y elegante, el problema es que tendré que entenderlo primero porque a partir de ahora simplemente suena como griego a mi!

    
pregunta Saad

3 respuestas

9

Hay tantas cosas en esta pregunta que es difícil saber por dónde empezar.

Estoy asumiendo que su lógica FPGA es un esclavo SPI, no un maestro. Si es un maestro, entonces tienes un conjunto de problemas completamente diferente que evitaré en este momento.

La respuesta directa simple a su pregunta es que necesita muestrear una señal asíncrona al menos dos veces la frecuencia de su señal. Entonces, si tiene un reloj de 4 MHz, entonces necesita muestrearlo a 8 MHz o más. Por supuesto, nada es simple o directo en este caso.

Tiene las cosas un poco más difíciles porque no está muestreando una señal asíncrona, está muestreando tres (CLK, CS y MOSI). También debe mantener esas tres señales alineadas en el tiempo entre sí a través del proceso de muestreo. Y tiene que escupir MISO de tal manera que no viole su tiempo de configuración / espera en el maestro.

Nada de esto es fácil, pero tener un reloj de mayor velocidad hará las cosas mucho más fáciles. Cuánto más depende de tu código, y no lo publicaste. Creo que podría escribir código para hacerlo con un reloj de 8x, pero eso es solo una conjetura. Honestamente, sin embargo, creo que este es el enfoque equivocado.

SPI es una interfaz super simple, y sería bueno si lo mantuvieras super simple. SPI tiene su propio reloj, y si lo usa como un reloj, entonces todo se vuelve casi fácil. En lugar de cambiar los dominios de reloj en la interfaz SPI en serie, cambie los dominios de reloj en los datos paralelos que entran / salen de sus registros de turnos. Si observa esas señales con atención, puede que incluso se dé cuenta de que no necesita hacer nada especial, o si lo hace, entonces es solo un flip-flop por señal. Entonces, no es necesario que su reloj principal sea más alto que su reloj SPI. ¡Tu reloj principal podría ser más lento!

Hago esto en mis interfaces SPI FPGA / CPLD y no tengo problemas para ejecutar SPI a más de 30 MHz, con o sin un segundo dominio de reloj.

    
respondido por el user3624
0

Sugeriría que, si fuera práctico, un esclavo SPI debería tener registros que estén sincronizados desde los pines SPI. Esos registros, o señales que se derivan de ellos, deberían estar sincronizados con otra lógica. Dependiendo de los requisitos de la aplicación, las señales de la otra lógica se pueden sincronizar dos veces con el reloj SPI o se pueden enviar a la salida de datos de SPI de forma asíncrona (si los datos de SPI se van a alimentar a una ruta de datos única que contiene al menos dos pestillos) antes de que se divida, y si a la aplicación no le importa si un intento de leer algo en el momento en que cambia tiene un rendimiento alto o bajo, el informe asíncrono puede estar bien).

Incluso si uno no desea diseñar una interfaz esclava SPI completa para ser sincronizada con SPI, todavía puede tener algunos aspectos clave de los cables SPI sincronizados. Como mínimo, sugeriría que el cable del reloj SPI debería activar un pestillo para los datos y un pestillo de palanca. Pase el bloqueo de datos a través de un nivel de sincronización más que el control de activación, y luego suponga que cada vez que el valor del control de activación difiere del anterior, existen nuevos datos en el cable del reloj. Alguna lógica de enganche adicional en el cable de selección de chip permitiría la detección confiable de eventos cortos de no selección / reselección.

PS - Dependiendo de cómo se diseñen la lógica y el protocolo de software, es posible que un dispositivo esclavo SPI funcione de manera confiable, incluso si a la lógica adjunta le gusta dormir cuando está inactiva y no se despierta "instantáneamente". Tal comportamiento no podría implementarse si todo el bus SPI tuviera que estar sincronizado con el reloj de lógica principal (que no se ejecutaría mientras el dispositivo estaba inactivo).

PPS: si realmente desea ejecutar todo de forma síncrona con algún reloj "principal", debe calcular que los tiempos de configuración y retención de los datos SPI entrantes se incrementarán en al menos un período de su reloj maestro, y Los datos salientes se retrasarán una cantidad impredecible que puede ser desde cero hasta un período de su reloj maestro. Su reloj maestro debe ser lo suficientemente rápido para que pueda agregar un período de reloj maestro completo a sus incertidumbres de tiempo sin violar sus restricciones de tiempo en cualquier lugar. Si su intervalo de tiempo permitido es 1/4 de su período de reloj SPI, su reloj maestro debe ser más de 4 veces su reloj SPI. Si su intervalo de tiempo permitido es 1/16 de su período de reloj SPI, su reloj maestro debe ser más de 16 veces su reloj SPI.

    
respondido por el supercat
0

El consejo de usar el reloj SPI para manejar el registro de cambios SPI es correcto y le ahorra la molestia de intentar sobreexplotar correctamente. Una pregunta está en cómo transfieres ese registro paralelo a otro dominio. NO use un simple sincronizador FF de dos etapas para cada bit como lo haría para una señal solitaria. Si utiliza los sincronizadores FF, existe un riesgo real de que la sincronización de datos no sea válida en el dominio de destino debido a la inclinación o la metastabilidad en algunos bits. Los FF emparejados solo hacen que la probabilidad de metastabilidad sea extremadamente baja pero no cero. Supongamos que sucederá.

Los buses paralelos deben transferirse a través de dominios con un sincronizador de intercambio. Para un diseño totalmente sincrónico que se pueda analizar con herramientas de temporización estáticas, el protocolo de cuatro fases es el mejor para usar. En este protocolo, tiene un par de señales válidas de datos y de acuse de recibo a través de dominios y garantiza que el bus sea estable mientras el dominio de destino lo registra.

Una excepción a esta regla es si tiene un contador codificado en gris. Debido a que solo se pueden enviar transiciones de un bit por conteo a través de dominios con los FF emparejados más simples.

    
respondido por el KevinThibedeau

Lea otras preguntas en las etiquetas