Einfacher Binäraddierer funktioniert nur teilweise

SPÄTER BEARBEITEN:

1. Ich habe das Kintex7-Gerät nach der Implementierung auch visuell untersucht (dh Verbindungen usw.) und alles sieht in Ordnung aus - keine Verbindungen, die darauf hinweisen würden, dass die Dinge nicht in Ordnung wären (natürlich bin ich nicht den ganzen Weg zurück zu gegangen). Eingänge, da ich annehme, dass, wenn das 2-FIFO-basierte Loopback funktioniert, der Addierer irgendwie kaputt ist).

Außerdem bin ich auf diesen Xilinx Answer Record gestoßen . Ich habe versucht, den vorgeschlagenen Fix anzuwenden, aber es schien nicht zu funktionieren.

2. Beim zweiten Ansatz (manuelle Eingabe auf Spartan6) stellte ich fest, dass das Problem ein fehlerhafter Schalter war, der den Eingabewert nicht richtig einstellen würde, wenn nicht darauf geachtet wurde. Ich habe nochmals sorgfältig getestet und diesmal den richtigen Wert erhalten. Ich habe dieses Design jedoch geändert, um stattdessen nur die Eingaben von ROM/LUT zu nehmen und ein Byte nach dem anderen anzuzeigen, abhängig von den als Eingaben angegebenen Auswahlschaltern. Es ist keine Uhr mehr beteiligt. Das Problem mit dem ursprünglichen Vivado-basierten Projekt besteht weiterhin, was mich zu der Annahme veranlasst, dass es sich um ein Synthese-/Implementierungs-/fehlerhaftes Hardwareproblem handeln könnte.

ORIGINALE NACHRICHT:

Ich habe ein xillybus-basiertes PCIe-Design, das komplexer werden soll, aber im Moment liest es inkrementell nur Paare von 32-Bit-Werten aus einem 32-Bit-Eingangs-FIFO, das mit xillybus verbunden ist, addiert sie und schreibt sie in ein 32-Bit-Ausgangs-FIFO, das mit xillybus verbunden ist.

Jetzt sehe ich folgendes Problem:
ANMERKUNG 1: Die Ausgangswellenform der Verhaltenssimulation (RTL-Pegel, Vorsynthese) gibt korrekte Werte zurück, es ist die tatsächliche Implementierung auf der Platine, bei der ein oder mehrere Bits umgedreht zu sein scheinen ( gegenüber dem erwarteten Wert, der derselbe ist wie der Ausgang der Simulation)
ANMERKUNG 2: Ich verwende zum Beispiel die Werte 0x3F800000und 0x3F800000(ja, zweimal den gleichen Wert), was nach und nach addiert werden sollte 0x7F000000- wird unten erwähnt was das (falsche) Ergebnis in jedem Szenario war:

  • Wenn ich einen Loopback-Ansatz versuche (durch Lesen vom Eingangs-FIFO und Ausgabe an den Ausgangs-FIFO - also Verwendung von 2 FIFOs -, NICHT durch Verwendung von nur einem FIFO, das sowohl mit dem Eingang als auch mit dem Ausgang von/zu xillybus verbunden ist), werden die Werte wie erwartet zurückgegeben. Ich habe das nicht ausgiebig getestet, aber es scheint für alle Werte, die ich getestet habe, in Ordnung zu sein.
  • Wenn ich versuche, das Design auf einem NetFPGA-1G-CML-Board (Kintex-7 XC7K325T-1FFG676 FPGA) auszuführen, erhalte ich das Ergebnis 0x7E000100(denken Sie daran, der Loopback-Test funktioniert).
  • Wenn ich versuche, das äquivalente Design (angepasst daran, wie die Werte eingegeben werden - dh nicht über PCIe vom PC, sondern manuell über Schalter, Monopulsgenerator-gefilterte Drucktasten und Ausgabe auf LEDs) auf einem Atlys-Board (Spartan -6 XC6SLX45-3CSG324), dann ist das Ergebnis 0x6F00000(näher, da nur ein bisschen falsch, aber immer noch nicht genug)

Beachten Sie, dass ich keine Beschwerden über Verstöße gegen Zeitbeschränkungen erhalte - und ich habe sowieso viele Möglichkeiten ausprobiert, um dem Problem auf den Grund zu gehen (dh einige Möglichkeiten herauszufiltern), aber ich bin zu keinem Ergebnis gekommen. Fürs Protokoll, ich habe sogar den einfachen Addierer in eine 2-Level-Pipeline umgewandelt, um "sicherzustellen", dass genug Zeit für die Berechnung des Ergebnisses bleibt, aber das Ergebnis war dasselbe (falsch).

Ich kann anscheinend keine Post-Synthese- oder Post-Implementierungs-Funktions- oder Timing-Simulation ausführen, da ich eine lästige Fehlermeldung bekomme

