El sumador binario simple funciona solo parcialmente

EDICIÓN POSTERIOR:

1. También investigué visualmente el dispositivo Kintex7 después de la implementación (es decir, interconexiones, etc.) y todo se ve bien; no hay conexiones que indiquen que las cosas no estarían bien (por supuesto, no volví al principio). entradas, ya que asumo que si el bucle invertido basado en 2-FIFO funciona, sería el sumador el que está roto de alguna manera).

Además, me encontré con este registro de respuestas de Xilinx . Traté de aplicar la solución sugerida, pero no pareció funcionar.

2. Para el segundo enfoque (entrada manual en Spartan6), me di cuenta de que el problema era un interruptor defectuoso que no establecería correctamente el valor de entrada a menos que se tuviera cuidado. He probado cuidadosamente de nuevo y esta vez obtuve el valor correcto. Sin embargo, cambié ese diseño para tomar las entradas de ROM/LUT y mostrar un byte a la vez, dependiendo de los interruptores de selección dados como entradas. Ya no hay reloj involucrado. El problema con el proyecto inicial basado en Vivado persiste, lo que me lleva a creer que podría ser un problema de síntesis/implementación/hardware defectuoso.

MENSAJE ORIGINAL:

Tengo un diseño PCIe basado en xillybus que está destinado a volverse más complejo, pero por ahora, de forma incremental, solo lee pares de valores de 32 bits de un FIFO de entrada de 32 bits conectado a xillybus y los suma, escribiéndolos en un FIFO de salida de 32 bits conectado a xillybus.

Ahora, el problema que veo es el siguiente:
NOTA 1: la forma de onda de salida de la simulación de comportamiento (nivel RTL, presíntesis) devuelve valores correctos, es la implementación real en la placa la que parece tener uno o más bits invertidos ( opuesto al valor esperado, que es el mismo que el resultado de la simulación)
NOTA 2: Yo, por ejemplo, uso los valores 0x3F800000y 0x3F800000(sí, el doble del mismo valor), que sumados poco a poco deberían sumar 0x7F000000- mencionaré a continuación cuál fue el resultado (incorrecto) en cada escenario:

  • Si pruebo un enfoque de bucle invertido (al leer desde el FIFO de entrada y enviarlo al FIFO de salida, por lo tanto, usando 2 FIFO, NO usando solo un FIFO conectado tanto a la entrada como a la salida desde/hacia xillybus), los valores se devuelven como se esperaba. No lo he probado exhaustivamente, pero parece estar bien para todos los valores que he probado.
  • Si trato de ejecutar el diseño en una placa NetFPGA-1G-CML (Kintex-7 XC7K325T-1FFG676 FPGA), obtengo el resultado 0x7E000100(recuerde, la prueba de bucle invertido funciona)
  • Si trato de ejecutar el diseño equivalente (adaptado a cómo se ingresan los valores, es decir, no a través de PCIe desde la PC, sino manualmente usando interruptores, botones pulsadores filtrados por generador monopulso y salida en LED) en una placa Atlys (Spartan -6 XC6SLX45-3CSG324), entonces el resultado es 0x6F00000(más cercano, ya que solo hay un poco de error, pero aún no es suficiente)

Tenga en cuenta que no recibo quejas sobre violaciones de restricciones de tiempo, y de todos modos he intentado muchas formas de concentrarme en el problema (es decir, filtrar algunas posibilidades), pero no llegué a ninguna conclusión. Para que conste, incluso transformé el sumador simple en una canalización de 2 niveles, para "garantizar" que haya suficiente tiempo para calcular el resultado, pero el resultado fue el mismo (incorrecto).

Aparentemente, no puedo ejecutar la simulación funcional o temporal posterior a la síntesis o posterior a la implementación, ya que aparece un error molesto que dice

ERROR: [VRFC 10-716] el puerto formal o del modo fuera no se puede asociar con el puerto real pcie_perst_b_ls del modo en [...]

