Sincronizando entrada y salida

1

¿Cómo sincronizo este sistema? Los datos válidos en la entrada indican cuándo los datos son válidos en la entrada. Del mismo modo, la salida de datos válida indica cuándo son válidos los datos de salida. Tanto data_in_valid como data_out_valid permanecen altos para un ciclo de reloj.

Esto se debe hacer sin un FIFO.

Lo que se requiere es que estoy escribiendo datos a 100 MHz y quiero leerlos a 30 MHz en la salida. La señal valid_in es respecto al reloj de entrada y valid_out es respecto a clk de salida.

clkA y clkB son ambas entradas. Data_in y Data_out se escriben y leen 1 byte respectivamente. Es necesario escribir datos en clkA cuando valid_in es alto y leer datos en clkB cuando valid_out es alto. Lo que respondí fue con el uso de un sincronizador de 2 etapas (que se ejecuta en clkB) tanto para data_in como valid_in, y las salidas de los sincronizadores serán válidas_out y data_out respectivamente.

    
pregunta user22348

3 respuestas

1

Creo que habría respondido a esta pregunta de la entrevista de la misma manera que tú. Creo que el requisito del entrevistador de "hacerse sin una FIFO" era porque una memoria intermedia FIFO es una forma válida y práctica de resolver el problema de los dominios de múltiples relojes, pero puede hacerse sin la lógica de cabeza / cola de una FIFO completa en muchos casos. Y en el contexto de una entrevista de trabajo, simplemente crear una instancia de un módulo estándar no demuestra que entienda cómo enfocar el diseño de FPGA / HDL. (He entrevistado a candidatos que ni siquiera pudieron manejar esa pequeña tarea).

El paso de datos entre diferentes dominios de reloj se hace generalmente con tres etapas de flip-flops. La primera etapa se encuentra en el dominio de reloj de origen (clkA), y los flip-flops de la segunda y tercera etapa están en el dominio de reloj del receptor (clkB). El tiempo de configuración del flip-flop de la segunda etapa se viola a veces porque los relojes no son síncronos, por lo que el flip-flop de la tercera etapa se usa para limpiar el tiempo. Dado que hay un retraso, la señal de data_valid se pasa en paralelo con los datos.

module SyncExample (
    input   wire            clkA,
    input   wire    [7:0]   Data_in,        // in clkA clock domain
    input   wire            Data_valid,     // in clkA clock domain
    input   wire            clkB,
    output  reg     [7:0]   Data_out,       // in clkB clock domain
    output  reg             Data_out_valid  // in clkB clock domain
    )

// First stage pipeline registers the clkA clock domain signals.
// pipeline_1_valid is set by Data_valid and remains set 
// until cleared by pipeline_1_valid_clear acknowledge from clkB domain.
reg [7:0] pipeline_1_data;
reg       pipeline_1_valid;
wire      pipeline_1_valid_clear;
initial begin
    pipeline_1_data <= 0;
    pipeline_1_valid <= 0;
end
always @(posedge clkA) begin
    if (Data_valid) begin
        // capture pipeline_1_data only when Data_in is valid
        pipeline_1_data <= Data_in;
    end
    // keep pipeline_1_valid set after Data_valid, until pipeline_1_valid_clear.
    pipeline_1_valid <= (Data_valid | (pipeline_1_valid & ~pipeline_1_valid_clear));
end

// Second stage pipeline registers the clkB clock domain signals.
// Because clkA and clkB are asynchronous clock domains, 
// setup time cannot be guaranteed for this stage.
// The previous pipeline_1 stage holds its data valid for
// more than one clkA cycle, to help achieve clkB setup requirement.
reg [7:0] pipeline_2_data;
reg       pipeline_2_valid;
initial begin
    pipeline_2_data <= 0;
    pipeline_2_valid <= 0;
end
always @(posedge clkB) begin
    pipeline_2_data <= pipeline_1_data;
    pipeline_2_valid <= pipeline_1_valid;
end

// Third stage pipeline registers the clkB clock domain signals.
initial begin
    Data_out <= 0;
    Data_out_valid <= 0;
end
always @(posedge clkB) begin
    Data_out <= pipeline_2_data;
    Data_out_valid <= pipeline_2_valid;
end

// pipeline_1_valid_clear timing feedback signals when the data-valid signal
// has propagated through all stages.
// For this simple example, we assume data_out is captured as soon as it is valid.
// A practical application should instead drive this with a read_data_out command.
assign pipeline_1_valid_clear = Data_out_valid;

endmodule;

También puede encontrar un código de ejemplo similar en las plantillas de idioma ISE de Xilinx en Verilog | Construcciones de síntesis | Ejemplos de codificación | Misceláneo Sincronización asíncrona de entrada.

editar: Se agregó la señal pipeline_1_valid_clear y el comportamiento de establecer / borrar para cumplir con el requisito de ancho de pulso mínimo del dominio de reloj más lento. Capture pipeline_1_data solo cuando Data_in sea válida.

    
respondido por el MarkU
0

Lo que sabemos: los datos de entrada y salida de datos están en serie, los datos son de 8 bits y los relojes son asíncronos y tienen diferentes frecuencias (no relacionadas). Tampoco estamos autorizados a utilizar FIFO.

No creo que sincronizar sea la terminología correcta. Es más parecido a un circuito de almacenamiento de datos, toma datos seriales rápidamente, los almacena y los registra en serie a una velocidad menor (por ejemplo, escritura en un disco duro, impresora, etc.). Dado que era una pregunta de entrevista, no se podían esperar grandes detalles del circuito.