FEHLER: [VRFC 10-716] formaler Port o des Modus out kann nicht mit dem tatsächlichen Port pcie_perst_b_ls des Modus in verknüpft werden, [...]

Beachten Sie, dass ich Vivado 2014.2 verwende (was ein bisschen alt ist, aber wäre das wirklich das Problem?)

Unten der Code für die 2 Ansätze (Xillybus-basierte PCIe-gesteuerte E/A und einfache physische E/A). Leider konnte ich es nicht besser formatieren:

  1. Ansatz für die Kintex-7 PCIe-basierte I/O:

    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. [VEREINFACHT IM VERGLEICH ZUM URSPRÜNGLICHEN, ABER NICHT MEHR RELEVANT - SIEHE SPÄTERE BEARBEITUNG OBEN] Ansatz für die physischen Spartan-6-I/O (Drucktasten, Schalter, LEDs):

    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
    
Bitte aktualisieren Sie auf die neueste Vivado-Version. Bitte stellen Sie auch eine geeignete Testbench bereit, damit andere Benutzer Ihr Problem reproduzieren können.

Antworten (2)

Ich kann nicht mit Sicherheit sagen, dass dies helfen wird, aber ich finde den „Pipeline“-Prozess in der „Adder“-Entität ungewöhnlich. Ich würde ein Elsif auf der steigenden Flanke der Uhr verwenden und A und B aus der Empfindlichkeitsliste entfernen.

Außerdem ist die Verwendung der steigenden Flanke von Schritt möglicherweise keine gute Idee, da es sich nicht um ein Taktsignal handelt. Eine alternative Lösung wäre, Folgendes zu tun:

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;
HINWEIS: Ich habe die ursprüngliche Nachricht aktualisiert, weil mir klar wurde, dass das Problem für das Spartan6-Design nur ein fehlerhafter Eingangsschalter war, der nur unter bestimmten physikalischen Umständen funktionieren würde. Dieser Teil ist jetzt gelöst, der erste - der wichtigste - bleibt noch.
Fürs Protokoll: Der Zweck des Pipeline-Prozesses bestand darin, diese einfache Addition in zwei Stufen aufzuteilen, nur für den Fall, dass es ein Timing-Problem gegeben haben könnte (Ergebnis, das sich nicht in einem Taktzyklus in einem Carry Propagate Adder / Ripple Carry Adder ausbreitet - das war nur um dieses Szenario zu eliminieren). Jetzt wurde dieser Prozess entfernt und das Spartan6-Design vereinfacht und funktioniert.
Während ich dem elsif auf der steigenden_Flanke der Uhr zustimme, hätte das Entfernen von A und B aus der Sensitivitätsliste keinen Sinn gemacht, da der im Prozess geänderte Wert von den Änderungen an A und B abhängt, da der Prozess basierend auf ihren Werten geänderte Wertänderungen.
@BogdanSorlea im Allgemeinen, wenn Sie einen synchronen Prozess haben, müssen die einzigen Dinge, die in der Sensitivitätsliste enthalten sein müssen, die Uhr und asynchrone Zurücksetzungen sein. Die Liste wird nur während der Simulation verwendet (für die meisten Tools, einschließlich Vivado), um dem Simulator mitzuteilen, bei welchen Ereignissen der Prozess ausgeführt werden muss, was eine potenzielle Verkürzung der Simulationszeit ermöglicht. Das Belassen von A und B in der Empfindlichkeitsliste ändert die Ergebnisse nicht, ist jedoch unnötig, da das einzige Ereignis, das den Simulator interessiert, das Zurücksetzen und die Taktflanken sind.
Verwenden Sie einen First-Word-Fall-Through-FIFO (FWFT)? Wenn nicht, sieht es so aus, als hätte operand_A_ieee während der ersten Addition ungültige Daten, da die FIFO-Ausgabe erst einen Takt nach rd_en = 1 gültig wäre. Außerdem würde ich empfehlen, die Addition zu takten, da dies sonst das Timing negativ beeinflusst. Und ich würde versuchen, kein FWFT-FIFO mit aktivierter "gültiger" Ausgabe zu verwenden, zumindest für die Simulation, damit Sie sehen können, wann Ihre data_in gültig ist.

Inzwischen habe ich herausgefunden, was das Problem war. Grundsätzlich hat es mit byteweiser Endianness zu tun - dh xillybus wird die Bytes in einem 32-Bit-Wert in umgekehrter Reihenfolge darstellen, in der sie in die Gerätedatei geschrieben wurden (und beim Schreiben vom Kern auf die gleiche Weise wieder umkehren). der Wirt).

Weitere Einzelheiten finden Sie unter: https://forums.xilinx.com/t5/Implementation/Implemented-simple-binary-adder-returns-incorrect-result/mp/689491/highlight/false#M15227