Tenga en cuenta que estoy ejecutando Vivado 2014.2 (que es un poco antiguo, pero ¿sería ese realmente el problema?)

Debajo, el código para los 2 enfoques (E/S impulsada por PCIe basada en xillybus y E/S física simple). Lo siento, no pude formatearlo mejor:

  1. Enfoque para las E/S basadas en Kintex-7 PCIe:

    library IEEE;
    use IEEE.STD_LOGIC_1164.ALL;
    use IEEE.NUMERIC_STD.ALL;
    
    entity fsm is
        port (
            data_in : in std_logic_vector(31 downto 0);
            data_out : out std_logic_vector(31 downto 0);
            rd_en : out std_logic;
            in_fifo_empty : in std_logic;
            wr_en : out std_logic;
            out_fifo_full : in std_logic;
            clk, rst : in std_logic
        );
    end fsm;
    
    architecture Behavioral of fsm is
    
        component core
           port (
                operand_A_ieee, operand_B_ieee : in std_logic_vector(31 downto 0);
                result_ieee : out std_logic_vector(31 downto 0);
                clk, rst : in std_logic 
           );
        end component;
    
        -- pipeline_depth and pipeline_wr_status are used (only) for pipelined cores to assert wr_en when needed
        -- ('1' added to the MSB of pipeline_wr_status when the second 32-bit operand is read and therefore the 
        -- core processing starts with valid data, so that it signals when a valid result reached the end of the core)
        --constant pipeline_depth : integer := 10;
        --signal pipeline_wr_status : std_logic_vector(pipeline_depth - 1 downto 0) := (others => '0');
    
        type state_type is ( start, readA, waitB, addAB );
        signal state, next_state: state_type;
    
        signal operand_A_ieee : std_logic_vector(31 downto 0) := (others => '0');
        signal result_ieee : std_logic_vector(31 downto 0);
    
    begin
    
        core_inst: core
            port map (
                operand_A_ieee => operand_A_ieee,
                operand_B_ieee => data_in,
                result_ieee => data_out,
                clk => clk,
                rst => rst
            );
    
        -- The loopback test (remove core_inst above) works as expected - in the out FIFO the value read from the in FIFO is saved
        --data_out <= data_in;
    
        SL: process (clk, rst, state, next_state, data_in)--, pipeline_wr_status)
        begin
            if rising_edge(clk) then
                state <= next_state;
                if state = readA then
                    operand_A_ieee <= data_in;
                end if;
                -- needed if pipelined core
                --if next_state = addAB then
                    --pipeline_wr_status <= "1" & pipeline_wr_status(pipeline_depth-1 downto 1);
                --else
                    --pipeline_wr_status <= "0" & pipeline_wr_status(pipeline_depth-1 downto 1);
                --end if;
            end if;
        end process;
    
        -- wr_en flag has beem moved out of the case/process below, for simplicity
        wr_en <= '1' when state = addAB else '0';
        --wr_en <= pipeline_wr_status(0);
    
        -- TODO: add rst signal as input to the state machine
        CL: process(rst, state, in_fifo_empty, out_fifo_full)
        begin
            case (state) is
                when start =>
                    if rst = '1' then
                        next_state <= start;
                        rd_en <= '0';
                    else
                        if in_fifo_empty = '1' then
                            next_state <= start;
                            rd_en <= '0';
                        else
                            next_state <= readA;
                            rd_en <= '1';
                        end if;
                    end if;
                when readA =>
                    if rst = '1' then
                        next_state <= start;
                        rd_en <= '0';
                    else
                        if in_fifo_empty = '1' then
                            next_state <= waitB;
                            rd_en <= '0';
                        else
                            next_state <= addAB;
                            rd_en <= '1';
                        end if;
                    end if;
                when waitB =>
                    if rst = '1' then
                        next_state <= start;
                        rd_en <= '0';
                    else
                        if in_fifo_empty = '1' then
                            next_state <= waitB;
                            rd_en <= '0';
                        else
                            if out_fifo_full = '1' then
                                next_state <= waitB;
                                rd_en <= '0';
                            else
                                next_state <= addAB;
                                rd_en <= '1';
                            end if;
                        end if;
                    end if;
                when addAB => -- aka readB (read of B operator happens here)
                    if rst = '1' then
                        next_state <= start;
                        rd_en <= '0';
                    else
                        if in_fifo_empty = '1' then
                            next_state <= start;
                            rd_en <= '0';
                        else
                            next_state <= readA;
                            rd_en <= '1';
                        end if;
                    end if;   
                when others =>
                    next_state <= start;
                    rd_en <= '0';
            end case;
        end process;
    
    end Behavioral;
    
    library IEEE;
    use IEEE.STD_LOGIC_1164.ALL;
    use IEEE.NUMERIC_STD.ALL;
    
    entity core is
        port (
            operand_A_ieee, operand_B_ieee : in std_logic_vector(31 downto 0);
            result_ieee : out std_logic_vector(31 downto 0);
            clk, rst : in std_logic
        );
    end core;
    
    architecture Behavioral of core is
    
        component adder
            port (
                A, B: in std_logic_vector(31 downto 0);
                R: out std_logic_vector(31 downto 0)
            );
        end component;
    
    begin
    
        addition: adder port map (operand_A_ieee, operand_B_ieee, result_ieee);
    
    end Behavioral;
    
    library IEEE;
    use IEEE.STD_LOGIC_1164.ALL;
    use IEEE.NUMERIC_STD.ALL;
    entity adder is
        port (
            A, B: in std_logic_vector(31 downto 0);
            R: out std_logic_vector(31 downto 0)
        );
    end adder;
    
    architecture Behavioral of adder is
    
    begin
    
        R <= std_logic_vector(unsigned(A) + unsigned(B));
    
    end Behavioral;
    
  2. [SIMPLIFICADO EN COMPARACIÓN CON EL INICIAL, PERO YA NO ES RELEVANTE - VÉASE LA EDICIÓN POSTERIOR ANTERIOR] Enfoque para la E/S física del Spartan-6 (botones pulsadores, interruptores, LED):

    library IEEE;
    use IEEE.STD_LOGIC_1164.ALL;
    use IEEE.NUMERIC_STD.ALL;
    
    entity top is
    
        port (
            sw : in std_logic_vector(1 downto 0);
            led : out std_logic_vector(7 downto 0)
        );
    
    end top;
    
    architecture Behavioral of top is
    
        signal a : std_logic_vector(31 downto 0) := x"3f800000";
        signal b : std_logic_vector(31 downto 0) := x"3f800000";
        signal r : std_logic_vector(31 downto 0);
    
    begin
    
        r <= std_logic_vector(unsigned(a) + unsigned(b));
        led <= r(8 * (to_integer(unsigned(sw)) + 1) - 1 downto 8 * to_integer(unsigned(sw)));
    
    end Behavioral;
    
    # onBoard SWITCHES
    NET "sw<0>" LOC = "A10"; # Bank = 0, Pin name = IO_L37N_GCLK12,         Sch name = SW0
    NET "sw<1>" LOC = "D14"; # Bank = 0, Pin name = IO_L65P_SCP3,       Sch name = SW1
    
    # onBoard Leds
    NET "led<0>" LOC = "U18"; # Bank = 1, Pin name = IO_L52N_M1DQ15,       Sch name = LD0
    NET "led<1>" LOC = "M14"; # Bank = 1, Pin name = IO_L53P,              Sch name = LD1
    NET "led<2>" LOC = "N14"; # Bank = 1, Pin name = IO_L53N_VREF,     Sch name = LD2
    NET "led<3>" LOC = "L14"; # Bank = 1, Pin name = IO_L61P,              Sch name = LD3
    NET "led<4>" LOC = "M13"; # Bank = 1, Pin name = IO_L61N,              Sch name = LD4
    NET "led<5>" LOC = "D4";  # Bank = 0, Pin name = IO_L1P_HSWAPEN_0,     Sch name = HSWAP/LD5
    NET "led<6>" LOC = "P16"; # Bank = 1, Pin name = IO_L74N_DOUT_BUSY_1, Sch name = LD6
    NET "led<7>" LOC = "N12"; # Bank = 2, Pin name = IO_L13P_M1_2,         Sch name = M1/LD7
    
