VHDL: Signalvergleiche für die Synthese optimieren

Als Vorwort werden in VHDL/Verilog bestimmte Codierungsstile verwendet, die den Synthesewerkzeugen helfen, auf unterschiedliche Hardware zu schließen (einige sind leistungsstärker als die anderen). Beispielsweise würde die Verwendung einer If-Else-If-Leiter auf eine Reihe von Muxen schließen, während eine Case-Anweisung auf einen einzelnen breiten Multiplexer schließen würde. Diese Codierungsstile sind nicht signifikant, wenn nur eine funktionale Simulation durchgeführt wird, aber wesentlich, wenn die RTL für die ASIC- oder FPGA-Implementierung angestrebt wird. Im Fall von FPGAs definiert die CLB-Architektur (konfigurierbarer Logikblock) die Fähigkeiten, die unter Verwendung von RTL erreicht werden (wie die LUT-Eingabebreite).

Kommen wir zur Frage: Ich habe viele Fälle gesehen, in denen zwei n-Bit breite Signale möglicherweise in VHDL verglichen werden müssen. Und ich brauche einen Rat zur Hardware-Inferenz. Ich werde das folgende Code-Snippet verwenden, um die Frage weiter einzugrenzen.

signal counter_a: unsigned(31 downto 0);
signal counter_b: unsigned(31 downto 0);
signal clk, trigger_en, count_b_en : std_logic;

counter_a_gen: process (clk) begin
    if(rising_edge(clk)) then
        counter_a <= counter_a + 1; -- free running counter
    end if;
end process;

counter_b_gen: process (clk) begin
    if(rising_edge(clk)) then
        if(count_b_en = '1') then
            counter_b <= counter_b + 1;
        else 
            counter_b <= (others => '0');
        end if;
    end if;
end process;

-- compare the counters to generate some logic
trigger_gen: process (clk) begin
    if(rising_edge(clk)) then
        if(counter_a = counter_b) then
            trigger_en <= '1';
        else 
            trigger_en <= '0';
        end if;
    end if;
end process;

Das obige Snippet hat zwei 32-Bit-Zähler counter_a und counter_b, die in einem sequentiellen Block verglichen werden müssen. Wenn ich eine LUT mit 4 Eingängen in einem FPGA betrachte, würde der Vergleich mehrere Logikebenen erfordern. Ein derartiger Pfad würde es aufgrund der enormen kombinatorischen Verzögerungen schwierig machen, das Timing einzuhalten. Meine Frage ist also, wie machen wir es optimiert? In diesem Fall um die Leistung zu steigern?

eine case-Anweisung würde auf einen einzigen breiten Multiplexer schließen. Nur wenn Sie die Parallel Case Synthesis-Direktive hinzufügen. Standardmäßig wird es auch als if-then-else-if behandelt.
Vielleicht. Aber ich denke, Synthesewerkzeuge sollten auch intelligent genug sein, um auf Parallelität in Schaltungen basierend auf benutzerdefinierten Anweisungen / Einschränkungen und nicht auf RTL-Attributen zu schließen.
Ich würde erwarten, dass die Tools die Logik so platzieren, dass sie eine Hochgeschwindigkeits-Carry-Kette verwenden kann, sodass sie möglicherweise nicht so schlimm ist, wie Sie denken. Eine andere Möglichkeit besteht darin, Dinge zu optimieren, damit das Tool die Art von DSP-Block verwendet, mit der Ihr Teil geliefert wird, geräteabhängiger Code, aber der DSP48E1 zum Beispiel hat einen Größenvergleich als einen seiner 'ALU'-Party-Tricks, von denen Sie wahrscheinlich einen bekommen könnten die Zähler aus demselben Block. Wenn Sie ultimative Geschwindigkeit benötigen, müssen Sie im Allgemeinen zumindest etwas in den Teil schreiben, auf den Sie abzielen.
Wie leiten wir DSP-Elemente aus Codierungsstilen ab? Oder müssen wir das Element zum Vergleich manuell instanziieren?
Xilinx Tools verwendet in diesem Fall keine Trageketten. Die PoC-Bibliothek bietet mehrere IP-Kerne, die eine bessere Leistung als die Standardsynthese erbringen.
Ein 32-Bit-Vergleich benötigt nur 11 LUT4 und hat eine Tiefe von 3. Das ist sehr schnell.
Hier ist der Vergleich auf zwei 32 Bit. Also schätze ich 21 LUT4s und immer noch 3 Logikebenen. Macht das Sinn?
Und wann verwendet Xilinx Trageketten? Zum Beispiel bei arithmetischen Operationen richtig?

Antworten (1)

  • Wenn Ihre Schaltung Verzögerungen des trigger_en-Signals unterstützen kann, können Sie den Vergleich aufteilen (z. B. 4 8-Bit-Komparatoren) und das Ergebnis über mehrere Zyklen leiten.

  • Sie können mehrere Komparatoren parallel mit zukünftigen Werten verwenden, Zähler_a + 1, Zähler_b + 1, Zähler a + 2, Zähler a + 3 ... leiten das Ergebnis jedes Komparators (wie oben) und dann entscheiden, welcher Vergleich gültig ist der Wert von count_b_en während der letzten 2 oder 3 Zyklen. Viel Hardware!

"Riesige kombinatorische Verzögerung": Es hängt wirklich von Ihrer Zielfrequenz ab. FPGAs haben eine schnelle Übertragsausbreitung und direkte Pfade zwischen benachbarten LUTs, sodass die Verzögerungen für 32-Bit-Komparatoren nicht sehr groß sind. (128 Bit ist ein großer Komparator, 32 Bit ist ziemlich schmal)

Der obige Vorschlag mit viel Hardware kann theoretisch schneller sein, aber manchmal praktisch langsamer, weil die zusätzliche Hardware Laufzeitverzögerungen, Signallast hinzufügt ...

Ich stimme zu, dass FPGAs schnelle Carry-Ketten haben, aber die durch diese Elemente hinzugefügte Ausbreitungsverzögerung ist immer noch signifikant, wenn die FPGA-Zielfrequenz größer als 250 MHz ist. Wie ich sehe, könnte die beste Methode darin bestehen, die Vergleiche auf mehrere Taktzyklen aufzuteilen.
Noch eine Ergänzung: Wenn ich einen Zähler mit einem statischen Wert vergleichen würde, wäre die Sache viel einfacher gewesen. Ich könnte den Zähler von (statischer Wert - 1) starten und bis -1 herunterzählen. Auf diese Weise kann anstelle eines 32 Bit breiten Vergleichs ein Einzelbit-MSB-Vergleich verwendet werden. Ich wollte wissen, ob man hier etwas ähnliches machen könnte.