Begrijpen van wiskunde in VHDL

VHDL (VHSIC Hardware Description Language) biedt robuuste ondersteuning voor het implementeren van rekenkundige bewerkingen, waardoor het een hoeksteen is voor het ontwerpen van digitale systemen zoals processors, digitale signaalprocessors (DSP's) en besturingseenheden. Aritmische blokken zoals adders, aftrekken en multipliers zijn fundamentele bouwstenen, en weten hoe ze efficiënt te implementeren is cruciaal voor zowel simulatie als synthese. Dit artikel biedt een grondige, productiegerichte exploratie van deze bewerkingen, die betrekking hebben op datatype selectie, gebruikersgebruik, architectonische stijlen en gemeenschappelijke optimalisatietechnieken.

Voordat je in specifieke componenten gaat duiken, is het essentieel om de twee belangrijkste VHDL-pakketten te begrijpen die rekenkundig mogelijk maken: ieeeee.numeric std en ieeeee.std logic unsigned[. De aanbevolen aanpak voor nieuwe ontwerpen is om numeric std te gebruiken, die unsigned[ en signated[ datatypes te gebruiken en overbelaste rekenkundige operatoren te leveren. Dit pakket zorgt voor consistent gedrag over simulatoren en synthesizers, waarbij de pitfalls worden vermeden die verband houden met het oudere std logic arith pakket.

Alle codevoorbeelden in dit artikel zijn geschreven met behulp van IEEE 1076-2008 compatibele VHDL en doel Xilinx of Intel (Altera) FPGA-apparaten, maar de concepten zijn van toepassing op elke digitale ontwerpstroom.

Adders in VHDL

Toevoeging is de meest fundamentele rekenkundige bewerking. In VHDL kunt u adders implementeren op verschillende niveaus van abstractie: gedrag (met behulp van de + operator), dataflow (met gelijktijdige signaaltoewijzing), of structurele (met instantiatie van lagere componenten).Voor de meeste praktische ontwerpen, gedragsmodellen met numeric std] zorgt het beste evenwicht tussen leesbaarheid en synthese-efficiëntie.

Eenvoudige Ripple-Carry-adder

Een rimpel-carry adder ketens full-adders samen, waar de uitvoering van elk bit voedt de carry-in van de volgende hogere bit. Het volgende voorbeeld toont een 4-bits niet-signe rimpel-carry adder met behulp van een gedragsproces:

library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.NUMERIC_STD.ALL;

entity adder4bit is
 Port (
 A : in unsigned(3 downto 0);
 B : in unsigned(3 downto 0);
 Sum : out unsigned(3 downto 0);
 Cout : out std_logic
 );
end adder4bit;

architecture Behavioral of adder4bit is
begin
 process(A, B)
 variable temp_sum : unsigned(4 downto 0);
 begin
 temp_sum := ('0' & A) + ('0' & B);
 Sum <= temp_sum(3 downto 0);
 Cout <= temp_sum(4);
 end process;
end Behavioral;

Belangrijkste punten:

  • De concatenatie ('0' & A) breidt de ingangen uit tot 5 bits, waarbij de uitvoering wordt vastgelegd.
  • Ongetekend direct met de +operator is synthesizeerbaar; het gereedschap geeft de juiste adderlogica (rimpel-carry, carry-lookahead, of LUT-gebaseerde in FPGA's).
  • De procesgevoeligheidslijst bevat alle invoersignalen, zodat de output onmiddellijk wordt bijgewerkt bij elke verandering (combinatiegedrag).

Carry-Lookahead Adder (CLA)

Voor bredere adders (bv. 16-bit of 32-bit) brengen rimpel-carry adders aanzienlijke vertraging in door de carry propagation. Een carry-lookahead adder vermindert deze vertraging door het genereren van draagsignalen parallel. De uitvoering van een volledige CLA in VHDL vereist echter een beschrijving van de generate ([gi] = ai[] & b[i[]]i[] XOR b[i]]) logica van de computergroep, dan dragen signalen automatisch bij de moderne synthesetools in de vorm van CLA-structuren in de praktijk.

entity adder16bit is
 Port (
 A : in unsigned(15 downto 0);
 B : in unsigned(15 downto 0);
 Sum : out unsigned(15 downto 0);
 CO : out std_logic
 );
end adder16bit;

architecture Behavioral of adder16bit is
 signal temp : unsigned(16 downto 0);
begin
 temp <= ('0' & A) + ('0' & B);
 Sum <= temp(15 downto 0);
 CO <= temp(16);
end Behavioral;

Adder met overflowdetectie

Bij het gebruik van signed-nummers, overflow optreedt wanneer het teken van het resultaat niet overeenkomt met het verwachte teken op basis van de ingangen. Overflow detecteren is essentieel bij processor ALU's. Het volgende knipsel toont een ondertekende adder met overflow detectie:

entity signed_adder is
 Port (
 A : in signed(7 downto 0);
 B : in signed(7 downto 0);
 Sum : out signed(7 downto 0);
 Overflow: out std_logic
 );
end signed_adder;

architecture Behavioral of signed_adder is
 signal extended_sum : signed(8 downto 0);
begin
 extended_sum <= (A(7) & A) + (B(7) & B); -- sign extend
 Sum <= extended_sum(7 downto 0);
 Overflow <= extended_sum(8) XOR extended_sum(7); -- sign mismatch
end Behavioral;

Aftrekken in VHDL

Aftrekken kan worden uitgevoerd door direct gebruik van de - operator of door het toevoegen van de twee complement van de subtrahend aan de minuend. Terwijl gedragsmodellering eenvoudig is, is het essentieel om de lening propagatie en het omgaan met negatieve resultaten te begrijpen.

Directe gedragsonderdrukker

De eenvoudigste afdrukker gebruikt de - operator met niet-gesigneerde of ondertekende types. Voor niet-gesigneerde aftrekken kan het resultaat negatief worden als B > A; in dergelijke gevallen moeten we een onderstroom detecteren (leen). Het volgende voorbeeld geeft zowel het verschil als een geleende vlag terug:

entity subtractor4bit is
 Port (
 A : in unsigned(3 downto 0);
 B : in unsigned(3 downto 0);
 Diff : out unsigned(3 downto 0);
 Borrow : out std_logic
 );
end subtractor4bit;

architecture Behavioral of subtractor4bit is
 signal temp_diff : signed(4 downto 0);
begin
 process(A, B)
 begin
 temp_diff <= signed('0' & A) - signed('0' & B);
 if temp_diff(4) = '1' then
 Borrow <= '1';
 else
 Borrow <= '0';
 end if;
 Diff <= unsigned(temp_diff(3 downto 0));
 end process;
end Behavioral;

Let op de conversie naar die is ondertekend voor de tussenliggende berekening; dit maakt een juiste omgang mogelijk met negatieve verschillen. De belangrijkste bit (temp diff(4) fungeert als de leenvlag.

Aftrekker met behulp van twee's Complement

Als alternatief kunt u aftrekken door de twee complementen van B toe te voegen. Deze techniek komt vaak voor bij het hergebruiken van een bestaande adder in een ALU. De twee complementen van B worden berekend als (not B) + 1. Hier is het concept:

signal B_comp : unsigned(3 downto 0);
signal sum_with_borrow : unsigned(4 downto 0);

B_comp <= (not B) + 1; -- two's complement
sum_with_borrow <= ('0' & A) + ('0' & B_comp);
Diff <= sum_with_borrow(3 downto 0);
Borrow <= not sum_with_borrow(4); -- borrow asserted if carry out is 0

Beide methoden zijn synthesevriendelijk; kies degene die past bij de architectonische voorkeuren van uw ontwerp.

Vergelijking en aftrekken

Aftrekken worden vaak gebruikt om vergelijkingsmaterialen te implementeren. Door het lenen of tekenen van het verschil te onderzoeken, kunt u bepalen of A > B, A < B, of A = B zonder een specifiek vergelijkingsteken. Bijvoorbeeld, na aftrekken, als het resultaat nul is (alle bits 0), zijn de ingangen gelijk. Als de lening/teken 1, dan A < B.

Multipliers in VHDL

Vermenigvuldigen is resource-intensief dan optellen of aftrekken. VHDL ondersteunt de * operator voor niet-gesigneerde en ondertekende types, die een combinatie multiplier in de hand werkt. Echter, voor grotere bitbreedtes kunnen combinatiemultipliers aanzienlijke logica gebruiken en lange propagatievertragingen hebben. Sequentiële en pijplijn implementaties zijn vaak nodig voor high-speed ontwerpen.

Combinatiemultiplier

Een 4-bits multiplier die de ]] operator gebruikt is triviaal:

entity multiplier4bit is
 Port (
 A : in unsigned(3 downto 0);
 B : in unsigned(3 downto 0);
 Product : out unsigned(7 downto 0)
 );
end multiplier4bit;

architecture Behavioral of multiplier4bit is
begin
 Product <= A * B;
end Behavioral;

Dit leidt tot een combinatie multiplier, die in een FPGA wordt meestal geïmplementeerd met behulp van dedicated DSP-slices (zoals Xilinx DSP48 blokken) of LUT-gebaseerde logica. Voor breedtes tot 18 bits, de meeste FPGA-tools kunnen de vermenigvuldiging in kaart brengen tot een enkele DSP-slice. Voor bredere multipliers, de synthese tool kan meerdere DSP-slices combineren of gebruik maken van zachte logica.

Sequentiële multiplier (Shift-and-Add)

Voor gebied-gestrainde ontwerpen of wanneer combinatievertraging onaanvaardbaar is, kan een sequentiële multiplier die over bits itereert worden gebruikt. Het klassieke shift-and-add algoritme vermenigvuldigt twee N-bits nummers over N klokcycli. Hieronder is een vereenvoudigd voorbeeld (4-bit multiplier, niet ondertekend, met controlesignalen weggelaten voor de duidelijkheid):

entity sequential_multiplier is
 Port (
 clk : in std_logic;
 reset : in std_logic;
 start : in std_logic;
 A : in unsigned(3 downto 0);
 B : in unsigned(3 downto 0);
 done : out std_logic;
 Product : out unsigned(7 downto 0)
 );
end sequential_multiplier;

architecture Behavioral of sequential_multiplier is
 signal multiplicand : unsigned(7 downto 0);
 signal multiplier : unsigned(3 downto 0);
 signal product_reg : unsigned(7 downto 0);
 signal count : integer range 0 to 4;
 signal busy : std_logic;
begin
 process(clk)
 begin
 if rising_edge(clk) then
 if reset = '1' then
 count <= 0;
 busy <= '0';
 product_reg <= (others => '0');
 done <= '0';
 elsif start = '1' and busy = '0' then
 multiplicand <= "0000" & A; -- left-aligned 4-bit multiplicand
 multiplier <= B;
 product_reg <= (others => '0');
 count <= 0;
 busy <= '1';
 done <= '0';
 elsif busy = '1' then
 if multiplier(0) = '1' then
 product_reg <= product_reg + multiplicand;
 end if;
 multiplicand <= multiplicand(6 downto 0) & '0'; -- shift left
 multiplier <= '0' & multiplier(3 downto 1); -- shift right
 count <= count + 1;
 if count = 3 then
 busy <= '0';
 done <= '1';
 end if;
 end if;
 end if;
 end process;
 Product <= product_reg;
end Behavioral;

Dit ontwerp maakt gebruik van een L-bit toevoeging per klokcyclus (4 cycli voor 4-bit ingangen). Het bespaart gebied maar offert doorvoer en latentie.

Multiplier met pijpleiding

Voor toepassingen met hoge doorvoer, een pijpleiding multiplier voegt registers tussen stadia van de combinatie vermenigvuldiging. Veel FPGA synthese tools kunnen automatisch pijplijn een multiplier wanneer u pijpleiding registers toe te voegen. Bijvoorbeeld, met behulp van een voor ... genereren loop of handmatige fase invoegen:

-- Pipelined unsigned 4x4 multiplier (2-stage pipeline)
architecture Pipelined of multiplier4bit is
 signal stage1_prod : unsigned(7 downto 0);
 signal stage1_A, stage1_B : unsigned(3 downto 0);
 signal stage2_prod : unsigned(7 downto 0);
begin
 process(clk)
 begin
 if rising_edge(clk) then
 stage1_A <= A;
 stage1_B <= B;
 stage1_prod <= stage1_A * stage1_B; -- first stage
 stage2_prod <= stage1_prod; -- second stage
 Product <= stage2_prod;
 end if;
 end process;
end Pipelined;

Deze eenvoudige tweetraps benadering verdubbelt de doorvoer (één resultaat per klok na de eerste latentie) en voegt slechts één extra registerlaag toe. Meer stappen kunnen worden toegevoegd voor hogere klokfrequenties.

DSP-slices gebruiken

Moderne FPGA's bevatten geharde DSP-schijven die zijn geconfigureerd voor vermenigvuldiging en accumulatie. Volg in VHDL, met behulp van de * operator, deze blokken vaak automatisch. Om DSP-inferentie te garanderen, de richtlijnen van de leverancier: houd operanden binnen de schijfbreedte (bijv. 18x18, 18x25), vermijd grote relationele operators op het resultaat, en gebruik de juiste breedte. Voor Xilinx 7-serie, kunt u ook instant de DSP48E1[]] primiveren, maar het gebruik van de operator heeft de voorkeur voor portabiliteit.

Xilinx Vivado Synthesis Guide

Optimalisatietechnieken en synthese-overwegingen

Bij de uitvoering van rekenkundige bewerkingen in VHDL, beïnvloeden verschillende factoren de kwaliteit van de resultaten:

  • Gegevensbreedte: Gebruik de kleinste noodzakelijke breedte om de logica te verminderen. Gebruik bijvoorbeeld unsigned(4 downto 0) als de ingangen 5 bits zijn.
  • Syntheseattributen: Attributen toepassen zoals houd , use dsp, of mult style om de mapping te beïnvloeden. Bijvoorbeeld:
  • Resource sharing: Als meerdere bewerkingen dezelfde adder of multiplier gebruiken, overweeg dan hardware te hergebruiken via een gedeeld onderdeel of een enkel rekenkundig blok met meerdere ingangen.
  • Pipelining: Invoegen registers om te voldoen aan timing beperkingen. Voor additieve ketens, balanceer de registerplaatsing om lange combinatoriale paden te vermijden.
  • Getekend vs Ongetekend: Gebruik ondertekend voor ondertekende bewerkingen; het synthesehulpmiddel zal de MSB behandelen als een teken beetje, die de rekenkundige logica afgeleid.
  • Kettingen voor de besturing: Voor brede adders is de draagketting een speciale hulpbron in FPGA's. Zorg ervoor dat uw synthesetool de keten niet breekt door ongepaste tussensignalen te gebruiken.

Combineren van operaties: ALU Voorbeeld

Om te illustreren hoe adders, aftrekken en multipliers integreren in een groter ontwerp, overwegen een eenvoudige Arithmetic Logic Unit (ALU) die twee 8-bits waarden kan toevoegen, aftrekken of vermenigvuldigen op basis van een geselecteerd signaal:

entity alu is
 Port (
 A, B : in signed(7 downto 0);
 op : in std_logic_vector(1 downto 0); -- "00": add, "01": sub, "10": mul
 result : out signed(15 downto 0)
 );
end alu;

architecture Behavioral of alu is
begin
 process(A, B, op)
 begin
 case op is
 when "00" => result <= resize(A + B, 16); -- sign extend
 when "01" => result <= resize(A - B, 16);
 when "10" => result <= A * B;
 when others => result <= (others => '0');
 end case;
 end process;
end Behavioral;

Deze ALU gebruikt hetzelfde resultaatregister en combineert de drie bewerkingen. Bij synthese wordt elke bewerking uitgevoerd als een apart blok, met de output geselecteerd door een multiplexer. Afhankelijk van het doelapparaat kan de multiplier het kritieke pad zijn.

Gebruik van IP-kernen voor complexe rekenkunde

Voor geavanceerde bewerkingen (bijvoorbeeld floating-point, vierkantswortel, modulo) of wanneer maximale prestaties nodig zijn, is het raadzaam om gebruik te maken van door leveranciers geleverde IP-kernen. Deze zijn zeer geoptimaliseerd en hebben geverifieerde simulatiemodellen. In VHDL instanteert u een IP-kern als onderdeel, waarbij uw signalen in kaart worden gebracht naar de poorten. De algemene kernen omvatten:

  • Xilinx Floating-Point Operator voor add/sub/multiply/divide in IEEE 754-formaat.
  • Altera (Intel) ALTMULT ADD voor vermenigvuldig-toevoegen operaties.
  • Lattice Divider voor vaste puntdeling.

Het gebruik van IP-kernen minimaliseert risico en resulteert vaak in betere prestaties dan handgecodeerde equivalenten. Raadpleeg de documentatie van de leverancier voor instantiation templates.

Intel FPGA IP Cores Guide

Testen en verifiëren

Simulatie is cruciaal voor rekenkundige ontwerpen. Schrijf testbanken die hoekcases uitvoeren: overflow, nul, maximum waarden en gemengde tekens (voor getekende types). Voor multiplicatoren, test alle combinaties van de kleinste inputs om het algoritme te verifiëren. Gebruik de assert verklaring om de verwachte resultaten te controleren. Voorbeeld testbank snippet voor een 4-bit adder:

signal A, B : unsigned(3 downto 0);
signal Sum : unsigned(3 downto 0);
signal Cout : std_logic;
...
A <= "1100"; B <= "0011"; wait for 10 ns;
assert (Sum = "1111" and Cout = '0')
 report "Adder failed for 12 + 3" severity error;

Voor grotere ontwerpen, overwegen met behulp van willekeurige stimulans en gouden modellen in scripting talen (Python, Tcl) om testvectoren te genereren.

VHDL Testbench Techniques (SynthWorks)

Conclusie

Het implementeren van rekenkundige bewerkingen in VHDL is een mix van het begrijpen van digitale rekenkunde, bekwaam gebruik van data types en operators, en bewustzijn van synthese gereedschap gedrag. Adders en aftrekken zijn eenvoudig bij het gebruik van numeric std, terwijl multiplicatoren vereisen zorgvuldige overweging van prestaties en gebied. Door gebruik te maken van gedragsbeschrijvingen, u snel werkontwerpen te bereiken, en door het toepassen van technieken zoals pipelining, resource sharing, en DSP-interferentie, optimaliseert u voor real-world hardware. Met de voorbeelden en richtlijnen die worden verstrekt, bent u uitgerust om robuuste rekenkundige eenheden te bouwen voor elk digitaal systeem.

Voor meer informatie, raadpleeg het IEEE VHDL Language Reference Manual en de leverancier-specifieke documentatie over rekenkundige gevolgtrekkingen.

IEEE Std 1076-2008 VHDL Taalreferentiehandboek