Actualice a la última versión de Vivado. Proporcione también un banco de pruebas adecuado, para que otros usuarios puedan reproducir su problema.

Respuestas (2)

No puedo decir con certeza que esto ayudará, pero encuentro que el proceso de "canalización" en la entidad "sumador" es inusual. Usaría un elsif en el borde ascendente del reloj y eliminaría A y B de la lista de sensibilidad.

Además, usar el flanco ascendente del paso puede no ser una buena idea ya que no es una señal de reloj. Una solución alternativa sería hacer algo así:

process(clk)
begin
    if rising_edge(clk) then
        step_r <= step;
        if step = '1' and step_r='0' then -- Finds rising edge of step
             <Logic>
        end if;
    end if;
end process;
NOTA: actualicé el mensaje original porque me di cuenta de que, para el diseño de Spartan6, el problema era solo un interruptor de entrada defectuoso que funcionaría solo en circunstancias físicas específicas. Esa parte ahora está resuelta, la primera, que es la importante, aún permanece.
Para que conste: el propósito del proceso de canalización era simplemente dividir esa adición simple en dos etapas, en caso de que pudiera haber un problema de tiempo (el resultado no se propaga en un sumador de propagación de acarreo / sumador de acarreo de ondulación en un ciclo de reloj, eso fue solo para eliminar este escenario). Ahora ese proceso se eliminó y ese diseño de Spartan6 se simplificó y funcionó.
Si bien estoy de acuerdo con elsif en el flanco ascendente del reloj, eliminar A y B de la lista de sensibilidad no habría tenido sentido, ya que el valor modificado en el proceso depende de los cambios en A y B, ya que el proceso se basa en sus valores. cambios de valor modificados.
@BogdanSorlea, en términos generales, si tiene un proceso síncrono, lo único que debe estar en la lista de sensibilidad es el reloj y los reinicios asíncronos. La lista solo se usa durante la simulación (para la mayoría de las herramientas, incluido Vivado) para que el simulador sepa durante qué eventos se debe ejecutar el proceso, lo que permite posibles reducciones en el tiempo de simulación. Dejar A y B en la lista de sensibilidad no cambiará los resultados, pero no es necesario porque el único evento que le importa al simulador es el reinicio y los bordes del reloj.
¿Está utilizando un FIFO de caída de primera palabra (FWFT)? De lo contrario, parece que operand_A_ieee tendrá datos no válidos durante la primera adición, ya que la salida FIFO no sería válida hasta un reloj después de rd_en = 1. Además, recomendaría registrar la adición; de lo contrario, tendrá un impacto negativo en el tiempo. Y trataría de no usar un FWFT FIFO con la salida "válida" habilitada al menos para la simulación para que pueda ver cuándo su entrada de datos es válida.

Mientras tanto, he descubierto cuál era el problema. Básicamente, tiene que ver con el endianismo de los bytes, es decir, xillybus presentará los bytes en un valor de 32 bits en el orden inverso al que se escribieron en el archivo del dispositivo (y volteará nuevamente de la misma manera al escribir desde el núcleo a el anfitrión).

Para obtener más detalles, consulte: https://forums.xilinx.com/t5/Implementation/Implemented-simple-binary-adder-returns-incorrect-result/mp/689491/highlight/false#M15227