480 likes | 686 Views
VHDL-Synthese für Fortgeschrittene. Ronald Hecht ronald.hecht@etechnik.uni-rostock.de. Motivation. Synthese-Tools werden immer leistungsfähiger Abstraktionslevel steigern! Lesbarkeit Wartbarkeit Design-Reuse Robustheit des Codes erhöhen!. Überblick. ieee.numeric_std und integer
E N D
VHDL-Synthese für Fortgeschrittene Ronald Hecht ronald.hecht@etechnik.uni-rostock.de
Motivation • Synthese-Tools werden immer leistungsfähiger • Abstraktionslevel steigern! • Lesbarkeit • Wartbarkeit • Design-Reuse • Robustheit des Codes erhöhen!
Überblick • ieee.numeric_std und integer • Packages • Clock und Reset • Technologie-Mapping • Flexible Interfaces • 2-Prozess-Methode
Das Paket numeric_std • std_logic_arith/signed/unsigned sind Synopsys Pakete • Defacto Industrie Standard, aber nicht standardisiert • numeric_std ist IEEE 1076.3 Standard • Strenger typisierte Operatoren • Für neue Designs streng empfohlen type UNSIGNED is array (NATURAL range <>) of STD_LOGIC; type SIGNED is array (NATURAL range <>) of STD_LOGIC;
Typumwandlungstd_logic_vector • Es ist nicht definiert, ob std_logic_vector signed oder unsigned ist • Deshalb explizite Typumwandlung variable slv : std_logic_vector(7 downto 0); variable u : unsigned(7 downto 0); variable s : signed(7 downto 0); slv := std_logic_vector(u); slv := std_logic_vector(s); u := unsigned(slv); s := signed(slv);
Typumwandlunginteger • Konvertierungsfunktionen to_... • Bei Umwandlung von std_logic_vector nach integer signed/unsigned beachten! variable ui : integer range 0 to 255; variable si : integer range -128 to 127; ui := to_integer(u); si := to_integer(s); ui := to_integer(unsigned(slv)); u := to_unsigned(ui, u'length); s := to_signed(si, s'length); slv := std_logic_vector(to_unsigned(ui, slv'length));
Operatoren in numeric_std • Alle gängigen Integeroperationen • Schiebeoperationen • Operationen mit std_logic_vector etwas umständlich aber eindeutig + - * / mod rem = /= < > <= >= shift_left shift_right rotate_left rotate_right sll srl rol ror u := u + 1; s := s + 1; slv := std_logic_vector(unsigned(slv) + 1);
integer und boolean versus std_logic_vector • signed/unsigned, std_logic_vector oft etwas umständlich • Basieren auf neunwertigem std_logic • Langsam in der Simulation im Vergleich zu integer und boolean • Verwende nach Möglichkeit integer und boolean für • Zähler, Adressen, Arithmetische Operationen • Interne Statussignale • Synthetisierbar!
Integer für die Synthese • Immer range angeben! • Alles optimal synthesefähig! variable a, b, c : integer range 0 to 255; variable addr : integer range 0 to depth - 1; c := a + b; c := a * b; -- shift left/right c := a * 4; c := a / 8; -- wrap counter c := (a + 1) mod 256; -- get some bits c := (a / 4) mod 16; -- access memory variable mem : mem_type; o := mem(addr); -- binary to one-hot o := (others => '0'); o(addr) := '1';
Verwende Variablen! • Hohe Simulationsgeschwindigkeit • Einfaches Debugging • Auch im Waveform-Viewer darstellbar • Sequentielle Darstellung • Sofortige Aktualisierung • Besser verständlich und wartbar • Sehr gute Syntheseergebnisse
Verwende Packages! • Design-Reuse, Wartbarkeit • Modularität, Abstraktion package package_name is -- Deklaration von: -- Typen und Untertypen -- Konstanten, Aliase -- Signalen, Files -- Funktionen -- Prozeduren -- Attributen -- Komponenten -- Definition von: -- Konstanten -- Attributen end package_name; package body package_name is -- Definition von vorher -- deklarierten Objekten: -- Funktionen -- Prozeduren -- Konstanten -- Deklaration/Definition -- von zusaetzlichen -- Objekten end package_name;
Beispiel CRC-Package • Beschreibung der komb. Logik -- CRC32 Generator: "CRC-32", Ethernet, AAL5 constant CRC32_GEN : std_logic_vector(31 downto 0) := "00000100110000010001110110110111"; -- CRC-CCITT Generator: HDLC constant CRC_CCITT_GEN : std_logic_vector(15 downto 0) := "0001000000100001"; -- calculates next crc value, data vector function next_crc ( crc : std_logic_vector; -- current CRC value data : std_logic_vector; -- incoming data gen : std_logic_vector) -- generator Polynom w/o x^n return std_logic_vector; begin ... ... end next_crc;
Weitere Anwendungen von Packages • Kombinatorische Logik • Interfaces, Komponenten • Helper-Funktionen, Typumwandlung • Design Konfiguration • Verhaltensmodelle • Logging
rising_edge(clk) • Zwei Möglichkeiten in VHDL für flankensensitive Elemente • rising_edge() ist in der Simulation robuster • Kürzer und einfacher aufzuschreiben • Verwende rising_edge() clk'event and clk = '1' rising_edge(clk) FUNCTION rising_edge (SIGNAL s : std_ulogic) RETURN BOOLEAN IS BEGIN RETURN (s'EVENT AND (To_X01(s) = '1') AND (To_X01(s'LAST_VALUE) = '0')); END;
Register mit und ohne Reset • Problem: Einige Register werden nicht zurückgesetzt • Gated Clock oder Clock Enable regs_wrong: process (clk, rst) begin if rst = '0' then r1 <= '0'; elsif rising_edge(clk) then r1 <= x; r2 <= y; end if; end process regs_wrong;
Alternative synthesefähige Beschreibung des Resets • Ausnutzung der Priorität durch Beschreibungsreihenfolge • Synchrones und asynchrones Reset regs_s: process (clk) begin if rising_edge(clk) then r1 <= x; r2 <= y; if rst = '0' then r1 <= '0'; end if; end if; end process regs_s; regs_as: process (clk, rst) begin if rising_edge(clk) then r1 <= x; r2 <= y; end if; if rst = '0' then r1 <= '0'; end if; end process regs_as;
Reset – Synchron oder Asynchron? • Beides hat Vor- und Nachteile • Synchrone Resets häufig für FPGAs • Asynchrone Resets oft für ASICs • Konfigurierbar? • Externes Reset immer einsynchronisieren! rst_sync: process (clk, pad_rst) begin if rising_edge(clk) then r <= '1' & r(r'high downto 1); end if; if pad_rst = '0' then r <= (others => '0'); end if; end process rst_sync; rst <= r(0);
Konfigurierbares Reset • Konfigurations-Package constant USE_SYNC_RESET : boolean := TRUE; constant USE_ASYNC_RESET : boolean := NOT(USE_SYNC_RESET); function sync_reset(rst : std_logic) return boolean is begin if (USE_SYNC_RESET) then return (rst = '0'); else return false; end if; end; function async_reset(rst : std_logic) return boolean is begin if (USE_ASYNC_RESET) then return (rst = '0'); else return false; end if; end;
Konfigurierbares Reset • Verwendung der Reset-Funktionen • Redundante Reset-Anweisungen vermeidbar durch Prozedur reg: process (clk, rst) begin if rising_edge(clk) then q <= d; if sync_reset(rst) then q <= '0'; end if; end if; if async_reset(rst) then q <= '0'; end if; end process regs;
Technologie Mapping • Schaltungen enthalten architektur-spezifische Komponenten • Pads • RAMs, Registerfiles • Spezielle Beschleunigende Hardware • Schlechte Portierbarkeit • Lösung: Technologie-Wrapper für diese Elemente
Technologie MappingDateien • Wrapper in tech_map • Ausnutzung von Designhierarchie
Technologie MappingVHDL-Implementierung package tech_map is component inpad port (pad : in std_logic; q : out std_logic); end component; ... end tech_map; entity inpad ... architecture rtl of inpad is begin ... ip0 : if TARGET_TECH = virtex generate ipx : virtex_inpad port map (q => q, pad => pad); end generate; ip4 : if TARGET_TECH = atc18 generate ipx : atc18_inpad port map (q => q, pad => pad); end generate; ... end rtl; type target_tech_type is (gen, virtex, virtex2, apex, atc18, ...); constant TARGET_TECH : target_tech_type := apex;
Flexible Interfaces • Probleme bei großen Designs: • Entities haben viele Signale • Änderung der Entities bedeutet Modifikationen in vielen Dateien • Schwierige Namensgebung • Schlecht wartbar • Lösung: Definition flexibler Signaltypen mit Records
Flexible Interfaces – in/out • Entity-in/out erzwingt zwei Datentypen • Deklaration von name_in_type und name_out_type type mem_in_type is record data : mem_data_type; end record; type mem_out_type is record address : mem_address_type; data : mem_data_type; data_tri : std_logic; csn : std_logic; oen : std_logic; writen : std_logic; end record; -- Memory controller entity mem_ctrl is port ( clk : in std_logic; rst : in std_logic; memi : in mem_in_type; memo : out mem_out_type -- noch weitere Signale -- Steuer- und -- Datenleitungen ); end mem_ctrl;
Flexible Interfaces – inout? • Pads können inout sein • Bei Verwendung von „drive“-Signalen kein inout-Typ notwendig -- Top Entity, Signale -- repraesentieren Pads entity top is port ( clk : in std_logic; rst : in std_logic; data : inout mem_data_type; address : out mem_address_type; csn : out std_logic; oen : out std_logic; writen : out std_logic -- noch weitere Signale ); end top; mem_ctrl_i: mem_ctrl port map ( clk => clk, rst => rst, memi => memi, memo => memo); data <= memo.data when memo.data_tri = '0' else (others => 'Z'); memi.data <= data;
Flexible Interfaces • Keine Records in den Ports der Top-Entity • Tristate-Buffer nur in der Top-Entity • Bei bidirektionalen On-Chip-Bussen Tristate-Buffer in der Bus-Entity • On-Chip-Tristate aber stets vermeiden! • Testbarkeit • Geschwindigkeit • inout-Type nicht nötig
Flexible InterfacesStrategien • IPCore-Schnittstellen ähneln sich • System, Steuer- und Statussignale, RX und TX
Flexible Interfaces – Tipps • Takt und Reset nicht in Records • Hierarchische Records entity hdlc_layer is port ( clk : in std_logic; rst : in std_logic; rx_clk : in std_logic; tx_clk : in std_logic; ctrli : in hdlc_ctrl_in_type; ctrlo : out hdlc_ctrl_out_type; clienti : in hdlc_client_in_type; cliento : out hdlc_client_out_type; hdlci : in hdlc_in_type; hdlco : out hdlc_out_type); end hdlc_layer; type hdlc_client_in_type is record rx : hdlc_client_rx_in_type; tx : hdlc_client_tx_in_type; end record; type hdlc_client_out_type is record rx : hdlc_client_rx_out_type; tx : hdlc_client_tx_out_type; end record;
Flexible InterfacesVorteile • Wenige Signale in der Entity und in Sensitivity-Listen • Einfaches Hinzufügen von Ports • Einfaches route-through • Gute Wartbarkeit • Hoher Abstraktionsgrad • Hierarchische Schnittstellen
Abstraktion digitaler Logik • Synchrones Design (Modul) besteht aus: • Kombinatorischer Logik • Registern (Flip-Flops, Speicher) • Register an den Ausgängen oder nicht? • Moore/Mealy haben im Normalfall keine Register am Ausgang
Nichtabgetaktete Ausgänge • Problematische Timinganalyse • Kombinatorische Schleifen möglich • Undefinierte Eingangssignallaufzeit für angeschlossene Module • Glitches Vorsicht, Probleme!
Abgetaktete Ausgänge! • Am Ausgang nach Möglichkeit immer Register • Ausnahme: Single-Cycle Handshake • req mit Register • ack ohne Register
Traditionelles RTL-VHDL • Viele kleine parallele Anweisungen und Prozesse • Viele Signale • state/next_state-Signalnamen • Unflexible Designpattern • Kombinatorische Logik • Register, FSMs • „Schematic-VHDL“
Traditionelles RTL-VHDL Probleme • Langsame Simulation, da viele Signale und Prozesse • Schwer und meist nur mit Hilfe eines Schaltplans zu verstehen • Schwieriges Debugging • Algorithmus nicht erkennbar • Fehleranfällig • Latches • Mehrfachtreiber
2-Prozess Methode • Modul besteht nur aus 2 Prozessen • Sequentieller Prozess r = rin • Kombinatorischer Prozess rin = f(r, in) • Ausgänge outreg = g(r) undoutunreg = h(r, in)
2-Prozess Methode • Zwei lokale Signale als Record für alle Register • Register Eingang rin • Register Ausgang r • Algorithmus • Kombinatorischer Prozess f(r, in) • Lokale Variable v für den neuen Registerinhalt rin = v • Weitere lokale Variablen für kombinatorische Ausgänge und Zwischenergebnisse
2-Prozess Methode VHDL Beispiel • Neuer Typ für alle Register • Zwei Signale und ein Prozess für alle Register • Einfaches Entfernen und Hinzufügen von Registern type state_type is (low, high); type reg_type is record state : state_type; o : std_logic; end record; signal r, rin : reg_type; reg : process (clk, rst) begin if rising_edge(clk) then r <= rin; end if; if async_reset(rst) then reset(r); end if; end process reg;
2-Prozess Methodereset(r) • Bei Registerdeklaration auch die reset-Prozedur definieren • Vermeidung von Redundanz procedure reset ( signal r : inout reg_type) is begin -- reset r.state <= low; r.o <= '0'; end reset; reg : process (clk, rst) begin if rising_edge(clk) then r <= rin; if sync_reset(rst) then reset(r); end if; end if; if async_reset(rst) then reset(r); end if; end process reg;
2-Prozess MethodeVHDL Beispiel • Variable zur Berechnung des neuen Register-wertes • Defaultzuweisung • Algorithmus • Zuweisung an den Registereingang • Treiben der Aus-gänge comb : process (r, i) variable v : reg_type; begin -- process comb -- Defaultzuweisung v := r; -- Ausgang meist '0' v.o := '0'; -- Zustandsmaschine case r.state is when low => if i = '1' then v.state := high; v.o := '1'; end if; when high => if i = '0' then v.state := low; end if; end case; -- Ausgaenge treiben rin <= v; o <= r.o; end process comb;
2-Prozess MethodeVorteile • Sequentielle Beschreibung des Algorithmus • Konsistenter Coding-Style • Einfach zu verstehen • Sehr gut wartbar • Schnell in der Simulation • Problemlose Synthese • Kaum Fehleranfällig
2-Prozess Methode – Tipps • Defaultwerte für v.xyz • Variablen für Zwischenergebnisse und kombinatorische Ausgänge • Kombinatorische Ausgabe auch mit o<=v.ack möglich • Funktionen und Prozeduren für häufig wiederkehrende Aufgaben • Mut zum einfacher lesbaren Code! • Beispiele: www.gaisler.com und LAPV5DL
Duplizieren von LogikSchleifen oder „generate“? • Grenzen müssen zur Compile-Time bekannt sein • Viele weitere Anwendungen • Mut zu Schleifen! for i in r.count'range loop v.count(i) := (r.count(i) + 1) mod 8; if v.count(i) = 0 then v.tick(i) := '1'; else v.tick(i) := '0'; end if; end loop;
Auswahl von Logikif-then oder „generate“? • Bedingung muss zur Compile-Time bekannt sein if CALIBRATE then -- remove this logic when -- CALIBRATE = false v.period_counter := r.period_counter + 1; if rising then v.period := v.period_counter; v.period_counter := 0; end if; else -- remove this logic when -- CALIBRATE = true v.period := CLK_RATE / FREQUENCY; end if;
Zusammenfassung • ieee.numeric_std verwenden • Integer, Variablen, Packages • Takt und Reset • Technologie Mapping • Flexible Interfaces • 2-Prozess-Methode