mikrocontroller.net

Forum: FPGA, VHDL & Co. Komplexe Pipeline mit AXI-Stream: Ready-Signal kombinatorisch?


Autor: dzopf (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallöle,
nachdem ich mich eine Weile mit Xilinx-Cores beschäftigt habe, und 
bereits komplexe Pipelines mit den Float-Generatoren gebaut habe, wollte 
ich nun alles mal selber machen und mir eine Pipeline bauen, die eine 
Multiplikation von 2 Posit-Nummern durchführt. Im Idealfall würde ich 
gerne die DSPs vom FPGA nutzen (per Inferenz) und AXI-Streams anbieten, 
die - wie die Float-IPs von Xilinx - in jeder Pipeline-Stufe einen Wert 
haben können, und dies auch funktioniert, wenn der Consumer danach nicht 
ready ist. Ein erster Prototyp existiert, dieser hat jedoch das Problem, 
dass ein sehr langer kombinatorischer Pfad entsteht, da alle 
ready-Signale jeder Pipeline-Stufe miteinander kombiniert werden, und 
das mehrstufig. Xilinx löst das Problem irgendwie, ich komme nur auf die 
Idee, jede Stufe mit einem "fully registered slice" auszustatten, 
welches die ready-Signale abkoppelt, dafür jedoch pro Pipeline-Stufe die 
doppelten Register braucht. Versteht einer, was ich meine und kann mir 
einen Tipp geben? Ich plane noch, die Pipeline zu verlängern (zurzeit 6 
Stufen) und würde gerne bei möglichst geringem Ressourcen-Aufwand die 
höchste Performance in Taktrate.

Autor: daniel__m (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
dzopf schrieb:
> fully registered slic

Hi,

das sind im Prinzip kleine, 2-stufige Fifos, welche das ready-Signal 
aufbrechen und somit den "Snake-Path" verkleinern.

Autor: dzopf (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Und das scheint mir sehr ineffizient zu sein, um nur das ready-Signal 
aufzusplitten. Außerdem stellt sich mir die Frage, ob ich bei diesen 
Register Slices überhaupt eine DSP mit allen internen Registern 
inferieren kann...

Autor: daniel__m (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
dzopf schrieb:
> sehr ineffizient

das ist relativ und kommt auf die Umsetzung an. Sofern "nur" Register 
en-masse verwendet werden, ist es weniger kritisch, da die "billig" sind 
und üblicherweise keine Problem darstellen.

Alternativ habe ich es so umgesetzt, dass ich am Ende ein Fifo packe und 
die Eingangsdaten "fire-and-forget" durch die Module schicke. Das Fifo 
steuert das Losschicken der Daten und muss natürlich die richtige 
Mindestgröße haben, dass es im Worst-Case alle Daten der Module 
aufnehmen kann (Latenz), wenn just in dem Moment das Down-Stream-Modul 
sein Ready wegnimmt.

Autor: Weltbester FPGA-Pongo (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
dzopf schrieb:
> ürde gerne bei möglichst geringem Ressourcen-Aufwand die
> höchste Performance in Taktrate.

Was ist bei Dir ein "geringer Bedarf" und was "eine hohe Taktrate"? Die 
DSPs kann man alternierend mit 500MHz takten (multi Cycle) sequenziell 
300MHz. So schnell sind die AXI-Busse normal nicht.

Autor: dzopf (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Die Nutzung der DSPs sind nur ein Beispiel gewesen, mir ging es auch um 
die Frage, wie man lange kombinatorische Pfade, die bei Pipelines 
aufgrund des ready-Signals entstehen, vermeidet. Die Idee mit der FIFO 
am Ende klingt schonmal gut, aber wird bei komplexeren Pipelines nicht 
immer funktionieren (bspw. wenn die Pipeline divergiert und mehrere 
FIFOs füttert, müsste man alle FIFOs auf deren Acceptance prüfen - wobei 
das bei registrierten Almost-Full-Signalen eigentlich hinhauen sollte). 
Meine Designs sind bisher immer sehr sparsam an Registern gewesen, bzw. 
war die Anzahl der LUTs immer höher. Bei aktuellen Bausteinen (habe den 
Cyclone 10 GX als Grundlage) ist die Anzahl der Register ja doppelt so 
hoch wie die der LUTs, es sollte also nichts dagegen sprechen, jede 
Pipeline-Stufe mit zwei Register-Sätzen auszustatten. Ich habe mich 
daran versucht, ein Code-Pattern zu entwickelt, um meine bisherige 
Implementierung möglichst einfach zu erweitern, aber ohne viel 
Boilerplate-Code wird das leider nichts. Ich arbeite nach der 
Gaisler-Methode, siehe Beispiel. Hier ist das alles noch sehr einfach, 
wird aber kompliziert, wenn die Pipeline-Stufe mehr als nur eine 
einfache Operation durchführt. Zurzeit versuche ich mich an einem 
Pattern, das auch bspw. eine Summation von einer Zahlenfolge durchführt, 
dabei aber auch zwei Output-Registersätze besitzt.
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;

entity multiplier is
  generic(
    DATA_WIDTH : positive := 16
  );
  port(
    clk_i     : in  std_ulogic;
    reset_i   : in  std_ulogic;
    -- Input A
    a_data_i  : in  std_ulogic_vector(DATA_WIDTH - 1 downto 0);
    a_valid_i : in  std_ulogic;
    a_ready_o : out std_ulogic;
    -- Input B
    b_data_i  : in  std_ulogic_vector(DATA_WIDTH - 1 downto 0);
    b_valid_i : in  std_ulogic;
    b_ready_o : out std_ulogic;
    -- Output C
    c_data_o  : out std_ulogic_vector(2 * DATA_WIDTH - 1 downto 0);
    c_valid_o : out std_ulogic;
    c_ready_i : in  std_ulogic
  );
end entity multiplier;

architecture rtl of multiplier is
  -- type declarations
  type output_fsm_t is (S_0, S_1, S_2);

  type reg_t is record
    -- Output state machine
    fsm       : output_fsm_t;
    -- Output-Sets
    set0_data : std_ulogic_vector(2 * DATA_WIDTH - 1 downto 0);
    set1_data : std_ulogic_vector(2 * DATA_WIDTH - 1 downto 0);
  end record reg_t;

  -- reset definition
  constant REG_RESET : reg_t := (
    fsm       => S_0,
    set0_data => (others => 'U'),
    set1_data => (others => 'U')
  );

  -- functions
  function mult(a, b : std_ulogic_vector(DATA_WIDTH - 1 downto 0))
  return std_ulogic_vector is
  begin
    return std_ulogic_vector(unsigned(a) * unsigned(b));
  end function mult;

  -- signal declarations
  signal r, rin : reg_t := REG_RESET;
begin
  comb : process(all)
    variable n : reg_t;
  begin
    n := r;

    n.set0_data := mult(a_data_i, b_data_i);
    n.set1_data := r.set0_data;

    case r.fsm is
      when S_0 =>
        if a_valid_i and b_valid_i then
          n.fsm := S_1;
        end if;

      when S_1 =>
        if a_valid_i and b_valid_i and c_ready_i then
          n.fsm := S_1;
        elsif a_valid_i and b_valid_i then
          n.fsm := S_2;
        elsif c_ready_i then
          n.fsm := S_0;
        else
          n.set0_data := r.set0_data;
        end if;

      when S_2 =>
        if c_ready_i then
          n.fsm := S_1;
        else
          n.set1_data := r.set1_data;
        end if;
    end case;

    rin <= n;

    -- Outputs
    case r.fsm is
      when S_0 =>
        a_ready_o <= b_valid_i;
        b_ready_o <= a_valid_i;

        c_data_o  <= (others => 'U');
        c_valid_o <= '0';

      when S_1 =>
        a_ready_o <= b_valid_i;
        b_ready_o <= a_valid_i;

        c_data_o  <= r.set0_data;
        c_valid_o <= '1';

      when S_2 =>
        a_ready_o <= '0';
        b_ready_o <= '0';

        c_data_o  <= r.set1_data;
        c_valid_o <= '1';
    end case;
  end process comb;

  regs : process(clk_i, reset_i)
  begin
    if reset_i then
      r <= REG_RESET;
    elsif rising_edge(clk_i) then
      r <= rin;
    end if;
  end process regs;
end architecture rtl;

Autor: daniel__m (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Per review würde ich sage, das sieht doch vielversprechend aus.

Wenn es um mehrere in- / outputs geht, evtl. wird es vereinfacher, wenn 
man erst per glue-logic die in-Streams zu einem Stream synchronisiert 
und auch ausgangsseitig erst im 2. Schritt per glue-logic aufsplittet.

Antwort schreiben

Die Angabe einer E-Mail-Adresse ist freiwillig. Wenn Sie automatisch per E-Mail über Antworten auf Ihren Beitrag informiert werden möchten, melden Sie sich bitte an.

Wichtige Regeln - erst lesen, dann posten!

  • Groß- und Kleinschreibung verwenden
  • Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang

Formatierung (mehr Informationen...)

  • [c]C-Code[/c]
  • [avrasm]AVR-Assembler-Code[/avrasm]
  • [vhdl]VHDL-Code[/vhdl]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] - Link zu Artikel
  • Verweis auf anderen Beitrag einfügen: Rechtsklick auf Beitragstitel,
    "Adresse kopieren", und in den Text einfügen




Bild automatisch verkleinern, falls nötig
Bitte das JPG-Format nur für Fotos und Scans verwenden!
Zeichnungen und Screenshots im PNG- oder
GIF-Format hochladen. Siehe Bildformate.

Mit dem Abschicken bestätigst du, die Nutzungsbedingungen anzuerkennen.