El digrama de bloques muestra el tipo de elementos que esperaría en un sistema así. Se necesitarían contadores para garantizar que se contabilizaran los 8 bits y una pequeña cantidad de lógica de cola (compuertas) para operar lectura / escritura, etc. Además, se podría agregar RAM entre los dispositivos SIPO y PISO para mejorar el rendimiento.

    
respondido por el JIm Dearden
0

Así es como entiendo que esto debería funcionar. Hice esto y lo probé en Xilinx ISim.

Esto sincroniza el indicador válido en los dominios, manteniéndolo en el dominio A hasta que se vea que llega al dominio B, y utilizando la detección de bordes en el dominio B para regenerar un solo ciclo estroboscópico.

El bus de datos se registra desde el dominio A en el dominio B cuando sabemos que se ha mantenido estable durante varios ciclos de reloj (en ambos dominios), por lo que es seguro usarlo así.

Este diseño podría tener cosas como una bandera lista, etc.

bus_sync.vhd:

-- CDC bus synchroniser
-- Depends on bus A being at least ~1.5x faster than bus B

library ieee;
use ieee.std_logic_1164.all;

entity bus_sync is
port (
    data_a  : in  std_logic_vector(7 downto 0);
    valid_a : in  std_logic;
    clk_a       : in  std_logic;
    data_b  : out std_logic_vector(7 downto 0);
    valid_b : out std_logic;
    clk_b       : in  std_logic
);
end bus_sync;

architecture behavioral of bus_sync is

    signal data_a_reg, data_b_reg : std_logic_vector(7 downto 0);
    signal valid_a_hold: std_logic := '0';
    signal valid_ack_a_sync, valid_ack_a: std_logic;
    signal valid_b_sync, valid_b_in, valid_b_last: std_logic;
    signal valid_b_reg: std_logic;

begin

    bus_in_sync_proc: process (clk_a)
    begin
        if rising_edge(clk_a) then
            if valid_a = '1' then
                data_a_reg <= data_a;
                valid_a_hold <= '1';
            elsif valid_ack_a = '1' then
                -- b domain has seen the valid strobe. We can deassert.
                valid_a_hold <= '0';
            end if;
        end if;
    end process;

    valid_sync_ack: process (clk_a)
    begin
        -- synchronise valid_b back into A domain as an 'ack'
        -- (could just keep valid_a_hold asserted for a number of clocks)
        -- domain A clocks 3x faster than domain B so we will not miss it.
        if rising_edge(clk_a) then
            valid_ack_a_sync <= valid_b_in; -- avoid metastability
            valid_ack_a <= valid_ack_a_sync;
        end if;
    end process;

    b_sync_proc: process (clk_b)
    begin
        if rising_edge(clk_b) then
            -- synchronise valid flag into b domain through 2 DFFs
            valid_b_sync <= valid_a_hold;
            valid_b_in <= valid_b_sync;
            -- and one more for edge detection
            valid_b_last <= valid_b_in;

            if valid_b_last = '0' and valid_b_in = '1' then
                -- valid strobe arriving in b domain
                -- registered data is stable in data_a_reg
                data_b_reg <= data_a_reg;
                valid_b_reg <= '1';
            else
                valid_b_reg <= '0';
            end if;
        end if;
    end process;

    data_b <= data_b_reg;
    valid_b <= valid_b_reg;

end behavioral;

un banco de pruebas de ejemplo:

library ieee;
use ieee.std_logic_1164.all;

entity bus_sync_tb is
end bus_sync_tb;

architecture behavior of bus_sync_tb is 

    -- component declaration for the unit under test (uut)
    component bus_sync
    port(
         data_a : in  std_logic_vector(7 downto 0);
         valid_a : in  std_logic;
         clk_a : in  std_logic;
         data_b : out  std_logic_vector(7 downto 0);
         valid_b : out  std_logic;
         clk_b : in  std_logic
        );
    end component;

   --inputs
   signal data_a : std_logic_vector(7 downto 0) := (others => '0');
   signal valid_a : std_logic := '0';
   signal clk_a : std_logic := '0';
   signal clk_b : std_logic := '0';

    --outputs
   signal data_b : std_logic_vector(7 downto 0);
   signal valid_b : std_logic;

   -- clock period definitions
   constant clk_a_period : time := 10 ns;
   constant clk_b_period : time := 33.333 ns;

begin

    -- instantiate the unit under test (uut)
    uut: bus_sync port map (
        data_a => data_a,
        valid_a => valid_a,
        clk_a => clk_a,
        data_b => data_b,
        valid_b => valid_b,
        clk_b => clk_b
    );

   -- clock process definitions
   clk_a_process :process
   begin
        clk_a <= '0';
        wait for clk_a_period/2;
        clk_a <= '1';
        wait for clk_a_period/2;
   end process;

   clk_b_process :process
   begin
        clk_b <= '0';
        wait for clk_b_period/2;
        clk_b <= '1';
        wait for clk_b_period/2;
   end process;


   -- stimulus process
   stim_proc: process
   begin
      wait for clk_a_period*10;

      -- change testbench inputs on falling edge of clk.
        -- makes the ordering easier to see in wave view.
        wait until clk_a = '0';
        data_a <= x"5a";
        valid_a <= '1';
        wait until clk_a = '0';
        data_a <= (others => '0');
        valid_a <= '0';

        -- must not send more data through too soon!

        wait for clk_b_period*10;

        -- halt the simulation the only way I know how in VHDL:
        assert false report "TEST COMPLETE OK" severity failure;

   end process;

end;
    
respondido por el blueshift

Lea otras preguntas en las etiquetas